本实验是通过LCD12864来显示键盘上被按下的按键,实验比较简单,在LCD12864固定的DDRAM地址上显示,缺点就是不能保存上一次被按的内容,后者会覆盖掉前面,所以屏上仅有一个字符显示。保存上一次内容不被覆盖掉方法还待改进。目前将就这样吧。

关于LCD12864显示可以参考“LCD12864 液晶显示-汉字及自定义显示(并口)”,代码稍微改了一下,可以参考代码。

PS2键盘解码实验也比较简单,可以参考特权的或是“verilog HDL的那些事儿”也可以在网上找到相关的资料。

ps2_control.v

 module ps2_control(
//input
sys_clk,
rst_n,
key_clk,
key_data, //output
data_buf,
);
input sys_clk; //50Mhz
input rst_n;
input key_clk; //键盘时钟
input key_data; //键盘数据 output [:] data_buf; //保存要显示的数据 //***********************************************
//检测key_clk的下降沿
//***********************************************
reg key_clk_1;
reg key_clk_2;
always @(posedge sys_clk or negedge rst_n)
if(!rst_n) begin
key_clk_1 <= 'b1;
key_clk_2 <= 'b1;
end
else begin
key_clk_1 <= key_clk;
key_clk_2 <= key_clk_1;
end wire key_clk_n;
assign key_clk_n = key_clk_2 & (~key_clk_1);
//***********************************************
//对key_data上的数据进行保存
//***********************************************
reg [:] i;
reg [:] data_temp;
always @(posedge sys_clk or negedge rst_n)
if(!rst_n) begin
i <= 'd0;
data_temp <= 'h00;
end
else if(key_clk_n) begin
case(i)
'd0: i <= i + 1'b1; //起始位不处理 'd1,4'd2,'d3,4'd4,'d5,4'd6,'d7,4'd8:
begin
i <= i + 'b1;
data_temp[i-] <= key_data;
end 'd9: i <= i + 1'b1; //奇校验位不处理 'd10: i <= 4'd0; //停止位不处理 default: ;
endcase
end reg key_f0; //松键标志位,置1表示接收到数据8'hf0,再接收到下一个数据后清零
reg[:] ps2_data;
always @(posedge sys_clk or negedge rst_n) //接收数据的相应处理,这里只对1byte的键值进行处理
if(!rst_n) begin
key_f0 <= 'b0;
ps2_data <= 'h00;
end
else if(i=='d10) //刚传送完一个字节数据
begin
if(data_temp == 'hf0)
key_f0 <= 'b1; //说明有键被释放
else if(!key_f0) //说明有键按下
ps2_data <= data_temp; //锁存当前键值
else
key_f0 <= 'b0;
end reg [:] data_buf;
always @ (ps2_data) begin
case (ps2_data)
'h15: data_buf = "Q";
'h1d: data_buf = "W";
'h24: data_buf = "E";
'h2d: data_buf = "R";
'h2c: data_buf = "T";
'h35: data_buf = "Y";
'h3c: data_buf = "U";
'h43: data_buf = "I";
'h44: data_buf = "O";
'h4d: data_buf = "P";
'h1c: data_buf = "A";
'h1b: data_buf = "S";
'h23: data_buf = "D";
'h2b: data_buf = "F";
'h34: data_buf = "G";
'h33: data_buf = "H";
'h3b: data_buf = "J";
'h42: data_buf = "K";
'h4b: data_buf = "L";
'h1a: data_buf = "Z";
'h22: data_buf = "X";
'h21: data_buf = "C";
'h2a: data_buf = "V";
'h32: data_buf = "B";
'h31: data_buf = "N";
'h3a: data_buf = "M";
default: data_buf = 'h00;
endcase
end endmodule

LCD12864.v

 module LCD12864(
//input
sys_clk,
rst_n,
data_buf, //output
lcd_rs,
lcd_rw,
lcd_en,
lcd_data,
lcd_psb
);
input sys_clk;// 50MHZ
input rst_n;
input [:] data_buf; output lcd_rs;//H:data L:command
output lcd_rw;//H:read module L:write module
output lcd_en;//H active
output [:] lcd_data;
output lcd_psb;//H:parallel module L:SPI module /***************************************************/
parameter T3MS = 'd149_999;
parameter IDLE = 'd0,
INIT_FUN_SET1 = 'd1,
INIT_FUN_SET2 = 'd2,
INIT_DISPLAY = 'd3,
INIT_CLEAR = 'd4,
INIT_DOT_SET = 'd5,
SET_DDRAM = 'd6,
WRITE_DATA1 = 'd7;
/* INIT_FUN_SET3 = 4'd8,
SET_CGRAM = 4'd9,
WRITE_DATA2 = 4'd10,
SET_DDRAM2 = 4'd11,
SET_CUSTOM_L = 4'd12,
SET_CUSTOM_H = 4'd13,
STOP = 4'd14;*/
/***************************************************/
//产生周期为6MS的lcd_clk给LCD
reg [:] cnt;
reg lcd_clk;
always @(posedge sys_clk or negedge rst_n)
if(!rst_n) begin
cnt <= 'd0;
lcd_clk <= 'b0;
end
else if(cnt == T3MS)begin
cnt <= 'd0;
lcd_clk <= ~lcd_clk;
end
else
cnt <= cnt + 'b1; /***************************************************/
reg lcd_rs;
always @(posedge lcd_clk or negedge rst_n)
if(!rst_n)
lcd_rs <= 'b0;
else if(state == WRITE_DATA1)
lcd_rs <= 'b1; //写数据模式
else
lcd_rs <= 'b0; //写命令模式
/***************************************************/
reg [:] state;
reg [:] lcd_data;
reg [:] num;
reg en;
always @(posedge lcd_clk or negedge rst_n)
if(!rst_n) begin
state <= IDLE;
lcd_data <= 'h00;
en <= 'b1;
num <= 'd0;
end
else
case(state)
IDLE:
begin
state <= INIT_FUN_SET1;
lcd_data <= 'hzz;
en <= 'b1;
end INIT_FUN_SET1:
begin
lcd_data <= 'h30; //功能设定
state <= INIT_FUN_SET2;
end INIT_FUN_SET2:
begin
lcd_data <= 'h30; //功能设定
state <= INIT_DISPLAY;
end INIT_DISPLAY:
begin
lcd_data <= 'h0c; //显示设定
state <= INIT_CLEAR;
end INIT_CLEAR:
begin
lcd_data <= 'h01; //清屏
state <= INIT_DOT_SET;
end INIT_DOT_SET:
begin
lcd_data <= 'h06; //进入点设定
state <= SET_DDRAM;
end SET_DDRAM:
begin
lcd_data <= 'h94;//2 line
state <= WRITE_DATA1;
end WRITE_DATA1:
begin
lcd_data <= data_buf;
state <= SET_DDRAM; //一直在同一个地方刷新显示
end /* STOP:
begin
en <= 1'b0;//显示完了,lcd_e就一直拉为低
state <= STOP;
end */ default: state <= IDLE;
endcase /***************************************************/
assign lcd_rw = 'b0;//只有写模式
assign lcd_psb = 'b1;//并口模式
assign lcd_en = en ? lcd_clk : 'b0;
/***************************************************/
endmodule

ps2_top.v

 module ps2_top(
//input
sys_clk,
rst_n,
key_clk,
key_data, //output
lcd_rs,
lcd_rw,
lcd_en,
lcd_data,
lcd_psb
);
input sys_clk;// 50MHZ
input rst_n;
input key_clk; //键盘时钟
input key_data; //键盘数据 output lcd_rs;//H:data L:command
output lcd_rw;//H:read module L:write module
output lcd_en;//H active
output [:] lcd_data;
output lcd_psb;//H:parallel module L:SPI module wire [:] data_buf; ps2_control u1(
//input
.sys_clk(sys_clk),
.rst_n(rst_n),
.key_clk(key_clk),
.key_data(key_data), //output
.data_buf(data_buf),
); LCD12864 u2(
//input
.sys_clk(sys_clk),
.rst_n(rst_n),
.data_buf(data_buf), //output
.lcd_rs(lcd_rs),
.lcd_rw(lcd_rw),
.lcd_en(lcd_en),
.lcd_data(lcd_data),
.lcd_psb(lcd_psb)
);
endmodule

PS2接口和LCD12864采用的是飞线连接的,显示效果图:

PS2键盘 + LCD12864 实验-LMLPHP                            PS2键盘 + LCD12864 实验-LMLPHP

05-04 07:01