1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
|
/* ----------------------------------------------------------------------
"Simple" Protomatter library example sketch (once you get past all
the various pin configurations at the top, and all the comments).
Shows basic use of Adafruit_Protomatter library with different devices.
This example is written for a 64x32 matrix but can be adapted to others.
Once the RGB matrix is initialized, most functions of the Adafruit_GFX
library are available for drawing -- code from other projects that use
LCDs or OLEDs can be easily adapted, or may be insightful for reference.
GFX library is documented here:
https://learn.adafruit.com/adafruit-gfx-graphics-library
------------------------------------------------------------------------- */
#include <Adafruit_Protomatter.h>
/* ----------------------------------------------------------------------
The RGB matrix must be wired to VERY SPECIFIC pins, different for each
microcontroller board. This first section sets that up for a number of
supported boards. Notes have been moved to the bottom of the code.
------------------------------------------------------------------------- */
#if defined(_VARIANT_MATRIXPORTAL_M4_) // MatrixPortal M4
uint8_t rgbPins[] = {7, 8, 9, 10, 11, 12};
uint8_t addrPins[] = {17, 18, 19, 20};
uint8_t clockPin = 14;
uint8_t latchPin = 15;
uint8_t oePin = 16;
#elif defined(_VARIANT_FEATHER_M4_) // Feather M4 + RGB Matrix FeatherWing
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;
#elif defined(__SAMD51__) // M4 Metro Variants (Express, AirLift)
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;
#elif defined(_SAMD21_) // Feather M0 variants
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;
#elif defined(NRF52_SERIES) // Special nRF52840 FeatherWing pinout
uint8_t rgbPins[] = {6, A5, A1, A0, A4, 11};
uint8_t addrPins[] = {10, 5, 13, 9};
uint8_t clockPin = 12;
uint8_t latchPin = PIN_SERIAL1_RX;
uint8_t oePin = PIN_SERIAL1_TX;
#elif defined(ESP32)
// 'Safe' pins, not overlapping any peripherals:
// GPIO.out: 4, 12, 13, 14, 15, 21, 27, GPIO.out1: 32, 33
// Peripheral-overlapping pins, sorted from 'most expendible':
// 16, 17 (RX, TX)
// 25, 26 (A0, A1)
// 18, 5, 9 (MOSI, SCK, MISO)
// 22, 23 (SCL, SDA)
uint8_t rgbPins[] = {4, 12, 13, 14, 15, 21};
uint8_t addrPins[] = {16, 17, 25, 26};
uint8_t clockPin = 27; // Must be on same port as rgbPins
uint8_t latchPin = 32;
uint8_t oePin = 33;
#elif defined(ARDUINO_TEENSY40)
uint8_t rgbPins[] = {15, 16, 17, 20, 21, 22}; // A1-A3, A6-A8, skip SDA,SCL
uint8_t addrPins[] = {2, 3, 4, 5};
uint8_t clockPin = 23; // A9
uint8_t latchPin = 6;
uint8_t oePin = 9;
#elif defined(ARDUINO_TEENSY41)
uint8_t rgbPins[] = {26, 27, 38, 20, 21, 22}; // A12-14, A6-A8
uint8_t addrPins[] = {2, 3, 4, 5};
uint8_t clockPin = 23; // A9
uint8_t latchPin = 6;
uint8_t oePin = 9;
#endif
/* ----------------------------------------------------------------------
Okay, here's where the RGB LED matrix is actually declared...
First argument is the matrix width, in pixels. Usually 32 or
64, but might go larger if you're chaining multiple matrices.
Second argument is the "bit depth," which determines color
fidelity, applied to red, green and blue (e.g. "4" here means
4 bits red, 4 green, 4 blue = 2^4 x 2^4 x 2^4 = 4096 colors).
There is a trade-off between bit depth and RAM usage. Most
programs will tend to use either 1 (R,G,B on/off, 8 colors,
best for text, LED sand, etc.) or the maximum of 6 (best for
shaded images...though, because the GFX library was designed
for LCDs, only 5 of those bits are available for red and blue.
Third argument is the number of concurrent (parallel) matrix
outputs. THIS SHOULD ALWAYS BE "1" FOR NOW. Fourth is a uint8_t
array listing six pins: red, green and blue data out for the
top half of the display, and same for bottom half. There are
hard constraints as to which pins can be used -- they must all
be on the same PORT register, ideally all within the same byte
of that PORT.
Fifth argument is the number of "address" (aka row select) pins,
from which the matrix height is inferred. "4" here means four
address lines, matrix height is then (2 x 2^4) = 32 pixels.
16-pixel-tall matrices will have 3 pins here, 32-pixel will have
4, 64-pixel will have 5. Sixth argument is a uint8_t array
listing those pin numbers. No PORT constraints here.
Next three arguments are pin numbers for other RGB matrix
control lines: clock, latch and output enable (active low).
Clock pin MUST be on the same PORT register as RGB data pins
(and ideally in same byte). Other pins have no special rules.
Last argument is a boolean (true/false) to enable double-
buffering for smooth animation (requires 2X the RAM). See the
"doublebuffer" example for a demonstration.
------------------------------------------------------------------------- */
Adafruit_Protomatter matrix(
64, // Width of matrix (or matrix chain) in pixels
4, // Bit depth, 1-6
1, rgbPins, // # of matrix chains, array of 6 RGB pins for each
4, addrPins, // # of address pins (height is inferred), array of pins
clockPin, latchPin, oePin, // Other matrix control pins
false); // No double-buffering here (see "doublebuffer" example)
// SETUP - RUNS ONCE AT PROGRAM START --------------------------------------
void setup(void) {
Serial.begin(9600);
// Initialize matrix...
ProtomatterStatus status = matrix.begin();
Serial.print("Protomatter begin() status: ");
Serial.println((int)status);
if(status != PROTOMATTER_OK) {
// DO NOT CONTINUE if matrix setup encountered an error.
for(;;);
}
// Since this is a simple program with no animation, all the
// drawing can be done here in setup() rather than loop():
// Make four color bars (red, green, blue, white) with brightness ramp:
for(int x=0; x<matrix.width(); x++) {
uint8_t level = x * 256 / matrix.width(); // 0-255 brightness
matrix.drawPixel(x, matrix.height() - 4, matrix.color565(level, 0, 0));
matrix.drawPixel(x, matrix.height() - 3, matrix.color565(0, level, 0));
matrix.drawPixel(x, matrix.height() - 2, matrix.color565(0, 0, level));
matrix.drawPixel(x, matrix.height() - 1,
matrix.color565(level, level, level));
}
// You'll notice the ramp looks smoother as bit depth increases
// (second argument to the matrix constructor call above setup()).
// Simple shapes and text, showing GFX library calls:
matrix.drawCircle(12, 10, 9, matrix.color565(255, 0, 0));
matrix.drawRect(14, 6, 17, 17, matrix.color565(0, 255, 0));
matrix.drawTriangle(32, 9, 41, 27, 23, 27, matrix.color565(0, 0, 255));
matrix.println("ADAFRUIT"); // Default text color is white
// AFTER DRAWING, A show() CALL IS REQUIRED TO UPDATE THE MATRIX!
matrix.show(); // Copy data to matrix buffers
}
// LOOP - RUNS REPEATEDLY AFTER SETUP --------------------------------------
void loop(void) {
// Since there's nothing more to be drawn, this loop() function just
// shows the approximate refresh rate of the matrix at current settings.
Serial.print("Refresh FPS = ~");
Serial.println(matrix.getFrameCount());
delay(1000);
}
// MORE NOTES --------------------------------------------------------------
/*
The "RGB and clock bits on same PORT register" constraint requires
considerable planning and knowledge of the underlying microcontroller
hardware. These are some earlier notes on various devices' PORT registers
and bits and their corresponding Arduino pin numbers. You probably won't
need this -- it's all codified in the #if defined() sections at the top
of this sketch now -- but keeping it around for reference if needed.
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
FEATHER nRF52840:
P0.00 P0.08 D12 P0.24 RXD P1.08 D5
P0.01 P0.09 P0.25 TXD P1.09 D13
P0.02 A4 P0.10 D2 (NFC) P0.26 D9 P1.10
P0.03 A5 P0.11 SCL P0.27 D10 P1.11
P0.04 A0 P0.12 SDA P0.28 A3 P1.12
P0.05 A1 P0.13 MOSI P0.29 P1.13
P0.06 D11 P0.14 SCK P0.30 A2 P1.14
P0.07 D6 P0.15 MISO P0.31 P1.15
FEATHER ESP32:
P0.00 P0.08 P0.16 16/RX P0.24 P1.00 32/A7
P0.01 P0.09 P0.17 17/TX P0.25 25/A1 P1.01 33/A9/SS
P0.02 P0.10 P0.18 18/MOSI P0.26 26/A0 P1.02 34/A2 (in)
P0.03 P0.11 P0.19 19/MISO P0.27 27/A10 P1.03
P0.04 4/A5 P0.12 12/A11 P0.20 P0.28 P1.04 36/A4 (in)
P0.05 5/SCK P0.13 13/A12 P0.21 21 P0.29 P1.05
P0.06 P0.14 14/A6 P0.22 22/SCL P0.30 P1.06
P0.07 P0.15 15/A8 P0.23 23/SDA P0.31 P1.07 39/A3 (in)
GRAND CENTRAL M4: (___ = byte boundaries)
PA00 PB00 D12 PC00 A3 PD00
PA01 PB01 D13 (LED) PC01 A4 PD01
PA02 A0 PB02 D9 PC02 A5 PD02
PA03 84 (AREF) PB03 A2 PC03 A6 PD03
PA04 A13 PB04 A7 PC04 D48 PD04
PA05 A1 PB05 A8 PC05 D49 PD05
PA06 A14 PB06 A9 PC06 D46 PD06
PA07 A15 ______ PB07 A10 ______ PC07 D47 _____ PD07 __________
PA08 PB08 A11 PC08 PD08 D51 (SCK)
PA09 PB09 A12 PC09 PD09 D52 (MOSI)
PA10 PB10 PC10 D45 PD10 D53
PA11 PB11 PC11 D44 PD11 D50 (MISO)
PA12 D26 PB12 D18 PC12 D41 PD12 D22
PA13 D27 PB13 D19 PC13 D40 PD13
PA14 D28 PB14 D39 PC14 D43 PD14
PA15 D23 ______ PB15 D38 ______ PC15 D42 _____ PD15 __________
PA16 D37 PB16 D14 PC16 D25 PD16
PA17 D36 PB17 D15 PC17 D24 PD17
PA18 D35 PB18 D8 PC18 D2 PD18
PA19 D34 PB19 D29 PC19 D3 PD19
PA20 D33 PB20 D20 (SDA) PC20 D4 PD20 D6
PA21 D32 PB21 D21 (SCL) PC21 D5 PD21 D7
PA22 D31 PB22 D10 PC22 D16 PD22
PA23 D30 ______ PB23 D11 ______ PC23 D17 _____ PD23 __________
PA24 PB24 D1
PA25 PB25 D0
PA26 PB26
PA27 PB27
PA28 PB28
PA29 PB29
PA30 PB30 96 (SWO)
PA31 __________ PB31 95 (SD CD) ______________________________
RGB MATRIX FEATHERWING NOTES:
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 fit 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, but would be super RAM-inefficient. Avoid.
Should be fine on other M0 devices like a Metro, if wiring manually
so one can pick a contiguous byte of PORT bits.
Original RGB Matrix FeatherWing will NOT work on Feather nRF52840
because RGB+clock are on different PORTs. This was resolved by making
a unique version of the FeatherWing that works with that board!
*/
|