Adafruit-RGB_matrix_Panel(32*16)

Dependencies:   Adafruit-GFX

Committer:
lelect
Date:
Sat May 24 17:33:23 2014 +0000
Revision:
3:aa3762e0dfee
Parent:
2:6136465ffd3a
Child:
4:0ff6053c4bb2
Using the matrix buffer is unknown;

Who changed what in which revision?

UserRevisionLine numberNew contents of line
lelect 2:6136465ffd3a 1 #define DEBUG
lelect 3:aa3762e0dfee 2 #undef DEBUG
lelect 0:06d9443a018f 3 #include "RGBmatrixPanel.h"
lelect 0:06d9443a018f 4 #include "gamma.h"
lelect 0:06d9443a018f 5
lelect 0:06d9443a018f 6 #define nPlanes 4
lelect 0:06d9443a018f 7
lelect 0:06d9443a018f 8 // The fact that the display driver interrupt stuff is tied to the
lelect 0:06d9443a018f 9 // singular Timer1 doesn't really take well to object orientation with
lelect 0:06d9443a018f 10 // multiple RGBmatrixPanel instances. The solution at present is to
lelect 0:06d9443a018f 11 // allow instances, but only one is active at any given time, via its
lelect 0:06d9443a018f 12 // begin() method. The implementation is still incomplete in parts;
lelect 0:06d9443a018f 13 // the prior active panel really should be gracefully disabled, and a
lelect 0:06d9443a018f 14 // stop() method should perhaps be added...assuming multiple instances
lelect 0:06d9443a018f 15 // are even an actual need.
lelect 0:06d9443a018f 16 static RGBmatrixPanel *activePanel = NULL;
lelect 0:06d9443a018f 17
lelect 0:06d9443a018f 18 // Code common to both the 16x32 and 32x32 constructors:
lelect 2:6136465ffd3a 19 void RGBmatrixPanel::init(uint8_t rows, bool dbuf)
lelect 0:06d9443a018f 20 {
lelect 0:06d9443a018f 21 nRows = rows; // Number of multiplexed rows; actual height is 2X this
lelect 0:06d9443a018f 22 // Allocate and initialize matrix buffer:
lelect 3:aa3762e0dfee 23 int buffsize = 32*nRows*3, // x3 = 3 bytes holds 4 planes "packed"
lelect 0:06d9443a018f 24 allocsize = (dbuf == true) ? (buffsize * 2) : buffsize;
lelect 0:06d9443a018f 25 if(NULL == (matrixbuff[0] = (uint8_t *)malloc(allocsize))) return;
lelect 0:06d9443a018f 26 memset(matrixbuff[0], 0, allocsize);
lelect 0:06d9443a018f 27 // If not double-buffered, both buffers then point to the same address:
lelect 0:06d9443a018f 28 matrixbuff[1] = (dbuf == true) ? &matrixbuff[0][buffsize] : matrixbuff[0];
lelect 0:06d9443a018f 29
lelect 0:06d9443a018f 30 plane = nPlanes - 1;
lelect 0:06d9443a018f 31 row = nRows - 1;
lelect 0:06d9443a018f 32 swapflag = false;
lelect 0:06d9443a018f 33 backindex = 0; // Array index of back buffer
lelect 0:06d9443a018f 34 }
lelect 0:06d9443a018f 35
lelect 0:06d9443a018f 36 // Constructor for 16x32 panel:
lelect 3:aa3762e0dfee 37 RGBmatrixPanel::RGBmatrixPanel(PinName r1,PinName r2,PinName g1,PinName g2,PinName b1,PinName b2,PinName a,PinName b, PinName c, PinName sclk, PinName latch, PinName oe, bool dbuf)
lelect 2:6136465ffd3a 38 :Adafruit_GFX(32, 16),
lelect 2:6136465ffd3a 39 _sclk(sclk),
lelect 2:6136465ffd3a 40 _latch(latch),
lelect 2:6136465ffd3a 41 _oe(oe),
lelect 3:aa3762e0dfee 42 _d(NC),
lelect 3:aa3762e0dfee 43 _dataBus(r1,g1,b1,r2,g2,b2),
lelect 3:aa3762e0dfee 44 _rowBus(c,b,a)
lelect 0:06d9443a018f 45 {
lelect 2:6136465ffd3a 46 init(8, dbuf);
lelect 0:06d9443a018f 47 }
lelect 0:06d9443a018f 48
lelect 0:06d9443a018f 49 // Constructor for 32x32 panel:
lelect 3:aa3762e0dfee 50 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)
lelect 2:6136465ffd3a 51 :Adafruit_GFX(32, 32),
lelect 2:6136465ffd3a 52 _sclk(sclk),
lelect 2:6136465ffd3a 53 _latch(latch),
lelect 2:6136465ffd3a 54 _oe(oe),
lelect 3:aa3762e0dfee 55 _d(d),// Init 32x32-specific elements:
lelect 3:aa3762e0dfee 56 _dataBus(r1,g1,b1,r2,g2,b2),
lelect 3:aa3762e0dfee 57 _rowBus(c,b,a)
lelect 0:06d9443a018f 58 {
lelect 2:6136465ffd3a 59 init(16,dbuf);
lelect 0:06d9443a018f 60 }
lelect 0:06d9443a018f 61
lelect 0:06d9443a018f 62 void RGBmatrixPanel::begin(void)
lelect 0:06d9443a018f 63 {
lelect 0:06d9443a018f 64
lelect 0:06d9443a018f 65 backindex = 0; // Back buffer
lelect 0:06d9443a018f 66 buffptr = matrixbuff[1 - backindex]; // -> front buffer
lelect 0:06d9443a018f 67 activePanel = this; // For interrupt hander
lelect 0:06d9443a018f 68
lelect 0:06d9443a018f 69 // The high six bits of the data port are set as outputs;
lelect 0:06d9443a018f 70 // Might make this configurable in the future, but not yet.
lelect 2:6136465ffd3a 71 /*
lelect 0:06d9443a018f 72 DATADIR = B11111100;
lelect 0:06d9443a018f 73 DATAPORT = 0;
lelect 2:6136465ffd3a 74 */
lelect 0:06d9443a018f 75
lelect 3:aa3762e0dfee 76 // Set up Timer for interrupt:
lelect 3:aa3762e0dfee 77 _refresh.attach(activePanel,(&RGBmatrixPanel::updateDisplay),0.001); //updateDisplay() called every 1ms
lelect 2:6136465ffd3a 78 /*
lelect 3:aa3762e0dfee 79 TCCR1A = _BV(WGM11); // Mode 14 (fast PWM), OC1A off
lelect 3:aa3762e0dfee 80 TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10); // Mode 14, no prescale
lelect 3:aa3762e0dfee 81 ICR1 = 100;
lelect 3:aa3762e0dfee 82 TIMSK1 |= _BV(TOIE1); // Enable Timer1 interrupt
lelect 3:aa3762e0dfee 83 sei(); // Enable global interrupts
lelect 3:aa3762e0dfee 84 */
lelect 0:06d9443a018f 85 }
lelect 0:06d9443a018f 86
lelect 0:06d9443a018f 87 // Original RGBmatrixPanel library used 3/3/3 color. Later version used
lelect 0:06d9443a018f 88 // 4/4/4. Then Adafruit_GFX (core library used across all Adafruit
lelect 0:06d9443a018f 89 // display devices now) standardized on 5/6/5. The matrix still operates
lelect 0:06d9443a018f 90 // internally on 4/4/4 color, but all the graphics functions are written
lelect 0:06d9443a018f 91 // to expect 5/6/5...the matrix lib will truncate the color components as
lelect 0:06d9443a018f 92 // needed when drawing. These next functions are mostly here for the
lelect 0:06d9443a018f 93 // benefit of older code using one of the original color formats.
lelect 0:06d9443a018f 94
lelect 0:06d9443a018f 95 // Promote 3/3/3 RGB to Adafruit_GFX 5/6/5
lelect 0:06d9443a018f 96 uint16_t RGBmatrixPanel::Color333(uint8_t r, uint8_t g, uint8_t b)
lelect 0:06d9443a018f 97 {
lelect 0:06d9443a018f 98 // RRRrrGGGgggBBBbb
lelect 0:06d9443a018f 99 return ((r & 0x7) << 13) | ((r & 0x6) << 10) |
lelect 0:06d9443a018f 100 ((g & 0x7) << 8) | ((g & 0x7) << 5) |
lelect 0:06d9443a018f 101 ((b & 0x7) << 2) | ((b & 0x6) >> 1);
lelect 0:06d9443a018f 102 }
lelect 0:06d9443a018f 103
lelect 0:06d9443a018f 104 // Promote 4/4/4 RGB to Adafruit_GFX 5/6/5
lelect 0:06d9443a018f 105 uint16_t RGBmatrixPanel::Color444(uint8_t r, uint8_t g, uint8_t b)
lelect 0:06d9443a018f 106 {
lelect 0:06d9443a018f 107 // RRRRrGGGGggBBBBb
lelect 0:06d9443a018f 108 return ((r & 0xF) << 12) | ((r & 0x8) << 8) |
lelect 0:06d9443a018f 109 ((g & 0xF) << 7) | ((g & 0xC) << 3) |
lelect 0:06d9443a018f 110 ((b & 0xF) << 1) | ((b & 0x8) >> 3);
lelect 0:06d9443a018f 111 }
lelect 0:06d9443a018f 112
lelect 0:06d9443a018f 113 // Demote 8/8/8 to Adafruit_GFX 5/6/5
lelect 0:06d9443a018f 114 // If no gamma flag passed, assume linear color
lelect 0:06d9443a018f 115 uint16_t RGBmatrixPanel::Color888(uint8_t r, uint8_t g, uint8_t b)
lelect 0:06d9443a018f 116 {
lelect 0:06d9443a018f 117 return ((r & 0xF8) << 11) | ((g & 0xFC) << 5) | (b >> 3);
lelect 0:06d9443a018f 118 }
lelect 0:06d9443a018f 119
lelect 0:06d9443a018f 120 // 8/8/8 -> gamma -> 5/6/5
lelect 1:0078213d3fa4 121 uint16_t RGBmatrixPanel::Color888(uint8_t r, uint8_t g, uint8_t b, bool gflag)
lelect 0:06d9443a018f 122 {
lelect 0:06d9443a018f 123 if(gflag) { // Gamma-corrected color?
lelect 1:0078213d3fa4 124 r = gamma[r]; // Gamma correction table maps
lelect 1:0078213d3fa4 125 g = gamma[g]; // 8-bit input to 4-bit output
lelect 1:0078213d3fa4 126 b = gamma[b];
lelect 0:06d9443a018f 127 return (r << 12) | ((r & 0x8) << 8) | // 4/4/4 -> 5/6/5
lelect 0:06d9443a018f 128 (g << 7) | ((g & 0xC) << 3) |
lelect 0:06d9443a018f 129 (b << 1) | ( b >> 3);
lelect 0:06d9443a018f 130 } // else linear (uncorrected) color
lelect 0:06d9443a018f 131 return ((r & 0xF8) << 11) | ((g & 0xFC) << 5) | (b >> 3);
lelect 0:06d9443a018f 132 }
lelect 0:06d9443a018f 133
lelect 1:0078213d3fa4 134 uint16_t RGBmatrixPanel::ColorHSV(long hue, uint8_t sat, uint8_t val, bool gflag)
lelect 0:06d9443a018f 135 {
lelect 0:06d9443a018f 136
lelect 0:06d9443a018f 137 uint8_t r, g, b, lo;
lelect 0:06d9443a018f 138 uint16_t s1, v1;
lelect 0:06d9443a018f 139
lelect 0:06d9443a018f 140 // Hue
lelect 0:06d9443a018f 141 hue %= 1536; // -1535 to +1535
lelect 0:06d9443a018f 142 if(hue < 0) hue += 1536; // 0 to +1535
lelect 0:06d9443a018f 143 lo = hue & 255; // Low byte = primary/secondary color mix
lelect 0:06d9443a018f 144 switch(hue >> 8) { // High byte = sextant of colorwheel
lelect 0:06d9443a018f 145 case 0 :
lelect 0:06d9443a018f 146 r = 255 ;
lelect 0:06d9443a018f 147 g = lo ;
lelect 0:06d9443a018f 148 b = 0 ;
lelect 0:06d9443a018f 149 break; // R to Y
lelect 0:06d9443a018f 150 case 1 :
lelect 0:06d9443a018f 151 r = 255 - lo;
lelect 0:06d9443a018f 152 g = 255 ;
lelect 0:06d9443a018f 153 b = 0 ;
lelect 0:06d9443a018f 154 break; // Y to G
lelect 0:06d9443a018f 155 case 2 :
lelect 0:06d9443a018f 156 r = 0 ;
lelect 0:06d9443a018f 157 g = 255 ;
lelect 0:06d9443a018f 158 b = lo ;
lelect 0:06d9443a018f 159 break; // G to C
lelect 0:06d9443a018f 160 case 3 :
lelect 0:06d9443a018f 161 r = 0 ;
lelect 0:06d9443a018f 162 g = 255 - lo;
lelect 0:06d9443a018f 163 b = 255 ;
lelect 0:06d9443a018f 164 break; // C to B
lelect 0:06d9443a018f 165 case 4 :
lelect 0:06d9443a018f 166 r = lo ;
lelect 0:06d9443a018f 167 g = 0 ;
lelect 0:06d9443a018f 168 b = 255 ;
lelect 0:06d9443a018f 169 break; // B to M
lelect 0:06d9443a018f 170 default:
lelect 0:06d9443a018f 171 r = 255 ;
lelect 0:06d9443a018f 172 g = 0 ;
lelect 0:06d9443a018f 173 b = 255 - lo;
lelect 0:06d9443a018f 174 break; // M to R
lelect 0:06d9443a018f 175 }
lelect 0:06d9443a018f 176
lelect 0:06d9443a018f 177 // Saturation: add 1 so range is 1 to 256, allowig a quick shift operation
lelect 0:06d9443a018f 178 // on the result rather than a costly divide, while the type upgrade to int
lelect 0:06d9443a018f 179 // avoids repeated type conversions in both directions.
lelect 0:06d9443a018f 180 s1 = sat + 1;
lelect 0:06d9443a018f 181 r = 255 - (((255 - r) * s1) >> 8);
lelect 0:06d9443a018f 182 g = 255 - (((255 - g) * s1) >> 8);
lelect 0:06d9443a018f 183 b = 255 - (((255 - b) * s1) >> 8);
lelect 0:06d9443a018f 184
lelect 0:06d9443a018f 185 // Value (brightness) & 16-bit color reduction: similar to above, add 1
lelect 0:06d9443a018f 186 // to allow shifts, and upgrade to int makes other conversions implicit.
lelect 0:06d9443a018f 187 v1 = val + 1;
lelect 0:06d9443a018f 188 if(gflag) { // Gamma-corrected color?
lelect 2:6136465ffd3a 189 r = gamma[(r * v1) >> 8]; // Gamma correction table maps
lelect 2:6136465ffd3a 190 g = gamma[(g * v1) >> 8]; // 8-bit input to 4-bit output
lelect 2:6136465ffd3a 191 b = gamma[(b * v1) >> 8];
lelect 2:6136465ffd3a 192 //before pgm_read_byte(&gamma[(b * v1) >> 8])
lelect 0:06d9443a018f 193 } else { // linear (uncorrected) color
lelect 0:06d9443a018f 194 r = (r * v1) >> 12; // 4-bit results
lelect 0:06d9443a018f 195 g = (g * v1) >> 12;
lelect 0:06d9443a018f 196 b = (b * v1) >> 12;
lelect 0:06d9443a018f 197 }
lelect 0:06d9443a018f 198 return (r << 12) | ((r & 0x8) << 8) | // 4/4/4 -> 5/6/5
lelect 0:06d9443a018f 199 (g << 7) | ((g & 0xC) << 3) |
lelect 0:06d9443a018f 200 (b << 1) | ( b >> 3);
lelect 0:06d9443a018f 201 }
lelect 0:06d9443a018f 202
lelect 0:06d9443a018f 203 void RGBmatrixPanel::drawPixel(int16_t x, int16_t y, uint16_t c)
lelect 0:06d9443a018f 204 {
lelect 0:06d9443a018f 205 uint8_t r, g, b, bit, limit, *ptr;
lelect 0:06d9443a018f 206 if((x < 0) || (x >= _width) || (y < 0) || (y >= _height)) return;
lelect 0:06d9443a018f 207 switch(rotation) {
lelect 0:06d9443a018f 208 case 1:
lelect 0:06d9443a018f 209 swap(x, y);
lelect 0:06d9443a018f 210 x = _rawWidth - 1 - x;
lelect 0:06d9443a018f 211 break;
lelect 0:06d9443a018f 212 case 2:
lelect 0:06d9443a018f 213 x = _rawWidth - 1 - x;
lelect 0:06d9443a018f 214 y = _rawHeight - 1 - y;
lelect 0:06d9443a018f 215 break;
lelect 0:06d9443a018f 216 case 3:
lelect 0:06d9443a018f 217 swap(x, y);
lelect 0:06d9443a018f 218 y = _rawHeight - 1 - y;
lelect 0:06d9443a018f 219 break;
lelect 0:06d9443a018f 220 }
lelect 0:06d9443a018f 221
lelect 0:06d9443a018f 222 // Adafruit_GFX uses 16-bit color in 5/6/5 format, while matrix needs
lelect 0:06d9443a018f 223 // 4/4/4. Pluck out relevant bits while separating into R,G,B:
lelect 0:06d9443a018f 224 r = c >> 12; // RRRRrggggggbbbbb
lelect 0:06d9443a018f 225 g = (c >> 7) & 0xF; // rrrrrGGGGggbbbbb
lelect 0:06d9443a018f 226 b = (c >> 1) & 0xF; // rrrrrggggggBBBBb
lelect 0:06d9443a018f 227 // Loop counter stuff
lelect 0:06d9443a018f 228 bit = 2;
lelect 0:06d9443a018f 229 limit = 1 << nPlanes;
lelect 0:06d9443a018f 230
lelect 0:06d9443a018f 231 if(y < nRows) {
lelect 2:6136465ffd3a 232 // Data for the upper half of the display is stored in the lower bits of each byte.
lelect 2:6136465ffd3a 233 ptr = &matrixbuff[backindex][y*_rawWidth*(nPlanes-1) + x]; // Base addr
lelect 0:06d9443a018f 234 // Plane 0 is a tricky case -- its data is spread about,
lelect 0:06d9443a018f 235 // stored in least two bits not used by the other planes.
lelect 2:6136465ffd3a 236 ptr[64] &= ~(_BV(0)|_BV(1)); // Plane 0 R,G mask(0b11111100) out in one op
lelect 2:6136465ffd3a 237 if(r & 1) ptr[64] |= _BV(0); // Plane 0 R: 64 bytes ahead, bit 0
lelect 2:6136465ffd3a 238 if(g & 1) ptr[64] |= _BV(1); // Plane 0 G: 64 bytes ahead, bit 1
lelect 2:6136465ffd3a 239 if(b & 1) ptr[32] |= _BV(0); // Plane 0 B: 32 bytes ahead, bit 0
lelect 2:6136465ffd3a 240 else ptr[32] &= ~_BV(0); // Plane 0 B unset; mask out
lelect 0:06d9443a018f 241 // The remaining three image planes are more normal-ish.
lelect 0:06d9443a018f 242 // Data is stored in the high 6 bits so it can be quickly
lelect 0:06d9443a018f 243 // copied to the DATAPORT register w/6 output lines.
lelect 0:06d9443a018f 244 for(; bit < limit; bit <<= 1) {
lelect 3:aa3762e0dfee 245 ptr[0] &= ~(_BV(2)|_BV(3)|_BV(4)); // Mask(0b00011100) out R,G,B in one op
lelect 2:6136465ffd3a 246 if(r & bit) *ptr |= _BV(2); // Plane N R: bit 2
lelect 2:6136465ffd3a 247 if(g & bit) *ptr |= _BV(3); // Plane N G: bit 3
lelect 2:6136465ffd3a 248 if(b & bit) *ptr |= _BV(4); // Plane N B: bit 4
lelect 2:6136465ffd3a 249 ptr += _rawWidth; // Advance to next bit plane
lelect 0:06d9443a018f 250 }
lelect 0:06d9443a018f 251 } else {
lelect 2:6136465ffd3a 252 // Data for the lower half of the display is stored in the upper bits, except for the plane 0 stuff, using 2 least bits.
lelect 2:6136465ffd3a 253 ptr = &matrixbuff[backindex][(y-nRows)*_rawWidth*(nPlanes-1) + x];
lelect 2:6136465ffd3a 254 *ptr &= ~(_BV(0)|_BV(1)); // Plane 0 G,B mask out in one op
lelect 2:6136465ffd3a 255 if(r & 1) ptr[32] |= _BV(1); // Plane 0 R: 32 bytes ahead, bit 1
lelect 2:6136465ffd3a 256 else ptr[32] &= ~_BV(2); // Plane 0 R unset; mask out
lelect 2:6136465ffd3a 257 if(g & 1) *ptr |= _BV(0); // Plane 0 G: bit 0
lelect 2:6136465ffd3a 258 if(b & 1) *ptr |= _BV(1); // Plane 0 B: bit 0
lelect 0:06d9443a018f 259 for(; bit < limit; bit <<= 1) {
lelect 2:6136465ffd3a 260 *ptr &= ~(_BV(5)|_BV(6)|_BV(7)); // Mask out R,G,B in one op
lelect 2:6136465ffd3a 261 if(r & bit) *ptr |= _BV(5); // Plane N R: bit 5
lelect 2:6136465ffd3a 262 if(g & bit) *ptr |= _BV(6); // Plane N G: bit 6
lelect 2:6136465ffd3a 263 if(b & bit) *ptr |= _BV(7); // Plane N B: bit 7
lelect 2:6136465ffd3a 264 ptr += _rawWidth; // Advance to next bit plane
lelect 0:06d9443a018f 265 }
lelect 0:06d9443a018f 266 }
lelect 0:06d9443a018f 267 }
lelect 0:06d9443a018f 268
lelect 0:06d9443a018f 269 void RGBmatrixPanel::fillScreen(uint16_t c)
lelect 0:06d9443a018f 270 {
lelect 0:06d9443a018f 271 if((c == 0x0000) || (c == 0xffff)) {
lelect 0:06d9443a018f 272 // For black or white, all bits in frame buffer will be identically
lelect 0:06d9443a018f 273 // set or unset (regardless of weird bit packing), so it's OK to just
lelect 0:06d9443a018f 274 // quickly memset the whole thing:
lelect 0:06d9443a018f 275 memset(matrixbuff[backindex], c, 32 * nRows * 3);
lelect 0:06d9443a018f 276 } else {
lelect 0:06d9443a018f 277 // Otherwise, need to handle it the long way:
lelect 0:06d9443a018f 278 Adafruit_GFX::fillScreen(c);
lelect 0:06d9443a018f 279 }
lelect 0:06d9443a018f 280 }
lelect 0:06d9443a018f 281
lelect 0:06d9443a018f 282 // Return address of back buffer -- can then load/store data directly
lelect 0:06d9443a018f 283 uint8_t *RGBmatrixPanel::backBuffer()
lelect 0:06d9443a018f 284 {
lelect 0:06d9443a018f 285 return matrixbuff[backindex];
lelect 0:06d9443a018f 286 }
lelect 0:06d9443a018f 287
lelect 0:06d9443a018f 288 // For smooth animation -- drawing always takes place in the "back" buffer;
lelect 0:06d9443a018f 289 // this method pushes it to the "front" for display. Passing "true", the
lelect 0:06d9443a018f 290 // updated display contents are then copied to the new back buffer and can
lelect 0:06d9443a018f 291 // be incrementally modified. If "false", the back buffer then contains
lelect 0:06d9443a018f 292 // the old front buffer contents -- your code can either clear this or
lelect 0:06d9443a018f 293 // draw over every pixel. (No effect if double-buffering is not enabled.)
lelect 0:06d9443a018f 294 void RGBmatrixPanel::swapBuffers(bool copy)
lelect 0:06d9443a018f 295 {
lelect 3:aa3762e0dfee 296 log_debug("call swapBuffers %s","\r\n");
lelect 0:06d9443a018f 297 if(matrixbuff[0] != matrixbuff[1]) {
lelect 0:06d9443a018f 298 // To avoid 'tearing' display, actual swap takes place in the interrupt
lelect 0:06d9443a018f 299 // handler, at the end of a complete screen refresh cycle.
lelect 0:06d9443a018f 300 swapflag = true; // Set flag here, then...
lelect 2:6136465ffd3a 301 while(swapflag == true) wait_ms(1); // wait for interrupt to clear it
lelect 3:aa3762e0dfee 302 if(copy == true) {
lelect 3:aa3762e0dfee 303 log_debug("\tmemcpy %s","\r\n");
lelect 0:06d9443a018f 304 memcpy(matrixbuff[backindex], matrixbuff[1-backindex], 32 * nRows * 3);
lelect 3:aa3762e0dfee 305 } else {
lelect 3:aa3762e0dfee 306 log_debug("\tnot memcpy %s","\r\n");
lelect 3:aa3762e0dfee 307 }
lelect 0:06d9443a018f 308 }
lelect 0:06d9443a018f 309 }
lelect 0:06d9443a018f 310
lelect 0:06d9443a018f 311 // Dump display contents to the Serial Monitor, adding some formatting to
lelect 0:06d9443a018f 312 // simplify copy-and-paste of data as a PROGMEM-embedded image for another
lelect 0:06d9443a018f 313 // sketch. If using multiple dumps this way, you'll need to edit the
lelect 0:06d9443a018f 314 // output to change the 'img' name for each. Data can then be loaded
lelect 0:06d9443a018f 315 // back into the display using a pgm_read_byte() loop.
lelect 0:06d9443a018f 316 void RGBmatrixPanel::dumpMatrix(void)
lelect 0:06d9443a018f 317 {
lelect 2:6136465ffd3a 318 log_debug("call dumpMatrix%s","\r\n");
lelect 3:aa3762e0dfee 319 int buffsize=32*nRows*3;
lelect 3:aa3762e0dfee 320 for(int item=0; item<buffsize; item++) {
lelect 3:aa3762e0dfee 321 if(item%(32*nRows)==0) {
lelect 3:aa3762e0dfee 322 for(int i=0; i<32*5; i++) {
lelect 3:aa3762e0dfee 323 log_debug("-%c",'\0');
lelect 3:aa3762e0dfee 324 }
lelect 2:6136465ffd3a 325 log_debug("-%s","\r\n");
lelect 2:6136465ffd3a 326 }
lelect 3:aa3762e0dfee 327 log_debug("0x%02X",matrixbuff[backindex][item]);
lelect 3:aa3762e0dfee 328 if((item%32)==31) log_debug(",\r\n");
lelect 3:aa3762e0dfee 329 else log_debug(",");
lelect 0:06d9443a018f 330 }
lelect 3:aa3762e0dfee 331 log_debug("%s","\r\n");
lelect 0:06d9443a018f 332 }
lelect 0:06d9443a018f 333
lelect 0:06d9443a018f 334 // -------------------- Interrupt handler stuff --------------------
lelect 2:6136465ffd3a 335 /*
lelect 0:06d9443a018f 336 ISR(TIMER1_OVF_vect, ISR_BLOCK) // ISR_BLOCK important -- see notes later
lelect 0:06d9443a018f 337 {
lelect 0:06d9443a018f 338 activePanel->updateDisplay(); // Call refresh func for active display
lelect 0:06d9443a018f 339 TIFR1 |= TOV1; // Clear Timer1 interrupt flag
lelect 0:06d9443a018f 340 }
lelect 2:6136465ffd3a 341 */
lelect 0:06d9443a018f 342 // Two constants are used in timing each successive BCM interval.
lelect 0:06d9443a018f 343 // These were found empirically, by checking the value of TCNT1 at
lelect 0:06d9443a018f 344 // certain positions in the interrupt code.
lelect 0:06d9443a018f 345 // CALLOVERHEAD is the number of CPU 'ticks' from the timer overflow
lelect 0:06d9443a018f 346 // condition (triggering the interrupt) to the first line in the
lelect 0:06d9443a018f 347 // updateDisplay() method. It's then assumed (maybe not entirely 100%
lelect 0:06d9443a018f 348 // accurately, but close enough) that a similar amount of time will be
lelect 0:06d9443a018f 349 // needed at the opposite end, restoring regular program flow.
lelect 0:06d9443a018f 350 // LOOPTIME is the number of 'ticks' spent inside the shortest data-
lelect 0:06d9443a018f 351 // issuing loop (not actually a 'loop' because it's unrolled, but eh).
lelect 0:06d9443a018f 352 // Both numbers are rounded up slightly to allow a little wiggle room
lelect 0:06d9443a018f 353 // should different compilers produce slightly different results.
lelect 0:06d9443a018f 354 #define CALLOVERHEAD 60 // Actual value measured = 56
lelect 0:06d9443a018f 355 #define LOOPTIME 200 // Actual value measured = 188
lelect 0:06d9443a018f 356 // The "on" time for bitplane 0 (with the shortest BCM interval) can
lelect 0:06d9443a018f 357 // then be estimated as LOOPTIME + CALLOVERHEAD * 2. Each successive
lelect 0:06d9443a018f 358 // bitplane then doubles the prior amount of time. We can then
lelect 0:06d9443a018f 359 // estimate refresh rates from this:
lelect 0:06d9443a018f 360 // 4 bitplanes = 320 + 640 + 1280 + 2560 = 4800 ticks per row.
lelect 0:06d9443a018f 361 // 4800 ticks * 16 rows (for 32x32 matrix) = 76800 ticks/frame.
lelect 0:06d9443a018f 362 // 16M CPU ticks/sec / 76800 ticks/frame = 208.33 Hz.
lelect 0:06d9443a018f 363 // Actual frame rate will be slightly less due to work being done
lelect 0:06d9443a018f 364 // during the brief "LEDs off" interval...it's reasonable to say
lelect 0:06d9443a018f 365 // "about 200 Hz." The 16x32 matrix only has to scan half as many
lelect 0:06d9443a018f 366 // rows...so we could either double the refresh rate (keeping the CPU
lelect 0:06d9443a018f 367 // load the same), or keep the same refresh rate but halve the CPU
lelect 0:06d9443a018f 368 // load. We opted for the latter.
lelect 0:06d9443a018f 369 // Can also estimate CPU use: bitplanes 1-3 all use 320 ticks to
lelect 0:06d9443a018f 370 // issue data (the increasing gaps in the timing invervals are then
lelect 0:06d9443a018f 371 // available to other code), and bitplane 0 takes 920 ticks out of
lelect 0:06d9443a018f 372 // the 2560 tick interval.
lelect 0:06d9443a018f 373 // 320 * 3 + 920 = 1880 ticks spent in interrupt code, per row.
lelect 0:06d9443a018f 374 // From prior calculations, about 4800 ticks happen per row.
lelect 0:06d9443a018f 375 // CPU use = 1880 / 4800 = ~39% (actual use will be very slightly
lelect 0:06d9443a018f 376 // higher, again due to code used in the LEDs off interval).
lelect 0:06d9443a018f 377 // 16x32 matrix uses about half that CPU load. CPU time could be
lelect 0:06d9443a018f 378 // further adjusted by padding the LOOPTIME value, but refresh rates
lelect 0:06d9443a018f 379 // will decrease proportionally, and 200 Hz is a decent target.
lelect 0:06d9443a018f 380
lelect 0:06d9443a018f 381 // The flow of the interrupt can be awkward to grasp, because data is
lelect 0:06d9443a018f 382 // being issued to the LED matrix for the *next* bitplane and/or row
lelect 0:06d9443a018f 383 // while the *current* plane/row is being shown. As a result, the
lelect 0:06d9443a018f 384 // counter variables change between past/present/future tense in mid-
lelect 0:06d9443a018f 385 // function...hopefully tenses are sufficiently commented.
lelect 0:06d9443a018f 386
lelect 0:06d9443a018f 387 void RGBmatrixPanel::updateDisplay(void)
lelect 0:06d9443a018f 388 {
lelect 3:aa3762e0dfee 389 //log_debug("call updateDisplay\t(plane,row)=(%d,%d)\r\n",plane,row);
lelect 3:aa3762e0dfee 390 _oe=1;
lelect 3:aa3762e0dfee 391 _latch=1;
lelect 3:aa3762e0dfee 392 if(++plane >= nPlanes) { // Advance plane counter. Maxed out?
lelect 0:06d9443a018f 393 plane = 0; // Yes, reset to plane 0, and
lelect 0:06d9443a018f 394 if(++row >= nRows) { // advance row counter. Maxed out?
lelect 3:aa3762e0dfee 395 row= 0; // Yes, reset row counter, then...
lelect 3:aa3762e0dfee 396 if(swapflag == true) { // Swap front/back buffers if requested
lelect 0:06d9443a018f 397 backindex = 1 - backindex;
lelect 3:aa3762e0dfee 398 log_debug("\t\treset swapflag%s","\r\n");
lelect 0:06d9443a018f 399 swapflag = false;
lelect 0:06d9443a018f 400 }
lelect 3:aa3762e0dfee 401 log_debug("\tReset into front buffer[%d]%s",backindex,"\r\n");
lelect 0:06d9443a018f 402 buffptr = matrixbuff[1-backindex]; // Reset into front buffer
lelect 0:06d9443a018f 403 }
lelect 0:06d9443a018f 404 } else if(plane == 1) {
lelect 3:aa3762e0dfee 405 log_debug("\r\n\tset row@(%d,%d)\r\n",plane,row);
lelect 3:aa3762e0dfee 406
lelect 3:aa3762e0dfee 407 /*
lelect 0:06d9443a018f 408 // Plane 0 was loaded on prior interrupt invocation and is about to
lelect 0:06d9443a018f 409 // latch now, so update the row address lines before we do that:
lelect 0:06d9443a018f 410 if(row & 0x1) *addraport |= addrapin;
lelect 0:06d9443a018f 411 else *addraport &= ~addrapin;
lelect 0:06d9443a018f 412 if(row & 0x2) *addrbport |= addrbpin;
lelect 0:06d9443a018f 413 else *addrbport &= ~addrbpin;
lelect 0:06d9443a018f 414 if(row & 0x4) *addrcport |= addrcpin;
lelect 0:06d9443a018f 415 else *addrcport &= ~addrcpin;
lelect 0:06d9443a018f 416 if(nRows > 8) {
lelect 0:06d9443a018f 417 if(row & 0x8) *addrdport |= addrdpin;
lelect 0:06d9443a018f 418 else *addrdport &= ~addrdpin;
lelect 0:06d9443a018f 419 }
lelect 3:aa3762e0dfee 420 */
lelect 0:06d9443a018f 421 }
lelect 3:aa3762e0dfee 422 _rowBus=row;
lelect 3:aa3762e0dfee 423 _oe=0;
lelect 3:aa3762e0dfee 424 _latch=0;
lelect 0:06d9443a018f 425 // buffptr, being 'volatile' type, doesn't take well to optimization.
lelect 0:06d9443a018f 426 // A local register copy can speed some things up:
lelect 3:aa3762e0dfee 427 uint8_t *ptr = (uint8_t *)buffptr;
lelect 3:aa3762e0dfee 428 /*
lelect 3:aa3762e0dfee 429 ICR1 = duration; // Set interval for next interrupt
lelect 3:aa3762e0dfee 430 TCNT1 = 0; // Restart interrupt timer
lelect 3:aa3762e0dfee 431 *oeport &= ~oepin; // Re-enable output
lelect 3:aa3762e0dfee 432 *latport &= ~latpin; // Latch down
lelect 0:06d9443a018f 433
lelect 3:aa3762e0dfee 434 // Record current state of SCLKPORT register, as well as a second
lelect 3:aa3762e0dfee 435 // copy with the clock bit set. This makes the innnermost data-
lelect 3:aa3762e0dfee 436 // pushing loops faster, as they can just set the PORT state and
lelect 3:aa3762e0dfee 437 // not have to load/modify/store bits every single time. It's a
lelect 3:aa3762e0dfee 438 // somewhat rude trick that ONLY works because the interrupt
lelect 3:aa3762e0dfee 439 // handler is set ISR_BLOCK, halting any other interrupts that
lelect 3:aa3762e0dfee 440 // might otherwise also be twiddling the port at the same time
lelect 3:aa3762e0dfee 441 // (else this would clobber them).
lelect 3:aa3762e0dfee 442 tock = SCLKPORT;
lelect 3:aa3762e0dfee 443 tick = tock | sclkpin;
lelect 3:aa3762e0dfee 444 */
lelect 3:aa3762e0dfee 445 if(plane > 0) { // 188 ticks from TCNT1=0 (above) to end of function
lelect 3:aa3762e0dfee 446 for(int i=0; i<32; i++) {
lelect 3:aa3762e0dfee 447 _dataBus=(ptr[i] << 6) | ((ptr[i+32] << 4)&0x30) | ((ptr[i+64] << 2)&0x0C)>>2;
lelect 3:aa3762e0dfee 448 _sclk=1;
lelect 3:aa3762e0dfee 449 _sclk=0;
lelect 3:aa3762e0dfee 450 }
lelect 0:06d9443a018f 451 buffptr += 32;
lelect 3:aa3762e0dfee 452 } else {
lelect 3:aa3762e0dfee 453 // 920 ticks from TCNT1=0 (above) to end of function
lelect 0:06d9443a018f 454 // Planes 1-3 (handled above) formatted their data "in place,"
lelect 0:06d9443a018f 455 // their layout matching that out the output PORT register (where
lelect 0:06d9443a018f 456 // 6 bits correspond to output data lines), maximizing throughput
lelect 0:06d9443a018f 457 // as no conversion or unpacking is needed. Plane 0 then takes up
lelect 0:06d9443a018f 458 // the slack, with all its data packed into the 2 least bits not
lelect 0:06d9443a018f 459 // used by the other planes. This works because the unpacking and
lelect 0:06d9443a018f 460 // output for plane 0 is handled while plane 3 is being displayed...
lelect 0:06d9443a018f 461 // because binary coded modulation is used (not PWM), that plane
lelect 0:06d9443a018f 462 // has the longest display interval, so the extra work fits.
lelect 3:aa3762e0dfee 463 for(int i=0; i<32; i++) {
lelect 3:aa3762e0dfee 464 _dataBus=(ptr[i] << 6) | ((ptr[i+32] << 4)&0x30) | ((ptr[i+64] << 2)&0x0C)>>2;
lelect 3:aa3762e0dfee 465 _sclk=1;
lelect 3:aa3762e0dfee 466 _sclk=0;
lelect 3:aa3762e0dfee 467 log_debug("\t\t %02x@(%d,%d)",_dataBus.read(),plane,row);
lelect 0:06d9443a018f 468 }
lelect 3:aa3762e0dfee 469 //buffptr += 32;
lelect 0:06d9443a018f 470 }
lelect 0:06d9443a018f 471 }