Ryan Savitski
/
LEDsnake
LED screen snake as an example of 48x48 panelspace working.
Embed:
(wiki syntax)
Show/hide line numbers
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 }
Generated on Tue Jul 19 2022 19:39:03 by 1.7.2