Arduino系列教程之 – PWM的秘密(上)
由 动力老男孩 发表于 2011/03/08 21:56:02今天是3.8妇女节,我也沾了点光,提前开溜了(公司的mm们都放假了,大家工作没动力啊)。呵呵,在此祝福所有的美女们节日快乐!
前几天在微博上看到了flamingoeda小盆友提到了PWM,毕竟微博只能有一百多字,没法详细的介绍清楚,特此补充一下。
PWM是啥玩意儿?
PWM是“怕玩命”的缩写,英文写法是“Pulse-width modulation”,也有些外行人士把它翻译成“脉冲宽度调制”。Arduino有很多种版本,这篇文章里是以ATmega168为例,有用过其他型号的兄弟请补充。
对于没有听说过PWM的同学,请先参考一下我的另一篇博客Arduino的模拟输入和输出。
PWM是用占空比不同的方波,来模拟“模拟输出”的一种方式。靠,这个太拗口了,简而言之就是电脑只会输出0和1,那么想输出0.5怎么办呢?于是输出01010101….,平均之后的效果就是0.5了。早这么说就了然了嘛。
PWM有神马作用?
举几个例子说明:
1.通过简单的滤波电路,就可以生成真正的模拟输出量;
2.控制灯光亮度,调节电机转速;请注意这和1不是重复的,因为不需要滤波就可以实现
3.控制舵机角度,这个请参考 Arduino开发板实验三:舵机控制
4.输出信号,例如接喇叭的时候可以发声
如何产生PWM?
Arduino有三种方式可以产生PWM。第一种:
用analogWrite(pin, val)命令
其中pin是腿的编号,传说中只能用3,5,6,9,10,11这几条;val是0~255的整数值,对应电压从0到+5V。注意,那几个脚的编号,指的是ATmega168的pin编号,Arduino的板子会用这几个管脚支持更多路的PWM输出,例如我的Arduino Mega168就支持0~13共14个PWM输出。
具体的使用可以看下面的示例代码:
int pin = 8; //0~13 void setup() { pinMode(pin, OUTPUT); } void loop() { analogWrite(pin, 128); delay(500); }
这种方式产生的方波周期大概是2ms左右(490Hz),不需要占用额外的cpu命令时间。据说99%的同学看到这里就可以下课了,技术宅请继续看第二种方式:
手动用代码实现PWM
int pin = 38; //这个可以随意点 void setup() { pinMode(pin, OUTPUT); } void loop() { digitalWrite(pin, HIGH); delayMicroseconds(100); digitalWrite(pin, LOW); delayMicroseconds(1000 - 100); }
上面这段代码会产生一个PWM=0.1的,周期为1ms的方波(1000Hz),这种方式的优缺点很明显:
1,PWM的比例可以更精确;
2,周期和频率可控制;
3,所有的pin脚都可以输出,不局限于那几个脚;
4,缺点:CPU干不了其他事情了;
好吧,缺点只有一个,却非常致命,以至于上面这些基本都是废话。但是对于周期比较大的PWM,可以用算法模拟CPU的多任务系统,从而在输出PWM的同时做点兼职。
那么能不能既调节PWM的频率和周期,又不要占用额外的CPU时间呢?请看第三种方式:
使用PWM寄存器
ATmega168有三个时钟,名字分别叫Timer0, Timer1和Timer2。每个时钟都使用了两个寄存器,其中一个是设定值例如128,另一个则从0开始不断递增,到1024之后溢出回到0。那么当两个值相同的时候,Timer就会把某个管脚反相。不同的Timer之间频率是相同的,占空比则根据设置值不同。
占空比有了,那么周期怎么控制呢?有一种叫做时钟控制器的东东,这个控制器可以设置周期为CPU周期的某个倍数,例如1,8,64,256,1024等等,Timer0和Timer1共用一个控制器,Timer2和它们是独立的。
今天先写这些,明天继续…..
本文内容基本都是参考自Arduino官网教程,心急的同学请看英文原版:
http://arduino.cc/en/Tutorial/SecretsOfArduinoPWM
第一段代码貌似错的,这应该是一个1HZ,高电平5V,低电平0.625V的一个方波信号。
是2Hz
呵呵,仔细看了下,是我搞错了,我用第一个程序写一个1KHz、50%的PWM控制轴流风扇转速,风扇不能正常调速,原来arduion模拟写入analogWrite()函数的PWM调制频率为30KHz~~~
嗯?应该是50Hz,一个周期20ms
好多东西都看不懂 看来真的落伍了··
大佬,你的示例1,有段注解:这种方式产生的方波周期大概是20ms左右(50Hz)
我想问一下,这个20ms是怎么算出来的?能解释一下吗?
这个不是算出来的,Arduino官网的文档里写的
第二段代码倒是看明白了…但是第一段没有看明白…analogWrite(pin,128)我理解这个应该是输出2.5V左右的电压,那么输出方波的占空比大概是50%吧,那么这个应该和方波周期没有关系。有关系的应该是delay了吧?但是看说明书,delay的单位是ms,那么delay(500)不是已经延时了500微秒了吗,周期为什么会是20微秒呢=……=?还是说输出方波的周期是和系统时钟有关系?不懂,不懂
嗯,这个例子写的不好,太容易误导了。
delay语句在这里没有实际意义,只是为了定期让程序循环。
但是还不能去掉,否则的话loop函数会不断的设置analogWrite。
你可以delay(10000)也无所谓。
你说的没错,analogWrite(pin,128)输出2.5V左右的平均电压,和方波的周期没有关系。而这个20ms的周期是Arduino系统默认的,大多数情况下我们不用改它。但是某些需要快速响应的情况下,需要缩短这个周期长度。
以下仅仅是猜测:
先假设示例1和2是正确的话,那就证明analogWrite和digitalWrite指令在频率方面是不一样的。从用途来说,Arduino所对应的接收模拟信号外部设备的一般都工作在工频50Hz,所以analogWrite指令执行以后只要在对应输出点输出50Hz点电平信号(是间接从220VAC频率是50Hz整流后得到的)就可以了。但是Arduino所对应的接收开关信号外部设备可能是步进电机、舵机等,这些设备对频率是有要求的,所以digitalWrite指令执行以后电平信号是一直保持着的(可以理解为占空比为100%),所以digitalWrite 要产生方波得delayMicroseconds的配合
简单说句,analogWrite指令执行后本身就会产生类似方波的信号,至于占空比可能没法控制。analogWrite指令执行后信号是一直处于高电平或者低电平,后面如果没有其他指令对该点进行控制,那占空比就为100%
同意!
其实我一般建议将主CPU和动力控制电路分开设计。我使用FPGA来自动控制动力系统,而CPU主要是干自己的事情,他对动力系统只需要写几个控制字就好,比如说直走、左转等。PWM如果由CPU来输出的话就太浪费鸟~
有道理!因为我手头目前只有一块板子,只好将就用了。
看来应该再买几块arduino的小板子,不需要那么多接口,专门用来控制各种电机,不然主控板子的压力就太大了
每100us产生高电平,每1000-100us产生低电平,1000us/次得出周期1000HZ
1/0.001s=1000HZ,占空比:100/1000%=10%
0.001(s) 1(次)
______ = _______
1(s) X
X= 1000HZ(次/秒)
各位看看我的理解正确吗?
没错,非常正确
谢谢,正在寻找如何得到特定周期PWM信号的方法。
我去看官方手册了~
analogWrite() 那个貌似是 500Hz 吧
没错,资料里显示是490Hz左右,我之前写的是错的,一直没改呢
ATmega168有三个时钟,名字分别叫Timer0, Timer1和Timer2。每个时钟都使用了两个寄存器,其中一个是设定值例如128,另一个则从0开始不断递增,到1024之后溢出回到0。那么当两个值相同的时候,Timer就会把某个管脚反相。不同的Timer之间频率是相同的,占空比则根据设置值不同。
这个没看懂,如果两个寄存器到同一值,电平反向,那第一个高电平的宽度是128,但之后的低电平和再次翻转都是1024了啊,为啥占空比会改变了?
果然啊,还是你看的细,我开始理解成从0到128的时候反相一次,到1024再反相一次。
看来是我理解错了,我再找找资料看看,多谢纠正!
老男哥,你这写的,我不看回复,还不知道你是错的!!!
double duration;
void setup() {
pinMode(9,OUTPUT);
pinMode(3,INPUT);
}
void loop() {
duration=pulseIn(3,HIGH);
digitalWrite(9,HIGH);
delay(20);
digitalWrite(9,LOW);
delay(20);
}
9引脚输出的pwm波会受输入pwm波的影响,此种情况下,输出的pwm波频率为60ms,而理想结果应该为40ms,求博主解释一下
我想知道PWM可以控制直流电机转特定的角度就停止吗。比如说我想让电机转1圈之后就停止不动。
pwm 怎么控制电机转的速度方向 和大小
大叔,你们说的我全部没看懂,感觉已经无法抢救了,还是要谢谢。