Speed up row addr selection, add doublebuffer example
This commit is contained in:
parent
dd414bbcc8
commit
7a2f65aaf6
4 changed files with 218 additions and 13 deletions
|
|
@ -18,6 +18,12 @@ static Adafruit_Protomatter *protoPtr = NULL;
|
|||
// for a single constant, thank you for coming to my TED talk!
|
||||
#define _PM_MAX_REFRESH_HZ 250
|
||||
|
||||
// Time (in milliseconds) to pause following any change in address lines
|
||||
// (individually or collectively). Some matrices respond slowly there...
|
||||
// must pause on change for matrix to catch up. Defined here (rather than
|
||||
// arch.h) because it's not architecture-specific.
|
||||
#define _PM_ROW_DELAY 8
|
||||
|
||||
Adafruit_Protomatter::Adafruit_Protomatter(
|
||||
uint16_t bitWidth, uint8_t bitDepth,
|
||||
uint8_t rgbCount, uint8_t *rgbList, uint8_t addrCount,
|
||||
|
|
@ -194,19 +200,38 @@ ProtomatterStatus Adafruit_Protomatter::begin(void) {
|
|||
uint32_t minPeriodPerFrame = _PM_timerFreq / _PM_MAX_REFRESH_HZ;
|
||||
uint32_t minPeriodPerLine = minPeriodPerFrame / rowPairs;
|
||||
minPeriod = minPeriodPerLine / ((1 << numPlanes) - 1);
|
||||
// Actual frame rate may be lower than this. That's OK, just
|
||||
// don't want to exceed this, as it'll eat all the CPU cycles.
|
||||
if(minPeriod < _PM_minMinPeriod) {
|
||||
minPeriod = _PM_minMinPeriod;
|
||||
}
|
||||
// Actual frame rate may be lower than this...it's only an estimate
|
||||
// and does not factor in things like address line selection delays
|
||||
// or interrupt overhead. That's OK, just don't want to exceed this
|
||||
// rate, as it'll eat all the CPU cycles.
|
||||
|
||||
// Once allocation is set up, configure all the
|
||||
// pins as outputs and initialize their states.
|
||||
plane = numPlanes - 1; // Initialize plane & row to their max values
|
||||
row = numRowPairs - 1; // so they roll over to start on 1st interrupt.
|
||||
prevRow = (numRowPairs > 1) ? (row - 1) : 1;
|
||||
|
||||
// Configure pins as outputs and initialize their states.
|
||||
pinMode(clockPin, OUTPUT); digitalWrite(clockPin, LOW);
|
||||
pinMode(latchPin, OUTPUT); digitalWrite(latchPin, LOW);
|
||||
pinMode(oePin , OUTPUT); digitalWrite(oePin , HIGH); // Disable output
|
||||
for(uint8_t i=0; i<parallel * 6; i++) {
|
||||
pinMode(rgbPins[i], OUTPUT); digitalWrite(rgbPins[i], LOW);
|
||||
}
|
||||
for(uint8_t i=0; i<numAddressLines; i++) {
|
||||
pinMode(addrPins[i], OUTPUT); digitalWrite(addrPins[i], LOW);
|
||||
#if defined(_PM_portToggleRegister)
|
||||
addrPortToggle = _PM_portToggleRegister(addrPins[0]);
|
||||
singleAddrPort = 1;
|
||||
#endif
|
||||
for(uint8_t line=0,bit=1; line<numAddressLines; line++, bit<<=1) {
|
||||
pinMode(addrPins[line], OUTPUT); digitalWrite(addrPins[line], LOW);
|
||||
digitalWrite(addrPins[line], prevRow & bit);
|
||||
#if defined(_PM_portToggleRegister)
|
||||
// If address pin on different port than addr 0, no singleAddrPort.
|
||||
if(_PM_portToggleRegister(addrPins[line]) != addrPortToggle) {
|
||||
singleAddrPort = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Get pointers to bit set and clear registers (and toggle, if present)
|
||||
|
|
@ -217,8 +242,6 @@ ProtomatterStatus Adafruit_Protomatter::begin(void) {
|
|||
#endif
|
||||
|
||||
protoPtr = this; // Only one active Adafruit_Protomatter object!
|
||||
plane = numPlanes - 1; // Initialize plane & row to their max values
|
||||
row = numRowPairs - 1; // so they roll over to start on 1st interrupt.
|
||||
_PM_timerInit(); // Configure timer
|
||||
_PM_timerStart(1000); // Start timer
|
||||
|
||||
|
|
@ -402,11 +425,35 @@ void Adafruit_Protomatter::row_handler(void) {
|
|||
}
|
||||
|
||||
if(prevPlane == 0) { // Plane 0 just finished loading
|
||||
// Configure row address lines:
|
||||
for(uint8_t line=0,bit=1; line<numAddressLines; line++, bit<<=1) {
|
||||
digitalWrite(addrPins[line], row & bit);
|
||||
delayMicroseconds(10);
|
||||
#if defined(_PM_portToggleRegister)
|
||||
// If all address lines are on a single PORT (and bit toggle is
|
||||
// available), do address line change all at once. Even doing all
|
||||
// this math takes MUCH less time than the delays required when
|
||||
// doing line-by-line changes.
|
||||
if(singleAddrPort) {
|
||||
// Make bitmasks of prior and new row bits
|
||||
uint32_t priorBits = 0, newBits = 0;
|
||||
for(uint8_t line=0,bit=1; line<numAddressLines; line++, bit<<=1) {
|
||||
if(row & bit) newBits |= _PM_portBitMask(addrPins[line]);
|
||||
if(prevRow & bit) priorBits |= _PM_portBitMask(addrPins[line]);
|
||||
}
|
||||
*addrPortToggle = newBits ^ priorBits;
|
||||
delayMicroseconds(_PM_ROW_DELAY);
|
||||
} else {
|
||||
#endif
|
||||
// Configure row address lines individually, making changes
|
||||
// (with delays) only where necessary.
|
||||
for(uint8_t line=0,bit=1; line<numAddressLines; line++, bit<<=1) {
|
||||
if((row & bit) != (prevRow & bit)) {
|
||||
digitalWrite(addrPins[line], row & bit);
|
||||
delayMicroseconds(_PM_ROW_DELAY);
|
||||
}
|
||||
}
|
||||
#if defined(_PM_portToggleRegister)
|
||||
}
|
||||
#endif
|
||||
prevRow = row;
|
||||
|
||||
// Optimization opportunity: if device has a toggle register, and if
|
||||
// all address lines are on same PORT, can do in a single operation
|
||||
// and not need delays for each address bit. Also consider even in
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ class Adafruit_Protomatter : public GFXcanvas16 {
|
|||
// This function needs to be declared public for protoPtr (in .cpp)
|
||||
// to access it, but should NOT be invoked by user code:
|
||||
void row_handler(void);
|
||||
// private:
|
||||
private:
|
||||
void blast_byte(uint8_t *data); // Data-writing functions
|
||||
void blast_word(uint16_t *data); // for 8/16/32 bit output
|
||||
void blast_long(uint32_t *data); // pin arrangements.
|
||||
|
|
@ -50,6 +50,7 @@ class Adafruit_Protomatter : public GFXcanvas16 {
|
|||
uint32_t bufferSize; // Bytes per matrix buffer (1 or 2)
|
||||
uint32_t bitZeroPeriod; // Bitplane 0 timer period
|
||||
uint32_t minPeriod; // Plane 0 timer period for ~400Hz
|
||||
volatile uint32_t *addrPortToggle; // See singleAddrPort below
|
||||
volatile uint32_t frameCount; // For estimating refresh rate
|
||||
uint8_t bytesPerElement; // Using 8, 16 or 32 bits of PORT?
|
||||
uint8_t clockPin; // Data clock pin (Arduino pin #)
|
||||
|
|
@ -61,9 +62,11 @@ class Adafruit_Protomatter : public GFXcanvas16 {
|
|||
uint8_t numPlanes; // Display bitplanes (1 to 6)
|
||||
uint8_t numRowPairs; // Addressable row pairs
|
||||
bool doubleBuffer; // 2X buffers for clean switchover
|
||||
bool singleAddrPort; // If 1, all addr lines on same PORT
|
||||
volatile uint8_t activeBuffer; // Index of currently-displayed buf
|
||||
volatile uint8_t plane; // Current bitplane (changes in ISR)
|
||||
volatile uint8_t row; // Current scanline (changes in ISR)
|
||||
volatile uint8_t prevRow; // Scanline from prior ISR
|
||||
volatile bool swapBuffers; // If 1, awaiting double-buf switch
|
||||
};
|
||||
|
||||
|
|
|
|||
16
arch.h
16
arch.h
|
|
@ -51,6 +51,16 @@ _PM_chunkSize: Matrix bitmap width (both in RAM and as issued
|
|||
certain chunkSizes are actually implemented,
|
||||
see .cpp code (avoiding GCC-specific tricks
|
||||
that would handle arbitrary chunk sizes).
|
||||
_PM_clockHoldHigh: Additional code (typically some number of NOPs)
|
||||
needed to delay the clock fall after RGB data is
|
||||
written to PORT. Only required on fast devices.
|
||||
If left undefined, no delay happens.
|
||||
_PM_clockHoldLow: Additional code (e.g. NOPs) needed to delay
|
||||
clock rise after writing RGB data to PORT.
|
||||
No delay if left undefined.
|
||||
_PM_minMinPeriod: Mininum value for the "minPeriod" class member,
|
||||
so bit-angle-modulation time always doubles with
|
||||
each bitplane (else lower bits may be the same).
|
||||
*/
|
||||
|
||||
|
||||
|
|
@ -180,6 +190,8 @@ _PM_chunkSize: Matrix bitmap width (both in RAM and as issued
|
|||
#define _PM_clockHoldHigh asm("nop; nop; nop");
|
||||
#endif
|
||||
|
||||
#define _PM_minMinPeriod 160
|
||||
|
||||
#else
|
||||
|
||||
// SAMD21 (presumably) -------------------------------------------------
|
||||
|
|
@ -301,4 +313,8 @@ _PM_chunkSize: Matrix bitmap width (both in RAM and as issued
|
|||
#define _PM_clockHoldLow
|
||||
#endif
|
||||
|
||||
#if !defined(_PM_minMinPeriod)
|
||||
#define _PM_minMinPeriod 100
|
||||
#endif
|
||||
|
||||
#endif // _PM_ARCH_H_
|
||||
|
|
|
|||
139
examples/doublebuffer/doublebuffer.ino
Normal file
139
examples/doublebuffer/doublebuffer.ino
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
#include "Adafruit_Protomatter.h"
|
||||
|
||||
/*
|
||||
METRO M0 PORT-TO-PIN ASSIGNMENTS BY BYTE:
|
||||
PA00 PA08 D4 PA16 D11 PB00 PB08 A1
|
||||
PA01 PA09 D3 PA17 D13 PB01 PB09 A2
|
||||
PA02 A0 PA10 D1 PA18 D10 PB02 A5 PB10 MOSI
|
||||
PA03 PA11 D0 PA19 D12 PB03 PB11 SCK
|
||||
PA04 A3 PA12 MISO PA20 D6 PB04 PB12
|
||||
PA05 A4 PA13 PA21 D7 PB05 PB13
|
||||
PA06 D8 PA14 D2 PA22 SDA PB06 PB14
|
||||
PA07 D9 PA15 D5 PA23 SCL PB07 PB15
|
||||
|
||||
SAME, METRO M4:
|
||||
PA00 PA08 PA16 D13 PB00 PB08 A4 PB16 D3
|
||||
PA01 PA09 PA17 D12 PB01 PB09 A5 PB17 D2
|
||||
PA02 A0 PA10 PA18 D10 PB02 SDA PB10 PB18
|
||||
PA03 PA11 PA19 D11 PB03 SCL PB11 PB19
|
||||
PA04 A3 PA12 MISO PA20 D9 PB04 PB12 D7 PB20
|
||||
PA05 A1 PA13 SCK PA21 D8 PB05 PB13 D4 PB21
|
||||
PA06 A2 PA14 MISO PA22 D1 PB06 PB14 D5 PB22
|
||||
PA07 PA15 PA23 D0 PB07 PB15 D6 PB23
|
||||
|
||||
FEATHER M4:
|
||||
PA00 PA08 PA16 D5 PB08 A2 PB16 D1/TX
|
||||
PA01 PA09 PA17 SCK PB09 A3 PB17 D0/RX
|
||||
PA02 A0 PA10 PA18 D6 PB10 PB18
|
||||
PA03 PA11 PA19 D9 PB11 PB19
|
||||
PA04 A4 PA12 SDA PA20 D10 PB12 PB20
|
||||
PA05 A1 PA13 SCL PA21 D11 PB13 PB21
|
||||
PA06 A5 PA14 D4 PA22 D12 PB14 PB22 MISO
|
||||
PA07 PA15 PA23 D13 PB15 PB23 MOSI
|
||||
|
||||
FEATHER M0:
|
||||
PA00 PA08 PA16 D11 PB00 PB08 A1
|
||||
PA01 PA09 PA17 D13 PB01 PB09 A2
|
||||
PA02 A0 PA10 TX/D1 PA18 D10 PB02 A5 PB10 MOSI
|
||||
PA03 PA11 RX/D0 PA19 D12 PB03 PB11 SCK
|
||||
PA04 A3 PA12 MISO PA20 D6 PB04 PB12
|
||||
PA05 A4 PA13 PA21 D7 PB05 PB13
|
||||
PA06 PA14 PA22 SDA PB06 PB14
|
||||
PA07 D9 PA15 D5 PA23 SCL PB07 PB15
|
||||
|
||||
RGB Matrix FeatherWing:
|
||||
R1 D6 A A5
|
||||
G1 D5 B A4
|
||||
B1 D9 C A3
|
||||
R2 D11 D A2
|
||||
G2 D10 LAT D0/RX
|
||||
B2 D12 OE D1/TX
|
||||
CLK D13
|
||||
RGB+clock in one PORT byte on Feather M4!
|
||||
RGB+clock are on same PORT but not within same byte on Feather M0 --
|
||||
the code could run there (with some work to be done in the convert_*
|
||||
functions), but would be super RAM-inefficient. Should be fine on other
|
||||
M0 devices like a Metro, if wiring manually so one can pick a contiguous
|
||||
byte of PORT bits.
|
||||
*/
|
||||
|
||||
#if defined(__SAMD51__)
|
||||
// Use FeatherWing pinout
|
||||
uint8_t rgbPins[] = {6, 5, 9, 11, 10, 12};
|
||||
uint8_t addrPins[] = {A5, A4, A3, A2};
|
||||
uint8_t clockPin = 13;
|
||||
uint8_t latchPin = 0;
|
||||
uint8_t oePin = 1;
|
||||
#else // SAMD21
|
||||
uint8_t rgbPins[] = {6, 7, 10, 11, 12, 13};
|
||||
uint8_t addrPins[] = {0, 1, 2, 3};
|
||||
uint8_t clockPin = SDA;
|
||||
uint8_t latchPin = 4;
|
||||
uint8_t oePin = 5;
|
||||
#endif
|
||||
|
||||
// Last arg here enables double-buffering
|
||||
Adafruit_Protomatter matrix(
|
||||
64, 6, 1, rgbPins, 4, addrPins, clockPin, latchPin, oePin, true);
|
||||
|
||||
const char str[] = "Adafruit 16x32 RGB LED Matrix";
|
||||
int16_t textX = matrix.width(),
|
||||
textMin = sizeof(str) * -12,
|
||||
hue = 0;
|
||||
int8_t ball[3][4] = {
|
||||
{ 3, 0, 1, 1 }, // Initial X,Y pos & velocity for 3 bouncy balls
|
||||
{ 17, 15, 1, -1 },
|
||||
{ 27, 4, -1, 1 }
|
||||
};
|
||||
|
||||
const uint16_t ballcolor[3] = {
|
||||
0b0000000001000000, // Dark green
|
||||
0b0000000000000001, // Dark blue
|
||||
0b0000100000000000 // Dark red
|
||||
};
|
||||
|
||||
void setup(void) {
|
||||
Serial.begin(9600);
|
||||
|
||||
ProtomatterStatus status = matrix.begin();
|
||||
Serial.print("Protomatter begin() status: ");
|
||||
Serial.println((int)status);
|
||||
|
||||
matrix.setTextWrap(false);
|
||||
matrix.setTextSize(2);
|
||||
matrix.setTextColor(0xFFFF); // White
|
||||
}
|
||||
|
||||
void loop(void) {
|
||||
byte i;
|
||||
|
||||
// Clear background
|
||||
matrix.fillScreen(0);
|
||||
|
||||
// Bounce three balls around
|
||||
for(i=0; i<3; i++) {
|
||||
// Draw 'ball'
|
||||
matrix.fillCircle(ball[i][0], ball[i][1], 5, ballcolor[i]);
|
||||
// Update X, Y position
|
||||
ball[i][0] += ball[i][2];
|
||||
ball[i][1] += ball[i][3];
|
||||
// Bounce off edges
|
||||
if((ball[i][0] == 0) || (ball[i][0] == (matrix.width() - 1)))
|
||||
ball[i][2] *= -1;
|
||||
if((ball[i][1] == 0) || (ball[i][1] == (matrix.height() - 1)))
|
||||
ball[i][3] *= -1;
|
||||
}
|
||||
|
||||
// Draw big scrolly text on top
|
||||
matrix.setCursor(textX, 1);
|
||||
matrix.print(str);
|
||||
|
||||
// Move text left (w/wrap), increase hue
|
||||
if((--textX) < textMin) textX = matrix.width();
|
||||
hue += 7;
|
||||
if(hue >= 1536) hue -= 1536;
|
||||
|
||||
matrix.show();
|
||||
|
||||
delay(20);
|
||||
}
|
||||
Loading…
Reference in a new issue