Adafruit-RGB_matrix_Panel(32*16)
Dependencies: Adafruit-GFX
Revision 4:0ff6053c4bb2, committed 2014-05-25
- Comitter:
- lelect
- Date:
- Sun May 25 09:32:41 2014 +0000
- Parent:
- 3:aa3762e0dfee
- Child:
- 5:1f8409ee8850
- Commit message:
- What's happen in updateDisplay.@The output(&read()) is different to the value written to BusOut instance.
Changed in this revision
RGBmatrixPanel.cpp | Show annotated file Show diff for this revision Revisions of this file |
RGBmatrixPanel.h | Show annotated file Show diff for this revision Revisions of this file |
--- a/RGBmatrixPanel.cpp Sat May 24 17:33:23 2014 +0000 +++ b/RGBmatrixPanel.cpp Sun May 25 09:32:41 2014 +0000 @@ -1,5 +1,5 @@ #define DEBUG -#undef DEBUG +//#undef DEBUG #include "RGBmatrixPanel.h" #include "gamma.h" @@ -13,7 +13,7 @@ // 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. -static RGBmatrixPanel *activePanel = NULL; +//static RGBmatrixPanel *activePanel = NULL; // Code common to both the 16x32 and 32x32 constructors: void RGBmatrixPanel::init(uint8_t rows, bool dbuf) @@ -64,24 +64,14 @@ backindex = 0; // Back buffer buffptr = matrixbuff[1 - backindex]; // -> front buffer - activePanel = this; // For interrupt hander - - // The high six bits of the data port are set as outputs; - // Might make this configurable in the future, but not yet. - /* - DATADIR = B11111100; - DATAPORT = 0; - */ + // activePanel = this; // For interrupt hander // Set up Timer for interrupt: - _refresh.attach(activePanel,(&RGBmatrixPanel::updateDisplay),0.001); //updateDisplay() called every 1ms - /* - 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 - */ +#ifndef DEBUG + _refresh.attach(this,(&RGBmatrixPanel::updateDisplay),0.0001); //updateDisplay() called every 1ms +#else + _refresh.attach(this,(&RGBmatrixPanel::updateDisplay),1.2); //updateDisplay() called every 2s +#endif } // Original RGBmatrixPanel library used 3/3/3 color. Later version used @@ -225,15 +215,15 @@ g = (c >> 7) & 0xF; // rrrrrGGGGggbbbbb b = (c >> 1) & 0xF; // rrrrrggggggBBBBb // Loop counter stuff + //log_debug("(%X, %X, %X)@(%d,%d)%s",r,g,b,x,y,"\t"); bit = 2; limit = 1 << nPlanes; if(y < nRows) { // Data for the upper half of the display is stored in the lower bits of each byte. ptr = &matrixbuff[backindex][y*_rawWidth*(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] &= ~(_BV(0)|_BV(1)); // Plane 0 R,G mask(0b11111100) out in one op + // Plane 0 is a tricky case -- its data is spread about, stored in least two bits not used by the other planes. + ptr[64] &= ~(_BV(1)|_BV(0)); // Plane 0 R,G mask(0b11111100) out in one op if(r & 1) ptr[64] |= _BV(0); // Plane 0 R: 64 bytes ahead, bit 0 if(g & 1) ptr[64] |= _BV(1); // Plane 0 G: 64 bytes ahead, bit 1 if(b & 1) ptr[32] |= _BV(0); // Plane 0 B: 32 bytes ahead, bit 0 @@ -242,22 +232,22 @@ // 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[0] &= ~(_BV(2)|_BV(3)|_BV(4)); // Mask(0b00011100) out R,G,B in one op - if(r & bit) *ptr |= _BV(2); // Plane N R: bit 2 - if(g & bit) *ptr |= _BV(3); // Plane N G: bit 3 - if(b & bit) *ptr |= _BV(4); // Plane N B: bit 4 - ptr += _rawWidth; // Advance to next bit plane + ptr[0] &= ~(_BV(4)|_BV(3)|_BV(2)); // Mask(0b11100011) out R,G,B in one op + if(r & bit) *ptr |= _BV(2); // Plane N R: bit 2 + if(g & bit) *ptr |= _BV(3); // Plane N G: bit 3 + if(b & bit) *ptr |= _BV(4); // Plane N B: bit 4 + ptr += _rawWidth; // 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)*_rawWidth*(nPlanes-1) + x]; - *ptr &= ~(_BV(0)|_BV(1)); // Plane 0 G,B mask out in one op + *ptr &= ~(_BV(1)|_BV(0)); // Plane 0 G,B mask out in one op if(r & 1) ptr[32] |= _BV(1); // Plane 0 R: 32 bytes ahead, bit 1 - else ptr[32] &= ~_BV(2); // Plane 0 R unset; mask out + else ptr[32] &= ~_BV(1); // Plane 0 R unset; mask out if(g & 1) *ptr |= _BV(0); // Plane 0 G: bit 0 if(b & 1) *ptr |= _BV(1); // Plane 0 B: bit 0 for(; bit < limit; bit <<= 1) { - *ptr &= ~(_BV(5)|_BV(6)|_BV(7)); // Mask out R,G,B in one op + *ptr &= ~(_BV(7)|_BV(6)|_BV(5)); // Mask out R,G,B in one op if(r & bit) *ptr |= _BV(5); // Plane N R: bit 5 if(g & bit) *ptr |= _BV(6); // Plane N G: bit 6 if(b & bit) *ptr |= _BV(7); // Plane N B: bit 7 @@ -293,7 +283,7 @@ // draw over every pixel. (No effect if double-buffering is not enabled.) void RGBmatrixPanel::swapBuffers(bool copy) { - log_debug("call swapBuffers %s","\r\n"); + log_debug("\r\ncall swapBuffers %s","\r\n"); 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. @@ -315,78 +305,22 @@ // back into the display using a pgm_read_byte() loop. void RGBmatrixPanel::dumpMatrix(void) { - log_debug("call dumpMatrix%s","\r\n"); +#ifdef DEBUG + log_debug("\r\ncall dumpMatrix%s","\r\n"); int buffsize=32*nRows*3; for(int item=0; item<buffsize; item++) { - if(item%(32*nRows)==0) { - for(int i=0; i<32*5; i++) { - log_debug("-%c",'\0'); - } - log_debug("-%s","\r\n"); - } log_debug("0x%02X",matrixbuff[backindex][item]); if((item%32)==31) log_debug(",\r\n"); else log_debug(","); } - log_debug("%s","\r\n"); -} + log_debug("%s","\r\n\r\n"); +#endif -// -------------------- Interrupt handler stuff -------------------- -/* -ISR(TIMER1_OVF_vect, ISR_BLOCK) // ISR_BLOCK important -- see notes later -{ - activePanel->updateDisplay(); // Call refresh func for active display - TIFR1 |= TOV1; // Clear Timer1 interrupt flag } -*/ -// Two constants are used in timing each successive BCM interval. -// These were found empirically, by checking the value of TCNT1 at -// 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% -// 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 -// 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 -// 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 -// 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. -// 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 -// the 2560 tick interval. -// 320 * 3 + 920 = 1880 ticks spent in interrupt code, per row. -// 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 -// 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 -// counter variables change between past/present/future tense in mid- -// function...hopefully tenses are sufficiently commented. void RGBmatrixPanel::updateDisplay(void) { - //log_debug("call updateDisplay\t(plane,row)=(%d,%d)\r\n",plane,row); + log_debug("\r\ncall updateDisplay\r\n"); _oe=1; _latch=1; if(++plane >= nPlanes) { // Advance plane counter. Maxed out? @@ -402,70 +336,45 @@ buffptr = matrixbuff[1-backindex]; // Reset into front buffer } } else if(plane == 1) { - log_debug("\r\n\tset row@(%d,%d)\r\n",plane,row); - - /* - // 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(nRows > 8) { - if(row & 0x8) *addrdport |= addrdpin; - else *addrdport &= ~addrdpin; - } - */ + log_debug("\tupdate row%s","\r\n"); + _rowBus=row; } - _rowBus=row; _oe=0; _latch=0; - // buffptr, being 'volatile' type, doesn't take well to optimization. - // A local register copy can speed some things up: - uint8_t *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 - - // Record current state of SCLKPORT register, as well as a second - // 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 - // 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 - // (else this would clobber them). - tock = SCLKPORT; - tick = tock | sclkpin; - */ - if(plane > 0) { // 188 ticks from TCNT1=0 (above) to end of function + log_debug("\t(row@plane)=(%d,%d)\r\n",row,plane); + int color; + if(plane > 0) { for(int i=0; i<32; i++) { - _dataBus=(ptr[i] << 6) | ((ptr[i+32] << 4)&0x30) | ((ptr[i+64] << 2)&0x0C)>>2; + color=0x3F&(buffptr[i]); + //buffptr[i]>>2 + _dataBus=color>>2; _sclk=1; _sclk=0; +#ifdef DEBUG + if(int(_dataBus)==color) { + log_debug(" %02x",int(_dataBus)); + } else { + _dataBus=color; + log_debug(" (%x->%x)%s",color,int(_dataBus),"\0"); + } +#endif } - buffptr += 32; + buffptr += _rawWidth; } else { - // 920 ticks from TCNT1=0 (above) to end of function - // 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 - // the slack, with all its data packed into the 2 least bits not - // 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(int i=0; i<32; i++) { - _dataBus=(ptr[i] << 6) | ((ptr[i+32] << 4)&0x30) | ((ptr[i+64] << 2)&0x0C)>>2; + color=0x3F&((buffptr[i]<<4)|((buffptr[i+32]<<2)&0x0C)|((buffptr[i+64])&0x03)); + _dataBus=color; _sclk=1; _sclk=0; - log_debug("\t\t %02x@(%d,%d)",_dataBus.read(),plane,row); +#ifdef DEBUG + if(int(_dataBus)==color) { + log_debug(" %02x",int(_dataBus)); + } else { + _dataBus=color; + log_debug(" (%x->%x)%s",color,int(_dataBus),"\0"); + } +#endif } - //buffptr += 32; } } +
--- a/RGBmatrixPanel.h Sat May 24 17:33:23 2014 +0000 +++ b/RGBmatrixPanel.h Sun May 25 09:32:41 2014 +0000 @@ -39,10 +39,10 @@ uint16_t ColorHSV(long hue, uint8_t sat, uint8_t val, bool gflag); private: - uint8_t *matrixbuff[2]; - uint8_t nRows; - volatile uint8_t backindex; - volatile bool swapflag; + uint8_t *matrixbuff[2]; + uint8_t nRows; + uint8_t backindex; + bool swapflag; // Init/alloc code common to both constructors: void init(uint8_t rows, bool dbuf); @@ -51,6 +51,6 @@ BusOut _dataBus,_rowBus; Ticker _refresh; // Counters/pointers for interrupt handler: - volatile uint8_t row, plane; - volatile uint8_t *buffptr; + uint8_t row, plane; + uint8_t *buffptr; }; \ No newline at end of file