| Author |
Message |
|
|
Posted: Jul 18, 2008 - 10:07 AM |
|


Joined: Jan 23, 2004
Posts: 7014
Location: Melbourne, Victoria, Australia
|
|
This is a continuation of my Timers tutorial and assumes the reader has read and understood that text before beginning this one.
Part Nine - Pulse Width Modulation (PWM)
The previous sections of the timers tutorial have been dealing with a simple task - flashing a LED. We've managed to accomplish that task using a variety of methods which all center around the "Clear On Timer Compare" (or CTC) method of using the timer, in both software and hardware. But what if we want to dim the LED instead? Now we are getting into a new timer concept, that of Pulse Width Modulation, abbreviated to PWM for short.
Pulse Width Modulation allows us to interface our digital components with the analogue world. We can use PWM to adjust a motor's speed, a LED's brightness or a speaker's tone. PWM is a very important concept, which is used all throughout the world of digital electronics. It is a way of approximating an analogue source, which with a little filtering can be used as a real analogue source (albeit with a few limitations).
It's important to note that PWM does not change our AVR's limitations; the signal sent out by the timer is still a true digital signal. However, by altering the on and off time of the signal at a given frequency, we can adjust the average on time to give an approximate analogue signal. This signal is good enough for immediate use in many applications - including motor speed control and LED dimming - without any extra filtering, or with the addition of a capacitor the output can be smoothed to a real analogue wave.
Let's start with a picture representing the different types of signals (modified from Wikipedia):
The top waveform is a traditional analogue signal, varying smoothly from GND to VCC volts. The bottom waveform is a digital representation of the top, using PWM.
PWM allows us to set an overall frequency, and then vary the on and off time (called the duty cycle) of the output within each timer cycle. A longer duty cycle gives a longer on time, resulting in an analogue representation closer to the VCC voltage. A duty cycle of 100% (on for the entire cycle) gives a VCC output, while a duty cycle of 0% (off for the entire cycle) gives a GND output.
When we use the CTC timer mode, what we are actually doing is essentially PWM, but with a fixed duty cycle of 50%, and a variable frequency. For this example, we're using PWM and will conversely be using a fixed frequency and varying the duty cycle to dim our LED.
There are several different PWM modes that the AVR timers can be initialized in, each with slightly different characteristics. These are as follows:
1) Fast PWM (8, 9 or 10 bit)
2) Phase Correct PWM
3) Phase and Frequency Correct PWM
Before we can explore the different modes, we need to understand a little of the timer terminology. Frequency, as we know, is the number of cycles per second that the timer runs at - a higher frequency gives us a faster timer. In the PWM world, having a faster PWM frequency gives us finer control over the output, as we can respond faster to new PWM duty cycles. This is especially important when using PWM to produce audio from digital samples; a faster PWM frequency is required to give the full output frequency range by varying the duty cycle.
Next, we have the BOTTOM value. This is the lowest value that the timer will reach in the current mode. In all timer modes on all AVRs, this will be zero. Accompanying BOTTOM is TOP, which is the maximum value that the timer will reach before either (depending on the mode) resetting back to 0x00 or begin counting in reverse back to BOTTOM. For the normal mode, TOP is equal to the maximum value the timer can reach. For CTC mode, TOP is user settable, which gives us our control over the timer period. We are now using PWM, thus TOP is now set to give the PWM frequency.
Finally, we have our PWM COMPARE value. This sets the duty cycle - when this value is reached the waveform is inverted from its current state. Varying the COMPARE value will give us our varying duty cycle.
Now, let's get down to business and go into the three PWM modes.
Fast PWM
Fast PWM is useful for outputting PWM values quickly, at a loss of phase correctness - changes to the duty cycle will be reflected immediately, with the new signal being phase incorrect. When selected, the timer will count up from BOTTOM to TOP, with TOP being fixed at the bit-width of the fast PWM -- we are given a choice of 8, 9 or 10 bits depending on the timer bit width (8 bit timers will obviously not offer Fast PWM in more than 8-bits). The timer, when started, will count up continuously until TOP is reached, when it wraps back to BOTTOM and starts again. Inside this period when the counter reaches the COMPARE value, the output pin is set when the correct value is reached (and cleared when the timer wraps back from TOP to BOTTOM), so that varying COMPARE we get a PWM of a fixed frequency but a variable duty cycle.
This gives us the ability to change the duty cycle rapidly, in applications where the phase change does not matter. Applications such as motor control are particular about the phase, so we need to use the Phase Correct PWM mode.
Phase Correct PWM
Phase correct PWM gives phase-correct output as the duty cycle changes. The concept of phase is foregn to some, so we'll take a look graphically at how phase-incorrect (Fast PWM) and phase-correct PWM waveforms differ:
Notice the difference? In the case of Fast PWM, the high points are always aligned to the start of each period, with a smaller duty cycle resulting in a shorter ON time. Phase Correct PWM by contrast aligns the high points to the center of each period, so that a smaller duty cycle results in a shorter ON time -- but the ON times of all signals are aligned. As inducated in the Fast PWM explanation, this mode is useful in applications where the phase of the signals should not change, even if the duty cycles do. Phase Correct is slower than Fast PWM (as the name of the latter implies) due to the way Phase Correct PWM works.
When selected, the timer will count up from BOTTOM to TOP, and then start counting downwards back to BOTTOM before repeating. When upcounting, reaching COMPARE will turn on the output pin, while reaching the same value when downcounting will turn the output off. This results in the timer period being twice as long as Fast PWM mode (since the timer also has to count back to BOTTOM rather than just wrapping) but produces a phase-correct signal.
In Phase Correct PWM, the TOP value is variable and so the mode supports a variable PWM frequency. For generating simple audio tones from a speaker it is common to use the variable frequence of this mode to change the note pitch, and use the duty cycle to change the volume; a higher duty cycle results in a longer ON timer for the speaker, and so produces a louder sound.
SECTION INCOMPLETE - More updates soon. |
_________________
Last edited by abcminiuser on Aug 31, 2008 - 12:44 PM; edited 1 time in total
|
| |
|
|
|
|
|
Posted: Aug 13, 2008 - 04:30 PM |
|

Joined: Aug 06, 2007
Posts: 27
|
|
Hi all,
Firstly I have to say the Dean is a gentleman and a scholar for providing such an educational tutorial.
Secondly, if anyone is interested here is some PWM code I wrote in AVR Studio. It uses UART commands from the PC for triggering the PWM on or off. It uses the Mega32 @10MHz and sets up the PWM for motor control with the frequency set at ~20kHz @ 75% duty cycle using Timer 0
Code:
void PWM_Init()
{
TCCR0 = (0<<WGM01)|(1<<WGM00)|(1<<CS00);//setup PMW, no prescaler
OCR0 = 0xC0; //set 75% duty cycle
DDRB |= (1<<DDB3);
}
void main()
{
unsigned char PWM_on;
unsigned char PWMON[]="PWM on Pin 4";
unsigned char PWMOFF[]="PWM off Pin 4";
PWM_on = 0;
PWM_Init();
while (1)
{
switch (Received_Command)
{
case '3':
if (PWM_on == 1)
{
TCCR0 ^=(1<<COM01); //disable OC0
PWM_on = 0;
UART_putstring(PWMOFF);
USART_Transmit(LF);
Received_Command = '0';
}
else
{
TCCR0 ^=(1<<COM01);
//Clear OC0 on compare match when up-counting. Set OC0 on compare match when downcounting.
PWM_on = 1;
UART_putstring(PWMON);
USART_Transmit(LF);
Received_Command = '0';
}
}//end while(1)
}
I won't bother with the UART code as there is a tute for this.
Enjoy  |
|
|
| |
|
|
|
|
|
Posted: Aug 13, 2008 - 04:35 PM |
|


Joined: Jul 18, 2005
Posts: 34381
Location: (using avr-gcc in) Finchingfield, Essex, England
|
|
Presumably this is cut from something larger? Otherwise what's the point of a switch() with only one case: ?
(also this is presumably only going to do 20KHz if F_CPU matches yours?) |
_________________
|
| |
|
|
|
|
|
Posted: Aug 13, 2008 - 04:44 PM |
|

Joined: Aug 06, 2007
Posts: 27
|
|
Yep it's huge, I just cut and pasted something that would work.
Quote:
(also this is presumably only going to do 20KHz if F_CPU matches yours?)
yes 20KHz will work only at 10MHz F_CPU with this setup - hence all the info.
Cheers |
|
|
| |
|
|
|
|
|
Posted: Aug 19, 2008 - 02:30 AM |
|


Joined: May 03, 2005
Posts: 211
Location: Windsor, CA
|
|
Man, I can't wait for him to finish this tutorial!!!
Does anyone know of any other PWM lessons that are relevant to AVR users? |
|
|
| |
|
|
|
|
|
Posted: Aug 30, 2008 - 09:05 AM |
|

Joined: Feb 07, 2007
Posts: 2395
Location: New Delhi, India
|
|
Simply beautiful, Dean. That explanation of Phase Correct PWM is the best I've ever read. Truly, a picture is worth a thousand words. (Not your words, of course. )
Quote:
As this tutorial chain gets a lot of discussion and questions I'd think that moving all the tutorials to a new thread which is then locked would make sense.
I concur with what JohanEkdahl suggests. In addition, some navigation tabs (index page) so the reader can quickly jump to the desired chapter/section. |
|
|
| |
|
|
|
|
|
Posted: Sep 29, 2008 - 09:34 PM |
|

Joined: Aug 29, 2008
Posts: 3
Location: Long Island, NY
|
|
| I still don't see the benefit to going to a phase corrected PWM and I don't agree that you get a shorter ON time with the phase corrected PWM. The only thing is that by decreasing your frequency you will have a wider range to work with, but the minimum pulse width will remain the same and the duty cycle percentage will appear smaller because you are comparing the minimum on time to a longer period. So it seems like phase correction is be done with the fast PWM and introducing a delay to achieve the correction. Can anybody give me some insight on this? |
Last edited by jimy.pesin on Oct 21, 2008 - 02:49 PM; edited 1 time in total
|
| |
|
|
|
|
|
Posted: Sep 29, 2008 - 09:35 PM |
|

Joined: Aug 29, 2008
Posts: 3
Location: Long Island, NY
|
|
| I still don't see the benefit to going to a phase corrected PWM and I don't agree that you get a shorter ON time with the phase corrected PWM. The only thing is that by decreasing your frequency you will have a wider range to work with, but the minimum pulse width will remain the same and the duty cycle percentage will appear smaller because you are comparing the minimum on time to a longer period. So it seems like phase correction can be done with the fast PWM and introducing a delay to achieve the correction. Can anybody give me some insight on this? |
|
|
| |
|
|
|
|
|
Posted: Oct 21, 2008 - 01:41 PM |
|

Joined: Oct 21, 2008
Posts: 4
|
|
@new2atmel
I tried to understand your code, but what means DDRB |= (1<<DDB3); and ^=
Thx in advance |
|
|
| |
|
|
|
|
|
Posted: Oct 21, 2008 - 01:44 PM |
|


Joined: Jul 18, 2005
Posts: 34381
Location: (using avr-gcc in) Finchingfield, Essex, England
|
|
|
|
|
|
|
Posted: Oct 21, 2008 - 01:46 PM |
|

Joined: Oct 21, 2008
Posts: 4
|
|
Thx that makes sense I think  |
|
|
| |
|
|
|
|
|
Posted: Nov 09, 2008 - 03:45 PM |
|


Joined: May 01, 2002
Posts: 747
Location: Texas
|
|
I would love to see an organized note on specific AVRs such as the mega128 with all the registers and code organized for timer use for all the modes. It could use a base clock of say 8Mhz and show PWM frequencies possible, etc.
Al Welch  |
|
|
| |
|
|
|
|
|
Posted: Nov 10, 2008 - 01:42 PM |
|

Joined: Sep 25, 2006
Posts: 51
Location: 28638
|
|
Dean (ABCMini),
Any chance of seeing the rest of this tutorial including some code. I have to say I love your "top down, fill in the pieces" style for tutorial writing.
I am trying to pick up the pieces and write an independent PWM motor control "module" so that I can have one or N motors. I am doing a project to control the motors on my son's ride on toy, where the lack of control causes burned up motors and stripped gears.
Thanks for the tutorial, I read the entire timer tutorial last night and other than the fact that I am still not fluent on the C bit operators it was an easy read which says a lot about your style.
Thanks, |
_________________ JWColby
AVRNubee
www.ColbyConsulting.com
|
| |
|
|
|
|
|
Posted: Nov 11, 2008 - 09:39 AM |
|


Joined: Jan 23, 2004
Posts: 7014
Location: Melbourne, Victoria, Australia
|
|
|
Quote:
Any chance of seeing the rest of this tutorial including some code. I have to say I love your "top down, fill in the pieces" style for tutorial writing.
Thanks! It's been a while since I wrote any tutorials, so I'll try to pick it up again. My personal projects are starting to slow down, which is unfortunate for me but should be to the benefit to others.
I'm currently in the middle of end-of-year University exams, so I won't be able to work on this just yet. My future depends on me knowing how to do series-series feedback analysis by tomorrow.
Quote:
Thanks for the tutorial, I read the entire timer tutorial last night and other than the fact that I am still not fluent on the C bit operators it was an easy read which says a lot about your style.
Always good to know what I write is readable. I'm not always certain; it's very difficult to think from a complete new user's perspective when writing tutorials, and not sink into the depths of assumed knowledge.
For the C bit operators, read EW's "Programming 101" tutorial in the tutorials forum.
- Dean  |
_________________
|
| |
|
|
|
|
|
Posted: Nov 13, 2008 - 09:45 AM |
|

Joined: Nov 12, 2008
Posts: 2
|
|
| can i get this code in ATmega8?????????? |
|
|
| |
|
|
|
|
|
Posted: Nov 13, 2008 - 02:44 PM |
|

Joined: Sep 25, 2006
Posts: 51
Location: 28638
|
|
I am thinking you can. I am hoping to get an Atmega8 running just a single PWM channel, which I will mount on a PC board with the HBridge to control a single motor. Then link it via two wire or RS232 to a central controller that actually runs my app.
Unfortunately I am still struggling to find code that I can get working. The unfortunate part is that according to my dev notes I had PWM code controlling a motor a year and a half ago but I lost the hard drive.  |
_________________ JWColby
AVRNubee
www.ColbyConsulting.com
|
| |
|
|
|
|
|
Posted: Nov 13, 2008 - 11:12 PM |
|


Joined: Jan 23, 2004
Posts: 7014
Location: Melbourne, Victoria, Australia
|
|
Here is a simple ATMEGA16 (IIRC) example code. It's actually a DAC - digital audio in on Port D and PWM output for a speaker on the Timer 1 compare output.
Code:
#include <avr/io.h>
int main (void)
{
// Speaker as output
DDRB |= (1 << 1);
// DAC inputs as input, pullup
PORTD |= 0xFF;
// Set on match, clear on TOP
TCCR1A = ((1 << COM1A1) | (1 << COM1A0));
// Phase + Frequency Correct PWM, Fcpu speed
TCCR1B = ((1 << CS10) | (1 << WGM13));
// TOP is 0xFF for 8-bit audio
ICR1 = 0xFF;
for (;;)
OCR1A = ((int8_t)PIND + 0x7F);
}
Phase and Freq correct mode only, but should get you started.
- Dean  |
_________________
|
| |
|
|
|
|
|
Posted: Nov 17, 2008 - 05:47 PM |
|

Joined: Nov 03, 2008
Posts: 11
|
|
| thanx a lot for the tutorials on timer. was having great problems in beginning with timers and interrupt. ur tutorial was pretty easy and coool to begin with. thanx again. my best wishes for ur exams |
|
|
| |
|
|
|
|
|
Posted: Nov 17, 2008 - 05:49 PM |
|

Joined: Nov 03, 2008
Posts: 11
|
|
| thanx a lot for the tutorials on timer. was having great problems in beginning with timers and interrupt. ur tutorial was pretty easy and coool to begin with. thanx again. my best wishes for ur exams |
|
|
| |
|
|
|
|
|
Posted: Nov 20, 2008 - 06:02 PM |
|

Joined: Sep 24, 2008
Posts: 12
|
|
Hi freaks,
First of all thank you Dean for making life so easy for newbies like me. Your tutorials are so simple and easy to follow. Hats off to you.
Coming to the problem, I am using atmega8 to design a robot. I have to use PWM to control two motors that control the two wheels of the robot. This is what I have done so far:
- Read your timer tutorial and PWM tutorial
- Read the missing part of PWM from Data Sheet
- Wrote a code to initalize PWM and test the motors
The code is as follows:
Code:
#include<avr/io.h>
#include<avr/interrupt.h>
#include<util/delay.h>
#define Delay( x ) _delay_loop_2( x )
#define F_CPU 1000000UL
pwm_init()
{
TCCR1A |= (1<< COM1A1) | (1<< COM1B1) | (1<< COM1A0) | (1<< COM1B0) | (1<< WGM10)| (1<< WGM11) ;// 10 bit PWM phase correct
TCCR1B |= (1<< CS10); // no prescaling
//TCCR1B |= (1<< CS10) | (1<< CS12) ;
TCNT1 = 0x0000;
}
void pwm_set( int left_speed, int right_speed )
{
OCR1A = left_speed;
OCR1B = right_speed;
}
int main ()
{
unsigned int i;
DDRD = 0xff;
PORTD = 0x00;
DDRB = 0xff;
// PD0 and PD1 - right motor
// PD7 and PD6 - left motor
// 1 0 forward
// 1 1 brake (both outputs low)
// 0 1 reverse
// 0 0 coast (both outputs off =high impedance?)
PORTB |= (1 << PB1) | (1 << PB2);
PORTD |= (1 << PD7) | (0 << PD6) | (1 << PD0) | (0 << PD1);
pwm_init();
while(1)
{
for(i=1;i<256;i++)
{
pwm_set(i,i);
Delay(5000); //20000 cycles
}
for(i=255;i>0;i--)
{
pwm_set(i,i);
Delay(5000);
}
}
}
What I don't understand is:
- When does OC1A and OC1B gets set or clear? In this PWM mode the TOP value is 0x03FF. And I am putting values ranfing from 1 to 255 and 255 to 1 in OCR1A and OCR1B. According to me, this code should change the speed of the motors, first increase and then decrease, or vice versa.
- Also how do I set duty cycle? Why would I need it? Suppose I want to use the duty cycle of 50%, that means i put half the value of 0x03FF into OCR1A...is that right?
- I want my robot to turn 90 degrees. I think this should be done by stopping one motor and rotating the other one. But for how long should I rotate the other one so that it turns 90 degrees. As in how do I fix the time for which a particular motor runs?
-How does prescaling affect PWM? I saw that changing prescaler values made the code not run any more.
Please consider my questions. I might look ignorant to some of you. Also if this was the wrong place to post please let me know and I will post in AVR Forums. I just had to thank Dean, so I posted here.
Regards.
Sid |
|
|
| |
|
|
|
|
|