This program makes a sort of animation with the 4 led lights that resembles a wave motion. Because of my obsessive development, it is quite long and complicated, but very fun and educational for begginers (like I was a few days ago). Uses terminal program like putty for interaction.

Dependencies:   mbed

main.cpp

Committer:
emollison
Date:
2011-09-06
Revision:
0:403b6acd3179

File content as of revision 0:403b6acd3179:

/*
Programmer:     Eric Mollison
Date:           9-6-2011
Hardware:       only mbed
Description:    "LED Wave" makes the 4 LEDs on the mbed shift in brightness
                with PWM (pulse width modulation) so as to appear to move
                smoothly to a side. LEDs are updated with a Ticker interrupt
                so that most of the processor capacity can collect input from
                a keyboard (if you installed the driver for the mbed 
                communicating with the pc). Optimized with precomputed values
                so that each step takes ~14us.
                Note:
                To use interactively, look up:
                http://mbed.org/handbook/SerialPC
                to find where to install drivers and putty or another terminal program
                Also note:
                You don't need to declare:
                "Serial pc(USBTX, USBRX);"
                probably because the code is in the mbed.h to autoinitialize 
                i/o functions like printf, putc getc, etc. (so I guess)
*/


#include "mbed.h"
#define PULSEWIDTH 18000    //time(us) for each pulse from the PWM
//flicker undetectable at pw <= 20000
//a pw too low will make too few brightness levels making another type of flicker
#define PI 3.1415926    //used to calculate sine from angle(radians): -PI/2 to PI/2

#define OSCIQUAR 500   //steps per quarter of a circle of angle
//5925 is max (wont work with too large of an array)
//11898 is max short array size that ever worked
//suspect malloc can do better
//can be smooth but fast at as low as 10

//OSCIQUAR * 2 = array size, and * 4 = steps or steps per cycle
#define STEPTIME 20000   //time(us) for each timed update or step of the lights
//no noticable flicker at 20ms step time
//13 us is the minimum (step takes >=13 microseconds)
//20 us is minimum to allow for keyboard input
//1 or 0 will freeze

#define LEDSTEP1(Num) {  \
    led##Num##.pulsewidth_us(getValFromCirc(OSCIQUAR,pwpt##Num##));  \
    pwpt##Num##+=stepRate; \
    if(pwpt##Num >= OSCIQUAR*4) pwpt##Num##-=OSCIQUAR*4;    \
    else if(pwpt##Num < 0) pwpt##Num##+=OSCIQUAR*4; \
}

#define DEBUGINT(Int) printf("%s = %d\n", #Int, Int);


//prototypes
void setpws(int qlen, int max);
void setpws_subt(int qlen, int max);
short getValFromCirc(int qlen, int cpt);
void step();

static int maxStepRate = OSCIQUAR*4*STEPTIME/1000000+1;   //set so that cycles take at least 1 second
static int stepAccel = maxStepRate/50+1;    //change in velocity with each keypress
static int stepRate = maxStepRate/10+1;    //initial velocity of moving light


PwmOut led1(LED1);  //pwmout sets up the leds for pulse width modulation
PwmOut led2(LED2);
PwmOut led3(LED3);
PwmOut led4(LED4);
Timer timer;        //timer is for testing performance
Ticker t_step;      //t_step controls the updates to brightness
static short *pwvals;   //array of precomputed brightnesses
//used short instead of int to save ram

int main() 
{

    printf("\nInitializing LED wave program...\n");
    pwvals = new short[OSCIQUAR*2];
    timer.start();  //begin counting time
    int begin, end;
    led4=1;
    
    begin = timer.read_us();    //find time to pre-compute
    setpws_subt(OSCIQUAR, PULSEWIDTH);
    end = timer.read_us();
    
    printf("intializing array of %d takes %d us\n", OSCIQUAR*2, end - begin);
    
    led1.period_us(PULSEWIDTH); //sets period for all leds on pwm
    
    t_step.attach_us(&step, STEPTIME);  //make ticker update LED
    printf("Successfully started cycling LEDs\n");
    printf("\nPush '=' or '+' to accelerate shift to the left.\n");
    printf("Push '-' to accelerate right.\n");
    printf("Push '0'(zero), 's' or 'S' to stop motion.\n\n");
    while(1)    //keyboard read loop
    {
        char c = getc(stdin);
        if (c=='=' || c=='+')
        {
            if(stepRate<maxStepRate)
            {
                stepRate+=stepAccel;
                if(stepRate>maxStepRate)stepRate=maxStepRate;
                DEBUGINT(stepRate)
            }
        }
        else if (c=='-')
        {
            if(stepRate>-maxStepRate)
            {
                stepRate-=stepAccel;
                if(stepRate<-maxStepRate)stepRate=-maxStepRate;
                DEBUGINT(stepRate)
            }
        }
        else if (c=='s' || c=='S' || c=='0')
        {
            stepRate=0;
            DEBUGINT(stepRate)
        }
        //wait_ms(10);
        
    }//end keyboard read loop
    
}

/*
repeated function run by Ticker which updates the lights at regular intervals
so that they appear to smoothly shift with "anti-aliasing"
*/
void step()
{
    static int pwpt1=0, pwpt2=OSCIQUAR, pwpt3=OSCIQUAR*2, pwpt4=OSCIQUAR*3;
    static int step_counter=0, time=0;
    const int check_interval=1000;    //if over 100M, may overflow time
        if(++step_counter==check_interval)
        {
            printf("Time(us) to step = %8f\n", (float)time/check_interval);
            step_counter=0;
            time=0;
        }
        time-=timer.read_us();
        
        //just showing how to shorten code with macros
        #ifdef LEDSTEP
        LEDSTEP(1)
        LEDSTEP(2)
        LEDSTEP(3)
        LEDSTEP(4)
        #else
        led1.pulsewidth_us(getValFromCirc(OSCIQUAR,pwpt1));
        led2.pulsewidth_us(getValFromCirc(OSCIQUAR,pwpt2));
        led3.pulsewidth_us(getValFromCirc(OSCIQUAR,pwpt3));
        led4.pulsewidth_us(getValFromCirc(OSCIQUAR,pwpt4));
        
        pwpt1+=stepRate;
        pwpt2+=stepRate;
        pwpt3+=stepRate;
        pwpt4+=stepRate;
        
        if(pwpt1 >= OSCIQUAR*4) pwpt1-=OSCIQUAR*4;
        else if(pwpt1 < 0) pwpt1+=OSCIQUAR*4;
        if(pwpt2 >= OSCIQUAR*4) pwpt2-=OSCIQUAR*4;
        else if(pwpt2 < 0) pwpt2+=OSCIQUAR*4;
        if(pwpt3 >= OSCIQUAR*4) pwpt3-=OSCIQUAR*4;
        else if(pwpt3 < 0) pwpt3+=OSCIQUAR*4;
        if(pwpt4 >= OSCIQUAR*4) pwpt4-=OSCIQUAR*4;
        else if(pwpt4 < 0) pwpt4+=OSCIQUAR*4;
        #endif
        
        time+=timer.read_us();
        
}

/*
initializes the array of pre-computed values for light intensity
applied by the PWM such that lights turn completely off for an instant
*/
void setpws_subt(int qlen, int max)
{
    int i=0;
    float tempsin, tempexp;
    float tempsinadd=.5-qlen;
    float tempsinmul=(PI/(OSCIQUAR*2));
    float expmul=4;
    float subtractor=(exp(-2*expmul))-.99/max;
    int newmax=max+(int)(subtractor*max);
    
    do
    {   
        tempsin = sin((i+tempsinadd)*tempsinmul);
        tempexp = exp((tempsin-1)*expmul)-subtractor;
        pwvals[i] = (short)(tempexp*newmax);
        //pwvals[i] = (short)(exp((sin((i+tempsinadd)*tempsinmul)-1)*4)*max);
    }while(++i<2*qlen);
}

/*
initializes the array of pre-computed values for light intensity
applied by the PWM. Setpws_subt is an alternative.
*/
void setpws(int qlen, int max)
{
    int i=0;
    float tempsin, tempexp;
    float tempsinadd=.5-qlen;
    float tempsinmul=(PI/(OSCIQUAR*2));
    float expmul=4;
    
    do
    {   
        //currently ~2000 cycles per loop
        
        tempsin = sin((i+tempsinadd)*tempsinmul);
        tempexp = exp((tempsin-1)*expmul);
        pwvals[i] = (short)(tempexp*max);
        //pwvals[i] = (short)(exp((sin((i+tempsinadd)*tempsinmul)-1)*expmul)*max);
    }while(++i<2*qlen);
}

/*
selects a value from an array by position on a circle from -PI/2 to (3/2)*PI
when the array is for -PI/2 to PI/2 and reverses from PI/2 to (3/2)*PI
*/
short getValFromCirc(int qlen, int cpt)
{
    if (cpt < 2*qlen)
    {
        return pwvals[cpt];
    }
    else
    {
        return pwvals[4*qlen-cpt-1];
    }
}