使用Arduino Uno输出自定义频率的PWM

摘要

现在需要用Arduino输出可自定义频率(100Hz)的PWM来控制电动机转速。Arduino里有简单的语句来实现PWM,但是输出信号的频率不能更改。本文结合网上的文章信息和我的研究结果,解释一下实现频率可调的PWM的过程。

PWM

PWM(Pulse Width Modulation)是一种方波控制信号。方波高电平的宽度在一个周期里的占比被称为占空比(Duty Cycle)。改变PWM的占空比,可以改变输出信号的平均电压,实现模拟电压的输出。

Arduino里的PWM

首先,Arduino Uno的5,6,9,10,3,11接口可以通过简单语句analogWrite(pin, dutyCycle)来实现一个指定占空比的PWM。其中pin的值选择(5,6,9,10,3,11),dutyCycle的值在0~255之间,0为占空比0%,255为占空比100%。但是这种方式PWM信号的频率是固定的默认值,大约1000Hz左右(16MHz/64/256)。

其次,手动切换高电平和低电平,再在中间加入delay函数,可以实现自定义频率的PWM:

void setup()
{
  pinMode(13, OUTPUT);
}

void loop()
{
  digitalWrite(13, HIGH);
  delayMicroseconds(100); // Approximately 10% duty cycle @ 1KHz
  digitalWrite(13, LOW);
  delayMicroseconds(1000 - 100); //修改这里的1000可以调整频率
}

但是,这种操作需要CPU全神贯注的查数,任何其他的进程的干扰会导致输出的信号频率不准。

综上,需要底层的手段来控制Arduino实现PWM的频率调节。

调节Arduino里的时钟频率

Arduino Uno里有三个Timer:Timer0,Timer1,Timer2。 三个Timer都可以自定义调整频率,但是各有特点。Timer0负责控制delay等函数,动了Timer0的频率会导致计时函数不准;Timer1的计数器是16位的,和Timer0,Timer2的8位计数器不太一样;Timer2的频率可调的档位更多,因为它有7档预除数,下文会进一步解释。这里选择Timer2进行调节操作,先上代码:

void setup() {
  // put your setup code here, to run once:

  pinMode(3, OUTPUT); 
  pinMode(11, OUTPUT); 
  
  TCCR2A = _BV(COM2A0) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20); //Set Timer2 to varying top limit fast PWM mode
  TCCR2B = _BV(WGM22) | _BV(CS22) | _BV(CS21) | _BV(CS20);//another way to set prescaler CS2=fff
  
  OCR2A = 155; //Top value A
  OCR2B = 30; //Toggle value B, Output at pin 3
 
  //CS2  Divisor  Frequency
  //001    1        31372.55
  //010    8        3921.16
  //011    32       980.39
  //100    64       490.20   <--DEFAULT
  //101    128      245.10
  //110    256      122.55
  //111    1024     30.64
}

void loop() {
  // put your main code here, to run repeatedly:

}

由以上代码可见,需要的设置分为三个部分:pinMode,TCCR2A/B,OCR2A/B(这里的2是因为选择了Timer2)。

  • pinMode:
    Timer2所控制的管脚是pin11和pin3(Timer0控制5,6;Timer1控制9,10--这是chip的datasheet上规定),所以把这两个管脚设为输出。

  • OCR2A/B:
    这部分的解释需要提到Timer的结构和原理。
    每个Timer里都有一个计数器和两个比较寄存器。Timer2里计数器从0数到255(8位)然后归0继续从头数;Timer2的两个比较寄存器分别为OCR2A和OCR2B。

    比较寄存器就是你设置一个小于255的数,比如155。当计数器数到0时输出为高电平,数到155的时候改变输出为低电平。这样就实现了占空比的调节。在普通模式下(Fast PWM),OCR2A控制pin11的占空比,OCR2B控制pin3的占空比。如下图所示。
    出处:http://www.righto.com/2009/07/secrets-of-arduino-pwm.html
  • TCCR2A/B:
    理解了Timer的原理,下面来讨论这个PWM的频率。Arduino Uno的芯片ATmega328,晶振频率为16MHz。Timer计数器的频率会在这个基础上除以一个预除数,Timer2可选择的预除数有(1,8,32,64,128,256,1024)。也就是说,如果预除数设为64(默认),计数器计数的频率是16MHz/64 。又因为计数器要数256下才会完成一个PWM周期,所以输出PWM的频率是16MHz/64/256,约等于1000Hz。若果要获得最低的输出频率,预除数要选1024,得到的PWM是61Hz。

    TCCR2A/B就是来控制Timer2计数器的模式与预除数的大小的,由于是分位赋值,看起来怪怪的,我来解释一下。先说CS2位,这个就是来控制Timer2计数器预除数的:
    _BV(CS22) | _BV(CS21) | _BV(CS20)的三部分由逻辑按位或“|”链接;每个BV是按位赋注(bit value)的意思;_BV(CS22 )= 在CS2里,1<<2(把1左移2位) = 00000100;得到三部分分别是00000100,00000010,00000001;按位或最终得到0111;查代码里的表得到对应的预除数是1024。

  • 模式选择:
    现在的问题是,我需要的是100Hz,不是1024预除数下的61Hz,如何实现?这就需要控制计数器模式来微调频率。请看下图:


    出处:http://www.righto.com/2009/07/secrets-of-arduino-pwm.html

    这张图中的模式可以在原有的fast PWM基础上提高频率,得到图中OCnB所示的信号。这个模式叫做Varying the timer top limit: fast PWM。比较寄存器OCR2A在这里不再控制管脚11的占空比,而是设定一个计数器的上限:计数器不用数到255而是达到OCR2A就可以归零。OCR2B依然控制管脚3的占空比。

    为了让pin11有活干,这里设置TCCR2A里的COM2A位=01(表示数到极限就把pin11的电平反转,本应用不需要),COM2B位=10(表示pin3输出非反转PWM)。

    那么是如何选择模式的?剩下的WGM2位就是确定模式的。在fast PWM模式下,WGM2位是011,Varying the timer top limit: fast PWM模式下,WGM2位是111。所以需要_BV(WGM22) | _BV(WGM21) | _BV(WGM20)。处于我所不理解的原因,这个赋值可以被分为两部分分别写在TCCR2A和TCCR2B里。有明白的高手麻烦留个言解释一下。

频率计算

到这里,所有设置已经解释完。下面来计算一下100Hz输出的PWM具体参数应该设为多少。
pin3的输出频率=16MHz / 1024/ (OCR2A + 1),因此100Hz对应的OCR2A=155。(+1是因为fast PWM是从0开始数到上限值)
占空比 = (OCR2B+1)/ (OCR2A+1),所以:

占空比 OCR2B值
20% 30
25% 38
33% 51
50% 77
100% 155

总结

至此,本文介绍了如何使用Arduino的Timer时钟功能自定义设置PWM的频率和占空比。简要解释了Timer的选择,输出管脚的确定,比较寄存器的设定,计数器的预除数选择和模式选择。最后演示了100HzPWM的个参数计算过程。

References

  1. http://www.diy-robots.com/?p=852
  2. http://www.righto.com/2009/07/secrets-of-arduino-pwm.html
  3. http://playground.arduino.cc/Main/TimerPWMCheatsheet
  4. http://www.geek-workshop.com/thread-25012-1-1.html

2019年6月补充

实验结果.jpeg

如图中所示,在setup()中设置好计数器以后,loop()为空,在示波器中Pin3读出的PWM波形频率为100Hz,占空比为21.67%,约为20%。经检测目标达成。


如果这篇文章对你有所帮助,还请帮忙点赞打赏评论分享~谢谢


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容