案例編號: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 研發項目。
人才服務——提供人才推薦、人才代培、人才派遣等服務。