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

Dependencies:   mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ledScreen.h Source File

ledScreen.h

00001 #include "mbed.h"
00002 
00003 /*
00004     collision detection
00005     dot
00006     scoring
00007     growth currently at tail?
00008     
00009 
00010 */
00011 
00012 extern "C" void frameout(unsigned char dsVal[], unsigned char transformedSource[]);
00013 
00014 class ledScreen {
00015 public:
00016     ledScreen();
00017     ~ledScreen() {}
00018 
00019     void transformFrame(unsigned char* imageSource);
00020     void outputFrame();
00021     void start();   // start outputting frames on an interrupt
00022 
00023 private:
00024 
00025     int MAX_PULSE_WIDTH; // constant: max enable pulse duration
00026     int pulseLength; // length of current pulse (used in delta-sigma pwm)
00027     int OP_TIME;
00028 
00029     static const int XPANS = 3; // number of panels horizontally
00030     static const int YUNITS = 1;
00031     static const int YPANS = 3; // 3* YUNITS
00032     static const int PIXPERPAN = 256;
00033 
00034     int running;
00035     int subFrameCtr;
00036 
00037     Timeout nextFrameTimer; // timeout routine
00038 
00039     // Buffers to hold the RGB data after rearranging to match the LED shifting pattern
00040     unsigned char transformedSource[3*PIXPERPAN*XPANS*YPANS];
00041 
00042     // Error values for all 256 brightness levels
00043     unsigned int dsErr[256];
00044     unsigned int ssdsErr[256];
00045 
00046     // On/off state per sub-frame for all 256 brightness levels
00047     unsigned char dsVal[256];
00048 
00049     // Precomputed gamma for all 256 brightness levels
00050     unsigned short gamma[256];
00051 
00052 
00053     DigitalOut flatch; // data latch (for all connected panels in parallel)
00054     DigitalOut MA0; // module address 0
00055     DigitalOut MA1;
00056     DigitalOut NREN; // active low enable for red channel (low -> LED on). Note: need to have enable high when latching data
00057     DigitalOut Rdat0; // red data
00058     DigitalOut Gdat0; // green data
00059     DigitalOut Bdat0; // blue data
00060     DigitalOut Rdat1; // red data
00061     DigitalOut Gdat1; // green data
00062     DigitalOut Bdat1; // blue data
00063     DigitalOut Rdat2; // red data
00064     DigitalOut Gdat2; // green data
00065     DigitalOut Bdat2; // blue data
00066     DigitalOut sclk; // clock
00067     
00068     DigitalOut debug;
00069 
00070 };
00071 
00072 ledScreen::ledScreen() :
00073         flatch(p10), // data latch (for all connected panels in parallel)
00074         MA0(p23), // module address 0
00075         MA1(p24),
00076         NREN(p9), // active low enable for red channel (low -> LED on). Note: need to have enable high when latching data
00077         Rdat0(p15), // red data
00078         Gdat0(p16), // green data
00079         Bdat0(p17), // blue data
00080         Rdat1(p7), // red data
00081         Gdat1(p6), // green data
00082         Bdat1(p5), // blue data
00083         Rdat2(p13), // red data
00084         Gdat2(p12), // green data
00085         Bdat2(p11), // blue data
00086         sclk(p14),
00087         debug(p27) { // clock
00088 
00089     // precompute gamma for every possible RGB intensity value (0-255).
00090     // Gamma correction with gamma = 3, downshifting by 8 to bring the range of values back to 0-65535
00091     for (int i=0; i<256; i++) {
00092         gamma[i] = pow(i, 2.2) * 0.33;//(i*i*i)>>8;
00093     }
00094 
00095     // initialising lines
00096     flatch = 1;
00097     NREN = 1;
00098     sclk = 1;
00099 
00100     // initialising values
00101     MAX_PULSE_WIDTH = 512; //must currently be a power of 2, and when changing this, you must change the ssdsErr crossover masking
00102     pulseLength = MAX_PULSE_WIDTH;
00103     OP_TIME = 510; //Determined by scoping. Change this every time you change num screens
00104     //NUM_PANELS = 3
00105 
00106     running=0;
00107     subFrameCtr=0;
00108 
00109     // initialising errors for delta-sigma
00110     for (int j=0; j<256; j++) {
00111         dsErr[j] = 0;
00112         ssdsErr[j] = 0;
00113     }
00114 
00115 }
00116 
00117 void ledScreen::start() {
00118     outputFrame();
00119 }
00120 
00121 
00122 
00123 void ledScreen::transformFrame(unsigned char* imageSource)
00124 {
00125     int i=0;
00126     int panseqnum=0, t=0, out=0, x=0, y=0;
00127 
00128     for (int q=0; q < 256*3*3*3; q+=3)
00129     {
00130         i = q/3;
00131         
00132         x = i % (16*XPANS);
00133         y = i / (16*XPANS);
00134         
00135         
00136         int MA = (y/16) % 3;
00137         panseqnum = x/16 + y/(16*3) * XPANS;
00138        
00139         if (y%2 == 0)
00140         {
00141                 t = (y%16)/2*0x20 + ((x%16)/8*0x10+(7-(x%16)%8));
00142         }
00143         else
00144         {
00145                 t = 8 + (y%16)/2*0x20 + ((x%16)/8*0x10+(x%16)%8);
00146         }
00147        
00148         out = 3*(MA * YUNITS * XPANS * 256 + t * XPANS * YUNITS + panseqnum);
00149         
00150         transformedSource[out] = imageSource[q];
00151         transformedSource[out+1] = imageSource[q+1];
00152         transformedSource[out+2] = imageSource[q+2];
00153     }
00154 
00155 }
00156 
00157 // Output one frame and call itself after a period of time if running is set to true
00158 void ledScreen::outputFrame() {
00159 
00160     debug = 1;
00161     
00162     NREN = 0; // turn off
00163 
00164     if (subFrameCtr<=0) subFrameCtr=36;
00165     subFrameCtr--;
00166 
00167     if (subFrameCtr == 0) {                  // Every cycle of delta sigma we take a snapshot of the error that needs to be corrected by the short pulses.
00168         for (int i = 0; i < 256; i++) {      // This is required to eliminate visible flicker due to beat frequencies otherwise created.
00169             dsErr[i] += ssdsErr[i] & 0xFE000000;
00170             ssdsErr[i] %= 0x10000;
00171             ssdsErr[i] += dsErr[i] % (512 * 0x10000);
00172             dsErr[i] &= 0xFE000000;
00173         }
00174 
00175         // Doing delta sigma for the snapshot
00176         for (int i = 0; i <= 9; i++) {
00177             int lpl = 1<<i;
00178 
00179             if (ssdsErr[i]/0x10000 & lpl)
00180                 ssdsErr[i]-=(0x10000-gamma[i])*lpl;
00181             else
00182                 ssdsErr[i]+=gamma[i]*lpl;
00183         }
00184 
00185     }
00186 
00187     // produce pulse lengths of 1, 2, 4, ... 256, spread throughout all subframes (only one in four are not MAX_PULSE_WIDTH long)
00188     pulseLength = ((subFrameCtr%4)?MAX_PULSE_WIDTH:(1<<(subFrameCtr>>2)));
00189 
00190     for (int i = 0; i < 256; i++) {
00191         if (pulseLength == MAX_PULSE_WIDTH) {
00192             // Delta-Sigma modulation with variable pulse length weighting
00193             // Based on energy dimensions (time * amplitude)
00194             if (dsErr[i] > (0x10000-gamma[i])*pulseLength) {
00195                 dsVal[i] = 0;//-1; Invert as we are using inverting buffers
00196                 dsErr[i]-=(0x10000-gamma[i])*pulseLength;
00197             } else {
00198                 dsVal[i] = (unsigned char)-1;
00199                 dsErr[i]+=gamma[i]*pulseLength;
00200             }
00201         } else { // if short pulse
00202             if (ssdsErr[i]/0x10000 & pulseLength) {
00203                 //Doing proper least significant delta sigma live still causes flicker (but only for dim pixels)
00204                 //ssdsErr[i]-=(0x10000-gamma[i])*pulseLength;
00205                 dsVal[i] = 0;
00206             } else {
00207                 dsVal[i] = (unsigned char)-1;
00208             }
00209 
00210         }
00211     }
00212 
00213     // output data
00214     for (int i = 0; i < 3; i++) {
00215         MA0 = !(i&1);
00216         MA1 = !(i&2);
00217 
00218         frameout(dsVal, &transformedSource[i*256*3*3]);
00219     }
00220 
00221     NREN = 0; // need to have enables high before every latch, (in case we are on a long pulse)
00222     flatch = 0; // latching all data to LEDs
00223     flatch = 1;
00224     NREN = 1; // turn on LEDs
00225 
00226     if (pulseLength < 4) { // short pulses done through wait
00227         wait_us(pulseLength);
00228         NREN = 0; //Turn off LEDs
00229         outputFrame(); //this will recurse only once due to the distrubution of pulses. pulseLength of the next instance will be attached.
00230     }
00231     else
00232     {
00233         // long waits done through attaching an interrupt that will turn off the LEDs at the start of next function call.
00234         // Meanwhile, the main code can run between the interrupts.
00235         nextFrameTimer.attach_us(this, &ledScreen::outputFrame, pulseLength);
00236         debug = 0;
00237     }
00238 }