Ryan Savitski
/
LED_multiple_panels
9 tile screen working with one image space, platform for development
Revision 0:8b26631e8c70, committed 2012-03-06
- Comitter:
- rsavitski
- Date:
- Tue Mar 06 19:58:34 2012 +0000
- Commit message:
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/frameout.s Tue Mar 06 19:58:34 2012 +0000 @@ -0,0 +1,146 @@ + AREA framefunc, CODE, READONLY +; Export my_asm function location so that C compiler can find it and link + EXPORT frameout +frameout + +; r0 - dsVal array base address +; r1 - data array base address +; r2 - base address for port 0 +; r3 - loop counter +; r4 - sclk bitmask +; r5 - RGB channels bitmask +; r6 - data reg 1 +; r7 - data reg 2 +; r8 - temporary value holding intensity info (used as an offset into dsVal array) + + +; Save state + PUSH {R4, R5, R6, R7, R8} + +; Load GPIO Port 0 base address in register R6 + LDR R2, =0x2009C000 ; GPIO port 0 base address + +; R3 - loop counter + MOV R3, #256 + +; sclk mask + MOV R4, #0x00010000 ; sclk bitmask + +LOOP + +;NOTE clocking swapped due to inverter used for logic levels + + MOV R5, #0x03800000 ; bitmask for RGB bits + STR R4, [R2, #0x1C] ; cleark clk to clock in data (negative edge) + + + STR R5, [R2, #0x1C] ; clear RGB via FIOCLEAR + + ; R channel + LDRB R8, [R1], #1 ; read data word (0-255 intensity) and post-increment array pointer to point to next word in data + LDRB R6, [R0, R8] ; loading on/off state from dsVal, pointed to by offset equal to intensity of data + + + ; G channel + LDRB R8, [R1], #1 ; read data word (0-255 intensity) into R8 and post-increment to point to next word in data + LDRB R7, [R0, R8] ; loading on/off state from dsVal, pointed to by offset equal to intensity of data + + AND R7, R7, #0x2 ; pick blue channel bit + + ORR R6, R7, R6, lsr #7 ; store RG bits in R6 with clever stuff in R chan + + ; B channel + LDRB R8, [R1], #1 ; read data word (0-255 intensity) into R8 and post-increment to point to next word in data + LDRB R7, [R0, R8] ; loading on/off state from dsVal, pointed to by offset equal to intensity of data + + AND R7, R7, #0x4 ; pick green channel bit + + ; writing to output + ORR R6, R6, R7 ; RGB bits in R6 + + MOV R6, R6, lsl #23 + + STR R6, [R2,#0x18] ; FIOSET on/off for LED + + ;SUB R1, R1, #3 + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + + MOV R5, #0x00000380 ; bitmask for RGB bits + + STR R5, [R2, #0x1C] ; clear RGB via FIOCLEAR + + ; R channel + LDRB R8, [R1], #1 ; read data word (0-255 intensity) and post-increment array pointer to point to next word in data + LDRB R6, [R0, R8] ; loading on/off state from dsVal, pointed to by offset equal to intensity of data + + + ; G channel + LDRB R8, [R1], #1 ; read data word (0-255 intensity) into R8 and post-increment to point to next word in data + LDRB R7, [R0, R8] ; loading on/off state from dsVal, pointed to by offset equal to intensity of data + + AND R7, R7, #0x2 ; pick blue channel bit + + ORR R6, R7, R6, lsr #7 ; store RG bits in R6 with clever stuff in R chan + + ; B channel + LDRB R8, [R1], #1 ; read data word (0-255 intensity) into R8 and post-increment to point to next word in data + LDRB R7, [R0, R8] ; loading on/off state from dsVal, pointed to by offset equal to intensity of data + + AND R7, R7, #0x4 ; pick green channel bit + + ; writing to output + ORR R6, R6, R7 ; RGB bits in R6 + + MOV R6, R6, lsl #7 + + STR R6, [R2,#0x18] ; FIOSET on/off for LED + + ;SUB R1, R1, #3 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + + MOV R5, #0x00068000 ; bitmask for RGB bits + + STR R5, [R2, #0x1C] ; clear RGB via FIOCLEAR + + ; R channel + LDRB R8, [R1], #1 ; read data word (0-255 intensity) and post-increment array pointer to point to next word in data + LDRB R6, [R0, R8] ; loading on/off state from dsVal, pointed to by offset equal to intensity of data + + + ; G channel + LDRB R8, [R1], #1 ; read data word (0-255 intensity) into R8 and post-increment to point to next word in data + LDRB R7, [R0, R8] ; loading on/off state from dsVal, pointed to by offset equal to intensity of data + + AND R7, R7, #0x4 ; pick blue channel bit + + ORR R6, R7, R6, lsr #7 ; store RG bits in R6 with clever stuff in R chan + + ; B channel + LDRB R8, [R1], #1 ; read data word (0-255 intensity) into R8 and post-increment to point to next word in data + LDRB R7, [R0, R8] ; loading on/off state from dsVal, pointed to by offset equal to intensity of data + + AND R7, R7, #0x8 ; pick green channel bit + + ; writing to output + ORR R6, R6, R7 ; RGB bits in R6 + + MOV R6, R6, lsl #15 + + STR R6, [R2,#0x18] ; FIOSET on/off for LED + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + SUBS R3, R3, #0x1 ; decrement loop counter + + STR R4, [R2, #0x18] ; setting clock high + + + BNE LOOP + + POP {R4, R5, R6, R7, R8} + + BX LR + END
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ledScreen.h Tue Mar 06 19:58:34 2012 +0000 @@ -0,0 +1,238 @@ +#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; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Tue Mar 06 19:58:34 2012 +0000 @@ -0,0 +1,121 @@ +#include "mbed.h" +#include "ledScreen.h" + +// screen instance +ledScreen screen; + +// sin lookup table and related functions +unsigned char sinlut[256]; + +void initSinLut() { + for (int i=0; i<256; i++) + sinlut[i] = cos((float)i / 256.0 * (3.14159265 * 2))*127 + 128; +} + +inline unsigned char lut_sin(int x) { + + return (x>0)?sinlut[x%256]:sinlut[(-x)%256]; +} + + +// Example frame makes: + + +//rainbow +void makeFrame1(unsigned char* data) { + + static int time=0; + time++; + + // override data with a intensity gradient test pattern + for (int x=0; x<3*16; x++) { + for (int y=0; y<48; y++) { + + int i = (x + y*(16*3)) * 3; // figure out the memory location + + data[i] = lut_sin((x+y)*255/48+(time/2)%256); //(sin((float)(x+time)/15.0)+1.0)*128 ; // red + data[i+1] = lut_sin(((x+y)*255/48+(time/2)+ 85)%256); //(sin((float)(x+time)/15.0)+1.0)*128 ; // red + data[i+2] = lut_sin(((x+y)*255/48+(time/2)+170)%256); //(sin((float)(x+time)/15.0)+1.0)*128 ; // red + } + } + +} + +//cool lines +void makeFrame2(unsigned char* data) { + + static int time=0; + time++; + + // override data with a intensity gradient test pattern + for (int x=0; x<3*16; x++) { + for (int y=0; y<16; y++) { + + int i = (x + y*(16*3)) * 3; // figure out the memory location + + data[i] = lut_sin(x*255/48+(time)%256 + y*16); //(sin((float)(x+time)/15.0)+1.0)*128 ; // red + data[i+1] = lut_sin(x*255/48+(time)%256 + y*16 + 85); //(sin((float)(x+time)/15.0)+1.0)*128 ; // red + data[i+2] = lut_sin(x*255/48+(time)%256 + y*16 + 170); //(sin((float)(x+time)/15.0)+1.0)*128 ; // red + } + } + +} + +// static gradients +void makeFrame3(unsigned char* data) { + + static int time=0; + time++; + + // override data with a intensity gradient test pattern + for (int x=0; x<3*16; x++) { + for (int y=0; y<16; y++) { + + int i = (x + y*(16*3)) * 3; // figure out the memory location + + data[i] = x*256/48; //(sin((float)(x+time)/15.0)+1.0)*128 ; // red + data[i+1] = 0; // green + data[i+2] = y*256/16; //(i/3)%256 ; // blue + } + } + +} + + +void makeFrame33(unsigned char* data) { + + static int time=0; + time++; + + for (int x=0; x<3*16; x++) { + for (int y=0; y<48; y++) { + + int i = (x + y*(16*3)) * 3; // figure out the memory location + + data[i] = x*256/48; //(sin((float)(x+time)/15.0)+1.0)*128 ; // red + data[i+1] = 0; // green + data[i+2] = y*256/48; //(i/3)%256 ; // blue + } + } +} + +int main() +{ + + // framebuffer on client side + unsigned char imageSource[256*3*3*3] = { 0 }; + + // prepare sin lookup table (optional) + initSinLut(); + + // start the screen output, which will keep outputting the frames that are in its internal buffer (updated via .transformFrame) + screen.start(); + + while (1) { + + makeFrame1(imageSource); // prepare framebuffer with current frame + screen.transformFrame(imageSource); // write framebuffer to output framebuffer + wait_ms(0.5); // slow down the framerate (optional) + } + +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Tue Mar 06 19:58:34 2012 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/4c0c40fd0593