文章目录
- 前言
- 实验手册(EP4CE6F17C8)
- 一、实验目的
- 二、实验原理
- 理论原理
- 三、系统架构设计
- 四、模块说明
- 1.模块端口信号列表
- 2.状态转移图
- 3.时序图
- 五、仿真波形图
- 六、引脚分配
- 七、代码实现
- 八、仿真代码
- 九、板级验证效果
前言
网上找资料时一般出现的是led灯1s从暗到亮,下一个1s从亮到暗,所以在此记录一篇2s的呼吸灯,也为日后自己复习提供一点帮助,结尾有源码。
实验手册(EP4CE6F17C8)
一、实验目的
四个LED灯2s从暗到亮,下一个2s从亮到暗,循环显示。
二、实验原理
理论原理
在fpga中,呼吸灯的实现是通过控制占空比的多少,输出两段,第一段:由暗到亮,占空比由0%到100%逐步递增,第二段:由亮到暗,占空比由100%到0%逐步递减
三、系统架构设计
- 呼气和吸气,周期为4秒。考虑呼气的过程,就是让led灯要有亮灭的变化,从暗到亮,再从亮到暗。
- 为了led的亮度变化比较流畅,也就是每隔2ms我们给led灯一个新的亮度,我们将2秒拆分为1000份的2ms,为了实现led灯亮度变化,每一份2ms波形的占空比都不能相同,且必须是连续增加或减小的。
- 2ms的时间内,我们一共有1000个状态,每一个状态就是一个占空比时间为2us。所以我们需要三个计数器。最后利用后面两个计数器cnt_2ms和cnt_2s的大小进行比较,来改变一个时钟周期内的占空比。
四、模块说明
1.模块端口信号列表
端口信号 | 信号类别 | 信号名称 | 信号作用 |
---|---|---|---|
input | wire | clk | 时钟信号 |
input | wire | rst_n | 复位信号 |
output | reg | led | led信号 |
2.状态转移图
3.时序图
该图是用time gen来画的
五、仿真波形图
-
这部分是从暗到亮的仿真波形,在breath_led一行我们可以很明显的看到从一开始基本看不见1111,到后面1111的占空比逐步扩大。
-
这部分则是从亮到暗,breath_led一行,0000的占空比逐步扩大
六、引脚分配
七、代码实现
module breath_led (
input clk ,
input rst_n ,
output reg [3:0] breath_led
);
parameter MAX_2us = 10'd99 ; //2us
//之所以2ms和2s都是999,这是因为这两个的开始信号我分别设置为cnt_2us的结束信号、cnt_2ms的结束信号
//记了1000次的us信号就等于2ms,2s也是这个道理
parameter MAX_2ms = 19'd999; //2ms = 1000 * 2us
parameter MAX_2s = 19'd999; //2s = 1000 * 2ms
reg [9:0] cnt_2us;
reg [18:0] cnt_2ms;
reg [18:0] cnt_2s;
reg [1:0] flag; //状态转变标志,判断是从暗到亮还是从亮到暗
wire add_cnt_2us; //计数器开始信号
wire end_cnt_2us; //计数器结束信号
wire add_cnt_2ms;
wire end_cnt_2ms;
wire add_cnt_2s;
wire end_cnt_2s;
always @(negedge rst_n or posedge clk) begin
if (!rst_n) begin
cnt_2us <= 6'd0;
end
else if (add_cnt_2us) begin
if (end_cnt_2us) begin
cnt_2us <= 6'd0;
end
else begin
cnt_2us <= cnt_2us + 6'd1;
end
end
else begin
cnt_2us <= cnt_2us;
end
end
assign add_cnt_2us = 1'd1;
assign end_cnt_2us = add_cnt_2us && cnt_2us == MAX_2us;
always @(negedge rst_n or posedge clk) begin
if (!rst_n) begin
cnt_2ms <= 6'd0;
end
else if (add_cnt_2ms) begin
if (end_cnt_2ms) begin
cnt_2ms <= 6'd0;
end
else begin
cnt_2ms <= cnt_2ms + 6'd1;
end
end
else begin
cnt_2ms <= cnt_2ms;
end
end
assign add_cnt_2ms = end_cnt_2us;
assign end_cnt_2ms = add_cnt_2ms && cnt_2ms == MAX_2ms;
always @(negedge rst_n or posedge clk) begin
if (!rst_n) begin
cnt_2s <= 6'd0;
end
else if (add_cnt_2s) begin
if (end_cnt_2s) begin
cnt_2s <= 6'd0;
end
else begin
cnt_2s <= cnt_2s + 6'd1;
end
end
else begin
cnt_2s <= cnt_2s;
end
end
assign add_cnt_2s = end_cnt_2ms;
assign end_cnt_2s = add_cnt_2s && cnt_2s == MAX_2s;
//每隔两秒,信号翻转,进入下一状态
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
flag <= 1'b0;
end
else if(end_cnt_2s)begin
flag <= ~flag;//1s取反
end
else begin
flag <= flag;
end
end
always @(negedge rst_n or posedge clk) begin
if (!rst_n) begin
breath_led <= 4'b0000;
end
//状态一:从暗到亮
else if (!flag)begin
breath_led <= (cnt_2s > cnt_2ms)?4'b1111:4'b0000;
end
//状态二:从亮到暗
else if (flag) begin
breath_led <= (cnt_2s > cnt_2ms)?4'b0000:4'b1111;
end
else begin
breath_led <= breath_led;
end
end
endmodule
八、仿真代码
`timescale 1ns/1ns
module breath_led_tb ();
//激励信号
reg clk;
reg rst_n;
//响应信号
wire [3:0] breath_led;
parameter CYCLE = 20;
//完整代码中的计数时间长,故在此重新赋予短时的参数,以便观察结果
parameter MAX_2us = 10;
parameter MAX_2ms = 20;
parameter MAX_2s = 20;
//产生时钟信号
always #(CYCLE/2) clk = ~clk;
//产生激励
initial begin
clk = 1'b0;
rst_n = 1'b0;
#(CYCLE);
rst_n = 1'b1;
#(2*(MAX_2us)*(MAX_2ms)*(MAX_2s)*CYCLE);//检测一个大周期:从暗到亮,从亮到暗
$stop;
end
//实例化
breath_led #(
.MAX_2us(MAX_2us),
.MAX_2ms(MAX_2ms),
.MAX_2s(MAX_2s)
)
u_breath_led(
.clk(clk),
.rst_n(rst_n),
.breath_led(breath_led)
);
endmodule