Go Down

Topic: Highest PWM frequency output for the Uno/Nano (Read 6250 times) previous topic - next topic

PeekabuPi

What is the highest PWM frequency that can be output by the Uno or Nano?

I cant remember exactly right now but I remember measuring around 70kHz output using an oscilloscope with the following program:

Code: [Select]

void loop ()
{
 digitalWrite(outPin, HIGH);
 digitalWrite(outPin, LOW);
}


That program above is essentially the fastest that it can generate a PWM pulse right? So is it capped at 70kHz then?

How do you get higher frequencies straight out of the digital I/O?

retrolefty

#1
Oct 21, 2012, 04:30 am Last Edit: Oct 21, 2012, 04:32 am by retrolefty Reason: 1
Direct port manipulations will allow you to toggle a output pin as fast as possible for a given clock rate.

http://www.arduino.cc/playground/Learning/PortManipulation

Lefty

Nick Gammon

#2
Oct 21, 2012, 05:24 am Last Edit: Oct 21, 2012, 05:29 am by Nick Gammon Reason: 1
Code: [Select]

 digitalWrite(outPin, HIGH);
 digitalWrite(outPin, LOW);


That's not really PWM. Use the internal timers to do that.

I have some examples near the bottom of this page:

http://www.gammon.com.au/forum/?id=11504

If you use the "fast PWM mode, top at OCR0A" mode then you can get quite high frequencies, the trade-off being lower PWM resolution.

As an example:

Code: [Select]

#include <TimerHelpers.h>

// Timer 0

// output    OC0B   pin 11  (D5)

const byte timer0OutputB = 5;
 
void setup() {
  pinMode (timer0OutputB, OUTPUT);
  TIMSK0 = 0;  // no interrupts
  Timer0::setMode (7, Timer0::PRESCALE_1, Timer0::CLEAR_B_ON_COMPARE);
  OCR0A = 3;   // count to 4, zero-relative
  OCR0B = 1;   // duty cycle
}  // end of setup

void loop() {}


Because OCR0A is small it counts up to 4, giving a frequency of 4 MHz (one quarter of the system clock). However the duty cycle can only be 0, 1 or 2, so basically you can have a 33% or 66% duty cycle.

The higher you make OCR0A the lower the frequency but the higher resolution of PWM duty cycle.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

Nick Gammon

Screenshot of above test confirms results:



Note the frequency and the duty cycle. To get a duty cycle of 33.3% you need OCR0B to be 2 rather than 1.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

PeekabuPi

#4
Oct 21, 2012, 05:37 am Last Edit: Oct 21, 2012, 05:40 am by PeekabuPi Reason: 1
Thanks. Looks like the default digitalWrite() has too much overhead. I just tried measuring the uno with the OP code and I topped at 116kHz. The nano that I measured before topped at 70kHz. But seeing there are many different timers I guess it depends on which pin too?

For the sample code you supplied using the TimersHelpers.h library, I downloaded it and tried the sample code. There was an error on debug saying TImer0 is not defined?

If I want to create 50% duty cycle signals at the highest frequency possible (essentially just a normal periodic square wave). Can that be done up to 4MHz (you mentioned 4MHz can only choose between 33% or 66% duty)?

Nick Gammon

#5
Oct 21, 2012, 05:50 am Last Edit: Oct 21, 2012, 05:52 am by Nick Gammon Reason: 1
For a normal square wave use a simpler timer mode like CTC. Example:

Code: [Select]

#include <TimerHelpers.h>

// Timer 0

// output    OC0A   pin 12  (D6)

const byte timer0OutputA = 6;
 
void setup() {
  pinMode (timer0OutputA, OUTPUT);
  TIMSK0 = 0;  // no interrupts
  Timer0::setMode (2, Timer0::PRESCALE_1, Timer0::TOGGLE_A_ON_COMPARE);
  OCR0A = 1;  // count to 2
}  // end of setup

void loop() {}


That outputs a 4 MHz square wave.

If you change OCR0A to 0 you get a 8 MHz square wave.

If you are getting compile errors, that code was for the Uno. Did you install the timer helpers library?

http://gammon.com.au/Arduino/TimerHelpers.zip
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

PeekabuPi

I am using an Uno right now too. Yeah I saw the link from the site you linked to earlier.

It says Timer0 is not declared?

Nick Gammon

Install the library, restart the IDE. It should compile then.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

Nick Gammon

Unzip that file, copy the folder TimerHelpers into the libraries folder (in your Arduino sketches folder) then restart the IDE. Copy the whole folder, not just the contents.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

PeekabuPi

#9
Oct 21, 2012, 06:14 am Last Edit: Oct 21, 2012, 06:23 am by PeekabuPi Reason: 1
Thanks. I got it to work. The restart seemed to do it.

I also measure 4MHz square wave now too! Cheers!A few noob question:

Does it still source the maximum 50mA current?
Does this code also work for the nano?
Does the code work for all digital pins?

I just tried to move part of the code over to loop and the signal is no longer steady and 50% dutycycle i guess because there is some time lag with the restarting the loop? So this method only works under void setup()?

Nick Gammon

Quote

Does it still source the maximum 50mA current?


20 mA more like. 40 mA is the absolute maximum.

Quote
Does this code also work for the nano?


I imagine so.

Quote
Does the code work for all digital pins?


The timers have fixed output pins. Each timer has two pins dedicated to it which is why you have 6 PWM outputs on the Uno (3 timers x 2 pins each).

Quote
I just tried to move part of the code over to loop and the signal is no longer steady and 50% duty cycle i guess because there is some time lag with the restarting the loop? So this method only works under void setup()?


That doesn't make sense. Show your amended code please. Once you have the timer set up it doesn't matter what the code is doing. However if you put that code into loop it might be restarting the timer very rapidly. You don't want that.

The timer (that I showed) runs independently, so that is why I had it in setup. You don't have to keep calling the code. It's all done in hardware.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

PeekabuPi

Hi I think I just tired this:


Code: [Select]
#include <TimerHelpers.h>

// Timer 0

// output    OC0A   pin 12  (D6)

const byte timer0OutputA = 6;
 
void setup() {
   pinMode (timer0OutputA, OUTPUT);
}  // end of setup

void loop() {

   TIMSK0 = 0;  // no interrupts
   Timer0::setMode (2, Timer0::PRESCALE_1, Timer0::TOGGLE_A_ON_COMPARE);
   OCR0A = 1;  // count to 2
}


Also I tried it with the nano recently. I noticed that the uno produces better square waves than the nano. Are the pulses generated by the microcontroller (which are the same for the uno and nano)? Or the crystal?

I was wondering if there is any documentation on how to use the TimeHelper header file?

Nick Gammon

Don't do that in loop.

Quoting myself, who you have chosen to ignore:

Quote
However if you put that code into loop it might be restarting the timer very rapidly. You don't want that.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

dhenry

Quote
What is the highest PWM frequency that can be output by the Uno or Nano?


The hardware pwm module can be quite helpful here.

The spi / uart module can also be used to generate pwm in case that's needed.


PeekabuPi

Sorry didnt mean to ignore the advice. I just tried it for the sake of it. Actually the reason is I intend to be able to modulate the frequency so maybe Id want it in the loop instead.

Go Up