案例编号: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. endmodule
1.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. endmodule
1.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. Endmodule
1.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(微信同步)