案例编号:001600000062
至简设计系列_按键控制数字时钟
--作者:小黑同学
本文为明德扬原创及录用文章,转载请注明出处!
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 顶层信号

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. endmodule1.2 按键检测模块设计
1.2.1 接口信号

1.2.2 设计思路
➢ 硬件电路

独立式按键工作原理如上图所示,4 条输入线连接到 FPGA 的 IO 口上,当按键 S1 按下时,3.
3V 的电源通过电阻 R53 再通过按键 S1 最终进入 GND 形成一条通路,这条线路的全部电压都加在 R
53 上,则 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: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_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. endmodule1.3 矩阵键盘模块设计
1.3.1 接口信号

1.3.2 设计思路
在前面的案例中已经有矩阵键盘的介绍,所以这里不在过多介绍,详细介绍请看下方链接:
http://fpgabbs.com/forum.php?mod=viewthread&tid=310
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. Endmodule1.4 时间产生模块设计
1.4.1 接口信号

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 &&cn
t0==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_fun
c)||(key1_func==0 &&end_cnt5),表示在设置状态下可通过按键 key3 来控制加一,或者在正常状态
时数到 1 小时就加 1;结束条件为 x,表示数到 x 小时就清零。
时高位计数器 cnt7:用于对 10 小时进行计数,加一条件为(key1_func &&cnt0==5 &&key3_fu
nc)||(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 接口信号

1.5.2 设计思路
在前面的案例中已经有数码管显示的介绍,所以这里不在过多介绍,详细介绍请看下方链接:
http://fpgabbs.com/forum.php?mod=viewthread&tid=399
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 效果和总结
➢ 下图是该工程在 mp801 开发板上的现象
其中按键 s4 控制数字时钟的暂停与开始,按键 s3 来选择需要设置的位,按键 s2 设置数值。

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

➢ 下图是该工程在 ms980 试验箱上的现象

其中按键 s1 控制数字时钟的暂停与开始,按键 s2 来选择需要设置的位,按键 s3 设置数值。
由于该项目的上板现象是动态的,开始、暂停、时间设置等
现象无法通过图片表现出来,想观看
完整现象的朋友可以看一下现象演示的视频。
设计视频教程、工程源文件请到明德扬论坛观看或下载!
感兴趣的朋友也可以访问明德扬论坛(http://www.fpgabbs.cn/)进行 FPGA 相关工程设计学习。
温馨提示:明德扬2023推出了全新课程——逻辑设计基本功修炼课,降低学习FPGA门槛的同时,增加了学习的趣味性,并组织了考试赢积分活动
http://www.mdy-edu.com/ffkc/415.html
(点击→了解课程详情☝)感兴趣请联系易老师:13112063618(微信同步)