1、LED灯
LED灯就是发光二极管,二极管在正向电压作用下电阻很小,处于导通状态,相当于一只接通的开关;在反向电压作用下,电阻很大,处于截止状态,如同一只断开的开关。发光二极管在导通的时候发光,在没有导通的时候不发光。
模块设计
由上图可以看出有四个led灯,FPGA输出高电平时,LED点亮;FPGA输出低电平时,LED熄灭。输入信号分别为时钟信号和复位信号,设计一个计时器。每0.2s改变四个led的状态,同一时刻下只能有一只led亮,其余的led灭。最后通过移位寄存器输出信号给四个led灯。
2、建立工程
在rtl路径下新建fsm_led.v代码文件
module fsm_led#(parameter TIME_1S = 50_000_000)(
input wire clk ,
input wire rst_n ,
output reg [3:0] led
);
parameter IDLE = 5'b00001, // 宏定义 定义状态
S1 = 5'b00010,
S2 = 5'b00100,
S3 = 5'b01000,
S4 = 5'b10000;
wire idle2s1 ; // 2 -> to 状态跳转条件
wire s12s2 ;
wire s22s3 ;
wire s32s4 ;
wire s42idle ;
reg [4:0] state_n ; // 次态 状态寄存器
reg [4:0] state_c ; // 现态
// parameter TIME_1S = 50_000_000; //计数1s
reg [25:0] cnt ; //计数1s计数器
wire add_cnt ; //计数器 开始或加一的条件
wire end_cnt ; // 计数器 结束的条件
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 26'd0;
end
else if(add_cnt)begin //加1条件有效的时候
if(end_cnt)begin //计数到了最大值
cnt <= 26'd0;
end
else begin
cnt <= cnt + 1'd1;
end
end
else begin //默认保持不变
cnt <= cnt ;
end
end
assign add_cnt = 1'b1;
assign end_cnt = add_cnt && cnt == TIME_1S - 1; // 1 && 1 = 1 1 && 0 = 0
//状态机的第一段 时序逻辑
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
state_c <= IDLE; //复位状态处于 空闲状态
end
else begin
state_c <= state_n; // 下一个时钟周期到来的时候 就把次态的值 赋予给我们的现态
end
end
// 状态机的第二段 组合逻辑 state_n 只在第二段状态机里面用
always @(*)begin // *代表的意思是 里面所有的敏感信号 我都不在意
case(state_c)
IDLE : begin
if(idle2s1)
state_n = S1;
else
state_n = state_c;
end
S1 : begin
if(s12s2)
state_n = S2;
else
state_n = state_c;
end
S2 : begin
if(s22s3)
state_n = S3;
else
state_n = state_c;
end
S3 : begin
if(s32s4)
state_n = S4;
else
state_n = state_c;
end
S4 : begin
if(s42idle)
state_n = IDLE;
else
state_n = state_c;
end
default : state_n = state_c;
endcase
end
assign idle2s1 = state_c == IDLE && end_cnt; //1s计数完了 在IDLE情况下 就满足跳转条件 end_cnt == 五千万减一 到了最大值就跳转
assign s12s2 = state_c == S1 && end_cnt;
assign s22s3 = state_c == S2 && end_cnt;
assign s32s4 = state_c == S3 && end_cnt;
assign s42idle = state_c == S4 && end_cnt;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
led <= 4'b1001;
end
else if(state_c == IDLE)begin
led <= 4'b0000;
end
else if(state_c == S1)begin
led <= 4'b0001;
end
else if(state_c == S2)begin
led <= 4'b0010;
end
else if(state_c == S3)begin
led <= 4'b0100;
end
else if(state_c == S4)begin
led <= 4'b1000;
end
else begin
led <= led;
end
end
endmodule
由代码可以看出,clk为50MHz,1s设置五千万信号,每个信号20ns,使用计数器当做计时器,每记录一个数字等于过去一个时钟周期,从0开始计数,所以计数器只需要记录到50_000_000-1即可,定义了一个26位的计数器cnt。
设置IDLE,S1 ,S2 ,S3 ,S4这几个状态,IDLE表示空闲状态, 当led输出为4’b0001时,第一个led点亮;经过1秒钟,输出4’b0010时,第二个led点亮;经过1秒钟,输出4’b0100时,第三个led点亮;经过1秒钟,输出4’b1000时,第四个led点亮;经过1秒钟,输出4’b0001时,第一个led点亮······按照上述的过程周而复始,就形成了流水灯。
仿真代码
`timescale 1ns/1ns //仿真尺度
module tb_fsm();
reg tb_clk ;//时钟信号
reg tb_rst_n;//复位信号
wire [3:0] tb_led ;//4个led灯
fsm_led#(.TIME_1S(50)) u_fsm_led(
.clk (tb_clk ) ,
.rst_n (tb_rst_n ) ,
.led (tb_led )
);
parameter CYCLE = 20;
always #(CYCLE / 2) tb_clk = ~tb_clk;//每10ns翻转一次时钟信号
initial begin
tb_clk = 1'b0;
tb_rst_n = 1'b0;
#(CYCLE * 3);
tb_rst_n = 1'b1;
#(CYCLE * 500);//500次循环后停止
$stop;
end
endmodule
3、实现
使用Quartus建立工程后,如下图
仿真
在wave界面,首先restart --> run-all --> Zoom Full,得到如下仿真结果
跑马灯只需要修改状态的二进制表示即可
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
led <= 4'b1001;
end
else if(state_c == IDLE)begin
led <= 4'b00001;
end
else if(state_c == S1)begin
led <= 4'b00011;
end
else if(state_c == S2)begin
led <= 4'b00111;
end
else if(state_c == S3)begin
led <= 4'b01111;
end
else if(state_c == S4)begin
led <= 4'b11111;
end
else begin
led <= led;
end
end
引脚分配