crazybird

【原创】FPGA应用(四)——电子钟

0
阅读(3883)

一、设计需求

    设计一个可以显示分、秒的电子钟模块并在红色飓风E45开发板的四个的数码管进行显示。

二、设计思路

    首先,我们得了解板上四个八段数码管的特性进行了解。图1所示为数码管的原理图,从中可以知道数码是共阴的,即当LED_AN0~LED_AN3为高电平时三极管导通,LED_S0~LED_S3为低电平,数码管被选中,其中LED_AN0~LED_AN3、LED_A~LED_G和LED_DP是直接连接到FPGA管脚上的。

1

图1 数码管原理图

    其次,根据数码管的原理图给出数码管的编码列表,如表1所示。其中的点号DP根据需要进行亮与灭的选择。

表1  数码管编码对应表

8 

    接着,我们要对设计的功能进行合理的划分。根据需求电子钟的功能包括计数和编码显示两部分,故本设计的功能模块组成如图2所示。

定时器

图2  定时器设计框架

    最后,再补充一点,由于数码管是动态扫描显示的,利用的人眼的视觉暂留效应及发光二极管的余晖效应,只要使扫描速度足够快(低于0.1秒),就不会看到由于数码管切换显示时的闪烁感。

三、设计实现

timer.v:

/**********************************************版权申明*************************************************
**                                   电子技术应用网站, CrazyBird
**                     http://www.chinaaet.com, http://blog.chinaaet.com/crazybird
**
**--------------------------------------------文件信息--------------------------------------------------
** 文件名:          timer.v
** 创建者:          CrazyBird
** 创建日期:        2015-7-26
** 版本号:           v1.0
** 功能描述:        电子钟顶层模块
**                   
********************************************************************************************************/
// synopsys translate_off
`timescale 1 ns / 1 ps
// synopsys translate_on
module timer(
    rst_n,
    clk,
    seg,
    sel
    );
    //******************************************************************************
    //                                  参数定义
    //******************************************************************************
    //  修改以下参数以满足需求
    parameter   CLK_CYCLE = 20;                                        //   时钟周期,单位ns
    parameter   T0        = 1000_000;                                  //   1ms延时
    //  修改以上参数以满足需求
    
    //******************************************************************************
    //                                 端口定义
    //******************************************************************************
    input                   rst_n;                                     //   全局复位,低电平有效
    input                   clk;                                       //   全局时钟,50MHz
    output      [7:0]       seg;                                       //   编码后的数码管输出
    output      [3:0]       sel;                                       //   数码管的位选
    
    //******************************************************************************
    //                                 变量定义
    //******************************************************************************
    wire        [2:0]       min_h;                                     //   分的十位数
    wire        [3:0]       min_l;                                     //   分的个位数
    wire        [2:0]       sec_h;                                     //   秒的十位数
    wire        [3:0]       sec_l;                                     //   秒的个位数
    wire                    display_flag;                              //   数码管动态显示标志位
    
    //******************************************************************************
    //                                 模块例化
    //******************************************************************************
    //  例化time_counter模块
    time_counter #(
        .CLK_CYCLE  (   CLK_CYCLE   ),
        .T0         (   T0          )
    )
    u_time_counter (
        .rst_n          (   rst_n       ),
        .clk            (   clk         ),
        .min_h          (   min_h       ),
        .min_l          (   min_l       ),
        .sec_h          (   sec_h       ),
        .sec_l          (   sec_l       ),
        .display_flag   (   display_flag)
    );
    
    //  例化display模块
    display u_display(
        .rst_n          (   rst_n       ),
        .clk            (   clk         ),
        .min_h          (   min_h       ),      
        .min_l          (   min_l       ),
        .sec_h          (   sec_h       ),      
        .sec_l          (   sec_l       ),
        .display_flag   (   display_flag),
        .seg            (   seg         ),
        .sel            (   sel         )
    );
    
    //******************************************************************************

endmodule
//*********************************************文件结束*****************************************************

time_counter.v:

/**********************************************版权申明*************************************************
**                                   电子技术应用网站, CrazyBird
**                     http://www.chinaaet.com, http://blog.chinaaet.com/crazybird
**
**--------------------------------------------文件信息--------------------------------------------------
** 文件名:          time_counter.v
** 创建者:          CrazyBird
** 创建日期:        2015-7-26
** 版本号:           v1.0
** 功能描述:        时间计数
**                   
********************************************************************************************************/
// synopsys translate_off
`timescale 1 ns / 1 ps
// synopsys translate_on
module time_counter(
    rst_n,
    clk,        
    min_h,      
    min_l,
    sec_h,      
    sec_l,
    display_flag
    );
    //******************************************************************************
    //                                  参数定义
    //******************************************************************************
    //  修改以下参数以满足需求
    parameter   CLK_CYCLE = 20;                                        //   时钟周期,单位ns
    parameter   T0        = 1000_000;                                  //   1ms延时
    //  修改以上参数以满足需求
    
    //  不要修改以下参数
    parameter   T0_VAL    = T0/CLK_CYCLE-1;                            //   1ms延时
    //  不要修改以上参数
    
    //******************************************************************************
    //                                  端口定义
    //******************************************************************************
    input                   rst_n;                                     //   全局复位,低电平有效
    input                   clk;                                       //   全局时钟,50MHz
    output reg  [2:0]       min_h;                                     //   分的十位数
    output reg  [3:0]       min_l;                                     //   分的个位数
    output reg  [2:0]       sec_h;                                     //   秒的十位数
    output reg  [3:0]       sec_l;                                     //   秒的个位数
    output                  display_flag;                              //   数码管动态扫描标志位
    
    //******************************************************************************
    //                                  1ms延时
    //******************************************************************************
    reg [15:0]  cnt;
    always@(posedge clk or negedge rst_n)
    begin
        if(rst_n == 1'b0)
            cnt <= (0);
        else if(cnt < T0_VAL)
            cnt <= cnt + 1'b1;
        else
            cnt <= (0);
    end
    assign  delay_1ms = (cnt == T0_VAL);                               //   1ms延时完成标志位
    assign  display_flag = delay_1ms;                                  //   数码管动态扫描标志位
    
    //******************************************************************************
    //                                 1s延时
    //******************************************************************************
    reg         [9:0]       mse;
    always@(posedge clk or  negedge rst_n)
    begin
        if(rst_n == 1'b0)
            mse <= (0);
        else
        begin
            if(delay_1ms == 1'b1)
            begin
                if(mse < 10'd9)
                    mse <= mse + 1'b1;
                else
                    mse <= (0);
            end
        end
    end
    wire    sec_l_flag = ((mse == 10'd9) && (delay_1ms == 1'b1));      //  1s延时完成标志位
    
    //******************************************************************************
    //                                 秒计数实现
    //******************************************************************************
    //  秒个位数计数
    always@(posedge clk or  negedge rst_n)
    begin
        if(rst_n == 1'b0)
            sec_l <= 0;
        else
        begin
            if(sec_l_flag == 1'b1)
            begin
                if(sec_l < 4'd9)
                    sec_l <= sec_l + 1'b1;
                else
                    sec_l <= 0;
            end
        end
    end
    wire    sec_h_flag = ((sec_l == 4'd9) && (sec_l_flag == 1'b1));    //  秒个位数进位标志位
    
    //  秒十位数计数
    always@(posedge clk or  negedge rst_n)
    begin
        if(rst_n == 1'b0)
            sec_h <= 0;
        else
        begin
            if(sec_h_flag == 1'b1)
            begin
                if(sec_h < 3'd5)
                    sec_h <= sec_h + 1'b1;
                else
                    sec_h <= 0;
                end
        end
    end
    wire    min_l_flag = ((sec_h == 3'd5) && (sec_h_flag == 1'b1));    //  秒十位数进位标志位
    
    //******************************************************************************
    //                                  分计数实现
    //******************************************************************************
    //  分个位数计数
    always@(posedge clk or  negedge rst_n)
    begin
        if(rst_n == 1'b0)
            min_l <= 0;
        else
        begin
            if(min_l_flag == 1'b1)
            begin
                if(min_l < 4'd9)
                    min_l <= min_l + 1'b1;
                else
                    min_l <= 0;
            end
        end
    end
    wire    min_h_flag = ((min_l == 4'd9) && (min_l_flag == 1'b1));    //  分个位数进位标志位
    
    //  分十位数计数
    always@(posedge clk or  negedge rst_n)
    begin
        if(rst_n == 1'b0)
            min_h <= 0;
        else
        begin
            if(min_h_flag == 1'b1)
            begin
                if(min_h < 3'd5)
                    min_h <= min_h + 1'b1;
                else
                    min_h <= 0;
            end
        end
    end
    //******************************************************************************

endmodule
//*********************************************文件结束*****************************************************

display.v:

/**********************************************版权申明*************************************************
**                                   电子技术应用网站, CrazyBird
**                     http://www.chinaaet.com, http://blog.chinaaet.com/crazybird
**
**--------------------------------------------文件信息--------------------------------------------------
** 文件名:          display.v
** 创建者:          CrazyBird
** 创建日期:        2015-7-26
** 版本号:           v1.0
** 功能描述:        编码显示
**                   
********************************************************************************************************/
// synopsys translate_off
`timescale 1 ns / 1 ps
// synopsys translate_on
module display(
    rst_n,
    clk,
    min_h,      
    min_l,
    sec_h,      
    sec_l,
    display_flag,
    seg,
    sel
    );
    //******************************************************************************
    //                                 端口定义
    //******************************************************************************
    input                   rst_n;                                     //   全局复位,低电平有效
    input                   clk;                                       //   全局时钟,50MHz
    input       [2:0]       min_h;                                     //   分的十位数
    input       [3:0]       min_l;                                     //   分的个位数
    input       [2:0]       sec_h;                                     //   秒的十位数
    input       [3:0]       sec_l;                                     //   秒的个位数
    input                   display_flag;                              //   数码管动态显示标志位
    output reg  [7:0]       seg;                                       //   编码后的数码管输出
    output reg  [3:0]       sel;                                       //   数码管的位选
    
    //******************************************************************************
    //                                  编码函数
    //******************************************************************************
    function [7:0]  seg_data;
        input   [3:0]       din;                                       //   待编码数据
        input               dp;                                        //   决定数码管点号是否点亮,1为点亮
        
        begin
            case(din)
            4'd0 : seg_data = {7'b1111110,dp};
            4'd1 : seg_data = {7'b0110000,dp};
            4'd2 : seg_data = {7'b1101101,dp};
            4'd3 : seg_data = {7'b1111001,dp};
            4'd4 : seg_data = {7'b0110011,dp};
            4'd5 : seg_data = {7'b1011011,dp};
            4'd6 : seg_data = {7'b1011111,dp};
            4'd7 : seg_data = {7'b1110000,dp};
            4'd8 : seg_data = {7'b1111111,dp};
            4'd9 : seg_data = {7'b1111011,dp};
            endcase
        end
    endfunction
    
    //******************************************************************************
    //                          数码管动态显示的计数器
    //******************************************************************************
    reg         [1:0]       cnt;                                       //   由于只有四个数码管,故只需两位
    always @(posedge clk or negedge rst_n)
    begin
        if(rst_n == 1'b0)
            cnt <= (0);
        else if(display_flag == 1'b1)
            cnt <= cnt + 1'b1;
        else
            cnt <= cnt;
    end
    
    //******************************************************************************
    //                                 编码输出 
    //******************************************************************************
    always @(posedge clk or negedge rst_n)
    begin
        if(rst_n == 1'b0)
        begin
            seg <= (0);
            sel <= (0);
        end
        else
        begin
            case(cnt)
            2'b00 :                                                    //   显示秒个位数
            begin
                seg <= seg_data(sec_l,1'b0);
                sel <= 4'b0001;
            end
            
            2'b01 :                                                    //   显示秒十位数
            begin
                seg <= seg_data({1'b0,sec_h},1'b0);
                sel <= 4'b0010;
            end
            
            2'b10 :                                                    //   显示分个位数
            begin
                seg <= seg_data(min_l,1'b1);
                sel <= 4'b0100;
            end
            
            2'b11 :                                                    //   显示分十位数
            begin
                seg <= seg_data({1'b0,min_h},1'b0);
                sel <= 4'b1000;
            end
            endcase
        end
    end
    
    //******************************************************************************

endmodule
//*********************************************文件结束*****************************************************

    电子钟的modelsim仿真结果如图3~图8所示。其中,为了减少仿真时间以及可以在较短时间内验证功能的正确性,将设计中的延时参数改小。

2

图3

3

图4

4

图5

5

图6

6

图7

7

图8

   最后,经过综合、实现、生成bit流文件以及下载到板子上,便可以在数码管上看到电子钟的效果。