analogWrite怎么做PWM输出?改PWM频率(定时器相关) 您所在的位置:网站首页 arduino测方波频率 analogWrite怎么做PWM输出?改PWM频率(定时器相关)

analogWrite怎么做PWM输出?改PWM频率(定时器相关)

2023-11-21 19:23| 来源: 网络整理| 查看: 265

文章目录 1. 首先来看看 PWM 仿真电压的原理, 这在官网上就有解说了:1.1 官网上的仿真范例:1.2 如果真的这样做, 有好处也有坏处, 官网上已经说了: 2. 那 Arduino 是怎么做的呢?2.1 通过 Timer 定时器直接控制 pin 做 PWM 输出, Arduino UNO 的 MCU 有三个 timer,2.1 timer 的基本知识:2.2 analogWrite( ) 真正请 timer 帮忙只做三件事:2.2 硬件 timer 控制 PWM 运作方式与原理 原文繁体,不易阅读,这里转换为简体并修改了格式,原文地址: https://www.arduino.cn/thread-12906-1-1.html 大家都知道在 Arduino UNO 有六支 pin 可以使用 analogWrite( )做 PWM 输出, 在板子上 pin 旁边标示有 “~” 符号, analogWrite( ) 可以用来产生仿真电压, 很多人一定很好奇那是怎么做到的 ?

1. 首先来看看 PWM 仿真电压的原理, 这在官网上就有解说了:

http://arduino.cc/en/Tutorial/SecretsOfArduinoPWM 所谓的 PWM 全称是 Pulse-Width Modulation (PWM), 其实这也没啥学问, 就是对 GPIO 脚位不断的切换 “有电” “没电”, 每秒钟循环几次即为其 Frequency(频率), 每次"有电"时间占一个循环的百分比称为其占空比(Duty cycle);

1.1 官网上的仿真范例: int pin = 13; void setup() { pinMode(pin, OUTPUT); } void loop(){ digitalWrite(pin, HIGH); delayMicroseconds(100); // Approximately 10% duty cycle @ 1KHz digitalWrite(pin, LOW); delayMicroseconds(1000 - 100); }

这个范例中, 一个循环是 1000 us = 1ms, 所以一秒循环 1000次, 因此 Frequency 是 1 KHz, 每个循环中, 有电的比率是 100/1000 * 100% = 10%, 所以 duty cycle (占空比)为 10%; 这样就可以模拟出 5Volt x 10% = 0.5 Volt 的电压!

1.2 如果真的这样做, 有好处也有坏处, 官网上已经说了:

好处是任一支 pin 都可这样用, 包括 Pint 0 到 Pin 13, 以及 Pin A0 到 A5 共 20支 pin 都可以!

坏处却更多, 首先就是频率(Frequency)和占空比(duty cycle)可能受中断(Interrup)的影响变成不是很准确 !

最大的坏处是, 在某支 pin 做 PWM 输出期间都没办法做别的事情 ! 既然说这只是示范可以这样做, 在 Arduino 当然不可能是这么做,

2. 那 Arduino 是怎么做的呢? 2.1 通过 Timer 定时器直接控制 pin 做 PWM 输出, Arduino UNO 的 MCU 有三个 timer, timer0 控制 pin 5, pin 6;timer1 控制 pin 9, pin 10;timer 2 控制 pin 11, pin 3;

我们可以对这些 pin 用 analogWrite(pin, val); 输出 0 到 255 的 val 值到 pin ;

如果输出 val 是 0, 它会偷偷直接改用 digitalWrite(pin, 0); 输出,如果 val 是 255, 也是会偷偷直接改用 digitalWrite(pin, 1); 输出!如果 val 是 1 到 254, 则会下命令请 pin 脚对应的 timer 定时器(定时器)帮忙! How ? 2.1 timer 的基本知识:

每个 timer 一定有个 counter, 例如 timer0 的TCNT0, timer1 的TCNT1, timer2 的TCNT2; 该 counter 一定是每个 tick 会加 1, 每个 tick 通常是把 CPU 的 clock 拿来经过一个除频电路, 然后给 timer 使用; Arduino UNO 采用 AVR ATmega328 MCU, 且 clock Rate 是 16MHz, 每个 timer 的除频 Prescaler 是独立设定的, 通常可以设 1, 2, 4, 8, 64, 256, or 1024 等, 这必须看 MCU 的 datasheet.

每个 timer 通常提供许多 mode 运作模式, 例如 counter溢出(Overflow)或Rollover归零时产生中断, 或TCNT(0,1,2) 达到某个值时产生中断等, Arduino ATmega328 的 timer 有 16种 mode, 许多 Mode 是与 PWM 有关; 要设定 timer 的 Mode 可以透过修改 timer 的控制缓存器, 例如 TCCR?A, TCCR?B, 注意以 ATmega328 为例, TCCR?A 和 TCCR?B 要合起来用, 此处的 A, B 与 channel A, channel B 无关!

每个 timer 通常有比较缓存器(Compare Register), 当 TCNT? 值与该些比较缓存器相同时可以做某事, 不一定是对 CPU 产生中断! Arduino 每个 timer 有两个比较缓存器, 分别命名 OCR?A 和 OCR?B,

其中 ? 是 0, 1, 2 分别对应到 timer0, timer1, 和 timer2 这三个定时器.

你可以先偷看 analogWrite( ) 的程序码: 在你 Arduino IDE 下的 hardware\arduino\cores\arduino\wiring_analog.c

2.2 analogWrite( ) 真正请 timer 帮忙只做三件事: 找出对应的 port,设定控制缓存器,填入 analog的值到比较缓存器!

不过你会发现看不太懂, 因为还不知道硬件 timer 控制 PWM 运作方式与原理! 不想看 datasheet 可以参考这: http://letsmakerobots.com/content/arduino-101-timers-and-interrupts

2.2 硬件 timer 控制 PWM 运作方式与原理

以Arduino UNO 的 timer1为例, 在 mode 5 (Fast PWM, 8 bit), 此时, TCNT1 从 0 数到 255, 通常从 255 (此 mode 的最大值)又加 1 变为 0 之时会产生 OVF 中断(TIMSK1的TOIE1要 set), 不过这与 PWM 无关! PWM 不是用 Interrupt 中断请求做的, 不必麻烦 CPU, CPU 只要下命令给 timer, timer 就会照命令执行PWM工作 !

PWM 是利用每个 timer 上的两个"匹配符合输出"缓存器(Compare Match Output) COM?A 和 COM?B; (注意虽是 Compare Match Output, 但缓存器名称是 COMxy 不是 CMOxy 喔 !)

在timer1 的 mode 5, 又称 Fast PWM mode, (不过请注意 Arduino 的 init( ) 设定只有 timer0 用这, 另外 timer1 和 timer2 不是用这 mode),

这时可以把 1 到 254 之间的值放入 OCR1A 或 OCR1B 以便控制 pin 9 或 pin 10的 PWM duty cycle, 1 到 254 分别对应到 (1+1)/256, …, (254+1)/256 的 duty cycle. +1 是硬件电路设计上的关系, data sheet 上说: Note that fast PWM holds the output high one cycle longer than the compare register value. 在 TCNT1 等于 0 之时, COM1A and/or COM1B 会输出(当然要 TCCR1A 内的 COM1A1 and/or COM1B1 有set), 然后在 TCNT1 等于 OCR1A 则关闭 COM1A, 当 TCNT1 等于 OCR1B 则关闭 COM1B,

注意没有立即关闭, 是延迟一个 tick 才关闭 ! 所以才会多加1, 因为一个循环是 256, 不是 255,如果不延迟加 1, 则输出 val 是 254 时变成 254/256, 还差一点点, 所以牺牲 1/256, 就是没有 1/256占空比 !

由于 Arduino 的 init( )把 timer1 的 Prescaler 设定为 64, (参考在你 Arduino IDE 内的 hardware\arduino\cores\arduino\wiring.c ) 且把 timer1 设定为 8-bit phase correct pwm mode, 所以其频率是 490.196Hz, 不是 976.5625Hz; 所谓的 8-bit phase correct pwm mode, 意思是 TCNT? 从 0 数到 255, 接着又从 255 倒着数回 0, 那何时把 COM1A and/or COM1B 的输出打开或关闭呢? 根据 datasheet, 在从 0 往上数, 碰到 OCR1A 时把 COM1A 关闭, 然后从 255 往回数, 数到 OCR1A 时把 COM1A 打开(有电); 对于 OCR1B 和 COM1B 也是这样! 这使得 duty cycle (占空比) 更准确, 也就是 val 1 ~ 254 分别对应到 1/255 到 254/255 的 duty cycle. 但是 Frequency 则不是除以 256, 是要除以 255 再除以 2, 于是: (注意是 255, 不是 256喔!) Frequency = 16 MHz / 64 / 255 / 2 = 490.196Hz;

timer 2 也是在 init( )被设为 Prescaler 64 的 phase correct pwm (8-bit); 但是, timer0 虽然 Prescaler 也设 64, 但 PWM 是用 Fast PWM mode, 不使用 phase correct mode 是为了避免影响维护 millis( ) 的中断 timer0 Overflow Interrupt,即 ISR(TIMER0_OVF_vect) 这中断处理程序, 否则 millis( ) 和 micros( ) 以及 delay() 都会受到影响 ! 因此 , timer0 控制的 PWM 其 Frequency 是 976.5625Hz, 16 MHz / 64 / 256 = 976.5625Hz 注意用 timer0 控制的pin 5, pin 6 之 PWM 的 duty cycle 无法是 1/256, 它是 0 再来就 2/256了! 结论: timer0 控制 pin 5, pin 6, PWM 频率 976.5625Hz, duty cycle可以 2/256 ~ 255/256 (对应 1 到254); timer1 控制 pin 9, pin 10, PWM 频率 490.196Hz, duty cycle 可以 1/255 ~ 254/255(对应 1 到254); timer2 控制 pin 11, pin 3, PWM 频率 与 duty cycle 跟 timer1 控制的相同 !

Q: 那 PWM 的 Frequency 可不可以更改? A: 可以, 偷改 timer 的 Prescaler 就可以达到更改 Frequency 的目的 ! 但是, 千万不要更改 timer0 的 Prescaler, 否则 millis( ) 和 micros( ) 以及 delay() 都会受到影响 !! 以下是以 timer1 控制的 pin 9, pin 10 为例(注意两个 pin 的频率相同!) 在你的 setup( ) { 内, 写如下两句即可: int fff = 3; // 可以是 1, 2, 3, 4, 5 TCCR1B = TCCR1B & 0xF8 | ?; 其中 fff 与对应频率如下:

?PrescalerFrequency1131372.549 Hz283921.569364490.196


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有