在前几节课中讲解了PLL,分频器,计数器,数码管的译码显示,按键去抖等内容,本节内容主要利用已学过的内容进行复杂数字钟的设计,以达到对学过的知识进行复习、练习以及综合设计及应用的目的。
1.复杂数字钟设计要求如下:
- 计数:设计月、日、时、分、秒计数器(为了简化设计,不考虑闰年效应, 2月统一为28天)。
- 显示:
- 分两个界面显示:第一个界面显示时、分、秒,之间每两位由小数点分隔;第二个界面显示字符“dd”,月份,日。字符“dd”用来区分这两个界面。
- 界面之间的切换:由移动闪动的小数点完成。当闪动的小数点移动到小时,继续左移时,即从第一个界面切换到第二个界面。当闪动的小数点移动到日,继续右移时,可以从第二个界面返回到第一个界面。
- 小数点:
- 两种模式显示:一种是分隔小数点,即每两位有一个小数点分隔,位置固定,常亮;另一种是调整指示小数点,以一秒的频率闪动,用来指示对应计数值的修改位置。
- 按键:使用4个按键,对应FII-006/010上的PB0 (+), PB3(-), PB1 (左移),PB2(右移)。当PB1(左移)按下时,闪动小数点左移,当PB2(右移)按下时,闪动小数点右移。当闪动的小数点移动到小时,继续左移时,即从第一个界面切换到第二个界面。当闪动的小数点移动到日,继续右移时,可以从第二个界面返回到第一个界面。在闪动的小数点指示位置,按动PB0(+),对应的数码管显示加1,按动PB3(-),对应的数码管显示减1。用于手动较准时间。
- 整体设计要求:层次化、模块化、参数化,自顶向下实现。
2.设计分析:设计架构设计
- 接口: 时钟,4个按键输入,6个数码管输出。
- 模块:
- 调度程序shdl(schedule,调度按键调整与计数显示之间的切换)
- 计数器
- 分频器
- PLL
- 扫描及显示译码
- 组装:分析各个模块之间的关系,把共同部分提取出来,来决定层次之间的关系。如PLL模块生成时钟信号,该信号将会被各个功能模块使用,因此PLL的例化应放在顶层模块。再比如分频的输出ms_p,不仅被s_p的分频使用,按键去抖,数码管扫描等都会使用,因此分频程序也在顶层模块例化,方便信号的相互使用,这样整个程序脉络清晰、整洁。

图1
- 自顶向下逐步实现各个底层模块。
3.设计步骤
- 在Quartus II新建工程,工程名称 complex_counter, 顶层实体名称 complex_counter, 顶层文件的名称complex_counter.v, 器件Cyclone 10LP, 10CL006YE144CG, 仿真工具选择Modelsim-Altera
- 编写顶层文件
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
module complex_counter ( input inclk, input sw_in_l, sw_in_r, sw_in_p, sw_in_m, output [5:0] SEAT, output DP, output [6:0] SEVEN_SEG ); wire pll_locked; wire sys_clk; wire rst = !pll_locked; wire ms_p; wire s_p; wire [3:0] s_l; wire [3:0] s_h; wire [3:0] m_l; wire [3:0] m_h; wire [3:0] h_l; wire [3:0] h_h; wire [3:0] d_l; wire [3:0] d_h; wire [3:0] mh_l; wire [3:0] mh_h; wire [5:0] dp_tmp; wire [3:0] seg_a; wire [3:0] seg_b; wire [3:0] seg_c; wire [3:0] seg_d; wire [3:0] seg_e; wire [3:0] seg_f; wire sw_l; wire sw_r; wire sw_p; wire sw_m; wire [9:0] dp_pos; //分频器模块,sys_clk 时钟输入,输出ms_p 毫秒单脉冲信号, s_p秒单脉冲信号,请参阅分频器设计部分内容。 f_div f_div_inst ( .rst (rst), .clk (sys_clk), .ms_p (ms_p), .s_p (s_p) ); //按键去抖,4个按键输入,输出为sw_out_l, asw_out_r,sw_out_p,sw_out_m,4个去抖后的输出,请参阅自适应按键去抖部分内容。 sw_debouce sw_debouse_inst ( .rst (rst), .clk (sys_clk), .ms_p (ms_p), .sw_in_l (sw_in_l), .sw_in_r (sw_in_r), .sw_in_p (sw_in_p), .sw_in_m (sw_in_m), .sw_out_l (sw_l), //用作小数点左移按键,l--->left .sw_out_r (sw_r), //用作小数点右移按键, r---> right .sw_out_p (sw_p), //对计数器调整的加(+)1按键 ,p---> plus (positive) .sw_out_m (sw_m) //对计数器调整的减一(-)按键, m---> minus ); //带设定的计数器,分别计数 秒、分、时、天、月,4个按键用于调整(或矫正)每位计数器的值。 count count_inst ( .rst (rst), .clk (sys_clk), .s_p (s_p), //秒脉冲 .sw_in_l (sw_l), //去抖后的4个按键 .sw_in_r (sw_r), .sw_in_p (sw_p), .sw_in_m (sw_m), .s_out_l (s_l), //秒,个位 .s_out_h (s_h), //秒,十位 .m_out_l (m_l), //分,个位 .m_out_h (m_h), //分,十位 .h_out_l (h_l), //时,个位 .h_out_h (h_h), //时,十位 .d_out_l (d_l), //天,个位 .d_out_h (d_h), //天,十位 .mh_out_l (mh_l), //月,个位 .mh_out_h (mh_h), //月,十位 .dp_out_pos (dp_pos) //小数点位置指示,用来指示要调整的是哪一位(秒,分、时、天、月,总共10个BCD计数) ); //数码管扫描显示,6个BCD吗输入,扫描显示在6个数码管上,同时将小数点的位置能正确显示。 scan_dis_7seg scan_dis_7seg_inst ( .rst (rst), .clk (sys_clk), .ms_p (ms_p), //schdl_adjust 程序在10个BCD计数器,按要求选出相应的计数器,显示在数码管上 .seg_in_a (seg_a), .seg_in_b (seg_b), .seg_in_c (seg_c), .seg_in_d (seg_d), .seg_in_e (seg_e), .seg_in_f (seg_f), .dp_in (dp_tmp), .SEAT (SEAT), .DP (DP), .seven_seg (SEVEN_SEG) ); schdl_adjust schdl_adjust_inst ( .rst (rst), .clk (sys_clk), .s_p (s_p), .dp_in_pos (dp_pos), //用于选择相应的BCD码显示在数码管上。 .sw_in_l (sw_l), .sw_in_r (sw_r), .sw_in_p (sw_p), .sw_in_m (sw_m), //输入10个BCD, 选出相应的输出到scan_dis_7seg 用于显示。选择由dp_in_pos决定 .s_in_l (s_l), .s_in_h (s_h), .m_in_l (m_l), .m_in_h (m_h), .h_in_l (h_l), .h_in_h (h_h), .d_in_l (d_l), .d_in_h (d_h), .mh_in_l (mh_l), .mh_in_h (mh_h), .dp_out (dp_tmp), .seg_out_a (seg_a), .seg_out_b (seg_b), .seg_out_c (seg_c), .seg_out_d (seg_d), .seg_out_e (seg_e), .seg_out_f (seg_f) ); pll1 pll1_inst ( .areset (1'b0), .inclk0 (inclk), // inclk 50Mhz .c0 (sys_clk), // sys_clk , PLL 输出,100Mhz .locked (pll_locked) ); endmodule |
PLL 在编写顶层文件完成后可以利用向导(wizard)生成,输入时钟50Mhz, 输出时钟100Mhz, 例化如下:
|
1 2 3 4 5 6 7 |
pll1 pll1_inst ( .areset (1'b0), .inclk0 (inclk), .c0 (sys_clk), .locked (pll_locked) ); |
4.在Quartus II 下编译,检查语法错误
修改过语法错误后,最后编译会因为没有底层的模块报错,如图2,

图2
图2中指出,scan_dis_7seg与schdl_adjust 没有定义底层模块,顶层模块的编写没有错误。一步步把底层实现后,该错误就会消除。底层模块的实现在下节内容介绍。
作业:在Quartus下利用图形输入法完成顶层模块设计。