【重拾FPGA】PWM的实现方法


首先看两张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的值也是。