1.PWM (Pulse Width Modulation) 脉宽调制
将模拟信号变换为脉冲的一种技术(WIKIPEDIA:一种对模拟信号电平的数字编码方法),一般变换后脉冲的周期固定,但脉冲的占空比会依模拟信号的大小而改变。通过使用高分辨率计数器(调制频率)调制方波的占空比,从而实现对一个模拟信号的电平进行编码。
只要带宽足够,任何模拟值都可以使用PWM 进行编码。输出的电压值是 通过通和断的时间进行计算的。输出电压值=(接通时间/脉冲时间)*最大电压值
duty cycle:占空比
其最大的优点是从处理器到被控对象之间的所有信号都是数字形式的,无需再进行数模转换过程;而且对噪声的抗干扰能力也大大增强(噪声只有在强到足以将逻辑值改变时,才可能对数字信号产生实质的影响),这也是PWM在通讯等信号传输行业得到大量应用的主要原因。
1.1例程:arduino的analogwrite()控制LED灯:
int pin = 8; //0~13
void setup()
{
pinMode(pin, OUTPUT);
}
void loop()
{
analogWrite(pin, 128);
delay(500);
}
增加电位器调节LED灯亮度:
// 注意:模拟接口自动设置为输入
// 10位的AD转换器,因此 analogRead() 读取的模拟值范围为 0~5V/1024
// 模拟值输入范围和分辨率可以通过 analogReference() 设置。引脚获取模拟电压值之后,将根据参考电压将模拟值转换到0~1023。
// 模拟输出是八位,范围为0~255,对应0~5V。
// 直接产生PWM方波的方法就是analogWrite
int potpin = 0; // 定义模拟接口A0
int ledpin = 11; // 定义数字接口~11
float val = 0;
void setup() {
pinMode(ledpin, OUTPUT);
Serial.begin(9600);
//analogReference(INTERNAL);
}
void loop() {
val = analogRead(potpin); // 读取传感器的模拟值并赋值给val
analogWrite(ledpin, val/4); // 十位模拟输入转换到八位模拟输出
val = 5*val/1024;
Serial.println(val);
delay(10);
}
1.2例程:arduino不使用analogwrite()产生PWM:
// 默认单位是 millisecond(毫秒)= 1000 microsecond(微秒) 毫 > 微 > 纳
// 产生一个PWM=0.1的,周期为1ms的方波(1000Hz)
// 优点:PWM的比例可以更精确;周期和频率可控制;所有的pin脚都可以输出,不局限于那几个脚;
// 占用CPU资源,CPU不能执行其他任务;
int pin = 12;
int T = 1000; // 周期设置位1ms,即1000Hz
void setup() {
// put your setup code here, to run once:
pinMode(pin, OUTPUT);
}
void loop() {
// put your main code here, to run repeatedly:
digitalWrite(pin, HIGH);
delayMicroseconds(100);
digitalWrite(pin, LOW);
delayMicroseconds(T - 100);
}
上面第二个的方法产生PWM的缺点(占用CPU时间)是致命的。下面介绍第三种方法,既调节PWM的频率和周期,又不占用额外的CPU时间。
1.3例程:使用PWM寄存器:
1.3.1ATmega 168/328的时钟
ATmega328P有三个时钟,Timer0,Timer1和Timer2。每个时钟都有两个比较寄存器,可以同时支持两路输出。比较寄存器用于控制PWM的占空比。大多数情况下,每个时钟的两路输出会有相同的频率,但根据两个比较寄存器的设置,可以有不同的占空比。
每个时钟都有一个“预定标器”,它的作用是设置timer的时钟周期,这个周期一般是用Arduino的系统时钟除以一个预设的因子。这个因子一般是1,8,64,256或1024这样的数值。Arduino的系统时钟周期是16MHz,所以这些Timer的频率就是系统时钟除以这个预设值的标定值。需要注意的是,Timer2的时钟标定值是独立的,而Timer0和Timer1使用的是相同的。
这些时钟都可以有多种不同的运行模式。常见的模式包括“快速PWM”和“相位修正PWM”。这些时钟可以从0计数到255,也可以计数到某个指定的值。例如16位的Timer1就可以支持计数到16位(2个字节)。
除了比较寄存器外,还有一些其他的寄存器用来控制时钟。例如TCCRnA和TCCRnB(这里的n指Timer n)就是用来设置时钟的计数位数。这些寄存器包含了很多位(bit),它们分别的作用如下:
脉冲生成模式控制位(WGM):用来设置时钟的模式
时钟选择位(CS):设置时钟的预定标器
输出模式控制位(COMnA和COMnB):使能/禁用/反相 输出A和输出B
输出比较器(OCRnA和OCRnB):当计数器等于这两个值时,输出值根据不同的模式进行变化
不同时钟的这些设置位稍有不同,使用时需要查资料。其中Timer1是一个16位的时钟,Timer2可以使用不同的预定标器。
1.3.2快速PWM
对于快速PWM来说,时钟都是从0计数到255。当计数器=0时,输出高电平1,当计数器等于比较寄存器时,输出低电平0。所以输出比较器越大,占空比越高。这就是传说中的快速PWM模式。后面的例子会解释如何用OCRnA和OCRnB设置两路输出的占空比。很明显这种情况下,这两路输出的周期是相同的,只是占空比不同。
快速PWM的例子:
下面这个例子以Timer2为例,把Pin3和Pin11作为快速PWM的两个输出管脚。其中:
WGM的设置为011,表示选择了快速PWM模式;
COM2A和COM2B设置为10,表示A和B输出都是非反转的PWM;
CS的设置为100,表示时钟周期是系统时钟的1/64;
OCR2A和OCR2B分别是180和50,表示两路输出的占空比;
1. pinMode(3, OUTPUT);
2. pinMode(11, OUTPUT);
3. TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20);
4. TCCR2B = _BV(CS22);
5. OCR2A = 180;
6. OCR2B = 50;
_BV(n)的意思就是1<<n,是移位命令。COM2A1,表示COM2A的第1位(其实是第2位,不过程序员们是从0开始数的)。所以:_BV(COM2A1)表示COM2A = 10;_BV(WGM21) | _BV(WGM20) 表示 WGM2 = 011。
在Arduino Duemilanove开发板,上面这几行代码的结果为:
输出 A 频率: 16 MHz / 64 / 256 = 976.5625Hz
输出 A 占空比: (180+1) / 256 = 70.7%
输出 B 频率: 16 MHz / 64 / 256 = 976.5625Hz
输出 B 占空比: (50+1) / 256 = 19.9%
频率的计算里都除以了256,这是因为除以64是得到了时钟的计数周期,从0到255是一个计数周期,即PWM的周期,周期的倒数即得到频率。占空比的计算也加了1。
1.3.3相位修正PWM
另外一种PWM模式是相位修正模式,也有人把它叫做“双斜率PWM”。这种模式下,计数器从0数到255,然后从255再倒数到0。当计数器在上升过程中遇到比较器的时候,输出0;在下降过程中遇到比较器的时候,输出1。这种模式频率接近降低了一倍,而且“它具有更加对称的输出”。
相位修正PWM的例子:
下面的例子还是以Timer2为例,设置Pin3和Pin11为输出管脚。其中WGM设置为001,表示相位修正模式,其他位设置和前面的例子相同:
1. pinMode(3, OUTPUT);
2. pinMode(11, OUTPUT);
3. TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM20);
4. TCCR2B = _BV(CS22);
5. OCR2A = 180;
6. OCR2B = 50;
在Arduino Duemilanove开发板,上面这几行代码的结果为:
输出 A 频率: 16 MHz / 64 / 255 / 2 = 490.196Hz
输出 A 占空比: 180 / 255 = 70.6%
输出 B 频率: 16 MHz / 64 / 255 / 2 = 490.196Hz
输出 B 占空比: 50 / 255 = 19.6%
这里的频率计数除以255,同时又多除了一个2。占空比的计算不用加1了。
1.3.4快速PWM下,修改时钟的计数上限:
快速PWM和相位修正PWM都可以重新设置输出的频率,先看看快速PWM是如何设置的。在修改频率的模式下,时钟从0开始计数到OCRA而不是255,注意这个OCRA我们之前是用来做比较用的。这样一来,频率的设置就非常灵活了。对Timer1来说,OCRA可以设置到16位(应该是0~65535)
OCRA用来设置总数,那么谁用来做比较?灵活的代价就是这种模式下,只能输出一路PWM。即OCRA用来设置总数,OCRB用来设置比较器。
尽管如此,无孔不入的程序员们依然还是设置了一种特殊的模式,每次计数器数到头的时候,输出A做一次反相,这样能凑合输出一个占空比为50%的方波。
下面的例子中,我们依然使用Timer2,Pin3和Pin11。其中OCR2A用来设置周期和频率,OCR2B用来设置B的占空比,同时A输出50%的方波。具体的设置是:
WGM设置为111表示“OCRA控制计数上限的快速PWM”;
OCR2A设置为180,表示从0数到180;
OCR2B设置比较器为50;
COM2A设置为01,表示OCR2A“当数到头是反相”,用来输出50%的方波(其中WGM被设置到了两个变量里);
1. pinMode(3, OUTPUT);
2. pinMode(11, OUTPUT);
3. TCCR2A = _BV(COM2A0) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20);
4. TCCR2B = _BV(WGM22) | _BV(CS22);
5. OCR2A = 180;
6. OCR2B = 50;
在Arduino Duemilanove开发板,上面这几行代码的结果为:
输出 A 频率: 16 MHz / 64 / (180+1) / 2 = 690.6Hz
输出 A 占空比: 50%
输出 B 频率: 16 MHz / 64 / (180+1) = 1381.2Hz
输出 B 占空比: (50+1) / (180+1) = 28.2%
其中频率的计算用了180+1,依然是数数的问题;A输出的频率是B输出的一半,因为输出A每两个大周期才能循环一次。
相位修正PWM下,修改时钟的计数上限:
类似的,相位修正模式下,也可以修改输出PWM的频率。代码几乎完全和上个例子一样,区别是WGM的值设置为101:
1. pinMode(3, OUTPUT);
2. pinMode(11, OUTPUT);
3. TCCR2A = _BV(COM2A0) | _BV(COM2B1) | _BV(WGM20);
4. TCCR2B = _BV(WGM22) | _BV(CS22);
5. OCR2A = 180;
6. OCR2B = 50;
在Arduino Duemilanove开发板,上面这几行代码的结果为:
输出 A 频率: 16 MHz / 64 / 180 / 2 / 2 = 347.2Hz
输出 A 占空比: 50%
输出 B 频率: 16 MHz / 64 / 180 / 2 = 694.4Hz
输出 B 占空比: 50 / 180 = 27.8%
跟之前的对比类似,相位修正模式下,一个大周期从0数到180,然后倒数到0,总共是360个时钟周期;而在快速PWM模式下,一个周期是从0数到180,实际上是181个时钟周期。这可能就是“更加对称”的好处。
以OCRA=3为例:
快速PWM:0123-0123-0123….. 每个周期时钟数是4=3+1
相位修正:012321-012321-012321….每个周期时钟数是6=3*2
相应的占空比计算也有微小的区别,快速PWM模式下,高位的输出会多一个时钟周期。上面这个例子,以比较器=1为例:
快速PWM:当计数器=1时反相,这时候已经经历了2个时钟周期,所以占空比是2/4 (0123)
相位修正:计数器0到1时输出0,计数器1到0时输出1,占空比是1/3 (012321)
以比较器=2为例:
快速PWM:当计数器=2时反相,这时候已经经历了3个时钟周期,所以占空比是3/4 (0123)
相位修正:计数器0到2时输出0,计数器1到0时输出1,占空比是2/3 (012321)
还有一件很有意思的现象:
对于快速PWM模式,如果我们设置analogWrite(5, 0),实际上应该有1/256的占空比,事实上你会发现输出的是永远低电平的0。这个实际上是在Arduino系统中强制设定的,如果发现输入的是0,那么就关闭PWM。随之而来的问题是,如果我们设置analogWrite(5, 1),那么占空比是多少呢?答案是2/256,也就是说0和1之间是有一个跳跃。
2.PPM (Pulse Position Modulation) 脉冲位置调制
PPM将多个通道的数值一个接一个合并进一个通道,用两个高电平之间的宽度来表示一个通道的值。
3.关于PPM和PWM在航模上的应用:
PWM每路只能传输一路信号,适合分别直接驱动不同设备的时候(如固定翼中每路驱动各自的舵机、电调)。
(天地飞遥控器是PPM编码,而Futaba, JR的遥控器上会有PCM/PPM切换。)但有些场合需要先集中获取接收机的多个通道值,再做其他处理(如教练模式、遥控器模拟器、多轴将接收机信号传给飞控),每个通道一组物理连线的方式就显得非常的繁琐和没有必要。
参考:http://www.5imx.com/portal.php?mod=view&aid=1351
http://blog.youkuaiyun.com/xuanyuanlei1020/article/details/49902779
注意:通常玩固定翼时,电调、舵机的信号线路是直接接在接收机上的。而玩 四轴时,电调、舵机的信号线路接在了飞控上。这时候,飞控以PPM方式接收航模接收机的信号,同时,飞控以PWM方式驱动电调、舵机。