|多模式 LED(按键切换模式)

多模式 LED(按键切换模式)

作者: 石志超,丁易杰更新于: 2026/2/27

复用 “流水灯 + 按键切换方向” 的 rst_sync、key_filter 模块

顶层 top.v

module top (
    input        sys_clk,
    input        sys_rst_n,
    input        key_n,
    output [3:0] led
);

// ── 时钟参数 ──
parameter CLK_FREQ = 50_000_000;       // 时钟频率(Hz)
localparam HALF_SEC = CLK_FREQ / 2;    // 0.5s 计数阈值
localparam ONE_MS   = CLK_FREQ / 1000; // 1ms 计数阈值

// ── 复位 & 按键(例化前页已有的子模块)──
wire rst_n;
rst_sync u_rst (
    .clk(sys_clk), .rst_n_in(sys_rst_n), .rst_n_out(rst_n)
);
wire key_press;
key_filter u_key (
    .clk(sys_clk), .rst_n(rst_n), .key_in(key_n), .key_press(key_press)
);

// ── 按键上升沿检测(单周期脉冲)──
reg key_press_d1, key_press_d2;
always @(posedge sys_clk or negedge rst_n) begin
    if (!rst_n) begin
        key_press_d1 <= 1'b0;
        key_press_d2 <= 1'b0;
    end else begin
        key_press_d1 <= key_press;
        key_press_d2 <= key_press_d1;
    end
end
wire key_trigger = key_press_d1 & ~key_press_d2;

// ── 基础定时:0.5s tick & 1ms tick ──
reg [$clog2(HALF_SEC)-1:0] cnt_half;
reg [$clog2(ONE_MS)-1:0]   cnt_ms;
reg        half_sec_tick, ms_tick;

always @(posedge sys_clk or negedge rst_n) begin
    if (!rst_n) begin
        cnt_half <= 0; half_sec_tick <= 0;
        cnt_ms   <= 0; ms_tick       <= 0;
    end else begin
        if (cnt_half >= HALF_SEC - 1) begin
            cnt_half      <= 0;
            half_sec_tick <= 1'b1;
        end else begin
            cnt_half      <= cnt_half + 1'b1;
            half_sec_tick <= 1'b0;
        end
        if (cnt_ms >= ONE_MS - 1) begin
            cnt_ms        <= 0;
            ms_tick       <= 1'b1;
        end else begin
            cnt_ms        <= cnt_ms + 1'b1;
            ms_tick       <= 1'b0;
        end
    end
end

// ── 模式状态机(独热码)──
localparam M_BLINK = 3'b001,
           M_SHIFT = 3'b010,
           M_FADE  = 3'b100;
reg [2:0] mode;

always @(posedge sys_clk or negedge rst_n) begin
    if (!rst_n) begin
        mode <= M_BLINK;
    end else if (key_trigger) begin
        case (mode)
            M_BLINK: mode <= M_SHIFT;
            M_SHIFT: mode <= M_FADE;
            M_FADE:  mode <= M_BLINK;
            default: mode <= M_BLINK;
        endcase
    end
end

// ── 闪烁:每 0.5s 全部翻转 ──
reg [3:0] led_blink;
always @(posedge sys_clk or negedge rst_n) begin
    if (!rst_n) begin
        led_blink <= 4'b1111;
    end else if (half_sec_tick) begin
        led_blink <= ~led_blink;
    end
end

// ── 流水:循环左移 ──
reg [3:0] led_shift;
always @(posedge sys_clk or negedge rst_n) begin
    if (!rst_n) begin
        led_shift <= 4'b0001;
    end else if (half_sec_tick) begin
        led_shift <= {led_shift[2:0], led_shift[3]};
    end
end

// ── 呼吸:PWM 渐亮渐暗 ──
reg [9:0] pwm_cnt;
reg [7:0] duty;
reg       duty_dir;  // 0=渐亮, 1=渐暗

always @(posedge sys_clk or negedge rst_n) begin
    if (!rst_n) begin
        pwm_cnt <= 0;
    end else begin
        pwm_cnt <= pwm_cnt + 1'b1;
    end
end

always @(posedge sys_clk or negedge rst_n) begin
    if (!rst_n) begin
        duty     <= 0;
        duty_dir <= 0;
    end else if (ms_tick) begin
        if (!duty_dir) begin
            if (duty >= 254) begin
                duty_dir <= 1'b1;
            end else begin
                duty <= duty + 2'd2;
            end
        end else begin
            if (duty <= 1) begin
                duty_dir <= 1'b0;
            end else begin
                duty <= duty - 2'd2;
            end
        end
    end
end

wire [3:0] led_fade = (pwm_cnt[9:2] < duty) ? 4'b1111 : 4'b0000;

// ── 模式选择输出(时序锁存,消除毛刺)──
reg [3:0] led_r;
always @(posedge sys_clk or negedge rst_n) begin
    if (!rst_n) begin
        led_r <= 4'b0000;
    end else begin
        case (mode)
            M_BLINK: led_r <= led_blink;
            M_SHIFT: led_r <= led_shift;
            M_FADE:  led_r <= led_fade;
            default: led_r <= 4'b0000;
        endcase
    end
end

// LED 输出(低电平点亮)
assign led = ~led_r;

endmodule