/ Adafruit_Protomatter.cpp
Adafruit_Protomatter.cpp
 1  // Arduino-specific wrapper for the Protomatter C library (provides
 2  // constructor and so forth, builds on Adafruit_GFX). There should
 3  // not be any device-specific #ifdefs here. See notes in core.c and
 4  // arch.h regarding portability.
 5  
 6  #include "Adafruit_Protomatter.h" // Also includes core.h & Adafruit_GFX.h
 7  
 8  extern Protomatter_core *_PM_protoPtr; // In core.c (via arch.h)
 9  
10  // Overall matrix refresh rate (frames/second) is a function of matrix width
11  // and chain length, number of address lines, number of bit planes, CPU speed
12  // and whether or not a GPIO toggle register is available. There is no "this
13  // will run at X-frames-per-second" constant figure. You typically just have
14  // to try it out and perhaps trade off some bit planes for refresh rate until
15  // the image looks good and stable. Anything over 100 Hz is usually passable,
16  // around 250 Hz is where things firm up. And while this could proceed higher
17  // in some situations, the tradeoff is that faster rates use progressively
18  // more CPU time (because it's timer interrupt based and not using DMA or
19  // special peripherals). So a throttle is set here, an approximate maximum
20  // frame rate which the software will attempt to avoid exceeding (but may
21  // refresh slower than this, and in many cases will...just need to set an
22  // upper limit to avoid excessive CPU load). An incredibly long comment block
23  // for a single constant, thank you for coming to my TED talk!
24  #define _PM_MAX_REFRESH_HZ 250
25  
26  // Time (in milliseconds) to pause following any change in address lines
27  // (individually or collectively). Some matrices respond slowly there...
28  // must pause on change for matrix to catch up. Defined here (rather than
29  // arch.h) because it's not architecture-specific.
30  #define _PM_ROW_DELAY 8
31  
32  
33  Adafruit_Protomatter::Adafruit_Protomatter(
34    uint16_t bitWidth, uint8_t bitDepth,
35    uint8_t rgbCount, uint8_t *rgbList,
36    uint8_t addrCount, uint8_t *addrList,
37    uint8_t clockPin, uint8_t latchPin, uint8_t oePin,
38    bool doubleBuffer, void *timer) :
39    GFXcanvas16(bitWidth, (2 << min(addrCount, 5)) * min(rgbCount, 5)) {
40      if(bitDepth > 6)  bitDepth = 6;   // GFXcanvas16 color limit (565)
41  
42      // Arguments are passed through to the C _PM_init() function which does
43      // some input validation and minor allocation. Return value is ignored
44      // because we can't really do anything about it in a C++ constructor.
45      // The class begin() function checks rgbPins for NULL to determine
46      // whether to proceed or indicate an error.
47      (void)_PM_init(&core, bitWidth, bitDepth, rgbCount, rgbList,
48        addrCount, addrList, clockPin, latchPin, oePin, doubleBuffer, timer);
49  }
50  
51  Adafruit_Protomatter::~Adafruit_Protomatter(void) {
52      _PM_free(&core);
53      _PM_protoPtr = NULL;
54  }
55  
56  ProtomatterStatus Adafruit_Protomatter::begin(void) {
57      _PM_protoPtr = &core;
58      _PM_begin(&core);
59      return PROTOMATTER_OK;
60  }
61  
62  // Transfer data from GFXcanvas16 to the matrix framebuffer's weird
63  // internal format. The actual conversion functions referenced below
64  // are in core.c, reasoning is explained there.
65  void Adafruit_Protomatter::show(void) {
66  
67      // Destination address is computed in convert function
68      // (based on active buffer value, if double-buffering),
69      // just need to pass in the canvas buffer address and
70      // width in pixels.
71      if(core.bytesPerElement == 1) {
72          _PM_convert_565_byte(&core, getBuffer(), WIDTH);
73      } else if(core.bytesPerElement == 2) {
74          _PM_convert_565_word(&core, getBuffer(), WIDTH);
75      } else {
76          _PM_convert_565_long(&core, getBuffer(), WIDTH);
77      }
78  
79      if(core.doubleBuffer) {
80          core.swapBuffers = 1;
81          // To avoid overwriting data on the matrix, don't return
82          // until the timer ISR has performed the swap at the right time.
83          while(core.swapBuffers);
84      }
85  }
86  
87  // Returns current value of frame counter and resets its value to zero.
88  // Two calls to this, timed one second apart (or use math with other
89  // intervals), can be used to get a rough frames-per-second value for
90  // the matrix (since this is difficult to estimate beforehand).
91  uint32_t Adafruit_Protomatter::getFrameCount(void) {
92      return _PM_getFrameCount(_PM_protoPtr);
93  }