明德扬吴老师 发表于 2020-7-15 21:50:06

【每周FPGA案例】至简设计系列_按键控制数字时钟 编号:001600000062

本案例的编号为:001600000062,如果有疑问,请按编号在下面贴子查找答案:MDY案例交流【汇总贴】_FPGA-明德扬科教 (mdy-edu.com)
【上板现象】

按键控制数字时钟在MP801的上板现象
https://www.bilibili.com/video/BV1Af4y117H4?p=4

按键控制数字时钟在点拨开发板的上板现象

https://www.bilibili.com/video/BV1Af4y117H4?p=5

按键控制数字时钟在实验箱的上板现象

https://www.bilibili.com/video/BV1Af4y117H4?p=6

【设计教程】

至简设计系列_按键控制数字时钟
--作者:小黑同学
本文为明德扬原创及录用文章,转载请注明出处!1.1 总体设计1.1.1 概述数字时钟是采用数字电路技术实现时、分、秒计时显示的装置,可以用数字同时显示时,分,秒的精确时间并实现准确校时,具备体积小、重量轻、抗干扰能力强、对环境要求高、高精确性、容易开发等特性,在工业控制系统、智能化仪器表、办公自动化系统等诸多领域取得了极为广泛的应用,诸如自动报警、按时自动打铃、时间程序自动控制、定时广播、自定启闭路灯、定时开关烘箱、通断动力设备、甚至各种定时电器的自动启用等。与传统表盘式机械时钟相比,数字时钟具有更高的准确性和直观性,由于没有机械装置,其使用寿命更长。
1.1.2 设计目标本设计要求实现可设置的数字时钟(速度快10倍,每过0.1s,秒数加1),具体要求如下:1、按下按键key1,时钟暂停,跳到设置时间状态,在按按键key1,回到正常状态。2、通过按键key2,选择要设置的位置,初始时设置秒低位,按一下,设置秒高位,再按下,设置分低位,依次类推,循环设置。3、通过按键key3,设置数值,按一下数值加1,如果溢出,则重新变为0。4、通过数码管将时间实时显示出来。5、如果开发板上的按键是矩阵键盘,那么要产生需要的按键信号,需要通过例化矩阵键盘模块来产生。1.1.3 系统结构框图系统结构框图如下所示:结构图共分两个,如果使用的开发板上是普通按键的时候,对应的结构图是图一。如果使用的开发板上是矩阵键盘的时候,对应的结构图是图二。
图一

图二1.1.4模块功能Ø按键检测模块实现功能将外来异步信号打两拍处理,将异步信号同步化;实现20ms按键消抖功能,并输出有效按键信号。Ø矩阵键盘模块实现功能将外来异步信号打两拍处理,将异步信号同步化;实现20ms按键消抖功能;实现矩阵键盘的按键检测功能,并输出有效按键信号。Ø时间产生模块实现功能产生时间数据;根据接收到的不同的按键信号,产生暂停、开启、设置时间的功能。Ø数码管显示模块实现功能对接收到的时间数据进行译码。1.1.5顶层信号

信号名接口方向定义
clk输入系统时钟,50Mhz
rst_n输入低电平复位信号
Key输入3位按键信号,开发板按键为矩阵键盘时,不需要该信号
Key_col输入4位矩阵键盘列信号,默认高电平,开发板按键为普通按键时,不需要该信号
Key_row输出4位矩阵键盘行信号,默认低电平,开发板按键为普通按键时,不需要该信号
Segment输出8位数码管段选信号
Seg_sel输出6位数码管位选信号
1.1.6参考代码下面是使用普通按键的顶层代码:1.modulekey_clock(2.      clk    ,3.      rst_n,4.      key    ,5.      segment,6.      seg_sel7.);8.    9.parameter   COUNT_TIME      =   23'd500_0000;10. parameter   DELAY_TIME      =   10000       ;11. parameter   SEG_WID         =   8         ;12. parameter   SEG_SEL         =   6         ;13.   14. parameter   KEY_S         =   4         ;15. parameter   KEY_W         =   3         ;16.   17. input                   clk         ;18. input                   rst_n       ;19. input   [ 2:0]          key         ;20. output[ 7:0]          segment   ;21. output[ 6:0]          seg_sel   ;22.   23. wire    [ 2:0]          key_vld   ;24. wire              segment_data;25. wire    [ 3:0]          cnt2      ;26. wire    [ 3:0]          cnt3      ;27. wire    [ 3:0]          cnt4      ;28. wire    [ 3:0]          cnt5      ;29. wire    [ 3:0]          cnt6      ;30. wire    [ 3:0]          cnt7      ;31.   32.   33.            key_moduleuut0(34.               .clk   (clk    ),35.               .rst_n   (rst_n),36.               .key_in(key    ),37.               .key_vld (key_vld)38.            );39.   40.   41.            time_datauut1(42.               .clk      (clk    ),   43.               .rst_n    (rst_n),   44.               .key_vld(key_vld),   45.               .cnt2   (cnt2   ),   46.               .cnt3   (cnt3   ),   47.               .cnt4   (cnt4   ),   48.               .cnt5   (cnt5   ),   49.               .cnt6   (cnt6   ),   50.               .cnt7   (cnt7   )   51.   52.            );53.   54.   55.            seg_dispuut2(56.                  .clk          (clk                        ),   57.                  .rst_n      (rst_n                        ),   58.                  .segment      (segment                      ),   59.                  .seg_sel      (seg_sel                      ),60.                  .segment_data ({cnt7,cnt6,cnt5,cnt4,cnt3,cnt2})   61.   62.            );63.   64.   65. endmodule
下面是使用矩阵键盘的顶层代码:66. modulekey_clock_jvzhen(67.   clk    ,68.   rst_n,69.   key_col,70.   key_row,71.   segment,72.   seg_sel73. );74.   75. parameter   COUNT_TIME      =   23'd500_0000;76. parameter   DELAY_TIME      =   10000       ;77. parameter   SEG_WID         =   8         ;78. parameter   SEG_SEL         =   6         ;79.   80. parameter   KEY_S         =   4         ;81. parameter   KEY_W         =   3         ;82.   83. input                   clk         ;84. input                   rst_n       ;85. input   [ 3:0]          key_col   ;86. output[ 3:0]          key_row   ;87. output[ 7:0]          segment   ;88. output[ 6:0]          seg_sel   ;89.   90. wire    [ 3:0]          key_vld   ;91. wire    [ 3:0]          cnt2      ;92. wire    [ 3:0]          cnt3      ;93. wire    [ 3:0]          cnt4      ;94. wire    [ 3:0]          cnt5      ;95. wire    [ 3:0]          cnt6      ;96. wire    [ 3:0]          cnt7      ;97.   98.   99.            key_scanuut0(100.               .clk   (clk    ),101.               .rst_n   (rst_n),102.               .key_col (key_col),103.               .key_row (key_row),104.               .key_en(key_vld)105.            );106.   107.   108.            time_datauut1(109.               .clk      (clk    ),   110.               .rst_n    (rst_n),   111.               .key_vld(key_vld),   112.               .cnt2   (cnt2   ),   113.               .cnt3   (cnt3   ),   114.               .cnt4   (cnt4   ),   115.               .cnt5   (cnt5   ),   116.               .cnt6   (cnt6   ),   117.               .cnt7   (cnt7   )   118.   119.            );120.   121.   122.            seg_dispuut2(123.                  .clk          (clk                            ),   124.                  .rst_n      (rst_n                        ),   125.                  .segment      (segment                        ),   126.                  .seg_sel      (seg_sel                        ),127.                  .segment_data ({cnt7,cnt6,cnt5,cnt4,cnt3,cnt2})128.            );129.   130.   131. endmodule

1.2 按键检测模块设计1.2.1 接口信号

信号接口方向定义
clk输入系统时钟
rst_n输入低电平复位信号
key_in输入按键输入
key_vld输出按键按下指示信号

1.2.2 设计思路Ø
硬件电路

独立式按键工作原理如上图所示,4条输入线连接到FPGA的IO口上,当按键S1按下时,3.3V的电源通过电阻R53再通过按键S1最终进入GND形成一条通路,这条线路的全部电压都加在R53上,则KS0是低电平。当松开按键后,线路断开,就不会有电流通过,KS0应该是3.3V,为高电平。我们可以通过KS0这个IO口的高低电平状态来判断是否有按键按下。其他按键原理与S1一致。从图上可以看出,如果我们按下按键,那么按键就会接通并连接到低电平GND,如果我们没有按下,那么按键就会断开并接到3.3V,因此按键为低电平有效。通常的按键所用开关为机械弹性开关,当机械触点断开或者闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而机械式按键在闭合及断开的瞬间均伴随有一连串的抖动,如果不进行处理,会使系统识别到抖动信号而进行不必要的反应,导致模块功能不正常,为了避免这种现象的产生,需要进行按键消抖的操作Ø按键消抖按键消抖主要分为硬件消抖和软件消抖。两个“与非”门构成一个RS触发器为常用的硬件消抖。软件方法消抖,即检测出键闭合后执行一个延时程序,抖动时间的长短由按键的机械特性决定,一般为5ms~20ms,让前沿抖动消失后再一次检测键的状态,如果仍保持闭合状态电平,则确认按下按键操作有效。当检测到按键释放后,也要给5ms~20ms的延时,待后沿抖动消失后才能转入该键的处理程序。经过按键消抖的行人优先按键,判断按键有效后,按键信号传递给控制系统,控制系统再进入相应的处理程序。
由于按键按下去的时间一般都会大于20ms,为了达到不管按键按下多久,都视为按下一次的效果,提出以下计数器架构,如下图所示:
消抖计数器cnt:用于计算20ms的时间,加一条件为flag==0 &&(&key_in_ff1==0),表示当某个按键按下就开始计数;结束条件为100000,表示数到20ms就结束按键:表示被按下的按键,没被按下时为高电平,按下后为低电平。Flag_add:20ms指示信号,默认为低电平,当按键按键按下20ms后变为高电平,直到按键信号变为高电平,重新拉低。
1.2.3参考代码使用明德扬的计数器模板,可以很快速很熟练地写出按键消抖模块。132. module key_module(133.   clk    ,134.   rst_n,135.   key_in ,136.   key_vld   137. );138. parameter                           DATA_W    = 20          ;139. parameter                           KEY_W   = 3         ;140. parameter                           TIME_20MS = 1_000_000   ;141.   142. input                               clk                     ;143. input                               rst_n                   ;144. input               key_in                  ;145. output                key_vld               ;146. reg                   key_vld               ;147. reg                   cnt                     ;148. wire                              add_cnt               ;149. wire                              end_cnt               ;150. reg                                 flag_add                ;151. reg                   key_in_ff1            ;152. reg                   key_in_ff0            ;153.   154.   155. always@(posedge clk or negedge rst_n)begin156.   if(rst_n==1'b0)begin157.         cnt <= 20'b0;158.   end159.   else if(add_cnt)begin160.         if(end_cnt)161.             cnt <= 20'b0;162.         else163.             cnt <= cnt + 1'b1;164.   end165.   else begin166.         cnt <= 0;167.   end168. end169.   170. assign add_cnt = flag_add==1'b0 && (&key_in_ff1==0);171. assign end_cnt = add_cnt && cnt == TIME_20MS - 1;172.   173.   174. always@(posedge clk or negedge rst_n)begin175.   if(rst_n==1'b0)begin176.         flag_add <= 1'b0;177.   end178.   else if(end_cnt)begin179.         flag_add <= 1'b1;180.   end181.   else if(&key_in_ff1==1)begin182.         flag_add <= 1'b0;183.   end184. end185.   186.   187. always@(posedge clk or negedge rst_n)begin188.   if(rst_n==1'b0)begin189.         key_in_ff0 <= {{KEY_W}{1'b1}};190.         key_in_ff1 <= {{KEY_W}{1'b1}};191.   end192.   else begin193.         key_in_ff0 <= key_in    ;194.         key_in_ff1 <= key_in_ff0;195.   end196. end197.   198.   199. always@(posedge clk or negedge rst_n)begin200.   if(rst_n==1'b0)begin201.         key_vld <= 0;202.   end203.   else if(end_cnt)begin204.         key_vld <= ~key_in_ff1;205.   end206.   else begin207.         key_vld <= 0;208.   end209. end210.   211.   212. endmodule
1.3 矩阵键盘模块设计1.3.1接口信号
信号接口方向定义
clk输入系统时钟
rst_n输入低电平复位信号
key_col输入矩阵键盘列输入信号
Key_row输出矩阵键盘行输出信号
Key_en输出按键按下指示信号

1.3.2 设计思路在前面的案例中已经有矩阵键盘的介绍,所以这里不在过多介绍,详细介绍请看下方链接:http://fpgabbs.com/forum.php?mod=viewthread&tid=3101.3.3参考代码1.modulekey_scan(2.                   clk    ,3.                   rst_n,4.                   key_col,5.                   key_row,6.                   key_en   7.               );8.    9.    10.   parameter      KEY_W    =   4      ;11.   parameter      CHK_COL=   0      ;12.   parameter      CHK_ROW=   1      ;13.   parameter      DELAY    =   2      ;14.   parameter      WAIT_END =   3      ;15.   parameter      COL_CNT=   16   ;16.   parameter      TIME_20MS=   1000000;17.   18.   input               clk            ;19.   input               rst_n            ;20.   input          key_col          ;21.   22.   output         key_en         ;23.   output   key_row          ;24.   25.   reg            key_out          ;26.   reg      key_row          ;27.   reg               key_vld          ;28.   29.   30.   reg            key_col_ff0      ;31.   reg            key_col_ff1      ;32.   reg            key_col_get      ;33.   reg            key_en         ;34.   wire                end_shake_cnt    ;35.   reg               end_shake_cnt_ff0;36.   reg            state_c          ;37.   reg         shake_cnt      ;38.   reg            state_n          ;39.   reg            row_index      ;40.   reg         row_cnt          ;41.   wire                col2row_start    ;   42.   wire                row2del_start    ;43.   wire                del2wait_start   ;44.   wire                wait2col_start   ;45.   wire                add_row_cnt      ;46.   wire                end_row_cnt      ;47.   wire                add_shake_cnt    ;48.   wire                add_row_index    ;49.   wire                end_row_index    ;50.   51.   52. always@(posedge clk or negedge rst_n)begin53.   if(rst_n==1'b0)begin54.         key_col_ff0 <= 4'b1111;55.         key_col_ff1 <= 4'b1111;56.   end57.   else begin58.         key_col_ff0 <= key_col    ;59.         key_col_ff1 <= key_col_ff0;60.   end61. end62.   63.   64. always @(posedge clk or negedge rst_n) begin   65.   if (rst_n==0) begin66.         shake_cnt <= 0;   67.   end68.   else if(add_shake_cnt) begin69.         if(end_shake_cnt)70.             shake_cnt <= 0;   71.         else72.             shake_cnt <= shake_cnt+1 ;73.    end74. end75. assign add_shake_cnt = key_col_ff1!=4'hf;76. assign end_shake_cnt = add_shake_cnt&& shake_cnt == TIME_20MS-1 ;77.   78.   79. always@(posedge clk or negedge rst_n)begin80.   if(rst_n==1'b0)begin81.         state_c <= CHK_COL;82.   end83.   else begin84.         state_c <= state_n;85.   end86. end87.   88. always@(*)begin89.   case(state_c)90.         CHK_COL: begin91.                      if(col2row_start )begin92.                        state_n = CHK_ROW;93.                      end94.                      else begin95.                        state_n = CHK_COL;96.                      end97.                  end98.         CHK_ROW: begin99.                      if(row2del_start)begin100.                        state_n = DELAY;101.                      end102.                      else begin103.                        state_n = CHK_ROW;104.                      end105.                  end106.         DELAY :begin107.                      if(del2wait_start)begin108.                        state_n = WAIT_END;109.                      end110.                      else begin111.                        state_n = DELAY;112.                      end113.                  end114.         WAIT_END: begin115.                      if(wait2col_start)begin116.                        state_n = CHK_COL;117.                      end118.                      else begin119.                        state_n = WAIT_END;120.                      end121.                   end122.      default: state_n = CHK_COL;123.   endcase124. end125. assign col2row_start = state_c==CHK_COL&& end_shake_cnt;126. assign row2del_start = state_c==CHK_ROW&& row_index==3 && end_row_cnt;127. assign del2wait_start= state_c==DELAY    && end_row_cnt;128. assign wait2col_start= state_c==WAIT_END && key_col_ff1==4'hf;129.   130. always@(posedge clk or negedge rst_n)begin131.   if(rst_n==1'b0)begin132.         key_row <= 4'b0;133.   end134.   else if(state_c==CHK_ROW)begin135.         key_row <= ~(1'b1 << row_index);136.   end137.   else begin138.         key_row <= 4'b0;139.   end140. end141.   142.   143.   144.   145.   146. always @(posedge clk or negedge rst_n) begin   147.   if (rst_n==0) begin148.         row_index <= 0;   149.   end150.   else if(add_row_index) begin151.         if(end_row_index)152.             row_index <= 0;   153.         else154.             row_index <= row_index+1 ;155.    end156.    else if(state_c!=CHK_ROW)begin157.      row_index <= 0;158.    end159. end160. assign add_row_index = state_c==CHK_ROW && end_row_cnt;161. assign end_row_index = add_row_index&& row_index == 4-1 ;162.   163.   164. always @(posedge clk or negedge rst_n) begin   165.   if (rst_n==0) begin166.         row_cnt <= 0;   167.   end168.   else if(add_row_cnt) begin169.         if(end_row_cnt)170.             row_cnt <= 0;   171.         else172.             row_cnt <= row_cnt+1 ;173.    end174. end175. assign add_row_cnt = state_c==CHK_ROW || state_c==DELAY;176. assign end_row_cnt = add_row_cnt&& row_cnt == 16-1 ;177.   178.   179.   180. always@(posedge clk or negedge rst_n)begin181.   if(rst_n==1'b0)begin182.         key_col_get <= 0;183.   end184.   else if(state_c==CHK_COL && end_shake_cnt ) begin185.         if(key_col_ff1==4'b1110)186.             key_col_get <= 0;187.         else if(key_col_ff1==4'b1101)188.             key_col_get <= 1;189.         else if(key_col_ff1==4'b1011)190.             key_col_get <= 2;191.         else   192.             key_col_get <= 3;193.   end194. end195.   196.   197. always@(posedge clk or negedge rst_n)begin198.   if(rst_n==1'b0)begin199.         key_out <= 0;200.   end201.   else if(state_c==CHK_ROW && end_row_cnt)begin202.         key_out <= {row_index,key_col_get};203.   end204.   else begin205.         key_out <= 0;206.   end207. end208.   209. always@(posedge clk or negedge rst_n)begin210.   if(rst_n==1'b0)begin211.         key_vld <= 1'b0;212.   end213.   else if(state_c==CHK_ROW && end_row_cnt && key_col_ff1==1'b0)begin214.         key_vld <= 1'b1;215.   end216.   else begin217.         key_vld <= 1'b0;218.   end219. end220.   221.   222. always@(*)begin223.   if(rst_n==1'b0)begin224.         key_en = 0;225.   end226.   else if(key_vld && key_out==0)begin227.         key_en = 4'b0001;228.   end229.   else if(key_vld && key_out==1)begin230.         key_en = 4'b0010;231.   end232.   else if(key_vld && key_out==2)begin233.         key_en = 4'b0100;234.   end235.   else begin236.         key_en = 0;237.   end238. end239.   240.   241. Endmodule

1.4 时间产生模块设计1.4.1接口信号
信号接口方向定义
clk输入系统时钟
rst_n输入低电平复位信号
key_vld输入按键按下指示信号
Cnt2输出秒低位计数器
Cnt3输出秒高位计数器
Cnt4输出分低位计数器
Cnt5输出分高位计数器
Cnt6输出时低位计数器
Cnt7输出时高位计数器
1.4.2设计思路根据题目功能要求可知,要设计数字时钟,由此我们可以提出7个计数器的架构,如下图所示:
该架构由7个计数器组成:时钟计数器cnt1、秒低位计数器cnt2、秒高位计数器cnt3、分低位计数器cnt4、分高位计数器cnt5、时低位计数器cnt6、时高位计数器cnt7。时钟计数器cnt1:用于计算0.1秒的时钟个数,加一条件为key1_func==0,表示刚上电时开始计数,key1按下之后停止计数,再按下又重新开始计数;结束条件为5000000,表示数到0.1秒就清零。秒低位计数器cnt2:用于对“1秒“(实际为0.1秒)进行计数,加一条件为(key1_func &&cnt0==0 &&key3_func)||(key1_func==0&&end_cnt1),表示在设置状态下可通过按键key3来控制加一,或者在正常状态时数到1秒就加1;结束条件为10,表示数到10秒就清零。秒高位计数器cnt3:用于对10秒进行计数,加一条件为(key1_func&&cnt0==1 &&key3_func)||(key1_func==0 &&end_cnt2),表示在设置状态下可通过按键key3来控制加一,或者在正常状态时数到10秒就加1;结束条件为6,表示数到60秒就清零。分低位计数器cnt4:用于对1分进行计数,加一条件为(key1_func&&cnt0==2 &&key3_func)||(key1_func==0 &&end_cnt3),表示在设置状态下可通过按键key3来控制加一,或者在正常状态时数到1分就加1;结束条件为10,表示数到10分就清零。分高位计数器cnt5:用于对10分进行计数,加一条件为(key1_func&&cnt0==3 &&key3_func)||(key1_func==0 &&end_cnt4),表示在设置状态下可通过按键key3来控制加一,或者在正常状态时数到10分就加1;结束条件为6,表示数到60分就清零。时低位计数器cnt6:用于对1小时进行计数,加一条件为(key1_func&&cnt0==4 &&key3_func)||(key1_func==0 &&end_cnt5),表示在设置状态下可通过按键key3来控制加一,或者在正常状态时数到1小时就加1;结束条件为x,表示数到x小时就清零。时高位计数器cnt7:用于对10小时进行计数,加一条件为(key1_func&&cnt0==5 &&key3_func)||(key1_func==0 &&end_cnt6),表示在设置状态下可通过按键key3来控制加一,或者在正常状态时数到10小时就加1;结束条件为y,表示数到y*10小时就清零。1.4.3参考代码使用明德扬的计数器模板,可以很快速很熟练地写出时间产生模块。1.module time_data(2.      clk      ,3.      rst_n    ,4.      key_vld,5.      cnt2   ,6.      cnt3   ,7.      cnt4   ,8.      cnt5   ,9.      cnt6   ,10.   cnt711. );12. input                   clk         ;13. input                   rst_n       ;14. input   [ 3:0]          key_vld   ;15. output[ 3:0]          cnt2      ;16. output[ 3:0]          cnt3      ;17. output[ 3:0]          cnt4      ;18. output[ 3:0]          cnt5      ;19. output[ 3:0]          cnt6      ;20. output[ 3:0]          cnt7      ;21.   22. reg                     key1_func   ;23. reg                     key3_func   ;24. reg   [ 2:0]          cnt0      ;25. wire                  add_cnt0    ;26. wire                  end_cnt0    ;27. reg   [ 23:0]          cnt1       ;28. wire                  add_cnt1    ;29. wire                  end_cnt1    ;30. reg   [ 3:0]          cnt2      ;31. wire                  add_cnt2    ;32. wire                  end_cnt2    ;33. reg   [ 3:0]          cnt3      ;34. wire                  add_cnt3    ;35. wire                  end_cnt3    ;36. reg   [ 3:0]          cnt4      ;37. wire                  add_cnt4    ;38. wire                  end_cnt4    ;39. reg   [ 3:0]          cnt5      ;40. wire                  add_cnt5    ;41. wire                  end_cnt5    ;42. reg   [ 3:0]          cnt6      ;43. reg   [ 3:0]          x         ;44. wire                  add_cnt6    ;45. wire                  end_cnt6    ;46. reg   [ 3:0]          cnt7      ;47. reg   [ 1:0]          y         ;48. wire                  add_cnt7    ;49. wire                  end_cnt7    ;50.   51.   52.   53. always@(posedge clk or negedge rst_n)begin54.   if(rst_n==1'b0)begin55.         key1_func<=1'b0;56.   end57.   else if(key_vld==1'b1)begin58.         key1_func<=~key1_func;59.   end60.   else begin61.         key1_func<=key1_func;62.   end63. end64.   65.   66.   67. always @(posedge clk or negedge rst_n) begin   68.   if (rst_n==0) begin69.         cnt0 <= 0;   70.   end71.   else if(add_cnt0) begin72.         if(end_cnt0)73.             cnt0 <= 0;   74.         else75.             cnt0 <= cnt0+1 ;76.    end77. end78. assign add_cnt0 = key_vld;79. assign end_cnt0 = add_cnt0&& cnt0 == 6-1 ;80.   81.   82. always@(posedge clk or negedge rst_n)begin83.   if(rst_n==1'b0)begin84.         key3_func<=1'b0;85.   end86.   else if(key1_func==1'b1 && key_vld==1'b1)begin       87.         key3_func<=1'b1;88.   end89.   else begin90.         key3_func<=1'b0;91.   end92. end93.   94.   95. always @(posedge clk or negedge rst_n) begin   96.   if (rst_n==0) begin97.         cnt1 <= 0;   98.   end99.   else if(add_cnt1) begin100.         if(end_cnt1)101.             cnt1 <= 0;   102.         else103.             cnt1 <= cnt1+1 ;104.    end105.    else begin106.      cnt1 <= 0;107.    end108. end109. assign add_cnt1 = key1_func==0;110. assign end_cnt1 = add_cnt1&& cnt1 == 500_0000-1 ;111.   112.   113.   114.   115. always @(posedge clk or negedge rst_n) begin   116.   if (rst_n==0) begin117.         cnt2 <= 0;   118.   end119.   else if(add_cnt2) begin120.         if(end_cnt2)121.             cnt2 <= 0;   122.         else123.             cnt2 <= cnt2+1 ;124.    end125. end126. assign add_cnt2 = (key1_func && cnt0==0 && key3_func) || (key1_func==0 && end_cnt1);127. assign end_cnt2 = add_cnt2&& cnt2 == 10-1 ;128.   129.   130.   131.   132. always @(posedge clk or negedge rst_n) begin   133.   if (rst_n==0) begin134.         cnt3 <= 0;   135.   end136.   else if(add_cnt3) begin137.         if(end_cnt3)138.             cnt3 <= 0;   139.         else140.             cnt3 <= cnt3+1 ;141.    end142. end143. assign add_cnt3 = (key1_func && cnt0==1 && key3_func) || (key1_func==0 && end_cnt2);144. assign end_cnt3 = add_cnt3&& cnt3 == 6-1 ;145.   146.   147.   148. always @(posedge clk or negedge rst_n) begin   149.   if (rst_n==0) begin150.         cnt4 <= 0;   151.   end152.   else if(add_cnt4) begin153.         if(end_cnt4)154.             cnt4 <= 0;   155.         else156.             cnt4 <= cnt4+1 ;157.    end158. end159. assign add_cnt4 = (key1_func && cnt0==2 && key3_func) || (key1_func==0 && end_cnt3);160. assign end_cnt4 = add_cnt4&& cnt4 == 10-1 ;161.   162.   163. always @(posedge clk or negedge rst_n) begin   164.   if (rst_n==0) begin165.         cnt5 <= 0;   166.   end167.   else if(add_cnt5) begin168.         if(end_cnt5)169.             cnt5 <= 0;   170.         else171.             cnt5 <= cnt5+1 ;172.    end173. end174. assign add_cnt5 = (key1_func && cnt0==3 && key3_func) || (key1_func==0 && end_cnt4);175. assign end_cnt5 = add_cnt5&& cnt5 == 6-1 ;176.   177.   178. always @(posedge clk or negedge rst_n) begin   179.   if (rst_n==0) begin180.         cnt6 <= 0;   181.   end182.   else if(add_cnt6) begin183.         if(end_cnt6)184.             cnt6 <= 0;   185.         else186.             cnt6 <= cnt6+1 ;187.    end188. end189. assign add_cnt6 = (key1_func && cnt0==4 && key3_func) || (key1_func==0 && end_cnt5);190. assign end_cnt6 = add_cnt6&& cnt6 == x-1 ;191.   192.   193.   194. always @(posedge clk or negedge rst_n) begin   195.   if (rst_n==0) begin196.         cnt7 <= 0;   197.   end198.   else if(add_cnt7) begin199.         if(end_cnt7)200.             cnt7 <= 0;   201.         else202.             cnt7 <= cnt7+1 ;203.    end204. end205. assign add_cnt7 = (key1_func && cnt0==5 && key3_func) || (key1_func==0 && end_cnt6);206. assign end_cnt7 = add_cnt7&& cnt7 == y-1 ;207.   208.   209. always@(*)begin210.   if(cnt7==2)begin211.         x = 4;212.   end213.   else begin214.         x =10;215.   end216. end217.   218. always@(*)begin219.   if(cnt6>=4)begin220.         y = 2;221.   end222.   else begin223.         y = 3;224.   end225. end226.   227.   228. endmodule
1.5 数码管显示模块设计1.5.1接口信号
信号接口方向定义
clk输入系统时钟
rst_n输入低电平复位信号
Segment_data输入时间数据
Segment输出数码管段选信号
Seg_sel输出数码管位选信号

1.5.2设计思路在前面的案例中已经有数码管显示的介绍,所以这里不在过多介绍,详细介绍请看下方链接:http://fpgabbs.com/forum.php?mod=viewthread&tid=3991.5.3参考代码1.module seg_disp(2.      clk         ,3.      rst_n       ,4.      segment   ,5.      segment_data,6.      seg_sel7.);8.parameter   ZERO            =   8'b1100_0000;9.parameter   ONE             =   8'b1111_1001;10. parameter   TWO             =   8'b1010_0100;11. parameter   THREE         =   8'b1011_0000;12. parameter   FOUR            =   8'b1001_1001;13. parameter   FIVE            =   8'b1001_0010;14. parameter   SIX             =   8'b1000_0010;15. parameter   SEVEN         =   8'b1111_1000;16. parameter   EIGHT         =   8'b1000_0000;17. parameter   NINE            =   8'b1001_0000;18.   19.   20.   21. input                   clk         ;22. input                   rst_n       ;23. input             segment_data;24. output[ 7:0]          segment   ;25. output[ 5:0]          seg_sel   ;26.   27.   28. reg   [ 7:0]          segment   ;29. wire    [ 7:0]          segment_tmp ;30. reg   [ 5:0]          seg_sel   ;31.   32.   33. reg             cnt8      ;34. wire                  add_cnt8    ;35. wire                  end_cnt8    ;36. reg   [ 2:0]          cnt9      ;37. wire                  add_cnt9    ;38. wire                  end_cnt9    ;39.   40.   41.   42.   43. always @(posedge clk or negedge rst_n) begin   44.   if (rst_n==0) begin45.         cnt8 <= 0;   46.   end47.   else if(add_cnt8) begin48.         if(end_cnt8)49.             cnt8 <= 0;   50.         else51.             cnt8 <= cnt8+1 ;52.    end53. end54. assign add_cnt8 = 1;55. assign end_cnt8 = add_cnt8&& cnt8 == 10000-1 ;56.   57.   58.   59.   60. always @(posedge clk or negedge rst_n) begin   61.   if (rst_n==0) begin62.         cnt9 <= 0;   63.   end64.   else if(add_cnt9) begin65.         if(end_cnt9)66.             cnt9 <= 0;   67.         else68.             cnt9 <= cnt9+1 ;69.    end70. end71. assign add_cnt9 = end_cnt8;72. assign end_cnt9 = add_cnt9&& cnt9 == 6-1 ;73.   74.   75.   76.   77. assign segment_tmp = segment_data[(1+cnt9)*4-1 -:4];78.   79. always@(posedge clk or negedge rst_n)begin80.   if(rst_n==1'b0)begin81.          segment<=ZERO;82.   end83.   elsebegin84.         case(segment_tmp)85.             4'd0:segment <= ZERO;86.             4'd1:segment <= ONE;87.             4'd2:segment <= TWO;88.             4'd3:segment <= THREE;89.             4'd4:segment <= FOUR;90.             4'd5:segment <= FIVE ;91.             4'd6:segment <= SIX ;92.             4'd7:segment <= SEVEN ;93.             4'd8:segment <= EIGHT ;94.             4'd9:segment <= NINE ;95.             default:begin96.               segment<=segment;97.             end98.         endcase99.   end100. end101.   102.   103.   104.   105. always@(posedge clk or negedge rst_n)begin106.   if(rst_n==1'b0)begin107.         seg_sel <= 6'b11_1110;108.   end109.   else begin110.         seg_sel <= ~(6'b1<<cnt9);111.   end112. end113.   114. endmodule
1.6 效果和总结Ø下图是该工程在mp801开发板上的现象其中按键s4控制数字时钟的暂停与开始,按键s3来选择需要设置的位,按键s2设置数值。
Ø下图是该工程在db603开发板上的现象其中按键s1控制数字时钟的暂停与开始,按键s2来选择需要设置的位,按键s3设置数值。
Ø下图是该工程在ms980试验箱上的现象其中按键s1控制数字时钟的暂停与开始,按键s2来选择需要设置的位,按键s3设置数值。


由于该项目的上板现象是动态的,开始、暂停、时间设置等现象无法通过图片表现出来,想观看完整现象的朋友可以看一下现象演示的视频。感兴趣的朋友也可以访问明德扬论坛(http://www.fpgabbs.cn/)进行FPGA相关工程设计学习,也可以看一下我们往期的文章:《基于FPGA的密码锁设计》《波形相位频率可调DDS信号发生器》《基于FPGA的曼彻斯特编码解码设计》《基于FPGA的出租车计费系统》《数电基础与Verilog设计》《基于FPGA的频率、电压测量》《基于FPGA的汉明码编码解码设计》《关于锁存器问题的讨论》《阻塞赋值与非阻塞赋值》《参数例化时自动计算位宽的解决办法》1.7 公司简介明德扬是一家专注于FPGA领域的专业性公司,公司主要业务包括开发板、教育培训、项目承接、人才服务等多个方向。点拨开发板——学习FPGA的入门之选。
MP801开发板——千兆网、ADDA、大容量SDRAM等,学习和项目需求一步到位。网络培训班——不管时间和空间,明德扬随时在你身边,助你快速学习FPGA。周末培训班——明天的你会感激现在的努力进取,升职加薪明德扬来助你。就业培训班——七大企业级项目实训,获得丰富的项目经验,高薪就业。专题课程——高手修炼课:提升设计能力;实用调试技巧课:提升定位和解决问题能力;FIFO架构设计课:助你快速成为架构设计师;时序约束、数字信号处理、PCIE、综合项目实践课等你来选。项目承接——承接企业FPGA研发项目。人才服务——提供人才推荐、人才代培、人才派遣等服务。
【设计教程下载】
【设计视频教程】
https://www.bilibili.com/video/BV1Af4y117H4?p=3


【工程源码】





lixin 发表于 2020-11-12 10:06:22

【问题1】矩阵键盘的视频讲解
答:【每周FPGA案例】至简设计系列_矩阵按键检测   http://www.fpgabbs.cn/forum.php?mod=viewthread&tid=1058&fromuid=100782

十年灯 发表于 2021-3-30 16:39:26

assign segment_tmp = segment_data[(1+cnt9)*4-1 -:4];请问这句代码的功能是什么,怎么解释
页: [1]
查看完整版本: 【每周FPGA案例】至简设计系列_按键控制数字时钟 编号:001600000062