PWM,也就是脉冲宽度调制,用于将一段信号编码为脉冲信号(方波信号)。是在数字电路中 达到 模拟输出效果的一种手段。即:使用数字控制产生占空比不同的方波(一个不停在开与关之间切换的信号)来控制模拟输出。我们要在数字电路中输出模拟信号,就可以使用PWM技术实现。
在单片机中,我们常用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。这个百分比就是下面要说的占空比
有了前面的知识,相信你已经对占空比理解了,其实很好理解 ,占空比就是 在一段调制周期时间内,某个信号持续的时间占这个时间段的百分比。
下面给出占空比的公式
下面是一个占空比不断变化的图示
所以我们可以很自然的得出结论:
低占空比意味着输出的能量低,因为在一个周期内大部分时间信号处于关闭状态,如果pwm控制的负载为led,则具体表现例如led灯很暗.
高占空比意味着输出的能量高,在一个周期内,大部分时间信号处于on状态,具体表现为LED比较亮。
当占空比为100%时,表示 fully on,也就是在一个周期内,信号都处于on状态,具体表现为led亮度到达100%
为0%时则表示 fully off,在一个周期内,一直处于off状态.具体表现为led熄灭。
现在一切都明了了:脉宽调制,脉宽调制,脉宽调制,这个宽,不是物体的宽度,而是高电平信号在一个调制周期中持续时间长短,它可以用占空比去衡量,占空比越大,脉冲越宽。
扩展:用PWM模拟出如下的正玄波(假设仅仅用3个周期去调制出这段正弦波)
道理和前面是一样的,只不过,因为从图中看出,模拟信号(黑色曲线)随着时间不断加强,因此,占空比要变化,也就是逐渐增加。下面3个调制周期中,占空比D逐渐增大。
pwm的频率决定了输出的数字信号 on ,1 和 off,0 的切换速度。频率越高,切换就越快。频率的大小就是前面提到的调制周期T的倒数 : f = 1/T
一般pwm频率都是因硬件设计而固定的,是由pwm发生器决定的。
PWM频率越高,调制出来的输出曲线就更加的smooth,效果越好。这个和手机的ppi越高,显示越清晰是一个道理。当然我想PWM的频率越高,对硬件的要求就也越高。
下图中,右边的频率是左边的2倍,调制出的曲线更加圆滑,贴近理想波形。
我一直觉得,用实践去验证教科书上的所谓真理是一件令人激动人心的事,可惜的是直到大学我才有这样的机会!
首先要确定你的Arduino 的哪些引脚支持PWM输出,一般标记了1个 ~ 就是支持PWM的。
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频率为490Hz,那么,一个调制周期为2ms多一点。
Arduino中控制PWM输出的函数
analogWrite(pin,value)
pin:PWM输出的引脚.
value:用于控制占空比,范围:0~255。值为0表示占空比为0,值为255表示占空比为100%,值为127表示占空比为50%。
由于PWM是需要完成一个周期的时间的,因此,这个函数的2次调用之间应该有时延。
analogWrite 和 模拟引脚A0,A1。。。以及analogRead没有任何关系 。
并不需在调用analogWrite前使用pinMode配置引脚的IO模式,因为analogWrite源代码中,已经帮我们配置了。
试验连线线图
const byte ledPin = 3; //pwm输出引脚 const byte 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的值,可以看到更加细腻。
夜晚的效果。