como-cambiar-la-frecuencia-de-un-pwm-en-arduino

How to change the PWM frequency on Arduino

  • 8 min

Today we are going to see how to change the frequency of an analog output pin on Arduino, which will require us to configure the registers that control the Timers associated with the PWM pins. But don’t worry, we’ll make it easy with a single function.

Using timers is common in processors. However, you may have often seen us recommend avoiding their manipulation in Arduino because it can have unexpected effects, especially with third-party libraries. So, if we usually recommend avoiding manipulating Timers in Arduino, why would we want to change their frequency now? Well, basically because this allows us to improve the behavior of certain devices when they are controlled by a PWM signal.

Remember that when controlling a device with a PWM signal, we are actually turning the device completely on and off multiple times per second. That is not the same as applying a true analog signal, and as a general rule, many actuators do not handle PWM control well.

Increasing the PWM frequency can improve the operation of certain devices because they have less time to “notice” that we have turned them off. If the device’s inertia is sufficient, and its natural frequency is higher than the PWM frequency, the behavior will be (almost) identical to supplying an analog voltage.

The most frequent practical case is when controlling the speed of a motor, where using PWM we turn the motor on and off multiple times per second. The motor is an inductive load, and each start-up results in a significant inrush current spike. This leads to a situation where, when applying a speed reduction, the motor consumes more power, heats up more, makes more noise… when, a priori, one might think it should be the opposite because it is running at a lower speed.

That also doesn’t mean we can increase the frequency indefinitely like crazy. Not all drivers we are using will support the frequency, and if they don’t… boom! Each particular case must be examined, but in general, raising the PWM control frequency to 4-10 kHz is quite reasonable.

So here is a cheat sheet with the frequency, prescalers, default values, and consequences for each of the PWM pins for Atmega328p and Atmega 32u2 processors, as well as a function to modify it easily.

Atmega 328p (Arduino Uno, Nano)

Frequency

PinsTimerFrequency
5, 6Timer062500 Hz
9, 10Timer131250 Hz
3, 11Timer231250 Hz

Default Values

PinsTimerPrescalersFrequency
5, 6Timer064977Hz
9, 10Timer164490Hz
3, 11Timer364490Hz

Prescalers

PinsTimerPrescalers
5, 6Timer01 8 64 256 1024
9, 10Timer11 8 64 256 1024
3, 11Timer21 8 32 64 128 256 1024

Consequences and Effects

PinsTimerEffect
5, 6Timer0delay() and millis()
9, 10Timer1Servo library
3, 11Timer2

Code

//Atmega 328p (Arduino Uno, Nano)
// Frequencies
// 5, 6   Timer0  62500 Hz
// 9, 10   Timer1  31250 Hz
// 3, 11   Timer2  31250 Hz

// Prescalers
// 5, 6   Timer0  1 8 64 256 1024
// 9, 10   Timer1  1 8 64 256 1024
// 3, 11   Timer2  1 8 32 64 128 256 1024

// Default values
// 5, 6   Timer0 64  977Hz
// 9, 10   Timer1 64  490Hz
// 3, 11   Timer2 64  490Hz

// Consequences
// 5, 6   Timer0  delay() and millis()
// 9, 10   Timer1  Servo library
// 3, 11   Timer2  

void setPWMPrescaler(uint8_t pin, uint16_t prescale) {
  
  byte mode;
  
  if(pin == 5 || pin == 6 || pin == 9 || pin == 10) {
    switch(prescale) {
      case    1: mode = 0b001; break;
      case    8: mode = 0b010; break;
      case   64: mode = 0b011; break; 
      case  256: mode = 0b100; break;
      case 1024: mode = 0b101; break;
      default: return;
    }
    
  } else if(pin == 3 || pin == 11) {
    switch(prescale) {
      case    1: mode = 0b001; break;
      case    8: mode = 0b010; break;
      case   32: mode = 0b011; break; 
      case   64: mode = 0b100; break; 
      case  128: mode = 0b101; break;
      case  256: mode = 0b110; break;
      case 1024: mode = 0b111; break;
      default: return;
    }
  }
  
  if(pin==5 || pin==6) {
    TCCR0B = TCCR0B & 0b11111000 | mode;
  } else if (pin==9 || pin==10) {
    TCCR1B = TCCR1B & 0b11111000 | mode;
  } else if (pin==3 || pin==11) {
    TCCR2B = TCCR2B & 0b11111000 | mode;
  }
}
Copied!

Atmega 32U (Micro and Leonardo)

Frequencies

PinsTimerFrequency
3, 11Timer064500Hz
9, 10Timer131250Hz
5Timer331250Hz
6, 13Timer431250Hz

Default Values

PinsTimerPrescalersFrequency
3, 11Timer064977Hz
9, 10Timer164490Hz
5Timer364490Hz
6, 13Timer464490Hz

Prescalers

PinsTimerPrescalers
3, 11Timer01 8 64 256 1024
9, 10Timer11 8 64 256 1024
5Timer31 8 64 256 1024
6, 13Timer41 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384

Consequences

PinsTimerEffect
3, 11Timer0delay() and millis()
9, 10Timer1
5Timer3
6, 13Timer4

Code

// Atmega 32U
// Frequencies
// 3, 11  Timer0  64500Hz
// 9, 10  Timer1  31250Hz
// 5  Timer3  31250Hz
// 6, 13  Timer4  31250Hz

// Prescalers
// 3, 11  Timer0  64  1 8 64 256 1024
// 9, 10  Timer1  64  1 8 64 256 1024
// 5  Timer3  64  1 8 64 256 1024
// 6, 13  Timer4  64  1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384

// Default values
// 3, 11  Timer0  64  977Hz
// 9, 10  Timer1  64  490Hz
// 5  Timer3  64  490Hz
// 6, 13  Timer4  64  490Hz

// Consequences
// 3, 11  millis(), delay()
// 9, 10
// 5
// 6, 13

void setPWMPrescaler(uint8_t pin, uint16_t prescale) 
{ 
  byte mode;
  
  if(pin==3 || pin==5 || pin==9 || pin==10 || pin==11) { 
    switch(prescale) {
      case    1: mode = 0b001; break;
      case    8: mode = 0b010; break;
      case   64: mode = 0b011; break; 
      case  256: mode = 0b100; break;
      case 1024: mode = 0b101; break;
      default: return;
    }
    
    
  } else if(pin==6 || pin==13) {
    switch(prescale) {
      case     1: mode = 0b0001; break;
      case     2: mode = 0b0010; break;
      case     4: mode = 0b0011; break;
      case     8: mode = 0b0100; break;
      case    16: mode = 0b0101; break;
      case    32: mode = 0b0110; break;
      case    64: mode = 0b0111; break;
      case   128: mode = 0b1000; break;
      case   256: mode = 0b1001; break;
      case   512: mode = 0b1010; break;
      case  1024: mode = 0b1011; break;
      case  2048: mode = 0b1100; break;
      case  4096: mode = 0b1101; break;
      case  8192: mode = 0b1110; break;
      case 16384: mode = 0b1111; break;
      default: return;
    }
  }
  
  if(pin==3 || pin==11) {
    TCCR0B = TCCR1B & 0b11111000 | mode;
  } else if (pin==9 || pin==10) {
    TCCR1B = TCCR1B & 0b11111000 | mode;
  } else if (pin==5) {
    TCCR3B = TCCR3B & 0b11111000 | mode;
  } else if (pin==6 || pin==13) {
    TCCR4B = TCCR4B & 0b11110000 | mode;
  }
}
Copied!

Download the Code

All the code from this post is available for download on Github. github-full