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 =  2* (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那些事儿》

posted @ 2021-10-31 16:43  IntoTheSky  阅读(5533)  评论(0编辑  收藏  举报