LED screen driver build for hackspace.

Dependencies:   mbed

Committer:
madcowswe
Date:
Wed Feb 29 17:09:46 2012 +0000
Revision:
1:1af5060b2a34
Parent:
0:f16a1d69a386

        

Who changed what in which revision?

UserRevisionLine numberNew contents of line
madcowswe 0:f16a1d69a386 1 #include "mbed.h"
madcowswe 0:f16a1d69a386 2
madcowswe 0:f16a1d69a386 3 /*
madcowswe 0:f16a1d69a386 4 ******************************************************
madcowswe 0:f16a1d69a386 5 *Hacked together by Imperial College Robotics Society*
madcowswe 0:f16a1d69a386 6 ******************************************************
madcowswe 0:f16a1d69a386 7
madcowswe 0:f16a1d69a386 8 Usage:
madcowswe 0:f16a1d69a386 9 Example code supplied (main.cpp). Format of data input: 8bit RGB channels per pixel sequential in one framebuffer.
madcowswe 0:f16a1d69a386 10
madcowswe 0:f16a1d69a386 11 Note:
madcowswe 0:f16a1d69a386 12 This version uses inverted outputs (to bring voltage from 3.3 to 5v and drive more current) for data, clock and address lines.
madcowswe 0:f16a1d69a386 13
madcowswe 0:f16a1d69a386 14 Quirks of current platform:
madcowswe 1:1af5060b2a34 15 transformFrame crammed into the code, should be united with transformFrame2 for proper orientation
madcowswe 1:1af5060b2a34 16
madcowswe 0:f16a1d69a386 17 address lines are put through inverters, code still thinks it's addressing with logic 1's,
madcowswe 0:f16a1d69a386 18 whereas in reality MA0 is inverted and put into the ribbon cable line of MA1 and vice-versa
madcowswe 0:f16a1d69a386 19
madcowswe 0:f16a1d69a386 20 */
madcowswe 0:f16a1d69a386 21
madcowswe 0:f16a1d69a386 22 extern "C" void frameout(unsigned char dsVal[], unsigned char transformedSource[]);
madcowswe 0:f16a1d69a386 23
madcowswe 0:f16a1d69a386 24 class ledScreen {
madcowswe 0:f16a1d69a386 25 public:
madcowswe 0:f16a1d69a386 26 ledScreen();
madcowswe 0:f16a1d69a386 27 ~ledScreen() {}
madcowswe 0:f16a1d69a386 28
madcowswe 0:f16a1d69a386 29 void transformFrame(unsigned char* imageSource);
madcowswe 0:f16a1d69a386 30 void transformFrame2(unsigned char* imageSource);
madcowswe 0:f16a1d69a386 31 void outputFrame();
madcowswe 0:f16a1d69a386 32 void start(); // start outputting frames on an interrupt
madcowswe 0:f16a1d69a386 33
madcowswe 0:f16a1d69a386 34 private:
madcowswe 0:f16a1d69a386 35
madcowswe 0:f16a1d69a386 36 int MAX_PULSE_WIDTH; // constant: max enable pulse duration
madcowswe 0:f16a1d69a386 37 int pulseLength; // length of current pulse (used in delta-sigma pwm)
madcowswe 0:f16a1d69a386 38 int OP_TIME;
madcowswe 0:f16a1d69a386 39
madcowswe 0:f16a1d69a386 40 static const int NUM_PANELS = 3; // number of panels horizontally
madcowswe 0:f16a1d69a386 41
madcowswe 0:f16a1d69a386 42 int running;
madcowswe 0:f16a1d69a386 43 int subFrameCtr;
madcowswe 0:f16a1d69a386 44
madcowswe 0:f16a1d69a386 45 Timeout nextFrameTimer; // timeout routine
madcowswe 0:f16a1d69a386 46
madcowswe 0:f16a1d69a386 47 // Buffers to hold the RGB data after rearranging to match the LED shifting pattern
madcowswe 0:f16a1d69a386 48 unsigned char transformedSource[256*3*NUM_PANELS];
madcowswe 0:f16a1d69a386 49
madcowswe 0:f16a1d69a386 50 // Error values for all 256 brightness levels
madcowswe 0:f16a1d69a386 51 unsigned int dsErr[256];
madcowswe 0:f16a1d69a386 52 unsigned int ssdsErr[256];
madcowswe 0:f16a1d69a386 53
madcowswe 0:f16a1d69a386 54 // On/off state per sub-frame for all 256 brightness levels
madcowswe 0:f16a1d69a386 55 unsigned char dsVal[256];
madcowswe 0:f16a1d69a386 56
madcowswe 0:f16a1d69a386 57 // Precomputed gamma for all 256 brightness levels
madcowswe 0:f16a1d69a386 58 unsigned short gamma[256];
madcowswe 0:f16a1d69a386 59
madcowswe 0:f16a1d69a386 60
madcowswe 0:f16a1d69a386 61 DigitalOut flatch; // data latch (for all connected panels in parallel)
madcowswe 0:f16a1d69a386 62 DigitalOut MA0; // module address 0
madcowswe 0:f16a1d69a386 63 DigitalOut MA1;
madcowswe 0:f16a1d69a386 64 DigitalOut NREN; // active low enable for red channel (low -> LED on). Note: need to have enable high when latching data
madcowswe 0:f16a1d69a386 65 DigitalOut Rdat; // red data
madcowswe 0:f16a1d69a386 66 DigitalOut Gdat; // green data
madcowswe 0:f16a1d69a386 67 DigitalOut Bdat; // blue data
madcowswe 0:f16a1d69a386 68 DigitalOut sclk; // clock
madcowswe 0:f16a1d69a386 69
madcowswe 0:f16a1d69a386 70 };
madcowswe 0:f16a1d69a386 71
madcowswe 0:f16a1d69a386 72 ledScreen::ledScreen() :
madcowswe 0:f16a1d69a386 73 flatch(p11), // data latch (for all connected panels in parallel)
madcowswe 0:f16a1d69a386 74 MA0(p18), // module address 0
madcowswe 0:f16a1d69a386 75 MA1(p19),
madcowswe 0:f16a1d69a386 76 NREN(p12), // active low enable for red channel (low -> LED on). Note: need to have enable high when latching data
madcowswe 0:f16a1d69a386 77 Rdat(p15), // red data
madcowswe 0:f16a1d69a386 78 Gdat(p16), // green data
madcowswe 0:f16a1d69a386 79 Bdat(p17), // blue data
madcowswe 0:f16a1d69a386 80 sclk(p14) { // clock
madcowswe 0:f16a1d69a386 81
madcowswe 0:f16a1d69a386 82 // precompute gamma for every possible RGB intensity value (0-255).
madcowswe 0:f16a1d69a386 83 // Gamma correction with gamma = 3, downshifting by 8 to bring the range of values back to 0-65535
madcowswe 0:f16a1d69a386 84 for (int i=0; i<256; i++) {
madcowswe 0:f16a1d69a386 85 gamma[i] = pow(i, 2.2) * 0.33;//(i*i*i)>>8;
madcowswe 0:f16a1d69a386 86 }
madcowswe 0:f16a1d69a386 87
madcowswe 0:f16a1d69a386 88 // initialising lines
madcowswe 0:f16a1d69a386 89 flatch = 0;
madcowswe 0:f16a1d69a386 90 NREN = 0;
madcowswe 0:f16a1d69a386 91 sclk = 0;
madcowswe 0:f16a1d69a386 92
madcowswe 0:f16a1d69a386 93 // initialising values
madcowswe 0:f16a1d69a386 94 MAX_PULSE_WIDTH = 512; //must currently be a power of 2, and when changing this, you must change the ssdsErr crossover masking
madcowswe 0:f16a1d69a386 95 pulseLength = MAX_PULSE_WIDTH;
madcowswe 0:f16a1d69a386 96 OP_TIME = 345; //Determined by scoping. Change this every time you change num screens
madcowswe 0:f16a1d69a386 97 //NUM_PANELS = 3
madcowswe 0:f16a1d69a386 98
madcowswe 0:f16a1d69a386 99 running=0;
madcowswe 0:f16a1d69a386 100 subFrameCtr=0;
madcowswe 0:f16a1d69a386 101
madcowswe 0:f16a1d69a386 102 // initialising errors for delta-sigma
madcowswe 0:f16a1d69a386 103 for (int j=0; j<256; j++) {
madcowswe 0:f16a1d69a386 104 dsErr[j] = 0;
madcowswe 0:f16a1d69a386 105 ssdsErr[j] = 0;
madcowswe 0:f16a1d69a386 106 }
madcowswe 0:f16a1d69a386 107
madcowswe 0:f16a1d69a386 108 }
madcowswe 0:f16a1d69a386 109
madcowswe 0:f16a1d69a386 110 void ledScreen::start() {
madcowswe 0:f16a1d69a386 111 running=1;
madcowswe 0:f16a1d69a386 112 outputFrame();
madcowswe 0:f16a1d69a386 113 }
madcowswe 0:f16a1d69a386 114
madcowswe 0:f16a1d69a386 115
madcowswe 0:f16a1d69a386 116 void ledScreen::transformFrame2(unsigned char* imageSource) {
madcowswe 0:f16a1d69a386 117
madcowswe 0:f16a1d69a386 118 int psp = 0; // panel space pointer
madcowswe 0:f16a1d69a386 119 int tpsp = 0; // target psp
madcowswe 0:f16a1d69a386 120 int isp = 8 * 3; // imag espace pointer
madcowswe 0:f16a1d69a386 121 // preprocessing the image data to match shifting pattern
madcowswe 0:f16a1d69a386 122 for (int panel = 0; panel < NUM_PANELS; panel++) {
madcowswe 0:f16a1d69a386 123 // int base = panel * 0x20; //i - image space*/
madcowswe 0:f16a1d69a386 124
madcowswe 0:f16a1d69a386 125 for (int drows = 0; drows < 8; drows++) {
madcowswe 0:f16a1d69a386 126 //for (int m = 0; m < 0x1F; m++) { //m - quad block in panel space
madcowswe 0:f16a1d69a386 127
madcowswe 0:f16a1d69a386 128 for (tpsp = psp + (8*3); psp < tpsp;) { //0th quad
madcowswe 0:f16a1d69a386 129 isp -= 3;
madcowswe 0:f16a1d69a386 130 transformedSource[psp++] = imageSource[isp];
madcowswe 0:f16a1d69a386 131 transformedSource[psp++] = imageSource[isp+1];
madcowswe 0:f16a1d69a386 132 transformedSource[psp++] = imageSource[isp+2];
madcowswe 0:f16a1d69a386 133 }
madcowswe 0:f16a1d69a386 134
madcowswe 0:f16a1d69a386 135 //next row
madcowswe 0:f16a1d69a386 136 isp += 0x10 * NUM_PANELS * 3;
madcowswe 0:f16a1d69a386 137 for (tpsp = psp + (8*3); psp < tpsp; isp+=3) { //2nd quad
madcowswe 0:f16a1d69a386 138 transformedSource[psp++] = imageSource[isp];
madcowswe 0:f16a1d69a386 139 transformedSource[psp++] = imageSource[isp+1];
madcowswe 0:f16a1d69a386 140 transformedSource[psp++] = imageSource[isp+2];
madcowswe 0:f16a1d69a386 141 }
madcowswe 0:f16a1d69a386 142
madcowswe 0:f16a1d69a386 143 //previous row
madcowswe 0:f16a1d69a386 144 isp -= (0x10 * NUM_PANELS - 8) * 3;
madcowswe 0:f16a1d69a386 145 for (tpsp = psp + (8*3); psp < tpsp;) { //1st quad
madcowswe 0:f16a1d69a386 146 isp-=3;
madcowswe 0:f16a1d69a386 147 transformedSource[psp++] = imageSource[isp];
madcowswe 0:f16a1d69a386 148 transformedSource[psp++] = imageSource[isp+1];
madcowswe 0:f16a1d69a386 149 transformedSource[psp++] = imageSource[isp+2];
madcowswe 0:f16a1d69a386 150 }
madcowswe 0:f16a1d69a386 151 isp += 0x10 * NUM_PANELS * 3;
madcowswe 0:f16a1d69a386 152 for (tpsp = psp + (8*3); psp < tpsp; isp+=3) { //3rd quad
madcowswe 0:f16a1d69a386 153 transformedSource[psp++] = imageSource[isp];
madcowswe 0:f16a1d69a386 154 transformedSource[psp++] = imageSource[isp+1];
madcowswe 0:f16a1d69a386 155 transformedSource[psp++] = imageSource[isp+2];
madcowswe 0:f16a1d69a386 156
madcowswe 0:f16a1d69a386 157 }
madcowswe 0:f16a1d69a386 158 isp += (0x10 * NUM_PANELS - 8) * 3;
madcowswe 0:f16a1d69a386 159 }
madcowswe 0:f16a1d69a386 160
madcowswe 0:f16a1d69a386 161 isp += (0x10 - 0x20 * NUM_PANELS * 8) * 3;
madcowswe 0:f16a1d69a386 162 }
madcowswe 0:f16a1d69a386 163 }
madcowswe 0:f16a1d69a386 164
madcowswe 0:f16a1d69a386 165 void ledScreen::transformFrame(unsigned char* imageSource) {
madcowswe 0:f16a1d69a386 166 unsigned char rotatedSource[256*3*NUM_PANELS];
madcowswe 0:f16a1d69a386 167
madcowswe 0:f16a1d69a386 168 for (int panels = 0; panels < 3; panels++){
madcowswe 0:f16a1d69a386 169 for (int x = 0; x<16; x++){
madcowswe 0:f16a1d69a386 170 for (int y = 0; y<16; y++){
madcowswe 0:f16a1d69a386 171 for (int c = 0; c < 3; c++){
madcowswe 0:f16a1d69a386 172 rotatedSource[((15-x) + panels*16 + 48*y)*3 + c] = imageSource[(y + panels*16 + 48*x)*3 + c];
madcowswe 0:f16a1d69a386 173 }
madcowswe 0:f16a1d69a386 174 }
madcowswe 0:f16a1d69a386 175 }
madcowswe 0:f16a1d69a386 176 }
madcowswe 0:f16a1d69a386 177
madcowswe 0:f16a1d69a386 178 transformFrame2(rotatedSource);
madcowswe 0:f16a1d69a386 179 }
madcowswe 0:f16a1d69a386 180
madcowswe 0:f16a1d69a386 181 // Output one frame and call itself after a period of time if running is set to true
madcowswe 0:f16a1d69a386 182 void ledScreen::outputFrame() {
madcowswe 0:f16a1d69a386 183
madcowswe 0:f16a1d69a386 184 if (pulseLength != MAX_PULSE_WIDTH)
madcowswe 0:f16a1d69a386 185 NREN = 1; // turn off
madcowswe 0:f16a1d69a386 186
madcowswe 0:f16a1d69a386 187 if (subFrameCtr<=0) subFrameCtr=36;
madcowswe 0:f16a1d69a386 188 subFrameCtr--;
madcowswe 0:f16a1d69a386 189
madcowswe 0:f16a1d69a386 190 if (subFrameCtr == 0) { // Every cycle of delta sigma we take a snapshot of the error that needs to be corrected by the short pulses.
madcowswe 0:f16a1d69a386 191 for (int i = 0; i < 256; i++) { // This is required to eliminate visible flicker due to beat frequencies otherwise created.
madcowswe 0:f16a1d69a386 192 dsErr[i] += ssdsErr[i] & 0xFE000000;
madcowswe 0:f16a1d69a386 193 ssdsErr[i] %= 0x10000;
madcowswe 0:f16a1d69a386 194 ssdsErr[i] += dsErr[i] % (512 * 0x10000);
madcowswe 0:f16a1d69a386 195 dsErr[i] &= 0xFE000000;
madcowswe 0:f16a1d69a386 196 }
madcowswe 0:f16a1d69a386 197
madcowswe 0:f16a1d69a386 198 // Doing delta sigma for the snapshot
madcowswe 0:f16a1d69a386 199 for (int i = 0; i <= 9; i++) {
madcowswe 0:f16a1d69a386 200 int lpl = 1<<i;
madcowswe 0:f16a1d69a386 201
madcowswe 0:f16a1d69a386 202 if (ssdsErr[i]/0x10000 & lpl)
madcowswe 0:f16a1d69a386 203 ssdsErr[i]-=(0x10000-gamma[i])*lpl;
madcowswe 0:f16a1d69a386 204 else
madcowswe 0:f16a1d69a386 205 ssdsErr[i]+=gamma[i]*lpl;
madcowswe 0:f16a1d69a386 206 }
madcowswe 0:f16a1d69a386 207
madcowswe 0:f16a1d69a386 208 }
madcowswe 0:f16a1d69a386 209
madcowswe 0:f16a1d69a386 210 // produce pulse lengths of 1, 2, 4, ... 256, spread throughout all subframes (only one in four are not MAX_PULSE_WIDTH long)
madcowswe 0:f16a1d69a386 211 pulseLength = ((subFrameCtr%4)?MAX_PULSE_WIDTH:(1<<(subFrameCtr>>2)));
madcowswe 0:f16a1d69a386 212
madcowswe 0:f16a1d69a386 213 for (int i = 0; i < 256; i++) {
madcowswe 0:f16a1d69a386 214 if (pulseLength == MAX_PULSE_WIDTH) {
madcowswe 0:f16a1d69a386 215 // Delta-Sigma modulation with variable pulse length weighting
madcowswe 0:f16a1d69a386 216 // Based on energy dimensions (time * amplitude)
madcowswe 0:f16a1d69a386 217 if (dsErr[i] > (0x10000-gamma[i])*pulseLength) {
madcowswe 0:f16a1d69a386 218 dsVal[i] = 0;//-1; Invert as we are using inverting buffers
madcowswe 0:f16a1d69a386 219 dsErr[i]-=(0x10000-gamma[i])*pulseLength;
madcowswe 0:f16a1d69a386 220 } else {
madcowswe 0:f16a1d69a386 221 dsVal[i] = (unsigned char)-1;
madcowswe 0:f16a1d69a386 222 dsErr[i]+=gamma[i]*pulseLength;
madcowswe 0:f16a1d69a386 223 }
madcowswe 0:f16a1d69a386 224 } else { // if short pulse
madcowswe 0:f16a1d69a386 225 if (ssdsErr[i]/0x10000 & pulseLength) {
madcowswe 0:f16a1d69a386 226 //Doing proper least significant delta sigma live still causes flicker (but only for dim pixels)
madcowswe 0:f16a1d69a386 227 //ssdsErr[i]-=(0x10000-gamma[i])*pulseLength;
madcowswe 0:f16a1d69a386 228 dsVal[i] = 0;
madcowswe 0:f16a1d69a386 229 } else {
madcowswe 0:f16a1d69a386 230 dsVal[i] = (unsigned char)-1;
madcowswe 0:f16a1d69a386 231 //ssdsErr[i]+=gamma[i]*pulseLength;
madcowswe 0:f16a1d69a386 232 }
madcowswe 0:f16a1d69a386 233
madcowswe 0:f16a1d69a386 234 }
madcowswe 0:f16a1d69a386 235 }
madcowswe 0:f16a1d69a386 236
madcowswe 0:f16a1d69a386 237 // output data
madcowswe 0:f16a1d69a386 238 for (int i = 0; i < NUM_PANELS; i++) { // NUM_PANELS
madcowswe 0:f16a1d69a386 239 MA0 = i&1;
madcowswe 0:f16a1d69a386 240 MA1 = i&2;
madcowswe 0:f16a1d69a386 241
madcowswe 0:f16a1d69a386 242 frameout(dsVal, &transformedSource[i*256*3]);
madcowswe 0:f16a1d69a386 243 }
madcowswe 0:f16a1d69a386 244
madcowswe 0:f16a1d69a386 245 NREN = 1; // need to have enables high before every latch, (in case we are on a long pulse)
madcowswe 0:f16a1d69a386 246 flatch = 1; // latching all data to LEDs
madcowswe 0:f16a1d69a386 247 flatch = 0;
madcowswe 0:f16a1d69a386 248 NREN = 0; // turn on LEDs
madcowswe 0:f16a1d69a386 249
madcowswe 0:f16a1d69a386 250 if (pulseLength < 4) { // short pulses done through wait
madcowswe 0:f16a1d69a386 251 wait_us(pulseLength);
madcowswe 0:f16a1d69a386 252 NREN = 1; //Turn off LEDs
madcowswe 0:f16a1d69a386 253
madcowswe 0:f16a1d69a386 254 bool wasrunning = running;
madcowswe 0:f16a1d69a386 255 running = false;
madcowswe 0:f16a1d69a386 256 outputFrame(); //this will recurse only once due to the distrubution of pulses. pulseLength of the next instance will be attached.
madcowswe 0:f16a1d69a386 257 running = wasrunning;
madcowswe 0:f16a1d69a386 258 }
madcowswe 0:f16a1d69a386 259 // long waits done through attaching an interrupt that will turn off the LEDs at the start of next function call.
madcowswe 0:f16a1d69a386 260 // Meanwhile, the main code can run between the interrupts.
madcowswe 0:f16a1d69a386 261 if (running) nextFrameTimer.attach_us(this, &ledScreen::outputFrame, (pulseLength == MAX_PULSE_WIDTH) ? pulseLength - OP_TIME : pulseLength);
madcowswe 0:f16a1d69a386 262 }