首先看两张PWM在FPGA上实现方式的原理图:引自http://www.stepfpga.com/doc/altera_9breath
呼吸灯设计要求呼吸的周期为2s,也就是说LED灯从最亮的状态开始,第一秒时间内逐渐变暗,第二秒的时间内再逐渐变亮,依次进行。
本设计中需要两个计数器cnt1和cnt2,cnt1随系统时钟同步计数(系统时钟上升沿时cnt1自加1)范围为0~T,cnt2随cnt1的周期同步计数(cnt1等于T时,cnt2自加1)范围也是0~T,这样每次cnt1在0~T的计数时,cnt2为一个固定值,相邻cnt1计数周期对应的cnt2的值逐渐增大,我们将cnt1计数0~T的时间作为脉冲周期,cnt2的值作为脉冲宽度,则占空比 = cnt2/T,占空比从0%到100%的时间 = cnt2*cnt1 = T^2 = 1s = 12M个系统时钟,T = 2400,我们定义CNT_NUM = 2400作为两个计数器的计数最大值。
我对此原理的理解为:
1.定义两个计数器,cnt1和cnt2,具体大小由具体情况计算确定,以及一个PWM增减方向指示器pwm_flag这里我就简单的取个值,做个实验。
output reg [2:0] cnt1; //定义脉冲周期计数器cnt1,最大值为4,共5次
output reg [2:0] cnt2; //脉冲宽度计数器cnt2
reg pwm_flag; //定义pwm增减方向,0加1减
2.cnt1每来一个时钟上升沿则计数加一,直至计数到设定值后清零
always@(posedge clk or negedge rst_n) //脉冲周期计数器模块
begin
if(!rst_n)
cnt1 <= 3'd0;
else if(cnt1 == 3'd4)
cnt1 <= 3'd0;
else
cnt1 <= cnt1 + 1'b1;
end
3.当cnt1计数值满的时候,对cnt2进行加1或者减1操作,若cnt2从0开始则加一,若cnt2已经到达最大值则减一
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
cnt2 <= 3'd0;
pwm_flag <= 0;
end
else
begin
if(cnt1 == 3'd4) //当cnt1计数满时开始对cnt2进行操作
begin
if(!pwm_flag) //根据flag方向来进行加一或者减一操作,注意操作完后对flag的修改
begin
if(cnt2 == 3'd4)
begin
pwm_flag <= 1;
end
else
cnt2 <= cnt2 + 1'b1;
end
if(pwm_flag)
begin
if(cnt2 == 3'd0)
begin
pwm_flag <= 0;
end
else
cnt2 <= cnt2 - 1'b1;
end
end
else //若cnt1还没满,则cnt2保持不变
cnt2 <= cnt2;
end
end
由此图可见:
当cnt1的值比cnt2大时,输出为高电平,而随着cnt2的自增,cnt1比cnt2大的时间越来越短,便可实现不同占空比的方波的输出。
故最终的输出pwm_out的逻辑为:
pwm_out = (cnt1
最终仿真结果为:
可见输出方波占空比在规则变化,且cnt2的值也是。