将PWM频率设置为25 kHz


12

我目前可以使用以下代码将四个PWM引脚设置为31 kHz左右:

void setup()
{
    TCCR1B = TCCR1B & B11111000 | B00000001; // Set PWM frequency for D9 & D10:
    pinMode(pwmPin9, OUTPUT); // Sets the pin as output
    pinMode(pwmPin10, OUTPUT); // Sets the pin as output


    TCCR2B = TCCR2B & B11111000 | B00000001; // Set PWM for D3 & D11
    pinMode(pwmPin3, OUTPUT); // Sets the pin as output
    pinMode(pwmPin11, OUTPUT); // Sets the pin as output
}

我在某处找到了此设置,但我不知道如何将这四个PWM引脚设置为25 kHz左右。那怎么可能?


3
您了解AVR计时器的工作原理吗?
伊格纳西奥·巴斯克斯


1
@ IgnacioVazquez-Abrams我不熟悉,我需要在开始时将这四个引脚设置为25kHz左右。我急着完成一个项目,我将很高兴得到任何帮助。我将代码设置为31kHz。我可以将其修改为25kHz吗?直流电动机需要该频率。
user16307

1
@NickGammon谢谢,但是我现在真的没有足够的时间研究这些。您能提供给我代码部分来设置25kHz吗?我迷路了
user16307 '16

2
我需要调整它们的确切转速,以便它们的占空比会略有不同。将2个引脚仅设置为25kHz怎么样?
user16307

Answers:


10

我发布了第二个答案,因为我意识到可以在单个Arduino Uno上以161个步长使用25 kHz的4个PWM通道。这涉及将主时钟频率更改为8 MHz,这会产生一些副作用,因为整个程序的运行速度将降低一半。它还涉及重新配置三个定时器,该装置松动Arduino的计时功能(millis()micros()delay()delayMicroseconds())。如果这些折衷是可以接受的,请按以下步骤进行:

void setup()
{
    // Set the main system clock to 8 MHz.
    noInterrupts();
    CLKPR = _BV(CLKPCE);  // enable change of the clock prescaler
    CLKPR = _BV(CLKPS0);  // divide frequency by 2
    interrupts();

    // Configure Timer 0 for phase correct PWM @ 25 kHz.
    TCCR0A = 0;           // undo the configuration done by...
    TCCR0B = 0;           // ...the Arduino core library
    TCNT0  = 0;           // reset timer
    TCCR0A = _BV(COM0B1)  // non-inverted PWM on ch. B
        | _BV(WGM00);  // mode 5: ph. correct PWM, TOP = OCR0A
    TCCR0B = _BV(WGM02)   // ditto
        | _BV(CS00);   // prescaler = 1
    OCR0A  = 160;         // TOP = 160

    // Same for Timer 1.
    TCCR1A = 0;
    TCCR1B = 0;
    TCNT1  = 0;
    TCCR1A = _BV(COM1A1)  // non-inverted PWM on ch. A
        | _BV(COM1B1)  // same on ch. B
        | _BV(WGM11);  // mode 10: ph. correct PWM, TOP = ICR1
    TCCR1B = _BV(WGM13)   // ditto
        | _BV(CS10);   // prescaler = 1
    ICR1   = 160;

    // Same for Timer 2.
    TCCR2A = 0;
    TCCR2B = 0;
    TCNT2  = 0;
    TCCR2A = _BV(COM2B1)  // non-inverted PWM on ch. B
        | _BV(WGM20);  // mode 5: ph. correct PWM, TOP = OCR2A
    TCCR2B = _BV(WGM22)   // ditto
        | _BV(CS20);   // prescaler = 1
    OCR2A  = 160;
}

void loop()
{
    analogWrite( 3,   1);  // duty cycle = 1/160
    analogWrite( 5,  53);  // ~ 1/3
    analogWrite( 9, 107);  // ~ 2/3
    analogWrite(10, 159);  // 159/160
}

其他答案不同,它不需要以下版本的修改版本analogWrite():标准版本 可以正常工作。仅应注意:

  1. 写入的值应介于0(始终表示LOW)到160(始终表示HIGH)之间(包括端点值)。
  2. 仅引脚3、5、9和10可用。尝试连接analogWrite() 到引脚6或11不仅会无法提供PWM输出,还会分别改变引脚5或3的频率。

已经很长时间了,现在我对使用另一个处理器的Arduino Due感到困惑。如果你在这里HAE任何输入我会很高兴arduino.stackexchange.com/questions/67053/...
user16307

11

您可以将定时器1配置为在相位校正PWM模式下以25 kHz的周期循环,并在引脚9和10上使用它的两个输出,如下所示:

// PWM output @ 25 kHz, only on pins 9 and 10.
// Output value should be between 0 and 320, inclusive.
void analogWrite25k(int pin, int value)
{
    switch (pin) {
        case 9:
            OCR1A = value;
            break;
        case 10:
            OCR1B = value;
            break;
        default:
            // no other pin will work
            break;
    }
}

void setup()
{
    // Configure Timer 1 for PWM @ 25 kHz.
    TCCR1A = 0;           // undo the configuration done by...
    TCCR1B = 0;           // ...the Arduino core library
    TCNT1  = 0;           // reset timer
    TCCR1A = _BV(COM1A1)  // non-inverted PWM on ch. A
           | _BV(COM1B1)  // same on ch; B
           | _BV(WGM11);  // mode 10: ph. correct PWM, TOP = ICR1
    TCCR1B = _BV(WGM13)   // ditto
           | _BV(CS10);   // prescaler = 1
    ICR1   = 320;         // TOP = 320

    // Set the PWM pins as output.
    pinMode( 9, OUTPUT);
    pinMode(10, OUTPUT);
}

void loop()
{
    // Just an example:
    analogWrite25k( 9, 110);
    analogWrite25k(10, 210);
    for (;;) ;  // infinite loop
}

用写入值0 analogWrite25k()表示该引脚将始终为低电平,而将320表示始终为高电平。常规analogWrite() 应该几乎可以工作,但是它将解释255与320相同(即始终为HIGH)。

该代码假定使用Arduino Uno或类似的板卡(ATmega168或328 @ 16 MHz)。这里使用的方法需要一个16位定时器,因此它使用定时器1,因为它是Uno上唯一可用的定时器。这就是为什么只有两个输出可用的原因。该方法可适用于具有16位定时器的其他基于AVR的板。正如Gerben指出的那样,该计时器应具有一个对应的ICRx寄存器。Arduino Mega上有4个这样的计时器,每个都有3个输出。


1
解释一下此方法仅适用于timer1可能是有用的,因为其他计时器没有ICRx寄存器。每个计时器最多只能有一个PWM引脚,用于计时器0和
2。– Gerben

1
@Gerben:不是所有的16位定时器都有该寄存器吗?至少在兆丰上他们这样做。
Edgar Bonet

1
是的,但是在ATMega328上只有timer1是16位的。其余均为8位。OP需要4个PWM输出,而您的解决方案仅提供2个。还是我弄错了?
Gerben

1
@Gerben:不,你是对的。我只是说,要求ICRx似乎是多余的,因为要求计时器为16位。至少对于Uno和Mega,不确定其他基于AVR的Arduino。OP理解这仅提供2个PWM通道:请参阅我对他的问题和答案的评论。
Edgar Bonet

2
@techniche:1.为我工作。也许你忘了设置COM4C1TCCR4A?2.如果这不是问题,请阅读如何问一个好问题?,然后通过包含完整的源代码并明确说明您希望程序执行什么以及执行什么来更新您的问题(“我看不到任何成功”被认为是无效的问题说明)。
Edgar Bonet
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.