9 tile screen working with one image space, platform for development

Dependencies:   mbed

ledScreen.h

Committer:
rsavitski
Date:
2012-03-06
Revision:
0:8b26631e8c70

File content as of revision 0:8b26631e8c70:

#include "mbed.h"

/*
    TODO: wasrunning - works as intended?
    TODO: OPTIME - tweak value

*/

extern "C" void frameout(unsigned char dsVal[], unsigned char transformedSource[]);

class ledScreen {
public:
    ledScreen();
    ~ledScreen() {}

    void transformFrame(unsigned char* imageSource);
    void outputFrame();
    void start();   // start outputting frames on an interrupt

private:

    int MAX_PULSE_WIDTH; // constant: max enable pulse duration
    int pulseLength; // length of current pulse (used in delta-sigma pwm)
    int OP_TIME;

    static const int XPANS = 3; // number of panels horizontally
    static const int YUNITS = 1;
    static const int YPANS = 3; // 3* YUNITS
    static const int PIXPERPAN = 256;

    int running;
    int subFrameCtr;

    Timeout nextFrameTimer; // timeout routine

    // Buffers to hold the RGB data after rearranging to match the LED shifting pattern
    unsigned char transformedSource[3*PIXPERPAN*XPANS*YPANS];

    // Error values for all 256 brightness levels
    unsigned int dsErr[256];
    unsigned int ssdsErr[256];

    // On/off state per sub-frame for all 256 brightness levels
    unsigned char dsVal[256];

    // Precomputed gamma for all 256 brightness levels
    unsigned short gamma[256];


    DigitalOut flatch; // data latch (for all connected panels in parallel)
    DigitalOut MA0; // module address 0
    DigitalOut MA1;
    DigitalOut NREN; // active low enable for red channel (low -> LED on). Note: need to have enable high when latching data
    DigitalOut Rdat0; // red data
    DigitalOut Gdat0; // green data
    DigitalOut Bdat0; // blue data
    DigitalOut Rdat1; // red data
    DigitalOut Gdat1; // green data
    DigitalOut Bdat1; // blue data
    DigitalOut Rdat2; // red data
    DigitalOut Gdat2; // green data
    DigitalOut Bdat2; // blue data
    DigitalOut sclk; // clock
    
    DigitalOut debug;

};

ledScreen::ledScreen() :
        flatch(p10), // data latch (for all connected panels in parallel)
        MA0(p18), // module address 0
        MA1(p19),
        NREN(p9), // active low enable for red channel (low -> LED on). Note: need to have enable high when latching data
        Rdat0(p15), // red data
        Gdat0(p16), // green data
        Bdat0(p17), // blue data
        Rdat1(p7), // red data
        Gdat1(p6), // green data
        Bdat1(p5), // blue data
        Rdat2(p13), // red data
        Gdat2(p12), // green data
        Bdat2(p11), // blue data
        sclk(p14),
        debug(p27) { // clock

    // precompute gamma for every possible RGB intensity value (0-255).
    // Gamma correction with gamma = 3, downshifting by 8 to bring the range of values back to 0-65535
    for (int i=0; i<256; i++) {
        gamma[i] = pow(i, 2.2) * 0.33;//(i*i*i)>>8;
    }

    // initialising lines
    flatch = 1;
    NREN = 1;
    sclk = 1;

    // initialising values
    MAX_PULSE_WIDTH = 512; //must currently be a power of 2, and when changing this, you must change the ssdsErr crossover masking
    pulseLength = MAX_PULSE_WIDTH;
    OP_TIME = 510; //Determined by scoping. Change this every time you change num screens
    //NUM_PANELS = 3

    running=0;
    subFrameCtr=0;

    // initialising errors for delta-sigma
    for (int j=0; j<256; j++) {
        dsErr[j] = 0;
        ssdsErr[j] = 0;
    }

}

void ledScreen::start() {
    running=1;
    outputFrame();
}



void ledScreen::transformFrame(unsigned char* imageSource)
{
    int i=0;
    int panseqnum=0, t=0, out=0, x=0, y=0, MA=0;

    for (int q=0; q < 256*3*3*3; q+=3)
    {
        i = q/3;
        
        x = i % (16*XPANS);
        y = i / (16*XPANS);
        
        
        int MA = (y/16) % 3;
        panseqnum = x/16 + y/(16*3) * XPANS;
       
        if (y%2 == 0)
        {
                t = (y%16)/2*0x20 + ((x%16)/8*0x10+(7-(x%16)%8));
        }
        else
        {
                t = 8 + (y%16)/2*0x20 + ((x%16)/8*0x10+(x%16)%8);
        }
       
        out = 3*(MA * YUNITS * XPANS * 256 + t * XPANS * YUNITS + panseqnum);
        
        transformedSource[out] = imageSource[q];
        transformedSource[out+1] = imageSource[q+1];
        transformedSource[out+2] = imageSource[q+2];
    }

}

// Output one frame and call itself after a period of time if running is set to true
void ledScreen::outputFrame() {

    debug = 1;

    if (pulseLength != MAX_PULSE_WIDTH)
        NREN = 0; // turn off

    if (subFrameCtr<=0) subFrameCtr=36;
    subFrameCtr--;

    if (subFrameCtr == 0) {                  // Every cycle of delta sigma we take a snapshot of the error that needs to be corrected by the short pulses.
        for (int i = 0; i < 256; i++) {      // This is required to eliminate visible flicker due to beat frequencies otherwise created.
            dsErr[i] += ssdsErr[i] & 0xFE000000;
            ssdsErr[i] %= 0x10000;
            ssdsErr[i] += dsErr[i] % (512 * 0x10000);
            dsErr[i] &= 0xFE000000;
        }

        // Doing delta sigma for the snapshot
        for (int i = 0; i <= 9; i++) {
            int lpl = 1<<i;

            if (ssdsErr[i]/0x10000 & lpl)
                ssdsErr[i]-=(0x10000-gamma[i])*lpl;
            else
                ssdsErr[i]+=gamma[i]*lpl;
        }

    }

    // produce pulse lengths of 1, 2, 4, ... 256, spread throughout all subframes (only one in four are not MAX_PULSE_WIDTH long)
    pulseLength = ((subFrameCtr%4)?MAX_PULSE_WIDTH:(1<<(subFrameCtr>>2)));

    for (int i = 0; i < 256; i++) {
        if (pulseLength == MAX_PULSE_WIDTH) {
            // Delta-Sigma modulation with variable pulse length weighting
            // Based on energy dimensions (time * amplitude)
            if (dsErr[i] > (0x10000-gamma[i])*pulseLength) {
                dsVal[i] = 0;//-1; Invert as we are using inverting buffers
                dsErr[i]-=(0x10000-gamma[i])*pulseLength;
            } else {
                dsVal[i] = (unsigned char)-1;
                dsErr[i]+=gamma[i]*pulseLength;
            }
        } else { // if short pulse
            if (ssdsErr[i]/0x10000 & pulseLength) {
                //Doing proper least significant delta sigma live still causes flicker (but only for dim pixels)
                //ssdsErr[i]-=(0x10000-gamma[i])*pulseLength;
                dsVal[i] = 0;
            } else {
                dsVal[i] = (unsigned char)-1;
            }

        }
    }

    // output data
    for (int i = 0; i < 3; i++) { //FIX
        MA0 = !(i&1);
        MA1 = !(i&2);

        frameout(dsVal, &transformedSource[i*256*3*3]);
    }

    NREN = 0; // need to have enables high before every latch, (in case we are on a long pulse)
    flatch = 0; // latching all data to LEDs
    flatch = 1;
    NREN = 1; // turn on LEDs

    if (pulseLength < 4) { // short pulses done through wait
        wait_us(pulseLength);
        NREN = 0; //Turn off LEDs

        bool wasrunning = running;
        running = false;
        outputFrame(); //this will recurse only once due to the distrubution of pulses. pulseLength of the next instance will be attached.
        running = wasrunning;
    }
    // long waits done through attaching an interrupt that will turn off the LEDs at the start of next function call.
    // Meanwhile, the main code can run between the interrupts.
    if (running) nextFrameTimer.attach_us(this, &ledScreen::outputFrame, (pulseLength == MAX_PULSE_WIDTH) ? pulseLength - OP_TIME : pulseLength);
    debug = 0;
}