Skip to content

January 4, 2006

6

ATmega32 PWM

This note briefly describes how to set up PWM on an ATmega32. In this instance, Timer/Counter 1 (16-bits wide) is used to set up a Fast PWM-mode PWM wave to drive a typical R/C servo. The PWM period is 20 ms (50 Hz), and the pulse width varies from 0.9 ms to 2.1 ms.

With a 16 MHz clock, the full 16-bit count wraps around in about 780 µs. Since we need a PWM period of 20 ms, this is clearly too fast. Prescaling Timer/Counter 1 by dividing the sytem clock by 64 gives us a full-count period of 50 ms. By setting the upper limit of the count using ICR1 to 12500, this results in the desired 20 ms period.

Update: I’ve gotten some math wrong in the preceding paragraph, and I’m not sure what I was thinking. It seems that a full 16-bit count would take 4.096 ms at 16 MHz. Also, the comments in the code regarding prescaling values don’t all agree. Sorry about that. I hope you can still find this useful.

To get the appropriate servo pulse width between 0.9 ms and 2.1 ms, the OCR1A register is loaded with values between 225 and 525.

Circuit Set-up

Please excuse the lack of a schematic; I simply don’t have time to create one right now.

The ATmega32 is set up to run with a 16-MHz crystal oscillator. On the PDIP 40 package, the following pins are connected:

Pin Description Connections & Use
6 PB5/MOSI ISSP
7 PB6/MISO ISSP
8 PB6/SCK ISSP
9 RESET Through 10 kΩ resistor to Vcc, N.O. switch to GND, ISSP
10 Vcc Power, 0.1 µF ceramic cap to GND, ISSP
11 GND Ground, ISSP
12 XTAL1 To 16 MHz crytsal, 10 pF ceramic cap to GND
13 XTAL2 To 16 MHz crytsal, 10 pF ceramic cap to GND
19 PD5/OC1A To Servo signal line
30 AVcc Power, 0.1 µF ceramic cap to GND, ISSP
31 GND Ground, ISSP
40 PA0/ADC0 To LED, LED to 470 Ω resistor, resitor to GND

Fuse Set-up

The fuses are set for a 16 MHz external crystal oscillator.

Name Description Value
Fuse Lo Fuse Low Byte 11101111b, 0xEF
Fuse Hi Fuse High Byte 10001001b, 0×89

Register Set-up

The following registers are set, in this order. Although the order isn’t strictly necessary, some subset of it might be (be specific).

Name Description Value
DDRA Port A Direction 00000001b, 0×01 (PA0 output, rest input)
DDRA Port B Direction 00000000b, 0×00 (all input)
DDRD Port D Direction 00110000b, 0×30 (PD4, PD5 output, rest input)
TCCR1A Timer/Counter 1 Configuration A 10100010b, 0xA2 (OC1A & OC1B set at BOTTOM, clear on compare match, WGM=14 [fast PWM, ICR1 is TOP])
TCCR1B Timer/Counter 1 Configuration B 00011011b, 0×1B (prescale ClkI/O/64)
ICR1 In Fast PWM mode, becomes TOP 12500d (20 ms PWM period with ClkI/O/64 prescale)

Code

#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
#define kPinInitCompleteLED         PA0
#define kDelay                      10000
#define kReverseDelay               300000
#define kLowerLimit                 300                 //  Min 225 == 0.9 ms
#define kUpperLimit                 450                 //  Max 525 == 2.1 ms
int
main()
{
    volatile long       d;
    short               dir = 1;
    
    //  Set LED output pins…
    
    DDRA = _BV(kPinInitCompleteLED);
    DDRB = 0;                                           //  Port B inputs
    
    //  Set up OCR pins (PD4, PD5) as outputs (00110000)…
    
    DDRD = _BV(PD4) | _BV(PD5);
    
    //  Set up Timer 1. Timer 1 should reset when
    //  it reaches TOP = ICR1 (WGM13:0 = 1110b). On
    //  compare match clear output, at TOP set (COM1A1:0 = 10b).
    
    TCCR1A = _BV(COM1A1) | !_BV(COM1A0)                 //  Both PWM outputs set at TOP,
                | _BV(COM1B1) | !_BV(COM1B0)            //    clear on compare match
                | !_BV(FOC1A) | !_BV(FOC1B)             //  PWM mode, can't force output
                | _BV(WGM11) | !_BV(WGM10);             //  Fast PWM, TOP = ICR1
    
    TCCR1B = !_BV(ICNC1) | !_BV(ICES1)                  //  Disable input capture noise canceler,
                                                        //    edge select to negative.
                | _BV(WGM13) | _BV(WGM12)               //  Fast PWM, TOP = ICR1
                | !_BV(CS12) | _BV(CS11) | _BV(CS10);   //  clk(i/o) / 1024
    
    //  PWM duty cycle…
    
    OCR1A = kLowerLimit;
    OCR1B = kLowerLimit;
    
    //  PWM period…
    
    ICR1 = 12500;
    
    //  Show initialization complete…
    
    PORTA = _BV(kPinInitCompleteLED);
    
    //  Loop forever steering left-to-right-to-left…
    
    OCR1A = kLowerLimit;            //    Min value == 0.9 ms
    while (1)
    {
        d = kDelay;
        while (d--);
        
        OCR1A += dir;
        
        if (OCR1A < kLowerLimit)
        {
            OCR1A = kLowerLimit;
            dir = 1;
            d = kReverseDelay;
            while (d--);
        }
        
        if (OCR1A > kUpperLimit)    //    Max value == 2.1 ms
        {
            OCR1A = kUpperLimit;
            dir = -1;
            d = kReverseDelay;
            while (d--);
        }
        
    }
    
    return 0;
}
Read more from AVR
6 Comments Post a comment
  1. James
    May 3 2006

    Hello,
    I’m having trouble with a similar project and i wonder if you can shed some light on your code for me?
    Whilst i include io.h and interrupt.h i have omitted inntypes.h and signal.h, can you breifly explain the purpose of these headers please?
    Congratulations on a very good post, I look forward to reading more of your rants:)
    J

  2. Alex De Camargo
    May 21 2007

    Hi,
    I am trying to learn how to use servos. I found this post and it is what I need to get started. I am not sure what ISSP means. Can you please clarify?
    Thanks,
    Alex

  3. Rick
    May 25 2007

    ISSP is “In-system serial programming”. Your best bet is to check out the hundreds of examples out there. Google “avr servo control” and you get many results. Try this one:
    http://winavr.scienceprog.com/example-avr-projects/servo-motor- control-using-avr.html

  4. Money
    Jan 24 2008

    Hi, I am wondering if anyone can help me. I need a program that uses the output compare function to create a 2kHz, 20% duty cycle square wave. I am using the atmega32 MCU. If you have any useful info, please send me an e-mail at buff4_life@hotmail.com

  5. vivek
    Jan 16 2009

    hi ,gr8 tuts but cudnt get the
    “With a 16 MHz clock, the full 16-bit count wraps around in about 780 µs”
    hw did u get 780 µs??

  6. reepjyoti
    May 29 2009

    can you please describe why u used da header inttypes and signal?? also please describe the TCCR1A,TCCR1B and the ICR1 part..

Share your thoughts, post a comment.

(required)
(required)

Note: HTML is allowed. Your email address will never be published.

Subscribe to comments