您好,欢迎访问三七文档
当前位置:首页 > 电子/通信 > 数据通信与网络 > 经典的verilog键盘扫描程序
经典的verilog键盘扫描程序作者:ilove314拿到威百仕(VibesIC)的板子后就迫不及待的开始我的学习计划,从最基础的分频程序开始,但看到这个键盘扫描程序后,直呼经典,有相见恨晚的感觉,还想说一句:威百仕(VibesIC),我很看好你!WHY?待我慢慢道来,这个程序的综合后是0error,0warning。想想自己编码的时候那个warning是满天飞,现在才明白HDL设计有那么讲究了,代码所设计的不仅仅是简单的逻辑以及时序的关系,更重要的是你要在代码中要表现出每一个寄存器,甚至每一个走线。想想我写过的代码,只注意到了前者,从没有注意过后者,还洋洋自得以为自己也算是个高手了,现在想来,实在惭愧啊!学习学习在学习,这也重新激发了我对HDL设计的激情,威百仕给了我一个方向,那我可要开始努力喽!废话说了一大堆,看程序吧:(本代码经过ise7.1i综合并下载到SP306板上验证通过)//当三个独立按键的某一个被按下后,相应的LED被点亮;再次按下后,LED熄灭,按键控制LED亮灭modulekey_debounce(clk,rst_n,s1_n,s2_n,s3_n,s4_n,s5_n,led_d1,led_d2,led_d3,led_d4,led_d5);inputclk;//主时钟信号,10MHzinputrst_n;//复位信号,低有效inputs1_n,s2_n,s3_n,s4_n,s5_n;outputled_d1,led_d2,led_d3,led_d4,led_d5;reg[4:0]s_rst;always@(posedgeclkornegedgerst_n)if(!rst_n)s_rst=5'b11111;elses_rst={s5_n,s4_n,s3_n,s2_n,s1_n};reg[4:0]s_rst_r;always@(posedgeclkornegedgerst_n)if(!rst_n)s_rst_r=5'b11111;elses_rst_r=s_rst;wire[4:0]s_an=s_rst_r&(~s_rst);reg[19:0]cnt;//计数寄存器always@(posedgeclkornegedgerst_n)if(!rst_n)cnt=20'd0;//异步复位elseif(s_an)cnt=20'd0;elsecnt=cnt+1'b1;reg[4:0]low_s;always@(posedgeclkornegedgerst_n)if(!rst_n)low_s=5'b11111;elseif(cnt==20'h30D40)low_s={s5_n,s4_n,s3_n,s2_n,s1_n};reg[4:0]low_s_r;always@(posedgeclkornegedgerst_n)if(!rst_n)low_s_r=5'b11111;elselow_s_r=low_s;wire[4:0]led_ctrl=low_s_r[4:0]&(~low_s[4:0]);regd1,d2,d3,d4,d5;always@(posedgeclkornegedgerst_n)if(!rst_n)begind1=1'b0;d2=1'b0;d3=1'b0;d4=1'b0;d5=1'b0;endelsebegin//if(led_ctrl[0])d1=~d1;if(led_ctrl[1])d2=~d2;if(led_ctrl[2])d3=~d3;if(led_ctrl[3])d4=~d4;if(led_ctrl[4])d5=~d5;endassignled_d1=d1?1'b1:1'b0;//LED翻转输出assignled_d2=d2?1'b1:1'b0;assignled_d3=d3?1'b1:1'b0;assignled_d4=d4?1'b1:1'b0;assignled_d5=d5?1'b1:1'b0;endmodule也许初看起来这段代码似乎有点吃力,好多的always好多的wire啊,而我们通常用得最多的判断转移好像不是主流。的确是这样,一个好的verilog代码,用多个always语句来分摊一个大的always来执行,会使得综合起来更快,这也是接前两篇日志说到代码优化的一个值得学习的方面。其次是wire连线很多,你要是仔细研究代码,不难发现所有的锁存器的连线关系编程者都考虑到了,这样就不会平白无故的生成意想不到的寄存器了,这也是一个优秀代码的必备要素。上面说的是代码风格,下面就看程序的编程思想吧。前两个always语句里其实是做了一个20ms的计数,每隔20ms就会读取键值,把这个键值放到寄存器low_sw中,接下来的一个always语句就是把low_sw的值锁存到low_sw_r里,这样以来,low_sw和low_sw_r就是前后两个时钟周期里的键值了,为什么要这样呢?看下一个语句吧:wire[2:0]led_ctrl=low_sw_r[2:0]&(~low_sw[2:0]);仔细分析,你会发现当没有键按下时,low_sw=low_sw_r=3’b111,此时的led_ctrl=3’b000;只有当low_sw和low_sw_r的某一位分别为0和1时,才可能使led_ctrl的值改变(也就是把led_ctrl的某一位拉高)。那么这意味着当键值由1跳变到0时才可能把led_ctrl拉高。回顾前面的20ms赋键值,也就是说每20ms内如果出现按键被按下,那么有一个时钟周期里led_ctrl是会被拉高的,而再看后面的程序,led_ctrl的置高就使得相应的LED灯的亮灭做一次改变,这就达到了目的。verilog键盘扫描程序之debug作者:ilove314:EDNChina上次的日志《经典的verilog键盘扫描程序》承蒙厚爱,已成博客精华,在EDN博客主页置顶多日。但是我发现那个经典程序还是存在一点点小bug,且听我慢慢道来。先放上仿真波形来说明一下问题吧:仿真说明:由于20ms检测一次按键值对于仿真来说太长了,所以只假定16个主时钟周期就做一次检测(也就是cnt[3]的下降沿锁存键值)。图1,sw1_n被按下(拉底)大约5个时钟周期(16),而此时与其相应的led_d5却改变状态了。说明的问题是,大多数时候按键消抖其实是到不了20ms的。其实这个小bug通常在下载后,测试键盘是不会有什么感觉的。但是问题是,如果真的出现那种抖动在20ms以内(甚至远小于20ms)的外部干扰存在时,这个bug就不可忽视了。因此,在原程序的基础上,做了如下的改进。其思想是在每个主时钟(50MHz)周期里都进行一次按键检测,如果前后两次键值改变了,说明有可能键盘被按下了,此时,在下一个时钟周期将复位20ms计数值,然后20ms后重新锁存键值,其它的和原程序基本相同,这样就达到了真正意义上的20ms消抖。重新修改代码后的仿真波形如下:点击看原图图2,可以看到此时在不满16个时钟周期的键值变化是不会然led做出变化的。图3,按键sw3_n的按下时间明显超过了16个时钟周期,那么在cnt重新记到16个时钟周期后,led_d4就做出了改变。重新修改后的代码如下://当三个独立按键的某一个被按下后,相应的LED被点亮;再次按下后,LED熄灭,按键控制LED亮灭key_led.vmodulekey_led(inputCLOCK_50,inputQ_KEY,input[4:1]KEY,outputreg[4:1]LED);//++++++++++++++++++++++++++++++++++++++//获取键值开始//++++++++++++++++++++++++++++++++++++++wire[4:1]key_val;//键值key_debounceu0(.i_clk(CLOCK_50),.i_rst_n(Q_KEY),.i_key(KEY),.o_key_val(key_val)//按下为0,松开为1);//--------------------------------------//获取键值结束//--------------------------------------//++++++++++++++++++++++++++++++++++++++//按下键后开关LED开始//++++++++++++++++++++++++++++++++++++++always@(posedgeCLOCK_50,negedgeQ_KEY)if(!Q_KEY)LED=4'hF;//0灭1亮elsecase(1'b0)key_val[1]:LED[1]=~LED[1];key_val[2]:LED[2]=~LED[2];key_val[3]:LED[3]=~LED[3];key_val[4]:LED[4]=~LED[4];default:LED=LED;//缺省亮灭情况不变endcase//--------------------------------------//按下键后开关LED结束//--------------------------------------endmodulekey_debounce.vmodulekey_debounce(inputi_clk,inputi_rst_n,input[4:1]i_key,//按下为0,松开为1outputreg[4:1]o_key_val//键值);//++++++++++++++++++++++++++++++++++++++reg[4:1]key_samp1,key_samp1_locked;//将i_key采集至key_samp1always@(posedgei_clk,negedgei_rst_n)if(!i_rst_n)key_samp1=4'hF;elsekey_samp1=i_key;//将key_samp1锁存至key_samp1_lockedalways@(posedgei_clk,negedgei_rst_n)if(!i_rst_n)key_samp1_locked=4'hF;elsekey_samp1_locked=key_samp1;//++++++++++++++++++++++++++++++++++++++wire[4:1]key_changed1;//当key_samp1由1变为0时//key_changed1由0变为1,只维持一个时钟周期assignkey_changed1=key_samp1_locked&(~key_samp1);//++++++++++++++++++++++++++++++++++++++reg[19:0]cnt;//一旦有按键按下,cnt立即被清零always@(posedgei_clk,negedgei_rst_n)if(!i_rst_n)cnt=20'h0;elseif(key_changed1)cnt=20'h0;elsecnt=cnt+1'b1;//++++++++++++++++++++++++++++++++++++++reg[4:1]key_samp2,key_samp2_locked;//只有当按键不变化(不抖动),且维持20ms以上时//才将i_key采集至key_samp2always@(posedgei_clk,negedgei_rst_n)if(!i_rst_n)key_samp2=4'hF;elseif(cnt==20'hF_FFFF)//0xFFFFF/50M=20.9715mskey_samp2=i_key;//将key_samp2锁存至key_samp2_lockedalways@(posedgei_clk,negedgei_rst_n)if(!i_rst_n)key_samp2_locked=4'hF;elsekey_samp2_locked=key_samp2;//++++++++++++
本文标题:经典的verilog键盘扫描程序
链接地址:https://www.777doc.com/doc-1567767 .html