LED screen snake as an example of 48x48 panelspace working.

Dependencies:   mbed

Files at this revision

API Documentation at this revision

Comitter:
rsavitski
Date:
Thu Mar 22 13:20:53 2012 +0000
Commit message:

Changed in this revision

frameout.s Show annotated file Show diff for this revision Revisions of this file
ledScreen.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
--- /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