|
我的第一个系统项目:矩阵键盘计算器。项目的目的是利用Zx-1开发板上4×4的矩阵键盘和数码管完成6位数的加减乘除运算并将计算过程和结果显示在数码管上。这个项目大体分为三大模块,分别为矩阵键盘模块、数码管模块和计算模块。其中矩阵键盘模块又分为键盘扫描模块和键盘消抖模块,数码管模块又分为数码管亮灭控制模块、数码管bcd转码模块和数码管显示模块。第一天首先进行矩阵键盘扫描的部分。该模块属于键盘模块,模块名称为keyscan。
单个键盘原理如下:
如上图所示,若按键s没有按下,由于输出端接了上拉电阻pullup,所以输出端为高电平;若给输入端一个低电平并按下按键s,则输出端会变为低电平。矩阵键盘的扫描也是相似的原理:
]
将4×4矩阵键盘分为4行ROW0~ROW3和四列COL0~COL3,将行作为输出,列作为输入。
先给第一列输入低电平,即COL=1110。扫描行是否有低电平,若此时第一行是低电平,即ROW=1110,则表示0号键盘按下;若此时第二行是低电平,即ROW=1101,则表示4号键盘按下;以此类推。
扫描结束后给第二列输入低电平,即COL=1110。行扫描方式同上。
以此类推,这样就可以将16个按键全部扫描完成。根据此原理我们来设计fpga矩阵键盘扫描模块keyboard
由keyboard输出[3:0]col来给矩阵键盘一个列扫描信号,col依次扫过四列,其值应在1110→1101→1011→0111→1110之间循环。为了后续计算模块以及显示模块考虑,此处给col加上一个0000的状态,用于判断是否有按键按下,若此时如果row有一位是低电平则说明有按键按下,否则说明按键抬起或者没有按键按下。
将矩阵键盘反馈回的row信号同col信号合并成一个scancode,则每个scancode就会对应一个按键,我们将按键值定义为key_num,key_num需要表示16个按键,应为4位,为便于后续模块使用,给key_num最高位加上以为判断位,若最高位是1则说明有按键按下,若最高位是0则说明按键抬起或没有按键按下。
这部分主要代码如下:
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n)
col_r <= 4'b0000;
else
begin
case(col_r)
4'b0000:col_r <= 4'b1110;
4'b1110:col_r <= 4'b1101;
4'b1101:col_r <= 4'b1011;
4'b1011:col_r <= 4'b0111;
4'b0111:col_r <= 4'b0000;
default:col_r <= 4'b0000;
endcase
end
end
assign col = col_r;
assign scancode = {col_r[3:0],row[3:0]};
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n)
key_num <= 5'b0_0000;
else
case(scancode)
/********** 检测是否有按键按下,无则key_num第一位为0,有则为1**********/
8'b0000_1111: key_num <= {1'b0,key_num[3:0]};
8'b0000_1110: key_num <= {1'b1,key_num[3:0]};
8'b0000_1101: key_num <= {1'b1,key_num[3:0]};
8'b0000_1011: key_num <= {1'b1,key_num[3:0]};
8'b0000_0111: key_num <= {1'b1,key_num[3:0]};
/**********扫描第一行按键*******/
8'b1110_1110: key_num <= 5'b1_0000;//0
8'b1101_1110: key_num <= 5'b1_0001;//1
8'b1011_1110: key_num <= 5'b1_0010;//2
8'b0111_1110: key_num <= 5'b1_0011;//3
/*********扫描第二行按键********/
8'b1110_1101: key_num <= 5'b1_0100;//4
8'b1101_1101: key_num <= 5'b1_0101;//5
8'b1011_1101: key_num <= 5'b1_0110;//6
8'b0111_1101: key_num <= 5'b1_0111;//7
/*********扫描第三行按键********/
8'b1110_1011: key_num <= 5'b1_1000;//8
8'b1101_1011: key_num <= 5'b1_1001;//9
8'b1011_1011: key_num <= 5'b1_1010;//a
8'b0111_1011: key_num <= 5'b1_1011;//b
/*********扫描第四行按键********/
8'b1110_0111: key_num <= 5'b1_1100;//c
8'b1101_0111: key_num <= 5'b1_1101;//d
8'b1011_0111: key_num <= 5'b1_1110;//e
8'b0111_0111: key_num <= 5'b1_1111;//f
default: key_num <=key_num;
endcase
end
绿色的default语句需注意,刚开始误写成了key_num <=5’b00000导致错误,因col是随时钟一直在1110→1101→1011→0111→0000→1110之间循环,所以当有一个按键按下过程中,scancode仍是不断变化的,例如按下了0号按键,则按键扫描模块会在col为1110时接收到row为1110的信号;下一刻会在col=0111,接收到row=1111的信号;再下一刻col=1011,row=1111;再下一刻col=1101,row=1111;再下一刻col=1110,row=1110,不断循环直至按键抬起,由以上容易看出scancode一直在11101110→01111111→10111111→11011111→11101110循环变化,红色的scancode超出了扫描定义范围,若将default的情况定义为default: key_num <=5’b00000,则按下一个按键会出现多个key_num,此处应定义为default: key_num <=key_num,即可实现按下一个按键只输出一个key_num。 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?我要注册
x
|