Pages Menu
TwitterRssFacebook
Categories Menu

Posted by on Jan 7, 2012 in Atmel AVR, Microcontrollers | 163 comments

AVR Timers – PWM Mode – Part II

AVR Timers – PWM Mode – Part II

AVR SeriesThis article is in continuation with the previous PWM post. Learn how to program the timers to operate in PWM mode! So let’s begin!

Hello folks! Long time no see! :)

In my previous post, we have discussed the basic concepts of PWM. Let’s summarize it first:

  • PWM stands for Pulse Width Modulation.
  • It can be generated by comparing predetermined waveform with a reference voltage level or by making simple analog circuits.
  • Duty Cycle of a PWM waveform is given by the following relation.
Duty Cycle
  • There are three modes of PWM operation – Fast PWM, Phase Correct PWM and Frequency and Phase Correct PWM
  • How to choose timer, operation mode and compare output mode for generating the desired PWM.
So now, without much hassle, let’s see how to implement it using the AVR microcontrollers. Before we proceed, I suggest you to go through my previous posts on Timers and PWM.

Problem Statement

Let us take a problem statement. We need to generate a 50 Hz PWM signal having 45% duty cycle.

Analysis

Given that

Frequency = 50 Hz

In other words, the time period, T

T = T(on) + T(off) = 1/50 = 0.02 s = 20 ms

Also, given that

Duty Cycle = 45%

Thus, solving according to equation given above, we get

T(on)  = 9 ms
T(off) = 11 ms

Now, this can be achieved in two ways:

  1. Use Timer in CTC Mode
  2. Use Timer in PWM Mode

Methodology – CTC Mode

Okay, so I won’t be writing any code here (just the pseudo code). I assume that after reading my previous posts, you are smart enough to write one yourself! We will discuss only the concepts.

Firstly, choose a suitable timer. For this application, we can choose any of the three timers available in ATMEGA32. Choose a suitable prescaler. Then set up the timer and proceed as usual. The catch lies here is that you need to update the compare value of OCRx register everytime. One such way is discussed in the pseudo code given below.

This is analogous to the traditional LED flasher, except the fact that the on and off times are different.

Pseudo Code

#include <avr/io.h>
#include <avr/interrupt.h>
 
uint8_t count = 0;               // global counter
 
// initialize timer, interrupt and variable
void timerX_init()
{
    // set up timerX with suitable prescaler and CTC mode
    // initialize counter
    // initialize compare value
    // enable compare interrupt
    // enable global interrupts
}
 
// process the ISR that is fired
ISR (TIMERx_COMPA_vect)
{
    // do whatever you want to do here
    // say, increment the global counter
    count++;
 
    // check for the global counter
    // if count == odd, delay required = 11 ms
    // if count == even, delay required = 9 ms
    // thus, the value of the OCRx should be constantly updated
    if (count % 2 == 0)
        OCRx = 9999;      // calculate and substitute appropriate value
    else
        OCRx = 10999;     // calculate and substitute appropriate value
}
 
int main(void)
{
    // initialize the output pin, say PC0
    DDRC |= (1 << 0);
 
    // initialize timerX
    timerX_init();
 
    // loop forever
    while(1)
    {
        // do nothing
 
    }
}

Now this is one method. And it’s very inefficient. You can increase its efficiency by writing a better C code (syntax-wise), however the concept remains the same. If you have any other method/concept, you are most welcome to share it here! :)

UPDATE: One of the readers of maxEmbedded, “coolpales” has written this code, and it worked for him.

Please note that this code not tested yet! So, if any of you is trying it out, do post your results here, I would be happy to see them! :)

Methodology – PWM Mode

Okay, so now lets learn about the PWM mode. The PWM Mode in AVR is hardware controlled. This means that everything, by everything I mean “everything”, is done by the AVR CPU. All you need to do is to initialize and start the timer, and set the duty cycle! Cool, eh?! Let’s learn how!

Here, I have used Timer0 of ATMEGA32 for demonstration. You can choose any other other timer or AVR microcontroller as well. Now let’s have a look at the registers.

TCCR0 – Timer/Counter0 Control Register

We have come across this register in my Timer0 tutorial. Here, we will learn how to set appropriate bits to run the timer in PWM mode.

TCCR0 Register

TCCR0 Register

We will discuss only those bits which are of interest to us now.

  • Bit 6,3 – WGM01:0 – Waveform Generation Mode – These bits can be set to either “00” or “01” depending upon the type of PWM you want to generate. Here’s the look up table.

    Waveform Generation Mode Bit Description

    Waveform Generation Mode Bit Description

  • Bit 5,4 – COM01:0 – Compare Match Output Mode – These bits are set in order to control the behavior of Output Compare pin (OC0, pin 4 in ATMEGA32) in accordance with the WGM01:0 bits. The following look up table determine the operations of OC0 pin for Fast PWM mode.
    Compare Output Mode, Fast PWM Mode

    Compare Output Mode, Fast PWM Mode

    Now lets have a look at the Fast PWM waveforms. Detailed explanation can be found in my previous tutorial.

    Fast PWM

    Fast PWM

    Now let me remind you that the AVR PWM is fully hardware controlled, which means that even the timer compare operation is done by the AVR CPU. All we need to do is to tell the CPU what to do once a match occurs. The COM01:0 pins come into play here. We see that by setting it to “10” or “11”, the output pin OC0 is either set or cleared (in other words, it determines whether the PWM is in inverted mode, or in non-inverted mode).

    Similarly for Phase Correct PWM, the look up table and the waveforms go like this.

    Compare Output Mode, Phase Correct PWM Mode

    Compare Output Mode, Phase Correct PWM Mode

    Phase Correct PWM

    Phase Correct PWM

    Even here, setting COM01:0 to “10” or “11” determines the behavior of OC0 pin. As shown in the waveforms, there are two instances – one during up-counting, and other during down-counting. The behavior is clearly described in the look up table.

    Please note that OC0 is an output pin. Thus, the effects of WGM and COM won’t  come into play unless the DDRx register is set properly. Refer this tutorial for more info.

  • Bit 2:0 – CS02:0 – Clock Select Bits – These bits are already discussed in Timer0 tutorial.

OCR0 – Output Compare Register

We have come across even this register in my Timer0 tutorial. We use this register to store the compare value. But when we use Timer0 in PWM mode, the value stored in it acts as the duty cycle (obviously!). In the problem statement, its given that the duty cycle is 45%, which means

OCR0 = 45% of 255 = 114.75 = 115

And that’s it! Now we are ready to write a code for it! :)

Edit: Note

The following code discusses how to create a PWM signal of a desired duty cycle. If you wish to change its frequency, you need to alter the TOP value, which can be done using the ICRx register (which is not supported by 8-bit timers). For 16-bit Timer1, it can be varied using ICR1A. I will discuss about this soon when we discuss about servo control.

Code

So here goes the code. To learn about I/O port operations in AVR, view this. To know about bit manipulations, view this. To learn how to use AVR Studio 5, view this. To learn how this code is structured, view the previous TIMER0 post.

#include <avr/io.h>
#include <util/delay.h>
void pwm_init()
{
    // initialize TCCR0 as per requirement, say as follows
    TCCR0 |= (1<<WGM00)|(1<<COM01)|(1<<WGM01)|(1<<CS00);
 
    // make sure to make OC0 pin (pin PB3 for atmega32) as output pin
    DDRB |= (1<<PB3);
}
 
void main()
{
    uint8_t duty;
    duty = 115;       // duty cycle = 45% of 255 = 114.75 = 115
 
    // initialize timer in PWM mode
    pwm_init();
 
    // run forever
    while(1)
    {
        OCR0 = duty;
    }
}

Problem Statement

So now, let’s take another problem statement. This one is going to be a more of a practical stuff unlike the previous one!

Let’s take the traditional LED flasher where we need to blink an LED at a particular frequency. But hey, wait, didn’t we discuss it long back in this post (scroll down towards the end)? Hmm, so let’s modify it so as to incorporate PWM. Unlike the traditional LED flasher (where LEDs are either ON or OFF), lets make it glow at the maximum brightness, and then slowly decrease its brightness till it reaches zero, and then again increase its brightness slowly till it becomes maximum.

Analysis and Code

So how do we do it? Yes, you guessed it right! Decrease the duty cycle slowly from 255 to zero, and then increase it from zero to 255. Depending upon the duty cycle, the voltage applied to the LED varies, and thus the brightness. The following formula gives the relation between voltage and duty cycle.

V_out equation

So here goes the code. I won’t explain it, you can decode it yourself. To learn about I/O port operations in AVR, view this. To know about bit manipulations, view this. To learn how to use AVR Studio 5, view this. To learn how this code is structured, view the previous TIMER0 post.

// program to change brightness of an LED
// demonstration of PWM
 
#include <avr/io.h>
#include <util/delay.h>
 
// initialize PWM
void pwm_init()
{
    // initialize timer0 in PWM mode
    TCCR0 |= (1<<WGM00)|(1<<COM01)|(1<<WGM01)|(1<<CS00);
 
    // make sure to make OC0 pin (pin PB3 for atmega32) as output pin
    DDRB |= (1<<PB3);
}
 
void main()
{
    uint8_t brightness;
 
    // initialize timer0 in PWM mode
    pwm_init();
 
    // run forever
    while(1)
    {
        // increasing brightness
        for (brightness = 0; brightness < 255; ++brightness)
        {
            // set the brightness as duty cycle
            OCR0 = brightness;
 
            // delay so as to make the user "see" the change in brightness
            _delay_ms(10);
        }
 
        // decreasing brightness
        for (brightness = 255; brightness > 0; --brightness)
        {
            // set the brightness as duty cycle
            OCR0 = brightness;
 
            // delay so as to make the user "see" the change in brightness
            _delay_ms(10);
        }
 
    // repeat this forever
    }
}

So here ends my another much awaited and long tutorial! Next up.. Serial Communication! See you around!! :)

And yeah, if you have any suggestions, doubts, constructive criticisms, etc, you are most welcome to drop a note  below! Subscribe to my blog or grab the RSS Feeds to stay updated!

163 Comments

  1. Can you please help me in accelerating/de-accelerating a bot using pwm technique??
    I dont understand how do i give give pwm output to motos??

    • Its simple.. just connect the pwm pins of the microcontroller to the input of the motor driver. Connect the motors to the output pins of the motor driver. Now, upon increasing the duty cycle of pwm, the speed of the motor increases (acceleration) and vice-versa. Its just like the second problem statement given above..

      • can this pwm technique be implemented using timer1 and in that case where shall be the output?

        • Yes, why not! I have followed the datasheet method of coding. You will be clear with it if you read my previous Timer posts. All you need to do is to initialize the TCCR1A and TCCR1B registers accordingly.

          in that case where shall be the output?

          The benefit of using timer1 is that it provides you with greater resolution. It depends upon the application where it is utilized. For example, in case of servo control, you cannot use 8bit pwm, you must go for a 16bit pwm (hence use timer1 for that). I will write a tutorial on servo control shortly.

          • I have programmed AVR Atmega16 ( 16 MHZ) , for PWM generation . I configured it in phase correct PWM mode for A chanel of Timer 1. I got PWM frequency 15KHz, but I am not understanding How ? Pl. explain

          • Hi Gaikwad
            The frequency of the PWM is determined by the value of the OCR0. Kindly read between the lines in the above post, and you will get to know how.

  2. hey iam using atmega128 controller ,so will the motors work on power supply given to the controller, or i should connect external power supply

    • the atmega128 works on a voltage level of 3.3-5V, where as a normal dc motor (which I assume you will be using) has a rated voltage of 12V. Thus, the controller would be able to generate only the control signals. You need to connect an external power source to the motor driver.

  3. hey..i just want to know how the ‘_delay_ms()’ function works

    • Have a look at mt timer tutorial. Its the same way the function works.

  4. oh yeah!! your tutorial are realy awsome! it have been so helpful.
    I just have been looking for information on spi, so I already am waiting for your next post!!
    thanks a lot!!

  5. hey , any suggestions i’m working on IR link between two mcus

    • Hmm.. so, how do you plan to install the IR emitter and detector? And how do you plan to send in the digital data from the mcu to the analog IR devices?

      • serial communication, tsop for detection, nd 38khz carrier, protocol is the problem i;m facing, and its quite simple jst like amplitude modulation

  6. It’s been a long, long time since last update.. Any chance for next tutorial anytime soon…?

    BTW: these tutorials make my life so much easier… I wish you covered more and more topics… Great job!

    • Hey Adam! Howdy!

      I know its long since I have updated any post. I have been very busy these past few months. However, this doesn’t mean that I am done with it! I am still working on them. You can expect more posts during the months of May-July, because that’s the only time of the year when I am totally free!
      In any case, I have been collecting information and making demonstrations for the upcoming posts – serial, spi, eeprom. You can also expect a few starter tutorials from me related to image processing using opencv and how to interface it with avr. Along with these, the website will also undergo a total makeover! But you gotta wait till mid-June for that.. ;)

      Anyways, thanks a lot for your concern! :)

  7. wht is the difference between timer and delay function both have same outputs but delay is a one step fn and timer much bigger…………!!

    • Timers are used for many other tasks other than delays. Timers can be used in counter mode, CTC mode, PWM mode, etc. You get full control of the timer and you can use it very effectively! You also use it to set the baud rate for serial communication.

      On the other hand, the delay function can be used only to provide some delay, or act as wait. If you want to use the timer as a delay or wait function, you can go for the single line delay function.

  8. hey in the above tut what is output compare value we need to initialise …since we hav two output compare values (1 for even count and the other for odd) pls help me out….

    • Initially, count = 0, which is even. So you can initialize it with the even compare value.

  9. is possible to use all PWM channels simultaneously using the TIMER 16bts?. i need to control 5 SERVOS

    • Nope! This is not possible. In your case, you have to manually generate a PWM signal on any of the digital I/O pins.

  10. hai ,
    i have come across a situation that i need 26us pulse in which 6us should be oN and 20us should be OFF .i am using atmega16A (my requirements it should run at 1M itself so without changing the master clock using pwm how to create the pulse dude try it and reply me as soom as possible as i am developing code for infrared technology . this is my problem help me out dude.

    • Sorry Bro! Unfortunately I can’t! I am here not to solve anybody’s problem, but to clarify their concepts. Whatever you asked for is clearly mentioned in this post as well as in some of my other timer posts. Kindly read between the lines thoroughly and I’m sure you will get it how to do it! All the best! :)

  11. Hey Mayank,
    Great tutorial! I am wondering if it is possible to read pwm with an ATMega? For instance to connect it to the receiver of a hobby remote control system then have the MCU do something based on the PWM signal it is getting from the receiver?

    • Hey Devin!
      I am so sorry for such a late reply.

      For such purposes, we mostly use serial communication (USART), since it is more reliable.
      However, for reading external signals, it may not be possible to read PWMs as whole, since the MCU is a digital device and understands only 0s and 1s. You can use the timers of the MCU in counter mode and count the number of pulses. You can also use interrupts to count external triggers.

  12. One more question. What is the best way to temporarily shut of the PWM output? Example; using PWM for a voltage boost convertor, I only want PWM output on when the voltage drops below a certain level. I am current using the directional register (DDRx). I am just wondering if there is a better way?

    • Hi Devin
      Sorry for such a late reply.
      Set the values of COM01:0 as 00. This disables the OC0 and reverts back to the normal port operation.

  13. hey how do i set bits for PWM MODE to get output through OCR1B register ………
    is OC1B the output pin for OCR1B register……….reply ASAP
    thx

    • Hi Sruteesh,
      Yes, OC1B is the output pin of OCR1B register. You need to read through the datasheet and get the information. If you are using ATmega16, then this information is available in page-113 of its datasheet. Select the proper WGM13:0 bits.

  14. how do you vary the speed/duty cycle if code is already programmed to a microchip(Atmega16) while testing………do i use if statements within the code to account for different duty cycles or is there another way of doing it?

    • Hi Batowns
      Apologies for such a late response.
      Well, you can connect a small potentiometer to the ADC of the microcontroller. Program the microcontroller to vary the duty cycle depending upon the readings from the ADC. To learn how to program the ADC of the AVR, view this.

  15. Hello Mayank, maybe you can help me with this, I have the Atmega164p and I would like to use timer 1 and timer 2 for pwm (servo), 4 on pins PD4,5, 6 and 7

    with timer 1 (16bits) it works OK but I have a hard time with the timer 2, I have 12MHz


    Code removed by admin and can be found here.

    I cant find the error, do you have any idea?

    thank you

    • Hi Cesar,

      Well, I haven’t gone through your code yet, but there is something I need to tell you (based upon my experiences with servos).
      I assume that timer1 is a 16bit timer, and timer2 is an 8bit timer.

      Conventional RC Servos are supposed to run at 50Hz (i.e. time period is 20ms). For a rotation from 0deg to 180deg, they need a pulse width of 0.9ms to 2.1ms (I have assumed an approx range). Now lets analyze:

      0 DEGREES
      Pulse Width = 0.9ms
      Time Period = 20ms
      Duty Cycle = 0.9/20 = 0.045
      Timer2 Value (8 bit) = 0.045*256 = 11.475 =~ 12
      Timer1 Value (16 bit) = 0.045*65536 = 2949.12 =~ 2950

      180 DEGREES
      Pulse Width = 2.1ms
      Time Period = 20ms
      Duty Cycle = 2.1/20 = 0.105
      Timer2 Value (8 bit) = 0.105*256 = 26.775 =~ 26
      Timer1 Value (16 bit) = 0.105*65536 = 6881.28 =~ 6881

      NO. OF STEPS IN BETWEEN 0 and 180 DEGREES
      Timer2 (8 bit) = 26-12 = 14
      Timer1 (16 bit) = 6881-2950 = 3931

      INFERENCE
      While you use an 8 bit timer, you can have only 14 different angle positions of servo in between 0deg and 180deg, whereas if you use 16 bit timer, you have 3931 different angle positions. Thus, we usually do not use 8 bit PWM to drive servos due to low resolution.

      I don’t know whether it was helpful or not, but if you have any more queries, do post them here, and I will try to get back to you asap.
      Thanks! :)

      • Hello Mayank, thank you.

        Yes, I am aware of the low resolution, actually in project the PWM outputs are for general purpose it just seemed more practical to connect a servo, might have been good idea to use software-based PWM.

        Regarding to the code:

        OCR2A=233;
        OCR2B=200;

        When configuring timer 2, and then:

        OCR2A=get_pulset2(degrees);
        OCR2B=get_pulset2(degrees);

        When setting the pulse, OCR2A and OCR2B are changing every time I set the pulse, so the first values (when configuring) are different, it is a good way to configure timer 2? because it works but I do not know why due to the new values of OCR2x every time.

        Thank you

        • Hi Cesar,

          I am really sorry for such a late response. I was really really busy till now, and didn’t even get a chance to log in.

          Anyways, have you tried implementing this using the AVR Simulator? You can debug your code step by step to understand what’s happening.

  16. hey, a great tutorial. But i wanted to know how can i use timers to generate a PWM signal whose duty cycle and frequency(say, 2Hz to 2.5KHz) can be varied. I wanted to understand the concept behind using the functions of this microcontroller.

    Thank you.

    • Hi Ashish
      Please read all of my timer tutorials. The same concept is used to generate PWM signal.

      • Hello Mayank,
        Thanks for your Timer tutorials! They are very informative and well illustrated. I am clear with the concept of using PWM modes to generate variable duty cycles. But i am still confused with the frequency varying part. I want to vary the frequency of the PWM signal from 2Hz to 2.5KHz in steps of 1Hz. How do i do that?

        Regards,
        Ashish

  17. hii ….
    i have genrated sq.wave by pwm and its giving 1pulse for evry 1ms and my problm is i want to stop tht sq wave aft some pulse ya i want to just disable the pwm. pls help .

    • There are bits to enable/disable the timer. You can use them to disable the PWM as well. You can set COM01:0 to 00 to disable the PWM.

  18. Ya thnks mayank. thankuu :-)

  19. hi mayak,
    Its very good tut indeed and helped me a lot , I have one question suppose i want to provide pwm with duty cycle 25% at certain frequency say 400hz , how to do that ?

    • Hi Arun,
      Well, I must confess that I forgot to cover this little detail in my post above! Here it goes:

      If you wish to change the frequency of the PWM, you must vary the TOP value. This TOP value can be varied using the ICRx register. This is usually not present for 8 bit timers. If you go for 16 bit Timer1, there you have the opportunity to vary the frequency using ICR1A register, and the duty cycle can be varied using the OCR1A register. I hope this is what you were asking for.

  20. Hi Thanks for the tutorial,

    I too am confused about varying the frequency of the PWM.
    We need to set the PWM Frequency as well as the duty cycle.
    It seems that the frequency is fixed to a few limited values.
    i.e we need to set the TOP value to any value between 0x01 and 0xFF. Also, do you like my Gravatar ?, why would anyone change it when the default is so fetching and attractive.

    • Hi Richard,
      Please check my reply to Arun above, which deals with changing the frequency of PWM.
      And yeah, you Gravatar looks super cool! Earlier they were just a simple image with the gravatar logo, then I changed it to this. Looks cool and funky! :D

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: