哪些 Arduino Nano Every I/O 引脚可用于 PWM 输出?
Arduino Nano Every 设计为使用引脚 D3、D5、D6、D9 和 D10 进行五个 PWM 输出。PWM 信号使用 analogWrite() 函数生成。例如,命令 analogWrite(3, 64); 将 Arduino 数字输出引脚 D3 配置为生成占空比为 25% 的 PWM 信号。
此表标识了 ATmega4809 物理端口。Arduino 引脚指定和相关的硬件计时器在我们继续逆向工程过程中将非常重要:
- PB0 (D9) - TCA
- PB1 (D10) - TCA
- PB2 (D5) - TCA
- PF4 (D6) - TCB
- PF5 (D3) - TCB
本工程简报假设您对 Arduino PWM 有一定了解,并正在寻找更改默认 PWM 频率的方法。有关 Arduino 和占空比的更多信息,请参阅此 Arduino 教程。
图 1 :Digilent Analog Discovery 作为逻辑分析仪,用于同时测量 Arduino Nano Every 的所有 PWM 引脚。
Arduino Nano Every 的默认 PWM 频率是多少?
Arduino Nano Every 的默认 PWM 频率为 976.5625 Hz。这似乎是一个奇怪的具体频率,但它直接来自 16 MHz 振荡器:
16 MHz / 64 / 256 = 976.5625 Hz
其中除以 64 是 TCA0_CTRLA 寄存器的配置设置,除以 256 是 8 位 PWM 的自然分频。在这种情况下,PWM 将 62,500 Hz 时钟(16 MHz / 64)细分为 256 个时间槽。
为什么要更改 PWM 频率?
默认的 Arduino 频率 976.5625 Hz 适用于许多简单项目,例如调暗 LED 和控制小型电机的速度。然而,当用于控制高功率电机等设备时,该频率可能会引起令人不快的啸叫。在超过人类听觉极限(约 20 kHz)的频率下操作可以消除噪音污染。
该频率可能与其他系统不兼容。例如,在未来的文章中,我将演示如何使用 Arduino 控制设计用于 25 kHz PWM 控制的可变速风扇。976.5625 Hz PWM 与 25 kHz 相差甚远。正如本文所演示的,我们可以生成 32 kHz PWM。这更接近所需的 25 kHz,但仍有一定差距。
另一个潜在的 Arduino PWM 探索领域是开关模式电源。虽然976.5625 Hz的信号可能有效,但它伴随着令人不快的噪音,且成本高、效率低。这些开关电源通常以高频率运行,以减少滤波器组件的尺寸。
我们如何逆向工程 Arduino 软件?
让我们首先认识到 Arduino 及其相关社区的杰出贡献。他们共同为整整一代学习者构建了一个起点。事实上,我挑战你找到一位没有编程过 Arduino 的电气工程师、技术专家或技术人员。
Arduino 成功的一个重要部分是硬件抽象。团队将PWM等复杂任务简化为一个简单的a nalogWrite() 函数。这对学习者来说是好事,但当我们试图逆向工程Arduino Nano Every 的 PWM功 能时就不那么好了。
假设我们希望继续使用 Arduino 抽象,我们可以深入研究Arduino代码库,或者配置微控制器并从相关寄存器中提取值。虽然探索代码是一个很好的学术练习,但它令人望而生畏,因为Arduino代码包含多个抽象层次,允许像analogWrite()这样的命令在Arduino产品线的所有成员上运行。另一方面,探索ATmega4809数据手册并非易事。
在所有情况下,我们都需要谨慎行事,因为我们有可能破坏相关的Arduino功能。例如,我们可以通过更改TCA(16位定时器/计数器类型A)外设中的CTRLA寄存器来轻松更改PWM频率。这解决了一个问题,但通过破坏其他Arduino函数引发了另一个问题。例如,我们可以将PWM频率提高32倍,不幸的是,millis()跟踪的时间也会改变32倍。这充其量是一种不便。这也是一个公开邀请bug的方式,因为命名函数没有执行它们预期的功能。
我们是如何确定除以 64 的?
完整的解释需要深入研究ATmega4809 数据手册,并对底层定时器外设进行详细研究。为了保持本文简短,我们将识别一些信号并描述它们如何影响PWM。
我们从16 MHz振荡器开始,这是微控制器中的主时钟。外设时钟(CLK_PER)是链中的下一个环节。它源自16 MHz振荡器,并用作所有外设的参考。
ATmega4809 遵循了实现预分频器的传统,如图2所示。这使得数字时钟信号可以被数字地除以2到64的因子。如前所述,我们可以通过查看 Arduino 代码来确定设置,或者我们可以执行以下 Arduino 代码行来确定操作微控制器中的实际设置。
Serial.print("MCLKCTRLA = "); Serial.println(CLKCTRL.MCLKCTRLB, BIN);
图 2 : 16 MHz时钟可以通过预分频器进行分频以产生外设时钟。
返回的结果为0。然后我们查阅数据手册中的寄存器以确定相应的设置。最重要的位是位0,它告诉我们预分频器已禁用,因此CLK_PER = CLK_MAIN。
我们的下一站是TCA定时器外设,特别是CTRLA寄存器,它标识了TCA模块本身的预分频器设置。
Serial.print("TCA0_CTRLA = "); Serial.println(TCA0.SINGLE.CTRLA, BIN);
结果是二进制1011。数据手册的探索告诉我们,前导101表示除以64。最低有效位表示外设已启用。这解决了TCA的问题,TCA是开头表格中标识的前三个PWM输出。
最后一步是将TCB输出与TCA关联起来。跳过几步,我们查看CTRLA寄存器。
Serial.print("TCB0_CTRLA = "); Serial.println(TCB0_CTRLA, BIN);
结果是二进制101。数据手册表明TCB外设正在使用来自TCA0的CLK_TCA。因此,更改A中的预分频器将改变B的操作,但反之则不然。
当我们修改定时器 / 计数器 A 时会出现什么问题?
再次,我们首先认识到Arduino抽象了硬件以使其更易于使用。如前所述,当我们修改/配置底层硬件时,我们可能会破坏Arduino的功能。
无需解释,我们注意到修改定时器A会破坏millis()函数。很可能,它还会破坏其他基于时间的函数。然而,修改B似乎不会破坏millis()。
如何提高 Arduino Nano Every 的 PWM 频率?
可以通过配置ATmega4809的A和B定时器中的时钟预分频器或更改主振荡器的频率/预分频器来修改PWM频率。然而,许多这些更改会破坏Arduino的功能。
本文的其余部分介绍了一种改变与TCB相关的两个PWM输出的方法。三个TCA PWM输出保持在原始的976.5625 Hz,millis()函数保持不变。请注意,尚未进行完整的功能测试。完全有可能其他Arduino功能会受到损害。如果您发现功能损坏,请在下方留言。
鉴于这些限制,我们只能对TCB PWM频率进行小幅更改。我们可以更改CTRLA寄存器以使用CLK_PER或CLK_PER/2,从而分别产生31.25 kHz和62.5 kHz的PWM频率。我们只需要设置适当的。
对于32.25 kHz的8位PWM,请使用:
TCB0_CTRLA = 0b00000011; // pin D6
TCB1_CTRLA = 0b00000011; // pin D3
对于62.5 kHz的8位PWM,请使用:
TCB0_CTRLA = 0b00000001; // pin D6
TCB1_CTRLA = 0b00000001; // pin D3
结果如图3所示,其中使用Digilent Analog Discovery作为逻辑分析仪。从上到下,PWM信号分别与Arduino引脚D3、D5、D6、D10和D11相关联。请注意,所有PWM信号的占空比均设置为25%。
图 3 :Digilent AnalogDiscovery屏幕截图,显示两个TCB PWM分别为31.25和62.5 kHz,而三个TCA PWM信号为976.5625 Hz。所有信号的占空比均为25%。
下一步
ATmega4809 能够从TCA提供高分辨率的 16 位PWM 信号。正如您可能从本文的共同主题中猜到的那样,配置外设通常会导致 Arduino 抽象的丢失和功能的损坏。
也许是时候放下Arduino抽象,学习使用MPLAB等工具进行裸机编程了。这是一项值得的时间投资,因为您将学会解锁微控制器的全部潜力,包括最新高性能添加的令人眼花缭乱的复杂性。
最后的思考
本文为微控制器提供了一个绝佳的学习机会。我们从顶部开始,使用Arduino抽象深入挖掘ATmega4809的定时器外设。我们了解了PWM生成器,并对Arduino抽象有了更深的理解。
至于PWM和增加频率,我们了解到与TCB相关的PWM信号可以增加到31和64 kHz,而不会破坏诸如millis()之类的功能。同时,我们承认其他尚未测试的功能可能会受到影响。如果您发现功能损坏,请在下方留言。此外,请留下完整的代码清单以帮助他人。