案例编号: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 相关工程设计学习。
往期推荐:
《基于 FPGA 的密码锁设计》
《波形相位频率可调 DDS 信号发生器》
《基于 FPGA 的曼彻斯特编码解码设计》
《基于 FPGA 的出租车计费系统》
《数电基础与 Verilog 设计》
《基于 FPGA 的频率、电压测量》
《基于 FPGA 的汉明码编码解码设计》
《关于锁存器问题的讨论》
《阻塞赋值与非阻塞赋值》
《参数例化时自动计算位宽的解决办法》
明德扬是一家专注于 FPGA 领域的专业性公司,公司主要业务包括开发板、教育培训、项目承
接、人才服务等多个方向。
点拨开发板——学习 FPGA 的入门之选。
MP801 开发板——千兆网、ADDA、大容量 SDRAM 等,学习和项目需求一步到位。
网络培训班——不管时间和空间,明德扬随时在你身边,助你快速学习 FPGA。
周末培训班——明天的你会感激现在的努力进取,升职加薪明德扬来助你。
就业培训班——七大企业级项目实训,获得丰富的项目经验,高薪就业。
专题课程——高手修炼课:提升设计能力;实用调试技巧课:提升定位和解决问题能力;FIFO 架构
设计课:助你快速成为架构设计师;时序约束、数字信号处理、PCIE、综合项目实践课等你来选。
项目承接——承接企业 FPGA 研发项目。
人才服务——提供人才推荐、人才代培、人才派遣等服务。