Pulse Width Modulation
脉冲宽度调制PWM
利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术
PWM是一种技术手段,PWM波是在这种技术手段控制下的脉冲波
PWM频率(单位Hz):1秒钟内信号从高电平到低电平再回到高电平的次数(一个周期)/一秒钟PWM有多少个周期
PWM周期 T=1/f
占空比(%):一个脉冲周期内,高电平的时间与整个时钟周期时间的比例
周期: 一个脉冲信号的时间 1s内测周期次数等于频率
脉宽时间: 高电平时间
PWM原理
在一定的频率下,通过不同的占空比 即可得到不同的输出模拟电压
PWM就是在合适的信号频率下,通过一个周期里改变占空比的方式来改变输出的有效电压
PWM输出呼吸灯
一般人眼睛对于80Hz 以上刷新频率则完全没有闪烁感。
频率太小的话 看起来就会闪烁
那么我们平时见到的LED灯,当它的频率大于50Hz的时候,人眼就会产生视觉暂留效果,基本就看不到闪烁了,而是一个常亮的LED灯,
你在1秒内,高电平0.5秒,低电平0.5秒,(频率1Hz)如此反复,那么你看到的电灯就会闪烁,
但是如果是10毫秒内,5毫秒打开,5毫秒关闭,(频率100Hz) 这时候灯光的亮灭速度赶不上开关速度(LED灯还没完全亮就又熄灭了),由于视觉暂留作用 人眼不感觉电灯在闪烁,而是感觉灯的亮度少了 因为高电平时间(占空比)为50% 亮度也就为之前的50% ,
频率很高时,看不到闪烁,占空比越大,LED越亮;
频率很低时,可看到闪烁,占空比越大,LED越亮。
所以,在频率一定下,可以用不同占空比改变LED灯的亮度。 使其达到一个呼吸灯的效果
(79 封私信 / 81 条消息) 这样理解PWM,想不懂都难!!! - 知乎
脉冲周期T:单位时间,ns、us、ms等
脉冲频率f:单位Hz、KHz等,f=1/T
脉冲宽度W:是脉冲高电平持续的时间,单位是时间,ns、us、ms等
占空比D:脉宽W/脉冲周期T,百分数表示
一般情况下,PWM波是幅值、周期(或频率)不变,脉宽(或占空比)可调的脉冲波
控制一个直流电机的转速,我们可以通过改变其两端电压即可;但是该种方法有很大的局限性,可调直流电源构造复杂、成本高昂,应用起来很不现实;电压源→驱动器→直流电机,电压源提供直流电压,不同的驱动器控制不同的直流电机,应用非常灵活。其中驱动器对电机的调速控制就是利用PWM
电机为某相同转速时,红色代表驱动器输出幅值不变的PWM波,蓝色代表可调直流电源输出的电压。两者都是直接作用到负载
当PWM波的占空比越大时,所对应的直流电压与PWM波的幅值越接近;反之与0V越接近;
周期的红色PWM波脉宽下的矩形面积之和与蓝色直流电压的面积相等,即伏秒积相等:
U红(幅值) × ton = U蓝 × T
两端同时除以T,得到如下关系式:
U红(幅值) × 占空比 = U蓝
代码实现
思想:
使用计数器计数,然后和两个值进行比较,一个值是高电平时间h_time,一个值是周期period,在下雨h_time期间,输出高电平;大于h_time期间,输出低电平,到达周期period,计数器清零
端口:
clk:时钟信号
nreset:复位信号,低电平复位
en:使能信号,高电平使能输出
period:PWM的周期
h_time:高电平的时间
pwm:pwn波输出
例如:主时钟96M,产生4MHz的波形,period=96/4=24,h_time=24*0.25=6
module pwm(
input nreset,
input clk,
input en,
input [15:0] period,
input [15:0] h_time,
output reg pwm
);
reg [31:0] CNT;
always@(posedge clk)
begin
if(!nreset)
CNT <= 0;
else if(CNT >= period-1)
CNT <= 0;
else
CNT <= CNT+1;
end
always@(posedge clk)
begin
if(!nreset)
pwm <= 0;
else
begin
if(en==0)
pwm <=0;
else
begin
if(CNT <=h_time-1)
pwm <=1;
else
pwm <=0;
end
end
end
endmodule
可产生任意占空的PWM波,若主时钟为96MHz。
- 产生4MHz,50%占空比:period = 24,h_time = 12;
- 产生3KHz,50%占空比:period = 32000,h_time = 16000;
- 产生宽度10us,间隔100us的脉冲信号:period = 10560,h_time = 960;
ALINX 《ZYNQ那些事儿》
module pwm
#(
parameter N = 16 //pwm bit width
)
(
input clk,
input rst,
input[N - 1:0]period, //pwm step value
input[N - 1:0]duty, //duty value
output pwm_out //pwm output
);
reg[N - 1:0] period_r; //period register
reg[N - 1:0] duty_r; //duty register
reg[N - 1:0] period_cnt; //period counter
reg pwm_r;
assign pwm_out = pwm_r;
always@(posedge clk or posedge rst)
begin
if(rst==1)
begin
period_r <= { N {1'b0} };
duty_r <= { N {1'b0} };
end
else
begin
period_r <= period;
duty_r <= duty;
end
end
//period counter, step is period value
always@(posedge clk or posedge rst)
begin
if(rst==1)
period_cnt <= { N {1'b0} };
else
period_cnt <= period_cnt + period_r;
end
always@(posedge clk or posedge rst)
begin
if(rst==1)
begin
pwm_r <= 1'b0;
end
else
begin
if(period_cnt >= duty_r) //if period counter is bigger or equals to duty value, then set pwm value to high
pwm_r <= 1'b1;
else
pwm_r <= 1'b0;
end
end
endmodule
仿真验证
系统时钟为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