Voting resources, early voting, and poll worker information - VOTE. ... Adafruit is open and shipping.
0

Help with debugging PWM code
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

Help with debugging PWM code

by schenckb on Sat Aug 21, 2010 3:51 pm

Hi all,

I am a bit new to AVR programming, but I already love it. I am having a lot of difficulty with a particular project though, and was wondering if anyone can help debug.

I am trying to get a simple LED Nightlight working, based on the code from FangleTronics. This code uses two timers and a little logic to create a PWM output for transitioning between stages of an RBG LED. it took me a while to figure out exactly what was going on here, but I thinks its something like this:

Every time the 8-bit TIMER overflows, an 8-bit counter is incremented. When this counter overflows, the LED state is updated (based on 256 different values for each color). The states are then compared against the value of the counter, thus creating a PWM signal.

The code was written for a single common-anode RBG LED, with the anode connected to Vcc. Therefore, an output of '1' will turn OFF the LED, an output of '0' will turn it ON (inverted PWM, sort of).

Since the MCU runs at 9.6 MHz, I would expect the timer overflow to happen every 26.6us (256/9600000). Add in the sCounter and the LED values would be read every 6.8ms (256*256/9600000). There is a delay in the main routine that seems to wait for 250ms after rbgCycle() is complete.

So, here's the part I don't get. I have been messing with this for some time and I don't think the code ever gets into the rbgCycle() routine after the initial one. Certainly, the simulator spends 100% of the time (other than at power-on) in the ISR because it happens so frequently.

I am trying to convert this code to use with a common cathode LED, which shouldn't seem like a big deal, except for the fact that I don't think this works as expected in the first place.

I should mention that, with a common-cathode LED hooked up using the common-anode code below, I get no transitioning. BLUE is constant, RED flickers but is mostly on and GREEN flickers but is mostly off.

Can anyone help out a newbie?

Thanks!


Code: Select all | TOGGLE FULL SIZE
#ifndef F_CPU
    #define F_CPU 9600000UL
#endif
 
 /*
 * rgb_strobe.c
 *
 * Distributed under Creative Commons 3.0 -- Attib & Share Alike
 *
 *  Created on: Feb 6, 2010
 *      Author: PaulBo
 */

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
 
 //Hardware definitions
#define RED_LED      PB2
#define GREEN_LED    PB1
#define BLUE_LED     PB0
#define ALL_LEDS    ((1 << RED_LED) | (1 << GREEN_LED) | (1 << BLUE_LED))
 
//Maximum value for led brightness
#define R_MAX 255
#define G_MAX 255
#define B_MAX 255
 
#define RED_INDEX   0
#define GREEN_INDEX 1
#define BLUE_INDEX  2
 
//Cycle States
#define RedToYellow     0
#define YellowToGreen   1
#define GreenToCyan     2
#define CyanToBlue      3
#define BlueToMagenta   4
#define MagentaToRed    5
 
//set red to max (we start in the RedToYellow state)
volatile unsigned char mRgbBuffer[] = {0,0,0};
unsigned char mRgbValues[]          = {255,0,0};
unsigned char mState;
 
void init_timers()
{
    TIMSK0 = (1 << TOIE0);         // enable overflow interrupt
    TCCR0B = (1 << CS00);          // start timer, no prescale
 
    //enable interrupts
    sei();
}
 
void rgbCycle(){
    switch (mState) {
    case RedToYellow:
        mRgbValues[GREEN_INDEX]++;
        if (mRgbValues[GREEN_INDEX] == G_MAX)
            mState++;
        break;
    case YellowToGreen:
        mRgbValues[RED_INDEX]--;
        if (mRgbValues[RED_INDEX] == 0)
            mState++;
        break;
    case GreenToCyan:
        mRgbValues[BLUE_INDEX]++;
        if (mRgbValues[BLUE_INDEX] == B_MAX)
            mState++;
        break;
    case CyanToBlue:
        mRgbValues[GREEN_INDEX]--;
        if (mRgbValues[GREEN_INDEX] == 0)
            mState++;
        break;
    case BlueToMagenta:
        mRgbValues[RED_INDEX]++;
        if (mRgbValues[RED_INDEX] == R_MAX)
            mState++;
        break;
    case MagentaToRed:
        mRgbValues[BLUE_INDEX]--;
        if (mRgbValues[BLUE_INDEX] == 0)
            mState++;
        break;
    }
 
    //state should never advance beyond 5.
    //It wraps back to 0 when we reach 6
    mState %= 6;
}
 
int main(void){
    //Set LED pins to OUTPUT mode (Data Direction Register)
    DDRB |= ALL_LEDS;
 
    init_timers();
 
    while (1) {
        rgbCycle();
        _delay_ms(250);
    }
    return 0;
}
 
/*
 * Timer/Counter overflow interrupt. This is called each time
 * the counter overflows (255 counts/cycles).
 */
ISR(TIM0_OVF_vect)
{
    //static variables maintain state between every call to ISR
   //these statements initialize the variables only at compile time
   //they won't be reset in every call
    static unsigned char sPortBmask = ALL_LEDS;
    static unsigned char sCounter = 255;

    // set port pins straight away (no waiting for processing)
   // PORTB is the actual value of the pin
   // Turn ON all ports at startup, otherwise update them to the current mask value
    PORTB = sPortBmask;
 
    //this counter will overflow back to 0 after reaching 255.
    //So we end up adjusting the LED states for every 256 interrupts/overflows.
   //First time through the counter will go through this loop
    if(++sCounter == 0)
    {
        mRgbBuffer[RED_INDEX]   = mRgbValues[RED_INDEX];
        mRgbBuffer[GREEN_INDEX] = mRgbValues[GREEN_INDEX];
        mRgbBuffer[BLUE_INDEX]  = mRgbValues[BLUE_INDEX];
 
        //reset the portMask to low every time we count 256 interrupts
      //(remember this is a common anode LED)
        sPortBmask &=~ ALL_LEDS;
    }
    //this loop is considered for every overflow interrupt.
    //this is the software PWM.
    if(mRgbBuffer[RED_INDEX]   == sCounter) sPortBmask |= (1 << RED_LED);
    if(mRgbBuffer[GREEN_INDEX] == sCounter) sPortBmask |= (1 << GREEN_LED);
    if(mRgbBuffer[BLUE_INDEX]  == sCounter) sPortBmask |= (1 << BLUE_LED);
}

schenckb
 
Posts: 5
Joined: Mon Apr 19, 2010 10:50 pm

Re: Help with debugging PWM code

by schenckb on Sun Aug 22, 2010 10:23 pm

Doh! Finally, some success...

I think this code was working, but it was taking SO long that I never saw (even after a few minutes) the results.

After changing the CKDIV8 fuse bit (turning DIV8 off) and reducing the delays in the program from 250 to 10, this seems to be working.

Now I have to make the necessary edits for common cathode instead of anode.

Thanks!
schenckb
 
Posts: 5
Joined: Mon Apr 19, 2010 10:50 pm

Please be positive and constructive with your questions and comments.