文章目录
- 前言
- 实验手册(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) beginif (!rst_n) begincnt_2us <= 6'd0;endelse if (add_cnt_2us) beginif (end_cnt_2us) begincnt_2us <= 6'd0;endelse begincnt_2us <= cnt_2us + 6'd1;endendelse begincnt_2us <= cnt_2us;end
endassign add_cnt_2us = 1'd1;assign end_cnt_2us = add_cnt_2us && cnt_2us == MAX_2us; always @(negedge rst_n or posedge clk) beginif (!rst_n) begincnt_2ms <= 6'd0;endelse if (add_cnt_2ms) beginif (end_cnt_2ms) begincnt_2ms <= 6'd0;endelse begincnt_2ms <= cnt_2ms + 6'd1;endendelse begincnt_2ms <= cnt_2ms;end
endassign add_cnt_2ms = end_cnt_2us;assign end_cnt_2ms = add_cnt_2ms && cnt_2ms == MAX_2ms; always @(negedge rst_n or posedge clk) beginif (!rst_n) begincnt_2s <= 6'd0;endelse if (add_cnt_2s) beginif (end_cnt_2s) begincnt_2s <= 6'd0;endelse begincnt_2s <= cnt_2s + 6'd1;endendelse begincnt_2s <= cnt_2s;end
endassign 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)beginflag <= 1'b0;end else if(end_cnt_2s)begin flag <= ~flag;//1s取反end else begin flag <= flag;end
endalways @(negedge rst_n or posedge clk) beginif (!rst_n) beginbreath_led <= 4'b0000;end//状态一:从暗到亮else if (!flag)beginbreath_led <= (cnt_2s > cnt_2ms)?4'b1111:4'b0000;end//状态二:从亮到暗else if (flag) beginbreath_led <= (cnt_2s > cnt_2ms)?4'b0000:4'b1111;endelse beginbreath_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 beginclk = 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