搜索
bottom↓
回复: 4

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

[复制链接]

出0入0汤圆

发表于 2013-4-22 00:21:09 | 显示全部楼层 |阅读模式
刚学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, 杜汶泽)

出0入0汤圆

 楼主| 发表于 2013-4-22 00:23:20 | 显示全部楼层
上传附件

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-4-22 00:27:36 | 显示全部楼层
请教两个问题:
1、怎么设置FPGA端口内部上拉?
2、怎么设置FPGA端口开漏输出?
希望路过的大牛不吝赐教。

出0入0汤圆

 楼主| 发表于 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

出0入0汤圆

 楼主| 发表于 2013-5-2 21:38:57 | 显示全部楼层
开漏输出设置与上拉设置方法基本相同,选项为Auto Open-Drain Pins
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-7-24 07:17

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表