PWM(Pulse Width Modulation)简介
PWM,也就是脉冲宽度调制,用于将一段信号编码为脉冲信号,也就是方波信号。多用于在数字电路中驱动负载随时间变化的电子元件,如LED,电机等。
在单片机中,我们常用PWM来驱动LED的暗亮程度,电机的转速等。
我们知道,在数字电路中,电压信号是离散的: 不是 0(0V) 就是 1(5V或者3.3V), 那么如何输出介于 0v 和 5V之间的某个电压值呢?
我们先来举个实际的例子,一看就懂,胜过千言万语。
如下图,要让让数字信号模拟出 3.7V 的电压输出。可以先假想:3.7V的电压输出是由多个周期时间为T ,电压值 都 为3.7V的信号 持续输出形成的。
根据PWM原理,我画出下面等效脉冲信号波形图(红色) 。PWM的理念是:连续的信号可以使用独立的方波信号模拟出来,手段就是调整输出的脉冲宽度,以达到同样的效果。你会怀疑:这真的可以达到同样的效果吗?
别急,让我们继续往后看。
如果一个周期T内的均值电压等于3.7V,那么,整体的输出就是3.7V,因为整体只不过是n个周期不断的重复而已。那么我们的主要问题就是如何让一个调制周期T时间内的均值电压等于3.7V。下面就开始计算。
设:脉冲信号的值随时间变化的函数为:
因为这里是数字电路的背景下的,所以v的值只能取 0v 或者 5v。
设:在一个周期T时间中,高电平持续时间占T的百分比为 D,则低电平持续时间在周期T中占的百分比为 1-D。
我们对 一个调制周期T内的电压值对时间积分,然后除以周期T,就得到了这个周期的输出电压均值。
由于这个积分图形是方波,所以很好计算(就是面积 除以T)。
可以看出,1个调制周期内,输出的电压均值只和D有关。也就是高电平信号占持续时间占这个周期的百分比决定这个周期内的输出电压。
上面说了,要让这个均值等于3.7v,则求出D为:0.74
那也就是说:如果在一个调制周期中,高电平持续时间占周期的百分比为74%,则整体输出的信号就是3.7V。这个百分比就是下面要说的占空比
占空比(duty cycle)
有了前面的知识,相信你已经对占空比理解了,其实很好理解 ,占空比就是 在一段调制周期时间内,某个信号持续的时间占这个时间段的百分比。
下面给出占空比的公式
D: 占空比
PW: 脉冲宽度(调制周期中脉冲持续时间)
T: 一个调制周期
下面是一个占空比不断变化的图示
所以我们可以很自然的得出结论:
低占空比意味着输出的能量低,因为在一个周期内大部分时间信号处于关闭状态,如果pwm控制的负载为led,则具体表现例如led灯很暗.
高占空比意味着输出的能量高,在一个周期内,大部分时间信号处于on状态,具体表现为LED比较亮。
当占空比为100%时,表示 fully on,也就是在一个周期内,信号都处于on状态,具体表现为led亮度到达100%
为0%时则表示 fully off,在一个周期内,一直处于off状态.具体表现为led熄灭。
现在一切都明了了:脉宽调制,脉宽调制,脉宽调制,这个宽,不是物体的宽度,而是信号的持续时间大小,它可以用占空比去衡量,占空比越大,脉冲越宽,反之亦然。
下面扩展一下,加深理解:用PWM模拟出如下的正玄波
道理是一样的,只不过,占空比要变化,这里就是逐渐增加。
PWM的频率 (PWM frequency)
pwm的频率决定了输出信号 on 和 off 的切换速度。比如你想让一盏电灯的在1s内的亮度降低为原来的一半,你可以前0.5s 打开开关,后0.5s关闭开关。也就是仅仅用了1个调制周期,那么这个频率就是1Hz,显然这个效果是不明显的,我门的电脑屏幕的刷新60Hz,才能足够顺滑。人眼的极限一般是50Hz。要达到50Hz在,意味着你要在1s时间内切换开关50次,单身一辈子的手速也不可能达到这么快~。还是让单片机去做吧。
一般pwm频率都是因硬件设计而固定的,是由pwm发生器决定的。
PWM频率越高,调制出来的输出曲线就更加的的smooth,效果越好。这个和手机的ppi越高,显示越清晰是一个道理。当然我想PWM的频率越高,对硬件的要求就也越高。
下图中,右边的频率是左边的2倍,调制出的曲线更加圆滑,贴近原始波形。
使用Arduino来实战!
我一直觉得,用实践去验证教科书上的所谓真理是一件令人激动人心的事,可惜的是直到大学我才有这样的机会!
首先要确定你的Arduino 的哪些引脚支持PWM输出,一般标记了1个 ~ 就是支持PWM的。
On most Arduino boards (those with the ATmega168 or ATmega328), this function works on pins 3, 5, 6, 9, 10, and 11. On the Arduino Mega, it works on pins 2 - 13 and 44 - 46. Older Arduino boards with an ATmega8 only support analogWrite() on pins 9, 10, and 11.
The frequency of the PWM signal on most pins is approximately 490 Hz. On the Uno and similar boards, pins 5 and 6 have a frequency of approximately 980 Hz. Pins 3 and 11 on the Leonardo also run at 980 Hz.
-- arduino.cc
Arduino 芯片为ATmega168 或者ATmega328的3, 5, 6, 9, 10, 和 11支持PWM, Arduino Mega的 2~13 , 44~46支持PWM,老板子ATmega8的9, 10,11脚支持PWM
一般的Arduino PWM的频率大约为490Hz,Uno 以及相同类型的板子的5,6脚可达980Hz,Leonardo的 3,11脚也可以达到980Hz
PWM的频率是不能由我们控制了,所以我们把注意力放在占空比上。
Arduino PWM输出函数,注意这个函数和模拟引脚没半毛钱的关系。使用这个函数的引脚无需 使用 pinMode 配置为 OUTPUT
analogWrite(pin,value)
value 的值在 0~255之间,value的值除以256就是PWM输出的占空比,如 analogWrite(pin,128) 输出的占空比为50%。
试验连线线图
const int ledPin = 3; //pwm输出引脚 const int button = 6; //按键引脚 byte pwmVal = 0; bool isKeyPressed(byte pin); void setup() { pinMode(button,INPUT_PULLUP); Serial.begin(9600); } void loop() { if(isKeyPressed(button)) //如果检测到按键按下,就让pwmVal 增加2 { pwmVal+=2; //pwmVal 的类型为byte,到了256会自动溢出回0,所以为没做检查,不过不要过度依赖这个技巧啊,规范些好 } analogWrite(ledPin,pwmVal); Serial.println(map(pwmVal,0,255,0,5)); //使用map函数映射为 0~5v的电压信号 delay(30); } bool isKeyPressed(byte pin) { bool pre = false; if(digitalRead(pin)==LOW) { delay(10); if(digitalRead(pin)==LOW) { pre = true; for(int a = 5;digitalRead(pin)==LOW&&a;--a) { delay(5); } } } return pre; }
在IDE的串口绘图器中查看输出的波形。因为是手动按键来调整占空比的,所以波形不好看。用电位器调更加好。
我们去掉map函数,直接输出pwmVal的值,可以看到更加细腻。
夜晚的效果。
欢迎转载,请注明出处:www.cnblogs.com/lulipro
为了获得更好的阅读体验,请访问原博客地址。
限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。
代码钢琴家