diff options
author | Raghuram Subramani <raghus2247@gmail.com> | 2022-06-19 19:47:51 +0530 |
---|---|---|
committer | Raghuram Subramani <raghus2247@gmail.com> | 2022-06-19 19:47:51 +0530 |
commit | 4fd287655a72b9aea14cdac715ad5b90ed082ed2 (patch) | |
tree | 65d393bc0e699dd12d05b29ba568e04cea666207 /circuitpython/lib/protomatter/examples | |
parent | 0150f70ce9c39e9e6dd878766c0620c85e47bed0 (diff) |
add circuitpython code
Diffstat (limited to 'circuitpython/lib/protomatter/examples')
6 files changed, 1130 insertions, 0 deletions
diff --git a/circuitpython/lib/protomatter/examples/animated_gif/.matrixportal.test.only b/circuitpython/lib/protomatter/examples/animated_gif/.matrixportal.test.only new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/circuitpython/lib/protomatter/examples/animated_gif/.matrixportal.test.only diff --git a/circuitpython/lib/protomatter/examples/animated_gif/animated_gif.ino b/circuitpython/lib/protomatter/examples/animated_gif/animated_gif.ino new file mode 100644 index 0000000..97446cc --- /dev/null +++ b/circuitpython/lib/protomatter/examples/animated_gif/animated_gif.ino @@ -0,0 +1,376 @@ +// Play GIFs from CIRCUITPY drive (USB-accessible filesystem) to LED matrix. +// ***DESIGNED FOR ADAFRUIT MATRIXPORTAL M4***, but may run on some other +// M4 & M0 and nRF52 boards (relies on TinyUSB stack). As written, runs on +// 64x32 pixel matrix, this can be changed by editing the WIDTH and HEIGHT +// definitions. See the "simple" example for a run-down on matrix config. +// Adapted from examples from Larry Bank's AnimatedGIF library and +// msc_external_flash example in Adafruit_TinyUSB_Arduino. +// Prerequisite libraries: +// - Adafruit_Protomatter +// - Adafruit_SPIFlash +// - Adafruit_TinyUSB +// - SdFat (Adafruit fork) +// - AnimatedGIF +// Set ENABLE_EXTENDED_TRANSFER_CLASS and FAT12_SUPPORT in SdFatConfig.h. +// Select Tools->USB Stack->TinyUSB before compiling. + +#include <Adafruit_Protomatter.h> +#include <Adafruit_SPIFlash.h> +#include <Adafruit_TinyUSB.h> +#include <AnimatedGIF.h> +#include <SPI.h> +#include <SdFat.h> + +// CONFIGURABLE SETTINGS --------------------------------------------------- + +char GIFpath[] = "/gifs"; // Absolute path to GIFs on CIRCUITPY drive +uint16_t GIFminimumTime = 10; // Min. repeat time (seconds) until next GIF +#define WIDTH 64 // Matrix width in pixels +#define HEIGHT 32 // Matrix height in pixels +// Maximim matrix height is 32px on most boards, 64 on MatrixPortal if the +// 'E' jumper is set. + +// FLASH FILESYSTEM STUFF -------------------------------------------------- + +// External flash macros for QSPI or SPI are defined in board variant file. +#if defined(EXTERNAL_FLASH_USE_QSPI) +Adafruit_FlashTransport_QSPI flashTransport; +#elif defined(EXTERNAL_FLASH_USE_SPI) +Adafruit_FlashTransport_SPI flashTransport(EXTERNAL_FLASH_USE_CS, + EXTERNAL_FLASH_USE_SPI); +#else +#error No QSPI/SPI flash are defined in your board variant.h! +#endif + +Adafruit_SPIFlash flash(&flashTransport); +FatFileSystem filesys; // Filesystem object from SdFat +Adafruit_USBD_MSC usb_msc; // USB mass storage object + +// RGB MATRIX (PROTOMATTER) LIBRARY STUFF ---------------------------------- + +#if defined(_VARIANT_MATRIXPORTAL_M4_) +uint8_t rgbPins[] = {7, 8, 9, 10, 11, 12}; +uint8_t addrPins[] = {17, 18, 19, 20, 21}; // 16/32/64 pixels tall +uint8_t clockPin = 14; +uint8_t latchPin = 15; +uint8_t oePin = 16; +#define BACK_BUTTON 2 +#define NEXT_BUTTON 3 +#elif defined(_VARIANT_METRO_M4_) +uint8_t rgbPins[] = {2, 3, 4, 5, 6, 7}; +uint8_t addrPins[] = {A0, A1, A2, A3}; // 16 or 32 pixels tall +uint8_t clockPin = A4; +uint8_t latchPin = 10; +uint8_t oePin = 9; +#elif defined(_VARIANT_FEATHER_M4_) +uint8_t rgbPins[] = {6, 5, 9, 11, 10, 12}; +uint8_t addrPins[] = {A5, A4, A3, A2}; // 16 or 32 pixels tall +uint8_t clockPin = 13; +uint8_t latchPin = 0; +uint8_t oePin = 1; +#endif +#if HEIGHT == 16 +#define NUM_ADDR_PINS 3 +#elif HEIGHT == 32 +#define NUM_ADDR_PINS 4 +#elif HEIGHT == 64 +#define NUM_ADDR_PINS 5 +#endif + +Adafruit_Protomatter matrix(WIDTH, 6, 1, rgbPins, NUM_ADDR_PINS, addrPins, + clockPin, latchPin, oePin, true); + +// ANIMATEDGIF LIBRARY STUFF ----------------------------------------------- + +AnimatedGIF GIF; +File GIFfile; +int16_t xPos = 0, yPos = 0; // Top-left pixel coord of GIF in matrix space + +// FILE ACCESS FUNCTIONS REQUIRED BY ANIMATED GIF LIB ---------------------- + +// Pass in ABSOLUTE PATH of GIF file to open +void *GIFOpenFile(char *filename, int32_t *pSize) { + GIFfile = filesys.open(filename); + if (GIFfile) { + *pSize = GIFfile.size(); + return (void *)&GIFfile; + } + return NULL; +} + +void GIFCloseFile(void *pHandle) { + File *f = static_cast<File *>(pHandle); + if (f) f->close(); +} + +int32_t GIFReadFile(GIFFILE *pFile, uint8_t *pBuf, int32_t iLen) { + int32_t iBytesRead = iLen; + File *f = static_cast<File *>(pFile->fHandle); + // If a file is read all the way to last byte, seek() stops working + if ((pFile->iSize - pFile->iPos) < iLen) + iBytesRead = pFile->iSize - pFile->iPos - 1; // ugly work-around + if (iBytesRead <= 0) return 0; + iBytesRead = (int32_t)f->read(pBuf, iBytesRead); + pFile->iPos = f->position(); + return iBytesRead; +} + +int32_t GIFSeekFile(GIFFILE *pFile, int32_t iPosition) { + File *f = static_cast<File *>(pFile->fHandle); + f->seek(iPosition); + pFile->iPos = (int32_t)f->position(); + return pFile->iPos; +} + +// Draw one line of image to matrix back buffer +void GIFDraw(GIFDRAW *pDraw) { + uint8_t *s; + uint16_t *d, *usPalette, usTemp[320]; + int x, y; + + y = pDraw->iY + pDraw->y; // current line in image + + // Vertical clip + int16_t screenY = yPos + y; // current row on matrix + if ((screenY < 0) || (screenY >= matrix.height())) return; + + usPalette = pDraw->pPalette; + + s = pDraw->pPixels; + // Apply the new pixels to the main image + if (pDraw->ucHasTransparency) { // if transparency used + uint8_t *pEnd, c, ucTransparent = pDraw->ucTransparent; + int x, iCount; + pEnd = s + pDraw->iWidth; + x = 0; + iCount = 0; // count non-transparent pixels + while (x < pDraw->iWidth) { + c = ucTransparent - 1; + d = usTemp; + while (c != ucTransparent && s < pEnd) { + c = *s++; + if (c == ucTransparent) { // done, stop + s--; // back up to treat it like transparent + } else { // opaque + *d++ = usPalette[c]; + iCount++; + } + } // while looking for opaque pixels + if (iCount) { // any opaque pixels? + span(usTemp, xPos + pDraw->iX + x, screenY, iCount); + x += iCount; + iCount = 0; + } + // no, look for a run of transparent pixels + c = ucTransparent; + while (c == ucTransparent && s < pEnd) { + c = *s++; + if (c == ucTransparent) + iCount++; + else + s--; + } + if (iCount) { + x += iCount; // skip these + iCount = 0; + } + } + } else { + s = pDraw->pPixels; + // Translate 8-bit pixels through RGB565 palette (already byte reversed) + for (x = 0; x < pDraw->iWidth; x++) + usTemp[x] = usPalette[*s++]; + span(usTemp, xPos + pDraw->iX, screenY, pDraw->iWidth); + } +} + +// Copy a horizontal span of pixels from a source buffer to an X,Y position +// in matrix back buffer, applying horizontal clipping. Vertical clipping is +// handled in GIFDraw() above -- y can safely be assumed valid here. +void span(uint16_t *src, int16_t x, int16_t y, int16_t width) { + if (x >= matrix.width()) return; // Span entirely off right of matrix + int16_t x2 = x + width - 1; // Rightmost pixel + if (x2 < 0) return; // Span entirely off left of matrix + if (x < 0) { // Span partially off left of matrix + width += x; // Decrease span width + src -= x; // Increment source pointer to new start + x = 0; // Leftmost pixel is first column + } + if (x2 >= matrix.width()) { // Span partially off right of matrix + width -= (x2 - matrix.width() + 1); + } + if(matrix.getRotation() == 0) { + memcpy(matrix.getBuffer() + y * matrix.width() + x, src, width * 2); + } else { + while(x <= x2) { + matrix.drawPixel(x++, y, *src++); + } + } +} + +// FUNCTIONS REQUIRED FOR USB MASS STORAGE --------------------------------- + +static bool msc_changed = true; // Is set true on filesystem changes + +// Callback on READ10 command. +int32_t msc_read_cb(uint32_t lba, void *buffer, uint32_t bufsize) { + return flash.readBlocks(lba, (uint8_t *)buffer, bufsize / 512) ? bufsize : -1; +} + +// Callback on WRITE10 command. +int32_t msc_write_cb(uint32_t lba, uint8_t *buffer, uint32_t bufsize) { + digitalWrite(LED_BUILTIN, HIGH); + return flash.writeBlocks(lba, buffer, bufsize / 512) ? bufsize : -1; +} + +// Callback on WRITE10 completion. +void msc_flush_cb(void) { + flash.syncBlocks(); // Sync with flash + filesys.cacheClear(); // Clear filesystem cache to force refresh + digitalWrite(LED_BUILTIN, LOW); + msc_changed = true; +} + +// Get number of files in a specified path that match extension ('filter'). +// Pass in absolute path (e.g. "/" or "/gifs") and extension WITHOUT period +// (e.g. "gif", NOT ".gif"). +int16_t numFiles(const char *path, const char *filter) { + File dir = filesys.open(path); + if (!dir) return -1; + char filename[256]; + for(int16_t num_files = 0;;) { + File entry = dir.openNextFile(); + if (!entry) return num_files; // No more files + entry.getName(filename, sizeof(filename) - 1); + entry.close(); + if (!entry.isDirectory() && // Skip directories + strncmp(filename, "._", 2)) { // and Mac junk files + char *extension = strrchr(filename, '.'); + if (extension && !strcasecmp(&extension[1], filter)) num_files++; + } + } + return -1; +} + +// Return name of file (matching extension) by index (0 to numFiles()-1) +char *filenameByIndex(const char *path, const char *filter, int16_t index) { + static char filename[256]; // Must be static, we return a pointer to this! + File entry, dir = filesys.open(path); + if (!dir) return NULL; + while(entry = dir.openNextFile()) { + entry.getName(filename, sizeof(filename) - 1); + entry.close(); + if(!entry.isDirectory() && // Skip directories + strncmp(filename, "._", 2)) { // and Mac junk files + char *extension = strrchr(filename, '.'); + if (extension && !strcasecmp(&extension[1], filter)) { + if(!index--) { + return filename; + } + } + } + } + return NULL; +} + +// SETUP FUNCTION - RUNS ONCE AT STARTUP ----------------------------------- + +void setup() { + pinMode(LED_BUILTIN, OUTPUT); +#if defined(BACK_BUTTON) + pinMode(BACK_BUTTON, INPUT_PULLUP); +#endif +#if defined(NEXT_BUTTON) + pinMode(NEXT_BUTTON, INPUT_PULLUP); +#endif + + // USB mass storage / filesystem setup (do BEFORE Serial init) + flash.begin(); + // Set disk vendor id, product id and revision + usb_msc.setID("Adafruit", "External Flash", "1.0"); + // Set disk size, block size is 512 regardless of spi flash page size + usb_msc.setCapacity(flash.pageSize() * flash.numPages() / 512, 512); + usb_msc.setReadWriteCallback(msc_read_cb, msc_write_cb, msc_flush_cb); + usb_msc.setUnitReady(true); // MSC is ready for read/write + usb_msc.begin(); + filesys.begin(&flash); // Start filesystem on the flash + + Serial.begin(115200); + //while (!Serial); + + // Protomatter (RGB matrix) setup + ProtomatterStatus status = matrix.begin(); + Serial.print("Protomatter begin() status: "); + Serial.println((int)status); + matrix.fillScreen(0); + matrix.show(); + + // GIF setup + GIF.begin(LITTLE_ENDIAN_PIXELS); +} + +// LOOP FUNCTION - RUNS REPEATEDLY UNTIL RESET / POWER OFF ----------------- + +int16_t GIFindex = -1; // Current file index in GIFpath +int8_t GIFincrement = 1; // +1 = next GIF, -1 = prev, 0 = same +uint32_t GIFstartTime = 0; // When current GIF started playing +bool GIFisOpen = false; // True if GIF is currently open + +void loop() { + if (msc_changed) { // If filesystem has changed... + msc_changed = false; // Clear flag + GIFincrement = 1; // Set index to next file when we resume here + return; // Prioritize USB, handled in calling func + } + +#if defined(BACK_BUTTON) + if(!digitalRead(BACK_BUTTON)) { + GIFincrement = -1; // Back + while(!digitalRead(BACK_BUTTON)); // Wait for release + } +#endif +#if defined(NEXT_BUTTON) + if(!digitalRead(NEXT_BUTTON)) { + GIFincrement = 1; // Forward + while(!digitalRead(NEXT_BUTTON)); // Wait for release + } +#endif + + if (GIFincrement) { // Change file? + if (GIFisOpen) { // If currently playing, + GIF.close(); // stop it + GIFisOpen = false; + } + GIFindex += GIFincrement; // Fwd or back 1 file + int num_files = numFiles(GIFpath, "GIF"); + if(GIFindex >= num_files) GIFindex = 0; // 'Wrap around' file index + else if(GIFindex < 0) GIFindex = num_files - 1; // both directions + + char *filename = filenameByIndex(GIFpath, "GIF", GIFindex); + if (filename) { + char fullname[sizeof GIFpath + 256]; + sprintf(fullname, "%s/%s", GIFpath, filename); // Absolute path to GIF + Serial.printf("Opening file '%s'\n", fullname); + if (GIF.open(fullname, GIFOpenFile, GIFCloseFile, + GIFReadFile, GIFSeekFile, GIFDraw)) { + matrix.fillScreen(0); + Serial.printf("GIF dimensions: %d x %d\n", + GIF.getCanvasWidth(), GIF.getCanvasHeight()); + xPos = (matrix.width() - GIF.getCanvasWidth()) / 2; // Center on matrix + yPos = (matrix.height() - GIF.getCanvasHeight()) / 2; + GIFisOpen = true; + GIFstartTime = millis(); + GIFincrement = 0; // Reset increment flag + } + } + } else if(GIFisOpen) { + if (GIF.playFrame(true, NULL) >= 0) { // Auto resets to start if needed + matrix.show(); + if ((millis() - GIFstartTime) >= (GIFminimumTime * 1000)) { + GIFincrement = 1; // Minimum time has elapsed, proceed to next GIF + } + } else { + GIFincrement = 1; // Decode error, proceed to next GIF + } + } +} diff --git a/circuitpython/lib/protomatter/examples/doublebuffer_scrolltext/doublebuffer_scrolltext.ino b/circuitpython/lib/protomatter/examples/doublebuffer_scrolltext/doublebuffer_scrolltext.ino new file mode 100644 index 0000000..03e2aa4 --- /dev/null +++ b/circuitpython/lib/protomatter/examples/doublebuffer_scrolltext/doublebuffer_scrolltext.ino @@ -0,0 +1,180 @@ +/* ---------------------------------------------------------------------- +Double-buffering (smooth animation) Protomatter library example. +PLEASE SEE THE "simple" EXAMPLE FOR AN INTRODUCTORY SKETCH. +Comments here pare down many of the basics and focus on the new concepts. + +This example is written for a 64x32 matrix but can be adapted to others. +------------------------------------------------------------------------- */ + +#include <Adafruit_Protomatter.h> +#include <Fonts/FreeSansBold18pt7b.h> // Large friendly font + +/* ---------------------------------------------------------------------- +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. +------------------------------------------------------------------------- */ + +#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 + +/* ---------------------------------------------------------------------- +Matrix initialization is explained EXTENSIVELY in "simple" example sketch! +It's very similar here, but we're passing "true" for the last argument, +enabling double-buffering -- this permits smooth animation by having us +draw in a second "off screen" buffer while the other is being shown. +------------------------------------------------------------------------- */ + +Adafruit_Protomatter matrix( + 64, // Matrix width in pixels + 6, // Bit depth -- 6 here provides maximum color options + 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 + true); // HERE IS THE MAGIG FOR DOUBLE-BUFFERING! + +// Sundry globals used for animation --------------------------------------- + +int16_t textX = matrix.width(), // Current text position (X) + textY, // Current text position (Y) + textMin, // Text pos. (X) when scrolled off left edge + hue = 0; +char str[50]; // Buffer to hold scrolling message text +int8_t ball[3][4] = { + { 3, 0, 1, 1 }, // Initial X,Y pos+velocity of 3 bouncy balls + { 17, 15, 1, -1 }, + { 27, 4, -1, 1 } +}; +uint16_t ballcolor[3]; // Colors for bouncy balls (init in setup()) + +// 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(;;); + } + + // Unlike the "simple" example, we don't do any drawing in setup(). + // But we DO initialize some things we plan to animate... + + // Set up the scrolling message... + sprintf(str, "Adafruit %dx%d RGB LED Matrix", + matrix.width(), matrix.height()); + matrix.setFont(&FreeSansBold18pt7b); // Use nice bitmap font + matrix.setTextWrap(false); // Allow text off edge + matrix.setTextColor(0xFFFF); // White + int16_t x1, y1; + uint16_t w, h; + matrix.getTextBounds(str, 0, 0, &x1, &y1, &w, &h); // How big is it? + textMin = -w; // All text is off left edge when it reaches this point + textY = matrix.height() / 2 - (y1 + h / 2); // Center text vertically + // Note: when making scrolling text like this, the setTextWrap(false) + // call is REQUIRED (to allow text to go off the edge of the matrix), + // AND it must be BEFORE the getTextBounds() call (or else that will + // return the bounds of "wrapped" text). + + // Set up the colors of the bouncy balls. + ballcolor[0] = matrix.color565(0, 20, 0); // Dark green + ballcolor[1] = matrix.color565(0, 0, 20); // Dark blue + ballcolor[2] = matrix.color565(20, 0, 0); // ark red +} + +// LOOP - RUNS REPEATEDLY AFTER SETUP -------------------------------------- + +void loop(void) { + // Every frame, we clear the background and draw everything anew. + // This happens "in the background" with double buffering, that's + // why you don't see everything flicker. It requires double the RAM, + // so it's not practical for every situation. + + matrix.fillScreen(0); // Fill background black + + // Draw the big scrolly text + matrix.setCursor(textX, textY); + matrix.print(str); + + // Update text position for next frame. If text goes off the + // left edge, reset its position to be off the right edge. + if((--textX) < textMin) textX = matrix.width(); + + // Draw the three bouncy balls on top of the text... + for(byte i=0; i<3; i++) { + // Draw 'ball' + matrix.fillCircle(ball[i][0], ball[i][1], 5, ballcolor[i]); + // Update ball's X,Y position for next frame + 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; + } + + // AFTER DRAWING, A show() CALL IS REQUIRED TO UPDATE THE MATRIX! + + matrix.show(); + + delay(20); // 20 milliseconds = ~50 frames/second +} diff --git a/circuitpython/lib/protomatter/examples/pixeldust/pixeldust.ino b/circuitpython/lib/protomatter/examples/pixeldust/pixeldust.ino new file mode 100644 index 0000000..0fa4126 --- /dev/null +++ b/circuitpython/lib/protomatter/examples/pixeldust/pixeldust.ino @@ -0,0 +1,140 @@ +/* ----------------------------------------------------------------------
+"Pixel dust" Protomatter library example. As written, this is
+SPECIFICALLY FOR THE ADAFRUIT MATRIXPORTAL M4 with 64x32 pixel matrix.
+Change "HEIGHT" below for 64x64 matrix. Could also be adapted to other
+Protomatter-capable boards with an attached LIS3DH accelerometer.
+
+PLEASE SEE THE "simple" EXAMPLE FOR AN INTRODUCTORY SKETCH,
+or "doublebuffer" for animation basics.
+------------------------------------------------------------------------- */
+
+#include <Wire.h> // For I2C communication
+#include <Adafruit_LIS3DH.h> // For accelerometer
+#include <Adafruit_PixelDust.h> // For sand simulation
+#include <Adafruit_Protomatter.h> // For RGB matrix
+
+#define HEIGHT 32 // Matrix height (pixels) - SET TO 64 FOR 64x64 MATRIX!
+#define WIDTH 64 // Matrix width (pixels)
+#define MAX_FPS 45 // Maximum redraw rate, frames/second
+
+#if HEIGHT == 64 // 64-pixel tall matrices have 5 address lines:
+uint8_t addrPins[] = {17, 18, 19, 20, 21};
+#else // 32-pixel tall matrices have 4 address lines:
+uint8_t addrPins[] = {17, 18, 19, 20};
+#endif
+
+// Remaining pins are the same for all matrix sizes. These values
+// are for MatrixPortal M4. See "simple" example for other boards.
+uint8_t rgbPins[] = {7, 8, 9, 10, 11, 12};
+uint8_t clockPin = 14;
+uint8_t latchPin = 15;
+uint8_t oePin = 16;
+
+Adafruit_Protomatter matrix(
+ WIDTH, 4, 1, rgbPins, sizeof(addrPins), addrPins,
+ clockPin, latchPin, oePin, true);
+
+Adafruit_LIS3DH accel = Adafruit_LIS3DH();
+
+#define N_COLORS 8
+#define BOX_HEIGHT 8
+#define N_GRAINS (BOX_HEIGHT*N_COLORS*8)
+uint16_t colors[N_COLORS];
+
+Adafruit_PixelDust sand(WIDTH, HEIGHT, N_GRAINS, 1, 128, false);
+
+uint32_t prevTime = 0; // Used for frames-per-second throttle
+
+// SETUP - RUNS ONCE AT PROGRAM START --------------------------------------
+
+void err(int x) {
+ uint8_t i;
+ pinMode(LED_BUILTIN, OUTPUT); // Using onboard LED
+ for(i=1;;i++) { // Loop forever...
+ digitalWrite(LED_BUILTIN, i & 1); // LED on/off blink to alert user
+ delay(x);
+ }
+}
+
+void setup(void) {
+ Serial.begin(115200);
+ //while (!Serial) delay(10);
+
+ ProtomatterStatus status = matrix.begin();
+ Serial.printf("Protomatter begin() status: %d\n", status);
+
+ if (!sand.begin()) {
+ Serial.println("Couldn't start sand");
+ err(1000); // Slow blink = malloc error
+ }
+
+ if (!accel.begin(0x19)) {
+ Serial.println("Couldn't find accelerometer");
+ err(250); // Fast bink = I2C error
+ }
+ accel.setRange(LIS3DH_RANGE_4_G); // 2, 4, 8 or 16 G!
+
+ //sand.randomize(); // Initialize random sand positions
+
+ // Set up initial sand coordinates, in 8x8 blocks
+ int n = 0;
+ for(int i=0; i<N_COLORS; i++) {
+ int xx = i * WIDTH / N_COLORS;
+ int yy = HEIGHT - BOX_HEIGHT;
+ for(int y=0; y<BOX_HEIGHT; y++) {
+ for(int x=0; x < WIDTH / N_COLORS; x++) {
+ //Serial.printf("#%d -> (%d, %d)\n", n, xx + x, yy + y);
+ sand.setPosition(n++, xx + x, yy + y);
+ }
+ }
+ }
+ Serial.printf("%d total pixels\n", n);
+
+ colors[0] = matrix.color565(64, 64, 64); // Dark Gray
+ colors[1] = matrix.color565(120, 79, 23); // Brown
+ colors[2] = matrix.color565(228, 3, 3); // Red
+ colors[3] = matrix.color565(255,140, 0); // Orange
+ colors[4] = matrix.color565(255,237, 0); // Yellow
+ colors[5] = matrix.color565( 0,128, 38); // Green
+ colors[6] = matrix.color565( 0, 77,255); // Blue
+ colors[7] = matrix.color565(117, 7,135); // Purple
+}
+
+// MAIN LOOP - RUNS ONCE PER FRAME OF ANIMATION ----------------------------
+
+void loop() {
+ // Limit the animation frame rate to MAX_FPS. Because the subsequent sand
+ // calculations are non-deterministic (don't always take the same amount
+ // of time, depending on their current states), this helps ensure that
+ // things like gravity appear constant in the simulation.
+ uint32_t t;
+ while(((t = micros()) - prevTime) < (1000000L / MAX_FPS));
+ prevTime = t;
+
+ // Read accelerometer...
+ sensors_event_t event;
+ accel.getEvent(&event);
+ //Serial.printf("(%0.1f, %0.1f, %0.1f)\n", event.acceleration.x, event.acceleration.y, event.acceleration.z);
+
+ double xx, yy, zz;
+ xx = event.acceleration.x * 1000;
+ yy = event.acceleration.y * 1000;
+ zz = event.acceleration.z * 1000;
+
+ // Run one frame of the simulation
+ sand.iterate(xx, yy, zz);
+
+ //sand.iterate(-accel.y, accel.x, accel.z);
+
+ // Update pixel data in LED driver
+ dimension_t x, y;
+ matrix.fillScreen(0x0);
+ for(int i=0; i<N_GRAINS ; i++) {
+ sand.getPosition(i, &x, &y);
+ int n = i / ((WIDTH / N_COLORS) * BOX_HEIGHT); // Color index
+ uint16_t flakeColor = colors[n];
+ matrix.drawPixel(x, y, flakeColor);
+ //Serial.printf("(%d, %d)\n", x, y);
+ }
+ matrix.show(); // Copy data to matrix buffers
+}
diff --git a/circuitpython/lib/protomatter/examples/simple/simple.ino b/circuitpython/lib/protomatter/examples/simple/simple.ino new file mode 100644 index 0000000..2f8d12d --- /dev/null +++ b/circuitpython/lib/protomatter/examples/simple/simple.ino @@ -0,0 +1,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! +*/ diff --git a/circuitpython/lib/protomatter/examples/tiled/tiled.ino b/circuitpython/lib/protomatter/examples/tiled/tiled.ino new file mode 100644 index 0000000..eb2cdb0 --- /dev/null +++ b/circuitpython/lib/protomatter/examples/tiled/tiled.ino @@ -0,0 +1,136 @@ +/* ---------------------------------------------------------------------- +"Tiled" Protomatter library example sketch. Demonstrates use of multiple +RGB LED matrices as a single larger drawing surface. This example is +written for two 64x32 matrices (tiled into a 64x64 display) but can be +adapted to others. If using MatrixPortal, larger multi-panel tilings like +this should be powered from a separate 5V DC supply, not the USB port +(this example works OK because the graphics are very minimal). + +PLEASE SEE THE "simple" EXAMPLE FOR AN INTRODUCTORY SKETCH. +------------------------------------------------------------------------- */ + +#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. +------------------------------------------------------------------------- */ + +#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 + +/* ---------------------------------------------------------------------- +Matrix initialization is explained EXTENSIVELY in "simple" example sketch! +It's very similar here, but we're passing an extra argument to define the +matrix tiling along the vertical axis: -2 means there are two matrices +(or rows of matrices) arranged in a "serpentine" path (the second matrix +is rotated 180 degrees relative to the first, and positioned below). +A positive 2 would indicate a "progressive" path (both matrices are +oriented the same way), but usually requires longer cables. +------------------------------------------------------------------------- */ + +Adafruit_Protomatter matrix( + 64, // Width of matrix (or matrices, if tiled horizontally) + 6, // 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) + -2); // Row tiling: two rows in "serpentine" path + +// 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 program has no animation, all the drawing can be done + // here in setup() rather than loop(). It's just a few basic shapes + // that span across the matrices...nothing showy, the goal of this + // sketch is just to demonstrate tiling basics. + + matrix.drawLine(0, 0, matrix.width() - 1, matrix.height() - 1, + matrix.color565(255, 0, 0)); // Red line + matrix.drawLine(matrix.width() - 1, 0, 0, matrix.height() - 1, + matrix.color565(0, 0, 255)); // Blue line + int radius = min(matrix.width(), matrix.height()) / 2; + matrix.drawCircle(matrix.width() / 2, matrix.height() / 2, radius, + matrix.color565(0, 255, 0)); // Green circle + + // 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 + // prints the approximate refresh rate of the matrix at current settings. + Serial.print("Refresh FPS = ~"); + Serial.println(matrix.getFrameCount()); + delay(1000); +} |