数码管实现电子秒表

一.功能描述

  • 6位数码管左边两位显示分钟,中间两位显示秒,右边两位显示毫秒的百位和十位;按键key[0]实现暂停计时和启动计时;按键key[1]实现清零。

二.模块设计图

在这里插入图片描述

三.设计思路

3.1按键消抖模块传输消抖成功后的按键信号

3.2设计计数器

  • 10ms计时:计10ms
  • ms计数器:每计10ms,ms计数器+1,最大值计到99
  • sec计数器:ms计数器每计满一次,sec计数器+1,最大值到59
  • min计数器:sec计数器每计满一次,min计数器+1,最大值到59

3.3设计暂停

  • 设计flag信号,当flag为0时暂停计数,当flag为1时启动计数。每识别到key[0]时,flag反转。
    在这里插入图片描述

3.4设计清零

  • 在结束计数的条件做修改,当它计满或者按下key[1]的时候则计数器清零。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 后来我才想到,好像复位就可以实现清零的操作。。。

3.5其余设计同上文电子时钟设计大同小异,可以参考前文

四.代码

  • key_debounce.v
/**************************************功能介绍***********************************
Date	: 2023年8月1日14:45:24
Author	: Alegg xy.
Version	: 1.0
Description: 四位按键消抖模块
*********************************************************************************/
    
//---------<模块及端口声名>------------------------------------------------------
module key_debounce #(parameter   WIDTH = 4,
                      parameter   DELAY_20MS = 1000_000
)( 
    input				           clk		,
    input				           rst_n	,
    input		   [WIDTH-1:0]     key_in	,
    output         [WIDTH-1:0]     key_out   
);								 
//---------<参数定义>--------------------------------------------------------- 
    
    reg [WIDTH-1:0] key_out_r;
    //状态机参数定义
    localparam  IDLE         = 4'b0001,//空闲状态
                FILETER_DOWN = 4'b0010,//按键按下抖动状态
                HOLD_DOWN    = 4'b0100,//按下稳定按下状态
                FILTER_UP    = 4'b1000;//按键释放抖动状态
    
//---------<内部信号定义>-----------------------------------------------------
    reg 	[3:0]	        cstate     ;//现态
    reg	    [3:0]	        nstate     ;//次态

    reg     [WIDTH-1:0]     key_r0      ;//同步打拍
    reg     [WIDTH-1:0]     key_r1      ;
    reg     [WIDTH-1:0]     key_r2      ;
    wire    [WIDTH-1:0]     n_edge      ;//下降沿
    wire    [WIDTH-1:0]     p_edge      ;//上升沿  

    reg		[19:0]	        cnt_20ms	;//20ms计数器
    wire				    add_cnt_20ms;
    wire				    end_cnt_20ms;

    //状态转移条件定义
    wire            idle2filter_down        ;
    wire            fiter_down2hold_down    ;
    wire            hold_down2filter_up     ;
    wire            filter_up2idle          ;

//****************************************************************
//--cstate
//****************************************************************
    //第一段:时序逻辑描述状态转移
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            cstate <= IDLE;//复位的初始状态
        end 
        else begin 
            cstate <= nstate;
        end 
    end
    
    //第二段:组合逻辑描述状态转移规律和状态转移条件
    always @(*) begin
        case(cstate)
            IDLE         : begin 
                if(idle2filter_down)begin 
                    nstate = FILETER_DOWN;
                end
                else begin
                    nstate = cstate;
                    // state_n = IDLE;
                end
            end
            FILETER_DOWN : begin 
                if(fiter_down2hold_down)begin 
                    nstate = HOLD_DOWN;
                end
                else begin
                    nstate = cstate;
                end
            end
            HOLD_DOWN    : begin 
                if(hold_down2filter_up)begin 
                    nstate = FILTER_UP;
                end
                else begin
                    nstate = cstate;
                end
            end
            FILTER_UP    : begin 
                if(filter_up2idle)begin 
                    nstate = IDLE;
                end
                else begin
                    nstate = cstate;
                end
            end
            default : nstate = IDLE;
        endcase
    end

    assign idle2filter_down     = cstate == IDLE         && n_edge;
    assign fiter_down2hold_down = cstate == FILETER_DOWN && end_cnt_20ms;
    assign hold_down2filter_up  = cstate == HOLD_DOWN    && p_edge;
    assign filter_up2idle       = cstate == FILTER_UP    && end_cnt_20ms;
                
//****************************************************************
//--n_edge、p_edge 
//****************************************************************
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            key_r0 <= {WIDTH{1'b1}};
            key_r1 <= {WIDTH{1'b1}};
            key_r2 <= {WIDTH{1'b1}};
        end 
        else begin 
            key_r0 <= key_in;
            key_r1 <= key_r0;
            key_r2 <= key_r1;
        end 
    end      
          
    assign n_edge = ~key_r1 & key_r2;
    assign p_edge = ~key_r2 & key_r1;          
    
//****************************************************************
//--cnt_20ms
//****************************************************************
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_20ms <= 'd0;
        end 
        else if(add_cnt_20ms)begin 
            if(end_cnt_20ms)begin 
                cnt_20ms <= 'd0;
            end
            else begin 
                cnt_20ms <= cnt_20ms + 1'b1;
            end 
        end
    end 
    
    assign add_cnt_20ms = cstate == FILETER_DOWN || cstate == FILTER_UP;
    assign end_cnt_20ms = add_cnt_20ms && cnt_20ms == DELAY_20MS - 1;
    
//****************************************************************
//--key_out
//****************************************************************
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            key_out_r <= 'd0;
        end 
        else if(hold_down2filter_up)begin 
            key_out_r <= ~key_r2;
        end 
        else begin 
            key_out_r <= 'd0;
        end 
    end
    
    assign key_out = key_out_r;
endmodule
  • time_count.v
/**************************************功能介绍***********************************
Date	:2023年8月1日14:46:12 
Author	: Alegg xy.
Version	: 1.0
Description: 
*********************************************************************************/
    
//---------<模块及端口声名>------------------------------------------------------
module time_count( 
    input				clk		,
    input				rst_n	,
    input       [3:0]   key     ,

    output      [6:0]   ms      ,
    output      [5:0]   sec     ,
    output      [5:0]   min     
);								 
//---------<参数定义>--------------------------------------------------------- 
    parameter   MAX_10ms = 19'd500_000,
                MAX_ms = 7'd99,
                MAX_sec = 6'd59,
                MAX_min = 6'd59;

    reg         flag;//flag为0暂停,flag为1启动计数   
//---------<内部信号定义>-----------------------------------------------------
    
    //定义flag
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            flag <= 1'b0;
        end 
        else if(key[0])begin 
            flag <= ~flag;
        end 
        else begin 
            flag <= flag;
        end 
    end

    //10ms计数器
    reg			[18:0]	cnt_10ms	   	;
    wire				add_cnt_10ms	;
    wire				end_cnt_10ms	;
    
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_10ms <= 19'd0;
        end 
        else if(add_cnt_10ms)begin 
            if(end_cnt_10ms)begin 
                cnt_10ms <= 19'd0;
            end
            else begin 
                cnt_10ms <= cnt_10ms + 1'd1;
            end 
        end
    end 
    
    assign add_cnt_10ms = flag;
    assign end_cnt_10ms = (add_cnt_10ms && cnt_10ms == MAX_10ms - 1'd1) || key[1];
    
    //ms计数器,每计满10ms,ms位就加一,最大值为99
    reg			[6:0]	cnt_ms	   	;
    wire				add_cnt_ms	;
    wire				end_cnt_ms	;
    
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_ms <= 7'd0;
        end 
        else if(add_cnt_ms)begin 
            if(end_cnt_ms)begin 
                cnt_ms <= 7'd0;
            end
            else begin 
                cnt_ms <= cnt_ms + 1'd1;
            end 
        end
    end 
    
    assign add_cnt_ms = end_cnt_10ms;
    assign end_cnt_ms = (add_cnt_ms && cnt_ms == MAX_ms) || key[1];
    
    
    //sec计数器,每计满100个10ms,sec位就加一,最大值为59
    reg			[5:0]	cnt_sec	   	;
    wire				add_cnt_sec	;
    wire				end_cnt_sec	;
    
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_sec <= 6'd0;
        end 
        else if(add_cnt_sec)begin 
            if(end_cnt_sec)begin 
                cnt_sec <= 6'd0;
            end
            else begin 
                cnt_sec <= cnt_sec + 1'd1;
            end 
        end
    end 
    
    assign add_cnt_sec = end_cnt_ms;
    assign end_cnt_sec = (add_cnt_sec && cnt_sec == MAX_sec) || key[1];

    //min计数器,每计满60s,min位就加一,最大值为59
    reg			[5:0]	cnt_min	   	;
    wire				add_cnt_min	;
    wire				end_cnt_min	;
    
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_min <= 6'd0;
        end 
        else if(add_cnt_min)begin 
            if(end_cnt_min)begin 
                cnt_min <= 6'd0;
            end
            else begin 
                cnt_min <= cnt_min + 1'd1;
            end 
        end
    end 
    
    assign add_cnt_min = end_cnt_sec;
    assign end_cnt_min = (add_cnt_min && cnt_min == MAX_min) || key[1];
    
    assign ms = cnt_ms;   
    assign sec = cnt_sec;
    assign min = cnt_min;

endmodule
  • seg_dirver.v
/**************************************功能介绍***********************************
Date	:2023年8月1日15:21:33 
Author	: Alegg xy.
Version	: 1.0
Description: 
*********************************************************************************/
    
//---------<模块及端口声名>------------------------------------------------------
module seg_dirver( 
    input				clk		,
    input				rst_n	,
    input		[6:0]	ms		,
    input		[5:0]	sec		,
    input		[4:0]	min	    ,
    output	reg	[5:0]	sel     ,
    output  reg [7:0]   seg	     
);								 
//---------<参数定义>--------------------------------------------------------- 
    parameter MAX_1ms = 16'd50000;
    reg [3:0]   data;//存取该位数值    
//---------<内部信号定义>-----------------------------------------------------
    //1ms计数器
    reg			[15:0]	cnt_1ms	   	;
    wire				add_cnt_1ms	;
    wire				end_cnt_1ms	;
    
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_1ms <= 16'd0;
        end 
        else if(add_cnt_1ms)begin 
            if(end_cnt_1ms)begin 
                cnt_1ms <= 16'd0;
            end
            else begin 
                cnt_1ms <= cnt_1ms + 1'd1;
            end 
        end
    end 
    
    assign add_cnt_1ms = 1'b1;
    assign end_cnt_1ms = add_cnt_1ms && cnt_1ms == MAX_1ms - 1'd1;
    
    //数码管位选
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            sel <= 6'b111_110;//循环移位实现时,需要给位选赋初值
        end 
        else if(end_cnt_1ms)begin 
            sel <= {sel[4:0],sel[5]};//循环左移
        end 
    end
    
    always @(*)begin 
        case (sel)
            6'b111_110 :data = min / 10;
            6'b111_101 :data = min % 10;
            6'b111_011 :data = sec / 10;
            6'b110_111 :data = sec % 10;
            6'b101_111 :data = ms / 10;
            6'b011_111 :data = ms % 10; 
            default: data = 0;
        endcase
    end

    always @(*)begin 
        case (data)
            4'd0:seg = 8'b1100_0000;
            4'd1:seg = 8'b1111_1001;
            4'd2:seg = 8'b1010_0100;
            4'd3:seg = 8'b1011_0000;
            4'd4:seg = 8'b1001_1001;
            4'd5:seg = 8'b1001_0010;
            4'd6:seg = 8'b1000_0010;
            4'd7:seg = 8'b1111_1000;
            4'd8:seg = 8'b1000_0000;
            4'd9:seg = 8'b1001_0000;
            default: seg = 8'b1100_0000;
        endcase
    end
endmodule
  • top_seg_dirver.v
/**************************************功能介绍***********************************
Date	: 
Author	: Alegg xy.
Version	: 
Description: 
*********************************************************************************/
    
//---------<模块及端口声名>------------------------------------------------------
module top_seg_dirver( 
    input				clk		,
    input				rst_n	,
    input		[3:0]	key_in	,
    output		[5:0]	sel	    ,
    output		[7:0]	seg     	
);								 
//---------<参数定义>--------------------------------------------------------- 
    wire [3:0]  key_link;
    wire [6:0]  ms_link;
    wire [5:0]  sec_link;
    wire [5:0]  min_link;
//---------<内部信号定义>-----------------------------------------------------
    key_debounce u_key_debounce(
        .clk		(clk),
        .rst_n	    (rst_n),
        .key_in	    (key_in),
        .key_out    (key_link)  
    );
    time_count u_time_count(
        .clk		(clk),
        .rst_n      (rst_n),	
        .key        (key_link),   
        .ms         (ms_link),   
        .sec        (sec_link),   
        .min        (min_link) 
    );
    seg_dirver u_seg_dirver(
        .clk		(clk),
        .rst_n      (rst_n),
        .ms		    (ms_link),
        .sec		(sec_link),
        .min	    (min_link),
        .sel        (sel),
        .seg	    (seg)    
    );
    
endmodule
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现电子时钟需要以下步骤: 1. 确定使用的数码管型号,并连接到89c52单片机的端口上。 2. 编写程序,实现时钟的计时和显示功能。可以使用定时器来计时,并在定时器中断中更新时钟的时间和数码管的显示。 3. 实现时钟的校准功能。可以在程序中加入按键检测,当按下校准键时,时钟停止计时,等待用户输入正确的时间,然后再重新开始计时。 4. 添加其他功能,例如闹钟、定时器等。 下面是一个简单的示例程序,用89c52单片机实现电子时钟的功能: ```c #include <reg52.h> #include <intrins.h> // 数码管引脚定义 #define DIG1 P1 #define DIG2 P1 #define DIG3 P1 #define DIG4 P1 #define SEG P0 // 定时器计数器初值 #define T0RH 0xFF #define T0RL 0x00 // 定义时间结构体 struct Time { unsigned char hour; unsigned char minute; unsigned char second; }; // 初始化定时器 void initTimer() { TMOD |= 0x01; // 定时器0工作在模式1 TH0 = T0RH; // 定时器计数器初值 TL0 = T0RL; TR0 = 1; // 启动定时器0 ET0 = 1; // 允许定时器0中断 EA = 1; // 开启全局中断 } // 定时器0中断服务函数 void timer0() interrupt 1 { static unsigned char count = 0; static struct Time time = {0, 0, 0}; count++; if (count == 20) { // 每1秒更新一次时间 count = 0; time.second++; if (time.second == 60) { time.second = 0; time.minute++; if (time.minute == 60) { time.minute = 0; time.hour++; if (time.hour == 24) { time.hour = 0; } } } } // 显示时间 DIG1 = 0x7F; // 显示小时的十位 SEG = time.hour / 10; _nop_(); DIG1 = 0xFF; DIG2 = 0xBF; // 显示小时的个位 SEG = time.hour % 10; _nop_(); DIG2 = 0xFF; DIG3 = 0xDF; // 显示分钟的十位 SEG = time.minute / 10; _nop_(); DIG3 = 0xFF; DIG4 = 0xEF; // 显示分钟的个位 SEG = time.minute % 10; _nop_(); DIG4 = 0xFF; } // 主函数 void main() { initTimer(); while (1); } ``` 在上面的程序中,数码管的引脚分别连接到单片机的P1和P0端口上,定时器0工作在模式1,每经过50ms就会产生一个中断,因此计数器初值为65536-5000=60536,即0xEE38。在定时器中断服务函数中,每经过20次中断(1秒),就会更新一次时间并显示到数码管上。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值