7

I currently can set four PWM pins to around 31 kHz with the following code:

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
}

I found this setup somewhere, but I don't know how I can set these four PWM pins to around 25 kHz instead. How is that possible?

  • 3
    Do you understand how the AVR timers work? – Ignacio Vazquez-Abrams Jun 24 '16 at 1:04
  • 3
    See my page about timers. – Nick Gammon Jun 24 '16 at 1:06
  • 1
    @IgnacioVazquez-Abrams Im not familiar and I need to set those four pins to around 25kHz at the beginning . I have hurry to finish a project and I would be glad any help. The code I have sets to 31kHz. Can I modify it to 25kHz? DC motors require that freq. – user16307 Jun 24 '16 at 1:08
  • 1
    @NickGammon Thanks but I really dont have enough time to study these at the moment. Could you provide me the code part to setup 25kHz. Im lost – user16307 Jun 24 '16 at 1:12
  • 2
    I need to tune their exact rpm so their duty cycles will be slightly different. How about is it possible to set 2 pins only to 25kHz? – user16307 Jun 24 '16 at 7:26
8

I am posting this second answer since I realized it is possible to have 4 PWM channels at 25 kHz with 161 steps on a single Arduino Uno. This involves changing the main clock frequency to 8 MHz, which has some side effects since the whole program will run half as fast. It also involves reconfiguring the three timers, which means loosing the Arduino timing functions (millis(), micros(), delay() and delayMicroseconds()). If these trade-offs are acceptable, here is how it goes:

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
}

Unlike the other answer, this does not need a modified version of analogWrite(): the standard one will work fine. Only care should be taken that:

  1. The value written should be between 0 (meaning always LOW) and 160 (always HIGH), inclusive.
  2. Only pins 3, 5, 9 and 10 are available. Attempting to analogWrite() to pins 6 or 11 will not only fail to deliver a PWM output, it will also change the frequency on pin 5 or 3 respectively.
7

You can configure Timer 1 to cycle at 25 kHz in phase correct PWM mode, and use it's two outputs on pins 9 and 10 like so:

// 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
}

Writing a value of 0 with analogWrite25k() means the pin will be always LOW, whereas 320 means always HIGH. The regular analogWrite() should almost work, but it will interpret 255 the same as 320 (i.e. always HIGH).

This code assumes an Arduino Uno or similar board (ATmega168 or 328 @ 16 MHz). The method used here requires a 16-bit timer, and thus it uses Timer 1 as it's the only one available on the Uno; that's why only two outputs are available. The method could be adapted to other AVR-based boards with a 16-bit timer. As Gerben noted, that timer should have a corresponding ICRx register. There are 4 such timers on the Arduino Mega, each with 3 outputs.

  • 1
    It might be useful to explain that this method only works for timer1, as the other timers don't have a ICRx register. At most, you can only have one PWM pin per timer, for timers 0 and 2. – Gerben Jun 24 '16 at 13:04
  • 1
    @Gerben: Don't all the 16-bit timers have that register? At least on the Mega they do. – Edgar Bonet Jun 24 '16 at 13:09
  • 1
    Yes, but only timer1 is 16-bit on the ATMega328. The rest are 8-bit. And the OP wants 4 PWM output, and your solution only provides 2. Or am I mistaken? – Gerben Jun 24 '16 at 15:12
  • 1
    @Gerben: No, you are right. I just say that requiring ICRx seems to be redundant with requiring the timer to be 16-bits. For the Uno and Mega at least, not sure about other AVR-based Arduinos. The OP understand this only provides 2 PWM channels: see my comment on his question and his answer. – Edgar Bonet Jun 24 '16 at 15:48
  • 2
    @techniche: 1. Works for me. Maybe you forgot to set COM4C1 in TCCR4A? 2. If that's not the problem, then read How do I ask a good question?, then update your question by including your full source code and clearly stating what you expect the program to do and what it does instead (“I dont see any success” is not considered a valid problem statement). – Edgar Bonet Sep 15 '17 at 8:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.