赞
踩
发现问题,用技术解决问题。兴趣是自己的源动力 !
呼吸灯的练习的主要目的是对计数器使用的进阶,理解计数器计数使用的基础时间单位的变化,对计数器的影响。
我们知道同一时间段内,如果供给led灯一个脉冲信号的低电平持续的时间越长(高电平持续的时间越短)led灯就越亮,我们就是通过调整PWM实现高低电平的占空来调控led灯的亮度,我们取n个相同的时间段,然后让低电平的持续时间按照相等的时间间隔逐渐增多,这样子我们看上去 的led灯就会越来越亮了。
总的来说就是控制亮和灭的占空比
实现呼吸灯的主要思想和步骤:
1.呼吸灯的原理:亮灭占空比的变化。假设低电平为亮,那么低电平的占空比就会不断升高
2.设定灭到亮、亮到灭分别占用1秒,那么如何调整这一秒中的亮灭占空比
3.假设:想实现灭到亮,那么就会有不同情况的占空比
4.要设置多少个不同情况的占空比呢?假定设置1000个不同情况的占空比(对于1s来说,就是1000ms)
5.有了1000个占空比的情况,接下来就去设置每一个占空比的具体占空比数值
6.此时,从1ms看(1000个占空比中的一个),假设1ms是一个时钟周期
(这里要理解占空比,占空比主要用于描述脉冲信号中高电平(或低电平)持续时间与整个周期时间的比例)
7.低电平的占空比逐渐增大。
8.这是就需要考虑去计数一个比1ms还要小的时间。本设计中用1us表示(也可以用其他数值)
小结:要实现以上,那么就要考虑到如何设计计数器和如何使用计数器了
实现波形图如下:
注意:下面代码中的计数器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
`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
下面代码中,为了计数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] FPGA系列教学
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。