当前位置:   article > 正文

FPGA数字时钟(可暂停调数,含代码)

fpga数字时钟

前言

前段时间刚刚开始初步学习FPGA相关知识,在学习了一段时间后,利用前面所学知识,写了一个数字时钟,顺便在这里写下总结,方便理解。

(本人小白一名,有错欢迎指出,欢迎探讨)

我使用的FPGA芯片型号是Cyclone IV的EP4CE6F17C8,如有想测试实现效果的同学,可以把后面3-1到3-5对应代码 建文件(3-5设置为顶层文件),设置好芯片型号 ,以及自己开发板对应引脚位置,进行测试。

PS:今天2023年10月31日,我更新一下此博客,之前有些地方认知错误,导致借鉴的同学出现一些奇怪的问题(见评论区),在此我梳理一下 :

第一: 之前不清楚外设引脚跟FPGA芯片关系,一直以为芯片型号会对应固定外设引脚,所以当有人问引脚图时,我发过去了我的引脚图,但实际按照我的这个引脚图来设置,很大概率不会成功 。

这是因为EP4CE6F17C8 仅仅是Altera的Cyclone IV系列的一款FPGA芯片型号,而它的封装的引脚虽然是固定的,但大家使用的开发板并不一定是相同的,即pcb可能不一样,走线不一样。 

(开发板 = PCB底板 + 外设 + FPGA芯片)

(芯片引脚与外设关系是:芯片封装引脚-->pcb走线-->外设接口)

可能我们芯片一样(也就是封装引脚一样),但是PCB(走线)不一样,可能我的按键1(外设)在我的开发板pcb走线下,走到了FPGA芯片的A4脚,所以A4对应我的按键1操控;而你的开发板按键1,在你的那款开发板pcb走线下,连接到FPGA芯片的B5脚,则你的开发板就是B5控制按键1。

所以我建议需要引脚图的同学,最好是去问问自己老师,同学 ,或者搜索找找自己那款开发板的对应引脚图 。下面还是放上我的开发板(应该是AIGO_C4MB_V11) 引脚图(原理图):

0844e2d574d44697ab9117bfbd04331b.png

第二:关于仿真 和 上板实验看效果 ,首先要明白第一个点,仿真跑的时间单位是跟我们上板实验的时间单位是不一样的, 比如modelsim仿真开始运行,我们等待10多秒中去查看,以为会跑10秒,以为那个 <秒信号> 会从0计数到9了,可实际查看,咦?为什么还是0 ,这是为什么啊博主 ?

这是为什么?因为modelsim仿真运行的单位是100ns(反正很小,该值可自己设置) ,现在中的十秒 对软件来说,可能就跑了几十us,而你设置的那个代码计数模块单位是每1000_000us ,<秒信号>才会加一,所以你怎么可能在仿真波形图中看到明显变化 。要想查看波形明显也很简单,只需要在对应模块中把参数修改一下就行(我已经在代码中注释了的,可以看看) ,当然如果你时间很多,可以开着modelsim跑个几十分钟,强行跑到现实的1秒,当我没说 .

一.数码管原理

我使用的开发板拥有6个数码管(由位选信号控制每一位的亮灭),每个数码管都有8个小段(由段选信号控制对应小段显示,得到目标数字,其中7个控制数码管小段,还有1个控制小数点),由于时钟显示时,每个位置数字都变化不同,而我们的段选信号是控制6个数码管的8个小段同时呈现什么效果(意思就是6个数码管上显示的数字是一样的),要想实现每个位置数字看起来不一样,我们就需要利用视觉暂留现象,显示时候只能让其中一个位显示,其他位不显示,让数码管位选信号切换的足够快,也就是说数码管对应位由亮到灭需要的时候很短,人眼就无法分清此时此刻数码管状态,使人感觉数码管一直都亮着,且亮着的那一位是正确的数字,这样就能达到看起来,不同位置数字不同且都亮着的效果(也就是我们需要的数字时钟效果)。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5YmR5LiO5qKm,size_20,color_FFFFFF,t_70,g_se,x_16

二.模块原理图

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5YmR5LiO5qKm,size_20,color_FFFFFF,t_70,g_se,x_16

有三个子模块和一个总的处理模块,其中三个子模块分别是key_debounce(消抖模块),count(计数时钟沿,计数1s模块),count2(计数20us,控制位选信号跳转时间)。总模块count_clk负责处理输入的信号,有6个计数器,分别计数秒,分,时的个位,十位,还有对位选信号的处理和译码部分。

三.代码

这里我使用的计数方法是6个计数器分别计数时,分,秒的个位和十位,计数满了则使进位符置1,给到下一位,这个方法不需要进行取余取整操作,使用触发器资源较多,但节省组合逻辑资源。

3.1计数1s模块代码

  1. module count(
  2. input wire clk,
  3. input wire rst_n,
  4. input wire ok,
  5. output reg flag1
  6. );
  7. reg[25:0] cnt;
  8. //parameter MAX=24'd31250;//飞驰版54秒24小时 (上板时也可以使用这个,可以看到数码管显示很快)
  9. parameter MAX=26'd50_000_000;//1s (上板时使用这个)
  10. //parameter MAX=20'd1000_000;//测试 (modelsim仿真时使用这个)
  11. always@(posedge clk or negedge rst_n)
  12. begin
  13. if(!rst_n)
  14. begin
  15. cnt<=26'd0;
  16. flag1<=1'd0;
  17. end
  18. else if(cnt==MAX-1'd1)
  19. begin
  20. cnt<=26'd0;
  21. flag1<=1'd1;
  22. end
  23. else if(ok)
  24. begin
  25. cnt<=cnt;
  26. flag1<=1'd0;
  27. end
  28. else
  29. begin
  30. cnt<=cnt+1'd1;
  31. flag1<=1'd0;
  32. end
  33. end
  34. endmodule

3.2计数20us模块代码

  1. module count2(
  2. input wire clk,
  3. input wire rst_n,
  4. output reg box
  5. );
  6. reg[19:0] cnt;
  7. parameter MAX=20'd1_000;//20us
  8. always@(posedge clk or negedge rst_n)
  9. begin
  10. if(!rst_n)
  11. begin
  12. cnt<=20'd0;
  13. box<=1'd0;
  14. end
  15. else if(cnt==MAX-1'd1)
  16. begin
  17. cnt<=20'd0;
  18. box<=1'd1;
  19. end
  20. else
  21. begin
  22. cnt<=cnt+1'd1;
  23. box<=1'd0;
  24. end
  25. end
  26. endmodule

3.3消抖模块代码(消抖方式是等待一定时间未动,则消抖成功)

  1. module key_debounce(
  2. input clk,
  3. input rst_n,
  4. input wire [2:0] key,
  5. output reg ok,
  6. output reg flag9,//改变暂停时数字
  7. output reg flag10//控制数码管调整数字的位置
  8. );
  9. parameter MAX_TEN=21'd1000_000;//20ms
  10. reg [20:0] delay_cnt;
  11. reg [20:0] delay_cnt2;
  12. reg [20:0] delay_cnt3;
  13. reg value_key;
  14. reg value_key2;
  15. reg value_key3;
  16. reg[0:0] flag8;
  17. //reg[0:0] flag9;
  18. //消抖模块
  19. always@(posedge clk or negedge rst_n)
  20. begin
  21. if(!rst_n)
  22. begin
  23. value_key<=1'd1;
  24. delay_cnt<=MAX_TEN;
  25. end
  26. else
  27. begin
  28. value_key<=key[0];//取得一瞬间key
  29. if(value_key==!key[0])//如果抖动
  30. begin
  31. delay_cnt<=MAX_TEN;
  32. end
  33. else if(delay_cnt>1'd0&&value_key==1'd0)//当没有抖动时候,开始计数
  34. begin
  35. delay_cnt<=delay_cnt-1'd1;
  36. end
  37. else
  38. begin
  39. delay_cnt<=21'd0;
  40. end
  41. end
  42. end
  43. //消抖符号的处理
  44. always@(posedge clk or negedge rst_n)
  45. begin
  46. if(!rst_n)
  47. begin
  48. flag8<=1'd0;
  49. end
  50. else if(delay_cnt==21'd1&&value_key==1'd0)
  51. begin
  52. flag8<=1'd1;
  53. end
  54. else
  55. begin
  56. flag8<=1'd0;
  57. end
  58. end
  59. //暂停标识符的转化
  60. always@(posedge flag8)
  61. begin
  62. if(flag8 && !value_key)//按下消除抖动后才置1(没有松开)
  63. begin
  64. ok<=~ok;
  65. end
  66. end
  67. //消抖模块(按键三的消抖)
  68. always@(posedge clk or negedge rst_n)
  69. begin
  70. if(!rst_n)
  71. begin
  72. value_key2<=1'd1;
  73. delay_cnt2<=MAX_TEN;
  74. end
  75. else
  76. begin
  77. value_key2<=key[1];//取得一瞬间key值(寄存器存储key值)
  78. if(value_key2==!key[1])//如果抖动
  79. begin
  80. delay_cnt2<=MAX_TEN;
  81. end
  82. else if(delay_cnt2>1'd0&&value_key2==1'd0)//当没有抖动时候,开始计数
  83. begin
  84. delay_cnt2<=delay_cnt2-1'd1;
  85. end
  86. else
  87. begin
  88. delay_cnt2<=21'd0;
  89. end
  90. end
  91. end
  92. //消抖符号的处理
  93. always@(posedge clk or negedge rst_n)
  94. begin
  95. if(!rst_n)
  96. begin
  97. flag9<=1'd0;
  98. end
  99. else if(delay_cnt2==21'd1&&value_key2==1'd0)
  100. begin
  101. flag9<=1'd1;//表示按键按下了一次并未松开,且抖动已经消除了
  102. end
  103. else
  104. begin
  105. flag9<=1'd0;
  106. end
  107. end
  108. //消抖模块(按键四的消抖)
  109. always@(posedge clk or negedge rst_n)
  110. begin
  111. if(!rst_n)
  112. begin
  113. value_key3<=1'd1;
  114. delay_cnt3<=MAX_TEN;//这里其实可以去掉,就不用给后面加东西了
  115. end
  116. else
  117. begin
  118. value_key3<=key[2];//取得一瞬间key
  119. if(value_key3==!key[2])//如果抖动
  120. begin
  121. delay_cnt3<=MAX_TEN;
  122. end
  123. else if(delay_cnt3>1'd0&&value_key3==1'd0)//当没有抖动时候,开始计数
  124. begin
  125. delay_cnt3<=delay_cnt3-1'd1;
  126. end
  127. else
  128. begin
  129. delay_cnt3<=21'd0;
  130. end
  131. end
  132. end
  133. //消抖符号的处理
  134. always@(posedge clk or negedge rst_n)
  135. begin
  136. if(!rst_n)
  137. begin
  138. flag10<=1'd0;
  139. end
  140. else if(delay_cnt3==21'd1&&value_key3==1'd0)//注意(当复位后,置了MAX值,所以会导致计数一次(此时没有抖动),会造成flag10跟flag9置1一次,进而导致暂停时候复位,会让调数的位置默认加一和数字默认加一)
  141. begin
  142. flag10<=1'd1;//表示按键按下了一次并松开,且抖动已经消除了
  143. end
  144. else
  145. begin
  146. flag10<=1'd0;
  147. end
  148. end
  149. //加一标识符的转变
  150. //always@(posedge flag9)
  151. //begin
  152. // if(flag9 && !value_key2)
  153. // begin
  154. // ok2<=~ok2;
  155. // end
  156. //end
  157. endmodule

3.4数码管计数显示模块

  1. module count_clk(
  2. input wire clk,
  3. input wire rst_n,
  4. input wire flag1,//计数满1s
  5. //input wire key,//1时,表示无效,没被按下
  6. input wire box,//控制位选跳转(流水灯)
  7. input wire flag9,
  8. input wire flag10,
  9. input wire ok,
  10. output reg[5:0] sel,
  11. output reg[7:0] seg
  12. );
  13. reg [2:0] sum=3'd0;//计数控制调哪个位置的数字
  14. reg [4:0] s_a;//秒的个位
  15. reg [3:0] s_b;//秒的十位
  16. reg [4:0] m_a;//分的个位
  17. reg [3:0] m_b;//分的十位
  18. reg [4:0] t_a;//时的个位
  19. reg [2:0] t_b;//时的十位
  20. reg [0:0] flag2 = 0;//秒的个位进位标识符
  21. reg [0:0] flag3 = 0;//秒的十位进位标识符
  22. reg [0:0] flag4 = 0;//分的个位进位标识符
  23. reg [0:0] flag5 = 0;//分的十位进位标识符
  24. reg [0:0] flag6 = 0;//时的个位进位标识符
  25. reg [0:0] flag7 = 0;//时的十位进位标识符
  26. reg [0:0] clear =0;//计数满了清零符
  27. always@(posedge clk or negedge rst_n)
  28. begin
  29. if(!rst_n)
  30. sum<=3'd0;
  31. else if(flag10)
  32. begin
  33. if(sum<3'd5)
  34. begin
  35. sum<=sum+1'd1;
  36. end
  37. else
  38. begin
  39. sum<=3'd0;
  40. end
  41. end
  42. else
  43. sum<=sum;
  44. end
  45. //秒的个位计数(暂停采用的止住flag进位,而不是采用禁用另一个计数模块的clk) (已经改成clk暂停了)
  46. always@(posedge clk or negedge rst_n )
  47. begin
  48. if(!rst_n)
  49. begin
  50. s_a<=5'd0;
  51. flag2<=1'd0;
  52. end
  53. else if(flag1)
  54. begin
  55. if(s_a<5'd9)
  56. begin
  57. s_a<=s_a+1'd1;
  58. flag2<=1'd0;
  59. end
  60. else
  61. begin
  62. s_a<=5'd0;
  63. flag2<=1'd1;
  64. end
  65. end
  66. else if(flag9)
  67. begin
  68. if(ok==1'd1&&sum==3'd0)
  69. begin
  70. if(s_a<5'd9)
  71. begin
  72. s_a<=s_a+1'd1;
  73. end
  74. else
  75. begin
  76. s_a<=5'd0;
  77. end
  78. end
  79. end
  80. else
  81. begin
  82. s_a<=s_a;
  83. flag2<=1'd0;//因为改成clk触发了,触发频率变大,如果不是设置置零,则会在下一个always语句中,多次触发flag21 的情况,疯狂计数(最开始写法为flagx触发时候,没有考虑到这种情况,现在改为clk,则需要考虑到不进位时clk上升沿来了触发的情况)
  84. end
  85. end
  86. //秒的十位计数
  87. always@(posedge clk or negedge rst_n )
  88. begin
  89. if(!rst_n)
  90. begin
  91. s_b<=3'd0;
  92. flag3<=1'd0;
  93. end
  94. else if(flag2)
  95. begin
  96. if(s_b<3'd5)
  97. begin
  98. s_b<=s_b+1'd1;
  99. flag3<=1'd0;
  100. end
  101. else
  102. begin
  103. s_b<=3'd0;
  104. flag3<=1'd1;
  105. end
  106. end
  107. else if(flag9)
  108. begin
  109. if(ok==1'd1&&sum==3'd1)
  110. begin
  111. if(s_b<3'd5)
  112. begin
  113. s_b<=s_b+1'd1;
  114. end
  115. else
  116. begin
  117. s_b<=3'd0;
  118. end
  119. end
  120. end
  121. else
  122. begin
  123. flag3<=1'd0;
  124. s_b<=s_b;
  125. end
  126. end
  127. //分的个位计数
  128. always@(posedge clk or negedge rst_n )
  129. begin
  130. if(!rst_n)
  131. begin
  132. m_a<=4'd0;
  133. flag4<=1'd0;
  134. end
  135. else if(flag3)
  136. begin
  137. if(m_a<4'd9)
  138. begin
  139. m_a<=m_a+1'd1;
  140. flag4<=1'd0;
  141. end
  142. else
  143. begin
  144. m_a<=4'd0;
  145. flag4<=1'd1;
  146. end
  147. end
  148. else if(flag9)
  149. begin
  150. if(ok==1'd1&&sum==3'd2)
  151. begin
  152. if(m_a<4'd9)
  153. begin
  154. m_a<=m_a+1'd1;
  155. end
  156. else
  157. begin
  158. m_a<=4'd0;
  159. end
  160. end
  161. end
  162. else
  163. begin
  164. m_a<=m_a;
  165. flag4<=1'd0;
  166. end
  167. end
  168. //分的十位计数
  169. always@(posedge clk or negedge rst_n )
  170. begin
  171. if(!rst_n)
  172. begin
  173. m_b<=3'd0;
  174. flag5<=1'd0;
  175. end
  176. else if(flag4)
  177. begin
  178. if(m_b<3'd5)
  179. begin
  180. m_b<=m_b+1'd1;
  181. flag5<=1'd0;
  182. end
  183. else
  184. begin
  185. m_b<=3'd0;
  186. flag5<=1'd1;
  187. end
  188. end
  189. else if(flag9)
  190. begin
  191. if(ok==1'd1&&sum==3'd3)
  192. begin
  193. if(m_b<3'd5)
  194. begin
  195. m_b<=m_b+1'd1;
  196. end
  197. else
  198. begin
  199. m_b<=3'd0;
  200. end
  201. end
  202. end
  203. else
  204. begin
  205. m_b<=m_b;
  206. flag5<=1'd0;
  207. end
  208. end
  209. //时的个位计数
  210. always@(posedge clk or negedge rst_n )
  211. begin
  212. if(!rst_n)
  213. begin
  214. t_a<=4'd0;
  215. flag6<=1'd0;
  216. end
  217. else if(flag5)
  218. begin
  219. if(t_a==4'd9)
  220. begin
  221. t_a<=4'd0;
  222. flag6<=1'd1;
  223. end
  224. else if(t_a==4'd3&&t_b==2'd2)
  225. begin
  226. t_a<=4'd0;
  227. clear<=1'd1;//传递给下一位使其判断清零的符号。
  228. flag6<=1'd1;
  229. end
  230. else
  231. begin
  232. t_a<=t_a+1'd1;
  233. flag6<=1'd0;
  234. clear<=1'd0;
  235. end
  236. end
  237. else if(flag9)
  238. begin
  239. if(ok==1'd1&&sum==3'd4)
  240. begin
  241. if(t_a<4'd9&&t_b!=2'd2)
  242. begin
  243. t_a<=t_a+1'd1;
  244. end
  245. else if(t_b==2'd2)
  246. begin
  247. if(t_a<4'd3)
  248. t_a<=t_a+1'd1;
  249. else
  250. t_a<=4'd0;
  251. end
  252. else
  253. begin
  254. t_a<=4'd0;
  255. end
  256. end
  257. end
  258. else
  259. begin
  260. t_a<=t_a;
  261. flag6<=1'd0;
  262. end
  263. end
  264. //时的十位计数(已设置满23:59:59自动清零)
  265. always@(posedge clk or negedge rst_n )//?不能加第二个(or posedge clear)加了会导致个位跟十位显示相同,小数点都相同
  266. begin
  267. if(!rst_n)
  268. begin
  269. t_b<=2'd0;
  270. end
  271. else if(flag6)//flag6表示进位,也可以代表清零。
  272. begin
  273. if(clear)
  274. begin
  275. t_b<=2'd0;
  276. end
  277. else
  278. t_b<=t_b+1'd1;
  279. end
  280. else if(flag9)
  281. begin
  282. if(ok==1'd1&&sum==3'd5)
  283. begin
  284. if(t_b<2'd2)
  285. begin
  286. t_b<=t_b+1'd1;
  287. end
  288. else
  289. begin
  290. t_b<=2'd0;
  291. end
  292. end
  293. end
  294. else
  295. begin
  296. t_b<=t_b;
  297. end
  298. end
  299. //位选流水灯(实现快速闪烁每一个数码管形成残影,造成时钟现象)
  300. always@(posedge clk or negedge rst_n)
  301. begin
  302. if(!rst_n)
  303. begin
  304. sel<=6'b111_110;
  305. end
  306. else if(box)
  307. begin
  308. sel<={sel[4:0],sel[5]};
  309. end
  310. else
  311. begin
  312. sel<=sel;
  313. end
  314. end
  315. //译码模块
  316. always@(sel)
  317. begin
  318. if(sel==6'b111110)
  319. begin
  320. case(s_a)
  321. 4'd0:seg=8'b11000000;
  322. 4'd1:seg=8'b11111001;
  323. 4'd2:seg=8'b10100100;
  324. 4'd3:seg=8'b10110000;
  325. 4'd4:seg=8'b10011001;
  326. 4'd5:seg=8'b10010010;
  327. 4'd6:seg=8'b10000010;
  328. 4'd7:seg=8'b11111000;
  329. 4'd8:seg=8'b10000000;
  330. 4'd9:seg=8'b10010000;
  331. default:;
  332. endcase
  333. end
  334. else if(sel==6'b111101)
  335. begin
  336. case(s_b)
  337. 3'd0:seg=8'b11000000;
  338. 3'd1:seg=8'b11111001;
  339. 3'd2:seg=8'b10100100;
  340. 3'd3:seg=8'b10110000;
  341. 3'd4:seg=8'b10011001;
  342. 3'd5:seg=8'b10010010;
  343. default:;
  344. endcase
  345. end
  346. else if(sel==6'b111011)
  347. begin
  348. case(m_a)
  349. 4'd0:seg=8'b01000000;
  350. 4'd1:seg=8'b01111001;
  351. 4'd2:seg=8'b00100100;
  352. 4'd3:seg=8'b00110000;
  353. 4'd4:seg=8'b00011001;
  354. 4'd5:seg=8'b00010010;
  355. 4'd6:seg=8'b00000010;
  356. 4'd7:seg=8'b01111000;
  357. 4'd8:seg=8'b00000000;
  358. 4'd9:seg=8'b00010000;
  359. default:;
  360. endcase
  361. end
  362. else if(sel==6'b110111)
  363. begin
  364. case(m_b)
  365. 3'd0:seg=8'b11000000;
  366. 3'd1:seg=8'b11111001;
  367. 3'd2:seg=8'b10100100;
  368. 3'd3:seg=8'b10110000;
  369. 3'd4:seg=8'b10011001;
  370. 3'd5:seg=8'b10010010;
  371. default:;
  372. endcase
  373. end
  374. else if(sel==6'b101111)
  375. begin
  376. case(t_a)
  377. 4'd0:seg=8'b01000000;
  378. 4'd1:seg=8'b01111001;
  379. 4'd2:seg=8'b00100100;
  380. 4'd3:seg=8'b00110000;
  381. 4'd4:seg=8'b00011001;
  382. 4'd5:seg=8'b00010010;
  383. 4'd6:seg=8'b00000010;
  384. 4'd7:seg=8'b01111000;
  385. 4'd8:seg=8'b00000000;
  386. 4'd9:seg=8'b00010000;
  387. default:;
  388. endcase
  389. end
  390. else if(sel==6'b011111)
  391. begin
  392. case(t_b)
  393. 2'd0:seg=8'b11000000;
  394. 2'd1:seg=8'b11111001;
  395. 2'd2:seg=8'b10100100;
  396. default:;
  397. endcase
  398. end
  399. end
  400. endmodule

3.5顶层模块

  1. module top_count_clk(
  2. input clk,
  3. input rst_n,
  4. input [2:0] key,
  5. output [5:0] sel,
  6. output [7:0] seg
  7. );
  8. wire flag1;
  9. wire box;
  10. wire ok;
  11. wire flag9;
  12. wire flag10;
  13. count u_count(
  14. .clk(clk),
  15. .rst_n(rst_n),
  16. .flag1(flag1),
  17. .ok(ok)
  18. );
  19. count2 u_count2(
  20. .clk(clk),
  21. .rst_n(rst_n),
  22. .box(box)
  23. );
  24. count_clk u_count_clk(
  25. .clk(clk),
  26. .rst_n(rst_n),
  27. .sel(sel),
  28. .seg(seg),
  29. .flag1(flag1),
  30. .box(box),//位选信号例化
  31. .flag9(flag9),
  32. .flag10(flag10),
  33. .ok(ok)
  34. );
  35. key_debounce u_key_debounce(
  36. .clk(clk),
  37. .rst_n(rst_n),
  38. .key(key),
  39. .ok(ok),
  40. .flag9(flag9),
  41. .flag10(flag10)
  42. );
  43. endmodule

3.6测试文件(做仿真时候,需要改一下对应模块参数)

  1. `timescale 1ns/1ps
  2. module top_count_clk_tb();
  3. reg clk;
  4. reg rst_n;
  5. wire [5:0] sel;
  6. wire [7:0] seg;
  7. parameter CYCLE=20;
  8. always#(CYCLE/2) clk=~clk;
  9. initial begin
  10. clk=1'd0;
  11. rst_n=1'd0;
  12. #(CYCLE);
  13. rst_n=1'd1;
  14. #(CYCLE*20'd1000_000*17'd86400); //对应上面1s计数模块中的仿真参数
  15. //#(CYCLE*17'd31250*17'd86400); //对应1s计数模块中的飞驰人生版本parameter
  16. $stop;
  17. end
  18. top_count_clk u_top_count_clk(
  19. .clk(clk),
  20. .rst_n(rst_n),
  21. .sel(sel),
  22. .seg(seg)
  23. );
  24. endmodule

四.理解总结

上板使用说明:这个实验设置了四个按键,分别是key0(复位按键),key1(暂停按键),key2控制我们进行调数的位数(也就是让我们选择调数字时钟的哪一位),key3则是直接的控制我们调的位的数(按一下加一)。

注:调数功能只能在暂停状态下进行,可在暂停时候进行复位功能。

做实验过程中遇到的问题:

1. 在做好时钟后,想要加入调数功能,结果发现多一个按键需要加入always语句中,因为不能在多个地方给同一个参数赋值,所以这个地方卡了很久,最后只能把posedge flag改为了时钟上升沿触发,但因此在调试过程中,忽略了一个东西,clk上升沿触发的频率比flag触发的次数多非常多,最初的flag一层一层触发,没出现这个问题,是因为对应flag5置零(在上一个flag4来之前),不会进入always语句,影响flag5进位符号对应的位置显示,所以我在每一个always语句后面加了一个else来置零对应flag(这样每次clk触发进入,就能让flag发挥用处后及时置零,不会持续让下一位进位)。

2. 遇到的第二个问题是,消抖模块没写完整,忽略了复位情况下,把cnt置MAX了,所以会默认触发一次flag9和flag10(不知道为什么暂停消抖部分没受影响),进而导致了暂停时候复位,会看到00:00:10的效果,解决方法也很简单,要么把复位的cnt置MAX删掉,要么后面加上判断,使之不能把复位判断成一次按键按下,这样就能解决这个问题了)

后来我看了下:重新梳理一遍,当按下第二个键,再次取消暂停时,其他键也抖动了,且按下时,cnt又置MAX了,

 watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5YmR5LiO5qKm,size_14,color_FFFFFF,t_70,g_se,x_16

结果时钟沿到来,没判定第一个if语句,直接第二个语句,value_key 直接取了key的瞬时值

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5YmR5LiO5qKm,size_20,color_FFFFFF,t_70,g_se,x_16

又因为按下暂停时,其他键很可能也抖动了,所以就会导致某位计数(?),直到计数为21’d1,然后看下一个图:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5YmR5LiO5qKm,size_20,color_FFFFFF,t_70,g_se,x_16

此时clk来了又判断key的瞬时值,就阴差阳错把flag9置1了,flag10同理,结果就导致了我暂停时候复位了,在取消暂停,结果数码管显示的00:00:10。

(注:代码截图是已经改过了的代码,错误已经纠正,前面出现问题的时候忘记截图了,时间久了想不起来具体怎么改的,下次写复位注意多考虑清楚时序关系!)

3. 有机会可以加个谱子,加个到时间自动放音乐。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/709750
推荐阅读
相关标签
  

闽ICP备14008679号