Arduino系列教程之 – PWM的秘密(上)

今天是3.8妇女节,我也沾了点光,提前开溜了(公司的mm们都放假了,大家工作没动力啊)。呵呵,在此祝福所有的美女们节日快乐!

前几天在微博上看到了flamingoeda小盆友提到了PWM,毕竟微博只能有一百多字,没法详细的介绍清楚,特此补充一下。

PWM是啥玩意儿?
PWM是“怕玩命”的缩写,英文写法是“Pulse-width modulation”,也有些外行人士把它翻译成“脉冲宽度调制”。Arduino有很多种版本,这篇文章里是以ATmega168为例,有用过其他型号的兄弟请补充。
对于没有听说过PWM的同学,请先参考一下我的另一篇博客Arduino的模拟输入和输出

PWM是用占空比不同的方波,来模拟“模拟输出”的一种方式。靠,这个太拗口了,简而言之就是电脑只会输出0和1,那么想输出0.5怎么办呢?于是输出01010101….,平均之后的效果就是0.5了。早这么说就了然了嘛。

pwm

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



对 “Arduino系列教程之 – PWM的秘密(上)” 的 30 条 评论

  1. clamber 说:

    第一段代码貌似错的,这应该是一个1HZ,高电平5V,低电平0.625V的一个方波信号。

  2. clamber 说:

    是2Hz

  3. clamber 说:

    呵呵,仔细看了下,是我搞错了,我用第一个程序写一个1KHz、50%的PWM控制轴流风扇转速,风扇不能正常调速,原来arduion模拟写入analogWrite()函数的PWM调制频率为30KHz~~~

  4. 好多东西都看不懂 看来真的落伍了··

  5. liuli 说:

    大佬,你的示例1,有段注解:这种方式产生的方波周期大概是20ms左右(50Hz)

    我想问一下,这个20ms是怎么算出来的?能解释一下吗?

  6. wlreg 说:

    第二段代码倒是看明白了…但是第一段没有看明白…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系统默认的,大多数情况下我们不用改它。但是某些需要快速响应的情况下,需要缩短这个周期长度。

  7. 小罗 说:

    以下仅仅是猜测:
    先假设示例1和2是正确的话,那就证明analogWrite和digitalWrite指令在频率方面是不一样的。从用途来说,Arduino所对应的接收模拟信号外部设备的一般都工作在工频50Hz,所以analogWrite指令执行以后只要在对应输出点输出50Hz点电平信号(是间接从220VAC频率是50Hz整流后得到的)就可以了。但是Arduino所对应的接收开关信号外部设备可能是步进电机、舵机等,这些设备对频率是有要求的,所以digitalWrite指令执行以后电平信号是一直保持着的(可以理解为占空比为100%),所以digitalWrite 要产生方波得delayMicroseconds的配合

  8. 小罗 说:

    简单说句,analogWrite指令执行后本身就会产生类似方波的信号,至于占空比可能没法控制。analogWrite指令执行后信号是一直处于高电平或者低电平,后面如果没有其他指令对该点进行控制,那占空比就为100%

  9. W.Haosen 说:

    其实我一般建议将主CPU和动力控制电路分开设计。我使用FPGA来自动控制动力系统,而CPU主要是干自己的事情,他对动力系统只需要写几个控制字就好,比如说直走、左转等。PWM如果由CPU来输出的话就太浪费鸟~

    • 有道理!因为我手头目前只有一块板子,只好将就用了。
      看来应该再买几块arduino的小板子,不需要那么多接口,专门用来控制各种电机,不然主控板子的压力就太大了

  10. SSS_SXS 说:

    每100us产生高电平,每1000-100us产生低电平,1000us/次得出周期1000HZ
    1/0.001s=1000HZ,占空比:100/1000%=10%

    0.001(s) 1(次)
    ______ = _______

    1(s) X

    X= 1000HZ(次/秒)

    各位看看我的理解正确吗?

  11. Selladore 说:

    谢谢,正在寻找如何得到特定周期PWM信号的方法。
    我去看官方手册了~

  12. 越锋利 说:

    analogWrite() 那个貌似是 500Hz 吧

  13. 探索者 说:

    ATmega168有三个时钟,名字分别叫Timer0, Timer1和Timer2。每个时钟都使用了两个寄存器,其中一个是设定值例如128,另一个则从0开始不断递增,到1024之后溢出回到0。那么当两个值相同的时候,Timer就会把某个管脚反相。不同的Timer之间频率是相同的,占空比则根据设置值不同。

    这个没看懂,如果两个寄存器到同一值,电平反向,那第一个高电平的宽度是128,但之后的低电平和再次翻转都是1024了啊,为啥占空比会改变了?

    • 果然啊,还是你看的细,我开始理解成从0到128的时候反相一次,到1024再反相一次。
      看来是我理解错了,我再找找资料看看,多谢纠正! :)

  14. Lilian 说:

    老男哥,你这写的,我不看回复,还不知道你是错的!!!

  15. daijiaoshou 说:

    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,求博主解释一下

  16. 夏天 说:

    我想知道PWM可以控制直流电机转特定的角度就停止吗。比如说我想让电机转1圈之后就停止不动。

  17. 说:

    pwm 怎么控制电机转的速度方向 和大小

  18. Croatia 说:

    大叔,你们说的我全部没看懂,感觉已经无法抢救了,还是要谢谢。

  19. 钢格板 说:

    好文章,内容文风幽默.禁止此消息:nolinkok@163.com

  20. 不错的文章,内容龙飞凤舞.禁止此消息:nolinkok@163.com

  21. 好文章,内容文风幽默.禁止此消息:nolinkok@163.com

  22. 勾花网 说:

    不错的文章,内容排山倒海.禁止此消息:nolinkok@163.com

发表评论

可以使用下列 XHTML 标签:<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>