Saturday, October 27, 2012

LED Brightness PWM

Here's a simple circuit and code to control the brightness of a LED using PWM on an ATMEGA88A. I had to go searching to find information about PWM and it all seemed to be for other models which had things set out in different ways. The main difference here being in most AVR chips it seems that each timer has its own control register whereas the chip I am using has two registers that share the timer attributes but are named as if they are for the separate compare units.

Firstly I highly recommend this tutorial on simply getting a LED to flash. It uses a different pin for the LED so is a slightly different circuit but not by much. I personally only used the electrical schematic and the code from the tutorial as I'm using a AVRISP MKII programmer and know basic electronics already.

So for my very small tutorial (not really a tutorial), I'm going to assume the reader can read an electrical schematic and knows how get the code from what you type to the chip. I'm also going to not really explain the code much as I tried to comment as much as I could. The reason for not explaining what I did so much is because there are heaps of tutorials out there for PWM on AVR chips. I only wanted to put this one out because I couldn't find simple code (rather than masses of subroutines) to control PWM using the 8bit timer0 on the ATMEGA88.

Without any further ado here's a video of how the LED should flash after the project.
So that is hopefully what it will look like after we are done. So the first thing to do is to wire the circuit up. Here's the schematic:
R1 is a pull up resistor to make sure the reset pin (which is labelled as pin PC6) is kept at logic one when not being programmed. I tried adding a 10uF capacitor connected to ground on the reset pin but the programmer was unable to connect, so I haven't added one in.  C1 is a smoothing capacitor just to help if there is any spikes in the supply voltage. R2 makes sure that there is enough current going through the LED to give enough brightness without pulling too much current from the microcontroller. The connector named ISP-CONNECT, is where the MKII attaches.

Finally the code is as follows.

/*
 * Name: ATMEGA88A - Breathing LED using PWM on timer0
 * Description: This sets a LED on portD.6 to glow in a
 *    pattern similar to breathing, like the Mac sleep LED does
 */

#include <avr/io.h>
#include <math.h>                   // Allows the use of pi and e in Equations
#include <avr/interrupt.h>

unsigned int count = 0;             // our variable to determine where in wave we are

int main()
{
    DDRD = (1 << DDD6);             // Set PD6 as an output
    OCR0A = 0;                      // clear OC0A (0% duty)
    OCR0B = 0;                      // clear OC0B for good practice
    TCNT0 = 0;                      // clear timer0 register
    TIFR0 = 0;                      // clear timer0 all interrupts if any
    TIMSK0 = (1 << OCIE0A);         // enable interrupt for OC0A
    TCCR0A = (0<<WGM01)|(1<<WGM00);    // timer0 to phase corrected PWM
    TCCR0B = (1<<CS00);             // timer0 to use clock with no prescale
    TCCR0A ^= (1 << COM0A1);        // Clear 0C0A on compare match (turn timer on)
    sei();                          // Enables global interrupts
    while(1);                       // Never ending loop
}

/**************************************/
/**** Interupt for timer0A compare ****/
/**************************************/
/* This is used to update the duty    */
/* cycle to follow our equation. The  */
/* equation is a modified version of  */
/* exp(sin(x)).                       */
/**************************************/
ISR(TIMER0_COMPA_vect)
{
    count++;                        // update where we are in the wave
    OCR0A = (255+255/exp(2))*exp(sin(2*M_PI*count/8192)-1)-255/exp(2);
                                    // set the new duty cycle





The only thing I really want to say about the code is about the equation on the third last line. The equation is based on exp(sin(x) ). This was then amplified and stretched to get the full voltage range and not flash too quickly. The number 8192 is 1/8th of the max value an unsigned integer, so to get a good time for each cycle without having any part of the wave skipped.

If anyone has any questions, write in the comments and I'll try and reply.