A few classes to interface one or more ShiftBrite module to the FRDM KL25Z.

Dependencies:   mbed

sbDriver.cpp

Committer:
JoKer
Date:
2014-08-20
Revision:
4:d2f8ddb423e2
Parent:
3:9376bf1f1bbd
Child:
5:aa0424f31fa1

File content as of revision 4:d2f8ddb423e2:

//Low level driver for shiftbrite modules
#include <iostream>
#include "mbed.h"
#include "sbDriver.h"

//#define ERRORPRINT //Comment out to not see any error message on STD::


//NOTES ON THE SHIFTBRITE MODULES
/*
 The modules are daisy chained and they buffer data, clock, latch and enable signals from in to out.
 Imagine the chain of leds as a VERY long shift register, growing by 32 bits (4 bytes) for each LED. 
 Each LED is 10 bits and the complete RGB set is
Bit #31                                 #0
      00010101  01010101 01010101 01010101
 (msb)xC\--LED1----/\--LED2---/\--LED3---/(lsb)
          BLUE         GREEN      RED
 Two most msb bits are not used except to enter COMMAND mode by setting bit 30
 In COMMAND mode set a value of 0 to 127 for each led that adjust the max current from 30% to 100%
 Manual recommends red120, green100 and blue100.
 
 Data is moved out startgin with MSB.
 Data is latched into the *WHOLE* chain with a *SINGLE* latch pulse.
 I.E, if you have 2 leds you clock in 64 bits followed by *one* latch pulse
 The first 32 bits will be latched into the LED on the very end of the chain. 
 Thus the last 32 bit will be latched into the LED closest to the MCU.
 
 There is no RESET command for the A6281's. They are only reset at power up.
 Thus, if they loose synchronization with the clock/data you have to cycle the power.
 I have a FET/Transisor circuit that allows me to do this using the reset() member function
 
 There needs to be a delay from the last bit to the positive edge of the latch pulse (see datasheet).
 
 */
 
 
//=============================================================================================    
// colour class to hold a led colour value
colour colour::operator=(unsigned short int v){
    setColour(v); // this will also range check
    return *this;
}
//--------------------

colour colour::operator=(colour c){
    value = c.value;
    return *this;
}


//=============================================================================================    
//rgbLed class to manupilate RGB colours as one object

rgbLed::rgbLed(unsigned long int rgbValue){ // constructor for RGB value e.g. 0xFFFFFF. Will expand 0XFF to 0x3FF as per colour range. Remember to update packet
    setRgbLed(rgbValue); // this way all the other house keeping chores will be done
}
//--------------------
rgbLed::rgbLed(unsigned short int red, unsigned short int green, unsigned short int blue){ //overload for  seperate r,g,b arguments. Remember to updatepacket
    this->red=red;
    this->green = green;
    this->blue = blue;
}
//--------------------

rgbLed::rgbLed(){//no arguments given so default to 0
    //red.setColour(0); //red is an colour object, not just a value
    red=0;//use overloaded operator
    //green.setColour(0);
    green = 0;
    //blue.setColour(0);
    blue = 0;
    //packet = 0; 
    
}
//--------------------
unsigned long int rgbLed::getPacket(){
    //Convert R, G and B values into one 4 byte packet ready for trnsmittion to SB
    //TO DO : Confirm that unsigned long int is 4 bytes long.......
    //NOTE, This compensates for SB being GRB and nor RGB
    unsigned long int temp;
    temp = 0; // initialize - DATA MODE
    //temp = 0x01; //init - COMMAND MODE
    temp <<=2; //shift in mode
    temp |= (blue.getColour()& 0X3FF);
    //ser_p->printf("BLUE: 0x%lx",temp);
    temp <<= 10;
    temp |= (red.getColour() & 0X3FF);
    temp <<=10;
    temp |= (green.getColour() & 0X3FF);
    return temp;
}
//--------------------

void rgbLed::setRgbLed(unsigned long int rgbValue){ // RGB value e.g. 0xFFFFFF. Will expand 0XFF to 0x3FF as per colour range
// THIS IS NOT THE BEST AS IT WILL EXPAND
// First, pull each colour out and set accordingly in rgbLed
    blue.setColour(4*((unsigned short int)(rgbValue & 0X0000FF)));//blue
    //But, remember, the range is 0-0x3FF, not 0-0xFF
    green = 4*((unsigned short int)((rgbValue >> 8) & 0X0000FF));//This uses the overloaded = operator
    red = 4*((unsigned short int)((rgbValue >> 16) & 0X0000FF));
}
//--------------------

void rgbLed::setRgbLed(unsigned short int red, unsigned short int green, unsigned short int blue){ //overload for  seperate r,g,b arguments
//THIS IS THE BEST WAY AS YOU CAN SPECIFY THE FULL 0-0X3FF RANGE
    this->red=red;//This should kick in the operator overloaded f() and do a range check
    this->green = green;
    this->blue = blue;
}



//=============================================================================================
// Display object tracking/controlling multiple rgbLed objects    
//shiftBriteDisplay::shiftBriteDisplay (Serial *port,DigitalOut &latch, DigitalOut &enable, DigitalOut &reset, SPI &spiPort, unsigned int moduleCount):/*PC(port),*/sb_latch(latch), sb_enable(enable), sb_reset(reset), spi(spiPort){ //constructor
shiftBriteDisplay::shiftBriteDisplay (DigitalOut &latch, DigitalOut &enable, DigitalOut &reset, SPI &spiPort, unsigned int moduleCount):/*PC(port),*/sb_latch(latch), sb_enable(enable), sb_reset(reset), spi(spiPort){ //constructor
// OK, so the sb control/data lines have been setup via references
//Allocate memory for moduleCount* sb modules
    //serial_p = port;
    spi.format(8,0);//8 bit, mode 0
    spi.frequency(100000);//100kHz clk


    this->moduleCount = moduleCount;
    module_p = new rgbLed[moduleCount];//
    if(!module_p){
        //to do : WHAT TO DO WITH ERRORS?
#ifdef ERRORPRINT
        serial_p->printf("Unable to allocate memory for RGB led array\r\n");
#endif
        while(1);// for now just get stuck here
    }
    
    priv_reset(); //assuming cct connected, remove power for a short time to reset the chips
    priv_SBEnable();
    
    setCurrentCorr(0x78,0x64,0x64);//setup default rgb values
    f_update = 0;
}
//--------------------

void shiftBriteDisplay::setCurrentCorr(unsigned short int red, unsigned short int green, unsigned short int blue){
        //Now, for each module, set a command packet with
        //appropriately loaded current control values
        //and send
        //Uses same module_p as normal operations
        //This saves and restores the current colour values
        unsigned short int i, r,g,b;//storage for temporary colour memory
        
        //save user selected value for current correction reg values with some range checking
        rCorr = red & 0X7F; // ensure that it is no larger than 0x7F
        gCorr = green & 0x7F;
        bCorr= blue & 0x7F;
        
        if(f_update == 0){
        f_update=1; //prevent the ISR from messing this up
        //Backup the colours
        
        //do it
        //As per recommenation, set red=120, green and blue = 100
        for(i=0; i != moduleCount; i++){
            r=module_p[i].getRed();
            g=module_p[i].getGreen();
            b=module_p[i].getBlue();
            setLed(i,red,green,blue);
            send(module_p[i],1);
            setLed(i,r,g,b); //restore colours
        }
 /*       for(i=0; i != moduleCount; i++){
            send(module_p[i],1);
        }*/
        priv_SBLatch();
        f_update=0;
#ifdef ERRORPRINT
        } 
        else {
            serial_p->printf("Current adj packet lost\r\n");
#endif
        }

}

//Overloaded member f()
void shiftBriteDisplay::setCurrentCorr(){
    setCurrentCorr(rCorr,gCorr,bCorr); // call the setter with the correct values
}
//--------------------

shiftBriteDisplay::~shiftBriteDisplay(){//Destructor
    delete [] module_p;//Be nice, clean the house. Not strictly required as this should never be called as we never reach the end of the program
}
//--------------------

void shiftBriteDisplay::setLed(unsigned int moduleNum, unsigned long int rgbValue){
    //Set the colour of a specific LED in the array
    moduleCount >= moduleNum ? module_p[moduleNum].setRgbLed(rgbValue): /*TO DO - set some sort of error? For now, mess up the LAST moodule setting*/ module_p[moduleCount-1].setRgbLed(rgbValue);
    }
//--------------------
void shiftBriteDisplay::setLed(unsigned int moduleNum, unsigned short int red, unsigned short int green, unsigned short int blue){
    module_p[moduleNum].setRgbLed(red,green,blue);
}  
//--------------------

void shiftBriteDisplay::send(rgbLed M, unsigned short int commandStatus){
    unsigned long int temp;
    unsigned char byt;
    temp = M.getPacket();//Will pull in the 4 bytes of RGB data
    //Now, massage the hardware to send the data bits for this particular led module
    //Remember, spi writes 8 bits at a time so I need to access the packet a byte at a time
    //but I also need to start at the MSB
    byt = (unsigned char)( 0X000000FF & (temp >> 24));//msb
    if(commandStatus == 1){//i.e. adjust current control registers
        byt |= 0x40;//Add in the control flag to tell the shift Brite LED module that we are talking to the control registers
    }
    spi.write(byt);
    byt = (unsigned char)( 0X000000FF & (temp >> 16));
    spi.write(byt);
    byt = (unsigned char)( 0X000000FF & (temp >> 8));
    spi.write(byt);
    byt = (unsigned char)( 0X000000FF & temp);
    spi.write(byt);
}

//--------------------

void shiftBriteDisplay::priv_reset(){
        sb_reset=0;
        wait(0.1);
        sb_reset=1;
        /*TO DO - init SB current control registers ? */
        wait(0.1); //allow power to settle
}

//--------------------

void shiftBriteDisplay::displayFrame(){
unsigned int i;
    if(f_update == 0){//Prevents doing this if an update is already in progress. This is b/c this member f() is called by an ISR etup in main
        f_update = 1;
        //TO DO - do any harware init e.g., reset the whole chain or enable the display or whatever
        for(i=0; i != moduleCount; i++){
         //do something with module_p[i]
            send(module_p[i]); // transmit the rgb info of this led to the chain
        }
        //Complete the hardware handshaking etc
        priv_SBLatch(); // Latch the complete string of LED data, i.e. the frame
        f_update = 0;
        
        setCurrentCorr();//force a reload of the current correction register values
        
    }else {
       //Do nothing if we are alreay in an update cycle. In fact, for now, destructively chuck the update away.
#ifdef ERRORPRINT
        serial_p->printf("Data Packet Lost\r\n");
#endif
    }
}

void shiftBriteDisplay::rotateLeft(){
     rgbLed temp; //temporary object to hold data
    unsigned int i;
        temp.setRgbLed(module_p[0].getRed(),module_p[0].getGreen(),module_p[0].getBlue());
        for(i=0; i != moduleCount-1; i++){
            setLed(i,module_p[i+1].getRed(),module_p[i+1].getGreen(),module_p[i+1].getBlue());
        }
        setLed(moduleCount-1,temp.getRed(),temp.getGreen(),temp.getBlue());   
}

void shiftBriteDisplay::shiftLeft(){;//info shifted out is lost, 
    unsigned int i;
        for(i=0; i != moduleCount-1; i++){
            setLed(i,module_p[i+1].getRed(),module_p[i+1].getGreen(),module_p[i+1].getBlue());
        }
        setLed(moduleCount-1,0,0,0);   
}

void shiftBriteDisplay::rotateRight(){;
     rgbLed temp; //temporary object to hold data
    unsigned int i;
        temp.setRgbLed(module_p[moduleCount-1].getRed(),module_p[moduleCount-1].getGreen(),module_p[moduleCount-1].getBlue());
        for(i=moduleCount; i != 0; i--){
            setLed(i-1,module_p[i-2].getRed(),module_p[i-2].getGreen(),module_p[i-2].getBlue());
        }
        setLed(0,temp.getRed(),temp.getGreen(),temp.getBlue());   
}
void shiftBriteDisplay::shiftRight(){;//info shifted out is lost
    unsigned int i;
        for(i=moduleCount; i != 0; i--){
            setLed(i-1,module_p[i-2].getRed(),module_p[i-2].getGreen(),module_p[i-2].getBlue());
        }
        setLed(0,0,0,0);   
}
void shiftBriteDisplay::turnOn(){;
    priv_SBEnable();
}
void shiftBriteDisplay::turnOff(){;
    priv_SBDisable();
}

void shiftBriteDisplay::flip(){; //swop positions
    rgbLed temp; //temporary object to hold data
    unsigned int i;
    for(i=0; i != moduleCount/2; i++){
        temp.setRgbLed(module_p[i].getRed(),module_p[i].getGreen(),module_p[i].getBlue());
        setLed(i,module_p[moduleCount-1-i].getRed(),module_p[moduleCount-1-i].getGreen(),module_p[moduleCount-1-i].getBlue());
        setLed(moduleCount-i-1,temp.getRed(),temp.getGreen(),temp.getBlue());        
    }
}

void shiftBriteDisplay::invert(){; //invert colours
    unsigned int i;
    for(i=0; i != moduleCount; i++){
        //module_p[i].getRed();
        setLed(i,1023-module_p[i].getRed(),1023-module_p[i].getGreen(),1023-module_p[i].getBlue());
    }
}

//=============================================================================================