skyxjh 发表于 2013-5-2 21:20:45

4x4键盘翻转扫描带消抖,4位数码管显示Verilog实现

本帖最后由 skyxjh 于 2013-5-2 21:32 编辑

刚学FPGA,基于红色飓风II开发板实现了4x4键盘翻转法扫描带消抖,4位数码管动态扫描显示,模块化设计,方便移植。

module ledbeepkeypad ( //顶层模块
        input clk, //时钟50MHz
        input rst, //复位信号,低电平复位
        inout keypad, //4x4键盘行线列线
        output ledd, //数码管段码数据接口
        output ledc, //数码管位控制接口
        output beep, //蜂鸣器,高电平响,低电平停
        );
       
        reg keycode = 5'h10; //当前按键编码,5'h0d表示编号为h0d的按键按下,5'h1x表示没有按键按下或非法按键组合
        reg lednum = 16'h1234; //4位数码管显示数据BCD码
        reg dotpoint = 4'b0000; //小数点,1表示对应位小数点显示
        reg cnt = 19'd0; //19位记数器,用于时序控制
       
        always @ (posedge clk)         cnt = cnt + 1'b1; //记数器向上记数
        keypad4x4scan keypad1(.clk(cnt),.keyp(keypad),.keycode(keycode));//4x4键盘扫描,时钟50M/(2^19)=95Hz
        keylogic keylogic1(.rst(rst),.clk(cnt),.keycode(keycode),.beep(beep),.lednum(lednum),.dotpoint(dotpoint)); //按键处理逻辑
        led4disp disp1(.clk(cnt),.lednum(lednum),.dotpoint(dotpoint),.ledd(ledd),.ledc(ledc));        //4位数码管显示,时钟50M/(2^17)=381Hz
       
endmodule

//////////////////////////////////////////////////////////////////
//keyp7        7                8                9                A      //
//keyp6        4                5                6                B      //
//keyp5        1                2                3                C      //
//keyp4        0                F                E                D      //
//                keyp3        keyp2        keyp1        keyp0    //
/////////////////////////////////////////////////////////////////
module keypad4x4scan ( //键盘扫描子模块
        input wire clk, //时钟>50Hz
        inout wire keyp, //键盘行(外接上拉电阻)列输入
        output reg keycode //按键编码
        );
       
        reg keyc = 8'd0; //按键编码
        reg swap = 1'b0; //行列翻转扫描标志
       
        assign keyp = swap ? 8'h0z : 8'hz0; //swap为1时keyp输出低电平,keyp上拉输入;
                                                                                                        //swap为0时keyp上拉输入,keyp输出低电平;
        always @ (posedge clk)        begin
                case (swap)
                        1'b1: keyc = keyp; //swap为1时读取keyp电平值
                        default: keyc = keyp; //swap为0时读取keyp电平值
                endcase
                swap <= ~swap; //行列翻转
        end
       
        always @ (keyc) begin
                case (keyc) //按键编码
                        8'b1110_0111: keycode = 5'h0;
                        8'b1101_0111: keycode = 5'h1;
                        8'b1011_0111: keycode = 5'h4;
                        8'b0111_0111: keycode = 5'h7;
                        8'b1110_1011: keycode = 5'hf;
                        8'b1101_1011: keycode = 5'h2;
                        8'b1011_1011: keycode = 5'h5;
                        8'b0111_1011: keycode = 5'h8;
                        8'b1110_1101: keycode = 5'he;
                        8'b1101_1101: keycode = 5'h3;
                        8'b1011_1101: keycode = 5'h6;
                        8'b0111_1101: keycode = 5'h9;
                        8'b1110_1110: keycode = 5'hd;
                        8'b1101_1110: keycode = 5'hc;
                        8'b1011_1110: keycode = 5'hb;
                        8'b0111_1110: keycode = 5'ha;
                        default: keycode = 5'h10; //无按键按下或无效按键组合
                endcase
        end
       
endmodule


module keylogic( //按键处理子模块
        input wire rst, //低电平复位信号
        input wire clk, //时钟>50Hz
        input wire keycode, //按键编码,5'h1x表示无按键按下或无效按键组合,5'h0x表示编号为x的按键按下
        output reg lednum, //4位数码管显示数据
        output reg dotpoint, //小数点驱动,1表示对应位显示小数点
        output reg beep //蜂鸣器驱动
        );
       
        reg keypressdone = 1'b1; //按键已处理标志,1表示已处理,0表示未处理
        reg keycode1 = 5'h10; //上一次检测到的按键值,用于消抖
       
        always @ (posedge clk) begin
                if(rst) begin
                        if (keycode1 == keycode) begin //连续2次检测到的按键值相同,稳定
                                if (keycode) begin //确认按键弹起
                                        beep = 1'b0; //按键弹起时蜂鸣器停
                                        keypressdone = 1'b1; //置按键已处理标志,准备处理下一次按键
                                        end
                                else if (keypressdone) begin //有按键按下,按键处理,只处理1次
                                        lednum = lednum << 4; //数码管前3位显示前3次按键值
                                        lednum = keycode1; //数码管第4位显示当前按键值
                                        dotpoint = 4'b0100; //小数点后2位数字
                                        beep = 1'b1; //按键按下时蜂鸣器响
                                        keypressdone = 1'b0; //按键未弹起之前按键已处理标志清零
                                        end
                                else ; //按键已处理
                                end
                        else ; //连续2次检测到的按键值不同(抖动)
                        end
                else begin //复位
                        lednum = 16'd0; //显示清零
                        dotpoint = 2'd0; //不显示小数点
                        beep = 1'b0; //蜂鸣器停
                        end
                keycode1 = keycode; //保存当前按键值
        end
               
endmodule


module led4disp(//4位数码管显示子模块
        input wire clk, //时钟>240Hz
        input wire lednum, //4数码管显示数字BCD码
        input wire dotpoint, //小数点,1表示对应位小数点显示
        output reg ledd, //数码管段码数据接口
        output reg ledc //数码管位选接口
        );
       
        reg ledmsk = 5'h0; //数码管驱动位图索引
        reg ledbit = 2'd0; //数码管位扫描
       
        always @ (posedge clk) begin //动态扫描4位数码管显示,刷新频率>60Hz
                ledbit = ledbit + 1'b1; //位扫描
        end
       
        always @ (ledbit,lednum) begin
                case (ledbit) //数码管当前位段码
                        2'd3:         ledmsk = lednum; //第1位
                        2'd2:         ledmsk = lednum; //第2位
                        2'd1:         ledmsk = lednum; //第3位
                        default: ledmsk = lednum; //第4位
                endcase
        end
       
        assign ledc = 4'b1 << ledbit; //数码管位选择,阴极加NPN三极管反向控制,高电平位选
        assign ledd = (dotpoint & ledc) ? 1'b1 : 1'b0; //小数点驱动
       
        always @ (ledmsk) begin
                case (ledmsk) //共阴极数码管段码数据输出
                        5'h0: ledd = 7'b011_1111; //显示数字0
                        5'h1: ledd = 7'b000_0110; //显示数字1
                        5'h2: ledd = 7'b101_1011; //显示数字2
                        5'h3: ledd = 7'b100_1111; //显示数字3
                        5'h4: ledd = 7'b110_0110; //显示数字4
                        5'h5: ledd = 7'b110_1101; //显示数字5
                        5'h6: ledd = 7'b111_1101; //显示数字6
                        5'h7: ledd = 7'b000_0111; //显示数字7
                        5'h8: ledd = 7'b111_1111; //显示数字8
                        5'h9: ledd = 7'b110_1111; //显示数字9
                        5'hA: ledd = 7'b111_0111; //显示数字A
                        5'hB: ledd = 7'b111_1100; //显示数字B
                        5'hC: ledd = 7'b011_1001; //显示数字C
                        5'hD: ledd = 7'b101_1110; //显示数字D
                        5'hE: ledd = 7'b111_1001; //显示数字E
                        5'hF: ledd = 7'b111_0001; //显示数字F
                        default: ledd = 7'b000_0000; //不显示
                endcase
        end
               
endmodule

编辑说明:排版调整

skyxjh 发表于 2013-5-2 21:27:41

FPAG引脚内部上拉设置方法:

skyxjh 发表于 2013-5-2 22:12:42

上传工程文件:
页: [1]
查看完整版本: 4x4键盘翻转扫描带消抖,4位数码管显示Verilog实现