Adafruit-RGB_matrix_Panel(32*16)

Dependencies:   Adafruit-GFX

Revision:
4:0ff6053c4bb2
Parent:
3:aa3762e0dfee
Child:
5:1f8409ee8850
--- 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;
     }
 }
+