Arduino Forum

Forum 2005-2010 (read only) => Hardware => Interfacing => Topic started by: sonofabell on May 14, 2008, 09:59 am

Title: PWM capabilities
Post by: sonofabell on May 14, 2008, 09:59 am
Apologies in advance if this is answered elsewhere or posted in the wrong forum.  I would like to drive a FET with a PWM in a very precise manner.

I need the PWM to run at a high frequency (around 100kHz, ideally).  I would like the PWM to be on and off at very specific intervals, roughly 3ms apart.  So I would basically need to be able to turn the PWM on and off with sub-ms accuracy.  On top of that, at every (roughly) 3ms interval I would like to be able to change the duty cycle of the PWM across the entire range, from 0% to 100%.

If you tell me this is all possible with the Arduino, you are my hero, and I will be somebody else's hero.   :)

If I was unclear about something, please let me know and I will clarify.

Title: Re: PWM capabilities
Post by: bens on May 14, 2008, 10:44 am
If you clock a hardware timer off of the Arduino's 16 MHz clock, you can generate a 100 kHz PWM by having the timer overflow after 160 counts.  This means that your PWM will have 160 steps between 0 and 100%.  Running at this frequency, you'd be able to easily turn the PWM on and off with sub-millisecond accuracy.

- Ben
Title: Re: PWM capabilities
Post by: sonofabell on May 17, 2008, 01:03 am
Not sure I follow.  Can I do this in software?  I need to be able to do this in software because I want to change the duty cycle of the PWM from time to time, as well as to turn it off at specific intervals.

I tried this code in void loop() :

for (int i = 1; i <= 300; i++){
 digitalWrite(10, HIGH);
 digitalWrite(10, LOW);

However, looking at the signal with a scope, I can tell that the command "delayMicroseconds(5);" is *not* 5us.  It is closer to 10us, which I assume is partly due to the overhead in executing the command.

What can I do to get more precision on my timing?
Title: Re: PWM capabilities
Post by: bens on May 17, 2008, 02:59 am
If you need precision timing, you will probably have to bypass many of the Arduino library functions and directly control the AVR peripherals (e.g. hardware timers) yourself using the mega168 registers (then again, I have little experience with what's out there so maybe you can find some existing libraries that will suit your needs).  My advice is: don't be afraid to get your hands dirty and delve into the mega168 datasheet.

The following code will generate a 100 kHz pwm on pin OC2B (Arduino digital pin 3) on a mega168 running at 16 MHz:

Code: [Select]

 // configure hardware timer2 to generate a fast PWM on OC2B (Arduino digital pin 3)
 // set pin high on overflow, clear on compare match with OCR2B
 TCCR2A = 0x23;
 TCCR2B = 0x09;  // select timer2 clock as unscaled 16 MHz I/O clock
 OCR2A = 159;  // top/overflow value is 159 => produces a 100 kHz PWM
 pinMode(3, OUTPUT);  // enable the PWM output (you now have a PWM signal on digital pin 3)

 OCR2B = 80;  // set the PWM to 50% duty cycle
 do some stuff;
 OCR2B = 40;  // set the PWM to 25% duty cycle
 do some stuff;
 pinMode(3, INPUT);  // turn off the PWM
 do some stuff;
 pinMode(3, OUTPUT);  // turn the PWM back on

Note that you can also turn the PWM off by setting the duty cycle to 0: OCR2B = 0.  However, AVRs aren't so great about handling a PWM with 0% duty cycle and will end up producing tiny glitches at your PWM frequency, so a cleaner way to disable them is by turning the output pin to an input.  The down side to using the input technique is that when you set it back to an output you might get an erroneous first high pulse that is much longer than it should be.

Also note that any Arduino library functions that rely on timer2 will probably conflict with this code.

Does this make any sense?

- Ben