EIC / RGB_matrix_Panel

Dependencies:   Adafruit-GFX

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers RGBmatrixPanel.cpp Source File

RGBmatrixPanel.cpp

00001 #define DEBUG
00002 #undef DEBUG
00003 #include "RGBmatrixPanel.h"
00004 #include "gamma.h"
00005 
00006 #define nPlanes 4
00007 
00008 // The fact that the display driver interrupt stuff is tied to the
00009 // singular Timer1 doesn't really take well to object orientation with
00010 // multiple RGBmatrixPanel instances.  The solution at present is to
00011 // allow instances, but only one is active at any given time, via its
00012 // begin() method.  The implementation is still incomplete in parts;
00013 // the prior active panel really should be gracefully disabled, and a
00014 // stop() method should perhaps be added...assuming multiple instances
00015 // are even an actual need.
00016 //static RGBmatrixPanel *activePanel = NULL;
00017 
00018 // Code common to both the 16x32 and 32x32 constructors:
00019 void RGBmatrixPanel::init(uint8_t rows, bool dbuf)
00020 {
00021     nRows = rows; // Number of multiplexed rows; actual height is 2X this
00022     // Allocate and initialize matrix buffer:
00023     int buffsize  = 32*nRows*3, // x3 = 3 bytes holds 4 planes "packed"
00024         allocsize = (dbuf == true) ? (buffsize * 2) : buffsize;
00025     if(NULL == (matrixbuff[0] = (uint8_t *)malloc(allocsize))) return;
00026     memset(matrixbuff[0], 0, allocsize);
00027     // If not double-buffered, both buffers then point to the same address:
00028     matrixbuff[1] = (dbuf == true) ? &matrixbuff[0][buffsize] : matrixbuff[0];
00029 
00030     plane     = nPlanes - 1;
00031     row       = nRows   - 1;
00032     swapflag  = false;
00033     backindex = 0;     // Array index of back buffer
00034 }
00035 
00036 // Constructor for 16x32 panel:
00037 RGBmatrixPanel::RGBmatrixPanel(PinName r1,PinName g1,PinName b1,PinName r2,PinName g2,PinName b2,PinName a,PinName b, PinName c, PinName sclk, PinName latch, PinName oe, bool dbuf)
00038     :Adafruit_GFX(32, 16),
00039      _dataBus(r1,g1,b1,r2,g2,b2),
00040      _rowBus(a,b,c),
00041      _d(NC),
00042      _sclk(sclk),
00043      _latch(latch),
00044      _oe(oe)
00045 {
00046     init(8, dbuf);
00047 }
00048 /*
00049 // Constructor for 32x32 panel:
00050 RGBmatrixPanel::RGBmatrixPanel(PinName r1,PinName r2,PinName g1,PinName g2,PinName b1,PinName b2,PinName a,PinName b,PinName c,PinName d,PinName sclk,PinName latch,PinName oe,bool dbuf)
00051     :Adafruit_GFX(32, 32),
00052      _dataBus(r1,g1,b1,r2,g2,b2),
00053      _rowBus(a,b,c),
00054      _d(d),// Init 32x32-specific elements:
00055      _sclk(sclk),
00056      _latch(latch),
00057      _oe(oe)
00058 {
00059     init(16,dbuf);
00060 }
00061 */
00062 void RGBmatrixPanel::begin(void)
00063 {
00064 
00065     backindex   = 0;                         // Back buffer
00066     buffptr     = matrixbuff[1 - backindex]; // -> front buffer
00067     // activePanel = this;                      // For interrupt hander
00068 
00069     // Set up Timer for interrupt:
00070 #ifndef DEBUG
00071     _refresh.attach_us(this,(&RGBmatrixPanel::updateDisplay ),100);   //updateDisplay() called every 1ms
00072 #else
00073     _refresh.attach(this,(&RGBmatrixPanel::updateDisplay ),0.5);   //updateDisplay() called every 2s
00074 #endif
00075 }
00076 
00077 // Original RGBmatrixPanel library used 3/3/3 color.  Later version used
00078 // 4/4/4.  Then Adafruit_GFX (core library used across all Adafruit
00079 // display devices now) standardized on 5/6/5.  The matrix still operates
00080 // internally on 4/4/4 color, but all the graphics functions are written
00081 // to expect 5/6/5...the matrix lib will truncate the color components as
00082 // needed when drawing.  These next functions are mostly here for the
00083 // benefit of older code using one of the original color formats.
00084 
00085 // Promote 3/3/3 RGB to Adafruit_GFX 5/6/5
00086 uint16_t RGBmatrixPanel::Color333(uint8_t r, uint8_t g, uint8_t b)
00087 {
00088     // RRRrrGGGgggBBBbb
00089     return ((r & 0x7) << 13) | ((r & 0x6) << 10) |
00090            ((g & 0x7) <<  8) | ((g & 0x7) <<  5) |
00091            ((b & 0x7) <<  2) | ((b & 0x6) >>  1);
00092 }
00093 
00094 // Promote 4/4/4 RGB to Adafruit_GFX 5/6/5
00095 uint16_t RGBmatrixPanel::Color444(uint8_t r, uint8_t g, uint8_t b)
00096 {
00097     // RRRRrGGGGggBBBBb
00098     return ((r & 0xF) << 12) | ((r & 0x8) << 8) |
00099            ((g & 0xF) <<  7) | ((g & 0xC) << 3) |
00100            ((b & 0xF) <<  1) | ((b & 0x8) >> 3);
00101 }
00102 
00103 // Demote 8/8/8 to Adafruit_GFX 5/6/5
00104 // If no gamma flag passed, assume linear color
00105 uint16_t RGBmatrixPanel::Color888(uint8_t r, uint8_t g, uint8_t b)
00106 {
00107     return ((r & 0xF8) << 11) | ((g & 0xFC) << 5) | (b >> 3);
00108 }
00109 
00110 // 8/8/8 -> gamma -> 5/6/5
00111 uint16_t RGBmatrixPanel::Color888(uint8_t r, uint8_t g, uint8_t b, bool gflag)
00112 {
00113     if(gflag) { // Gamma-corrected color?
00114         r = gamma[r]; // Gamma correction table maps
00115         g = gamma[g]; // 8-bit input to 4-bit output
00116         b = gamma[b];
00117         return (r << 12) | ((r & 0x8) << 8) | // 4/4/4 -> 5/6/5
00118                (g <<  7) | ((g & 0xC) << 3) |
00119                (b <<  1) | ( b        >> 3);
00120     } // else linear (uncorrected) color
00121     return ((r & 0xF8) << 11) | ((g & 0xFC) << 5) | (b >> 3);
00122 }
00123 
00124 uint16_t RGBmatrixPanel::ColorHSV(long hue, uint8_t sat, uint8_t val, bool gflag)
00125 {
00126 
00127     uint8_t  r, g, b, lo;
00128     uint16_t s1, v1;
00129 
00130     // Hue
00131     hue %= 1536;             // -1535 to +1535
00132     if(hue < 0) hue += 1536; //     0 to +1535
00133     lo = hue & 255;          // Low byte  = primary/secondary color mix
00134     switch(hue >> 8) {       // High byte = sextant of colorwheel
00135         case 0 :
00136             r = 255     ;
00137             g =  lo     ;
00138             b =   0     ;
00139             break; // R to Y
00140         case 1 :
00141             r = 255 - lo;
00142             g = 255     ;
00143             b =   0     ;
00144             break; // Y to G
00145         case 2 :
00146             r =   0     ;
00147             g = 255     ;
00148             b =  lo     ;
00149             break; // G to C
00150         case 3 :
00151             r =   0     ;
00152             g = 255 - lo;
00153             b = 255     ;
00154             break; // C to B
00155         case 4 :
00156             r =  lo     ;
00157             g =   0     ;
00158             b = 255     ;
00159             break; // B to M
00160         default:
00161             r = 255     ;
00162             g =   0     ;
00163             b = 255 - lo;
00164             break; // M to R
00165     }
00166 
00167     // Saturation: add 1 so range is 1 to 256, allowig a quick shift operation
00168     // on the result rather than a costly divide, while the type upgrade to int
00169     // avoids repeated type conversions in both directions.
00170     s1 = sat + 1;
00171     r  = 255 - (((255 - r) * s1) >> 8);
00172     g  = 255 - (((255 - g) * s1) >> 8);
00173     b  = 255 - (((255 - b) * s1) >> 8);
00174 
00175     // Value (brightness) & 16-bit color reduction: similar to above, add 1
00176     // to allow shifts, and upgrade to int makes other conversions implicit.
00177     v1 = val + 1;
00178     if(gflag) { // Gamma-corrected color?
00179         r = gamma[(r * v1) >> 8]; // Gamma correction table maps
00180         g = gamma[(g * v1) >> 8]; // 8-bit input to 4-bit output
00181         b = gamma[(b * v1) >> 8];
00182         //before pgm_read_byte(&gamma[(b * v1) >> 8])
00183     } else { // linear (uncorrected) color
00184         r = (r * v1) >> 12; // 4-bit results
00185         g = (g * v1) >> 12;
00186         b = (b * v1) >> 12;
00187     }
00188     return (r << 12) | ((r & 0x8) << 8) | // 4/4/4 -> 5/6/5
00189            (g <<  7) | ((g & 0xC) << 3) |
00190            (b <<  1) | ( b        >> 3);
00191 }
00192 uint16_t RGBmatrixPanel::ColorHSV(float hue, float sat, float val, bool gflag)
00193 {
00194     int i = floor(hue * 6);
00195     float f = hue*6-i;
00196     float p = val*(1-sat);
00197     float q = val*(1-f*sat);
00198     float t = val*(1-(1-f)*sat);
00199     float r = 0,g=0,b=0;
00200     switch(i % 6) {
00201         case 0:
00202             r = val;
00203             g = t;
00204             b = p;
00205             break;
00206         case 1:
00207             r = q;
00208             g = val;
00209             b = p;
00210             break;
00211         case 2:
00212             r = p;
00213             g = val;
00214             b = t;
00215             break;
00216         case 3:
00217             r = p;
00218             g = q;
00219             b = val;
00220             break;
00221         case 4:
00222             r = t;
00223             g = p;
00224             b = val;
00225             break;
00226         case 5:
00227             r = val;
00228             g = p;
00229             b = q;
00230             break;
00231     }
00232     return Color888(r*255,g*255,b*255,gflag);
00233 }
00234 void RGBmatrixPanel::drawPixel(int16_t x, int16_t y, uint16_t c)
00235 {
00236     uint8_t r, g, b, bit, limit, *ptr;
00237     if((x < 0) || (x >= _width) || (y < 0) || (y >= _height)) return;
00238     switch(rotation) {
00239         case 1:
00240             swap(x, y);
00241             x = _rawWidth  - 1 - x;
00242             break;
00243         case 2:
00244             x = _rawWidth  - 1 - x;
00245             y = _rawHeight - 1 - y;
00246             break;
00247         case 3:
00248             swap(x, y);
00249             y = _rawHeight - 1 - y;
00250             break;
00251     }
00252 
00253     // Adafruit_GFX uses 16-bit color in 5/6/5 format, while matrix needs
00254     // 4/4/4.  Pluck out relevant bits while separating into R,G,B:
00255     r =  c >> 12;        // RRRRrggggggbbbbb
00256     g = (c >>  7) & 0xF; // rrrrrGGGGggbbbbb
00257     b = (c >>  1) & 0xF; // rrrrrggggggBBBBb
00258     // Loop counter stuff
00259     bit   = 2;
00260     limit = 1 << nPlanes;
00261     if(y < nRows) {
00262         // Data for the upper half of the display is stored in the lower bits of each byte.
00263         ptr = &matrixbuff[backindex][y*_rawWidth*(nPlanes-1) + x]; // Base addr
00264         // Plane 0 is a tricky case -- its data is spread about, stored in least two bits not used by the other planes.
00265         ptr[64] &= ~(_BV(1)|_BV(0));    // Plane 0 R,G mask(0b11111100) out in one op
00266         if(r & 1) ptr[64] |=  _BV(0);  // Plane 0 R: 64 bytes ahead, bit 0
00267         if(g & 1) ptr[64] |=  _BV(1);  // Plane 0 G: 64 bytes ahead, bit 1
00268         if(b & 1) ptr[32] |=  _BV(0);  // Plane 0 B: 32 bytes ahead, bit 0
00269         else      ptr[32] &= ~_BV(0);  // Plane 0 B unset; mask out
00270         // The remaining three image planes are more normal-ish.
00271         // Data is stored in the high 6 bits so it can be quickly
00272         // copied to the DATAPORT register w/6 output lines.
00273         for(; bit < limit; bit <<= 1) {
00274             *ptr &= ~(_BV(4)|_BV(3)|_BV(2)) ;  // Mask(0b11100011) out R,G,B in one op
00275             if(r & bit) *ptr |= _BV(2); // Plane N R: bit 2
00276             if(g & bit) *ptr |= _BV(3); // Plane N G: bit 3
00277             if(b & bit) *ptr |= _BV(4); // Plane N B: bit 4
00278             ptr  += _rawWidth;          // Advance to next bit plane
00279         }
00280     } else {
00281         // Data for the lower half of the display is stored in the upper bits, except for the plane 0 stuff, using 2 least bits.
00282         ptr = &matrixbuff[backindex][(y-nRows)*_rawWidth*(nPlanes-1) + x];
00283         *ptr &= ~(_BV(1)|_BV(0));               // Plane 0 G,B mask out in one op
00284         if(r & 1)  ptr[32] |=  _BV(1); // Plane 0 R: 32 bytes ahead, bit 1
00285         else       ptr[32] &= ~_BV(1); // Plane 0 R unset; mask out
00286         if(g & 1) *ptr     |=  _BV(0); // Plane 0 G: bit 0
00287         if(b & 1) *ptr     |=  _BV(1); // Plane 0 B: bit 0
00288         for(; bit < limit; bit <<= 1) {
00289             *ptr &= ~(_BV(7)|_BV(6)|_BV(5));             // Mask out R,G,B in one op
00290             if(r & bit) *ptr |= _BV(5);  // Plane N R: bit 5
00291             if(g & bit) *ptr |= _BV(6);  // Plane N G: bit 6
00292             if(b & bit) *ptr |= _BV(7);  // Plane N B: bit 7
00293             ptr  += _rawWidth;                  // Advance to next bit plane
00294         }
00295     }
00296 }
00297 
00298 void RGBmatrixPanel::fillScreen(uint16_t c)
00299 {
00300     if((c == 0x0000) || (c == 0xffff)) {
00301         // For black or white, all bits in frame buffer will be identically
00302         // set or unset (regardless of weird bit packing), so it's OK to just
00303         // quickly memset the whole thing:
00304         memset(matrixbuff[backindex], c, 32 * nRows * 3);
00305     } else {
00306         // Otherwise, need to handle it the long way:
00307         Adafruit_GFX::fillScreen(c);
00308     }
00309 }
00310 
00311 // Return address of back buffer -- can then load/store data directly
00312 uint8_t *RGBmatrixPanel::backBuffer()
00313 {
00314     return matrixbuff[backindex];
00315 }
00316 
00317 // For smooth animation -- drawing always takes place in the "back" buffer;
00318 // this method pushes it to the "front" for display.  Passing "true", the
00319 // updated display contents are then copied to the new back buffer and can
00320 // be incrementally modified.  If "false", the back buffer then contains
00321 // the old front buffer contents -- your code can either clear this or
00322 // draw over every pixel.  (No effect if double-buffering is not enabled.)
00323 void RGBmatrixPanel::swapBuffers (bool copy)
00324 {
00325     if(matrixbuff[0] != matrixbuff[1]) {
00326         // To avoid 'tearing' display, actual swap takes place in the interrupt
00327         // handler, at the end of a complete screen refresh cycle.
00328         swapflag = true;                  // Set flag here, then...
00329         while(swapflag == true) wait_ms(1); // wait for interrupt to clear it
00330         if(copy == true) {
00331             memcpy(matrixbuff[backindex], matrixbuff[1-backindex], 32 * nRows * 3);
00332         }
00333     }
00334 }
00335 
00336 // Dump display contents to the Serial Monitor, adding some formatting to
00337 // simplify copy-and-paste of data as a PROGMEM-embedded image for another
00338 // sketch.  If using multiple dumps this way, you'll need to edit the
00339 // output to change the 'img' name for each.  Data can then be loaded
00340 // back into the display using a pgm_read_byte() loop.
00341 void RGBmatrixPanel::dumpMatrix(void)
00342 {
00343 #ifdef DEBUG
00344     log_debug("\r\ncall dumpMatrix%s","\r\n");
00345     int buffsize=32*nRows*3;
00346     for(int item=0; item<buffsize; item++) {
00347         log_debug("0x%02X",matrixbuff[backindex][item]);
00348         if((item%32)==31)    log_debug(",\r\n");
00349         else                log_debug(",");
00350     }
00351     log_debug("%s","\r\n\r\n");
00352 #endif
00353 
00354 }
00355 
00356 void RGBmatrixPanel::updateDisplay (void)
00357 {
00358     _oe=1;
00359     _latch=1;
00360     if(++plane >= nPlanes) {        // Advance plane counter.  Maxed out?
00361         plane = 0;                  // Yes, reset to plane 0, and
00362         if(++row >= nRows) {        // advance row counter.  Maxed out?
00363             row= 0;                 // Yes, reset row counter, then...
00364             if(swapflag == true) {  // Swap front/back buffers if requested
00365                 backindex = 1 - backindex;
00366                 swapflag  = false;
00367             }
00368             buffptr = matrixbuff[1-backindex]; // Reset into front buffer
00369         }
00370     } else if(plane == 1) {
00371         _rowBus.write(row);
00372     }
00373     _oe=0;
00374     _latch=0;
00375     if(plane > 0) {
00376         for(int i=0; i<32; i++) {
00377             _dataBus.write((*(buffptr+i)>>2));
00378             _sclk=1;
00379             _sclk=0;
00380         }
00381         buffptr += _rawWidth;
00382     } else {
00383         for(int i=0; i<32; i++) {
00384             _dataBus.write(((buffptr[i]<<4)|((buffptr[i+32]<<2)&0x0C)|((buffptr[i+64])&0x03)));
00385             _sclk=1;
00386             _sclk=0;
00387         }
00388     }
00389 
00390 }
00391