|
刚学FPGA,用Verilog HDL写了一个4x4键盘扫描带消抖,4位数码管动态扫描显示,4位流水灯和蜂鸣器驱动的实验代码,分享给初学FPGA的电工。
自认为代码风格较好,代码思路清析,用REDLOGIC II EP1C6开发板调试通过。希望各位路过的FPGA大牛不吝赐教。
module ledbeepkeypad (
input wire clk, //时钟50MHz
input wire rst, //复位信号
input wire [3:0] keypadi, //键盘行输入(外接上拉电阻)
output reg [3:0] keypado, //键盘列输出扫描(低电平)
output reg [7:0] ledd, //数码管数据接口
output reg [3:0] ledc, //数码管位控制接口
output reg beep, //蜂鸣器
output reg [3:0] led //4个指示灯
);
reg [7:0] keypad = 8'h00; //保存键盘扫描码
reg [4:0] keycode = 5'h10; //当前按键编码,5'h0d表示编号为h0d的按键按下,5'h1x表示没有按键按下或非法按键组合
reg [3:0] keystate1 = 4'hf; //每列上一次检测到的按键状态,用于消抖
reg [3:0] keystate2 = 4'hf; //每列上上一次检测到的按键状态,用于消抖
reg keypressdone = 1'b1; //按键已处理标志,用于消抖,0表示按键未处理,1到示按键已处理
reg [4:0] ledmsk = 5'h0; //数码管驱动位图索引
reg [15:0] lednum = 16'h1234; //4位数码管显示数据BCD码
reg [1:0] dotpint = 2'd0; //小数点后有几位数据
reg [27:0] cnt = 28'd0; //28位记数器,用于时序控制
always @ (posedge clk) //记数器向上记数
cnt = cnt + 1'b1;
always @ (*) //4个指示灯按记数规律亮灭
led[3:0] = cnt[27:24];
always @ (posedge cnt[18]) begin //4x4键盘扫描,(2^19)*4/50000=42ms扫描一遍
case (keypado[3:0]) //列扫描输出低电平
4'b0111: keypado[3:0] <= 4'b1011; //非阻塞赋值
4'b1011: keypado[3:0] <= 4'b1101;
4'b1101: keypado[3:0] <= 4'b1110;
default: keypado[3:0] <= 4'b0111;
endcase
keypad[7:4] = keypadi[3:0]; //行检测
keypad[3:0] = keypado[3:0]; //完整键盘扫描码
case (keypad[7:0]) //按键译码
8'b11101110: keycode[4:0] = 5'hD; //阻塞赋值
8'b11101101: keycode[4:0] = 5'hE;
8'b11101011: keycode[4:0] = 5'hF;
8'b11100111: keycode[4:0] = 5'h0;
8'b11011110: keycode[4:0] = 5'hC;
8'b11011101: keycode[4:0] = 5'h3;
8'b11011011: keycode[4:0] = 5'h2;
8'b11010111: keycode[4:0] = 5'h1;
8'b10111110: keycode[4:0] = 5'hB;
8'b10111101: keycode[4:0] = 5'h6;
8'b10111011: keycode[4:0] = 5'h5;
8'b10110111: keycode[4:0] = 5'h4;
8'b01111110: keycode[4:0] = 5'hA;
8'b01111101: keycode[4:0] = 5'h9;
8'b01111011: keycode[4:0] = 5'h8;
8'b01110111: keycode[4:0] = 5'h7;
default: keycode[4:0] = 5'h10; //没有按键或非法按键组合
endcase
if(rst) begin //按键处理
case (keypado) //保存每列按键状态
default: keystate1[3] = keycode[4];
4'b1011: keystate1[2] = keycode[4];
4'b1101: keystate1[1] = keycode[4];
4'b1110: keystate1[0] = keycode[4];
endcase
keystate2[3:0] = keystate1[3:0]; //保存上一次每列按键状态
if(keystate1[3:0] == keystate2[3:0]) begin //2次检测按键状态相同
if(keystate1[3:0] == 4'b1111) begin //按键释放
keypressdone = 1'b0; //确认按键释放
beep = 1'b0; //蜂鸣器停
end
else begin //按键按下
if(keypressdone) ; //按键已处理
else begin //按键未处理则处理当前按键
lednum[15:0] = lednum[15:0] << 4; //数码管前3位显示前3次按键值
lednum[3:0] = keycode[3:0]; //数码管第4位显示当前按键值
keypressdone = 1'b1; //按键处理完毕
beep = 1'b1; //蜂鸣器响
end
end
end
else ; //两次检测按键状态不同
end
else ; //复位时不处理按键
end
always @ (negedge cnt[16]) begin //动态扫描4位数码管显示,刷新频率50000000/((2^17)*4)=95Hz
case (ledc) //数码管位扫描
default: begin
ledc = 4'b1000;
ledmsk[3:0] = lednum[15:12];
ledd[7] = dotpint == 2'd3 ? 1'b1 : 1'b0; //小数点驱动
end
4'b1000: begin
ledc = 4'b0100;
ledmsk[3:0] = lednum[11:8];
ledd[7] = dotpint == 2'd2 ? 1'b1 : 1'b0; //小数点驱动
end
4'b0100: begin
ledc = 4'b0010;
ledmsk[3:0] = lednum[7:4];
ledd[7] = dotpint == 2'd1 ? 1'b1 : 1'b0; //小数点驱动
end
4'b0010: begin
ledc = 4'b0001;
ledmsk[3:0] = lednum[3:0];
ledd[7] = 1'b0; //末位小数点不显示
end
endcase
case (ledmsk) //数码管掩码数据输出
5'h0: ledd[6:0] = 7'b0111111;
5'h1: ledd[6:0] = 7'b0000110;
5'h2: ledd[6:0] = 7'b1011011;
5'h3: ledd[6:0] = 7'b1001111;
5'h4: ledd[6:0] = 7'b1100110;
5'h5: ledd[6:0] = 7'b1101101;
5'h6: ledd[6:0] = 7'b1111101;
5'h7: ledd[6:0] = 7'b0000111;
5'h8: ledd[6:0] = 7'b1111111;
5'h9: ledd[6:0] = 7'b1101111;
5'hA: ledd[6:0] = 7'b1110111;
5'hB: ledd[6:0] = 7'b1111100;
5'hC: ledd[6:0] = 7'b0111001;
5'hD: ledd[6:0] = 7'b1011110;
5'hE: ledd[6:0] = 7'b1111001;
5'hF: ledd[6:0] = 7'b1110001;
default: ledd[6:0] = 7'b0000000;
endcase
end
endmodule
|
阿莫论坛20周年了!感谢大家的支持与爱护!!
知道什么是神吗?其实神本来也是人,只不过神做了人做不到的事情 所以才成了神。 (头文字D, 杜汶泽)
|