Maker.io main logo

Adafruit Protomatter RGB Matrix Library

2024-10-16 | By Adafruit Industries

License: See Original Project LED Matrix Arduino

Courtesy of Adafruit

Guide by Phillip Burgess

Overview

 

Adafruit_Protomatter is a C library (with Arduino ‎and CircuitPython front-ends) for driving RGB LED matrices ‎‎(colloquially called “HUB75” matrices but that’s a vague term and ‎not entirely accurate). We already have one such library —‎‎ RGBmatrixPanel for Arduino — it’s older code that works fine for ‎AVR chips (and a couple others) but has some limitations.‎

This guide was mostly to assist with porting Adafruit_Protomatter to ‎new hardware but has since been expanded with installation and ‎basic use.‎

‎“Protomatter” was intended as a stand-in name until something ‎better was decided upon, but we’d already moved in by that point.‎

In CircuitPython, it’s only seen by the sensible name rgbmatrix. But if ‎working with the underlying C code or the Arduino library…sorry, ‎you’re stuck with the awkward-to-type Protomatter name.‎

It originates in a quote from the character David Marcus in Star Trek ‎III: “I used protomatter in the Genesis matrix.” Hopefully, the library ‎doesn’t develop a similar reputation as “unstable” and “dangerously ‎unpredictable.”‎

led_matrices_trek3_1

Protomatter was planned with newer hardware in mind and ‎will not be back-ported to AVR — simply use the prior ‎RGBmatrixPanel there if you need it. The new library takes a casual ‎approach to some things and isn’t particularly RAM-efficient, a ‎crucial concern on AVRs. What Protomatter does provide includes:‎

  • More flexibility in matrix chain width and height ‎‎(RGBmatrixPanel only supports a few sizes)‎

  • More flexibility in pin selection (old code was specifically ‎designed for an Arduino UNO shield)‎

  • Configurable bit depth, up to the maximum “565” color format ‎used by Adafruit_GFX

  • Mostly doesn’t rely on esoteric peripherals — sticks with basic ‎‎“PORT” GPIO and a timer interrupt. Some exceptions were ‎made for ESP32-S3 and -S3‎

  • Mostly avoids cycle-counting

A likely candidate device for porting will have:‎

  • A reasonably fast 32-bit RISC core or similar (e.g., ARM, ESP32). ‎Minimum we’ve used is a 48 MHz Cortex-M0+‎

  • One or more timer peripherals, 16-bit or better, with ‎configurable period and with interrupts

  • GPIO with atomic bit-set and bit-clear registers, typically 32 bits ‎wide…ideally tolerating writes to individual sub-bytes or words

  • RAM usage depends on matrix size, bit depth and GPIO pin ‎selection. Minimum device we’ve used has 32 KB RAM (total for ‎device, not all consumed by the library)‎

As currently written, Protomatter eschews the use of DMA or special ‎peripherals beyond what’s described above. Goal is simply to get this ‎working on a variety of devices with a minimum of fuss. We can ‎tweak and optimize later.‎

Things You’ll Need

  • A datasheet or reference manual for the device being ported to

  • Hardware to test on. Having both a logic analyzer and a known-‎compatible-with-existing-devices RGB matrix is really helpful ‎to verify that all signals are doing the right things at the right ‎times

Text editor powered by tinymce.‎

To work with the Protomatter library in the Arduino IDE, access the ‎Library Manager from the Sketch menu…‎

Sketch→Include Library→Manage Libraries…‎

sketch_2

Type “protomatter” in the search field at the top-right of the Library ‎Manager window, then click Install for the Adafruit ‎Protomatter library.‎

library_3

The very latest versions of the Arduino IDE automatically install other ‎dependent libraries. If you’re on a slightly earlier version, search for ‎‎“gfx” and install Adafruit GFX Library manually…‎

version_4

Adafruit_GFX is the same library that drives ‎many of our LCD and OLED displays…if ‎you’ve done other graphics projects, you ‎might already be familiar! And if not, we ‎have a separate guide explaining all of the ‎available drawing functions. Most folks can ‎get a quick start by looking at the ‎Protomatter library’s “simple” and ‎‎“doublebuffer_scrolltext” examples and ‎tweaking these for their needs.‎

Some of the Protomatter examples rely on ‎the Adafruit_PixelDust, Adafruit_LIS3DH and/or AnimatedGIF libraries…so again, search for and install those if not already handled ‎automatically by Arduino.‎

If you’re using an Adafruit MatrixPortal board specifically, the ‎MatrixPortal guide has a whole page for all of the needed libraries.‎

For Developers

If your plan is to adapt Protomatter to new devices, not just use it ‎with existing boards, you’ll want to remove any Arduino-installed ‎version of the library and download it from GitHub instead. This way ‎you can submit pull requests for inclusion in future releases of the ‎code.‎

You’ll find the Protomatter library ‎at https://github.com/adafruit/Adafruit_Protomatter

and the GFX library, if any changes are needed, is ‎at https://github.com/adafruit/Adafruit-GFX-Library

Text editor powered by tinymce.‎

Let’s look at a minimal Arduino example for the Adafruit_Protomatter ‎library to illustrate how this works (this is pared down from the ‎‎“simple” example sketch):‎

Copy Code
#include <Adafruit_Protomatter.h>

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;

Adafruit_Protomatter matrix(
  64, 4, 1, rgbPins, 4, addrPins, clockPin, latchPin, oePin, false);

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) {
    for(;;);
  }

  // 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));
  }

  // Simple shapes and text, showing GFX library calls:
  matrix.drawCircle(12, 10, 9, matrix.color565(255, 0, 0));               // Red
  matrix.drawRect(14, 6, 17, 17, matrix.color565(0, 255, 0));             // Green
  matrix.drawTriangle(32, 9, 41, 27, 23, 27, matrix.color565(0, 0, 255)); // Blue
  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
}

void loop(void) {
  Serial.print("Refresh FPS = ~");
  Serial.println(matrix.getFrameCount());
  delay(1000);
}

Breaking it down into steps…‎

Include Protomatter Library

First is to #include the library’s header file. This in ‎turn #includes Adafruit_GFX.h, so you don’t have to.‎

Download File

Copy Code
#include <Adafruit_Protomatter.h>

Setting Up Matrix Pin Usage

The next few lines spell out the pin numbers being used. Using ‎variables for this isn’t entirely necessary…one could just pass the ‎same numeric values directly to functions…but it makes the code a ‎little more self-documenting (and easier to adapt the same sketch ‎for multiple boards — the full example code has #ifdefs for each ‎board with different pin assignments). These also could ‎be #defines or const if one wants to be all Proper™ about it.‎

Technical stuff for developers, skip this if you just want to use the ‎library:‎

This is the one part of the Arduino code where some knowledge of the ‎underlying hardware is required. rgbPins[] and clockPin must all be on the ‎same GPIO PORT peripheral (e.g., all PORTA, all PORTB, etc.). The other ‎pins have no such restrictions. Additionally, if the PORT has an atomic ‎bit-toggle register, RAM requirements are minimized ‎if rgbPins[] and clockPin are all within the same byte of that PORT*. ‎They do not need to be contiguous nor in any particular ‎sequence within that byte. If not within the same byte, next most ‎efficient has them in the same upper or lower 16-bit word of the ‎PORT. Scattered around a full 32-bit PORT still works but is the least ‎RAM-efficient option.‎

‎* For devices lacking an atomic bit-toggle register…clockPin does ‎not need to be in the same byte, but still must be in the same PORT. ‎Should still aim for rgbPins[] in a single byte or word though!‎

With those constraints in mind, here’s what the code looks like for an ‎Adafruit MatrixPortal M4 with a 64x32 pixel matrix:‎

Download File

Copy Code
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;

The full “simple” example sketch has setups for a number of different ‎boards and adapters.‎

Create the Protomatter Object

Next, still in the global area above setup(), we call the constructor. The ‎Arduino library can only drive one matrix at a time (or one chain of ‎matrices, where “out” from one is linked to “in” of the next), so we ‎just have one instance of an Adafruit_Protomatter object here, which we’ll ‎call matrix:‎

Download File

Copy Code
Adafruit_Protomatter matrix(
  64, 4, 1, rgbPins, 4, addrPins, clockPin, latchPin, oePin, false);

The Adafruit_Protomatter constructor expects between 9 and 11 ‎arguments depending on the situation. The vital ones here, in order, ‎are:‎

  • 64 — the total matrix chain width, in pixels. This will usually ‎be 64 or 32, the width of most common RGB LED matrices…but, ‎if you have some other size or multiple matrices chained ‎together, add up the total width here. For example, three ‎chained 32-pixel-wide matrices would be 96.‎

  • 4 — the bit depth, in planes, from 1 to 6 (see below). More ‎bitplanes provides greater color fidelity at the expense of more ‎RAM. A value of 4 here (4 bits) provides 16 brightness levels ‎each for red, green, and blue — yielding 4,096 distinct colors ‎possible.‎

  • ‎1 — the number of matrix chains in parallel. This will almost ‎always be 1, but the library could conceivably support up to ‎‎5, if the hardware driving it is set up precisely just so.‎

  • rgbPins — a uint8_t array of pin numbers, which issue the red, ‎green and blue data for the upper and lower half of the matrix ‎‎(sometimes labeled R1, G1, B1, R2, G2, B2 on the matrix input). ‎The array should contain six times the prior argument…so, ‎usually, six. If driving two chains in parallel, then 12 pin numbers ‎and so forth. Obviously 12 pins won’t fit in a single PORT byte, ‎and you should aim for the upper or lower 16-bit word in that ‎case, for best RAM utilization. Three or more chains, doesn’t ‎matter, but the pins all do still need to be in the same PORT.‎

  • ‎4 — the number of row-select “address lines” used by the LED ‎matrix (sometimes labeled A, B, C, etc. on the matrix input). 16-‎pixel-tall matrices will be three row-select lines, 32-pixel will ‎have four, and 64-pixel will have five. Matrix height is ‎always inferred from this value, not passed explicitly like width.‎

  • addrPins — a uint8_t array of pin numbers, one for each row-‎select address line, starting from least-significant bit. These do ‎not need to be on the same PORT as rgbPins or each other…they ‎can be mixed about anywhere.‎

  • clockPin — pin number which drives the RGB clock (CLK on ‎matrix input). This must be on the same PORT register ‎as rgbPins, and in most cases should also try to be in the ‎same byte.‎

  • latchPin — pin number for “latch” signal (LAT on matrix input), ‎indicating end-of-data. Can be any output-capable pin, no ‎special constraints.‎

  • oePin — pin number for “!OE” signal (output-enable low, OE on ‎matrix input). Can be any output-capable pin, no special ‎constraints.‎

  • false — this flag indicates if the display should be double-‎buffered, better for animation at the expense of double the ‎RAM usage. Since the protomatter example isn’t using ‎animation, it passes false here…but if you look at ‎the doublebuffer_scrolltext example, it uses true. A double-‎buffered display only modifies the matrix between refreshes, ‎avoiding “tearing” artifacts. Optional. Default, if left unspecified, ‎is false.‎

  • Not used here, an optional 11th argument supports “tiling” of ‎matrices vertically. Horizontal tiling is already implicit in the ‎first argument — if you had two 64x32 matrices side-by-side, ‎you’d pass 128 there. But if you had four such matrices arranged ‎‎2x2, you’d still pass 128 for the first argument, but then add ‎either 2 here (if cabling is in a “progressive” order) or -2 (if a ‎‎“serpentine” order, where the second row of panels is rotated ‎‎180° relative to the first…the cabling is a little easier). The ‎‎“tiled.ino” example demonstrates this. The concept is explained ‎further in the CircuitPython LED Matrix guide…the same ‎principles apply to the Arduino library, the arguments are just a ‎little different here. Default if unspecified is 1 (no vertical tiling).‎

  • Also not used here, an optional 12th argument is a pointer to a ‎hardware-specific timer structure…this is super exceedinglyesoteric and not really used for now, but in principle would ‎allow the library to work with other timer peripherals than the ‎default.‎

Begin Protomatter Driver

Now, with the matrix object created, inside setup() we call ‎its begin() function. It’s pretty important to look at the value returned, ‎which is a ProtomatterStatus type:‎

Download File

Copy Code
ProtomatterStatus status = matrix.begin();

Possible return status values include:‎

  • PROTOMATTER_OK — everything is good, and the program can ‎proceed (otherwise it should stop…the example code is not a ‎good neighbor in this regard)‎

  • PROTOMATTER_ERR_PINS — the RGB data and clock pins are not all ‎on the same PORT. Can’t continue, the library requires these ‎pins in this layout

  • PROTOMATTER_ERR_MALLOC — couldn’t allocate enough memory for ‎display. Can’t continue. This is usually an error that happens in ‎the begin() function, but in extreme cases even the constructor ‎could hit an allocation problem, but you won’t get this response ‎until calling begin()‎

  • PROTOMATTER_ERR_ARG — some other bad input to function, distinct ‎from PROTOMATTER_ERR_PINS. Exceedingly rare, might only ‎happen if constructor failed

Draw Shapes & Text Using ‎Adafruit GFX

Then we draw some stuff on the display. Any graphics primitive ‎supported by the Adafruit_GFX library is available here.‎

Adafruit_GFX is the same library that drives ‎many of our LCD and OLED displays…if ‎you’ve done other graphics projects, you ‎might already be familiar! And if not, we ‎have a separate guide explaining all of the ‎available drawing functions. Most folks can ‎get a quick start by looking at the “simple” ‎and “doublebuffer_scrolltext” examples ‎and tweaking these for their needs.‎

Any color argument passed to a drawing function here is a 16-bit ‎value, with the highest 5 bits representing red brightness (0 to 31), ‎middle 6 bits for green (0 to 63), and least 5 bits for blue (0 to 31). It’s ‎just how Adafruit_GFX works and is a carryover from early PC ‎graphics and most small LCD/OLED displays.‎

The effect of bit depth on image quality. Color values are always ‎specified as full 16-bit “565” values but will quantize to coarser ‎representations at lower bit depths.‎

Sometimes you might want to avoid 6-bit depth even if RAM permits ‎it. Only green handles the full 6 bits, while red and blue are ‎quantized to 5 bits. This can result in some colors or gradients having ‎slight green or magenta tints to them. 5-bit depth is slightly blockier, ‎but colors are more predictable.‎

displays_5

Download File

Copy Code
matrix.drawCircle(12, 10, 9, matrix.color565(255, 0, 0));               // Red
matrix.drawRect(14, 6, 17, 17, matrix.color565(0, 255, 0));             // Green
...etc...
matrix.show(); // Copy data to matrix buffers

Notice though the call to matrix.show() at the end. Drawing operations ‎have no immediate effect on the LED matrix, and instead are ‎working on a buffer in RAM behind the scenes. ‎Calling show() is required — it “pushes” the display data from that ‎buffer to the matrix. You can call it after each drawing function, or ‎group up a bunch of drawing commands with a ‎single show() afterward to all appear at once. If you’ve worked with ‎NeoPixel programming, it’s a similar phenomenon.‎

Since this program isn’t animating anything, it’s finished at that ‎point and loop() could be empty.‎

Check Refresh Rate

For the sake of curious information though, the example shows the ‎matrix refresh rate using getFrameCount(). This returns the number of ‎frames since the last call to the same function, not the refresh ‎rate…but if spaced about one second apart (delay(1000)), you get a fair ‎approximation of refresh rate:‎

Download File

Copy Code
Serial.println(matrix.getFrameCount());
delay(1000);

The matrix refresh rate is influenced by so many factors…processor ‎speed, matrix chain length, bit depth…that it’s difficult to accurately ‎predict ahead of time, so this is a way to see what you get when ‎changing different values in the constructor.‎

This is a subjective thing, but in broad terms 200 Hz or better should ‎provide a solid image…any less and it starts to become flickery, so ‎you might want a lower bit depth in that case. Conversely, refreshing ‎too fast would waste CPU cycles that you probably want for other ‎tasks like animation. The library does its best to throttle back and not ‎refresh faster than practically needed.‎

Text editor powered by tinymce.‎

The Adafruit_Protomatter repository on Github contains all source ‎code for the project. Create a new branch, to facilitate merging pull ‎requests later.‎

Files there include:‎

table_1

Adafruit_GFX

The remaining files in the repository are for GitHub automation, the ‎Arduino Library manager, Arduino examples and so forth. Probably ‎won’t need editing, except for an occasional bump to the version ‎number in library.properties.‎

Expanding arch.h For a New Part

Inside arch.h, you’ll find whole sections of code conditionally ‎compiled in #if defined (and corresponding #endif) statements. You’ll ‎want to add a new conditional check for your device — neither too ‎specific (there’s usually some #define that broadly relates to a family ‎of related devices), nor too vague that it accidentally compiles on an ‎incompatible chip.‎

For example, anything in the Microchip SAMD51 family is covered by ‎this:‎

Download File

Copy Code
#if defined(__SAMD51__)
  ...a whole bunch of code...
#endif // end __SAMD51__

Within each device family section, it’s further divided by (usually) a ‎pair of #if defined checks, one to test if we’re compiling in the Arduino ‎environment, another if CircuitPython…perhaps others in the future:‎

Download File

Copy Code
#if defined(ARDUINO)
  ...a whole bunch of code...
#elif defined(CIRCUITPY)
  ...a whole bunch of code...
#endif

This is because each environment has different convenience ‎functions for certain operations. Arduino, for example, ‎provides digitalWrite() for GPIO output. It’s different in CircuitPython, ‎usually distinct to each architecture. The Protomatter code builds on ‎these common operations. Additionally, in the Arduino setting, the ‎library is usually attached to a specific timer/counter peripheral, set ‎at compile-time, whereas in CircuitPython timers are a dynamically ‎allocated resource…no telling what timer you’re using until run-time.‎

THEREFORE, each architecture and environment is expected to ‎establish a known and fixed set of macros or functions providing these ‎operations. core.c, which #includes arch.h, then goes about its ‎business using only those known function names, never having to ‎refer to device-specific hardware.‎

There are three groups of macros and functions: one related to GPIO, ‎one related to timers, and one miscellaneous category. With just a ‎few exceptions where noted, the following macros or functions ‎are required.‎

The macros/functions are all prefixed with ‎_PM_ (for Protomatter), ‎sort of a brute-force namespacing of things (to reduce likelihood of ‎collisions with user code) since we’re in simple C here.‎

GPIO-Related Macros/Functions

‎“Pin numbers,” as described here, refer to an environment’s ‎particular indexing of pins, which might not map directly to a ‎device’s PORTs and bits. Arduino digital and analog pins, for ‎example, might really be scattered all over the place, but are ‎exposed to the programmer as a tidy sequential list starting from ‎zero. Other environments may have their own numbering ‎system…but it’s always assumed there’s some numbering system. If ‎not, you’ll need to make one up.‎

table_2

Timer-Related Macros/Functions

The (void*) argument passed to these functions is some ‎implementation-specific representation of a timer peripheral. In ‎some cases (such as on SAMD microcontrollers) it’s simply a pointer ‎directly to a timer/counter peripheral’s register base address. If an ‎implementation requires more data associated alongside a ‎peripheral, this could instead be a pointer to a struct, or an integer ‎index.‎

table_3

A timer interrupt service routine is also required, syntax for which ‎varies between architectures.‎

Usually, the ISR needs to be related to a timer peripheral at compile-‎time, which is another reason why the Arduino implementation is ‎always tied to a specific timer…other libraries, and the Arduino core ‎itself, have their own ISRs for specific timers, we can’t take them all ‎and dole them out on request.‎

Miscellaneous Macros/Functions

table_4

If adapting to some environment that’s neither Arduino nor ‎CircuitPython: it’s easiest if the internal representation of an image in ‎RAM matches what Adafruit_GFX is using: one 16-bit unsigned word ‎per pixel, row major, no row padding. For example, a 64x32 pixel ‎matrix will use 64x32 uint16_ts, or 4 kilobytes. The first word ‎corresponds to pixel (0,0) at the top left.‎

Note that this is the drawing canvas into which points or lines or ‎other primitives are drawn, but it’s distinct from additional space ‎required by Protomatter, which must refresh the matrix plane-by-‎plane. Call the _PM_convert_565() function to process the simple canvas to ‎the shuffled matrix representation and update the display.‎

If a different canvas representation is used, you’ll have to provide ‎your own conversion function…_PM_convert_565() (and the functions it ‎calls in turn) might offer some insights there.‎

Arduino and CircuitPython implementations already handle this.‎

Insights and Surprises

Troubleshooting by just looking at an attached matrix probably ‎won’t yield much success. An oscilloscope or logic analyzer is really ‎helpful. Initially, take a good look at the clock signal…this is the ‎fastest signal the software has to generate (but mustn’t run too fast ‎for the matrix, hence _PM_clockHoldHigh and _PM_clockHoldLow, if ‎needed). Second, watch the !OE (output enable) signal…if the timer is ‎properly configured and working, you should see the time interval ‎between pulses double with each bitplane (e.g., N microseconds, N*2 ‎microseconds, N*4, etc.). You should also see an obvious sequential ‎bit-count among the row-select address lines.‎

A couple devices threw us for a loop…these problems were ‎surmountable, but worth specifically mentioning here as it may be ‎relevant to future porting efforts…‎

STM32

PORT bit-set and bit-clear registers do not correspond to 32-bit ports. ‎Instead, a single 32-bit register has 16-bit set and clear sections. At ‎least the bits are contiguous, and PMportSetRegister() ‎and PMportClearRegister() can just return pointers to the upper or ‎lower half of the register. Constrained to 16 bits, this does mean that ‎STM32 is limited to a maximum of two concurrent matrix chains.‎

ESP32

A little peculiar in that the bit-set and bit-clear registers aren’t ‎entirely atomic. If two set or clear operations occur in rapid sequence ‎‎(as happens in a couple places in the library), the second has no ‎effect. One solution would be adding NOP instructions, but this is ‎kludgey in that it doesn’t automatically handle faster CPU variants if ‎those come along in the future. Workaround was to always alternate ‎bit-set with bit-clear, using a bitmask of 0 for the second operation. ‎This waits for the first operation to “latch,” and the second has no ‎effect…we can follow up with another bit-set and it works reliably ‎now.‎

ESP32 needs the timer ISR function (and any sub-functions it calls) in ‎RAM rather than flash. This is done with an IRAM_ATTR attribute on a ‎function and broke our rule of “keep any device-specific code out ‎of core.c.” So…if any other devices also require in-RAM ISRs, and if ‎they use an attribute other than IRAM_ATTR…one should #define IRAM_ATTR to ‎whatever attribute is required there, so that section of the code will ‎handle either case.‎

ESP32-S2 and -S3

GPIO set/clear operations are somewhat slower than the original ‎ESP32, which would result in a flickery display, so these two make an ‎exception to the GPIO+timer rule and rely on chip-specific ‎peripherals. On the ESP32-S2, the Dedicated GPIO peripheral is used. ‎On ESP32-S3, the LCD controller peripheral. An interesting side effect ‎of this, because the ESP32 family has very flexible pin-MUXing ‎capabilities, is that any pins can be used to drive the matrix…there’s ‎no specific order or continuity required.‎

Text editor powered by tinymce.‎

制造商零件编号 4745
ADAFRUIT MATRIX PORTAL - CIRCUIT
Adafruit Industries LLC
¥203.09
Details
制造商零件编号 4702
ADAFRUIT RGB MATRIX FEATHERWING
Adafruit Industries LLC
¥61.05
Details
制造商零件编号 3036
RGB MATRIX FEATHERWING KIT M0 M4
Adafruit Industries LLC
¥63.19
Details
制造商零件编号 2601
RGB MATRIX SHIELD FOR ARDUINO
Adafruit Industries LLC
¥48.43
Details
制造商零件编号 3649
ADDRESS LED MATRIX SERIAL R/G/B
Adafruit Industries LLC
¥463.00
Details
制造商零件编号 4732
64X64 RGB LED MATRIX - 3MM PITCH
Adafruit Industries LLC
¥505.12
Details
制造商零件编号 2279
64X32 RGB LED MATRIX - 3MM PITCH
Adafruit Industries LLC
¥378.74
Details
制造商零件编号 2278
64X32 RGB LED MATRIX - 4MM PITCH
Adafruit Industries LLC
¥336.61
Details
Add all DigiKey Parts to Cart
TechForum

Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.

Visit TechForum