Ryan Savitski
/
LED_multiple_panels
9 tile screen working with one image space, platform for development
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; }