Driving a HL1606 RGB LED strip with the new mbed m0 LPC11U24: first version, without the use of the SI wire, software-PWM only.

main.cpp

Committer:
uski
Date:
2011-12-23
Revision:
0:5ebdba8c620f

File content as of revision 0:5ebdba8c620f:

#include "mbed.h"

DigitalOut mbedLED1(LED1);
DigitalOut mbedLED2(LED2);
DigitalOut mbedLED3(LED3);
DigitalOut mbedLED4(LED4);

Ticker tkrHL1606_update;

#define HL1606_LEDcnt 160 //Depends of your particular strip, here 5meters=80 HL1606=160 LEDs
int HL1606_pwmCounter; //Used in HL1606_update, PWM counter

unsigned char HL1606_redPWM[2][HL1606_LEDcnt];
unsigned char HL1606_greenPWM[2][HL1606_LEDcnt];
unsigned char HL1606_bluePWM[2][HL1606_LEDcnt];
unsigned char HL1606_curFrame;
unsigned char HL1606_lastFrameDisplayed;

DigitalOut HL1606_latchPin(p8);
SPI HL1606_SPI(p5, p6, p7); // mosi, miso, sclk

// Send updated RGB values to the strip
void HL1606_update()
{
  unsigned char i, d, curFrameLatched;
  
  // Remember which frame we're showing, in case the background code changes it while we run
  curFrameLatched=HL1606_curFrame;
  
  //Duty cycle of the led = cpu usage of this func
  mbedLED4 = 1;
  
  // write out data to strip 
  for (i=0; i < HL1606_LEDcnt; i++) {
    d = 0x80;          // set the latch bit
    // calculate the next LED's byte
    if (HL1606_pwmCounter < HL1606_redPWM[curFrameLatched][i]) {
      d |= 0x04;
    } 
    if (HL1606_pwmCounter < HL1606_bluePWM[curFrameLatched][i]) {
      d |= 0x10;
    } 
    if (HL1606_pwmCounter < HL1606_greenPWM[curFrameLatched][i]) {
      d |= 0x01;
    } 

    // send new data
    HL1606_SPI.write(d); 
  }

  // increment our PWM counter
  HL1606_pwmCounter += 1;
  // 2 bits per pixel, max value 3 (0 1 2 3)
  if (HL1606_pwmCounter > 3) HL1606_pwmCounter = 0;

  // latch
  HL1606_latchPin = 1;
  wait_us(2);
  HL1606_latchPin = 0;
  
  mbedLED4 = 0;
}

// LOW LEVEL
unsigned int lfsr_stat;
unsigned int lfsr()
{
  unsigned int bit;
  /* taps: 16 14 13 11; characteristic polynomial: x^16 + x^14 + x^13 + x^11 + 1 */
  bit  = ((lfsr_stat >> 0) ^ (lfsr_stat >> 2) ^ (lfsr_stat >> 3) ^ (lfsr_stat >> 5) ) & 1;
  lfsr_stat = (lfsr_stat >> 1) | (bit << 15);
  return lfsr_stat;
}

void HL1606_fillRGB(unsigned char red,unsigned char green,unsigned char blue)
{
    unsigned char workBuf = HL1606_curFrame ^ 1; //work with the frame buffer not displayed
    
    for (int i = 0; i < HL1606_LEDcnt; i++)
    {
        HL1606_redPWM[workBuf][i] = red;
        HL1606_greenPWM[workBuf][i] = green;
        HL1606_bluePWM[workBuf][i] = blue;
    }
    HL1606_curFrame = workBuf;
}

//HIGH LEVEL

void HL1606_doBlueWhiteFading(float delayDuringFading, float delayBetweenFadings)
{
    //Start white
    HL1606_fillRGB(3,3,2);
    wait(delayBetweenFadings);
    //Fade to blue
    for (int i = 0; i <= 3; i++)
    {
        HL1606_fillRGB(3-i,3-i,2);
        wait(delayDuringFading);
    }
    //Stay blue for a while
    HL1606_fillRGB(0,0,2);
    wait(delayBetweenFadings);
    //Go back to white
    for (int i = 0; i <= 3; i++)
    {
        HL1606_fillRGB(i,i,2);
        wait(delayDuringFading);
    }    
}

//Simulate a old-school christmas light
void HL1606_simulateOldSchoolRGBY(float delay)
{
    unsigned char workBuf;
    unsigned char curBrightness = 3;
    unsigned char curColor = 0;
    
    //Each color after the other
    for (curColor = 0; curColor < 4; curColor++)
    {
        workBuf = HL1606_curFrame ^ 1; //work with the frame buffer not displayed
        //Draw the LEDs
        for (int i = 0; i < HL1606_LEDcnt; i++)
        {
            unsigned int color = i & 3; //get LSB 2 bits : tells the current color
            if (color == curColor)
            {
                if (color == 0)
                {
                    HL1606_redPWM[workBuf][i] = curBrightness;
                    HL1606_greenPWM[workBuf][i] = 0;
                    HL1606_bluePWM[workBuf][i] = 0;
                }
                else if (color == 1)
                {
                    HL1606_redPWM[workBuf][i] = 0;
                    HL1606_greenPWM[workBuf][i] = curBrightness;
                    HL1606_bluePWM[workBuf][i] = 0;
                }
                else if (color == 2)
                {
                    HL1606_redPWM[workBuf][i] = 0;
                    HL1606_greenPWM[workBuf][i] = 0;
                    HL1606_bluePWM[workBuf][i] = curBrightness;
                }
                else
                {
                    HL1606_redPWM[workBuf][i] = curBrightness;
                    HL1606_greenPWM[workBuf][i] = curBrightness;
                    HL1606_bluePWM[workBuf][i] = 0;
                }
            }
            else
            {
                HL1606_redPWM[workBuf][i] = 0;
                HL1606_greenPWM[workBuf][i] = 0;
                HL1606_bluePWM[workBuf][i] = 0;
            }
        }
        HL1606_curFrame = workBuf;
        wait(delay);
    }
}

void HL1606_doSparkle()
{
    unsigned char workBuf = HL1606_curFrame ^ 1; //work with the frame buffer not displayed
    
    for (int i = 0; i < HL1606_LEDcnt; i++)
    {
        HL1606_redPWM[workBuf][i] =  HL1606_greenPWM[workBuf][i] = HL1606_bluePWM[workBuf][i] = ((lfsr() & 63) == 0)?3:0;
    }
    HL1606_curFrame = workBuf;
}

void HL1606_doRainbow(float delay)
{
        //now: 3,0,0 (red)
        for (int i = 0; i<4; i++)
        {
            HL1606_fillRGB(0x3,i,0x0);
            wait(delay);
        }
        //now: 3,3,0 (yellow)
        for (int i = 0; i<4; i++)
        {
            HL1606_fillRGB(0x3-i,0x3,0x0);
            wait(delay);
        }
        //now: 0,3,0 (green)
        for (int i = 0; i<4; i++)
        {
            HL1606_fillRGB(0x0,0x3,i);
            wait(delay);
        }
        //now: 0,3,3 (cyan)
        for (int i = 0; i<4; i++)
        {
            HL1606_fillRGB(0x0,0x3-i,0x3);
            wait(delay);
        }
        //now: 0,0,3 (blue)
        for (int i = 0; i<4; i++)
        {
            HL1606_fillRGB(i,0x0,0x3);
            wait(delay);
        }        
        //now: 3,0,3 (purple)
        for (int i = 0; i<4; i++)
        {
            HL1606_fillRGB(0x3,0x0,0x3-i);
            wait(delay);
        }        
        //now: 3,0,0 (red)
}

void HL1606_doK2000(float delay, unsigned char speed, unsigned char length)
{
    unsigned char workBuf = HL1606_curFrame ^ 1; //work with the frame buffer not displayed
    
    //end stores the position of the end of the light line (it can be higher than HL1606_LEDcnt because
    //the led line can go completely off the display
    for (int end = 0; end <  HL1606_LEDcnt + length; end += speed)
    {    
        for (int i = 0; i < HL1606_LEDcnt; i++)
        {
            HL1606_redPWM[workBuf][i] = ((i<end)&&(i>(end-length)))?3:0;
            HL1606_greenPWM[workBuf][i] = ((i<end)&&(i>(end-length)))?1:0;
            HL1606_bluePWM[workBuf][i] = 0;
        }
        HL1606_curFrame = workBuf;    
        wait(delay);
    }

    //reverse
    for (int end = HL1606_LEDcnt + length - 1; end >= 0; end -= speed)
    {    
        for (int i = 0; i < HL1606_LEDcnt; i++)
        {
            HL1606_redPWM[workBuf][i] = ((i<end)&&(i>(end-length)))?3:0;
            HL1606_greenPWM[workBuf][i] = ((i<end)&&(i>(end-length)))?1:0;;
            HL1606_bluePWM[workBuf][i] = 0;
        }
        HL1606_curFrame = workBuf;    
        wait(delay);
    }
    
}

void HL1606_doXmas(float delay, unsigned char speed, unsigned char length)
{
    unsigned char workBuf = HL1606_curFrame ^ 1; //work with the frame buffer not displayed
    
    //display red/green alternating bars, moving together
    for (int shift = 0; shift < length*2; shift += speed)
    {    
        unsigned char countSameColor = shift%length;
        unsigned char state;
        if (shift>=length) state = 0;
        else state = 1;
        for (int i = 0; i < HL1606_LEDcnt; i++)
        {

            countSameColor++;
            if (countSameColor == length)
            {
                state = state ^ 1;
                countSameColor = 0;            
            }
            
            HL1606_redPWM[workBuf][i] = state?3:0;
            HL1606_greenPWM[workBuf][i] = state?0:1;
            HL1606_bluePWM[workBuf][i] = 0;
        }
        HL1606_curFrame = workBuf;    
        wait(delay);
    }
    
}

void HL1606_doFrenchFlag()
{
    unsigned char workBuf = HL1606_curFrame ^ 1; //work with the frame buffer not displayed
    //This wasn't meant to be very patriotic, just wanted to do a quick test
    //Blue, white, red (reverse because of the order of the LEDs)
    for (int i = 0; i < HL1606_LEDcnt/3; i++)
    {
        HL1606_redPWM[workBuf][i] = 0;
        HL1606_greenPWM[workBuf][i] = 0;
        HL1606_bluePWM[workBuf][i] = 3;
    }
    for (int i = HL1606_LEDcnt/3; i < (HL1606_LEDcnt/3)*2; i++)
    {
        HL1606_redPWM[workBuf][i] = 2;
        HL1606_greenPWM[workBuf][i] = 2;
        HL1606_bluePWM[workBuf][i] = 3;
    }
    for (int i = (HL1606_LEDcnt/3)*2; i < HL1606_LEDcnt; i++)
    {
        HL1606_redPWM[workBuf][i] = 3;
        HL1606_greenPWM[workBuf][i] = 0;
        HL1606_bluePWM[workBuf][i] = 0;
    }

    HL1606_curFrame = workBuf;
}

int main() {

    HL1606_SPI.format(8,3); // 8 bits per frame, SPI mode 3
    HL1606_SPI.frequency(450000); // 450kHz freq; higher speed = draw errors
    
    lfsr_stat = 0xACE1; //LFSR init
    
    mbedLED1 = 0;
    mbedLED2 = 0;
    mbedLED3 = 0;
    mbedLED4 = 0;
    
    HL1606_curFrame = 0; //init double-buffer cur frame pointer
    
    for (int i = 0; i < 160; i++)
    {
        HL1606_redPWM[0][i] = i & 0x03;
        HL1606_greenPWM[0][i] = (i & 0x0C) >> 2;
        HL1606_bluePWM[0][i] = (i & 0x30) >> 4;
    }

    // Update LEDs every 4.5ms
    tkrHL1606_update.attach(&HL1606_update, 0.0045);

    // Main effect loop
    while(1) {
        //Sparkle
        for (int i = 0; i < 900; i++)
        {
            HL1606_doSparkle();
            wait(0.025);
        }
        //Xmas banner
        for (int i = 0; i < 20; i++) HL1606_doXmas(0.04,1,25);
        //French flag :)
        HL1606_doFrenchFlag();
        wait(8);
        //Blue and white fading
        for (int i = 0; i < 5; i++) HL1606_doBlueWhiteFading(0.3,1.5);
        //Chase like K2000
        for (int i = 0; i < 5; i++) HL1606_doK2000(0.01,1,25);
        //Rainbow: red->yellow->green->cyan->blue->purple...
        HL1606_doRainbow(0.3);
        HL1606_doRainbow(0.3);
        HL1606_doRainbow(0.3);
        HL1606_doRainbow(0.3);
        //Old school RGBY light
        for (int i = 0; i < 15; i++) HL1606_simulateOldSchoolRGBY(0.4);
    }
}