Step 5: 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.
Thank you<strong> HardwareHank</strong> for taking the time to post this jewel! Thanks also to<strong> jdelongpre</strong> &amp; <strong>average_male</strong> for the Arduino conversion info!<br> <br> I'm diving back into <em>'lectronics<strong>*</strong> prototyping</em> again after a 5 year break. I just got an Arduino Mega R3 and am loving it.&nbsp;<br> <br> This is a cool project that's looks easy to complete because it's well documented with plenty of tips and suggestions. &nbsp;Great job...you've made it easier and fun for the rest of us.<br> <br> <strong>*</strong>NOTE: <em>'lectronics</em> - This is a term usually heard only in the US Southern States. &nbsp;Usually, it's being shouted over a 2W loudspeaker system in Walmart: <em>&quot; ... 'lectronics, Line 1....&quot;</em>
Nice effect! I like it. *favorited*<br><br>I'm just getting started with microcontrollers, and projects like this seem doable for a noob.<br><br>With some tweaking, this sketch would work on an Atmega chip as well, right?
Thanks :).<br> <br> It is definitely doable for someone inexperienced with microcontrollers who is just getting comfortable with the AVR platform. It could definitely be adapted to an ATmega - the only changes would be register naming I think. Just find the equivalent PWM registers in your datasheet and everything should work fine :).<br> <br> Oh, and AFAIK, <strong>sketch</strong>&nbsp;refers to an Arduino program written in Processing. This is a <strong>program</strong>. It's fo' real, yo!
<br> In Arduino, sketch refers to ANY program. They are called &quot;sketches&quot; to keep artsie folks from getting scared by the name.<br> <br> To run this on an Arduino, you must change just one line:<br> &nbsp;&nbsp;&nbsp; DDRB |= _BV(DDB0); // for Attiny85 OC0A=PB0 (comment added)<br> -- to--:<br> &nbsp;&nbsp;&nbsp; DDRD |= _BV(DDB6); // for Arduino OC0A=PD6<br> -- or-- in the language of a sketch:<br> &nbsp;&nbsp;&nbsp; pinMode(6, OUTPUT); // enable OC0a output<br> The two forms do exactly the same thing and either works.<br> <br> Now use digital pin 6 for your LED (with a current limiting resistor in series to ground)
Is the comment still correct for the Fast PWM when the code has the assignment of the &quot;TCCR0B&quot; register commented out? <br> <br> // Use Fast PWM, OCRA TOP <br> TCCR0A |= _BV(WGM00); <br> TCCR0A |= _BV(WGM01); <br> //TCCR0B |= _BV(WGM02); <br> <br>Seeing how TCCR0B is not assigned, the comment should read as follows per the datasheet: <br> <br> // Use Fast PWM, OCR0A = 0xFF <br> TCCR0A |= _BV(WGM00); <br> TCCR0A |= _BV(WGM01); <br> <br>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: <br> <br> // General case - increment direction either way <br> OCR0A += dir; <br> <br>Please advise, <br>Moe Howard (brother of Shemp)
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.

About This Instructable




Bio: Just a dude who reads a lot of Instructables.
More by hardwarehank:Archiving Super Audio CDs (SACDs) with an old PS3 Slim DVD case tablet stand AVR LCD Namebadge 
Add instructable to: