呼吸灯 verilog FPGA 基础练习8

呼吸灯 verilog FPGA 基础练习8

发现问题,用技术解决问题。兴趣是自己的源动力 !

前言

呼吸灯的练习的主要目的是对计数器使用的进阶,理解计数器计数使用的基础时间单位的变化,对计数器的影响。

一、呼吸灯

1.1 呼吸灯原理

我们知道同一时间段内,如果供给led灯一个脉冲信号的低电平持续的时间越长(高电平持续的时间越短)led灯就越亮,我们就是通过调整PWM实现高低电平的占空来调控led灯的亮度,我们取n个相同的时间段,然后让低电平的持续时间按照相等的时间间隔逐渐增多,这样子我们看上去 的led灯就会越来越亮了。

总的来说就是控制亮和灭的占空比

1.2 实现方案

实现呼吸灯的主要思想和步骤:

1.呼吸灯的原理:亮灭占空比的变化。假设低电平为亮,那么低电平的占空比就会不断升高
2.设定灭到亮、亮到灭分别占用1秒,那么如何调整这一秒中的亮灭占空比
3.假设:想实现灭到亮,那么就会有不同情况的占空比
4.要设置多少个不同情况的占空比呢?假定设置1000个不同情况的占空比(对于1s来说,就是1000ms)
5.有了1000个占空比的情况,接下来就去设置每一个占空比的具体占空比数值
6.此时,从1ms看(1000个占空比中的一个),假设1ms是一个时钟周期
(这里要理解占空比,占空比主要用于描述脉冲信号中高电平(或低电平)持续时间与整个周期时间的比例)
7.低电平的占空比逐渐增大。
8.这是就需要考虑去计数一个比1ms还要小的时间。本设计中用1us表示(也可以用其他数值)
小结:要实现以上,那么就要考虑到如何设计计数器和如何使用计数器了
实现波形图如下:
在这里插入图片描述

1.2.1 功能代码

注意:下面代码中的计数器1s和1ms的理解,会在1.2.4给出。

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 , //系统时钟50MHz
input wire sys_rst_n , //全局复位

 output reg led_out //输出信号,控制led灯
 );
// *******************************************
// 实现呼吸灯的主要思想和步骤:
// 1.呼吸灯的原理:亮灭占空比的变化。假设低电平为亮,那么低电平的占空比就会不断升高
// 2.设定灭到亮、亮到灭分别占用1秒,那么如何调整这一秒中的亮灭占空比
// 3.假设:想实现灭到亮,那么就会有不同情况的占空比
// 4.要设置多少个不同情况的占空比呢?假定设置1000个不同情况的占空比(对于1s来说,就是1000ms)
// 5.有了1000个占空比的情况,接下来就去设置每一个占空比的具体占空比数值
// 6.此时,从1ms看(1000个占空比中的一个),假设1ms是一个时钟周期
// (这里要理解占空比,占空比主要用于描述脉冲信号中高电平(或低电平)持续时间与整个周期时间的比例)
// 7.低电平的占空比逐渐增大。
// 8.这是就需要考虑去计数一个比1ms还要小的时间。本设计中用1us表示(也可以用其他数值)
// 小结:要实现以上,那么就要考虑到如何设计计数器和如何使用计数器了
// *******************************************


// 实现1s的计数器,注意理解cnt_1s,这个不是代表该值为2就是2s,而是表示达到1s的过程和达到1s为目的,1s中包含的ms值
reg [9:0]cnt_1s;
reg [9:0]cnt_1ms;
reg [6:0]cnt_1us;
reg led_en;
// 另外需要理解时间怎么计数到1us:
// 系统时钟是50MHZ,也就是说50_000_000次对上升沿的计数就是1s,那么1us就是50次时钟上升沿的计数


// 为了将1s分成1000个不同占空比的情况,因此计数器递增间隔为1ms
always@(posedge sys_clk or negedge sys_rst_n) begin
	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_1US_MAX & cnt_1us == CNT_1US_MAX)
		cnt_1s <= cnt_1s +10'b1;
end

// 考虑1ms内的占空比变化,如果将1ms分成1000份,就是1us,那么也是递增1us
always@(posedge sys_clk or negedge sys_rst_n) begin
	if(sys_rst_n == 1'b0) 
		cnt_1ms <= 10'b0; 
	else if(cnt_1ms == CNT_1MS_MAX & cnt_1us == CNT_1US_MAX) // 考虑归0,这里思考cnt_1ms刚刚等于10'd999就是达到1ms了吗?
		cnt_1ms <= 10'b0;                              
	else if(cnt_1us == CNT_1US_MAX)
		cnt_1ms <= cnt_1ms +10'b1;
	else
		cnt_1ms <= cnt_1ms;
end

// 计数1us,用于计数的基础时间单位
always@(posedge sys_clk or negedge sys_rst_n) begin
	if(sys_rst_n == 1'b0) 
		cnt_1us <= 10'b0; 
	else if(cnt_1us == CNT_1US_MAX) // 考虑归0
		cnt_1us <= 10'b0; 
	else	
		cnt_1us <= cnt_1us + 6'b1;
end

// 设定 亮到灭和灭到亮的使能
always@(posedge sys_clk or negedge sys_rst_n) begin
	if(sys_rst_n == 1'b0) 
		led_en <= 1'b0;  // 0是灭到亮
	else if(cnt_1s == CNT_1S_MAX & cnt_1ms == CNT_1MS_MAX & cnt_1us == CNT_1US_MAX ) // 一秒切换一次
		led_en <= ~led_en;
	else 
		led_en <= led_en;
end

// 通过计数器来控制灯的亮灭占空比
always@(posedge sys_clk or negedge sys_rst_n) begin
	if(sys_rst_n == 1'b0) 
		led_out <= 1'b1;  // 低是亮
	else if( (led_en == 1'b0 & cnt_1ms <= cnt_1s)  ||  (led_en == 1'b1 & cnt_1ms > cnt_1s)      ) // 由ms控制us,达到亮的时间
		led_out <= 1'b0;
	else	
		led_out <= 1'b1;
end

endmodule

1.2.2 仿真代码

`timescale 1ns/1ns
module tb_top();


//\* Parameter and Internal Signal \//

//wire define
wire led_out ;

 //reg define
 reg sys_clk ;
 reg sys_rst_n ;

 ///
 //\* Main Code \//
 
 //初始化系统时钟、全局复位
 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;

 
 //\* Instantiation \//
 
 //-------------------- 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 ), //input sys_clk
 .sys_rst_n (sys_rst_n ), //input sys_rst_n

 .led_out (led_out ) //output led_out
 );

 endmodule

1.2.3 仿真结果

在这里插入图片描述

1.2.4 计数器的基本时间单位

下面代码中,为了计数1us,计数器的基本时间单位是一个时钟周期。当一个时钟clk的上升沿采集到cnt_1us == CNT_1US_MAX,那么就是1us。

这里需要理解,如果以一个clk的一个时钟周期作为1us的计数单位,那么下面的时间是不可再分的)。如果没有理解,再往下看。

always@(posedge sys_clk or negedge sys_rst_n) begin
   if(sys_rst_n == 1'b0) 
   	cnt_1us <= 10'b0; 
   else if(cnt_1us == CNT_1US_MAX) // 考虑归0
   	cnt_1us <= 10'b0; 
   else	
   	cnt_1us <= cnt_1us + 6'b1;
end

下面代码中,为了计数1ms,使用的基本时间单位是1us。很多人以为(包括我),只要cnt_1ms == 10’d999,就是1ms了,其实不是。计数的最基本的时间单位看,计数器的基本时间单位是一个时钟周期。如果当前cnt_1ms == 10’d999,clk的上升沿采样到cnt_1ms == 10’d999,你可以思考以下,这个第10’d999ms的时间,真的完全过去了吗?

注意:时间是一个过程量,之所以在计数cnt_1us的时候,直接用clk上升沿采样到了当前值,是因为我们使用的是一个clk周期就是最基本的时间单位,当采样到了,那么采样到的这个数值对应的时间已经完成了。

always@(posedge sys_clk or negedge sys_rst_n) begin
	if(sys_rst_n == 1'b0) 
		cnt_1ms <= 10'b0; 
	else if(cnt_1ms == CNT_1MS_MAX & cnt_1us == CNT_1US_MAX) // 考虑归0,这里思考cnt_1ms刚刚等于10'd999就是达到1ms了吗?
		cnt_1ms <= 10'b0;                              
	else if(cnt_1us == CNT_1US_MAX)
		cnt_1ms <= cnt_1ms +10'b1;
	else
		cnt_1ms <= cnt_1ms;
end

可以结合时钟来理解
在这里插入图片描述
对于1s,其基本时间单位是ms,ms的基本时间单位是us(us最基本的时间单位了),所以当清零或自加时要考虑下面的基本时间单位。

always@(posedge sys_clk or negedge sys_rst_n) begin
	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_1US_MAX & cnt_1us == CNT_1US_MAX)
		cnt_1s <= cnt_1s +10'b1;
end

小结:计数器的计数,对于清零和自增,需要考虑一层一层基本时间单位的划分,要找到最小的那个基本时间单位

总结

  • 核心思想:要理解计数的基本时间单位是什么,明白时间的过程量的性质,是否真正达到这个时间了吗?
  • 知识点总结:
    1. 如何实现1us、1ms、1s?
    2. 基本时间单位是什么,会对计数器有哪些影响
    3. 呼吸灯产生的原理是什么?
  • 欢迎一起交流学习,如有错误之处,还请各位指正。

参考资料

[1] FPGA系列教学

  • 35
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值