至简设计系列_闹钟
--作者:小黑同学
本文为明德扬原创及录用文章,转载请注明出处!
1.1 总体设计
1.1.1 概述
数字时钟是采用数字电路技术实现时、分、秒计时显示的装置,可以用数字同时显示时,分,秒
的精确时间并实现准确校时,具备体积小、重量轻、抗干扰能力强、对环境要求高、高精确性、容易
开发等特性,在工业控制系统、智能化仪器表、办公自动化系统等诸多领域取得了极为广泛的应用,
诸如自动报警、按时自动打铃、时间程序自动控制、定时广播、自定启闭路灯、定时开关烘箱、通断
动力设备、甚至各种定时电器的自动启用等。与传统表盘式机械时钟相比,数字时钟具有更高的准确
性和直观性,由于没有机械装置,其使用寿命更长。
1.1.2 设计目标
设计一款具有闹钟功能的数字时钟,具体要求如下
1、 用 8 个数码管实现,四个一组,每组有分钟和秒。左边一组是时间显示,右边一组用来
做闹钟时间。
2、 当左边时间等于右边时,蜂鸣器响 5 秒。
3、 闹钟时间和显示时间均可通过 3 个按键设置。设置方法:按下按键 1,时钟暂停,跳到
设置时间状态,再按下按键 1,回到正常状态。通过按键 2,选择要设置的位置,初始
设置秒个位,按一下,设置秒十位,再按下,设置分个位,以此类推,循环设置。通过
按键 3,设置数值,按一下数值加 1,如果溢出则重新变为 0。
1.1.3 系统结构框图
系统结构框图如下所示:
结构图共分两个,如果使用的开发板上是矩阵键盘的时候,对应的结构图是图一。如果使用的开
发板上是普通按键的时候,对应的结构图是图二。
图一
图二
1.1.4 模块功能
➢ 按键检测模块实现功能
1、将外来异步信号打两拍处理,将异步信号同步化。
2、实现 20ms 按键消抖功能,并输出有效按键信号。
➢ 矩阵键盘模块实现功能
1、将外来异步信号打两拍处理,将异步信号同步化。
2、实现 20ms 按键消抖功能。
3、实现矩阵键盘的按键检测功能,并输出有效按键信号。
➢ 时间产生模块实现功能
1、 产生显示时间数据。
2、 产生闹钟时间数据,
3、根据接收到的不同的按键信号,产生暂停、开启、设置时间的功能。
➢ 数码管显示模块实现功能
1、 对接收到的时间数据进行译码。
➢ 蜂鸣器模块实现功能
1、 将接受到的显示时间数据与闹钟时间数据进行比较,控制蜂鸣器的开启。
1.1.5 顶层信号
1.1.6 参考代码
下面是使用普通按键的顶层代码:
1. module alarm_clock(
2. clk ,
3. rst_n ,
4. key ,
5. segment ,
6. seg_sel ,
7. beep
8. );
9. input clk ;
10. input rst_n ;
11. input [2:0 ] key ;
12. output [7:0 ] segment ;
13. output [7:0 ] seg_sel ;
14. output beep ;
15.
16. wire [7:0 ] segment ;
17. wire [7:0 ] seg_sel ;
18. wire beep ;
19. wire [3:0 ] xs_sec_low ;
20. wire [3:0 ] xs_sec_high ;
21. wire [3:0 ] xs_min_low ;
22. wire [3:0 ] xs_min_high ;
23. wire [3:0 ] sec_low ;
24. wire [3:0 ] sec_high ;
25. wire [3:0 ] min_low ;
26. wire [3:0 ] min_high ;
27. wire [25:0] counter ;
28. wire [3:0 ] key_vld ;
29. wire flag_set ;
30.
31. key_module u0(
32. .clk (clk ),
33. .rst_n (rst_n ),
34. .key_in (key ),
35. .key_vld (key_vld )
36. );
37. time_data u1(
38. .clk (clk ),
39. .rst_n (rst_n ),
40. .key_vld (key_vld ),
41. .flag_set (flag_set ),
42. .counter (counter ),
43. .sec_low (sec_low ),
44. .sec_high (sec_high ),
45. .min_low (min_low ),
46. .min_high (min_high ),
47. .xs_sec_low (xs_sec_low ),
48. .xs_sec_high (xs_sec_high ),
49. .xs_min_low (xs_min_low ),
50. .xs_min_high (xs_min_high )
51. );
52. beep u2(
53. .clk (clk ),
54. .rst_n (rst_n ),
55. .flag_set (flag_set ),
56. .counter (counter ),
57. .beep (beep ),
58. .sec_low (sec_low ),
59. .sec_high (sec_high ),
60. .min_low (min_low ),
61. .min_high (min_high ),
62. .xs_sec_low (xs_sec_low ),
63. .xs_sec_high (xs_sec_high ),
64. .xs_min_low (xs_min_low ),
65. .xs_min_high (xs_min_high )
66. );
67. seg_disp u3(
68. .clk (clk ),
69. .rst_n (rst_n ),
70. .segment_data({xs_min_high,xs_min_low,xs_sec_high,xs_sec_low,min_high,min_low,sec_high,sec_low}),
71. .segment (segment ),
72. .seg_sel (seg_sel )
73. );
74.
75.
76. endmodule
下面是使用矩阵键盘的顶层代码:
1. module alarm_clock_jvzhen(
2. clk ,
3. rst_n ,
4. key_col ,
5. key_row ,
6. segment ,
7. seg_sel ,
8. beep
9. );
10. input clk ;
11. input rst_n ;
12. input [3:0 ] key_col ;
13. output [3:0 ] key_row ;
14. output [7:0 ] segment ;
15. output [7:0 ] seg_sel ;
16. output beep ;
17.
18. wire [7:0 ] segment ;
19. wire [7:0 ] seg_sel ;
20. wire beep ;
21. wire [3:0 ] xs_sec_low ;
22. wire [3:0 ] xs_sec_high ;
23. wire [3:0 ] xs_min_low ;
24. wire [3:0 ] xs_min_high ;
25. wire [3:0 ] sec_low ;
26. wire [3:0 ] sec_high ;
27. wire [3:0 ] min_low ;
28. wire [3:0 ] min_high ;
29. wire [25:0] counter ;
30. wire [3:0 ] key_vld ;
31. wire flag_set ;
32. wire [15:0] key_out ;
33.
34. key_scan u0(
35. .clk (clk ),
36. .rst_n (rst_n ),
37. .key_col (key_col ),
38. .key_row (key_row ),
39. .key_en (key_vld )
40. );
41. time_data u1(
42. .clk (clk ),
43. .rst_n (rst_n ),
44. .key_vld (key_vld ),
45. .flag_set (flag_set ),
46. .counter (counter ),
47. .sec_low (sec_low ),
48. .sec_high (sec_high ),
49. .min_low (min_low ),
50. .min_high (min_high ),
51. .xs_sec_low (xs_sec_low ),
52. .xs_sec_high (xs_sec_high ),
53. .xs_min_low (xs_min_low ),
54. .xs_min_high (xs_min_high )
55. );
56. beep u2(
57. .clk (clk ),
58. .rst_n (rst_n ),
59. .flag_set (flag_set ),
60. .counter (counter ),
61. .beep (beep ),
62. .sec_low (sec_low ),
63. .sec_high (sec_high ),
64. .min_low (min_low ),
65. .min_high (min_high ),
66. .xs_sec_low (xs_sec_low ),
67. .xs_sec_high (xs_sec_high ),
68. .xs_min_low (xs_min_low ),
69. .xs_min_high (xs_min_high )
70. );
71. seg_disp u3(
72. .clk (clk ),
73. .rst_n (rst_n ),
74. .segment_data({xs_min_high,xs_min_low,xs_sec_high,xs_sec_low,min_high,min_low,sec_high,sec_low}),
75. .segment (segment ),
76. .seg_sel (seg_sel )
77. );
78.
79.
80. endmodule
1.2 按键检测模块设计
1.2.1 接口信号
1.2.2 设计思路
在前面的案例中已经有按键检测的介绍,所以这里不在过多介绍,详细介绍请看下方链接:
【每周 FPGA 案例】至简设计系列_按键控制数字时钟
1.2.3 参考代码
使用明德扬的计数器模板,可以很快速很熟练地写出按键消抖模块。
1. always @(posedge clk or negedge rst_n)begin
2. if(rst_n==1"b0)begin
3. cnt <= 20"b0;
4. end
5. else if(add_cnt)begin
6. if(end_cnt)
7. cnt <= 20"b0;
8. else
9. cnt <= cnt + 1"b1;
10. end
11. else begin
12. cnt <= 0;
13. end
14. end
15.
16. assign add_cnt = flag_add==1"b0 && (&key_in_ff1==0);
17. assign end_cnt = add_cnt && cnt == TIME_20MS - 1;
18.
19.
20. always @(posedge clk or negedge rst_n)begin
21. if(rst_n==1"b0)begin
22. flag_add <= 1"b0;
23. end
24. else if(end_cnt)begin
25. flag_add <= 1"b1;
26. end
27. else if(&key_in_ff1==1)begin
28. flag_add <= 1"b0;
29. end
30. end
31.
32.
33. always @(posedge clk or negedge rst_n)begin
34. if(rst_n==1"b0)begin
35. key_in_ff0 <= {{KEY_W}{1"b1}};
36. key_in_ff1 <= {{KEY_W}{1"b1}};
37. end
38. else begin
39. key_in_ff0 <= key_in ;
40. key_in_ff1 <= key_in_ff0;
41. end
42. end
43.
44.
45. always @(posedge clk or negedge rst_n)begin
46. if(rst_n==1"b0)begin
47. key_vld <= 0;
48. end
49. else if(end_cnt)begin
50. key_vld <= ~key_in_ff1;
51. end
52. else begin
53. key_vld <= 0;
54. end
55. end
1.3 矩阵键盘模块设计
1.3.1 接口信号