LED screen driver build for hackspace.

Dependencies:   mbed

Committer:
madcowswe
Date:
Wed Feb 29 17:01:43 2012 +0000
Revision:
0:f16a1d69a386
Child:
1:1af5060b2a34

        

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