这两天做了呼吸灯实验,今天和大家分享一下。我们采用PWM脉冲宽度调制输出连续的、占空比变化的脉冲信号来实现LED由灭到亮,再由亮到灭。
1.呼吸灯的原理
首先我们要知道,影响呼吸灯亮灭有2个因素:1.PWM波的占空比;2.PWM波的周期。
需要两者配合,方能实现效果较佳的呼吸灯。
我们假设占空比为50%,周期为2s,那么我们看到的就是LED亮一秒,灭一秒。但是如果我们把周期缩小到10ms,占空比仍然是50%。由于人眼的视觉残留效应,那么我们看见的就是LED的亮度会低一些,并不会出现亮一会灭一会的情况。
所以,根据上述分析,我们可以把PWM波的周期缩小到一个很小的值(比如10ms),然后让占空比每个周期都产生变化。(比如:从灭到亮时,每10ms占空比增加1%,增加到100%时再每10ms占空比减小1%,实现从亮到灭。这样整个亮灭过所需时间就是2s=10ms*100+10ms*100。)
2.源文件
首先我们需要两个计数器,一个10ms计数,另一个其实只要统计10ms的次数,200次就是2s。
然后因为在1s时我们需要将状态从灭到亮改为从亮到灭,所以我们需要一个用来切换状态的标志位及相应代码。
剩下就是占空比的调节和pwm的输出。
占空比在相应状态下,通过每次步进5000的计数值来每10ms改变一次1%的占空比;另外,我们要注意到PWM的信号输出应该和10ms计数器同步,10ms计数值小于dutycycle时,pwm输出0。反之,输出1。
`timescale 1ns / 1ps
module BreathLed(clk,rst_n,led);
input wire clk;
input wire rst_n;
output wire led;
//定义一些常量,时钟主频50M,计满10ms需要计数50_0000次,计数满2s,只要重复200次即可
parameter CLK_10ms = 500000-1;
parameter Times_2s = 200-1;
parameter ChangeTime =100-1;
//每次占空比改变整个周期的1%,0.01*500000
parameter pwm_offset = 5000;
//需要两个计数器,一个工作状态改变标志位
reg [19:0] CLK_10MS;
reg [7:0] Times2s;
reg pwm;
reg [19:0] dutyCycle;
reg changeflag;
assign led = ~pwm;
/*--------10ms计数器---*/
always @(posedge clk or negedge rst_n)
begin
if(rst_n==1'b0)
begin
CLK_10MS <='d0;
end
else if(CLK_10MS==CLK_10ms) CLK_10MS <='d0;
else begin
CLK_10MS <= CLK_10MS+1'b1;
end
end
/*-----2s计数器----*/
always @(posedge clk or negedge rst_n)
begin
if(rst_n==1'b0) Times2s <='d0;
else if(CLK_10MS==CLK_10ms)begin
if(Times2s==Times_2s) Times2s <='d0;
else Times2s <= Times2s+1'b1;
end
end
//-------亮灭状态管理切换----
always @(posedge clk or negedge rst_n)
begin
if(rst_n==1'b0)
changeflag <= 'b0;
else if(Times2s==ChangeTime && CLK_10MS==CLK_10ms)
begin
changeflag <= 1'b1;//状态改为:从亮到灭
end
else if(Times2s==Times_2s && CLK_10MS==CLK_10ms)
begin
changeflag <= 1'b0;//状态改为:从灭到亮
end
end
//-----占空比调节
always @(posedge clk or negedge rst_n)
begin
if(rst_n==1'b0)dutyCycle='d0;
else if(changeflag==1'b0)
begin
if(CLK_10MS==CLK_10ms) dutyCycle <= dutyCycle+pwm_offset;
else dutyCycle <= dutyCycle;
end
else if(changeflag ==1'b1)
begin
if(CLK_10MS==CLK_10ms) dutyCycle <= dutyCycle-pwm_offset;
else dutyCycle <= dutyCycle;
end
end
always @(posedge clk or negedge rst_n)
begin
if(rst_n==1'b0) pwm <= 1'b0;
else if(CLK_10MS < dutyCycle) pwm <= 1'b0;
else pwm <= 1'b1;
end
endmodule
3.Testbench代码
/*-------testbench编写基本流程------*/
//* module Test_bench();
//通常无输入出 通常无输入出
//信号或变量声明定义
//逻辑设计中输入对应 reg型
//逻辑设计中输出对应 wire 型
//使用 initial 或 always语句产生激励 语句产生激励
//例化待测试模块
//监控和比较输出响应
//
module BreathLed_simul();
//变量和信号的声明
reg clk;
reg rst_n;
wire led;
parameter CLK_10ms = 500000-1;
parameter Times_2s = 200-1;
parameter ChangeTime =100-1;
//每次占空比改变整个周期的1%,0.01*500000
parameter pwm_offset = 5000;
//例化一个我们之前写的模块
BreathLed #(.CLK_10ms(CLK_10ms),
.Times_2s(Times_2s),
.ChangeTime(ChangeTime),
.pwm_offset(pwm_offset))
u1(
.clk(clk),
.rst_n(rst_n),
.led(led)
);
initial
begin
//初始化激励(时钟和复位信号)
clk=1'b0;
rst_n=1'b0;
#10
rst_n=1'b1;
end
always #(10) clk=~clk;
endmodule