From 8024bb2cb1e098007df153d97b2256e86ac630ff Mon Sep 17 00:00:00 2001 From: Aerospacesmith Date: Mon, 21 Jul 2014 22:53:16 -0500 Subject: [PATCH 1/9] Update RGBmatrixPanel.cpp Added unit32_t color support --- RGBmatrixPanel.cpp | 408 +++++++++++++++++++++++++-------------------- 1 file changed, 228 insertions(+), 180 deletions(-) diff --git a/RGBmatrixPanel.cpp b/RGBmatrixPanel.cpp index a637d96..f70d062 100644 --- a/RGBmatrixPanel.cpp +++ b/RGBmatrixPanel.cpp @@ -1,32 +1,32 @@ /* RGBmatrixPanel Arduino library for Adafruit 16x32 and 32x32 RGB LED -matrix panels. Pick one up at: - http://www.adafruit.com/products/420 - http://www.adafruit.com/products/607 +matrix panels. Pick one up at: +http://www.adafruit.com/products/420 +http://www.adafruit.com/products/607 This version uses a few tricks to achieve better performance and/or lower CPU utilization: - To control LED brightness, traditional PWM is eschewed in favor of - Binary Code Modulation, which operates through a succession of periods - each twice the length of the preceeding one (rather than a direct - linear count a la PWM). It's explained well here: +Binary Code Modulation, which operates through a succession of periods +each twice the length of the preceeding one (rather than a direct +linear count a la PWM). It's explained well here: - http://www.batsocks.co.uk/readme/art_bcm_1.htm +http://www.batsocks.co.uk/readme/art_bcm_1.htm - I was initially skeptical, but it works exceedingly well in practice! - And this uses considerably fewer CPU cycles than software PWM. +I was initially skeptical, but it works exceedingly well in practice! +And this uses considerably fewer CPU cycles than software PWM. - Although many control pins are software-configurable in the user's - code, a couple things are tied to specific PORT registers. It's just - a lot faster this way -- port lookups take time. Please see the notes - later regarding wiring on "alternative" Arduino boards. +code, a couple things are tied to specific PORT registers. It's just +a lot faster this way -- port lookups take time. Please see the notes +later regarding wiring on "alternative" Arduino boards. - A tiny bit of inline assembly language is used in the most speed- - critical section. The C++ compiler wasn't making optimal use of the - instruction set in what seemed like an obvious chunk of code. Since - it's only a few short instructions, this loop is also "unrolled" -- - each iteration is stated explicitly, not through a control loop. +critical section. The C++ compiler wasn't making optimal use of the +instruction set in what seemed like an obvious chunk of code. Since +it's only a few short instructions, this loop is also "unrolled" -- +each iteration is stated explicitly, not through a control loop. Written by Limor Fried/Ladyada & Phil Burgess/PaintYourDragon for Adafruit Industries. @@ -37,7 +37,7 @@ BSD license, all text above must be included in any redistribution. #include "gamma.h" // A full PORT register is required for the data lines, though only the -// top 6 output bits are used. For performance reasons, the port # cannot +// top 6 output bits are used. For performance reasons, the port # cannot // be changed via library calls, only by changing constants in the library. // For similar reasons, the clock pin is only semi-configurable...it can // be specified as any pin within a specific PORT register stated below. @@ -48,38 +48,38 @@ BSD license, all text above must be included in any redistribution. // the Mega requires connecting the matrix data lines to different pins. // Digital pins 24-29 are used for the data interface, and 22 & 23 are // unavailable for other outputs because the software needs to write to - // the full PORTA register for speed. Clock may be any pin on PORTB -- + // the full PORTA register for speed. Clock may be any pin on PORTB -- // on the Mega, this CAN'T be pins 8 or 9 (these are on PORTH), thus the // wiring will need to be slightly different than the tutorial's - // explanation on the Uno, etc. Pins 10-13 are all fair game for the + // explanation on the Uno, etc. Pins 10-13 are all fair game for the // clock, as are pins 50-53. - #define DATAPORT PORTA - #define DATADIR DDRA - #define SCLKPORT PORTB +#define DATAPORT PORTA +#define DATADIR DDRA +#define SCLKPORT PORTB #elif defined(__AVR_ATmega32U4__) // Arduino Leonardo: this is vestigial code an unlikely to ever be - // finished -- DO NOT USE!!! Unlike the Uno, digital pins 2-7 do NOT + // finished -- DO NOT USE!!! Unlike the Uno, digital pins 2-7 do NOT // map to a contiguous port register, dashing our hopes for compatible - // wiring. Making this work would require significant changes both to + // wiring. Making this work would require significant changes both to // the bit-shifting code in the library, and how this board is wired to - // the LED matrix. Bummer. - #define DATAPORT PORTD - #define DATADIR DDRD - #define SCLKPORT PORTB + // the LED matrix. Bummer. +#define DATAPORT PORTD +#define DATADIR DDRD +#define SCLKPORT PORTB #else // Ports for "standard" boards (Arduino Uno, Duemilanove, etc.) - #define DATAPORT PORTD - #define DATADIR DDRD - #define SCLKPORT PORTB +#define DATAPORT PORTD +#define DATADIR DDRD +#define SCLKPORT PORTB #endif #define nPlanes 4 // The fact that the display driver interrupt stuff is tied to the // singular Timer1 doesn't really take well to object orientation with -// multiple RGBmatrixPanel instances. The solution at present is to +// multiple RGBmatrixPanel instances. The solution at present is to // allow instances, but only one is active at any given time, via its -// begin() method. The implementation is still incomplete in parts; +// begin() method. The implementation is still incomplete in parts; // the prior active panel really should be gracefully disabled, and a // stop() method should perhaps be added...assuming multiple instances // are even an actual need. @@ -92,7 +92,7 @@ void RGBmatrixPanel::init(uint8_t rows, uint8_t a, uint8_t b, uint8_t c, nRows = rows; // Number of multiplexed rows; actual height is 2X this // Allocate and initialize matrix buffer: - int buffsize = 32 * nRows * 3, // x3 = 3 bytes holds 4 planes "packed" + int buffsize = 32 * nRows * 3, // x3 = 3 bytes holds 4 planes "packed" allocsize = (dbuf == true) ? (buffsize * 2) : buffsize; if(NULL == (matrixbuff[0] = (uint8_t *)malloc(allocsize))) return; memset(matrixbuff[0], 0, allocsize); @@ -100,30 +100,30 @@ void RGBmatrixPanel::init(uint8_t rows, uint8_t a, uint8_t b, uint8_t c, matrixbuff[1] = (dbuf == true) ? &matrixbuff[0][buffsize] : matrixbuff[0]; // Save pin numbers for use by begin() method later. - _a = a; - _b = b; - _c = c; - _sclk = sclk; + _a = a; + _b = b; + _c = c; + _sclk = sclk; _latch = latch; - _oe = oe; + _oe = oe; // Look up port registers and pin masks ahead of time, // avoids many slow digitalWrite() calls later. - sclkpin = digitalPinToBitMask(sclk); - latport = portOutputRegister(digitalPinToPort(latch)); - latpin = digitalPinToBitMask(latch); - oeport = portOutputRegister(digitalPinToPort(oe)); - oepin = digitalPinToBitMask(oe); + sclkpin = digitalPinToBitMask(sclk); + latport = portOutputRegister(digitalPinToPort(latch)); + latpin = digitalPinToBitMask(latch); + oeport = portOutputRegister(digitalPinToPort(oe)); + oepin = digitalPinToBitMask(oe); addraport = portOutputRegister(digitalPinToPort(a)); - addrapin = digitalPinToBitMask(a); + addrapin = digitalPinToBitMask(a); addrbport = portOutputRegister(digitalPinToPort(b)); - addrbpin = digitalPinToBitMask(b); + addrbpin = digitalPinToBitMask(b); addrcport = portOutputRegister(digitalPinToPort(c)); - addrcpin = digitalPinToBitMask(c); - plane = nPlanes - 1; - row = nRows - 1; - swapflag = false; - backindex = 0; // Array index of back buffer + addrcpin = digitalPinToBitMask(c); + plane = nPlanes - 1; + row = nRows - 1; + swapflag = false; + backindex = 0; // Array index of back buffer } // Constructor for 16x32 panel: @@ -144,63 +144,85 @@ RGBmatrixPanel::RGBmatrixPanel( init(16, a, b, c, sclk, latch, oe, dbuf); // Init a few extra 32x32-specific elements: - _d = d; + _d = d; addrdport = portOutputRegister(digitalPinToPort(d)); - addrdpin = digitalPinToBitMask(d); + addrdpin = digitalPinToBitMask(d); } void RGBmatrixPanel::begin(void) { - backindex = 0; // Back buffer - buffptr = matrixbuff[1 - backindex]; // -> front buffer - activePanel = this; // For interrupt hander + backindex = 0; // Back buffer + buffptr = matrixbuff[1 - backindex]; // -> front buffer + activePanel = this; // For interrupt hander // Enable all comm & address pins as outputs, set default states: - pinMode(_sclk , OUTPUT); SCLKPORT &= ~sclkpin; // Low - pinMode(_latch, OUTPUT); *latport &= ~latpin; // Low - pinMode(_oe , OUTPUT); *oeport |= oepin; // High (disable output) - pinMode(_a , OUTPUT); *addraport &= ~addrapin; // Low - pinMode(_b , OUTPUT); *addrbport &= ~addrbpin; // Low - pinMode(_c , OUTPUT); *addrcport &= ~addrcpin; // Low + pinMode(_sclk , OUTPUT); SCLKPORT &= ~sclkpin; // Low + pinMode(_latch, OUTPUT); *latport &= ~latpin; // Low + pinMode(_oe , OUTPUT); *oeport |= oepin; // High (disable output) + pinMode(_a , OUTPUT); *addraport &= ~addrapin; // Low + pinMode(_b , OUTPUT); *addrbport &= ~addrbpin; // Low + pinMode(_c , OUTPUT); *addrcport &= ~addrcpin; // Low if(nRows > 8) { - pinMode(_d , OUTPUT); *addrdport &= ~addrdpin; // Low + pinMode(_d , OUTPUT); *addrdport &= ~addrdpin; // Low } // The high six bits of the data port are set as outputs; // Might make this configurable in the future, but not yet. - DATADIR = B11111100; + DATADIR = B11111100; DATAPORT = 0; // Set up Timer1 for interrupt: - TCCR1A = _BV(WGM11); // Mode 14 (fast PWM), OC1A off - TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10); // Mode 14, no prescale - ICR1 = 100; + TCCR1A = _BV(WGM11); // Mode 14 (fast PWM), OC1A off + TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10); // Mode 14, no prescale + ICR1 = 100; TIMSK1 |= _BV(TOIE1); // Enable Timer1 interrupt - sei(); // Enable global interrupts + sei(); // Enable global interrupts } -// Original RGBmatrixPanel library used 3/3/3 color. Later version used -// 4/4/4. Then Adafruit_GFX (core library used across all Adafruit -// display devices now) standardized on 5/6/5. The matrix still operates +// Original RGBmatrixPanel library used 3/3/3 color. Later version used +// 4/4/4. Then Adafruit_GFX (core library used across all Adafruit +// display devices now) standardized on 5/6/5. The matrix still operates // internally on 4/4/4 color, but all the graphics functions are written // to expect 5/6/5...the matrix lib will truncate the color components as -// needed when drawing. These next functions are mostly here for the +// needed when drawing. These next functions are mostly here for the // benefit of older code using one of the original color formats. // Promote 3/3/3 RGB to Adafruit_GFX 5/6/5 uint16_t RGBmatrixPanel::Color333(uint8_t r, uint8_t g, uint8_t b) { // RRRrrGGGgggBBBbb return ((r & 0x7) << 13) | ((r & 0x6) << 10) | - ((g & 0x7) << 8) | ((g & 0x7) << 5) | - ((b & 0x7) << 2) | ((b & 0x6) >> 1); + ((g & 0x7) << 8) | ((g & 0x7) << 5) | + ((b & 0x7) << 2) | ((b & 0x6) >> 1); +} + +uint16_t RGBmatrixPanel::Color333(uint32_t c) { + // RRRrrGGGgggBBBbb + uint8_t + r = (uint8_t)(c >> 16), + g = (uint8_t)(c >> 8), + b = (uint8_t)c; + return ((r & 0x7) << 13) | ((r & 0x6) << 10) | + ((g & 0x7) << 8) | ((g & 0x7) << 5) | + ((b & 0x7) << 2) | ((b & 0x6) >> 1); } // Promote 4/4/4 RGB to Adafruit_GFX 5/6/5 uint16_t RGBmatrixPanel::Color444(uint8_t r, uint8_t g, uint8_t b) { // RRRRrGGGGggBBBBb return ((r & 0xF) << 12) | ((r & 0x8) << 8) | - ((g & 0xF) << 7) | ((g & 0xC) << 3) | - ((b & 0xF) << 1) | ((b & 0x8) >> 3); + ((g & 0xF) << 7) | ((g & 0xC) << 3) | + ((b & 0xF) << 1) | ((b & 0x8) >> 3); +} + +uint16_t RGBmatrixPanel::Color444(uint32_t c) { + // RRRrrGGGgggBBBbb + uint8_t + r = (uint8_t)(c >> 16), + g = (uint8_t)(c >> 8), + b = (uint8_t)c; + return ((r & 0xF) << 12) | ((r & 0x8) << 8) | + ((g & 0xF) << 7) | ((g & 0xC) << 3) | + ((b & 0xF) << 1) | ((b & 0x8) >> 3); } // Demote 8/8/8 to Adafruit_GFX 5/6/5 @@ -209,6 +231,16 @@ uint16_t RGBmatrixPanel::Color888(uint8_t r, uint8_t g, uint8_t b) { return ((r & 0xF8) << 11) | ((g & 0xFC) << 5) | (b >> 3); } +uint16_t RGBmatrixPanel::Color888(uint32_t c) { + uint8_t + r = (uint8_t)(c >> 16), + g = (uint8_t)(c >> 8), + b = (uint8_t)c; + return ((r & 0xF8) << 11) | ((g & 0xFC) << 5) | (b >> 3); +} + + + // 8/8/8 -> gamma -> 5/6/5 uint16_t RGBmatrixPanel::Color888( uint8_t r, uint8_t g, uint8_t b, boolean gflag) { @@ -217,8 +249,25 @@ uint16_t RGBmatrixPanel::Color888( g = pgm_read_byte(&gamma[g]); // 8-bit input to 4-bit output b = pgm_read_byte(&gamma[b]); return (r << 12) | ((r & 0x8) << 8) | // 4/4/4 -> 5/6/5 - (g << 7) | ((g & 0xC) << 3) | - (b << 1) | ( b >> 3); + (g << 7) | ((g & 0xC) << 3) | + (b << 1) | ( b >> 3); + } // else linear (uncorrected) color + return ((r & 0xF8) << 11) | ((g & 0xFC) << 5) | (b >> 3); +} + +// 8/8/8 -> gamma -> 5/6/5 +uint16_t RGBmatrixPanel::Color888( + uint32_t c, boolean gflag) { + if(gflag) { // Gamma-corrected color? + r = (uint8_t)(c >> 16), + g = (uint8_t)(c >> 8), + b = (uint8_t)c; + r = pgm_read_byte(&gamma[r]); // Gamma correction table maps + g = pgm_read_byte(&gamma[g]); // 8-bit input to 4-bit output + b = pgm_read_byte(&gamma[b]); + return (r << 12) | ((r & 0x8) << 8) | // 4/4/4 -> 5/6/5 + (g << 7) | ((g & 0xC) << 3) | + (b << 1) | ( b >> 3); } // else linear (uncorrected) color return ((r & 0xF8) << 11) | ((g & 0xFC) << 5) | (b >> 3); } @@ -226,29 +275,29 @@ uint16_t RGBmatrixPanel::Color888( uint16_t RGBmatrixPanel::ColorHSV( long hue, uint8_t sat, uint8_t val, boolean gflag) { - uint8_t r, g, b, lo; + uint8_t r, g, b, lo; uint16_t s1, v1; // Hue - hue %= 1536; // -1535 to +1535 - if(hue < 0) hue += 1536; // 0 to +1535 - lo = hue & 255; // Low byte = primary/secondary color mix - switch(hue >> 8) { // High byte = sextant of colorwheel - case 0 : r = 255 ; g = lo ; b = 0 ; break; // R to Y - case 1 : r = 255 - lo; g = 255 ; b = 0 ; break; // Y to G - case 2 : r = 0 ; g = 255 ; b = lo ; break; // G to C - case 3 : r = 0 ; g = 255 - lo; b = 255 ; break; // C to B - case 4 : r = lo ; g = 0 ; b = 255 ; break; // B to M - default: r = 255 ; g = 0 ; b = 255 - lo; break; // M to R + hue %= 1536; // -1535 to +1535 + if(hue < 0) hue += 1536; // 0 to +1535 + lo = hue & 255; // Low byte = primary/secondary color mix + switch(hue >> 8) { // High byte = sextant of colorwheel + case 0 : r = 255 ; g = lo ; b = 0 ; break; // R to Y + case 1 : r = 255 - lo; g = 255 ; b = 0 ; break; // Y to G + case 2 : r = 0 ; g = 255 ; b = lo ; break; // G to C + case 3 : r = 0 ; g = 255 - lo; b = 255 ; break; // C to B + case 4 : r = lo ; g = 0 ; b = 255 ; break; // B to M + default: r = 255 ; g = 0 ; b = 255 - lo; break; // M to R } // Saturation: add 1 so range is 1 to 256, allowig a quick shift operation // on the result rather than a costly divide, while the type upgrade to int // avoids repeated type conversions in both directions. s1 = sat + 1; - r = 255 - (((255 - r) * s1) >> 8); - g = 255 - (((255 - g) * s1) >> 8); - b = 255 - (((255 - b) * s1) >> 8); + r = 255 - (((255 - r) * s1) >> 8); + g = 255 - (((255 - g) * s1) >> 8); + b = 255 - (((255 - b) * s1) >> 8); // Value (brightness) & 16-bit color reduction: similar to above, add 1 // to allow shifts, and upgrade to int makes other conversions implicit. @@ -263,8 +312,8 @@ uint16_t RGBmatrixPanel::ColorHSV( b = (b * v1) >> 12; } return (r << 12) | ((r & 0x8) << 8) | // 4/4/4 -> 5/6/5 - (g << 7) | ((g & 0xC) << 3) | - (b << 1) | ( b >> 3); + (g << 7) | ((g & 0xC) << 3) | + (b << 1) | ( b >> 3); } void RGBmatrixPanel::drawPixel(int16_t x, int16_t y, uint16_t c) { @@ -275,10 +324,10 @@ void RGBmatrixPanel::drawPixel(int16_t x, int16_t y, uint16_t c) { switch(rotation) { case 1: swap(x, y); - x = WIDTH - 1 - x; + x = WIDTH - 1 - x; break; case 2: - x = WIDTH - 1 - x; + x = WIDTH - 1 - x; y = HEIGHT - 1 - y; break; case 3: @@ -288,13 +337,13 @@ void RGBmatrixPanel::drawPixel(int16_t x, int16_t y, uint16_t c) { } // Adafruit_GFX uses 16-bit color in 5/6/5 format, while matrix needs - // 4/4/4. Pluck out relevant bits while separating into R,G,B: - r = c >> 12; // RRRRrggggggbbbbb - g = (c >> 7) & 0xF; // rrrrrGGGGggbbbbb - b = (c >> 1) & 0xF; // rrrrrggggggBBBBb + // 4/4/4. Pluck out relevant bits while separating into R,G,B: + r = c >> 12; // RRRRrggggggbbbbb + g = (c >> 7) & 0xF; // rrrrrGGGGggbbbbb + b = (c >> 1) & 0xF; // rrrrrggggggBBBBb // Loop counter stuff - bit = 2; + bit = 2; limit = 1 << nPlanes; if(y < nRows) { @@ -303,36 +352,36 @@ void RGBmatrixPanel::drawPixel(int16_t x, int16_t y, uint16_t c) { ptr = &matrixbuff[backindex][y * WIDTH * (nPlanes - 1) + x]; // Base addr // Plane 0 is a tricky case -- its data is spread about, // stored in least two bits not used by the other planes. - ptr[64] &= ~B00000011; // Plane 0 R,G mask out in one op - if(r & 1) ptr[64] |= B00000001; // Plane 0 R: 64 bytes ahead, bit 0 - if(g & 1) ptr[64] |= B00000010; // Plane 0 G: 64 bytes ahead, bit 1 - if(b & 1) ptr[32] |= B00000001; // Plane 0 B: 32 bytes ahead, bit 0 - else ptr[32] &= ~B00000001; // Plane 0 B unset; mask out + ptr[64] &= ~B00000011; // Plane 0 R,G mask out in one op + if(r & 1) ptr[64] |= B00000001; // Plane 0 R: 64 bytes ahead, bit 0 + if(g & 1) ptr[64] |= B00000010; // Plane 0 G: 64 bytes ahead, bit 1 + if(b & 1) ptr[32] |= B00000001; // Plane 0 B: 32 bytes ahead, bit 0 + else ptr[32] &= ~B00000001; // Plane 0 B unset; mask out // The remaining three image planes are more normal-ish. // Data is stored in the high 6 bits so it can be quickly // copied to the DATAPORT register w/6 output lines. for(; bit < limit; bit <<= 1) { - *ptr &= ~B00011100; // Mask out R,G,B in one op - if(r & bit) *ptr |= B00000100; // Plane N R: bit 2 - if(g & bit) *ptr |= B00001000; // Plane N G: bit 3 - if(b & bit) *ptr |= B00010000; // Plane N B: bit 4 - ptr += WIDTH; // Advance to next bit plane + *ptr &= ~B00011100; // Mask out R,G,B in one op + if(r & bit) *ptr |= B00000100; // Plane N R: bit 2 + if(g & bit) *ptr |= B00001000; // Plane N G: bit 3 + if(b & bit) *ptr |= B00010000; // Plane N B: bit 4 + ptr += WIDTH; // Advance to next bit plane } } else { // Data for the lower half of the display is stored in the upper // bits, except for the plane 0 stuff, using 2 least bits. ptr = &matrixbuff[backindex][(y - nRows) * WIDTH * (nPlanes - 1) + x]; - *ptr &= ~B00000011; // Plane 0 G,B mask out in one op - if(r & 1) ptr[32] |= B00000010; // Plane 0 R: 32 bytes ahead, bit 1 - else ptr[32] &= ~B00000010; // Plane 0 R unset; mask out - if(g & 1) *ptr |= B00000001; // Plane 0 G: bit 0 - if(b & 1) *ptr |= B00000010; // Plane 0 B: bit 0 + *ptr &= ~B00000011; // Plane 0 G,B mask out in one op + if(r & 1) ptr[32] |= B00000010; // Plane 0 R: 32 bytes ahead, bit 1 + else ptr[32] &= ~B00000010; // Plane 0 R unset; mask out + if(g & 1) *ptr |= B00000001; // Plane 0 G: bit 0 + if(b & 1) *ptr |= B00000010; // Plane 0 B: bit 0 for(; bit < limit; bit <<= 1) { - *ptr &= ~B11100000; // Mask out R,G,B in one op - if(r & bit) *ptr |= B00100000; // Plane N R: bit 5 - if(g & bit) *ptr |= B01000000; // Plane N G: bit 6 - if(b & bit) *ptr |= B10000000; // Plane N B: bit 7 - ptr += WIDTH; // Advance to next bit plane + *ptr &= ~B11100000; // Mask out R,G,B in one op + if(r & bit) *ptr |= B00100000; // Plane N R: bit 5 + if(g & bit) *ptr |= B01000000; // Plane N G: bit 6 + if(b & bit) *ptr |= B10000000; // Plane N B: bit 7 + ptr += WIDTH; // Advance to next bit plane } } } @@ -355,16 +404,16 @@ uint8_t *RGBmatrixPanel::backBuffer() { } // For smooth animation -- drawing always takes place in the "back" buffer; -// this method pushes it to the "front" for display. Passing "true", the +// this method pushes it to the "front" for display. Passing "true", the // updated display contents are then copied to the new back buffer and can -// be incrementally modified. If "false", the back buffer then contains +// be incrementally modified. If "false", the back buffer then contains // the old front buffer contents -- your code can either clear this or -// draw over every pixel. (No effect if double-buffering is not enabled.) +// draw over every pixel. (No effect if double-buffering is not enabled.) void RGBmatrixPanel::swapBuffers(boolean copy) { if(matrixbuff[0] != matrixbuff[1]) { // To avoid 'tearing' display, actual swap takes place in the interrupt // handler, at the end of a complete screen refresh cycle. - swapflag = true; // Set flag here, then... + swapflag = true; // Set flag here, then... while(swapflag == true) delay(1); // wait for interrupt to clear it if(copy == true) memcpy(matrixbuff[backindex], matrixbuff[1-backindex], 32 * nRows * 3); @@ -373,8 +422,8 @@ void RGBmatrixPanel::swapBuffers(boolean copy) { // Dump display contents to the Serial Monitor, adding some formatting to // simplify copy-and-paste of data as a PROGMEM-embedded image for another -// sketch. If using multiple dumps this way, you'll need to edit the -// output to change the 'img' name for each. Data can then be loaded +// sketch. If using multiple dumps this way, you'll need to edit the +// output to change the 'img' name for each. Data can then be loaded // back into the display using a pgm_read_byte() loop. void RGBmatrixPanel::dumpMatrix(void) { @@ -382,15 +431,15 @@ void RGBmatrixPanel::dumpMatrix(void) { Serial.print("\n\n" "#include \n\n" - "static const uint8_t PROGMEM img[] = {\n "); + "static const uint8_t PROGMEM img[] = {\n "); for(i=0; iupdateDisplay(); // Call refresh func for active display - TIFR1 |= TOV1; // Clear Timer1 interrupt flag + activePanel->updateDisplay(); // Call refresh func for active display + TIFR1 |= TOV1; // Clear Timer1 interrupt flag } // Two constants are used in timing each successive BCM interval. @@ -408,28 +457,28 @@ ISR(TIMER1_OVF_vect, ISR_BLOCK) { // ISR_BLOCK important -- see notes later // certain positions in the interrupt code. // CALLOVERHEAD is the number of CPU 'ticks' from the timer overflow // condition (triggering the interrupt) to the first line in the -// updateDisplay() method. It's then assumed (maybe not entirely 100% +// updateDisplay() method. It's then assumed (maybe not entirely 100% // accurately, but close enough) that a similar amount of time will be // needed at the opposite end, restoring regular program flow. // LOOPTIME is the number of 'ticks' spent inside the shortest data- // issuing loop (not actually a 'loop' because it's unrolled, but eh). // Both numbers are rounded up slightly to allow a little wiggle room // should different compilers produce slightly different results. -#define CALLOVERHEAD 60 // Actual value measured = 56 -#define LOOPTIME 200 // Actual value measured = 188 +#define CALLOVERHEAD 60 // Actual value measured = 56 +#define LOOPTIME 200 // Actual value measured = 188 // The "on" time for bitplane 0 (with the shortest BCM interval) can -// then be estimated as LOOPTIME + CALLOVERHEAD * 2. Each successive -// bitplane then doubles the prior amount of time. We can then +// then be estimated as LOOPTIME + CALLOVERHEAD * 2. Each successive +// bitplane then doubles the prior amount of time. We can then // estimate refresh rates from this: // 4 bitplanes = 320 + 640 + 1280 + 2560 = 4800 ticks per row. // 4800 ticks * 16 rows (for 32x32 matrix) = 76800 ticks/frame. // 16M CPU ticks/sec / 76800 ticks/frame = 208.33 Hz. // Actual frame rate will be slightly less due to work being done // during the brief "LEDs off" interval...it's reasonable to say -// "about 200 Hz." The 16x32 matrix only has to scan half as many +// "about 200 Hz." The 16x32 matrix only has to scan half as many // rows...so we could either double the refresh rate (keeping the CPU // load the same), or keep the same refresh rate but halve the CPU -// load. We opted for the latter. +// load. We opted for the latter. // Can also estimate CPU use: bitplanes 1-3 all use 320 ticks to // issue data (the increasing gaps in the timing invervals are then // available to other code), and bitplane 0 takes 920 ticks out of @@ -438,26 +487,26 @@ ISR(TIMER1_OVF_vect, ISR_BLOCK) { // ISR_BLOCK important -- see notes later // From prior calculations, about 4800 ticks happen per row. // CPU use = 1880 / 4800 = ~39% (actual use will be very slightly // higher, again due to code used in the LEDs off interval). -// 16x32 matrix uses about half that CPU load. CPU time could be +// 16x32 matrix uses about half that CPU load. CPU time could be // further adjusted by padding the LOOPTIME value, but refresh rates // will decrease proportionally, and 200 Hz is a decent target. // The flow of the interrupt can be awkward to grasp, because data is // being issued to the LED matrix for the *next* bitplane and/or row -// while the *current* plane/row is being shown. As a result, the +// while the *current* plane/row is being shown. As a result, the // counter variables change between past/present/future tense in mid- // function...hopefully tenses are sufficiently commented. void RGBmatrixPanel::updateDisplay(void) { - uint8_t i, tick, tock, *ptr; + uint8_t i, tick, tock, *ptr; uint16_t t, duration; - *oeport |= oepin; // Disable LED output during row/plane switchover + *oeport |= oepin; // Disable LED output during row/plane switchover *latport |= latpin; // Latch data loaded during *prior* interrupt // Calculate time to next interrupt BEFORE incrementing plane #. // This is because duration is the display time for the data loaded - // on the PRIOR interrupt. CALLOVERHEAD is subtracted from the + // on the PRIOR interrupt. CALLOVERHEAD is subtracted from the // result because that time is implicit between the timer overflow // (interrupt triggered) and the initial LEDs-off line at the start // of this method. @@ -467,33 +516,33 @@ void RGBmatrixPanel::updateDisplay(void) { // Borrowing a technique here from Ray's Logic: // www.rayslogic.com/propeller/Programming/AdafruitRGB/AdafruitRGB.htm // This code cycles through all four planes for each scanline before - // advancing to the next line. While it might seem beneficial to + // advancing to the next line. While it might seem beneficial to // advance lines every time and interleave the planes to reduce // vertical scanning artifacts, in practice with this panel it causes // a green 'ghosting' effect on black pixels, a much worse artifact. - if(++plane >= nPlanes) { // Advance plane counter. Maxed out? - plane = 0; // Yes, reset to plane 0, and - if(++row >= nRows) { // advance row counter. Maxed out? - row = 0; // Yes, reset row counter, then... - if(swapflag == true) { // Swap front/back buffers if requested + if(++plane >= nPlanes) { // Advance plane counter. Maxed out? + plane = 0; // Yes, reset to plane 0, and + if(++row >= nRows) { // advance row counter. Maxed out? + row = 0; // Yes, reset row counter, then... + if(swapflag == true) { // Swap front/back buffers if requested backindex = 1 - backindex; - swapflag = false; + swapflag = false; } buffptr = matrixbuff[1-backindex]; // Reset into front buffer } } else if(plane == 1) { // Plane 0 was loaded on prior interrupt invocation and is about to // latch now, so update the row address lines before we do that: - if(row & 0x1) *addraport |= addrapin; - else *addraport &= ~addrapin; - if(row & 0x2) *addrbport |= addrbpin; - else *addrbport &= ~addrbpin; - if(row & 0x4) *addrcport |= addrcpin; - else *addrcport &= ~addrcpin; + if(row & 0x1) *addraport |= addrapin; + else *addraport &= ~addrapin; + if(row & 0x2) *addrbport |= addrbpin; + else *addrbport &= ~addrbpin; + if(row & 0x4) *addrcport |= addrcpin; + else *addrcport &= ~addrcpin; if(nRows > 8) { - if(row & 0x8) *addrdport |= addrdpin; - else *addrdport &= ~addrdpin; + if(row & 0x8) *addrdport |= addrdpin; + else *addrdport &= ~addrdpin; } } @@ -501,15 +550,15 @@ void RGBmatrixPanel::updateDisplay(void) { // A local register copy can speed some things up: ptr = (uint8_t *)buffptr; - ICR1 = duration; // Set interval for next interrupt - TCNT1 = 0; // Restart interrupt timer - *oeport &= ~oepin; // Re-enable output - *latport &= ~latpin; // Latch down + ICR1 = duration; // Set interval for next interrupt + TCNT1 = 0; // Restart interrupt timer + *oeport &= ~oepin; // Re-enable output + *latport &= ~latpin; // Latch down // Record current state of SCLKPORT register, as well as a second - // copy with the clock bit set. This makes the innnermost data- + // copy with the clock bit set. This makes the innnermost data- // pushing loops faster, as they can just set the PORT state and - // not have to load/modify/store bits every single time. It's a + // not have to load/modify/store bits every single time. It's a // somewhat rude trick that ONLY works because the interrupt // handler is set ISR_BLOCK, halting any other interrupts that // might otherwise also be twiddling the port at the same time @@ -526,16 +575,16 @@ void RGBmatrixPanel::updateDisplay(void) { // A tiny bit of inline assembly is used; compiler doesn't pick // up on opportunity for post-increment addressing mode. // 5 instruction ticks per 'pew' = 160 ticks total - #define pew asm volatile( \ - "ld __tmp_reg__, %a[ptr]+" "\n\t" \ - "out %[data] , __tmp_reg__" "\n\t" \ - "out %[clk] , %[tick]" "\n\t" \ - "out %[clk] , %[tock]" "\n" \ - :: [ptr] "e" (ptr), \ - [data] "I" (_SFR_IO_ADDR(DATAPORT)), \ - [clk] "I" (_SFR_IO_ADDR(SCLKPORT)), \ - [tick] "r" (tick), \ - [tock] "r" (tock)); +#define pew asm volatile( \ +"ld __tmp_reg__, %a[ptr]+" "\n\t" \ +"out %[data] , __tmp_reg__" "\n\t" \ +"out %[clk] , %[tick]" "\n\t" \ +"out %[clk] , %[tock]" "\n" \ +:: [ptr] "e" (ptr), \ +[data] "I" (_SFR_IO_ADDR(DATAPORT)), \ +[clk] "I" (_SFR_IO_ADDR(SCLKPORT)), \ +[tick] "r" (tick), \ +[tock] "r" (tock)); // Loop is unrolled for speed: pew pew pew pew pew pew pew pew @@ -550,20 +599,19 @@ void RGBmatrixPanel::updateDisplay(void) { // Planes 1-3 (handled above) formatted their data "in place," // their layout matching that out the output PORT register (where // 6 bits correspond to output data lines), maximizing throughput - // as no conversion or unpacking is needed. Plane 0 then takes up + // as no conversion or unpacking is needed. Plane 0 then takes up // the slack, with all its data packed into the 2 least bits not - // used by the other planes. This works because the unpacking and + // used by the other planes. This works because the unpacking and // output for plane 0 is handled while plane 3 is being displayed... // because binary coded modulation is used (not PWM), that plane // has the longest display interval, so the extra work fits. for(i=0; i<32; i++) { DATAPORT = - ( ptr[i] << 6) | + ( ptr[i] << 6) | ((ptr[i+32] << 4) & 0x30) | ((ptr[i+64] << 2) & 0x0C); SCLKPORT = tick; // Clock lo SCLKPORT = tock; // Clock hi - } + } } } - From 615d40b16c4fd23c0741347857c04b285444ec61 Mon Sep 17 00:00:00 2001 From: Aerospacesmith Date: Mon, 21 Jul 2014 22:54:03 -0500 Subject: [PATCH 2/9] Update RGBmatrixPanel.h Added unit32_t color support --- RGBmatrixPanel.h | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/RGBmatrixPanel.h b/RGBmatrixPanel.h index 7922a60..96501c1 100644 --- a/RGBmatrixPanel.h +++ b/RGBmatrixPanel.h @@ -1,8 +1,8 @@ #if ARDUINO >= 100 - #include "Arduino.h" +#include "Arduino.h" #else - #include "WProgram.h" - #include "pins_arduino.h" +#include "WProgram.h" +#include "pins_arduino.h" #endif #include "Adafruit_GFX.h" @@ -29,15 +29,19 @@ class RGBmatrixPanel : public Adafruit_GFX { *backBuffer(void); uint16_t Color333(uint8_t r, uint8_t g, uint8_t b), + Color333(uint32_t c), Color444(uint8_t r, uint8_t g, uint8_t b), + Color444(uint32_t c), Color888(uint8_t r, uint8_t g, uint8_t b), + Color888(uint32_t c), Color888(uint8_t r, uint8_t g, uint8_t b, boolean gflag), + Color888(uint32_t c, boolean gflag), ColorHSV(long hue, uint8_t sat, uint8_t val, boolean gflag); private: - uint8_t *matrixbuff[2]; - uint8_t nRows; + uint8_t *matrixbuff[2]; + uint8_t nRows; volatile uint8_t backindex; volatile boolean swapflag; @@ -56,4 +60,3 @@ class RGBmatrixPanel : public Adafruit_GFX { volatile uint8_t row, plane; volatile uint8_t *buffptr; }; - From 9ea0f729d0cfbed36e50bfcd221873469b79829c Mon Sep 17 00:00:00 2001 From: Aerospacesmith Date: Mon, 21 Jul 2014 22:55:11 -0500 Subject: [PATCH 3/9] Update README.txt --- README.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.txt b/README.txt index ade6fb3..16a92e8 100644 --- a/README.txt +++ b/README.txt @@ -6,7 +6,7 @@ Pick one up at http://www.adafruit.com/products/420 & http://www.adafruit.com/pr Written by Limor Fried/Ladyada & Phil Burgess/PaintYourDragon for Adafruit Industries. BSD license, all text above must be included in any redistribution -To download. click the ZIP (download) button, rename the uncompressed folder RGBLEDMatrix. +To download. click the ZIP (download) button, rename the uncompressed folder RGBmatrixPanel. Check that the RGBmatrixPanel folder contains RGBmatrixPanel.cpp and RGBmatrixPanel.h Place the RGBmatrixPanel library folder your arduinosketchfolder/libraries/ folder. @@ -15,4 +15,4 @@ Restart the IDE. We also have a great tutorial on Arduino library installation at: -http://learn.adafruit.com/adafruit-all-about-arduino-libraries-install-use \ No newline at end of file +http://learn.adafruit.com/adafruit-all-about-arduino-libraries-install-use From adad41515883d1e2971b2706d165c3defdb09524 Mon Sep 17 00:00:00 2001 From: Aerospacesmith Date: Mon, 21 Jul 2014 23:02:13 -0500 Subject: [PATCH 4/9] Update RGBmatrixPanel.cpp Added Color method to pack r,g,b values into a single unit32_t value --- RGBmatrixPanel.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/RGBmatrixPanel.cpp b/RGBmatrixPanel.cpp index f70d062..537b2d0 100644 --- a/RGBmatrixPanel.cpp +++ b/RGBmatrixPanel.cpp @@ -187,6 +187,14 @@ void RGBmatrixPanel::begin(void) { // needed when drawing. These next functions are mostly here for the // benefit of older code using one of the original color formats. + + +// Convert separate R,G,B into packed 32-bit RGB color. +// Packed format is always RGB. +uint32_t RGBmatrixPanel::Color(uint8_t r, uint8_t g, uint8_t b) { + return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; +} + // Promote 3/3/3 RGB to Adafruit_GFX 5/6/5 uint16_t RGBmatrixPanel::Color333(uint8_t r, uint8_t g, uint8_t b) { // RRRrrGGGgggBBBbb From b6b6150290bfe98e5abb7c2b76a1f6b7389d3b4b Mon Sep 17 00:00:00 2001 From: Aerospacesmith Date: Mon, 21 Jul 2014 23:02:34 -0500 Subject: [PATCH 5/9] Update RGBmatrixPanel.h Added Color method to pack r,g,b values into a single unit32_t value --- RGBmatrixPanel.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RGBmatrixPanel.h b/RGBmatrixPanel.h index 96501c1..fe74b78 100644 --- a/RGBmatrixPanel.h +++ b/RGBmatrixPanel.h @@ -27,6 +27,8 @@ class RGBmatrixPanel : public Adafruit_GFX { dumpMatrix(void); uint8_t *backBuffer(void); + static uint32_t + Color(uint8_t r, uint8_t g, uint8_t b); uint16_t Color333(uint8_t r, uint8_t g, uint8_t b), Color333(uint32_t c), From f65502262a510aa26deec16eff7466481dffa359 Mon Sep 17 00:00:00 2001 From: Aerospacesmith Date: Mon, 21 Jul 2014 23:27:12 -0500 Subject: [PATCH 6/9] Update RGBmatrixPanel.h r,g,b to uint32_t support --- RGBmatrixPanel.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/RGBmatrixPanel.h b/RGBmatrixPanel.h index fe74b78..4fbc6e9 100644 --- a/RGBmatrixPanel.h +++ b/RGBmatrixPanel.h @@ -31,11 +31,11 @@ class RGBmatrixPanel : public Adafruit_GFX { Color(uint8_t r, uint8_t g, uint8_t b); uint16_t Color333(uint8_t r, uint8_t g, uint8_t b), - Color333(uint32_t c), + Color333(uint32_t c), Color444(uint8_t r, uint8_t g, uint8_t b), - Color444(uint32_t c), + Color444(uint32_t c), Color888(uint8_t r, uint8_t g, uint8_t b), - Color888(uint32_t c), + Color888(uint32_t c), Color888(uint8_t r, uint8_t g, uint8_t b, boolean gflag), Color888(uint32_t c, boolean gflag), ColorHSV(long hue, uint8_t sat, uint8_t val, boolean gflag); From 45d6cd6acdd6d31eb8fadbbd77f81e7322de00a8 Mon Sep 17 00:00:00 2001 From: Aerospacesmith Date: Mon, 21 Jul 2014 23:28:25 -0500 Subject: [PATCH 7/9] r,g,b to uint32_t support r,g,b to uint32_t support --- RGBmatrixPanel.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/RGBmatrixPanel.cpp b/RGBmatrixPanel.cpp index 537b2d0..a33f0fb 100644 --- a/RGBmatrixPanel.cpp +++ b/RGBmatrixPanel.cpp @@ -188,7 +188,6 @@ void RGBmatrixPanel::begin(void) { // benefit of older code using one of the original color formats. - // Convert separate R,G,B into packed 32-bit RGB color. // Packed format is always RGB. uint32_t RGBmatrixPanel::Color(uint8_t r, uint8_t g, uint8_t b) { From c280762fae593c6d0e3d6a6fb8d7362d643dcf51 Mon Sep 17 00:00:00 2001 From: Aerospacesmith Date: Tue, 22 Jul 2014 12:02:31 -0500 Subject: [PATCH 8/9] Update RGBmatrixPanel.cpp Fixed Color888 --- RGBmatrixPanel.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/RGBmatrixPanel.cpp b/RGBmatrixPanel.cpp index a33f0fb..481de60 100644 --- a/RGBmatrixPanel.cpp +++ b/RGBmatrixPanel.cpp @@ -266,6 +266,7 @@ uint16_t RGBmatrixPanel::Color888( uint16_t RGBmatrixPanel::Color888( uint32_t c, boolean gflag) { if(gflag) { // Gamma-corrected color? + uint8_t r = (uint8_t)(c >> 16), g = (uint8_t)(c >> 8), b = (uint8_t)c; From 73efe4280d2d9bde7220c06aba2ea11fd71dfd15 Mon Sep 17 00:00:00 2001 From: Aerospacesmith Date: Tue, 22 Jul 2014 12:06:25 -0500 Subject: [PATCH 9/9] Update RGBmatrixPanel.cpp Fix Color888, again --- RGBmatrixPanel.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/RGBmatrixPanel.cpp b/RGBmatrixPanel.cpp index 481de60..b80c542 100644 --- a/RGBmatrixPanel.cpp +++ b/RGBmatrixPanel.cpp @@ -265,11 +265,11 @@ uint16_t RGBmatrixPanel::Color888( // 8/8/8 -> gamma -> 5/6/5 uint16_t RGBmatrixPanel::Color888( uint32_t c, boolean gflag) { - if(gflag) { // Gamma-corrected color? uint8_t - r = (uint8_t)(c >> 16), - g = (uint8_t)(c >> 8), - b = (uint8_t)c; + r = (uint8_t)(c >> 16), + g = (uint8_t)(c >> 8), + b = (uint8_t)c; + if(gflag) { // Gamma-corrected color? r = pgm_read_byte(&gamma[r]); // Gamma correction table maps g = pgm_read_byte(&gamma[g]); // 8-bit input to 4-bit output b = pgm_read_byte(&gamma[b]);