基于FPGA的呼吸灯设计

描述

设计规划

呼吸灯的效果是LED灯在一段时间内从完全熄灭的状态逐渐变到最亮,再在同样的时间段内逐渐达到完全熄灭的状态。这里我们需要实现1s内实现从灭到亮,1s内从亮到灭。

LED的明亮有两种方式,第一种是在安全范围内给LED灯不同的供电电压可以控制明灭;第二种是通过控制PWM的 占空比 ,同一时间段内供给LED灯一个脉冲信号的低电平持续时间越长,LED灯越亮。我们取n个相同的时间段,让低电平的持续时间按照相等的时间间隔逐渐增多,led灯就会越来越亮了。

首先我们将1s分为1000个时间段,每段1ms。再将1ms分为1000个时间小段,每段1us。第一个1ms内亮0us(灭),并在第二个1ms内亮1us,...,在第1000个1ms内亮999us。划分的等级越多,看上去效果越好,但是人眼分辨率有限,大于这个分辨率就看起来几乎没有差异。

我们至少需要三个计数器,用来计时1s,1ms,1us。cnt_1us需要从0计数到49,不复位就计数,计满就清零。cnt_1ms完全没必要重新产生,每当cnt_1ms计数到999且cnt_1us计数到49,cnt_1ms就清零。cnt_1s同理在cnt_1ms计数到999且cnt_1us计数到49时就清零。

脉冲信号

led_out要在不同时间段低电平持续时长慢慢增加,需要一个小技巧。观察上图,LED为低电平时,cnt_1s是0时,cnt_1ms是1-999;cnt_1s是1时,cnt_1ms是2-999...

脉冲信号

LED灯从亮到灭是相反的过程,LED灯为高电平时,cnt_1s>cnt_1ms。

区分从灭到亮和从亮到灭,可以使用一个电平标志信号(使能)cnt_1s_en。cnt_1s_en为低电平时实现呼吸灯从灭到亮的过程,cnt_1s_en为高的时候实现呼吸灯从亮到灭的过程,cnt_1s清零时cnt_1s_en取反。

脉冲信号

脉冲信号

编写代码

module breath_led
#(
parameter CNT_1US_MAX = 6'd49 ,
parameter CNT_1MS_MAX = 10'd999 ,
parameter CNT_1S_MAX = 10'd999
)
(
input wire sys_clk , 
input wire sys_rst_n , 
output reg led_out 
 );


 //reg define
 reg [5:0] cnt_1us ;
 reg [9:0] cnt_1ms ;
 reg [9:0] cnt_1s ;
 reg cnt_1s_en ;


 //cnt_1us:1us计数器
 always@(posedge sys_clk or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 cnt_1us <= 6'b0;
 else if(cnt_1us == CNT_1US_MAX)
 cnt_1us <= 6'b0;
 else
 cnt_1us <= cnt_1us + 1'b1;


 //cnt_1ms:1ms计数器
 always@(posedge sys_clk or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 cnt_1ms <= 10'b0;
 else if(cnt_1ms == CNT_1MS_MAX && cnt_1us == CNT_1US_MAX)
 cnt_1ms <= 10'b0;
 else if(cnt_1us == CNT_1US_MAX)
 cnt_1ms <= cnt_1ms + 1'b1;


 //cnt_1s:1s计数器
 always@(posedge sys_clk or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 cnt_1s <= 10'b0;
 else if(cnt_1s == CNT_1S_MAX && cnt_1ms == CNT_1MS_MAX
 && cnt_1us == CNT_1US_MAX)
 cnt_1s <= 10'b0;
 else if(cnt_1ms == CNT_1MS_MAX && cnt_1us == CNT_1US_MAX)
 cnt_1s <= cnt_1s + 1'b1;


 //cnt_1s_en:1s计数器标志信号
 always@(posedge sys_clk or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 cnt_1s_en <= 1'b0;
 else if(cnt_1s == CNT_1S_MAX && cnt_1ms == CNT_1MS_MAX
 && cnt_1us == CNT_1US_MAX)
 cnt_1s_en <= ~cnt_1s_en;


 //led_out:输出信号连接到外部的led灯
 always@(posedge sys_clk or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 led_out <= 1'b0;
 else if((cnt_1s_en == 1'b1 && cnt_1ms < cnt_1s)||
 (cnt_1s_en == 1'b0 && cnt_1ms > cnt_1s))
 led_out <= 1'b0;
 else
 led_out <= 1'b1;
 endmodule

参数定义,输入输出定义,中间信号(计数器和使能信号)定义都是很熟悉的。

1us计数器 :复位有效时拉低电平;计满时清零;其他情况+1。

1ms计数器 :复位有效时拉低电平;计满时清零(计满条件是达到CNT_1MS_MAX和CNT_1US_MAX);达到CNT_1US_MAX时+1。

1s计数器 :复位有效时拉低电平;计满时清零(计满条件是达到CNT_1S_MAX,CNT_1MS_MAX和CNT_1US_MAX);达到CNT_1MS_MAX且CNT_1US_MAX时+1。

1s使能 :复位有效时拉低电平,计满1s时取反(达到CNT_1S_MAX,CNT_1MS_MAX和CNT_1US_MAX,和1s计数器的清零条件一样)。

led_out :复位有效时拉低电平,使能为高电平且cnt_1mscnt_1s时led_out拉低,反之拉高。

脉冲信号

编写testbench

`timescale 1ns/1ns
module tb_breath_led();
//wire define
wire led_out ;
//reg define
reg sys_clk ;
reg sys_rst_n ;


 //初始化系统时钟、全局复位
 initial begin
 sys_clk = 1'b1;
 sys_rst_n <= 1'b0;
 #20
 sys_rst_n <= 1'b1;
 end


//sys_clk:模拟系统时钟,每10ns电平翻转一次,周期为20ns,频率为50MHz
 always #10 sys_clk = ~sys_clk;


//-------------------- breath_led_inst --------------------
 breath_led
 #(
 .CNT_1US_MAX(6'd4 ),
 .CNT_1MS_MAX(10'd9 ),
 .CNT_1S_MAX (10'd9 )
 )
 breath_led_inst
 (
 .sys_clk (sys_clk ), 
 .sys_rst_n (sys_rst_n ), 
 .led_out (led_out ) 
 );


 endmodule

信号定义,初始化,规定时钟频率,参数设置(参数设置得小一点节省时间),实例化

对比波形

cnt_1ns,cnt_1ms,cnt_1s,cnt_1s_en是中间信号,查看波形需要手动添加

脉冲信号

将要查看的信号拖进波形图中

脉冲信号

我们可以观察到,计数器的最大值分别为4,9,9,与我们在testbench中设置的参数一致。输出信号的波形也达到了预期效果,先由灭到亮,再由亮到灭

分配管脚

脉冲信号

全编译后上板验证

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分