Home » Creating a variable frequency PWM output on Arduino UNO

Creating a variable frequency PWM output on Arduino UNO

The ARDUINO UNO is an amazing product that incorporates an ATmega328P onto a development board with a USB Bootloader.
The product offers the user the ability to program in C language, and with a lot of libraries and open source software available, most applications are very easily achieved.
However, the challenge that faced me was producing a product that gave me a PWM signal(s) running on a variable frequency between 100Hz – 4kHz.
The Arduino offers six PWM outputs, and they are connected to three timers in the circuit in pairs:
Timer0: Pins 5 and 6
Timer1: Pins 9 and 10
Timer2: Pins 11 and 3
Being connected to three different timers means that almost always these are not in sync, despite running off of one main clock, which is 16MHz in the case of the Arduino UNO.
There is a lot of help on adjusting the frequencies.
I found the following link very useful, and as it says, it is a very good cheat sheet:
http://www.arduino.cc/playground/Main/TimerPWMCheatsheet
This allows the user to use the different PWM signals at the prescaled frequencies without having to go through the ATmega328p datasheet.
It also covers the issues you may encounter if you use that approach.
The second article which takes this to the next level is Ken Shirriff’s blog: Secrets of Arduino PWM.
http://www.arcfn.com/2009/07/secrets-of-arduino-pwm.html
It is also available on the Arduino website, but over there it misses a few figures, so it is better to go to the original source.
http://arduino.cc/en/Tutorial/SecretsOfArduinoPWM
While it is not the easiest read, it is much easier than reading the datasheet, and Ken has managed to bring it down to a few numbers.
He uses Timer2 as an example, and while he explains everything in a great and easy to understand manner, the disadvantage there is that the user has to sacrifice one of the PWM outputs to gain the frequency adjustment on the other.
So in my application, I used Timer1, which is 16bit, and allows for a number to be stored in a register (ICR1), and the frequency is calculated based on that number. This gives me outputs on pins 9 and 10.
The number can be any value from 0 – 65535 (size = 16 bits).
The output frequency is given by the formula:
clip_image002
Where f = main clock (16MHz)
N = pre-scalar (set to 8 in my application)
TOP = the value in ICR1
Based on the values of ICR1 from 1 to 65535 we get a frequency range from 15Hz to 1MHz.
So for example:
ICR1 = 10,000 gives f = 100Hz
ICR1 = 5000 gives f = 200Hz
ICR1 = 2500 gives f = 500Hz
ICR1 = 1000 gives f = 1kHz
ICR1 = 500 gives f = 2kHz
ICR1 = 333 gives f = 3kHz
ICR1 = 250 gives f = 4kHz
ICR1 = 100 gives f = 10kHz
To do this, we configure the timer to run in phase and frequency correct mode, and we can set it to either inverting or non-inverting mode. We also select the pre-scalar.
These configurations are done by adjusting Timer1 registers TCCR1A and TCCR1B.
clip_image004
clip_image006
First thing to adjust is choosing Phase and Frequency correct mode in invert or non-invert mode.
clip_image008
The last two options give us non-invert and invert mode respectively.
Second thing to adjust is PWM, Phase and Frequency correct mode, with the TOP value determined by the ICR1 register, which is what determines the PWM Frequency.
clip_image010
In our case we will use Mode 8.
The last thing to do is to adjust the pre-scalar.
This is done by choosing the right modes from the following table. As discussed earlier, we shall be using mode which divides the main clock by 8.
clip_image012
All the above tables have been copied from the ATmega328P datasheet.
Now I shall go through the code which I wrote for my program. In my program, I had a POT on A3 to adjust the required frequency using ‘if’, and POT on A0 and A2 to adjust the output duty cycle.

unsigned long duty1,duty2;
// Duty Cycle in terms of a percentage.
unsigned long plus;
// Value read from A1, in case plus mode is activated
float xxx;
// Float numbers to calculate duty for PWM 1 and PWM 2
float yyy;
unsigned long pwm1;
// Value read from A0 and A2 to give PWM duty cycle output in terms // of 0-5V
unsigned long pwm2;
void setup(){

pinMode(9, OUTPUT);
pinMode(10, OUTPUT);
TCCR1A = _BV(COM1A1) | _BV(COM1B1) ; // phase and frequency correct mode. NON-inverted mode
// TCCR1A = _BV(COM1A1) | _BV(COM1B1) | _BV(COM1A0) | _BV(COM1B0) ;
//phase/frequency correct mode. SELECT THIS FOR INVERTED OUTPUTS.
TCCR1B = _BV(WGM13) | _BV(CS11);
// Select mode 8 and select divide by 8 on main clock.
}


void loop(){

// Program that lets different values on A3 choose different values of frequency, e.g. 100,200,400,500,1k,2k,3k,4k,10k,
//etc in relation with a free input.
if (analogRead(A3) <100) ICR1 = 10000; // 100Hz - Default value to 100Hz for A3 = 0V
if (analogRead(A3) 100) ICR1 = 5000; // 200Hz
if (analogRead(A3) 200) ICR1 = 2500; // 400Hz
if (analogRead(A3) 300) ICR1 = 1000; // 1000Hz
if (analogRead(A3) 400) ICR1 = 500; // 2000Hz
if (analogRead(A3) 500) ICR1 = 333; // 3000Hz
if (analogRead(A3) 600) ICR1 = 250; // 4000Hz
if (analogRead(A3) 700) ICR1 = 100; // 10000Hz
if (analogRead(A3) > 800) ICR1 = 1000; // Default value to 1kHz for A3 = 5V
//ICR1 = 1000; // for ICR1 = 1000, frequency = 1kHz.
pwm1 = analogRead(A2); // read duty from A2 for PWM 2
pwm2 = analogRead(A0); // read duty from A0 for PWM 1
xxx = float(pwm2);
// Turn read values from the POTs to float for mathematical
// adjustment.
yyy = float(pwm1);
xxx = xxx * ICR1;
// Multiply with ICR1 and divide by 1023 to give required percentage
yyy = yyy * ICR1;
xxx = xxx / 1023;
yyy = yyy / 1023;
//Assign values to OCR Registers, which output the PWM duty cycle.
OCR1B = int(xxx);
OCR1A = int(yyy);
}

Related Posts

78 thoughts on “Creating a variable frequency PWM output on Arduino UNO

  1. šŸ™‚ I came back to see updates. Ye freq and all doesn’t go well with me – I understand only computer languages šŸ™‚

  2. Most helpful posting I have seen so far on AVR timers. Still working on it. Where should I read to find out more about which registers define the compare level, which define top (I’m confused why it switches between ICR1 and OCR1A).

    I’m also wanting to see if I can define an ISR for when the Phase (and freq?) correct count goes down to 0. i.e. I want an interrupt at the start of each pulse period.

    I’ll also want to adjust the timing a bit on the fly, depending on sensors.
    AFAIK, I should be able to set the current count register for micro-timing adjustments.
    I may want to adjust period/compare level on the fly too according to sensor info.

    1. Thanks Aaron.
      I think your best bet is to read the ATMEL datasheet for whatever Arduino chip you are using on your Arduino. It is a lot to read but from what I gather, you are not scared to touch registers. The ICR1/OCR1A etc are defined in those datasheets.

      I do not have much experience with interrupts, so I would refer you to either the datasheet, or the ATMEL/ARDUINO forums.

      As far as adjusting timing on the fly is concerned, that is not very easy. Really depends on what you mean by on the fly. For example if a certain condition is fulfilled, yeah, just update the values in the register and your frequency would change. However, if you want to adjust the values with say an attached pot constantly, that does not quite work.

      Hopefully this answers some/most of your queries.

      Thanks for your kind comments, and spread the word šŸ™‚

  3. can smo please make a fuction out of it, for god’s sake.
    I donot understand a thing.
    like:
    void startPwmService(float MHz, int pinid, int pwmToSacrifice…. bla bla)
    and
    void stopPwmService(float MHz, int pinid, int pwmToSacrifice…. bla bla)

    Regrads,

    1. Hi jrb,
      Thanks for your comment. Theoretically, this could work with minimum change on the ATTiny85, since it is the same architecture. However, I have not gone through the datasheet, so not entirely sure if all the timers and associated parameters are the same. Feel free to try the code, and if it works, do tell us about it šŸ™‚
      Regards,
      Sami

  4. Look at your hardware. See the microcontroller on it. If you are using an Arduino, it is probably an Atmel. Look it up. Google that number. You will get the datasheet through that.

  5. Excellent Information, this is a great learning curve..
    thank you
    i am working on a project that gives an output of – square wave on a varying frequency for an old scope i still have in use..

  6. hi, can i change the frequency of the pwm up to 500kHz by changing the value in the ‘if ‘ code lines.

    thank you1

      1. Hi sami

        I have already tried to change the pwm frequency up to 500khz by changing your ‘if’ code above and replace it with mapping code. I mapped the analog input of frequency 0 – 1023 with ICR1 value 1000 – 2 to vary the frequency from 1khz to 500 khz and it worked.

        The problems is as the frequency goes higher the pwm output become fuzzy. It is like not stable. Does it because of the coding i used.
        Please reply. I appreciate it.

        Thank you for you coding!

        1. There could be a few reasons for your PWM being fuzzy:

          – Your wiring – if the distance between your output and your measurement is a long wire, it will attract more noise. Higher the frequency, the more noise you get.
          – The noise could be picked up by your Scope. Try applying BW limiting to your scope.
          – It could be a natural limitation of how the Arduino has been laid out. It is not the best laid out board, as it is designed to be a learning tool.

          I suggest raising the frequency slowly, and see how it goes. If it starts getting fuzzier above a certain value, then focus on that value.

          Add some Low pass filtering to your circuit. A simple RC network should do nicely. Calculate values as per your circuit/frequency requirements.

          Give me a shout to let me know how this progressed šŸ™‚

  7. In my program, I had a POT on A3 to adjust the required frequency using ā€˜ifā€™, and POT on A0 and A2 to adjust the output duty cycle.

    Can you tell me what value pots you used please?

  8. can i know what is most important on this value between voltage and frequency ?i know to use the formula , but i not clear when my PID controller output(A3) want to get this range, help please tq,

    Program that lets different values on A3 choose different values of frequency, e.g. 100,200,400,500,1k,2k,3k,4k,10k,
    //etc in relation with a free input.
    if (analogRead(A3) <100) ICR1 = 10000; // 100Hz – Default value to 100Hz for A3 = 0V
    if (analogRead(A3) 100) ICR1 = 5000; // 200Hz
    if (analogRead(A3) 200) ICR1 = 2500; // 400Hz
    if (analogRead(A3) 300) ICR1 = 1000; // 1000Hz
    if (analogRead(A3) 400) ICR1 = 500; // 2000Hz
    if (analogRead(A3) 500) ICR1 = 333; // 3000Hz
    if (analogRead(A3) 600) ICR1 = 250; // 4000Hz
    if (analogRead(A3) 700) ICR1 = 100; // 10000Hz
    if (analogRead(A3) > 800) ICR1 = 1000; // Default value to 1kHz for A3 = 5V

          1. so i can change the of frequency that i want by change the value of ICR1,,,,,, sorry can you tell me the important of range here?,,,,,i just the begginer in arduino sorry

      1. Iā€™ve just found out that I can actually turn my Analog input pins into Digital Inputs or Outputs. Itā€™s really simple. it’s same concept with this?

  9. when i use non inverted mode ,the PWM output give perfect result ,,,but when i used inverted mode such as TCCR1A = _BV(COM1A1) | _BV(COM1B1) | _BV(COM1A0) | _BV(COM1B0) the output pwm not give the good result because the output pwm not inverted ,, please help me ,what happen here, and to solve this problem?

  10. The COM1A1/COM1A0 bits control pin 9 – set to 10 is one sense, set to 11 is the inverse. Ditto COM1B1/COM1B0 bits control for pin 10. change the code TCCR1A = _BV(COM1A1) | _BV(COM1B1) | _BV(COM1B0) ; ,,,it’is true? i’am confious

      1. i got the perfect invert mode ,,,,just only change THIS ->TCCR1A = _BV(COM1A1) | _BV(COM1A0) | _BV(COM1B1) | _BV(COM1B0) to this ->TCCR1A = _BV(COM1A1) | _BV(COM1B1) | _BV(COM1B0),,,thanks a lot for this coding very helping for my project šŸ™‚

  11. Hi, I am interested on your program, i am working on modified sine wave which is delaying at the 2nd 3rd and so on. Can you help me how to delay output signal as shifted 90degrees and adding 2 more output?

  12. Wow, this is rather over my head! šŸ™

    I’m trying to find out how I can set a pin to use a PWM frequency of 960Hz… can you or anyone reading this blog assist?

    Many thanks in advance

      1. Thanks Sami. Any quick and easy usable example? I’ve still not quite fathomed out to implement all this stuff from this page.

        1. See the formula which relates N to frequency at the start. That should give you the exact number. As for example, the quickest one I can give is the one here.

  13. Hi,
    Base on my understanding this code changes the frequency therefore, the input of the frequency is from POT A3. We would have determine the frequency. I was wondering if you know how to create a code with fix frequency but changing the duty cycle. I would greatly appreciate if you could help me. Thank you very much.

    1. Hi Anisah,

      To create a fixed code, do not use A3 to adjust frequency. Just input one of the values into ICR3 as per this code:

      if (analogRead(A3) <100) ICR1 = 10000; // 100Hz - Default value to 100Hz for A3 = 0V
      if (analogRead(A3) 100) ICR1 = 5000; // 200Hz
      if (analogRead(A3) 200) ICR1 = 2500; // 400Hz
      if (analogRead(A3) 300) ICR1 = 1000; // 1000Hz
      if (analogRead(A3) 400) ICR1 = 500; // 2000Hz
      if (analogRead(A3) 500) ICR1 = 333; // 3000Hz
      if (analogRead(A3) 600) ICR1 = 250; // 4000Hz
      if (analogRead(A3) 700) ICR1 = 100; // 10000Hz

      i.e. , if you want 10kHz, just put in ICR1.

      Using the same code that you have in this article, you can still adjust the duty cycle, with A0 and A2.

  14. Hi,
    I have tried out the code and realize that it need a fix frequency input from a source. I was wondering if you know how to create a code by using the PWM of the arduino itself? Thank you very much.

  15. Hi,

    still looking for an easy PWM-frequency to ICR1-value conversion? Here you go:

    // f_XTAL ( e.g.16’000’000), N_prescale (acc. TCCR1B setting), f_req (=desired PWM-frequency [1Hz,1MHz])
    int freqToICR1 (long f_req, int N_prescale, long f_XTAL) {
    long temp1 = 2*N_prescale*f_req;
    int ICR1_val = f_XTAL/temp1;
    return ICR1_val;
    }

    Remark: no argument checking & …

  16. I found the original post extremely helpful in moving from the 490 Hz provided by the analog write command to my desired 16 kHz output. I built a test circuit and I reworked the code a bit to provide continuous frequency control, to provide a Serial output to see what is going on, and used map commands to keep everything in bounds.

    Using this it should be very easy to see the relationship between frequency and number of PWM steps available — particularly if a scope is used to view the output.

    It should be possible to copy and paste this directly into a sketch and have it work (after straightening out the Serial.print command).

    In my target program, I mapped the available control values into the available steps and it worked well. Thanks all.

    //Pgm Timing vars
    long previousMillis = 0; // will store last time valuse measured
    long interval = 100; // interval at which to Measure values (milliseconds)
    unsigned long currentMillis = 0;

    //pwm vars
    unsigned pwm1; // Value read from A0 to give PWM duty cycle output in terms of 0-5V
    unsigned pwm2;// Value read from A0 to give PWM duty cycle output in terms of 0-5V
    unsigned pwmSpan;

    void setup(){

    //Set up PWM
    pinMode(9, OUTPUT);
    pinMode(10, OUTPUT);

    // phase and frequency correct mode. NON-inverted mode
    TCCR1A = _BV(COM1A1) | _BV(COM1B1) ;
    //phase/frequency correct mode. SELECT THIS FOR INVERTED OUTPUTS.
    // TCCR1A = _BV(COM1A1) | _BV(COM1B1) | _BV(COM1A0) | _BV(COM1B0) ;
    // Select mode 8 and select divide by 8 on main clock.
    TCCR1B = _BV(WGM13) | _BV(CS11);

    Serial.begin(9600);
    }

    void loop(){
    currentMillis = millis();
    if(currentMillis – previousMillis > interval)
    {
    previousMillis = currentMillis;

    //Use for fixed frequency
    //ICR1 = pwmSpan = 10; // 62 for 16.16 KHz // for ICR1 = 1000, frequency = 1kHz.

    // Program that lets different values on A3 choose different values of frequency,
    // e.g. 100,200,400,500,1k,2k,3k,4k,10k,etc in relation with a free input
    // Pot at A3 Use to vary the frequency.
    ICR1 = pwmSpan = map(analogRead(A3),0,1023,100,10);
    //Set last two valuse in the above map command to set frequency range of Pot A3
    //10000 = 100 Hz (10,000 PWM steps), 1000 = 1 kHz (1000 PWM Stemps),
    //100 = 10 khz (100 PWM steps), 10 = 100khz (10 pwm steps)

    pwm1 = analogRead(A0); // read duty from A0 for PWM 1
    pwm2 = analogRead(A2); // read duty from A2 for PWM 2

    pwm1 = map(pwm1,0,1023,0,pwmSpan); // map the duty cycle to the available steps
    pwm2 = map(pwm2,0,1023,0,pwmSpan); //map the duty cycle to the available steps

    OCR1A = pwm1; // set PWM pin 9
    OCR1B = pwm2; // set PWM Pin 10

    Serial.println(“pwm1 = “+String(pwm1)+” pwm2 = “+String(pwm2)+” pwmSpan = “+String(pwmSpan));

    //The following lines can be used in place of the four lines above the Serial.print line
    //if Serial print not used: (or if you want to view the A0 and A2 pot outputs)
    //OCR1A = map(pwm1,0,1023,0,pwmSpan); //set PWM pin 9
    //OCR1B = map(pwm2,0,1023,0,pwmSpan); //set PWM Pin 10

    }
    }

    1. Could you possibly share your test circuit design for your above code? I am looking to build an LED strobopscope that can operate with adjustable frequency and duty cycle. I have very little experience with circuit design. Does the resistance of the pot matter or not so much since you set the frequency range in the code above?

      I have seen several other designs for something similar to what I am trying to build but they lack the ability to strobe at high frequency with adjustable pulse width.

      Any help would be greatly appreciated!

      Thanks,
      Jim

  17. Hello
    Thank you very much for your information helped me a lot, but even achievement not finish my goal is Generate 6 PWM to 27KHz in Arduino UNO, Which is the counter 1 it fit my needs and works very well, But I can not make it same with the other two counters adjust to the same frequency and change duty cycle.

    Please auxiliary can me, I appreciate forever.

  18. Great tutorial, works šŸ™‚ if I understand it right, this part of code:

    xxx = xxx * ICR1;
    // Multiply with ICR1 and divide by 1023 to give required percentage
    yyy = yyy * ICR1;
    xxx = xxx / 1023;
    yyy = yyy / 1023;

    will always keep duty cycle in proportion/relation to changed frequency
    if I would like to set static duty cycle, let’s say 200 and prevent it from change even if frequency change, which step would you eliminate? thank you

    1. So xxx and yyy are giving you pwm outputs on two separate pins. To have a constant frequency you can replace the variable ICR1 with the actual value. This will be 5000 in the case of 200 Hz.

  19. Thanks for a great explanation. Using this method is it possible to generate complimentary square waves on pins 9 and 10? (non-inverted and inverted signals)

  20. Hiā€¦.anyone could help me pleaseā€¦i really need your helpā€¦i need a arduino PWM generator coding for boost converterā€¦if anyone willing to send..please send to my email sasidharan21777@gmail.comā€¦i really appreciate if anyone help meā€¦thankz in advanced.

Leave a Reply

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