一 、矩阵键盘原理
如何将自己理解的东西教给别人,这是一门学问,我很不擅长,所以希望通过写点博客,来锻炼自个的表达能力,今天匆忙搬去房山区良乡,惊魂未定,草草写了这个文章,如有错误之处,敬请谅解。
我们要写的是矩阵键盘的驱动,因此必须先了解键盘的工作原理,只有知道键盘需要什么样的信号、有哪些输入输出,你才好确定驱动该怎么写。
我使用的是至芯科技的开发板,其键盘电路原理图如下:
4x4矩阵键盘电路原理图
把键盘设计成矩阵的形式,就是为了减少端口的消耗。图中的行信号(row)是键盘反馈给FPGA的信号,列信号(col)是FPGA给键盘的信号,是键盘的输入,键盘扫描方式采用低电平列扫描,详细的原理就不细说了。
二、设计代码
采用模块化的设计思想,首先设计一个keyboard_drive的顶层,顶层下面例化三个子模块:矩阵键盘keyboard模块、按键值寄存和增加位宽show_data模块,及七段数码管seven_tube_drive显示模块。代码如下:
1、顶层设计
module keyboard_drive(inputwireclk,inputwirerst_n,inputwire[3:0]row,outputwire[3:0]col,outputwire[7:0]seven_tube_seg,outputwire[2:0]seven_tube_sel);wire[3:0]key_num;wireflag;wire[23:0]show_data;keyboard keyboard_inst(.clk(clk),.rst_n(rst_n),.row(row),.key_num(key_num),.col(col),.flag(flag));show_data show_data_inst(.clk(clk),.rst_n(rst_n),.key_num(key_num),.flag(flag),.show_data(show_data));seven_tube_drive seven_tube_drive_inst(.clk(clk),.rst_n(rst_n),.show_data(show_data),.seven_tube_seg(seven_tube_seg),.seven_tube_sel(seven_tube_sel));endmodule
2、矩阵键盘模块
module keyboard(inputwireclk,inputwirerst_n,inputwire[3:0]row,outputreg[3:0]key_num,outputreg[3:0]col,outputregflag);parameterCNT1_MAX=50_000;parameterCNT2_MAX=20;localparamS0=3'b001;localparamS1=3'b010;localparamS2=3'b100;reg[2:0]state;reg[15:0]cnt1;reg[4:0]cnt2;reg[7:0]row_col;wireflag_1ms;always @ (posedge clk, negedge rst_n)beginif (!rst_n)cnt1 <= 0;elseif (cnt1 < CNT1_MAX - 1)cnt1 <= cnt1 + 1'b1;elsecnt1 <= 0;endassign flag_1ms = (cnt1 == CNT1_MAX - 1) ? 1'b1 : 1'b0;always @ (posedge clk, negedge rst_n)beginif (!rst_n)begincnt2 <= 0;col <= 4'b0000;row_col <= 8'b1111_0000;flag <= 0;state <= S0;endelsecase (state)S0:if (flag_1ms == 0)state <= S0;elseif (row == 4'b1111)state <= S0;elseif (cnt2 < CNT2_MAX - 1)cnt2 <= cnt2 + 1'b1;elsebegincnt2 <= 0;col <= 4'b1110;state <= S1;endS1:if (flag_1ms == 0)state <= S1;elseif (row == 4'b1111)col <= {col[2:0],col[3]};elsebeginrow_col <= {row,col};col <= 4'b0000;flag <= 1;state <= S2;endS2:if (flag_1ms == 0)beginflag <= 0;state <= S2;endelseif (row != 4'b1111)state <= S2;elseif (cnt2 < CNT2_MAX - 1)cnt2 <= cnt2 + 1'b1;elsebegincnt2 <= 0;col <= 4'b0000;state <= S0;endendcaseendalways @ (*)beginif (!rst_n)key_num <= 4'h0;elsecase (row_col)8'b1110_1110:key_num = 4'h0;8'b1110_1101:key_num = 4'h1;8'b1110_1011:key_num = 4'h2;8'b1110_0111:key_num = 4'h3;8'b1101_1110:key_num = 4'h4;8'b1101_1101:key_num = 4'h5;8'b1101_1011:key_num = 4'h6;8'b1101_0111:key_num = 4'h7;8'b1011_1110:key_num = 4'h8;8'b1011_1101:key_num = 4'h9;8'b1011_1011:key_num = 4'ha;8'b1011_0111:key_num = 4'hb;8'b0111_1110:key_num = 4'hc;8'b0111_1101:key_num = 4'hd;8'b0111_1011:key_num = 4'he;8'b0111_0111:key_num = 4'hf;default:key_num = 4'h0;endcaseendendmodule
3、show_data模块
module show_data(inputwireclk,inputwirerst_n,inputwire[3:0]key_num,inputwireflag,outputreg[23:0]show_data);always @ (posedge clk, negedge rst_n)beginif (!rst_n)show_data <= 24'h00_0000;elseif (flag)show_data <= {show_data[19:0],key_num};elseshow_data <= show_data;endendmodule
4、七段数码管seven_tube_drive显示模块
module seven_tube_drive(inputwireclk,inputwirerst_n,inputwire[23:0]show_data,outputreg[7:0]seven_tube_seg,outputreg[2:0]seven_tube_sel);parameterT_1ms=50_000;//parametershow_data = 24'h123456;// developmentboard testlocalparamONE=6'b00_0001;localparamTWO=6'b00_0010;localparamTHREE=6'b00_0100;localparamFOUR=6'b00_1000;localparamFIVE=6'b01_0000;localparamSIX=6'b10_0000;reg[15:0]cnt_1ms;wireflag_1ms;reg[5:0]c_state;reg[5:0]n_state;reg[3:0]temp;always @ (posedge clk, negedge rst_n)beginif (!rst_n)cnt_1ms <= 0;elseif (cnt_1ms < T_1ms - 1)cnt_1ms <= cnt_1ms + 1'b1;elsecnt_1ms <= 0;endassign flag_1ms = (cnt_1ms == T_1ms - 1) ? 1'b1 : 1'b0;always @ (posedge clk, negedge rst_n)beginif (!rst_n)c_state <= ONE;elsec_state <= n_state;endalways @ (*)beginif (!rst_n)n_state <= ONE;elsecase (c_state)ONE:if (flag_1ms)n_state <= TWO;elsen_state <= ONE;TWO:if (flag_1ms)n_state <= THREE;elsen_state <= TWO;THREE:if (flag_1ms)n_state <= FOUR;elsen_state <= THREE;FOUR:if (flag_1ms)n_state <= FIVE;elsen_state <= FOUR;FIVE:if (flag_1ms)n_state <= SIX;elsen_state <= FIVE;SIX:if (flag_1ms)n_state <= ONE;elsen_state <= SIX;default:n_state <= ONE;endcaseendalways @ (posedge clk, negedge rst_n)beginif (!rst_n)seven_tube_sel <= 3'd0;elsecase (c_state)ONE:seven_tube_sel <= 3'b000;TWO:seven_tube_sel <= 3'b001;THREE:seven_tube_sel <= 3'b010;FOUR:seven_tube_sel <= 3'b011;FIVE:seven_tube_sel <= 3'b100;SIX:seven_tube_sel <= 3'b101;default:seven_tube_sel <= 3'b000;endcaseendalways @ (*)begincase (c_state)ONE:temp = show_data[23:20];TWO:temp = show_data[19:16];THREE:temp = show_data[15:12];FOUR:temp = show_data[11:8];FIVE:temp = show_data[7:4];SIX:temp = show_data[3:0];default:temp = 4'd0;endcaseendalways @ (posedge clk, negedge rst_n)beginif (!rst_n)seven_tube_seg <= 8'hff;elsecase (temp)0: seven_tube_seg <= 8'b1100_0000;1: seven_tube_seg <= 8'b1111_1001;2:seven_tube_seg <= 8'b1010_0100;3: seven_tube_seg <= 8'b1011_0000;4: seven_tube_seg <= 8'b1001_1001;5: seven_tube_seg <= 8'b1001_0010;6: seven_tube_seg <= 8'b1000_0010;7: seven_tube_seg <= 8'b1111_1000;8: seven_tube_seg <= 8'b1000_0000;9: seven_tube_seg <= 8'b1001_0000;10:seven_tube_seg <= 8'b1000_1000;11:seven_tube_seg <= 8'b1000_0011;12:seven_tube_seg <= 8'b1100_0110;13:seven_tube_seg <= 8'b1010_0001;14:seven_tube_seg <= 8'b1000_0110;15:seven_tube_seg <= 8'b1000_1110;default:seven_tube_seg <= 8'hff;endcaseendendmodule
5、仿真测试文件
`timescale 1ns/1psmodule keyboard_tb;regclk;regrst_n;reg[3:0]row;reg[4:0]key;wire[3:0]key_num;wire[3:0]col;defparamkeyboard_inst.CNT1_MAX = 10;keyboard keyboard_inst(.clk(clk),.rst_n(rst_n),.row(row),.key_num(key_num),.col(col),.flag(flag));initial clk = 1;always # 10 clk = ~ clk;initialbeginrst_n = 0;key = 5'h10;// MSB=1 stands for no key pressed row = 4'h1111;# 201rst_n = 1;key = 5'h09;#25_000key = 5'h10;#50_000key = 5'h0a;#30_000key = 5'h10;#50_000key = 5'h0b;#35_000key = 5'h10;#50_000$stop;endalways @ (*)begincase (key)5'h10:row = 4'b1111;5'h00:row = {1'b1,1'b1,1'b1,col[0]};5'h01:row = {1'b1,1'b1,1'b1,col[1]};5'h02:row = {1'b1,1'b1,1'b1,col[2]};5'h03:row = {1'b1,1'b1,1'b1,col[3]};5'h04:row = {1'b1,1'b1,col[0],1'b1};5'h05:row = {1'b1,1'b1,col[1],1'b1};5'h06:row = {1'b1,1'b1,col[2],1'b1};5'h07:row = {1'b1,1'b1,col[3],1'b1};5'h08:row = {1'b1,col[0],1'b1,1'b1};5'h09:row = {1'b1,col[1],1'b1,1'b1};5'h0a:row = {1'b1,col[2],1'b1,1'b1};5'h0b:row = {1'b1,col[3],1'b1,1'b1};5'h0c:row = {col[0],1'b1,1'b1,1'b1};5'h0d:row = {col[1],1'b1,1'b1,1'b1};5'h0e:row = {col[2],1'b1,1'b1,1'b1};5'h0f:row = {col[3],1'b1,1'b1,1'b1};default:row = 4'b1111;endcaseendendmodule
三、Modelsim仿真
仿真根据tb文件练习一下,比较简单。
四、个人总结
本人在学习这一块时,很久没明白是怎么进行扫描的,也就是这段没理解。因为起初脑子一根筋认为按键按下了row的值必定不全为1,那不就不用进行col的移位了吗,row是行信号,因此它只可能有一行不是row=4’b1111,其他三行都是,所以存在这个判断过程,自然就有了移位的过程。
S1:if (flag_1ms == 0)state <= S1;elseif (row == 4'b1111)col <= {col[2:0],col[3]};elsebeginrow_col <= {row,col};col <= 4'b0000;flag <= 1;state <= S2;end``