Step 5: The code

Picture of The code
Here's my code.  Hopefully it's commented well enough.
/* LED Pulsate with PWM
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/sleep.h>

int main()
    // Turn on LED output pin
    DDRB   |= _BV(DDB0);

    // Clear OC0A on match
    TCCR0A |= _BV(COM0A1);

    // Set timer to count with F_CPU / 64
    TCCR0B |= _BV(CS10); // F_CPU
    TCCR0B |= _BV(CS01) | _BV(CS00);  // /64 prescale

    // Use Fast PWM, OCRA TOP
    TCCR0A |= _BV(WGM00);
    TCCR0A |= _BV(WGM01);
    //TCCR0B |= _BV(WGM02);

    // Initial value for our pulse width is 0
    OCR0A = 0x00;

    uint8_t dir = 1;           // Direction 
    uint16_t div = 500;        // # of clocks per inc/decrement
    uint16_t stall = 0;        // Initial stall counter
    uint16_t stall_time = 500; // # of clocks to stall at top/bottom
    for(;;) {
        // We only want to update every div counts
        if(TCNT0 % div != 0) continue;

        // Stall at the top and bottom, and change direction
        if(OCR0A == 255 || OCR0A == 0) {
            // Switch directon at top
            if(OCR0A == 255) dir = -1;
            {   // Disable LED at bottom
                // And switch direction
                DDRB  &= ~(_BV(DDB0));
                dir = 1; 
            while(stall < stall_time) 
            // Turn output back on if necessary
            if(OCR0A == 0) DDRB  |= _BV(DDB0);
            stall = 0;
        // General case - increment direction either way
        OCR0A += dir;
    return 0;
Hopefully, this code is self-documenting. Basically, we're changing the pulse width every few hundred clocks in one direction or the other. We have a special case for when we get to the top or bottom (change direction and linger), just like the Apple stuff does. There's another special case when we want to completely turn off the LED at the bottom - if we don't do this, it still has a (1/256)% duty cycle, and you can see it. Otherwise, the code should be rather concise. Constructive criticism encouraged.
Is the comment still correct for the Fast PWM when the code has the assignment of the "TCCR0B" register commented out?

// Use Fast PWM, OCRA TOP
TCCR0A |= _BV(WGM00);
TCCR0A |= _BV(WGM01);
//TCCR0B |= _BV(WGM02);

Seeing how TCCR0B is not assigned, the comment should read as follows per the datasheet:

// Use Fast PWM, OCR0A = 0xFF
TCCR0A |= _BV(WGM00);
TCCR0A |= _BV(WGM01);

Or does that matter as your code happens to assign 0xFF (255) to OCR0A via the following line and then later have a check for this value elsewhere in the code:

// General case - increment direction either way
OCR0A += dir;

Please advise,
Moe Howard (brother of Shemp)
hardwarehank (author)  average_male3 years ago
I think you're right. It's been a while since I've looked at this, but I think that OCRA TOP part refers to the commented line involving TCCR0B. Cheers.