Ryan Savitski
/
LEDsnake
LED screen snake as an example of 48x48 panelspace working.
Revision 0:b38330b559d4, committed 2012-03-22
- Comitter:
- rsavitski
- Date:
- Thu Mar 22 13:20:53 2012 +0000
- Commit message:
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/frameout.s Thu Mar 22 13:20:53 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 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; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Thu Mar 22 13:20:53 2012 +0000 @@ -0,0 +1,243 @@ +#include "mbed.h" +#include "ledScreen.h" + +// screen instance +ledScreen screen; + +// controller +AnalogIn xJoy(p20); +AnalogIn yJoy(p19); +DigitalIn Abutton(p22); +DigitalIn Bbutton(p21); + +//snake +struct snakeBitPos +{ + signed char x; + signed char y; +}; + +// 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 makeEmptyFrame(unsigned char* data) +{ + for (int x=0; x<3*16; x++) { + for (int y=0; y<3*16; y++) { + + int i = (x + y*(16*3)) * 3; // figure out the memory location + + data[i] = 0; + data[i+1] = 0; + data[i+2] = 0; + } + } + +} + +void stepSnake(unsigned char* data) +{ + static snakeBitPos dotLoc; // going to be the dot + static unsigned short length=2; + static unsigned char dir; + static snakeBitPos snakeArr[100]; + + static bool firstCall=true; + if (firstCall) { snakeArr[0].x=20; snakeArr[0].y=20; firstCall=false; dotLoc.x=rand()%48; dotLoc.y=rand()%48; } + static bool gameover=false; + static short speed_n = 50; + + if (!gameover) + { + + static unsigned short time=0; + time++; + time = time%(speed_n/2); // smaller constant = higher speed + + + // updating direction on every invocation + float xReading = xJoy.read(); + float yReading = yJoy.read(); + + if (xReading > 0.9 && dir != 1) + {dir = 0; } + else if(xReading < 0.3 && dir != 0) + {dir = 1; } + else if(yReading > 0.9 && dir != 3) + {dir = 2; } + else if(yReading < 0.3 && dir != 2) + {dir = 3; } + + + // moving the snake forward + if (time == 0) + { + for (int j=length; j>0; j--) + { + snakeArr[j]=snakeArr[j-1]; + } + switch(dir) + { + case(0): + (snakeArr[0]).x++; + break; + case(1): + (snakeArr[0]).x--; + break; + case(2): + (snakeArr[0]).y++; + break; + case(3): + (snakeArr[0]).y--; + break; + } + + // wrapping + if (snakeArr[0].x==-1) snakeArr[0].x=47; + if (snakeArr[0].x==48) snakeArr[0].x=0; + if (snakeArr[0].y==48) snakeArr[0].y=0; + if (snakeArr[0].y==-1) snakeArr[0].y=47; + + if (snakeArr[0].y==dotLoc.y && snakeArr[0].x==dotLoc.x) + { + dotLoc.x = rand()%48; + dotLoc.y = rand()%48; + length++; + speed_n--; if (speed_n<2) speed_n=2; + } + + // outputting + int i=0; + // killing own tail (no need to zero whole screen) + i = (snakeArr[length].x+(snakeArr[length].y)*48)*3; + data[i] = 0; + data[i+2] = 0; + + // new head alone (no need for looping) + i = (snakeArr[0].x+(snakeArr[0].y)*48)*3; + + // if hitting self, game over + if (data[i]==255) {data[i+1] = 255; gameover=true;} + + data[i] = 255; + data[i+2] = 127; + + data[(dotLoc.x+dotLoc.y*48)*3 +2] = 255; + } + } +} + + +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(); + + makeEmptyFrame(imageSource); + + while (1) { + + stepSnake(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 Thu Mar 22 13:20:53 2012 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/4c0c40fd0593