Wednesday 12 June 2013

PWM Configuration for ATMEGA16

     In previous articles we have talked about digital reading, digital writing, now let's learn how to use the timers to generate PWM.
  • What is PWM? For simplicity, let's say it's a square signal with a given frequency and a given duty cycle. For more details refer to this pwm explanation.


  • To generate PWM , we will use the timer
What  is a timer? Well, I'll keep it simple. Let's say that a timer is a unit that counts up or down when a clock edge appears. So if the clock frequency is 10Hz, the counter does something ten times a second.
Atmega16 has three timers called Timer0(8 bit), Timer1(16 bit), Timer2(8 bit).
If a timer is an 8bit timer, it can count up to 2^8-1 = 255. If it is a 16 bit timer, it can count up to 2^16-1 = 65535.

  • For this tutorial we will study Timer0. Timer1 and 2 are configured in the same way. 

Timers have some registers that can be used.
The first one is the Timer/Counter Configuration Register (TCCR0). This register is used to configure the timer, what we want the timer to do, what function to be executed. We said that the timer counts up or down. The number that is increased or decreased will be found in TCNT0 register(8 bit register), so it can be between 0 and 255.
Now, it counts up or down, but the timer can do something like put a pin on "1" or "0" if the timer reaches a limit. This limit is set in the Output Compare Register(OCR0).
TCNT0 is compared with OCR0. On compare match it can generate an interrupt or it can generate a signal to the outside world.

  • Let's have a look in the datasheet at page 83, at Waveform Generation Mode Bit Description Table 38. We can see that for FastPWM we have to set WGM01 and WGM00 to "1".  Now let's have a look at Table 40 Compare Output Mode, Fast PWM Mode and choose from there the third option(non-inverting mode). So, we have to set COM01 to "1". Until now we have configured what function the timer has to execute(non inverting fast PWM). 
  • FastPWM non-inverting works something like this(simple explanation).
TCNT0 is set to 0. You set the duty cycle in OCR Value.
The timer starts counting up, TCNT0 increases. Until TCNT0 doesn't reach the value in OCR0, the output ping OC0 is set to "1". When TCNT0 is greater than OCR0 , OC0 is set to "0".  TCNT0 continues to count up until it reaches the maximum limit of 255, after that it goes back to 0 (cleared) and OC0 is set back to "1".

So that why the frequency between the rising edges of OC0 is (fTimer / 2^8)

Now, we have to set the frequency.

The timer executes when a clock edge appears. The clock is the one that is used for the uC. So , for example is you have a 16MHz clock, this signal will be used as a clock for the timer. However, if you want, you can use this value divided by 8, 64, 256, or 1024 or you can even use an external clock source. The divider is called prescaler.  You can select the clock that is used for the timer by setting CS00 CS01 CS02 bits.

The frequency of your PWM signal will be equal to (CLK freq/(N*256)) where N is the clock divider. 256 is 2^8.

CLKfreq/N is fTimer from the explanation of FastPWM.

  • Having set the mode of operation and the frequency, all that remains is to set the duty cycle. The duty cycle can be set by writing the OCR0 register. The duty cycle will be OCR0value/255. 
  • Also, OC0 is set as OUTPUT, so let us set the correct value in the Data Direction Register .  OC0 is PB3




#include <avr/io.h>
#include <avr/interrupt.h>
#define F_CPU 16000000
#include <util/delay.h>

int main()
{
    TCCR0 |= (1 << WGM00) | (1 << COM01) | (1 << WGM01) | (1 << CS00); /*Configure the timer for FastPWM at frequency of  f =~ 62.5kHz with no prescaling */
    DDRB|=( 1 << PB3 );//set OC0 as output.
    OCR0 = 200;//set the duty cycle(200/255)
   
    while(1); 
    return 0;
}



Usage:

- Modifying the speed of a motor
- Controlling the luminosity of an LED or other element
- Controlling the average voltage
- Controlling servo-motors
-Other things :)