Menu Close

利用Verilog 进行复杂数字钟设计(1)–程序功能划分技巧

在前几节课中讲解了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的分频使用,按键去抖,数码管扫描等都会使用,因此分频程序也在顶层模块例化,方便信号的相互使用,这样整个程序脉络清晰、整洁。

%title插图%num

图1

  • 自顶向下逐步实现各个底层模块。

 

 3.设计步骤

  1. 在Quartus II新建工程,工程名称 complex_counter, 顶层实体名称 complex_counter, 顶层文件的名称complex_counter.v, 器件Cyclone 10LP, 10CL006YE144CG, 仿真工具选择Modelsim-Altera
  2. 编写顶层文件
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,  例化如下:

pll1 pll1_inst
(
    .areset (1'b0),
    .inclk0 (inclk),
    .c0     (sys_clk),
    .locked (pll_locked)
);

 

4.在Quartus II 下编译,检查语法错误

修改过语法错误后,最后编译会因为没有底层的模块报错,如图2,

%title插图%num

图2

图2中指出,scan_dis_7seg与schdl_adjust 没有定义底层模块,顶层模块的编写没有错误。一步步把底层实现后,该错误就会消除。底层模块的实现在下节内容介绍。

作业:在Quartus下利用图形输入法完成顶层模块设计。

 

Posted in FPGA, FPGA, Quartus II, Verilog, Verilog, 开发工具, 教材与教案, 文章

发表评论

相关链接