一、引言
在FPGA开发中,状态机是一种重要的设计工具,用于处理具有时间顺序的事件。本文将详细介绍如何使用状态机实现一个LED流水灯的效果。
二、状态机概述
状态机(FSM)是一种行为模型,用于表示系统在不同状态下的行为以及状态之间的转移。在FPGA中,状态机通常用于控制复杂的流程。
2.1 分类
状态机主要分为两类:
- 摩尔(Moore)型状态机:输出只取决于当前状态。
- 米勒(Mealy)型状态机:输出取决于当前状态和输入条件。
2.2 写法
根据写法,状态机可以分为:
- 一段式:所有逻辑在一个always模块中。
- 二段式:状态转移和输出分别在两个always模块中。
- 三段式:状态转移、转移条件和输出分别在三个always模块中。
三、流水灯实现
本文采用二段式状态机实现LED流水灯。
3.1 顶层模块
顶层模块是整个设计的核心,它负责连接和协调各个子模块。在本设计中,顶层模块将分频模块、按键消抖模块和状态机模块有机地结合在一起,形成了一个完整的系统。
module top (
input clk,
input rst_n,
input KEY0,
input KEY1,
output [5:0] led
);
wire en_1Hz;
wire key0_debounced, key1_debounced;
reg pause;
wire fsm_rst_n;
reg key1_debounced_prev;
key_debounce u_key0_deb (
.clk(clk),
.rst_n(rst_n),
.key_in(~KEY0),
.key_out(key0_debounced)
);
key_debounce u_key1_deb (
.clk(clk),
.rst_n(rst_n),
.key_in(~KEY1),
.key_out(key1_debounced)
);
assign fsm_rst_n = ~key0_debounced;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
pause <= 1'b0;
key1_debounced_prev <= 1'b0;
end else begin
key1_debounced_prev <= key1_debounced;
if (key1_debounced_prev && !key1_debounced) begin
pause <= ~pause;
end
end
end
clk_divider #(
.CLK_FREQ(50_000_000),
.OUT_FREQ(1)
) u_clk_div (
.clk(clk),
.rst_n(rst_n),
.en(en_1Hz)
);
led_fsm u_led_fsm (
.clk(clk),
.rst_n(fsm_rst_n),
.en(en_1Hz),
.pause(pause),
.led(led)
);
endmodule
3.2 分频模块
分频模块的作用是将FPGA的高频时钟信号转换为低频信号,以满足流水灯的时序要求。在本设计中,我们将50MHz的输入时钟分频为1Hz,使得流水灯的每个LED灯能够以1秒为间隔依次点亮。分频模块通过计数器实现,当计数器达到预设的最大值时,产生一个使能信号,触发状态机的状态转移。
module clk_divider #(
parameter CLK_FREQ = 50_000_000,
parameter OUT_FREQ = 1
) (
input clk,
input rst_n,
output reg en
);
localparam CNT_MAX = CLK_FREQ / OUT_FREQ - 1;
reg [25:0] cnt;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt <= 0;
en <= 0;
end else begin
if (cnt == CNT_MAX) begin
cnt <= 0;
en <= 1;
end else begin
cnt <= cnt + 1;
en <= 0;
end
end
end
endmodule
3.3 按键消抖模块
按键消抖模块用于消除按键抖动的影响,确保按键信号的稳定性和可靠性。在实际应用中,机械按键在按下和释放瞬间会产生多次抖动,这可能导致误触发。消抖模块通过延时采样和状态判断,有效地滤除了抖动信号,为后续的逻辑处理提供了稳定的输入。
module key_debounce(
input clk,
input rst_n,
input key_in,
output reg key_out
);
parameter DEBOUNCE_TIME = 1_000_000;
reg [19:0] counter;
reg key_reg;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
counter <= 0;
key_reg <= 1;
key_out <= 1'b0;
end else begin
key_reg <= key_in;
if (key_reg != key_in) begin
counter <= 0;
end else if (counter < DEBOUNCE_TIME) begin
counter <= counter + 1;
end else begin
key_out <= key_in;
end
end
end
endmodule
3.4 流水灯状态机模块
状态机模块是流水灯设计的核心部分,它定义了流水灯的状态转移逻辑。在本设计中,我们定义了六个状态,每个状态对应一个LED灯的点亮。通过状态转移,实现流水灯的依次点亮效果。状态机的输出直接控制LED灯的亮灭,而状态的转移则由分频模块产生的使能信号触发。
module led_fsm6 (
input clk,
input rst_n,
input en,
input pause,
output [5:0] led
);
parameter S_LED0 = 6'b000001;
parameter S_LED1 = 6'b000010;
parameter S_LED2 = 6'b000100;
parameter S_LED3 = 6'b001000;
parameter S_LED4 = 6'b010000;
parameter S_LED5 = 6'b100000;
reg [5:0] current_state, next_state;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
current_state <= S_LED0;
end else if (en && !pause) begin
current_state <= next_state;
end
end
always @(*) begin
case (current_state)
S_LED0: next_state = S_LED1;
S_LED1: next_state = S_LED2;
S_LED2: next_state = S_LED3;
S_LED3: next_state = S_LED4;
S_LED4: next_state = S_LED5;
S_LED5: next_state = S_LED0;
default: next_state = S_LED0;
endcase
end
assign led = current_state;
endmodule
四、运行结果
在实际运行中,流水灯按照预设的状态顺序依次点亮,每个LED灯亮起的时间间隔为1秒。通过按键KEY0可以复位流水灯,使其从第一个LED开始重新运行。通过按键KEY1可以暂停和继续流水灯的运行。
五、总结
本文详细介绍了如何使用FPGA状态机实现流水灯效果。通过状态机的设计,可以清晰地控制流水灯的状态转移,使整个系统更加稳定和易于维护。状态机的设计方法和实现过程对于其他复杂的FPGA项目也具有重要的参考价值。