案例編號: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.cqqtmy.cn/ffkc/415.html
(點擊→了解課程詳情?)感興趣請聯系易老師:13112063618(微信同步)