Arduino Uno – High Frequency PWM

I did some tests to see what max PWM frequency you can archive without overclocking the Uno and running it with the stock 16Mhz crystal. The default Arduino PWM frequency is 500Hz. If you need a higher frequency you have to manually bit-bang one of the output pins.

This example shows how to do a 30% duty cycle. The delays (and the overhead of the other instructions) determine the PWM frequency. In this case I got a PWM frequency of 9.1kHz . I disabled all interrupts to avoid any interference from processing interrupt service routines.

const int PWM_PIN = 8;

void setup()
{
  pinMode(PWM_PIN, OUTPUT);
  noInterrupts();

  // Don't use loop() to avoid serialEventRun overhead
  while (true) {
    // 30% duty
    digitalWrite(PWM_PIN, HIGH);
    delayMicroseconds(30);
    digitalWrite(PWM_PIN, LOW);
    delayMicroseconds(70);
  }
}

void loop(){}


By removing the delays we can increase the frequency to 97.5kHz. This way we lose the ability to set the duty cycle though and end up with a fixed duty cycle of 50%.

void setup()
{
  pinMode(8, OUTPUT);
  noInterrupts();

  while (true) {
    digitalWrite(PWM_PIN, HIGH);
    digitalWrite(PWM_PIN, LOW);
  }
}

void loop(){}

As we are just turning pins on and off, considering a 16MHz clock frequency, 97kHz seems pretty low. The problem is the overhead of using digitalWrite(). If we just populate the port registers manually we can increase the frequency to 4Mhz!:

void setup()
{
  pinMode(8, OUTPUT);
  noInterrupts();

  while (true) {
    PORTB = B000001;
    PORTB = B000000;
  }
}

void loop(){}

At this point things start to become a bit unstable though. The duty cycle that should be around 50% fluctuated between 16-33% in my tests.

4MHz

To push things even more we need to get rid of the while() loop which seems to take up 2 cycles each time. That’s were things get messy. To “remove” the loop I ended up repeating the writes to the port registers till the program storage space was full. If you repeat the 2 instructions 7500 times you fill up the program storage somewhere around 94%. Now the while loop only gets evaluated every 15,000 instructions. With this sketch the PWM freqency is 8MHz which is max what you can get from a 16MHz clock.

const int PWM_PIN = 8;

void setup()
{
  pinMode(PWM_PIN, OUTPUT);
  noInterrupts();

  while (true) {
    PORTB = B000001;
    PORTB = B000000;
    PORTB = B000001;
    PORTB = B000000;
    ... (repeat ~7500 times)
    PORTB = B000001;
    PORTB = B000000;
    PORTB = B000001;
    PORTB = B000000;
  }
}

void loop(){}

8MHz

Again the duty cycle is not 50% and as you are not able to execute any other code apart from the PWM this is not really of any practical use. But still an interesting experiment that helped me to learn about PWM and optimizing Arduino code.

I'm available for contracting work. Check out my LinkedIn profile and my portfolio page for an overview of my skills and experience. If you are interested in working with me please use the contact form to get in touch.

Arduino Uno – High Frequency PWM

6 thoughts on “Arduino Uno – High Frequency PWM

    1. Michael Thessel says:

      The Software is called Saleae Logic (https://www.saleae.com). It comes with their logic analyzers. Their software is also compatible with a lot of logic analyzer clones you can get for cheap on Ebay.

    1. Michael Thessel says:

      Unfortunately not, this was just an experiment and with these high frequencies you don’t have the fine grained control to adjust to 3 or 6 MHz.

Leave a Reply

Your email address will not be published. Required fields are marked *