PWM(Pulse Width Modulation)是一种方波控制信号。方波高电平的宽度在一个周期里的占比被称为占空比(Duty Cycle)。改变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 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。
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,如何实现?这就需要控制计数器模式来微调频率。请看下图:
这张图中的模式可以在原有的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的个参数计算过程。
链接:https://www.jianshu.com/p/cef09d3411ab
也称作LED数码管,是一种可以显示数字和其他信息的电子设备。玻璃管中包括一个金属丝网制成的阳极和多个阴极。大部分数码管阴极的形状为数字。
ESP32的DAC函数可以实现真正的模拟输出。
ESP32 没有Arduino输出 PWM 的 analogWrite(pin, value) 方法,取而代之的 ESP32 有一个 LEDC 来实现PWM功能。
本书由少年创学院联合创始人兼院长、知名创客程晨撰写,以Arduino作为硬件平台,介绍了使用米思齐(Mixly)软件进行程序开发的方法。
machine.pwm是MicroPython中用于控制PWM输出的模块之一,它提供了一些方法和属性,用于设置和控制PWM输出的频率、占空比等参数,从而实现对各种应用场景的控制。
本文档作为UNO R4 WiFi的技术概览,您将找到一系列资源和指南链接,帮助您开始下一个项目。
Arduino OneButton库是一个用于简化按钮操作的库,它可以轻松地处理按钮的单击、双击和长按等操作。适用于Arduino开发板以及ESP32等其他基于Arduino的开发板。
本文从Arduino Uno入手,看看怎样在这个"简单"的8bit单片机搭建一个属于自己的bootloader
许多硬件厂商都希望自己的开发板能被Arduino IDE集成开发环境所支持。这里就以小脚丫开发板所使用的开发包为例,介绍一下第三方开发包的制作方法。
Pico 上的每个 GPIO 引脚都能够进行脉宽调制,通过改变引脚打开和关闭的频率,可以改变或调制这些脉冲的宽度实现模拟输出。