|流水灯 + 按键切换方向

流水灯 + 按键切换方向

作者: 石志超更新于: 2026/2/27

状态机切换左移/右移(顶层模块)

顶层文件 top.v

module top (
    input        sys_clk,
    input        sys_rst_n,
    input        key_n,        // 按键切换方向
    output [3:0] led
);

// ── 例化异步复位同步释放 ──
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 dir;  // 0=左移, 1=右移
always @(posedge sys_clk or negedge rst_n) begin
    if (!rst_n) dir <= 0;
    else if (key_press) dir <= ~dir;  // 按一次切换方向
end

// ── 定时 + 移位 ──
parameter CNT_MAX = 25_000_000;  // 0.5s
reg [24:0] cnt;
reg [3:0]  led_r;
always @(posedge sys_clk or negedge rst_n) begin
    if (!rst_n) begin
        cnt   <= 0;
        led_r <= 4'b0001;
    end else if (cnt >= CNT_MAX - 1) begin
        cnt <= 0;
        if (!dir)
            led_r <= {led_r[2:0], led_r[3]};  // 循环左移
        else
            led_r <= {led_r[0], led_r[3:1]};  // 循环右移
    end else
        cnt <= cnt + 1;
end

assign led = ~led_r;
endmodule

子模块:rst_sync.v(异步复位同步释放)

module rst_sync (
    input  clk,
    input  rst_n_in,   // 原始异步复位
    output rst_n_out   // 同步释放后的复位
);
reg r1, r2;
always @(posedge clk or negedge rst_n_in) begin
    if (!rst_n_in) begin r1 <= 0; r2 <= 0; end
    else           begin r1 <= 1; r2 <= r1; end
end
assign rst_n_out = r2;
endmodule

子模块:key_filter.v(两拍同步 + 消抖 + 下降沿检测)

module key_filter #(
    parameter CNT_MAX = 1_000_000  // 20ms @ 50MHz
)(
    input  clk,
    input  rst_n,
    input  key_in,      // 原始按键输入(低有效)
    output key_press    // 单脉冲:检测到按下
);
// 两拍同步
reg s1, s2;
always @(posedge clk or negedge rst_n)
    if (!rst_n) begin s1 <= 1; s2 <= 1; end
    else        begin s1 <= key_in; s2 <= s1; end

// 消抖
reg [19:0] cnt;
reg        key_db;
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin cnt <= 0; key_db <= 1; end
    else if (s2 != key_db) begin
        if (cnt >= CNT_MAX - 1) begin cnt <= 0; key_db <= s2; end
        else                     cnt <= cnt + 1;
    end else cnt <= 0;
end

// 下降沿检测
reg prev;
always @(posedge clk or negedge rst_n)
    if (!rst_n) prev <= 1;
    else        prev <= key_db;

assign key_press = prev & ~key_db;
endmodule