FPGA产生可调频率占空比的PWM
1.PWM产生原理
首先需要一个N比特的计数器,此计数器最大值为2N,最小值为0。为了控制PWM的频率,需要一个步进值“period”,为了控制占空比,需要一个阈值“duty”。
如下图所示,计数器(cnt)在每个系统时钟周期中增加“period”大小,当cnt < duty 时,输出pwm_out为0,当cnt >= duty 时,输出pwn_out 置1,调整period和duty的值即可控制PWM波形的频率和占空比。
计算公式为 period = 2N * (Tsysclk / Tpwm) = 2N * (fpwm /fsysclk) , duty = 2N * (1 - DC).
式中Tsysclk、fsysclk为系统时钟的周期、频率,Tpwm、fpwm为产生的PWM的周期、频率,DC为产生PWM的占空比(小于1的数,如30%)。位宽N越大,产生的PWM频率越准确。
2.Verilog代码
1 module pwm 2 #( 3 parameter N = 16 //pwm bit width 4 ) 5 ( 6 input clk, 7 input rst, 8 input[N - 1:0]period, //pwm step value 9 input[N - 1:0]duty, //duty value 10 11 output pwm_out //pwm output 12 ); 13 14 reg[N - 1:0] period_r; //period register 15 reg[N - 1:0] duty_r; //duty register 16 reg[N - 1:0] period_cnt; //period counter 17 reg pwm_r; 18 assign pwm_out = pwm_r; 19 always@(posedge clk or posedge rst) 20 begin 21 if(rst==1) 22 begin 23 period_r <= { N {1'b0} }; 24 duty_r <= { N {1'b0} }; 25 end 26 else 27 begin 28 period_r <= period; 29 duty_r <= duty; 30 end 31 end 32 //period counter, step is period value 33 always@(posedge clk or posedge rst) 34 begin 35 if(rst==1) 36 period_cnt <= { N {1'b0} }; 37 else 38 period_cnt <= period_cnt + period_r; 39 end 40 41 always@(posedge clk or posedge rst) 42 begin 43 if(rst==1) 44 begin 45 pwm_r <= 1'b0; 46 end 47 else 48 begin 49 if(period_cnt >= duty_r) //if period counter is bigger or equals to duty value, then set pwm value to high 50 pwm_r <= 1'b1; 51 else 52 pwm_r <= 1'b0; 53 end 54 end 55 56 endmodule
3.仿真验证
系统时钟为50MHz,使用上述代码产生频率200Hz,占空比30%的 PWM,取N = 32,计算得period = 17197.87 = 17198,duty = 3006477107
仿真测试代码
`timescale 1ns / 1ps module pwm_tb(); parameter N = 32; //计数器位宽 //reg define reg sys_clk ; reg rst ; reg [N-1:0] period ; reg [N-1:0] duty ; //wire define wire pwm_out ; //初始化输入信号 initial begin sys_clk = 1'b1; rst = 1'b1; period = 32'd0; duty = 32'd0; #10 rst = 1'b0; period = 32'd17198; //步进值 duty = 32'd3_006_477_107; //占空比阈值 end //sys_clk:模拟系统时钟,每10ns电平翻转一次,周期为20ns,频率为50Mhz always #10 sys_clk = ~sys_clk; //例化PWM模块 pwm #( .N (N) //pwm bit width ) pwm_inst ( .clk (sys_clk ), .rst (rst ), .period (period ), //pwm step value .duty (duty ), //duty value .pwm_out(pwm_out) //pwm output ); endmodule
结果如下图,波形成功产生
【参考资料】
1. ALINX 《ZYNQ那些事儿》