Oskar Weigl
/
LED_for_hackspace
LED screen driver build for hackspace.
ledScreen.h
- Committer:
- madcowswe
- Date:
- 2012-02-29
- Revision:
- 1:1af5060b2a34
- Parent:
- 0:f16a1d69a386
File content as of revision 1:1af5060b2a34:
#include "mbed.h" /* ****************************************************** *Hacked together by Imperial College Robotics Society* ****************************************************** Usage: Example code supplied (main.cpp). Format of data input: 8bit RGB channels per pixel sequential in one framebuffer. Note: This version uses inverted outputs (to bring voltage from 3.3 to 5v and drive more current) for data, clock and address lines. Quirks of current platform: transformFrame crammed into the code, should be united with transformFrame2 for proper orientation address lines are put through inverters, code still thinks it's addressing with logic 1's, whereas in reality MA0 is inverted and put into the ribbon cable line of MA1 and vice-versa */ extern "C" void frameout(unsigned char dsVal[], unsigned char transformedSource[]); class ledScreen { public: ledScreen(); ~ledScreen() {} void transformFrame(unsigned char* imageSource); void transformFrame2(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 NUM_PANELS = 3; // number of panels horizontally 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[256*3*NUM_PANELS]; // 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 Rdat; // red data DigitalOut Gdat; // green data DigitalOut Bdat; // blue data DigitalOut sclk; // clock }; ledScreen::ledScreen() : flatch(p11), // data latch (for all connected panels in parallel) MA0(p18), // module address 0 MA1(p19), NREN(p12), // active low enable for red channel (low -> LED on). Note: need to have enable high when latching data Rdat(p15), // red data Gdat(p16), // green data Bdat(p17), // blue data sclk(p14) { // 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 = 0; NREN = 0; sclk = 0; // 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 = 345; //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() { running=1; outputFrame(); } void ledScreen::transformFrame2(unsigned char* imageSource) { int psp = 0; // panel space pointer int tpsp = 0; // target psp int isp = 8 * 3; // imag espace pointer // preprocessing the image data to match shifting pattern for (int panel = 0; panel < NUM_PANELS; panel++) { // int base = panel * 0x20; //i - image space*/ for (int drows = 0; drows < 8; drows++) { //for (int m = 0; m < 0x1F; m++) { //m - quad block in panel space for (tpsp = psp + (8*3); psp < tpsp;) { //0th quad isp -= 3; transformedSource[psp++] = imageSource[isp]; transformedSource[psp++] = imageSource[isp+1]; transformedSource[psp++] = imageSource[isp+2]; } //next row isp += 0x10 * NUM_PANELS * 3; for (tpsp = psp + (8*3); psp < tpsp; isp+=3) { //2nd quad transformedSource[psp++] = imageSource[isp]; transformedSource[psp++] = imageSource[isp+1]; transformedSource[psp++] = imageSource[isp+2]; } //previous row isp -= (0x10 * NUM_PANELS - 8) * 3; for (tpsp = psp + (8*3); psp < tpsp;) { //1st quad isp-=3; transformedSource[psp++] = imageSource[isp]; transformedSource[psp++] = imageSource[isp+1]; transformedSource[psp++] = imageSource[isp+2]; } isp += 0x10 * NUM_PANELS * 3; for (tpsp = psp + (8*3); psp < tpsp; isp+=3) { //3rd quad transformedSource[psp++] = imageSource[isp]; transformedSource[psp++] = imageSource[isp+1]; transformedSource[psp++] = imageSource[isp+2]; } isp += (0x10 * NUM_PANELS - 8) * 3; } isp += (0x10 - 0x20 * NUM_PANELS * 8) * 3; } } void ledScreen::transformFrame(unsigned char* imageSource) { unsigned char rotatedSource[256*3*NUM_PANELS]; for (int panels = 0; panels < 3; panels++){ for (int x = 0; x<16; x++){ for (int y = 0; y<16; y++){ for (int c = 0; c < 3; c++){ rotatedSource[((15-x) + panels*16 + 48*y)*3 + c] = imageSource[(y + panels*16 + 48*x)*3 + c]; } } } } transformFrame2(rotatedSource); } // Output one frame and call itself after a period of time if running is set to true void ledScreen::outputFrame() { if (pulseLength != MAX_PULSE_WIDTH) NREN = 1; // 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; //ssdsErr[i]+=gamma[i]*pulseLength; } } } // output data for (int i = 0; i < NUM_PANELS; i++) { // NUM_PANELS MA0 = i&1; MA1 = i&2; frameout(dsVal, &transformedSource[i*256*3]); } NREN = 1; // need to have enables high before every latch, (in case we are on a long pulse) flatch = 1; // latching all data to LEDs flatch = 0; NREN = 0; // turn on LEDs if (pulseLength < 4) { // short pulses done through wait wait_us(pulseLength); NREN = 1; //Turn off LEDs bool wasrunning = running; running = false; outputFrame(); //this will recurse only once due to the distrubution of pulses. pulseLength of the next instance will be attached. running = wasrunning; } // 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. if (running) nextFrameTimer.attach_us(this, &ledScreen::outputFrame, (pulseLength == MAX_PULSE_WIDTH) ? pulseLength - OP_TIME : pulseLength); }