skyxjh 发表于 2013-4-22 00:21:09

4x4键盘扫描消抖4位数码管显示4位流水灯蜂鸣器驱动v代码

刚学FPGA,用Verilog HDL写了一个4x4键盘扫描带消抖,4位数码管动态扫描显示,4位流水灯和蜂鸣器驱动的实验代码,分享给初学FPGA的电工。
自认为代码风格较好,代码思路清析,用REDLOGIC II EP1C6开发板调试通过。希望各位路过的FPGA大牛不吝赐教。

module ledbeepkeypad (
        input wire clk, //时钟50MHz
        input wire rst, //复位信号
        input wire keypadi, //键盘行输入(外接上拉电阻)
        output reg keypado, //键盘列输出扫描(低电平)
        output reg ledd, //数码管数据接口
        output reg ledc, //数码管位控制接口
        output reg beep, //蜂鸣器
        output reg led //4个指示灯
        );
       
reg keypad = 8'h00; //保存键盘扫描码
reg keycode = 5'h10; //当前按键编码,5'h0d表示编号为h0d的按键按下,5'h1x表示没有按键按下或非法按键组合
reg keystate1 = 4'hf; //每列上一次检测到的按键状态,用于消抖
reg keystate2 = 4'hf; //每列上上一次检测到的按键状态,用于消抖
reg keypressdone = 1'b1; //按键已处理标志,用于消抖,0表示按键未处理,1到示按键已处理

reg ledmsk = 5'h0; //数码管驱动位图索引
reg lednum = 16'h1234; //4位数码管显示数据BCD码
reg dotpint = 2'd0; //小数点后有几位数据

reg cnt = 28'd0; //28位记数器,用于时序控制
       
always @ (posedge clk)//记数器向上记数
        cnt = cnt + 1'b1;

always @ (*)//4个指示灯按记数规律亮灭
        led = cnt;
       
always @ (posedge cnt)        begin //4x4键盘扫描,(2^19)*4/50000=42ms扫描一遍
        case (keypado) //列扫描输出低电平
                4'b0111: keypado <= 4'b1011; //非阻塞赋值
                4'b1011: keypado <= 4'b1101;
                4'b1101: keypado <= 4'b1110;
                default: keypado <= 4'b0111;
                endcase
        keypad = keypadi; //行检测
        keypad = keypado; //完整键盘扫描码
        case (keypad) //按键译码
                8'b11101110: keycode = 5'hD; //阻塞赋值
                8'b11101101: keycode = 5'hE;
                8'b11101011: keycode = 5'hF;
                8'b11100111: keycode = 5'h0;
                8'b11011110: keycode = 5'hC;
                8'b11011101: keycode = 5'h3;
                8'b11011011: keycode = 5'h2;
                8'b11010111: keycode = 5'h1;
                8'b10111110: keycode = 5'hB;
                8'b10111101: keycode = 5'h6;
                8'b10111011: keycode = 5'h5;
                8'b10110111: keycode = 5'h4;
                8'b01111110: keycode = 5'hA;
                8'b01111101: keycode = 5'h9;
                8'b01111011: keycode = 5'h8;
                8'b01110111: keycode = 5'h7;
                default: keycode = 5'h10; //没有按键或非法按键组合
                endcase
        if(rst) begin        //按键处理
                case (keypado) //保存每列按键状态
                        default: keystate1 = keycode;
                        4'b1011: keystate1 = keycode;
                        4'b1101: keystate1 = keycode;
                        4'b1110: keystate1 = keycode;
                        endcase
                        keystate2 = keystate1; //保存上一次每列按键状态
                if(keystate1 == keystate2) begin //2次检测按键状态相同
                        if(keystate1 == 4'b1111) begin //按键释放
                                keypressdone = 1'b0; //确认按键释放
                                beep = 1'b0; //蜂鸣器停
                                end
                        else begin //按键按下
                                if(keypressdone) ; //按键已处理
                                else begin //按键未处理则处理当前按键
                                        lednum = lednum << 4; //数码管前3位显示前3次按键值
                                        lednum = keycode; //数码管第4位显示当前按键值
                                        keypressdone = 1'b1; //按键处理完毕
                                        beep = 1'b1; //蜂鸣器响
                                        end
                                end
                        end
                else ; //两次检测按键状态不同
                end
                else ; //复位时不处理按键
        end
       
always @ (negedge cnt) begin //动态扫描4位数码管显示,刷新频率50000000/((2^17)*4)=95Hz
        case (ledc) //数码管位扫描
                default: begin
                        ledc = 4'b1000;
                        ledmsk = lednum;
                        ledd = dotpint == 2'd3 ? 1'b1 : 1'b0; //小数点驱动
                        end
                4'b1000: begin
                        ledc = 4'b0100;
                        ledmsk = lednum;
                        ledd = dotpint == 2'd2 ? 1'b1 : 1'b0; //小数点驱动
                        end
                4'b0100: begin
                        ledc = 4'b0010;
                        ledmsk = lednum;
                        ledd = dotpint == 2'd1 ? 1'b1 : 1'b0; //小数点驱动
                        end
                4'b0010: begin
                        ledc = 4'b0001;
                        ledmsk = lednum;
                        ledd = 1'b0; //末位小数点不显示
                        end
                endcase
        case (ledmsk) //数码管掩码数据输出
                5'h0: ledd = 7'b0111111;
                5'h1: ledd = 7'b0000110;
                5'h2: ledd = 7'b1011011;
                5'h3: ledd = 7'b1001111;
                5'h4: ledd = 7'b1100110;
                5'h5: ledd = 7'b1101101;
                5'h6: ledd = 7'b1111101;
                5'h7: ledd = 7'b0000111;
                5'h8: ledd = 7'b1111111;
                5'h9: ledd = 7'b1101111;
                5'hA: ledd = 7'b1110111;
                5'hB: ledd = 7'b1111100;
                5'hC: ledd = 7'b0111001;
                5'hD: ledd = 7'b1011110;
                5'hE: ledd = 7'b1111001;
                5'hF: ledd = 7'b1110001;
                default: ledd = 7'b0000000;
                endcase
        end
       
endmodule

skyxjh 发表于 2013-4-22 00:23:20

上传附件

skyxjh 发表于 2013-4-22 00:27:36

请教两个问题:
1、怎么设置FPGA端口内部上拉?
2、怎么设置FPGA端口开漏输出?
希望路过的大牛不吝赐教。

skyxjh 发表于 2013-4-28 23:50:31

Altera FPGA管脚弱上拉电阻的软件设置方法
1.打开Assignments Editer;
2.选择I/O features;
3.查找需要上拉的端口;
4.选择需要上拉的端口;
5.选择上拉方式;
6.在value中选择ON,Enable选择YES;
7.保存然后再重新编译,设置完成。在.qsf中可以看到对应的端口已经上拉。
PIN_NAME引脚设置弱上拉:set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to PIN_NAME
PIN_NAME引脚关闭弱上拉:set_instance_assignment -name WEAK_PULL_UP_RESISTOR OFF -to PIN_NAME

skyxjh 发表于 2013-5-2 21:38:57

开漏输出设置与上拉设置方法基本相同,选项为Auto Open-Drain Pins
页: [1]
查看完整版本: 4x4键盘扫描消抖4位数码管显示4位流水灯蜂鸣器驱动v代码