马上注册,看完整文章,学更多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.1.4模块功能Ø 按键检测模块实现功能将外来异步信号打两拍处理,将异步信号同步化; 实现20ms按键消抖功能,并输出有效按键信号。 Ø 矩阵键盘模块实现功能将外来异步信号打两拍处理,将异步信号同步化; 实现20ms按键消抖功能; 实现矩阵键盘的按键检测功能,并输出有效按键信号。 Ø 时间产生模块实现功能产生时间数据; 根据接收到的不同的按键信号,产生暂停、开启、设置时间的功能。 Ø 数码管显示模块实现功能对接收到的时间数据进行译码。 1.1.5顶层信号
信号名 | | | | | | | | | | | 3位按键信号,开发板按键为矩阵键盘时,不需要该信号 | | | 4位矩阵键盘列信号,默认高电平,开发板按键为普通按键时,不需要该信号 | | | 4位矩阵键盘行信号,默认低电平,开发板按键为普通按键时,不需要该信号 | | | | | | |
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 形成一条通路,这条线路的全部电压都加在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接口信号
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接口信号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接口信号
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 效果和总结Ø 下图是该工程在mp801开发板上的现象其中按键s4控制数字时钟的暂停与开始,按键s3来选择需要设置的位,按键s2设置数值。
Ø 下图是该工程在db603开发板上的现象其中按键s1控制数字时钟的暂停与开始,按键s2来选择需要设置的位,按键s3设置数值。
Ø 下图是该工程在ms980试验箱上的现象其中按键s1控制数字时钟的暂停与开始,按键s2来选择需要设置的位,按键s3设置数值。
由于该项目的上板现象是动态的,开始、暂停、时间设置等现象无法通过图片表现出来,想观看完整现象的朋友可以看一下现象演示的视频。 感兴趣的朋友也可以访问明德扬论坛(http://www.fpgabbs.cn/)进行FPGA相关工程设计学习,也可以看一下我们往期的文章: 1.7 公司简介明德扬是一家专注于FPGA领域的专业性公司,公司主要业务包括开发板、教育培训、项目承接、人才服务等多个方向。点拨开发板——学习FPGA的入门之选。
MP801开发板——千兆网、ADDA、大容量SDRAM等,学习和项目需求一步到位。网络培训班——不管时间和空间,明德扬随时在你身边,助你快速学习FPGA。周末培训班——明天的你会感激现在的努力进取,升职加薪明德扬来助你。就业培训班——七大企业级项目实训,获得丰富的项目经验,高薪就业。专题课程——高手修炼课:提升设计能力;实用调试技巧课:提升定位和解决问题能力;FIFO架构设计课:助你快速成为架构设计师;时序约束、数字信号处理、PCIE、综合项目实践课等你来选。项目承接——承接企业FPGA研发项目。人才服务——提供人才推荐、人才代培、人才派遣等服务。
|