请选择 进入手机版 | 继续访问电脑版

明德扬论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

微信扫一扫,快捷登录!

查看: 9889|回复: 2

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

[复制链接]
发表于 2020-7-15 21:50:06 | 显示全部楼层 |阅读模式

马上注册,看完整文章,学更多FPGA知识。

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

x
本案例的编号为: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.png

图一
2.png


图二
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.  module  key_clock(  
2.      clk    ,  
3.      rst_n  ,  
4.      key    ,  
5.      segment,  
6.      seg_sel  
7.  );  
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    [23:0]          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_module  uut0(  
34.                 .clk     (clk    ),  
35.                 .rst_n   (rst_n  ),  
36.                 .key_in  (key    ),  
37.                 .key_vld (key_vld)  
38.              );  
39.   
40.   
41.              time_data  uut1(  
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_disp  uut2(  
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. module  key_clock_jvzhen(  
67.     clk    ,  
68.     rst_n  ,  
69.     key_col,  
70.     key_row,  
71.     segment,  
72.     seg_sel  
73. );  
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_scan  uut0(  
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_data  uut1(  
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_disp  uut2(  
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 设计思路Ø  
3.png

硬件电路


独立式按键工作原理如上图所示,4条输入线连接到FPGAIO口上,当按键S1按下时,3.3V的电源通过电阻R53再通过按键S1最终进入GND形成一条通路,这条线路的全部电压都加在R53上,则KS0是低电平。当松开按键后,线路断开,就不会有电流通过,KS0应该是3.3V,为高电平。我们可以通过KS0这个IO口的高低电平状态来判断是否有按键按下。其他按键原理与S1一致。
从图上可以看出,如果我们按下按键,那么按键就会接通并连接到低电平GND,如果我们没有按下,那么按键就会断开并接到3.3V,因此按键为低电平有效。通常的按键所用开关为机械弹性开关,当机械触点断开或者闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而机械式按键在闭合及断开的瞬间均伴随有一连串的抖动,如果不进行处理,会使系统识别到抖动信号而进行不必要的反应,导致模块功能不正常,为了避免这种现象的产生,需要进行按键消抖的操作
Ø  按键消抖
按键消抖主要分为硬件消抖和软件消抖。两个与非门构成一个RS触发器为常用的硬件消抖。软件方法消抖,即检测出键闭合后执行一个延时程序,抖动时间的长短由按键的机械特性决定,一般为5ms20ms,让前沿抖动消失后再一次检测键的状态,如果仍保持闭合状态电平,则确认按下按键操作有效。当检测到按键释放后,也要给5ms20ms的延时,待后沿抖动消失后才能转入该键的处理程序。经过按键消抖的行人优先按键,判断按键有效后,按键信号传递给控制系统,控制系统再进入相应的处理程序。
4.png

由于按键按下去的时间一般都会大于20ms,为了达到不管按键按下多久,都视为按下一次的效果,提出以下计数器架构,如下图所示:
5.png

消抖计数器cnt:用于计算20ms的时间,加一条件为flag==0 &&&key_in_ff1==0),表示当某个按键按下就开始计数;结束条件为100000,表示数到20ms就结束
按键:表示被按下的按键,没被按下时为高电平,按下后为低电平。
Flag_add20ms指示信号,默认为低电平,当按键按键按下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_W-1 :0]        key_in                  ;  
145. output          [KEY_W-1 :0]        key_vld                 ;  
146. reg             [KEY_W-1 :0]        key_vld                 ;  
147. reg             [DATA_W-1:0]        cnt                     ;  
148. wire                                add_cnt                 ;  
149. wire                                end_cnt                 ;  
150. reg                                 flag_add                ;  
151. reg             [KEY_W-1 :0]        key_in_ff1              ;  
152. reg             [KEY_W-1 :0]        key_in_ff0              ;  
153.   
154.   
155. always  @(posedge clk or negedge rst_n)begin  
156.     if(rst_n==1'b0)begin  
157.         cnt <= 20'b0;  
158.     end  
159.     else if(add_cnt)begin  
160.         if(end_cnt)  
161.             cnt <= 20'b0;  
162.         else  
163.             cnt <= cnt + 1'b1;  
164.     end  
165.     else begin  
166.         cnt <= 0;  
167.     end  
168. end  
169.   
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)begin  
175.     if(rst_n==1'b0)begin  
176.         flag_add <= 1'b0;  
177.     end  
178.     else if(end_cnt)begin  
179.         flag_add <= 1'b1;  
180.     end  
181.     else if(&key_in_ff1==1)begin  
182.         flag_add <= 1'b0;  
183.     end  
184. end  
185.   
186.   
187. always  @(posedge clk or negedge rst_n)begin  
188.     if(rst_n==1'b0)begin  
189.         key_in_ff0 <= {{KEY_W}{1'b1}};  
190.         key_in_ff1 <= {{KEY_W}{1'b1}};  
191.     end  
192.     else begin  
193.         key_in_ff0 <= key_in    ;  
194.         key_in_ff1 <= key_in_ff0;  
195.     end  
196. end  
197.   
198.   
199. always  @(posedge clk or negedge rst_n)begin  
200.     if(rst_n==1'b0)begin  
201.         key_vld <= 0;  
202.     end  
203.     else if(end_cnt)begin  
204.         key_vld <= ~key_in_ff1;  
205.     end  
206.     else begin  
207.         key_vld <= 0;  
208.     end  
209. end  
210.   
211.   
212. endmodule  

1.3 矩阵键盘模块设计1.3.1接口信号
  
信号
  
接口方向
定义
clk
输入
系统时钟
rst_n
输入
低电平复位信号
key_col
输入
矩阵键盘列输入信号
Key_row
输出
矩阵键盘行输出信号
Key_en
输出
按键按下指示信号

1.3.2 设计思路
在前面的案例中已经有矩阵键盘的介绍,所以这里不在过多介绍,详细介绍请看下方链接:
1.3.3参考代码
1.  module  key_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 [3:0]         key_col          ;  
21.   
22.     output[3:0]         key_en           ;  
23.     output[KEY_W-1:0]   key_row          ;  
24.   
25.     reg   [3:0]         key_out          ;  
26.     reg   [KEY_W-1:0]   key_row          ;  
27.     reg                 key_vld          ;  
28.   
29.   
30.     reg   [3:0]         key_col_ff0      ;  
31.     reg   [3:0]         key_col_ff1      ;  
32.     reg   [1:0]         key_col_get      ;  
33.     reg   [3:0]         key_en           ;  
34.     wire                end_shake_cnt    ;  
35.     reg                 end_shake_cnt_ff0;  
36.     reg   [3:0]         state_c          ;  
37.     reg   [19:0]        shake_cnt        ;  
38.     reg   [3:0]         state_n          ;  
39.     reg   [1:0]         row_index        ;  
40.     reg   [15:0]        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)begin  
53.     if(rst_n==1'b0)begin  
54.         key_col_ff0 <= 4'b1111;  
55.         key_col_ff1 <= 4'b1111;  
56.     end  
57.     else begin  
58.         key_col_ff0 <= key_col    ;  
59.         key_col_ff1 <= key_col_ff0;  
60.     end  
61. end  
62.   
63.   
64. always @(posedge clk or negedge rst_n) begin   
65.     if (rst_n==0) begin  
66.         shake_cnt <= 0;   
67.     end  
68.     else if(add_shake_cnt) begin  
69.         if(end_shake_cnt)  
70.             shake_cnt <= 0;   
71.         else  
72.             shake_cnt <= shake_cnt+1 ;  
73.    end  
74. end  
75. 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)begin  
80.     if(rst_n==1'b0)begin  
81.         state_c <= CHK_COL;  
82.     end  
83.     else begin  
84.         state_c <= state_n;  
85.     end  
86. end  
87.   
88. always  @(*)begin  
89.     case(state_c)  
90.         CHK_COL: begin  
91.                      if(col2row_start )begin  
92.                          state_n = CHK_ROW;  
93.                      end  
94.                      else begin  
95.                          state_n = CHK_COL;  
96.                      end  
97.                  end  
98.         CHK_ROW: begin  
99.                      if(row2del_start)begin  
100.                          state_n = DELAY;  
101.                      end  
102.                      else begin  
103.                          state_n = CHK_ROW;  
104.                      end  
105.                  end  
106.         DELAY :  begin  
107.                      if(del2wait_start)begin  
108.                          state_n = WAIT_END;  
109.                      end  
110.                      else begin  
111.                          state_n = DELAY;  
112.                      end  
113.                  end  
114.         WAIT_END: begin  
115.                      if(wait2col_start)begin  
116.                          state_n = CHK_COL;  
117.                      end  
118.                      else begin  
119.                          state_n = WAIT_END;  
120.                      end  
121.                   end  
122.        default: state_n = CHK_COL;  
123.     endcase  
124. end  
125. 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)begin  
131.     if(rst_n==1'b0)begin  
132.         key_row <= 4'b0;  
133.     end  
134.     else if(state_c==CHK_ROW)begin  
135.         key_row <= ~(1'b1 << row_index);  
136.     end  
137.     else begin  
138.         key_row <= 4'b0;  
139.     end  
140. end  
141.   
142.   
143.   
144.   
145.   
146. always @(posedge clk or negedge rst_n) begin   
147.     if (rst_n==0) begin  
148.         row_index <= 0;   
149.     end  
150.     else if(add_row_index) begin  
151.         if(end_row_index)  
152.             row_index <= 0;   
153.         else  
154.             row_index <= row_index+1 ;  
155.    end  
156.    else if(state_c!=CHK_ROW)begin  
157.        row_index <= 0;  
158.    end  
159. end  
160. 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) begin  
166.         row_cnt <= 0;   
167.     end  
168.     else if(add_row_cnt) begin  
169.         if(end_row_cnt)  
170.             row_cnt <= 0;   
171.         else  
172.             row_cnt <= row_cnt+1 ;  
173.    end  
174. end  
175. 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)begin  
181.     if(rst_n==1'b0)begin  
182.         key_col_get <= 0;  
183.     end  
184.     else if(state_c==CHK_COL && end_shake_cnt ) begin  
185.         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.     end  
194. end  
195.   
196.   
197. always  @(posedge clk or negedge rst_n)begin  
198.     if(rst_n==1'b0)begin  
199.         key_out <= 0;  
200.     end  
201.     else if(state_c==CHK_ROW && end_row_cnt)begin  
202.         key_out <= {row_index,key_col_get};  
203.     end  
204.     else begin  
205.         key_out <= 0;  
206.     end  
207. end  
208.   
209. always  @(posedge clk or negedge rst_n)begin  
210.     if(rst_n==1'b0)begin  
211.         key_vld <= 1'b0;  
212.     end  
213.     else if(state_c==CHK_ROW && end_row_cnt && key_col_ff1[key_col_get]==1'b0)begin  
214.         key_vld <= 1'b1;  
215.     end  
216.     else begin  
217.         key_vld <= 1'b0;  
218.     end  
219. end  
220.   
221.   
222. always  @(*)begin  
223.     if(rst_n==1'b0)begin  
224.         key_en = 0;  
225.     end  
226.     else if(key_vld && key_out==0)begin  
227.         key_en = 4'b0001;  
228.     end  
229.     else if(key_vld && key_out==1)begin  
230.         key_en = 4'b0010;  
231.     end  
232.     else if(key_vld && key_out==2)begin  
233.         key_en = 4'b0100;  
234.     end  
235.     else begin  
236.         key_en = 0;  
237.     end  
238. end  
239.   
240.   
241. Endmodule


1.4 时间产生模块设计1.4.1接口信号
  
信号
  
接口方向
定义
clk
输入
系统时钟
rst_n
输入
低电平复位信号
key_vld
输入
按键按下指示信号
Cnt2
输出
秒低位计数器
Cnt3
输出
秒高位计数器
Cnt4
输出
分低位计数器
Cnt5
输出
分高位计数器
Cnt6
输出
时低位计数器
Cnt7
输出
时高位计数器
1.4.2设计思路
根据题目功能要求可知,要设计数字时钟,由此我们可以提出7个计数器的架构,如下图所示:
6.png

该架构由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.     cnt7  
11. );  
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)begin  
54.     if(rst_n==1'b0)begin  
55.         key1_func<=1'b0;  
56.     end  
57.     else if(key_vld[0]==1'b1)begin  
58.         key1_func<=~key1_func;  
59.     end  
60.     else begin  
61.         key1_func<=key1_func;  
62.     end  
63. end  
64.   
65.   
66.   
67. always @(posedge clk or negedge rst_n) begin   
68.     if (rst_n==0) begin  
69.         cnt0 <= 0;   
70.     end  
71.     else if(add_cnt0) begin  
72.         if(end_cnt0)  
73.             cnt0 <= 0;   
74.         else  
75.             cnt0 <= cnt0+1 ;  
76.    end  
77. end  
78. assign add_cnt0 = key_vld[1];  
79. assign end_cnt0 = add_cnt0  && cnt0 == 6-1 ;  
80.   
81.   
82. always  @(posedge clk or negedge rst_n)begin  
83.     if(rst_n==1'b0)begin  
84.         key3_func<=1'b0;  
85.     end  
86.     else if(key1_func==1'b1 && key_vld[2]==1'b1)begin      
87.         key3_func<=1'b1;  
88.     end  
89.     else begin  
90.         key3_func<=1'b0;  
91.     end  
92. end  
93.   
94.   
95. always @(posedge clk or negedge rst_n) begin   
96.     if (rst_n==0) begin  
97.         cnt1 <= 0;   
98.     end  
99.     else if(add_cnt1) begin  
100.         if(end_cnt1)  
101.             cnt1 <= 0;   
102.         else  
103.             cnt1 <= cnt1+1 ;  
104.    end  
105.    else begin  
106.        cnt1 <= 0;  
107.    end  
108. end  
109. 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) begin  
117.         cnt2 <= 0;   
118.     end  
119.     else if(add_cnt2) begin  
120.         if(end_cnt2)  
121.             cnt2 <= 0;   
122.         else  
123.             cnt2 <= cnt2+1 ;  
124.    end  
125. end  
126. 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) begin  
134.         cnt3 <= 0;   
135.     end  
136.     else if(add_cnt3) begin  
137.         if(end_cnt3)  
138.             cnt3 <= 0;   
139.         else  
140.             cnt3 <= cnt3+1 ;  
141.    end  
142. end  
143. 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) begin  
150.         cnt4 <= 0;   
151.     end  
152.     else if(add_cnt4) begin  
153.         if(end_cnt4)  
154.             cnt4 <= 0;   
155.         else  
156.             cnt4 <= cnt4+1 ;  
157.    end  
158. end  
159. 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) begin  
165.         cnt5 <= 0;   
166.     end  
167.     else if(add_cnt5) begin  
168.         if(end_cnt5)  
169.             cnt5 <= 0;   
170.         else  
171.             cnt5 <= cnt5+1 ;  
172.    end  
173. end  
174. 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) begin  
180.         cnt6 <= 0;   
181.     end  
182.     else if(add_cnt6) begin  
183.         if(end_cnt6)  
184.             cnt6 <= 0;   
185.         else  
186.             cnt6 <= cnt6+1 ;  
187.    end  
188. end  
189. 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) begin  
196.         cnt7 <= 0;   
197.     end  
198.     else if(add_cnt7) begin  
199.         if(end_cnt7)  
200.             cnt7 <= 0;   
201.         else  
202.             cnt7 <= cnt7+1 ;  
203.    end  
204. end  
205. 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  @(*)begin  
210.     if(cnt7==2)begin  
211.         x = 4;  
212.     end  
213.     else begin  
214.         x =10;  
215.     end  
216. end  
217.   
218. always  @(*)begin  
219.     if(cnt6>=4)begin  
220.         y = 2;  
221.     end  
222.     else begin  
223.         y = 3;  
224.     end  
225. end  
226.   
227.   
228. endmodule  

1.5 数码管显示模块设计1.5.1接口信号
  
信号
  
接口方向
定义
clk
输入
系统时钟
rst_n
输入
低电平复位信号
Segment_data
输入
时间数据
Segment
输出
数码管段选信号
Seg_sel
输出
数码管位选信号

1.5.2设计思路
在前面的案例中已经有数码管显示的介绍,所以这里不在过多介绍,详细介绍请看下方链接:
1.5.3参考代码
1.  module seg_disp(  
2.      clk         ,  
3.      rst_n       ,  
4.      segment     ,  
5.      segment_data,  
6.      seg_sel  
7.  );  
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   [23:0]          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     [15:0]          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) begin  
45.         cnt8 <= 0;   
46.     end  
47.     else if(add_cnt8) begin  
48.         if(end_cnt8)  
49.             cnt8 <= 0;   
50.         else  
51.             cnt8 <= cnt8+1 ;  
52.    end  
53. end  
54. 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) begin  
62.         cnt9 <= 0;   
63.     end  
64.     else if(add_cnt9) begin  
65.         if(end_cnt9)  
66.             cnt9 <= 0;   
67.         else  
68.             cnt9 <= cnt9+1 ;  
69.    end  
70. end  
71. 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)begin  
80.     if(rst_n==1'b0)begin  
81.          segment<=ZERO;  
82.     end  
83.     else  begin  
84.         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:begin  
96.                 segment<=segment;  
97.             end  
98.         endcase  
99.     end  
100. end  
101.   
102.   
103.   
104.   
105. always@(posedge clk or negedge rst_n)begin  
106.     if(rst_n==1'b0)begin  
107.         seg_sel <= 6'b11_1110;  
108.     end  
109.     else begin  
110.         seg_sel <= ~(6'b1<<cnt9);  
111.     end  
112. end  
113.   
114. endmodule  

1.6 效果和总结&#216;  下图是该工程在mp801开发板上的现象
其中按键s4控制数字时钟的暂停与开始,按键s3来选择需要设置的位,按键s2设置数值。
7.png

&#216;  下图是该工程在db603开发板上的现象
其中按键s1控制数字时钟的暂停与开始,按键s2来选择需要设置的位,按键s3设置数值。
8.png

&#216;  下图是该工程在ms980试验箱上的现象
其中按键s1控制数字时钟的暂停与开始,按键s2来选择需要设置的位,按键s3设置数值。
9.png



由于该项目的上板现象是动态的,开始、暂停、时间设置等现象无法通过图片表现出来,想观看完整现象的朋友可以看一下现象演示的视频。
感兴趣的朋友也可以访问明德扬论坛(http://www.fpgabbs.cn/)进行FPGA相关工程设计学习,也可以看一下我们往期的文章:
1.7 公司简介
明德扬是一家专注于FPGA领域的专业性公司,公司主要业务包括开发板、教育培训、项目承接、人才服务等多个方向。点拨开发板——学习FPGA的入门之选。
MP801
开发板——千兆网、ADDA、大容量SDRAM等,学习和项目需求一步到位。网络培训班——不管时间和空间,明德扬随时在你身边,助你快速学习FPGA周末培训班——明天的你会感激现在的努力进取,升职加薪明德扬来助你。就业培训班——七大企业级项目实训,获得丰富的项目经验,高薪就业。专题课程——高手修炼课:提升设计能力;实用调试技巧课:提升定位和解决问题能力;FIFO架构设计课:助你快速成为架构设计师;时序约束、数字信号处理、PCIE、综合项目实践课等你来选。项目承接——承接企业FPGA研发项目。人才服务——提供人才推荐、人才代培、人才派遣等服务。

【设计教程下载】
至简设计系列_按键控制数字时钟.pdf (1.35 MB, 下载次数: 2011)
FPGA视频课程  培训班 FPGA学习资料
吴老师 18022857217(微信同号) Q1241003385

35

主题

57

帖子

7013

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
7013

荣誉管理论坛元老

QQ
发表于 2020-11-12 10:06:22 | 显示全部楼层
【问题1】矩阵键盘的视频讲解
答:【每周FPGA案例】至简设计系列_矩阵按键检测   http://www.fpgabbs.cn/forum.php? ... &fromuid=100782

0

主题

1

帖子

84

积分

注册会员

Rank: 2

积分
84
发表于 2021-3-30 16:39:26 | 显示全部楼层
assign segment_tmp = segment_data[(1+cnt9)*4-1 -:4];  请问这句代码的功能是什么,怎么解释
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|MDYBBS ( 粤ICP备16061416号 )

GMT+8, 2024-4-17 02:21 , Processed in 0.074409 second(s), 25 queries .

Powered by Discuz! X3.4

本论坛由广州健飞通信有限公司所有

© 2001-2019 Comsenz Inc.

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