Projects for geeks


11 Aug 2009

From the beginning AVR has the ability to generate PWM signals using internal hardware. Number of PWM channels (independent PWM signals) vary depending on chip. Typically it is 4 or 6 in modern chips, but can be more or less.

Here is a list of popular chips with number of PWM channels:

Microcontroller PWM channels
ATmega8 3
ATmega48 6
ATmega88 6
ATmega168 6
ATmega328 6
ATmega16 4
ATmega32 4
ATmega128 8
ATtiny2313 4

PWM functions are controlled by the timers. PWM is just one of the timer modes. As you can guess, number of PWM channels depend on the number of timers in AVR chip. All the above microcontrollers have 3 timers. 16 bit timer1 controls 2 PWM channels, timer0 and timer2 one PWM channel. One exception is for ATmega8, its timer0 has no ability to generate PWM (it is also true for some other AVR chips). If microcontroller has more PWM channels, that means it has more timers (e.g. ATmega128).

PWM signal is generated on fixed pins, you can not use pin of your choice. The name of the pin is OCx (from Output Compare), x is the number of associated timer. As timer1 is responsible for 2 PWM channels, letters A and B were added. Below is the PDIP version of ATmega32 with PWM pins marked red.

Every microcontroller, not only AVR series can use what is called software PWM. It is a method of PWM signal generation by the software routine. It means control program changes state of the output pin from 0 to 1 in time. This method is not as robust as hardware PWM, special care must be taken when writing a program. It is strongly recommended to not use software PWM if possible. One advantage is that you can create as many PWM signals as you have free output pins (and you can use any pin for that purpose).

PWM setup

To start using PWM you must:

1. Setup OCx pin as output
2. Select PWM mode of timer
3. Set appropriate prescaler divider
4. Set Compare Output Mode to Clear or Set on compare match
5. Write duty cycle value to OCRx

Setting a pin as output is not a problem. Appropriate bit must be set for DDRx register. Here’s an example in C (assuming only one output which is PD4=OC1B):

DDRD |= _BV(DDD4);

Timer configuration is much more complicated. This is because timer has a lot of features and it is hard to figure out what should be set to get what you need.

To configure timer, appropriate bits must be set in Timer/Counter Control Register, is short TCCRx, where x is the timer number. Timer1 (16 bit) has two control registers TCCR1A and TCCR1B which are used to control behavior of the timer.

Configuration of the timer can be divided in tree steps:

  1. Select timer mode
  2. Select prescaler
  3. Set duty cycle to start generating wave

Mode of the timer is selected by setting WMGxx bits in TCCRx register. Bits for prescaler are CSxx. Note that timer is not running if prescaler is not configured. To run timer and start generating PWM wave you must set at least CS10 bit (Prescaler=1). To stop PWM, clear all CSxx bits.

The other very important register is OCRx, it controls duty cycle. For 16 bit timer1 it is 16 bit, for 8 bit timers it is 8 bit.

For modes where frequency is regulated, ICRx register controls frequency of the signal.

In Timer/Counter Control Register bits CSxx (Clock Select) controls frequency, bits WMGxx (Waveform Generation Mode) controls PWM mode.

Worth to mention is COMx register — it controls whether PWM wave is inverted or not. Typically only COMx1 (COMx2=0) is set (non-inverting mode). For most applications it really does not matter in which mode PWM wave is generated. Look at the picture below to see the difference.

Note that WMGxx and CSx bits are located in 2 registers for timer1, TCC1A has different bits than TCC1B.

Typical operations

Here you have the table with typical operations for 3 timers of ATmega16/32 microcontrollers. Note that for timer1 only 3 modes (8 bit) of 15 are shown and not all features are described. Operations for timer1A and timer1B are the same except the first and the last one.

Operation timer0 timer1A timer1B timer2
OCx pin as output DDRB |= _BV(DDB3); DDRD |= _BV(DDD5); DDRD |= _BV(DDD4); DDRD |= _BV(DDD7);
Phase correct PWM mode TCCR0 |= _BV(WGM00); TCCR1A |= _BV(WGM10); TCCR2 |= _BV(WGM20);
CTC PWM mode TCCR0 |= _BV(WGM01); TCCR1A |= _BV(WGM12); TCCR2 |= _BV(WGM21);
Fast PWM mode TCCR0 |= _BV(WGM00) | _BV(WGM01); TCCR1A |= _BV(WGM10) | _BV(WGM12); TCCR2 = _BV(WGM20) | _BV(WGM21);
Prescaler divider 1 TCCR0 |= _BV(CS00); TCCR1B |= _BV(CS10) TCCR2 |= _BV(CS20);
Prescaler divider 8 TCCR0 |= _BV(CS01); TCCR1B |= _BV(CS11) TCCR2 |= _BV(CS21);
Prescaler divider 1024 TCCR0 |= _BV(CS02) | _BV(CS00); TCCR1B |= _BV(CS12) | _BV(CS10); TCCR2 |= _BV(CS22)|_BV(CS21)|_BV(CS20);
Clear OCx on compare match TCCR0 |= _BV(COM01); TCCR1A |= _BV(COM1B1); TCCR2 |= _BV(COM21);
Set duty cycle OC0=100; OC1A=100; OC1B=100; OC2=100;