Ryan Savitski
/
LEDsnake
LED screen snake as an example of 48x48 panelspace working.
Diff: ledScreen.h
- Revision:
- 0:b38330b559d4
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ledScreen.h Thu Mar 22 13:20:53 2012 +0000 @@ -0,0 +1,238 @@ +#include "mbed.h" + +/* + collision detection + dot + scoring + growth currently at tail? + + +*/ + +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(p23), // module address 0 + MA1(p24), + 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() { + outputFrame(); +} + + + +void ledScreen::transformFrame(unsigned char* imageSource) +{ + int i=0; + int panseqnum=0, t=0, out=0, x=0, y=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; + + 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++) { + 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 + outputFrame(); //this will recurse only once due to the distrubution of pulses. pulseLength of the next instance will be attached. + } + else + { + // 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. + nextFrameTimer.attach_us(this, &ledScreen::outputFrame, pulseLength); + debug = 0; + } +}