–作者:小黑同学
本文为明德扬原创及录用文章,转载请注明出处!
1.1 总体设计
1.1.1 概述
数字时钟是采用数字电路技术实现时、分、秒计时显示的装置,可以用数字同时显示时,分,秒的精确时间并实现准确校时,具备体积小、重量轻、抗干扰能力强、对环境要求高、高精确性、容易开发等特性,在工业控制系统、智能化仪器表、办公自动化系统等诸多领域取得了极为广泛的应用,诸如自动报警、按时自动打铃、时间程序自动控制、定时广播、自定启闭路灯、定时开关烘箱、通断动力设备、甚至各种定时电器的自动启用等。与传统表盘式机械时钟相比,数字时钟具有更高的准确性和直观性,由于没有机械装置,其使用寿命更长。
1.1.2 设计目标
本设计要求实现可设置的数字时钟(速度快10倍,每过0.1s,秒数加1),具体要求如下:
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.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 设计思路
硬件电路
独立式按键工作原理如上图所示,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_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 设计思路
在前面的案例中已经有矩阵键盘的介绍,所以这里不在过多介绍,详细介绍请看下方链接:
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.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. 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 设计思路
在前面的案例中已经有数码管显示的介绍,所以这里不在过多介绍,详细介绍请看下方链接:
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<
1.6 效果和总结
下图是该工程在mp801开发板上的现象
其中按键s4控制数字时钟的暂停与开始,按键s3来选择需要设置的位,按键s2设置数值。
下图是该工程在db603开发板上的现象
其中按键s1控制数字时钟的暂停与开始,按键s2来选择需要设置的位,按键s3设置数值。
下图是该工程在ms980试验箱上的现象
其中按键s1控制数字时钟的暂停与开始,按键s2来选择需要设置的位,按键s3设置数值。