增加PWM位分辨率


9

我想提高Arduino Uno的PWM位分辨率。此刻它是8位,我认为太低了。在不丧失中断和延迟能力的情况下是否有可能?

高恩

编辑此设置提供16位结果

void setupPWM16() {
    DDRB |= _BV(PB1) | _BV(PB2);        /* set pins as outputs */
    TCCR1A = _BV(COM1A1) | _BV(COM1B1)  /* non-inverting PWM */
        | _BV(WGM11);                   /* mode 14: fast PWM, TOP=ICR1 */
    TCCR1B = _BV(WGM13) | _BV(WGM12)
        | _BV(CS11);                    /* prescaler: clock / 8 */
    ICR1 = 0xffff;                      /* TOP counter value (freeing OCR1A*/
}
/* Comments about the setup
Changing ICR1 will effect the amount of bits of resolution.
ICR1 = 0xffff; (65535) 16-bit resolution
ICR1 = 0x7FFF; (32767) 15-bit resolution
ICR1 = 0x3FFF; (16383) 14-bit resolution etc....

Changing the prescaler will effect the frequency of the PWM signal.
Frequency[Hz}=CPU/(ICR1+1) where in this case CPU=16 MHz
16-bit PWM will be>>> (16000000/8)/(65535+1)=30.5175Hz
*/

/* 16-bit version of analogWrite(). Works only on pins 9 and 10. */
void analogWrite16(uint8_t pin, uint16_t val)
{
    switch (pin) {
        case  9: OCR1A = val; break;
        case 10: OCR1B = val; break;
    }
}

Answers:


15

Arduino Uno基于ATmega382P微控制器。该芯片有两个8位定时器,每个驱动两个PWM通道,一个16位定时器,驱动最后两个通道。

您不能提高8位定时器的分辨率。但是,您可以将16位计时器设置为16位模式,而不是Arduino核心库使用的8位模式。这将为您提供两个16位PWM通道,降低的频率为244 Hz(最大)。您可能必须自己配置计时器,并且不会从易于使用的analogWrite()功能中受益。有关详细信息,请参见ATmega328P数据表中有关定时器1的部分。

更新:这是16位的实现analogWrite()。它仅适用于引脚9和10,因为这是连接到16位定时器的唯一引脚。

/* Configure digital pins 9 and 10 as 16-bit PWM outputs. */
void setupPWM16() {
    DDRB |= _BV(PB1) | _BV(PB2);        /* set pins as outputs */
    TCCR1A = _BV(COM1A1) | _BV(COM1B1)  /* non-inverting PWM */
        | _BV(WGM11);                   /* mode 14: fast PWM, TOP=ICR1 */
    TCCR1B = _BV(WGM13) | _BV(WGM12)
        | _BV(CS10);                    /* no prescaling */
    ICR1 = 0xffff;                      /* TOP counter value */
}

/* 16-bit version of analogWrite(). Works only on pins 9 and 10. */
void analogWrite16(uint8_t pin, uint16_t val)
{
    switch (pin) {
        case  9: OCR1A = val; break;
        case 10: OCR1B = val; break;
    }
}

您可能会注意到,计数器序列的顶部已显式配置。您可以将此值更改为较小的值以使PWM更快,但会降低分辨率。

这是一个示例草图,说明了其用法:

void setup() {
    setupPWM16();
}

/* Test: send very slow sawtooth waves. */
void loop() {
    static uint16_t i;
    analogWrite16(9, i);
    analogWrite16(10, 0xffff - i);
    i++;
    delay(1);
}

哇,非常感谢您,这正是我所需要的。我希望我的PWM结果与我的传感器分辨率相同。如果我将代码更改为<查看我的编辑>,它将是13位结果吗?如果是这样,频率将是多少?我将使用它来驱动直流电动机,所以我猜想它会降低
244Hz

@KoenR:不,预分频器对分辨率没有影响,其目的是减慢计数速度。将预分频器设置为8将为您提供30.5 Hz的PWM频率。如果你想13位分辨率,设置ICR10x1fff,那么你的频率将是1953年赫兹(F_CPU /(TOP + 1)),一个带有预分频器为1
埃德加·博内特

谢谢你的解释。编辑了我的问题,使其涵盖了这些错误。这样其他人可以直接看到它。谢谢!
KoenR

1
@Edgar Bonet这很好,但是我似乎无法完全关闭LED。我正在使用ICR1 = 0x03FF,在0处,我看到示波器上的一个小脉冲足以点亮LED。有任何想法吗?
davivid

1
@davivid:是的,您不能有零占空比。analogWrite16(pin, val)给出占空比(val + 1)/ ICR1。作为一种解决方法,Arduino的analogWrite()做到了if (val == 0) digitalWrite(pin, LOW); else if (val == 255) digitalWrite(pin, HIGH);。但是,那么您将无法获得1 / ICR1的占空比...
Edgar Bonet

3

通过一些校准,您可以将两个PWM通道的输出与不同的加权电阻相加。在极端情况下,您可以使用一个输出来提供8位的分辨率,而将另一个输出缩放到该电平的1/256并相加,这样第二个通道就可以覆盖一个位的范围,您(再次)也可以得到16位的分辨率。没有大量的照顾和调整,您将一团糟。
但是,通过将第二个通道除以16或32,可以增加PWM分辨率的几个额外位。仅通过添加2个PWM通道模拟滤波输出,您就可以增加一个额外的位(因为在不改变mV /位的情况下电位范围加倍)。
名义上(再次)每再除以2,您将获得一个额外的分辨率,但这只能对额外的4或5或6个位进行,这对缩放电阻的精度要求越来越高,并且校准难度更大,而且容易出错。

简短的例子。
如果将一个PWM比例缩小到以1 mV的步长给出0-255 mV,则将两个具有相同幅度的PWM相加将以1 mV的步长给出0-510 mV的范围。
如果将一个PWM的比例缩小32倍,则无需在初始PWM范围内增加255 mV,而是在顶端增加8 mV(0.256.32 = 8 mV,但分辨率为0.03125(1/32 )mV步长。

尽管这可能纯粹通过电阻求和和RC滤波即可实现,但使用运算放大器加法器将大大改善结果。

同样,PWM纹波可以通过一个简单的RC滤波器进行滤波,但是使用一个运算放大器作为缓冲器(或者甚至仅使用一个晶体管作为发射极跟随器)将为您提供3或5极低通滤波,并且有更多的机会实现额外的PWM解析度。我尚未检查PWM输出的“相位相干性”,但希望它们以相对锁步的方式移动,因此您将无法获得添加两个不相关波形的平滑优势。

如果需要,可以进行更多评论。询问是否有兴趣。


这很聪明!看来Mozzi声音合成库使用此技巧的原因是所谓的“ HIFI”模式。
Edgar Bonet

PWM真是太好了。但这不会平滑波形吗?我问这是因为您使用的是RC滤波器。在我的问题中没有提到这一点,但是我正在用它驱动DC电机<感到羞愧>。谢谢你的反馈!
KoenR

@KoenR(foww:我看不出有什么可羞愧的。)我不知道您想要ADC输出中的频率响应/变化率是多少。或者为什么要N位或足够大。电动机通常不会有效地被超过8位控制-取决于您的应用程序的精度。由于电感,电动机充当平滑滤波器的一部分。您需要说出哪种电机以及如何驱动。电路图至关重要。除非马达很小,否则您会有驱动器。当PWM关闭时,有刷电机供电的PWM必须具有一个捕捉二极管以通过电机电流。加两个...
拉塞尔·麦克马洪

...此处的PWM完全可行,但需要了解电路细节。
罗素·麦克马洪

谨防!在某些情况下,用低通RC平滑PWM是不理想的。例如,如果您将Arduino输出插入MOSFET的栅极,则只要它由干净的PWM驱动,MOSFET就会保持低温状态。但是,如果将其平滑,MOSFET将开始散发更多的热量。有时候那不是一件好事。
弗洛林·安德烈
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.