FPGA之状态机练习
一、状态机1
实现一个测试过程,该过程包括启动准备状态、启动测试、停止测试、查询测试结果、显示测试结果、测试结束返回初始化6个状态;用时间来控制该过程,90秒内完成该过程.
新建工程后,创建fsm_test.v
文件(新建工程可参考Quartus II的使用):
module fsm_test(
input wire clk,
input wire rst_n,
output wire [3:0] led
);
parameter T = 33'd449_999_999; // 测试使用9s
reg [32:0] cnt; // 计时器
parameter READY_STATE = 0; // 准备状态, 0.5s
parameter STARTUP_STATE = 1; // 启动测试状态, 1s
parameter STOPTEST_STATE = 2; // 停止测试状态, 1s
parameter QUERY_STATE = 3; // 查询测试结果状态, 3s
parameter DISPLAY_STATE = 4; // 显示测试结果状态, 3s
parameter STOP_STATE = 5; // 测试结束状态, 0.5s
reg [3:0] now_state = READY_STATE; // 当前状态
reg [3:0] led_r;
// 计时
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
cnt <= 33'd0;
else if(cnt == T)
cnt <= 33'd0;
else
cnt <= cnt + 1'd1;
end
// 状态改变
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
now_state <= READY_STATE;
else begin
case (now_state)
READY_STATE: begin
if (cnt == T/18)
now_state <= STARTUP_STATE;
else
now_state <= now_state;
end
STARTUP_STATE: begin
if (cnt == T*3/18)
now_state <= STOPTEST_STATE;
else
now_state <= now_state;
end
STOPTEST_STATE: begin
if (cnt == T*5/18)
now_state <= QUERY_STATE;
else
now_state <= now_state;
end
QUERY_STATE: begin
if (cnt == T*11/18)
now_state <= DISPLAY_STATE;
else
now_state <= now_state;
end
DISPLAY_STATE: begin
if (cnt == T*17/18)
now_state <= STOP_STATE;
else
now_state <= now_state;
end
STOP_STATE: begin
if (cnt == T)
now_state <= READY_STATE;
else
now_state <= now_state;
end
default: begin
now_state <= now_state;
end
endcase
end
end
// led根据状态显示
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
led_r <= 4'b0000;
else if(now_state == READY_STATE)
led_r <= 4'b0001;
else if(now_state == STARTUP_STATE)
led_r <= 4'b0010;
else if(now_state == STOPTEST_STATE)
led_r <= 4'b0011;
else if(now_state == QUERY_STATE)
led_r <= 4'b0100;
else if(now_state == DISPLAY_STATE)
led_r <= 4'b0101;
else if(now_state == STOP_STATE)
led_r <= 4'b0110;
else
led_r <= 4'b1111;
end
assign led = led_r;
endmodule
演示结果:
二、检测10010串
检测10010的输入
状态图:
创建工程后,首先创建消抖模块文件key_dobounce.v
:
// 按键消抖模块
module key_dobounce(
input wire clk, //不写类型默认为wire
input wire rst_n,
input wire key,
output reg flag, // 判断是否消除抖动,0为结束,1为抖动结束
output reg key_value // 消抖后稳定的按键值
);
// 20ms延时计数器, 1s 50_000_000
// 20ms 1_000_000
parameter T = 1_000_000;
reg [19:0] delay_cnt = T;
// 寄存一次key的值,用于判断按键是否消抖成功
reg key_reg;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
key_reg <= 1'b1; // 高电平,未按
delay_cnt <= 1'b0;
end
else begin
key_reg <= key;
if (key_reg == 1 && key == 0) // 当这一次的key值和上一次的key值不相等时,说明正在抖动
delay_cnt <= T; // 抖动未结束时,延迟始终为20ms
else if (delay_cnt > 0) // 已消除抖动,开始计数
delay_cnt <= delay_cnt - 1;
else
delay_cnt <= delay_cnt;
end
end
// 根据延时计数器获取按键状态及其按键值
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
flag <= 1'b0; // 未消除,默认值
key_value <= 1'b1; // 按键高电平,初始值
end
else begin
if (delay_cnt == 20'd1)begin // 记完数
flag <= 1'b1; // 消除抖动
key_value <= key;
end
else begin // 未记完数
flag <= 1'b0; // 未消除
key_value <= key_value;
end
end
end
endmodule
然后创建test_str.v
:
module test_str(
input wire clk,
input wire rst_n,
input wire key0, // 0按键,1为按下
input wire key1, // 1按键,1为按下
output wire[3:0] led
);
parameter S0 = 0; // 未输入状态
parameter S1_1 = 1; // 1
parameter S2_10 = 2; // 10
parameter S3_100 = 3; // 100
parameter S4_1001 = 4; // 1001
parameter S5_10010 = 5; // 10010,闪烁
reg [2:0] now_state = S0; // 当前状态
parameter T = 49_999_999; // 1s
reg [25:0] cnt = 26'd0; // 计时器,用于闪烁
reg [3:0] led_r;
// 计时
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
cnt <= 26'd0;
else if (cnt == T || (now_state==S4_1001 && key0))
cnt <= 26'd0;
else
cnt <= cnt + 1'd1;
end
// 状态切换
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
now_state <= S0;
else begin
case (now_state)
S0: begin
if (key1)
now_state <= S1_1;
else if (key0)
now_state <= S0;
else
now_state <= now_state;
end
S1_1: begin
if (key1)
now_state <= S0;
else if (key0)
now_state <= S2_10;
else
now_state <= now_state;
end
S2_10: begin
if (key1)
now_state <= S0;
else if (key0)
now_state <= S3_100;
else
now_state <= now_state;
end
S3_100: begin
if (key1)
now_state <= S4_1001;
else if (key0)
now_state <= S0;
else
now_state <= now_state;
end
S4_1001: begin
if (key1)
now_state <= S0;
else if (key0)
now_state <= S5_10010;
else
now_state <= now_state;
end
endcase
end
end
// LED
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
led_r <= 4'b0000;
else begin
case (now_state)
S0: led_r <= 4'b0000;
S1_1: led_r <= 4'b0001;
S2_10: led_r <= 4'b0010;
S3_100: led_r <= 4'b0100;
S4_1001: led_r <= 4'b1001;
S5_10010: begin // 闪烁
if (cnt < T/2)
led_r <= 4'b1111;
else
led_r <= 4'b0000;
end
default: led_r <= 4'b1110;
endcase
end
end
assign led = led_r;
endmodule
最后创建顶层文件test_str_top.v
:
module test_str_top(
input wire clk,
input wire rst_n,
input wire key0_in,
input wire key1_in,
output wire [3:0] led
);
wire flag0;
wire flag1;
wire key_value0;
wire key_value1;
key_dobounce inst_key0_dobounce(
.clk (clk),
.rst_n (rst_n),
.key (key0_in),
.flag (flag0), // 判断是否消除抖动,0为结束,1为抖动结束
.key_value (key_value0) // 消抖后稳定的按键值
);
key_dobounce inst_key1_dobounce(
.clk (clk),
.rst_n (rst_n),
.key (key1_in),
.flag (flag1), // 判断是否消除抖动,0为结束,1为抖动结束
.key_value (key_value1) // 消抖后稳定的按键值
);
test_str inst_test_str(
.clk (clk),
.rst_n (rst_n),
.key0 (flag0 && !key_value0), // 0按键,1为按下
.key1 (flag1 && !key_value1), // 1按键,1为按下
.led (led)
);
endmodule
演示结果: