FPGA学习之 状态机实现数码管的数字时钟

Posted 满足没有

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FPGA学习之 状态机实现数码管的数字时钟相关的知识,希望对你有一定的参考价值。

FPGA学习之 状态机实现数字时钟

开发板型号:EP4CE6F17C8
六位数码管原理图:

由图可知,数码管段选和片选均为低电平有效。
由于人眼的视觉残留,我们控制一定频率对每一位数码管进行刷新,就能实现显示6位数字时钟。

项目功能描述
1.实现数字时钟自动计时,设置6个计数器,分别计数时分秒的十位和个位
2.时间设置功能
3.闹钟设置
4.按键实现模式切换(主要是 初始计时、时间设置、闹钟设置)
5.蜂鸣器发声(设置的闹钟时间到来时,蜂鸣器按一定频率播放”两只老虎“)

代码:
顶层模块:

module top(
    input 			clk		,
    input 			rst_n	,
    input   [2:0]   key_in  ,
    output  [5:0]   sel     ,
    output  [7:0]   dig     ,
    output          beep
);

wire [23:0] time_data;//显示数据
wire [2:0]  key_out;//按键消抖输出
wire alarm_clk_flag;//闹钟响标志
wire [5:0] shan_shuo;

//数字时钟显示驱动
seg_driver u_seg_driver(
    .clk     (clk       ),
    .rst_n   (rst_n     ),
    .din     (time_data ),
    .sel     (sel       ),
    .dig     (dig       ),
    .shan_shuo(shan_shuo)
);
//按键消抖
fsm_key_debounce # (.KEY_W(3)) u_fsm_key_debounce(
    .clk		(clk        ),
    .rst_n	    (rst_n      ),
    .key_in	    (key_in     ),
    .key_out    (key_out    )	 
);
//控制模块
control u_control(
    .clk                    (clk                ),
    .rst_n                  (rst_n              ),
    .key                    (key_out            ),
    .time_data              (time_data          ),
    .alarm_clk_flag         (alarm_clk_flag     ),
    .shan_shuo              (shan_shuo)
);


//蜂鸣器
beep u_beep(
    .clk                (clk                ),
    .rst_n              (rst_n              ),
    .alarm_clk_flag     (alarm_clk_flag     ),
    .beep               (beep               )
);

endmodule

按键消抖模块

module fsm_key_debounce # (parameter KEY_W = 3,TIME_20MS = 1_000_000)(
    input 			            clk		,
    input 			            rst_n	,
    input 		[KEY_W - 1:0]	key_in	,

    output 		[KEY_W - 1:0]	key_out	 
);
//参数定义
localparam IDLE  = 4'b0001;//初始状态 
localparam DOWN  = 4'b0010;//按键按下抖动
localparam HOLD  = 4'b0100;//按键按下后稳定
localparam UP    = 4'b1000;//按键上升抖动
//信号定义
reg [3:0] state_c;//现态
reg [3:0] state_n;//次态

//状态转移条件定义
wire idle2down;
wire down2idle;
wire down2hold;
wire hold2up  ;
wire up2idle  ;

reg [KEY_W - 1:0] key_r0;//同步
reg [KEY_W - 1:0] key_r1;//打拍
wire [KEY_W - 1:0] nedge;//下降沿
wire [KEY_W - 1:0] pedge;//上升沿

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

reg [KEY_W - 1:0] key_out_r;//输出寄存

always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        state_c <= IDLE;
    end
    else begin
        state_c <= state_n;
    end
end

always@(*)begin
    case(state_c)
        IDLE:begin
            if(idle2down)begin
                state_n = DOWN;
            end
            else begin
                state_n = state_c;
            end
        end
        DOWN:begin
            if(down2idle)begin
                state_n = IDLE;
            end
            else if(down2hold)begin
                state_n = HOLD;
            end
            else begin
                state_n = state_c;
            end
        end
        HOLD:begin
            if(hold2up)begin
                state_n = UP;
            end
            else begin
                state_n = state_c;
            end
        end
        UP:begin
            if(up2idle)begin
                state_n = IDLE;
            end
            else begin
                state_n = state_c;
            end
        end
        default:state_n = state_c;
    endcase
end

assign idle2down = (state_c == IDLE) && nedge;//检测到下降沿
assign down2idle = (state_c == DOWN) && (pedge&& end_cnt_20ms);//计时未到20ms时且出现上升沿表示按键意外抖动,回到初始态
assign down2hold = (state_c == DOWN) && (~pedge && end_cnt_20ms);//计时到20ms时没有出现上升沿标志按键按下后保持稳定
assign hold2up   = (state_c == HOLD) && (pedge);//检测到上升沿跳转到上升态
assign up2idle   = (state_c == UP)   && end_cnt_20ms;//计数器计数到20ms跳转到初始态
//20ms计数器
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt_20ms <= 0;
    end
    else if(add_cnt_20ms)begin
        if(end_cnt_20ms)begin
            cnt_20ms <= 0;
        end
        else begin
            cnt_20ms <= cnt_20ms + 1'b1;
        end
    end
end
assign add_cnt_20ms = state_c == DOWN || state_c == UP;//当按键按下或上弹时开始计数
assign end_cnt_20ms = add_cnt_20ms && ((cnt_20ms == TIME_20MS - 1) || pedge);//当计数到最大值或检测到上升沿计数器清零



//同步打拍
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        key_r0 <= KEY_W1'b1;
        key_r1 <= KEY_W1'b1;
    end
    else begin
        key_r0 <= key_in;
        key_r1 <= key_r0;
    end
end

assign nedge = ~key_r0 &  key_r1;//检测下降沿
assign pedge =  key_r0 & ~key_r1;//检测上升沿

//按键赋值
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        key_out_r <= KEY_W1'b0;
    end
    else if(state_c == HOLD && hold2up)begin
        key_out_r <= ~key_r1;
    end
    else begin
        key_out_r <= KEY_W1'b0;
    end
end
assign key_out = key_out_r;

endmodule

蜂鸣器模块

module beep(
    input       clk                ,
    input       rst_n              ,
    input       alarm_clk_flag     ,
    output      beep               
);

parameter M1 = 17'd95600;
parameter M2 = 17'd85150;
parameter M3 = 17'd75850;
parameter M4 = 17'd71600;
parameter M5 = 17'd63750;
parameter M6 = 17'd56800;
parameter M7 = 17'd50600;

reg beep_r;
reg alarm_clk_flag_r;

reg [16:0] cnt0;//音符周期计数器
wire add_cnt0;
wire end_cnt0;

reg [8:0] cnt1;//音符重复次数计数器
wire add_cnt1;
wire end_cnt1;

reg [4:0] cnt2;//音符总次数
wire add_cnt2;
wire end_cnt2;

reg [1:0] cnt3;//音乐时间计数器 7次完结
wire add_cnt3;
wire end_cnt3;

reg 	[16:0] preset_note;//预设音符周期数
wire 	[16:0] preset_duty;//占空比

always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        alarm_clk_flag_r <= 1'b0;
    end
    else if(alarm_clk_flag)begin
        alarm_clk_flag_r <= 1'b1;
    end
    else if(cnt3 == 1)begin
        alarm_clk_flag_r <= 1'b0;
    end
end

//音符周期计数
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt0 <= 17'b0;
    end
    else if(add_cnt0)begin
        if(end_cnt0)begin
            cnt0 <= 17'b0;
        end
        else begin
            cnt0 <= cnt0 +1'b1;
        end
    end
end
assign add_cnt0 = 1'b1;
assign end_cnt0 = add_cnt0 && (cnt0 == preset_note - 1);

//音符重复次数
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt1 <= 9'b0;
    end
    else if(add_cnt1)begin
        if(end_cnt1)begin
            cnt1 <= 9'b0;
        end
        else begin
            cnt1 <= cnt1 +1'b1;
        end
    end
end
assign add_cnt1 = end_cnt0;
assign end_cnt1 = add_cnt1 && (cnt1 == 299);

//音符总次数
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt2 <= 5'b0;
    end
    else if(add_cnt2)begin
        if(end_cnt2)begin
            cnt2 <= 5'b0;
        end
        else begin
            cnt2 <= cnt2 +1'b1;
        end
    end
end
assign add_cnt2 = end_cnt1;
assign end_cnt2 = add_cnt2 && (cnt2 == 31);

//音乐时间计数器 响3次
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt3 <= 0;
    end
    else if(add_cnt3)begin
        if(end_cnt3)begin
            cnt3 <= 0;
        end
        else begin
            cnt3 <= cnt3 + 1;
        end
    end
end
assign add_cnt3 = end_cnt2;
assign end_cnt3 = add_cnt3 && (cnt3 == 4 - 1);

//给音符周期赋值
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        preset_note <= 17'b0;
    end
    else begin
        case(cnt2)
            6'd0    :   preset_note <= M1;
            6'd1    :   preset_note <= M2;
            6'd2    :   preset_note <= M3;
            6'd3    :   preset_note <= M1;
            6'd4    :   preset_note <= M1;
            6'd5    :   preset_note <= M2;
            6'd6    :   preset_note <= M3;
            6'd7    :   preset_note <= M1;
            6'd8    :   preset_note <= M3;
            6'd9    :   preset_note <= M4;
            6'd10   :   preset_note <= M5;
            6'd11   :   preset_note <= M3;
            6'd12   :   preset_note <= M4;
            6'd13   :   preset_note <= M5;
            6'd14   :   preset_note <= M5;
            6'd15   :   preset_note <= M6;
            6'd16   :   preset_note <= M5;
            6'd17   :   preset_note <= M4;
            6'd18   :   preset_note <= M3;
            6'd19   :   preset_note <= M1;
            6'd20   :   preset_note <= M5;
            6'd21   :   preset_note <= M6;
            6'd22   :   preset_note <= M5;
            6'd23   :   preset_note <= M4;
            6'd24   :   preset_note <= M3;
            6'd25   :   preset_note <= M1;
            6'd26   :   preset_note <= M2;
            6'd27   :   preset_note <= M5;
            6'd28   :   preset_note <= M1;
            6'd29   :   preset_note <= M2;
            6'd30   :   preset_note <= M5;
            6'd31   :   preset_note <= M1;
            default :   preset_note <= M1;
        endcase
    end
end

//给蜂鸣器赋值,并设定占空比
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        beep_r <= 1'b1;
    end
    else if(cnt3 == 2'd1)begin
        beep_r <= 1'b1;
    end
    else if(alarm_clk_flag_r)begin
			if(cnt0 <= preset_duty)begin
				beep_r <= 1'b0;
			end
            else begin
                beep_r <= 1'b1;
            end
	 end
    else begin     
        beep_r <= 1'b1;
    end
end
assign preset_duty = preset_note >> 1;
assign beep = beep_r;

endmodule

数字时钟显示:

module seg_driver(
    input               clk     ,
    input               rst_n   ,
    input      [23:0]   din     ,
    output reg [5:0]    sel     ,
    output reg [7:0]    dig     ,     
    input      [5:0]    shan_shuo
);

parameter TIME_10MS = 25_000;
parameter TIME_500MS = 25_000_000;
//共阳极数码管  低电平点亮
    localparam  ZERO     = 7'b100_0000,
                ONE      = 7'b111_1001,
                TWO      = 7'b010_0100,   
                THREE    = 7'b011_0000,   
                FOUR     = 7'b001_1001,
                FIVE     = 7'b001_0010, 
                SIX      = 7'b000_0010, 
                SEVEN    = 7'b111_1000, 
                EIGHT    = 7'b000_0000,
                NINE     = 7'b001_0000,
                OFF      = 7'b111_1111;

//信号定义

reg dot;

reg [19:0] cnt;//扫描计数器
wire add_cnt;
wire end_cnt;

reg [3:0] data;//根据片选信号选择高四位还是第四位



//刷新频率计数器
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt <= 0;
    end
    else if(add_cnt)begin
        if(end_cnt)begin
            cnt <= 0;
        end
        else begin
           cnt <= cnt + 1'b1;
        end
    end
end
assign add_cnt = 1'b1;
assign end_cnt = add_cnt && (cnt == TIME_10MS - 1);
//片选信号选择
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        sel <= 6'b111_110;;
    end
    else if(end_cnt)begin
        sel <= sel[0],sel[5:1];
	 end
end

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        data <= 4'b0;
    end
    else begin
       case(sel)
            6'b111_110: begin data <= shan_shuo[0]?  7'd10:din [3:0]   ;dot <=  1'b1;end
            6'b111_101: begin data <= shan_shuo[1]?  7'd10:din [7:4]   ;dot <=  1'b1;end
            6'b111_011: begin data <= shan_shuo[2]?  7'd10:din [11:8]  ;dot <=  1'b0;end
            6'b110_111: begin data <= shan_shuo[3]?  7'd10:din [15:12] ;dot <=  1'b1;end
            6'b101_111: begin data <= shan_shuo[4]?  7'd10:din [19:16] ;dot <=  1'b0;end
            6'b011_111: begin data <= shan_shuo[5]?  7'd10:din [23:20] ;dot <=  1'b1;end
            default: data <= din[3:0];
        endcase 
    end  
end

always @(*) begin
    case(data)
        4'd0:dig = dot,ZERO    ;
        4'd1:dig = dot,ONE     ;
        4'd2:dig = dot,TWO     ;
        4'd3:dig = dot,THREE   ;
        4'd4:dig = dot,FOUR    ;
        4'd5:dig = dot,FIVE    ;
        4'd6:dig = dot,SIX     ;
        4'd7:dig = dot,SEVEN   ;
        4'd8:dig = dot,EIGHT   ;
        4'd9:dig = dot,NINE    ;
        4'd10:dig = dot,OFF;
        default: dig = dot,ZERO;
    endcase
end
endmodule

控制模块
控制模块主要设计了3组寄存器,分别寄存 自动计数的时间、手动设置的时间、闹钟时间

自动计数就不说了。
设置时间:key[0]进入设置时间,再次按下退出,并将设置的时间赋值给自动计数寄存器。key[2]控制选中时钟位闪烁,key[1]对选中的位进行加数。
闹钟时间:key[1]进入设置时间,再次按下退出,并保存时间。key[2]进行选位,key[0]控制加数。

module control(
    input               clk                    ,
    input               rst_n                  ,
    input   [2:0]       key                    ,
    output  [23:0]      time_data              ,
    output              alarm_clk_flag         ,
    output  [5:0]       shan_shuo
);
localparam TIME_1S = 50_000_000;
localparam TIME_200MS = 10_000_000;
localparam IDLE = 3'b001;
localparam SET_TIME  = 3'b010;
localparam SET_ALARM  = 3'b100;

wire idle2set_time;
wire idle2set_alarm;
wire set_time2idle;
wire set_alarm2idle;
reg [2:0] state_c;
reg [2:0] state_n;

reg [5:0] shanshuo_r;//闪烁标志,选中位0/1不停变换

reg set_time_flag;//设置时间标志
reg set_alarm_flag;//设置闹钟标志

reg [23:0] time_data_r;

reg [24:0]  cnt_200ms;  
wire add_cnt_200ms;
wire end_cnt_200ms;

reg [25:0] cnt_1s;//1s计数器
wire add_cnt_1s;
wire end_cnt_1s;

reg [3:0] cnt0;//s个位计数器
wire add_cnt0;
wire end_cnt0;

reg [3:0] cnt1;//s十位计数器
wire add_cnt1;
wire end_cnt1;

reg [3:0] cnt2;//m个位计数器
wire add_cnt2;
wire end_cnt2;

reg [3:0] cnt3;//m十位计数器
wire add_cnt3;
wire end_cnt3;

reg [3:0] cnt4;//h个位计数器
wire add_cnt4;
wire end_cnt4;

reg [3:0] cnt5;//h十位计数器
wire add_cnt5;
wire end_cnt5;


//设置时间寄存器
reg [3:0] set_time_cnt0;//s个位计数器

reg [3:0] set_time_cnt1;//s十位计数器

reg [3:0] set_time_cnt2;//m个位计数器

reg [3:0] set_time_cnt3;//m十位计数器

reg [3:0] set_time_cnt4;//h个位计数器

reg [3:0] set_time_cnt5;//h十位计数器

reg [2:0] key_sel_time;//设置时间片选记录
wire add_key_sel_time;
wire end_key_sel_time;

reg [2:0] key_sel_alarm;//设置闹钟片选记录
wire add_key_sel_alarm;
wire end_key_sel_alarm;

//设置闹钟寄存器
reg [23:0] alarm;//保存闹钟数据
reg [3:0] set_alarm_cnt0;//s个位计数器

reg [3:0] set_alarm_cnt1;//s十位计数器

reg [3:0] set_alarm_cnt2;//m个位计数器

reg [3:0] set_alarm_cnt3;//m十位计数器

reg [3:0] set_alarm_cnt4;//h个位计数器

reg [3:0] set_alarm_cnt5;//h十位计数器


always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        state_c <= IDLE;
    end
    else begin
        state_c <= state_n;
    end
end

always @(*) begin
    case(state_c)
        IDLE:begin
            if(idle2set_time)begin
                state_n <= SET_TIME;
            end
            else if(idle2set_alarm)begin
                state_n <= SET_ALARM;
            end
            else begin
                state_n <= state_c;
            end
        end
        SET_TIME:begin
            if(set_time2idle)begin
                state_n <= IDLE;
            end
            else begin
                state_n <= state_c;
            end
        end
        SET_ALARM:begin
            if(set_alarm2idle)begin
                state_n <= IDLE;
            end
            else begin
                state_n <= state_c;
            end
        end
        default:state_n = state_c;
    endcase
end

assign idle2set_time    = (state_c == IDLE)     && (set_time_flag);
assign idle2set_alarm   = (state_c == IDLE)     && (set_alarm_flag);
assign set_time2idle    = (state_c == SET_TIME) && (!set_time_flag);
assign set_alarm2idle   = (state_c == SET_ALARM)&& (!set_alarm_flag);


//200ms计数器
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt_200ms <= 25'b0;
    end
    else if(add_cnt_200ms)begin
        if(end_cnt_200ms)begin
            cnt_200ms <= 26'b0;
        end
        else begin
            cnt_200ms <= cnt_200ms + 1'b1;
        end
    end
end
assign add_cnt_200ms = 1'b1;
assign end_cnt_200ms = add_cnt_200ms && (cnt_200ms == TIME_200MS - 1);



//1s计数器
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt_1s <= 26'b0;
    end
    else if(add_cnt_1s)begin
        if(end_cnt_1s)begin
            cnt_1s <= 26'b0;
        end
        else begin
            cnt_1s <= cnt_1s + 1'b1;
        end
    end
end
assign add_cnt_1s = 1'b1;
assign end_cnt_1s = add_cnt_1s && (cnt_1s == TIME_1S - 1);
//s个位计数器
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt0 <= 4'b0;
    end
    else if(add_cnt0)begin
        if(end_cnt0)begin
            cnt0 <= 4'b0;
        end
        else begin
            cnt0 <= cnt0 + 1'b1;
        end
    end
    else if(set_time2idle)begin
        cnt0 <= set_time_cnt0;
    end
end
assign add_cnt0 = end_cnt_1s;
assign end_cnt0 = add_cnt0 && (cnt0 == 10 - 1);
//s十位计数器
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt1 <= 4'b0;
    end
    else if(add_cnt1)begin
        if(end_cnt1)begin
            cnt1 <= 4'b0;
        end
        else begin
            cnt1 <= cnt1 + 1'b1;
        end
    end
    else if(set_time2idle)begin
        cnt1 <= set_time_cnt1;
    end
end
assign add_cnt1 = end_cnt0;
assign end_cnt1 = add_cnt1 && (cnt1 == 6 - 1);
//m个位计数器
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt2 <= 4'b0;
    end
    else if(add_cnt2)begin
        if(end_cnt2)begin
            cnt2 <= 4'b0;
        end
        else begin
            cnt2 <= cnt2 + 1'b1;
        end
    end
    else if(set_time2idle)begin
        cnt2 <= set_time_cnt2;
    end
end
assign add_cnt2 = end_cnt1;
assign end_cnt2 = add_cnt2 && (cnt2 == 10 - 1);
//m十位计数器
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt3 <= 4'b0;
    end
    else if(add_cnt3)begin
        if(end_cnt3)begin
            cnt3 <= 4'b0;
        end
        else begin
            cnt3 <= cnt3 + 1'b1;
        end
    end
    else if(set_time2idle)begin
        cnt3 <= set_time_cnt3;
    end
end
assign add_cnt3 = end_cnt2;
assign end_cnt3 = add_cnt3 && (cnt3 == 6 - 1);
//h个位计数器
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt4 <= 4'b0;
    end
    else if(add_cnt4)begin
        if(end_cnt4)begin
            cnt4 <= 4'b0;
        end
        else begin
            cnt4 <= cnt4 + 1'b1;
        end
    end
    else if(set_time2idle)begin
        cnt4 <= set_time_cnt4;
    end
end
assign add_cnt4 = end_cnt3;
assign end_cnt4 = add_cnt4 && (cnt4 == (cnt5 == 2)?(4 - 1):(10 - 1));
//h十位计数器
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt5 <= 4'b0;
    end
    else if(add_cnt5)begin
        if(end_cnt5)begin
            cnt5 <= 4'b0;
        end
        else begin
            cnt5 <= cnt5 + 1'b1;
        end
    end
    else if(set_time2idle)begin
        cnt5 <= set_time_cnt5;
    end
end
assign add_cnt5 = end_cnt4;
assign end_cnt5 = add_cnt5 && (cnt5 == 2);

//选中位反转
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        shanshuo_r <= 0;
    end
    else if(state_c == IDLE)begin
        shanshuo_r <= 6'b000_000;
    end
    else if(state_c == SET_TIME)begin
        case(key_sel_time)
            3'd0:begin
                shanshuo_r[5] <= 0;
                if(end_cnt_200ms)begin
                    shanshuo_r[0] <= ~shanshuo_r[0];
                end
            end
            3'd1:begin
                shanshuo_r[0] <= 0;
                if(end_cnt_200ms)begin
                    shanshuo_r[1] <= ~shanshuo_r[1];
                end
            end
            3'd2:begin
                shanshuo_r[1] <= 0;
                if(end_cnt_200ms)begin
                    shanshuo_r[2] <= ~shanshuo_r[2];
                end
            end
            3'd3:begin
                shanshuo_r[2] <= 0;
                if(end_cnt_200ms)begin
                    shanshuo_r[3] <= ~shanshuo_r[3];
                end
            end
            3'd4:begin
                shanshuo_r[3] <= 0;
                if(end_cnt_200ms)begin
                    shanshuo_r[4] <= ~shanshuo_r[4];
                end
            end
            3'd5:begin
                shanshuo_r[4] <= 0;
                if(end_cnt_200ms)begin
                    shanshuo_r[5] <= ~shanshuo_r[5];
                end
            end
            default:shanshuo_r <= shanshuo_r;
        endcase
    end
    else if(state_c == SET_ALARM)begin
        case(key_sel_alarm)
            3'd0:begin
                shanshuo_r[5] <= 0;
                if(end_cnt_200ms)begin
                    shanshuo_r[0] <= ~shanshuo_r[0];
                end
            end
            3'd1:begin
                shanshuo_r[0] <= 0;
                if(end_cnt_200ms)begin
                    shanshuo_r[1] <= ~shanshuo_r[1];
                end
            end
            3'd2:begin
                shanshuo_r[1] <= 0;
                if(end_cnt_200ms)begin
                    shanshuo_r[2] <= ~shanshuo_r[2];
                end
            end
            3'd3:begin
                shanshuo_r[2] <= 0;
                if(end_cnt_200ms)begin
                    shanshuo_r[3] <= ~shanshuo_r[3];
                end
            end
            3'd4:begin
                shanshuo_r[3] <= 0;
                if(end_cnt_200ms)begin
                    shanshuo_r[4] <= ~shanshuo_r[4];
                end
            end
            3'd5:begin
                shanshuo_r[4] <= 0;
                if(end_cnt_200ms)begin
                    shanshuo_r[5] <= ~shanshuo_r[5];
                end
            end
            default:shanshuo_r <= shanshuo_r;
        endcase
    end
end
assign shan_shuo = shanshuo_r;

//-----------------------------设置时间------------------------------------------------------------------------

//时间设置标志
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        set_time_flag <= 1'b0;
    end
    else if((state_c == IDLE) && key[0])begin
        set_time_flag <= 1'b1;
    end
    else if((state_c == SET_TIME) && key[0])begin
        set_time_flag <= 1'b0;
    end
end
//时间设置寄存器赋值
//s个位
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        set_time_cnt0 <= 0;
    end
    else if(idle2set_time)begin
        set_time_cnt0 <= cnt0;
    end
    else if((state_c == SET_TIME) && key[1] && key_sel_time == 3'd0)begin
        if(set_time_cnt0 == 10 -1)begin
            set_time_cnt0 <= 0;
        end
        else begin
            set_time_cnt0 <= set_time_cnt0 + 1;
        end     
    end
end
//s十位
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        set_time_cnt1 <= 0;
    end
    else if(idle2set_time)begin
        set_time_cnt1 <= cnt1;
    end
    else if((state_c == SET_TIME) && key[1] && key_sel_time == 3'd1)begin
        if(set_time_cnt1 == 6 - 1)begin
            set_time_cnt1 <= 0;
        end
        else begin
            set_time_cnt1 <= set_time_cnt1 + 1;
        end
    end
end
//m个位
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        set_time_cnt2 <= 0;
    end
    else if(idle2set_time)begin
        set_time_cnt2 <= cnt2;
    end
    else if((state_c == SET_TIME) && key[1] && key_sel_time == 3'd2)begin
        if(set_time_cnt2 == 10 - 1)begin
            set_time_cnt2 <= 0;
        end
        else begin
            set_time_cnt2 <= set_time_cnt2 + 1;
        end   
    end
end
//m十位
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        set_time_cnt3 <= 0;
    end
    else if(idle2set_time)begin
        set_time_cnt3 <= cnt3;
    end
    else if((state_c == SET_TIME) && key[1] && key_sel_time == 3'd3)begin
        if(set_time_cnt3 == 6 - 1)begin
            set_time_cnt3 <= 0;
        end
        else begin
            set_time_cnt3 <= set_time_cnt3 + 1;
        end 
    end
end
//h个位
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        set_time_cnt4 <= 0;
    end
    else if(idle2set_time)begin
        set_time_cnt4 <= cnt4;
    end
    else if((state_c == SET_TIME) && key[1] && key_sel_time == 3'd4)begin
        if(set_time_cnt4 == ((set_time_cnt5 == 2)?(4 - 1):(10 - 1)))begin
            set_time_cnt4 <= 0;
        end
        else begin
            set_time_cnt4 <= set_time_cnt4 + 1;
        end  
    end
end
//h十位
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        set_time_cnt5 <= 0;
    end
    else if(idle2set_time)begin
        set_time_cnt5 <= cnt5;
    end
    else if((state_c == SET_TIME) && key[1] && key_sel_time == 3'd5)begin
        if(set_time_cnt5 == (set_time_cnt4>= 4 ? 1 : 2))begin
            set_time_cnt5 <= 0;
        end
        else begin
            set_time_cnt5 <= set_time_cnt5 + 1;
        end 
    end
end
//设置时间片选计数器
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        key_sel_time <= 0;
    end
    else if(add_key_sel_time)begin
        if(end_key_sel_time)begin
            key_sel_time <= 0;
        end
        else begin
            key_sel_time <= key_sel_time + 1;
        end
    end
end
assign add_key_sel_time = key[2] && (state_c == SET_TIME);
assign end_key_sel_time = add_key_sel_time && (key_sel_time == 6 - 1);


//---------------------------设置闹钟------------------------------------------------------------------

//闹钟设置标志
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        set_alarm_flag <= 1'b0;
    end
    else if((state_c == IDLE) && key[1])begin
        set_alarm_flag <= 1'b1;
    end
    else if((state_c == SET_ALARM) && key[1])begin
        set_alarm_flag <= 1'b0;
    end
end

//设置闹钟片选计数器
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        key_sel_alarm <= 0;
    end
    else if(add_key_sel_alarm)begin
        if(end_key_sel_alarm)begin
            key_sel_alarm <= 0;
        end
        else begin
            key_sel_alarm <= key_sel_alarm + 1;
        end
    end
end
assign add_key_sel_alarm = key[2] && (state_c == SET_ALARM);
assign end_key_sel_alarm = add_key_sel_alarm && (key_sel_alarm == 6 - 1);
//s个位
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        set_alarm_cnt0 <= 0;
    end
    else if(idle2set_alarm)begin
        set_alarm_cnt0 <= cnt0;
    end
    else if((state_c == SET_ALARM) && key[0] && key_sel_alarm == 3'd0)begin
        if(set_alarm_cnt0 == 10 -1)begin
            set_alarm_cnt0 <= 0;
        end
        else begin
            set_alarm_cnt0 <= set_alarm_cnt0 + 1;
        end 
    end
end
//s十位
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        set_alarm_cnt1 <= 0;
    end
    else if(idle2set_alarm)begin
        set_alarm_cnt1 <= cnt1;
    end
    else if((state_c == SET_ALARM) && key[0] && key_sel_alarm == 3'd1)begin
        if(set_alarm_cnt1 == 6 -1)begin
            set_alarm_cnt1 <= 0;
        end
        else begin
            set_alarm_cnt1 <= set_alarm_cnt1 + 1;
        end  
    end
end
//m个位
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        set_alarm_cnt2 <= 0;
    end
    else if(idle2set_alarm)begin
        set_alarm_cnt2 <= cnt2;
    end
    else if((state_c == SET_ALARM) && key[0] && key_sel_alarm == 3'd2)begin
        if(set_alarm_cnt2 == 10 -1)begin
            set_alarm_cnt2 <= 0;
        end
        else begin
            set_alarm_cnt2 <= set_alarm_cnt2 + 1;
        end
    end
end
//m十位
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        set_alarm_cnt3 <= 0;
    end
    else if(idle2set_alarm)begin
        set_alarm_cnt3 <= cnt3;
    end
    else if((state_c == SET_ALARM) && key[0] && key_sel_alarm == 3'd3)begin
        if(set_alarm_cnt3 == 6 -1)begin
            set_alarm_cnt3 <= 0;
        end
        else begin
            set_alarm_cnt3 <= set_alarm_cnt3 + 1;
        end
    end
end
//h个位
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        set_alarm_cnt4 <= 0;
    end
    else if(idle2set_alarm)begin
        set_alarm_cnt4 <= cnt4;
    end
    else if((state_c == SET_ALARM) && key[0] && key_sel_alarm == 3'd4)begin
        if(set_alarm_cnt4 == ((set_alarm_cnt5 == 2)?(4 - 1):(10 - 1)))begin
            set_alarm_cnt4 <= 0;
        end
        else begin
            set_alarm_cnt4 <= set_alarm_cnt4 + 1;
        end
        
    end
end
//h十位
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        set_alarm_cnt5 <= 0;
    end
    else if(idle2set_alarm)begin
        set_alarm_cnt5 <= cnt5;
    end
    else if((state_c == SET_ALARM) && key[0] && key_sel_alarm == 3'd5)begin
        if(set_alarm_cnt5 == (set_alarm_cnt4 >=4 ? 1 : 2))begin
            set_alarm_cnt5 <= 0;
        end
        else begin
            set_alarm_cnt5 <= set_alarm_cnt5 + 1;
        end
    end
end

//保存闹钟
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        alarm <= 4'd2,4'd3,4'd5,4'd9,4'd5,4'd9;
    end
    else if(set_alarm2idle)begin
        alarm <= set_alarm_cnt5,set_alarm_cnt4,set_alarm_cnt3,set_alarm_cnt2,set_alarm_cnt1,set_alarm_cnt0;
    end
end

//数码管显示
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        time_data_r <= 0;
    end
    else begin
        case(state_c)
            IDLE:     time_data_r <= cnt5,cnt4,cnt3,cnt2,cnt1,cnt0;  
            SET_TIME: time_data_r <= set_time_cnt5,set_time_cnt4,set_time_cnt3,set_time_cnt2,set_time_cnt1,set_time_cnt0;
            SET_ALARM:time_data_r <= set_alarm_cnt5,set_alarm_cnt4,set_alarm_cnt3,set_alarm_cnt2,set_alarm_cnt1,set_alarm_cnt0;
            default:  time_data_r <= cnt5,cnt4,cnt3,cnt2,cnt1,cnt0;
        endcase
    end
end
assign time_data = time_data_r;

//闹钟标志信号
assign alarm_clk_flag = ((state_c == IDLE) && (alarm == cnt5,cnt4,cnt3,cnt2,cnt1,cnt0));

endmodule

以上是关于FPGA学习之 状态机实现数码管的数字时钟的主要内容,如果未能解决你的问题,请参考以下文章

FPGA学习之 状态机实现数码管的数字时钟

FPGA学习之 状态机实现数码管的数字时钟

FPGA学习之数码管(封装)显示时间

FPGA实验数码管静态显示

FPGA开发随笔汇总

基于FPGA的数字时钟verilog开发