【FPGA】状态机写电子时钟

一、任务要求

电子时钟

  1. 按键切换三个模式,第一个是初始的电子时钟,第二个是修改时间,第三个是设置闹钟时间
  2. 按键切换位选
  3. 按键给那一位的值自加1
  4. 等时间到了设置的闹钟时间,蜂鸣器响以及led亮
  5. 按键关闭蜂鸣器和led

二、设计思路

这个状态机很ez的,就三个状态
1.IDLE(常规初始化状态)
2.SET_T(设置时间状态)
3.SET_A(设置闹钟时间状态)

状态转移图

在这里插入图片描述
状态转移条件

assign  idle2st = (state_c == IDLE)  && (key_out[0]);// 按下设置时钟键
assign  idle2sa = (state_c == IDLE)  && (key_out[1]);// 按下设置闹铃时钟键
assign  st2idle = (state_c == SET_T) && (key_out[2]);// 按下确定键
assign  sa2idle = (state_c == SET_A) && (key_out[2]);// 按下确定键

其他的都是时钟逻辑的部分,比如在修改时间或者设置时间闹钟先需要把时钟原本的值赋值给修改的时间,还要按键选择哪一位,以及按键加时间个位和十位的限制等等需要考虑的因素。

三、代码部分

电子时钟逻辑模块
fsm_digital_clock.v

module fsm_digital_clock #(parameter KEY_W = 3,CNT_TIME_1S = 50_000_000,TIME = 24'h123456,CNT_TIME_500MS = 25_000_000)(
    input                       clk,
    input                       rst_n,
    input    [KEY_W-1:0]        key_out,
    output   [23:0]             data,
    // output  reg     [5:0]    point
    output   reg       [3:0]    led,
    output   reg                beep
);
// 3种状态
localparam  IDLE  = 3'b001,
            SET_T = 3'b010,
            SET_A = 3'b100;
// 当前状态
reg     [2:0]       state_c;
// 下一状态
reg     [2:0]       state_n;

// 状态转移条件
wire                idle2st;
wire                idle2sa;
wire                st2idle;
wire                sa2idle;

// 寄存输出的时钟数据
reg     [23:0]      data_r;

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

// 闪烁计数器
reg     [26:0]      cnt_flash;
wire                add_cnt_flash;
wire                end_cnt_flash;

// ---初始电子时钟---
// 秒的个位计数器
reg     [3:0]       cnt_s_g;
wire                add_cnt_s_g;
wire                end_cnt_s_g;
// 秒的十位计数器
reg     [3:0]       cnt_s_s;
wire                add_cnt_s_s;
wire                end_cnt_s_s;
// 分的个位计数器
reg     [3:0]       cnt_m_g;
wire                add_cnt_m_g;
wire                end_cnt_m_g;
// 分的十位计数器
reg     [3:0]       cnt_m_s;
wire                add_cnt_m_s;
wire                end_cnt_m_s;
// 时的个位计数器
reg     [3:0]       cnt_h_g;
wire                add_cnt_h_g;
wire                end_cnt_h_g;
// 时的十位计数器
reg     [3:0]       cnt_h_s;
wire                add_cnt_h_s;
wire                end_cnt_h_s;             
// 初始化时钟数据
wire    [23:0]      digital_clock;

// ---设置时间的电子时钟---
// 秒的个位计数器
reg     [3:0]       set_cnt_s_g;
// 秒的十位计数器
reg     [3:0]       set_cnt_s_s;
// 分的个位计数器
reg     [3:0]       set_cnt_m_g;
// 分的十位计数器
reg     [3:0]       set_cnt_m_s;
// 时的个位计数器
reg     [3:0]       set_cnt_h_g;
// 时的十位计数器
reg     [3:0]       set_cnt_h_s;           
// 初始化时钟数据
wire    [23:0]      set_digital_clock;

// ---设置闹钟时间的电子时钟---
// 秒的个位计数器
reg     [3:0]       alarm_cnt_s_g;
// 秒的十位计数器
reg     [3:0]       alarm_cnt_s_s;
// 分的个位计数器
reg     [3:0]       alarm_cnt_m_g;
// 分的十位计数器
reg     [3:0]       alarm_cnt_m_s;
// 时的个位计数器
reg     [3:0]       alarm_cnt_h_g;
// 时的十位计数器
reg     [3:0]       alarm_cnt_h_s;           
// 初始化时钟数据
wire    [23:0]      alarm_digital_clock;
// 片选信号
reg     [5:0]       sel_value;

// 闹钟信号
reg                 alarm_flag;

// 状态机
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(idle2st)begin
                          state_n = SET_T;
                        end
                        else if(idle2sa)begin
                          state_n = SET_A;
                        end
                        else begin
                          state_n = state_c;
                        end
                    end
        SET_T   :   begin
                        if(st2idle)begin
                          state_n = IDLE;
                        end
                        else begin
                          state_n = state_c;
                        end
                    end
        SET_A   :   begin
                        if(sa2idle)begin
                          state_n = IDLE;
                        end
                        else begin
                          state_n = state_c;
                        end
                    end
        default: state_n = IDLE;
    endcase
end

assign  idle2st = (state_c == IDLE)  && (key_out[0]);// 按下设置时钟键
assign  idle2sa = (state_c == IDLE)  && (key_out[1]);// 按下设置闹铃时钟键
assign  st2idle = (state_c == SET_T) && (key_out[2]);// 按下确定键
assign  sa2idle = (state_c == SET_A) && (key_out[2]);// 按下确定键

// 不同状态下的数码管数据
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        data_r <= 0;
    end  
    else begin 
        case (state_c)
            IDLE    :   data_r <= digital_clock;
            SET_T   :   begin
                            case (sel_value)
                                0   :   begin
                                            if(cnt_flash <= CNT_TIME_500MS)begin
                                                data_r <= {set_digital_clock[23:4],4'd12};
                                            end
                                            else begin
                                                data_r <= set_digital_clock;
                                            end
                                        end
                                1   :   begin
                                            if(cnt_flash <= CNT_TIME_500MS)begin
                                                data_r <= {set_digital_clock[23:8],4'd12,set_digital_clock[3:0]};
                                            end
                                            else begin
                                                data_r <= set_digital_clock;
                                            end
                                        end
                                2   :   begin
                                            if(cnt_flash <= CNT_TIME_500MS)begin
                                                data_r <= {set_digital_clock[23:12],4'd12,set_digital_clock[7:0]};
                                            end
                                            else begin
                                                data_r <= set_digital_clock;
                                            end
                                        end
                                3   :   begin
                                            if(cnt_flash <= CNT_TIME_500MS)begin
                                                data_r <= {set_digital_clock[23:16],4'd12,set_digital_clock[11:0]};
                                            end
                                            else begin
                                                data_r <= set_digital_clock;
                                            end
                                        end
                                4   :   begin
                                            if(cnt_flash <= CNT_TIME_500MS)begin
                                                data_r <= {set_digital_clock[23:20],4'd12,set_digital_clock[15:0]};
                                            end
                                            else begin
                                                data_r <= set_digital_clock;
                                            end
                                        end
                                5   :   begin
                                            if(cnt_flash <= CNT_TIME_500MS)begin
                                                data_r <= {4'd12,set_digital_clock[19:0]};
                                            end
                                            else begin
                                                data_r <= set_digital_clock;
                                            end
                                        end      
                                default: data_r <= set_digital_clock;
                            endcase
                        end
            SET_A   :   begin
                            case (sel_value)
                                0   :   begin
                                            if(cnt_flash <= CNT_TIME_500MS)begin
                                                data_r <= {set_digital_clock[23:4],4'd12};
                                            end
                                            else begin
                                                data_r <= alarm_digital_clock;
                                            end
                                        end
                                1   :   begin
                                            if(cnt_flash <= CNT_TIME_500MS)begin
                                                data_r <= {alarm_digital_clock[23:8],4'd12,alarm_digital_clock[3:0]};
                                            end
                                            else begin
                                                data_r <= alarm_digital_clock;
                                            end
                                        end
                                2   :   begin
                                            if(cnt_flash <= CNT_TIME_500MS)begin
                                                data_r <= {alarm_digital_clock[23:12],4'd12,alarm_digital_clock[7:0]};
                                            end
                                            else begin
                                                data_r <= alarm_digital_clock;
                                            end
                                        end
                                3   :   begin
                                            if(cnt_flash <= CNT_TIME_500MS)begin
                                                data_r <= {alarm_digital_clock[23:16],4'd12,alarm_digital_clock[11:0]};
                                            end
                                            else begin
                                                data_r <= alarm_digital_clock;
                                            end
                                        end
                                4   :   begin
                                            if(cnt_flash <= CNT_TIME_500MS)begin
                                                data_r <= {alarm_digital_clock[23:20],4'd12,alarm_digital_clock[15:0]};
                                            end
                                            else begin
                                                data_r <= alarm_digital_clock;
                                            end
                                        end
                                5   :   begin
                                            if(cnt_flash <= CNT_TIME_500MS)begin
                                                data_r <= {4'd12,alarm_digital_clock[19:0]};
                                            end
                                            else begin
                                                data_r <= alarm_digital_clock;
                                            end
                                        end      
                                default: data_r <= alarm_digital_clock;
                            endcase
                        end 
            default:  data_r <= digital_clock;
        endcase
    end 
end

assign data = data_r;

// 设置闪烁时间
always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_flash <= 0;
    end 
    else if(add_cnt_flash)begin 
            if(end_cnt_flash)begin 
                cnt_flash <= 0;
            end
            else begin 
                cnt_flash <= cnt_flash + 1;
            end 
    end
   else  begin
       cnt_flash <= cnt_flash;
    end
end 

assign add_cnt_flash = (state_c == SET_T) || (state_c == SET_A);
assign end_cnt_flash = add_cnt_flash && cnt_flash == CNT_TIME_1S - 1;


// 1s计数器
always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_1s <= 0;
    end 
    else if(add_cnt_1s)begin 
            if(end_cnt_1s)begin 
                cnt_1s <= 0;
            end
            else begin 
                cnt_1s <= cnt_1s + 1;
            end 
    end
   else  begin
       cnt_1s <= cnt_1s;
    end
end 
assign add_cnt_1s = 1'b1;
assign end_cnt_1s = add_cnt_1s && cnt_1s == CNT_TIME_1S - 1;
// ---初始电子时钟---
// 秒的个位计数器
always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_s_g <= TIME[3:0];
    end
    else if((state_c == SET_T) && key_out[2])begin
        cnt_s_g <= set_cnt_s_g;
    end 
    else if(add_cnt_s_g)begin 
            if(end_cnt_s_g)begin 
                cnt_s_g <= 0;
            end
            else begin 
                cnt_s_g <= cnt_s_g + 1;
            end 
    end
   else  begin
       cnt_s_g <= cnt_s_g;
    end
end 
assign add_cnt_s_g = end_cnt_1s;
assign end_cnt_s_g = add_cnt_s_g && cnt_s_g == 10 - 1;
// 秒的十位计数器
always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_s_s <= TIME[7:4];
    end
    else if((state_c == SET_T) && key_out[2])begin
        cnt_s_s <= set_cnt_s_s;
    end  
    else if(add_cnt_s_s)begin 
            if(end_cnt_s_s)begin 
                cnt_s_s <= 0;
            end
            else begin 
                cnt_s_s <= cnt_s_s + 1;
            end 
    end
   else  begin
       cnt_s_s <= cnt_s_s;
    end
end 
assign add_cnt_s_s = end_cnt_s_g;
assign end_cnt_s_s = add_cnt_s_s && cnt_s_s == 6 - 1;
// 分的个位计数器
always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_m_g <= TIME[11:8];
    end
    else if((state_c == SET_T) && key_out[2])begin
        cnt_m_g <= set_cnt_m_g;
    end  
    else if(add_cnt_m_g)begin 
            if(end_cnt_m_g)begin 
                cnt_m_g <= 0;
            end
            else begin 
                cnt_m_g <= cnt_m_g + 1;
            end 
    end
   else  begin
       cnt_m_g <= cnt_m_g;
    end
end 
assign add_cnt_m_g = end_cnt_s_s;
assign end_cnt_m_g = add_cnt_m_g && cnt_m_g == 10 - 1;
// 分的十位计数器
always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_m_s <= TIME[15:12];
    end
    else if((state_c == SET_T) && key_out[2])begin
        cnt_m_s <= set_cnt_m_s;
    end  
    else if(add_cnt_m_s)begin 
            if(end_cnt_m_s)begin 
                cnt_m_s <= 0;
            end
            else begin 
                cnt_m_s <= cnt_m_s + 1;
            end 
    end
   else  begin
       cnt_m_s <= cnt_m_s;
    end
end 
assign add_cnt_m_s = end_cnt_m_g;
assign end_cnt_m_s = add_cnt_m_s && cnt_m_s == 6 - 1;
// 时的个位计数器
always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_h_g <= TIME[19:16];
    end 
    else if((state_c == SET_T) && key_out[2])begin
        cnt_h_g <= set_cnt_h_g;
    end 
    else if(add_cnt_h_g)begin 
            if(end_cnt_h_g)begin 
                cnt_h_g <= 0;
            end
            else begin 
                cnt_h_g <= cnt_h_g + 1;
            end 
    end
   else  begin
       cnt_h_g <= cnt_h_g;
    end
end 
assign add_cnt_h_g = end_cnt_m_s;
assign end_cnt_h_g = add_cnt_h_g && (cnt_h_g == ((cnt_h_s == 2) ? (4 - 1) : (10 - 1)));
// 时的十位计数器
always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_h_s <= TIME[23:20];
    end 
    else if((state_c == SET_T) && key_out[2])begin
        cnt_h_s <= set_cnt_h_s;
    end 
    else if(add_cnt_h_s)begin 
            if(end_cnt_h_s)begin 
                cnt_h_s <= 0;
            end
            else begin 
                cnt_h_s <= cnt_h_s + 1;
            end 
    end
   else  begin
       cnt_h_s <= cnt_h_s;
    end
end 
assign add_cnt_h_s = end_cnt_h_g;
assign end_cnt_h_s = add_cnt_h_s && cnt_h_s == 3 - 1;
// 初始化电子时钟数据
assign digital_clock = {cnt_h_s,cnt_h_g,cnt_m_s,cnt_m_g,cnt_s_s,cnt_s_g};

// 按键控制片选
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        sel_value <= 0;
    end
    else if(state_c <= IDLE)begin
        sel_value <= 0;
    end 
    else if((state_c == SET_T || state_c == SET_A) && key_out[1])begin 
        if(sel_value == 5)begin
          sel_value <= 0;
        end
        else begin
          sel_value <= sel_value + 1;
        end
    end 
    else begin 
        sel_value <= sel_value;
    end 
end


// ---按键设置时间---
// 秒的个位
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        set_cnt_s_g <= 0;
    end 
    else if(idle2st)begin
        set_cnt_s_g <= cnt_s_g;
    end
    else if(state_c == SET_T && key_out[0] && sel_value == 0)begin
        if(set_cnt_s_g == 9)begin
          set_cnt_s_g <= 0;
        end
        else begin
          set_cnt_s_g <= set_cnt_s_g + 1;
        end 
    end 
    else begin 
        set_cnt_s_g <= set_cnt_s_g;
    end 
end
// 秒的十位
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        set_cnt_s_s <= 0;
    end
    else if(idle2st)begin
        set_cnt_s_s <= cnt_s_s;
    end 
    else if(state_c == SET_T && key_out[0] && sel_value == 1)begin
        if(set_cnt_s_s == 5)begin
          set_cnt_s_s <= 0;
        end
        else begin
          set_cnt_s_s <= set_cnt_s_s + 1;
        end 
    end 
    else begin 
        set_cnt_s_s <= set_cnt_s_s;
    end 
end
// 分的个位
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        set_cnt_m_g <= 0;
    end
    else if(idle2st)begin
        set_cnt_m_g <= cnt_m_g;
    end  
    else if(state_c == SET_T && key_out[0] && sel_value == 2)begin
        if(set_cnt_m_g == 9)begin
          set_cnt_m_g <= 0;
        end
        else begin
          set_cnt_m_g <= set_cnt_m_g + 1;
        end 
    end 
    else begin 
        set_cnt_m_g <= set_cnt_m_g;
    end 
end
// 分的十位
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        set_cnt_m_s <= 0;
    end 
    else if(idle2st)begin
        set_cnt_m_s <= cnt_m_s;
    end 
    else if(state_c == SET_T && key_out[0] && sel_value == 3)begin
        if(set_cnt_m_s == 5)begin
          set_cnt_m_s <= 0;
        end
        else begin
          set_cnt_m_s <= set_cnt_m_s + 1;
        end 
    end 
    else begin 
        set_cnt_m_s <= set_cnt_m_s;
    end 
end
// 时的个位
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        set_cnt_h_g <= 0;
    end 
    else if(idle2st)begin
        set_cnt_h_g <= cnt_h_g;
    end 
    else if((set_cnt_h_s == 2) && (set_cnt_h_g >= 4))begin
        set_cnt_h_g <= 0;
    end
    else if(state_c == SET_T && key_out[0] && sel_value == 4)begin
        if(((set_cnt_h_g == 9) && (set_cnt_h_s < 2)) || ((set_cnt_h_g == 3) && (set_cnt_h_s == 2)))begin
          set_cnt_h_g <= 0;
        end
        else begin
          set_cnt_h_g <= set_cnt_h_g + 1;
        end 
    end 
    else begin 
        set_cnt_h_g <= set_cnt_h_g;
    end 
end
// 时的十位
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        set_cnt_h_s <= 0;
    end
    else if(idle2st)begin
        set_cnt_h_s <= cnt_h_s;
    end
    else if((set_cnt_h_s == 2) && (set_cnt_h_g >= 4))begin
        set_cnt_h_s <= 0;
    end  
    else if((state_c == SET_T) && key_out[0] && (sel_value == 5))begin
        if(set_cnt_h_s == 2)begin
          set_cnt_h_s <= 0;
        end
        else begin
          set_cnt_h_s <= set_cnt_h_s + 1;
        end 
    end 
    else begin 
        set_cnt_h_s <= set_cnt_h_s;
    end 
end

assign set_digital_clock = {set_cnt_h_s,set_cnt_h_g,set_cnt_m_s,set_cnt_m_g,set_cnt_s_s,set_cnt_s_g};

// ---按键设置时间设置闹钟---
// 秒的个位
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        alarm_cnt_s_g <= 0;
    end 
    else if(idle2sa)begin
        alarm_cnt_s_g <= cnt_s_g;
    end
    else if(state_c == SET_A && key_out[0] && sel_value == 0)begin
        if(alarm_cnt_s_g == 9)begin
          alarm_cnt_s_g <= 0;
        end
        else begin
          alarm_cnt_s_g <= alarm_cnt_s_g + 1;
        end 
    end 
    else begin 
        alarm_cnt_s_g <= alarm_cnt_s_g;
    end 
end
// 秒的十位
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        alarm_cnt_s_s <= 0;
    end
    else if(idle2sa)begin
        alarm_cnt_s_s <= cnt_s_s;
    end 
    else if(state_c == SET_A && key_out[0] && sel_value == 1)begin
        if(alarm_cnt_s_s == 5)begin
          alarm_cnt_s_s <= 0;
        end
        else begin
          alarm_cnt_s_s <= alarm_cnt_s_s + 1;
        end 
    end 
    else begin 
        alarm_cnt_s_s <= alarm_cnt_s_s;
    end 
end
// 分的个位
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        alarm_cnt_m_g <= 0;
    end
    else if(idle2sa)begin
        alarm_cnt_m_g <= cnt_m_g;
    end  
    else if(state_c == SET_A && key_out[0] && sel_value == 2)begin
        if(alarm_cnt_m_g == 9)begin
          alarm_cnt_m_g <= 0;
        end
        else begin
          alarm_cnt_m_g <= alarm_cnt_m_g + 1;
        end 
    end 
    else begin 
        alarm_cnt_m_g <= alarm_cnt_m_g;
    end 
end
// 分的十位
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        alarm_cnt_m_s <= 0;
    end 
    else if(idle2sa)begin
        alarm_cnt_m_s <= cnt_m_s;
    end 
    else if(state_c == SET_A && key_out[0] && sel_value == 3)begin
        if(alarm_cnt_m_s == 5)begin
          alarm_cnt_m_s <= 0;
        end
        else begin
          alarm_cnt_m_s <= alarm_cnt_m_s + 1;
        end 
    end 
    else begin 
        alarm_cnt_m_s <= alarm_cnt_m_s;
    end 
end
// 时的个位
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        alarm_cnt_h_g <= 0;
    end 
    else if(idle2sa)begin
        alarm_cnt_h_g <= cnt_h_g;
    end 
    else if(state_c == SET_A && key_out[0] && sel_value == 4)begin
        if(((alarm_cnt_h_g == 9) && (alarm_cnt_h_s < 2)) || ((alarm_cnt_h_g == 3) && (alarm_cnt_h_s == 2)))begin
          alarm_cnt_h_g <= 0;
        end
        else begin
          alarm_cnt_h_g <= alarm_cnt_h_g + 1;
        end 
    end 
    else begin 
        alarm_cnt_h_g <= alarm_cnt_h_g;
    end 
end
// 时的十位
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        alarm_cnt_h_s <= 0;
    end
    else if(idle2sa)begin
        alarm_cnt_h_s <= cnt_h_s;
    end  
    else if(state_c == SET_A && key_out[0] && sel_value == 5)begin
        if(alarm_cnt_h_s == 2)begin
          alarm_cnt_h_s <= 0;
        end
        else begin
          alarm_cnt_h_s <= alarm_cnt_h_s + 1;
        end 
    end 
    else begin 
        alarm_cnt_h_s <= alarm_cnt_h_s;
    end 
end

assign alarm_digital_clock = {alarm_cnt_h_s,alarm_cnt_h_g,alarm_cnt_m_s,alarm_cnt_m_g,alarm_cnt_s_s,alarm_cnt_s_g};

// 闹钟标志 当设置的时间到了并且在初始状态下alarm_flag拉高
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        alarm_flag <= 0;
    end 
    else if(state_c == IDLE && (alarm_digital_clock == digital_clock))begin 
        alarm_flag <= 1;
    end 
    else begin 
        alarm_flag <= 0;
    end 
end

// 蜂鸣器和led
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        led <= 4'b0000;
        beep <= 1'b1;
    end 
    else if(alarm_flag)begin 
        led <= 4'b1111;
        beep <= 1'b0;
    end 
    else if(state_c == IDLE && key_out[2])begin
        led <=4'b0000;
        beep <= 1'b1;
    end
    else begin 
        led <= led;
        beep <= beep;
    end 
end
endmodule

按键消抖模块
key_filter.v

// 按键消抖
module key_filter #(parameter KEY_W = 3,CNT_TIME = 1_000_000)(
    input                       clk,
    input                       rst_n,
    input         [KEY_W-1:0]   key_in,
    output  reg   [KEY_W-1:0]   key_out
);

// 定义4种状态
parameter   IDLE  = 4'b0001,
            F_D   = 4'b0010,
            H_D   = 4'b0100,
            F_U   = 4'b1000;
            

// 20ms计数器
reg     [19:0]          cnt;
wire                    add_cnt;
wire                    end_cnt;

// 同步
reg     [KEY_W-1:0]     key_r0;

// 打拍
reg     [KEY_W-1:0]     key_r1;

// 上升沿
wire    [KEY_W-1:0]     p_edge;
// 下降沿
wire    [KEY_W-1:0]     n_edge;

// 当前状态
reg     [3:0]           state_c;
// 下一状态
reg     [3:0]           state_n;   

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

// 检测到下降沿或上升沿开始计数20ms
always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt <= 0;
    end 
    else if(add_cnt)begin 
            if(end_cnt || p_edge)begin 
                cnt <= 0;
            end
            else begin 
                cnt <= cnt + 1;
            end 
    end
   else  begin
       cnt <= cnt;
    end
end 

assign add_cnt = (state_c == F_D) || (state_c == F_U);
assign end_cnt = add_cnt && cnt == CNT_TIME - 1;


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


// 下降沿的检测
assign  n_edge = ~key_r0 & key_r1;
// 上升沿的检测
assign  p_edge = ~key_r1 & key_r0;


// 状态机
// 1.状态转移 下一个状态到现态 状态更新
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(n_edge)begin
                          state_n = F_D;
                        end
                        else begin
                          state_n = state_c;
                        end
                    end 
        F_D   :   begin
                        if(end_cnt)begin
                          state_n = H_D;
                        end
                        else if(p_edge)begin
                          state_n = IDLE;
                        end
                        else begin
                          state_n = state_c;
                        end
                    end
        H_D   :   begin
                        if(p_edge)begin
                          state_n = F_U;
                        end
                        else begin
                          state_n = state_c;
                        end
                    end 
        F_U   :   begin
                        if(end_cnt)begin
                          state_n = IDLE;
                        end
                        else begin
                          state_n = state_c;
                        end
                    end  
         default:    state_n = state_c;
     endcase
end


// 根据状态输出
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        key_out <= {KEY_W{1'b0}};
    end  
    else if(state_c == H_D && p_edge)begin 
        key_out <= ~key_r1;
    end 
    else begin
        key_out <= 0;
    end
end

endmodule

数码管驱动模块
seg_driver.v

// 数码管驱动
module seg_driver(
    input                       clk,
    input                       rst_n,
    input           [23:0]      data,
    input           [5:0]       point,
    
    output   reg    [7:0]       seg_dig,
    output   reg    [5:0]       seg_sel
);

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,
            A     = 7'b000_1000,
            B     = 7'b000_0011,
            NONE  = 7'b111_1111;// 显示一条线

parameter SCAN_TIME = 50_000;

// 扫描计数器1ms
reg     [17:0]      scan_cnt;
wire                add_scan_cnt;
wire                end_scan_cnt;

reg     [3:0]       num;

// 小数点
reg                 seg_point;

// 扫描计数器1ms
always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        scan_cnt <= 0;
    end 
    else if(add_scan_cnt)begin 
            if(end_scan_cnt)begin 
                scan_cnt <= 0;
            end
            else begin 
                scan_cnt <= scan_cnt + 1;
            end 
    end
   else  begin
       scan_cnt <= scan_cnt;
    end
end 

assign add_scan_cnt = 1'b1;
assign end_scan_cnt = add_scan_cnt && scan_cnt == SCAN_TIME - 1;

// 计数器扫描位选
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        seg_sel <= 6'b111110;
    end 
    else if(end_scan_cnt)begin 
        seg_sel <= {seg_sel[4:0],seg_sel[5]};
    end 
    else begin 
        seg_sel <= seg_sel;
    end 
end

// 根据位选来给数据
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        num <= 4'b0;
        seg_point <= 6'h111111;
    end 
    else begin 
        case (seg_sel)
            6'b111110   :   begin 
                                num <= data[3:0];
                                // point <= 1'b1;
                                seg_point <= point[0];
                            end
            6'b111101   :   begin 
                                num <= data[7:4];
                                // point <= 1'b1;
                                seg_point <= point[1];
                            end
            6'b111011   :   begin 
                                num <= data[11:8];
                                // point <= 1'b0;
                                seg_point <= point[2];
                            end
            6'b110111   :   begin 
                                num <= data[15:12];
                                // point <= 1'b1;
                                seg_point <= point[3];
                            end
            6'b101111   :   begin 
                                num <= data[19:16];
                                // point <= 1'b1;
                                seg_point <= point[4];
                            end
            6'b011111   :   begin 
                                num <= data[23:20];
                                // point <= 1'b0;
                                seg_point <= point[5];
                            end                                                 
            default: ;
        endcase
    end 
end


// 根据num来对段选赋值
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        seg_dig <= 8'hff;
    end 
    else begin 
        case (num)
            0   :   seg_dig <={seg_point,ZERO};
            1   :   seg_dig <={seg_point,ONE};
            2   :   seg_dig <={seg_point,TWO};
            3   :   seg_dig <={seg_point,THREE};
            4   :   seg_dig <={seg_point,FOUR};
            5   :   seg_dig <={seg_point,FIVE};
            6   :   seg_dig <={seg_point,SIX};
            7   :   seg_dig <={seg_point,SEVEN};
            8   :   seg_dig <={seg_point,EIGHT};
            9   :   seg_dig <={seg_point,NINE};
            // 10  :   seg_dig <={seg_point,A};
            // 11  :   seg_dig <={seg_point,B};
            12  :   seg_dig <={seg_point,NONE};
            default: ;
        endcase
    end 
end
endmodule

顶层
top.v

module top #(parameter KEY_W = 3)(
    input                   clk,
    input                   rst_n,
    input   [KEY_W-1:0]     key_in,
    output  [7:0]           seg_dig,
    output  [5:0]           seg_sel,
    output  [3:0]           led,
    output                  beep
);

wire    [KEY_W-1:0]     key_out;
wire    [23:0]          data;

// 按键消抖模块
key_filter u_key_filter(
/*input                       */.clk        (clk),
/*input                       */.rst_n      (rst_n),
/*input         [KEY_W-1:0]   */.key_in     (key_in),
/*output  reg   [KEY_W-1:0]   */.key_out    (key_out)
);

// 数码管驱动模块
seg_driver u_seg_driver(
/*input                       */.clk        (clk),
/*input                       */.rst_n      (rst_n),
/*input           [23:0]      */.data       (data),
/*input           [5:0]       */.point      (6'b101_011),
/*output   reg    [7:0]       */.seg_dig    (seg_dig),
/*output   reg    [5:0]       */.seg_sel    (seg_sel)
);

// 电子时钟逻辑模块
fsm_digital_clock  u_fsm_digital_clock(
/*input                       */.clk        (clk),
/*input                       */.rst_n      (rst_n),
/*input    [KEY_W-1:0]        */.key_out    (key_out),
/*output  reg     [23:0]      */.data       (data), 
// /*output  reg     [5:0]       */.point      (point)
/*output   reg       [3:0]    */.led        (led),
/*output   reg                */.beep       (beep)
);

endmodule

四、仿真验证

这里就不仿真了,有点小麻烦,我也怕麻烦,哈哈

五、总结

我这个小小的电子时钟都写了我一天,从早上11点写到下午5点,过程中出现了不断的错误,去调试,去验证,最终还是做成了,挺有成就感的,尤其当一个一个的功能能实现的时候。

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值