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:
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.
First thing to adjust is choosing Phase and Frequency correct mode in invert or non-invert mode.
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.
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.
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);
}
hey! long time… 🙂
and after so many ages I drop by to see a typically “geeky” code..so cool! 🙂
Thanks! Yeah has been a while. Have not really done the blogging I have been planning to do lately but at least got some stuff in 🙂
Shall be updating the blog with numbers for Aruino MEGA in some while as well if anybody is interested!
🙂 I came back to see updates. Ye freq and all doesn’t go well with me – I understand only computer languages 🙂
to me frequency and pwm are very much of computer languages as well… but then again i did specialize in electronics so I cannot judge 🙂
Well i know about arduino that it can understand alphabetical as well as numerical the great advantage of arduino uno.
Jaycon
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.
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 🙂
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,
Dear Unknown,
You’re not the only one 🙂
Yes. Dear Sami Mughal, can you pls show us a simple way to make a pwm pin on arduino to pulse at any frequency we would want to.
Hi Shankar,
I think the way above was the best way I found to do that.
Thanks for your comment.
curious if this code can be loaded on to a ATTiny85 as a standalone w/o the arduino.
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
why u use “2” in ur frequency equation?
which 2 are you talking about?
F/2.N.TOP
WHAT DOES THE 2 MEAN THERE?
That is part of the formula, as given by the datasheet. Refer to the datasheet for more information.
any links to the datasheet reference please?I have been trying to look but cant find.
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.
thanks boss. got it.
This is without a doubt the best Timer explanation I have found for the Arduino. I have being able to push my Atmega328 so much further! Thank you!
Thank you ! Glad this is helping people!
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..
hi, can i change the frequency of the pwm up to 500kHz by changing the value in the ‘if ‘ code lines.
thank you1
Theoretically yes, but I haven’t tried it so do try it and let us know too.
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!
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 🙂
ok thank you.
will do!
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?
Unfortunately I cannot. It was too long ago. I imagine they will be 1k or 10k.
hye can i replace the POT input with the PID controller use this code.
thank you
Anything that can give an output of a number can be used. It is up to you how you use it.
Just replace the input from the POT with whatever you want to use.
ok got it
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
What output does your PID have? If it is voltage, you can use the same method as I did with the POT.
You can normally keep the frequency fixed.
current value is my output from pid ,so if i change the range the ICR1 will also change and also frequency ,,,
ok i got it the value of frequency and ICR1 keep use as you code ,,just change the range ,,,is that true?
Go for it.
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
You’ll have to read the data sheet for the microcontroller on your Arduino.
at the pin A0 and A2 no have anything such input or output in hardware configuration right?
Once again, if you look at the datasheet, you’ll find the answer.
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?
Yes. Though not all of them will give out pwm
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?
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
I’m sorry it’s been a few years since I have read the data sheet. What does it say?
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 🙂
I’m sorry it’s been a few years since I have read the data sheet. What does it say?
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?
Hi Ginanjar, unfortunately I tried that back then and couldn’t find how to do it. There maybe a way now, so don’t give up.
fast response, thank you. Maybe you can find out the way to shifting the phase a signal?
what must I do?
There wasn’t any command that I could find sadly.
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
To get 960 Hz, a value of N just over a 1000 should do the trick.
Thanks Sami. Any quick and easy usable example? I’ve still not quite fathomed out to implement all this stuff from this page.
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.
I made an adjustable frequency PWM library for Arduino. it does 1Hz to 2MHz, with 125ns resolution for pulse width.
http://www.rmcybernetics.com/projects/code/index.htm
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.
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.
Thank you very much for your help. I will try out the program.
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.
Try using the simple analogWrite code, as explained on the following page :
https://www.arduino.cc/en/Reference/AnalogWrite
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 & …
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
}
}
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
Thanks for this updated codes!
Thanks for the post! I am able to adjust the frequency and the duty cycle independently!
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.
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
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.
Well sometimes you just have to try and it worked for me, I have just declared OCR1A = 100;
and the result is duty in lenght of 0.1mS
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)
Hi… Not that I am aware of. I wrote about this in this article: https://www.oxgadgets.com/2011/07/arduino-out-of-sync-pwms.html
You can do it using an Arduino Mega.
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.
COMPLETE DETAILED TUTORIAL