Compare commits
27 commits
master
...
circuitpyt
| Author | SHA1 | Date | |
|---|---|---|---|
| 3a817d6057 | |||
|
|
4cbf99aa54 | ||
|
|
a51676db5e | ||
|
|
40cb6a9cac | ||
|
|
87a95379b5 | ||
|
|
8968259c14 | ||
|
|
fc180e4689 | ||
|
|
9f71088d2c | ||
|
|
973de07bfc | ||
| 1b8f461f64 | |||
| 241bf2516c | |||
| 41a92d7731 | |||
|
|
fac7eea645 | ||
|
|
3bc6f23541 | ||
| c411714cbd | |||
| c3a3e35731 | |||
| 7f448357d0 | |||
| 969672dff1 | |||
|
|
5a1151db28 | ||
|
|
e1659f270c | ||
|
|
20c3b67bc3 | ||
|
|
103ea89935 | ||
|
|
51f39d9594 | ||
|
|
0958b759a5 | ||
|
|
97c2122ba7 | ||
|
|
250675d2fc | ||
|
|
831a253898 |
11 changed files with 1986 additions and 1145 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Our handy .gitignore for automation ease
|
||||
Doxyfile*
|
||||
doxygen_sqlite3.db
|
||||
html
|
||||
41
.travis.yml
Normal file
41
.travis.yml
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
language: c
|
||||
sudo: false
|
||||
cache:
|
||||
directories:
|
||||
- ~/arduino_ide
|
||||
- ~/.arduino15/packages/
|
||||
git:
|
||||
depth: false
|
||||
quiet: true
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- llvm-toolchain-trusty-5.0
|
||||
- key_url: 'http://apt.llvm.org/llvm-snapshot.gpg.key'
|
||||
packages:
|
||||
- python3-pip
|
||||
- python3-wheel
|
||||
- clang-format-5.0
|
||||
env:
|
||||
global:
|
||||
# - ARDUINO_IDE_VERSION="1.8.10"
|
||||
- PRETTYNAME="Adafruit Protomatter"
|
||||
# Optional, will default to "$TRAVIS_BUILD_DIR/Doxyfile"
|
||||
# - DOXYFILE: $TRAVIS_BUILD_DIR/Doxyfile
|
||||
|
||||
before_install:
|
||||
- source <(curl -SLs https://raw.githubusercontent.com/adafruit/travis-ci-arduino/master/install.sh)
|
||||
- curl -SLs https://raw.githubusercontent.com/adafruit/travis-ci-arduino/master/run-clang-format.py > run-clang-format.py
|
||||
|
||||
install:
|
||||
- arduino --install-library "Adafruit GFX Library"
|
||||
|
||||
script:
|
||||
- python run-clang-format.py -r .
|
||||
- build_m4_platforms
|
||||
- build_nrf5x_platforms
|
||||
|
||||
# Generate and deploy documentation
|
||||
after_success:
|
||||
- source <(curl -SLs https://raw.githubusercontent.com/adafruit/travis-ci-arduino/master/library_check.sh)
|
||||
- source <(curl -SLs https://raw.githubusercontent.com/adafruit/travis-ci-arduino/master/doxy_gen_and_deploy.sh)
|
||||
|
|
@ -1,3 +1,38 @@
|
|||
/*!
|
||||
* @file Adafruit_Protomatter.cpp
|
||||
*
|
||||
* @mainpage Adafruit Protomatter RGB LED matrix library.
|
||||
*
|
||||
* @section intro_sec Introduction
|
||||
*
|
||||
* This is documentation for Adafruit's protomatter library for HUB75-style
|
||||
* RGB LED matrices. It is designed to work with various matrices sold by
|
||||
* Adafruit ("HUB75" is a vague term and other similar matrices are not
|
||||
* guaranteed to work). This file is the Arduino-specific calls; the
|
||||
* underlying C code is more platform-neutral.
|
||||
*
|
||||
* Adafruit invests time and resources providing this open source code,
|
||||
* please support Adafruit and open-source hardware by purchasing products
|
||||
* from Adafruit!
|
||||
*
|
||||
* @section dependencies Dependencies
|
||||
*
|
||||
* This library depends on
|
||||
* <a href="https://github.com/adafruit/Adafruit-GFX-Library">Adafruit_GFX</a>
|
||||
* being present on your system. Please make sure you have installed the
|
||||
* latest version before using this library.
|
||||
*
|
||||
* @section author Author
|
||||
*
|
||||
* Written by Phil "Paint Your Dragon" Burgess and Jeff Epler for
|
||||
* Adafruit Industries, with contributions from the open source community.
|
||||
*
|
||||
* @section license License
|
||||
*
|
||||
* BSD license, all text here must be included in any redistribution.
|
||||
*
|
||||
*/
|
||||
|
||||
// Arduino-specific wrapper for the Protomatter C library (provides
|
||||
// constructor and so forth, builds on Adafruit_GFX). There should
|
||||
// not be any device-specific #ifdefs here. See notes in core.c and
|
||||
|
|
@ -5,7 +40,7 @@
|
|||
|
||||
#include "Adafruit_Protomatter.h" // Also includes core.h & Adafruit_GFX.h
|
||||
|
||||
extern Protomatter_core *_PM_protoPtr; // In core.c (via arch.h)
|
||||
extern Protomatter_core *_PM_protoPtr; ///< In core.c (via arch.h)
|
||||
|
||||
// Overall matrix refresh rate (frames/second) is a function of matrix width
|
||||
// and chain length, number of address lines, number of bit planes, CPU speed
|
||||
|
|
@ -21,31 +56,31 @@ extern Protomatter_core *_PM_protoPtr; // In core.c (via arch.h)
|
|||
// refresh slower than this, and in many cases will...just need to set an
|
||||
// upper limit to avoid excessive CPU load). An incredibly long comment block
|
||||
// for a single constant, thank you for coming to my TED talk!
|
||||
#define _PM_MAX_REFRESH_HZ 250
|
||||
#define _PM_MAX_REFRESH_HZ 250 ///< Upper limit (ish) to matrix refresh rate
|
||||
|
||||
// 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
|
||||
#define _PM_ROW_DELAY 8 ///< Delay time between row address line changes (ms)
|
||||
|
||||
|
||||
Adafruit_Protomatter::Adafruit_Protomatter(
|
||||
uint16_t bitWidth, uint8_t bitDepth,
|
||||
Adafruit_Protomatter::Adafruit_Protomatter(uint16_t bitWidth, uint8_t bitDepth,
|
||||
uint8_t rgbCount, uint8_t *rgbList,
|
||||
uint8_t addrCount, uint8_t *addrList,
|
||||
uint8_t clockPin, uint8_t latchPin, uint8_t oePin,
|
||||
bool doubleBuffer, void *timer) :
|
||||
GFXcanvas16(bitWidth, (2 << min(addrCount, 5)) * min(rgbCount, 5)) {
|
||||
if(bitDepth > 6) bitDepth = 6; // GFXcanvas16 color limit (565)
|
||||
uint8_t clockPin, uint8_t latchPin,
|
||||
uint8_t oePin, bool doubleBuffer,
|
||||
void *timer)
|
||||
: GFXcanvas16(bitWidth, (2 << min(addrCount, 5)) * min(rgbCount, 5)) {
|
||||
if (bitDepth > 6)
|
||||
bitDepth = 6; // GFXcanvas16 color limit (565)
|
||||
|
||||
// Arguments are passed through to the C _PM_init() function which does
|
||||
// some input validation and minor allocation. Return value is ignored
|
||||
// because we can't really do anything about it in a C++ constructor.
|
||||
// The class begin() function checks rgbPins for NULL to determine
|
||||
// whether to proceed or indicate an error.
|
||||
(void)_PM_init(&core, bitWidth, bitDepth, rgbCount, rgbList,
|
||||
addrCount, addrList, clockPin, latchPin, oePin, doubleBuffer, timer);
|
||||
(void)_PM_init(&core, bitWidth, bitDepth, rgbCount, rgbList, addrCount,
|
||||
addrList, clockPin, latchPin, oePin, doubleBuffer, timer);
|
||||
}
|
||||
|
||||
Adafruit_Protomatter::~Adafruit_Protomatter(void) {
|
||||
|
|
@ -55,33 +90,15 @@ Adafruit_Protomatter::~Adafruit_Protomatter(void) {
|
|||
|
||||
ProtomatterStatus Adafruit_Protomatter::begin(void) {
|
||||
_PM_protoPtr = &core;
|
||||
_PM_begin(&core);
|
||||
return PROTOMATTER_OK;
|
||||
return _PM_begin(&core);
|
||||
}
|
||||
|
||||
// Transfer data from GFXcanvas16 to the matrix framebuffer's weird
|
||||
// internal format. The actual conversion functions referenced below
|
||||
// are in core.c, reasoning is explained there.
|
||||
void Adafruit_Protomatter::show(void) {
|
||||
|
||||
// Destination address is computed in convert function
|
||||
// (based on active buffer value, if double-buffering),
|
||||
// just need to pass in the canvas buffer address and
|
||||
// width in pixels.
|
||||
if(core.bytesPerElement == 1) {
|
||||
_PM_convert_565_byte(&core, getBuffer(), WIDTH);
|
||||
} else if(core.bytesPerElement == 2) {
|
||||
_PM_convert_565_word(&core, getBuffer(), WIDTH);
|
||||
} else {
|
||||
_PM_convert_565_long(&core, getBuffer(), WIDTH);
|
||||
}
|
||||
|
||||
if(core.doubleBuffer) {
|
||||
core.swapBuffers = 1;
|
||||
// To avoid overwriting data on the matrix, don't return
|
||||
// until the timer ISR has performed the swap at the right time.
|
||||
while(core.swapBuffers);
|
||||
}
|
||||
_PM_convert_565(&core, getBuffer(), WIDTH);
|
||||
_PM_swapbuffer_maybe(&core);
|
||||
}
|
||||
|
||||
// Returns current value of frame counter and resets its value to zero.
|
||||
|
|
|
|||
|
|
@ -7,18 +7,95 @@
|
|||
#include "core.h"
|
||||
#include <Adafruit_GFX.h>
|
||||
|
||||
/*!
|
||||
@brief Class representing the Arduino-facing side of the Protomatter
|
||||
library. Subclass of Adafruit_GFX's GFXcanvas16 to allow all
|
||||
the drawing operations.
|
||||
*/
|
||||
class Adafruit_Protomatter : public GFXcanvas16 {
|
||||
public:
|
||||
Adafruit_Protomatter(uint16_t bitWidth, uint8_t bitDepth,
|
||||
uint8_t rgbCount, uint8_t *rgbList,
|
||||
uint8_t addrCount, uint8_t *addrList,
|
||||
public:
|
||||
/*!
|
||||
@brief Adafruit_Protomatter constructor.
|
||||
@param bitWidth Total width of RGB matrix chain, in pixels.
|
||||
Usu. some multiple of 32, but maybe exceptions.
|
||||
@param bitDepth Color "depth" in bitplanes, determines range of
|
||||
shades of red, green and blue. e.g. passing 4
|
||||
bits = 16 shades ea. R,G,B = 16x16x16 = 4096
|
||||
colors. Max is 6, since the GFX library works
|
||||
with "565" RGB colors (6 bits green, 5 red/blue).
|
||||
@param rgbCount Number of "sets" of RGB data pins, each set
|
||||
containing 6 pins (2 ea. R,G,B). Typically 1,
|
||||
indicating a single matrix (or matrix chain).
|
||||
In theory (but not yet extensively tested),
|
||||
multiple sets of pins can be driven in parallel,
|
||||
up to 5 on some devices (if the hardware design
|
||||
provides all those bits on one PORT).
|
||||
@param rgbList A uint8_t array of pins (Arduino pin numbering),
|
||||
6X the prior rgbCount value, corresponding to
|
||||
the 6 output color bits for a matrix (or chain).
|
||||
Order is upper-half red, green, blue, lower-half
|
||||
red, green blue (repeat for each add'l chain).
|
||||
All the RGB pins (plus the clock pin below on
|
||||
some architectures) MUST be on the same PORT
|
||||
register. It's recommended (but not required)
|
||||
that all RGB pins (and clock depending on arch)
|
||||
be within the same byte of a PORT (but do not
|
||||
need to be sequential or contiguous within that
|
||||
byte) for more efficient RAM utilization. For
|
||||
two concurrent chains, same principle but 16-bit
|
||||
word instead of byte.
|
||||
@param addrCount Number of row address lines required of matrix.
|
||||
Total pixel height is then 2 x 2^addrCount, e.g.
|
||||
32-pixel-tall matrices have 4 row address lines.
|
||||
@param addrList A uint8_t array of pins (Arduino pin numbering),
|
||||
one per row address line.
|
||||
@param clockPin RGB clock pin (Arduino pin #).
|
||||
@param latchPin RGB data latch pin (Arduino pin #).
|
||||
@param oePin Output enable pin (Arduino pin #), active low.
|
||||
@param doubleBuffer If true, two matrix buffers are allocated,
|
||||
so changing display contents doesn't introduce
|
||||
artifacts mid-conversion. Requires ~2X RAM.
|
||||
@param timer Pointer to timer peripheral or timer-related
|
||||
struct (architecture-dependent), or NULL to
|
||||
use a default timer ID (also arch-dependent).
|
||||
*/
|
||||
Adafruit_Protomatter(uint16_t bitWidth, uint8_t bitDepth, uint8_t rgbCount,
|
||||
uint8_t *rgbList, uint8_t addrCount, uint8_t *addrList,
|
||||
uint8_t clockPin, uint8_t latchPin, uint8_t oePin,
|
||||
bool doubleBuffer, void *timer=NULL);
|
||||
bool doubleBuffer, void *timer = NULL);
|
||||
~Adafruit_Protomatter(void);
|
||||
|
||||
/*!
|
||||
@brief Start a Protomatter matrix display running -- initialize
|
||||
pins, timer and interrupt into existence.
|
||||
@return A ProtomatterStatus status, one of:
|
||||
PROTOMATTER_OK if everything is good.
|
||||
PROTOMATTER_ERR_PINS if data and/or clock pins are split
|
||||
across different PORTs.
|
||||
PROTOMATTER_ERR_MALLOC if insufficient RAM to allocate
|
||||
display memory.
|
||||
PROTOMATTER_ERR_ARG if a bad value was passed to the
|
||||
constructor.
|
||||
*/
|
||||
ProtomatterStatus begin(void);
|
||||
|
||||
/*!
|
||||
@brief Process data from GFXcanvas16 to the matrix framebuffer's
|
||||
internal format for display.
|
||||
*/
|
||||
void show(void);
|
||||
|
||||
/*!
|
||||
@brief Returns current value of frame counter and resets its value
|
||||
to zero. Two calls to this, timed one second apart (or use
|
||||
math with other intervals), can be used to get a rough
|
||||
frames-per-second value for the matrix (since this is
|
||||
difficult to estimate beforehand).
|
||||
@return Frame count since previous call to function, as a uint32_t.
|
||||
*/
|
||||
uint32_t getFrameCount(void);
|
||||
private:
|
||||
|
||||
private:
|
||||
Protomatter_core core; // Underlying C struct
|
||||
void convert_byte(uint8_t *dest); // GFXcanvas16-to-matrix
|
||||
void convert_word(uint16_t *dest); // conversion functions
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Adafruit_Protomatter
|
||||
# Adafruit_Protomatter [](https://travis-ci.com/adafruit/Adafruit_Protomatter)
|
||||
|
||||
"I used protomatter in the Genesis matrix." - David Marcus, Star Trek III
|
||||
|
||||
|
|
@ -6,8 +6,6 @@ Code for driving HUB75-style RGB LED matrices, targeted at 32-bit MCUs
|
|||
using brute-force GPIO (that is, not relying on DMA or other specialized
|
||||
peripherals beyond a timer interrupt, goal being portability).
|
||||
|
||||
Name might change as it's nondescriptive and tedious to type in code.
|
||||
|
||||
# Matrix Concepts and Jargon
|
||||
|
||||
HUB75 RGB LED matrices are basically a set of six concurrent shift register
|
||||
|
|
|
|||
318
core.c
318
core.c
|
|
@ -1,3 +1,19 @@
|
|||
/*!
|
||||
* @file core.c
|
||||
*
|
||||
* Part of Adafruit's Protomatter library for HUB75-style RGB LED matrices.
|
||||
*
|
||||
* Adafruit invests time and resources providing this open source code,
|
||||
* please support Adafruit and open-source hardware by purchasing
|
||||
* products from Adafruit!
|
||||
*
|
||||
* Written by Phil "Paint Your Dragon" Burgess and Jeff Epler for
|
||||
* Adafruit Industries, with contributions from the open source community.
|
||||
*
|
||||
* BSD license, all text here must be included in any redistribution.
|
||||
*
|
||||
*/
|
||||
|
||||
// Device- and environment-neutral core matrix-driving functionality.
|
||||
// See notes near top of arch.h regarding assumptions of hardware
|
||||
// "common ground." If you find yourself doing an "#ifdef ARDUINO" or
|
||||
|
|
@ -15,6 +31,8 @@
|
|||
|
||||
#include "core.h" // enums and structs
|
||||
#include "arch.h" // Do NOT include this in any other source files
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
// Overall matrix refresh rate (frames/second) is a function of matrix width
|
||||
// and chain length, number of address lines, number of bit planes, CPU speed
|
||||
|
|
@ -30,13 +48,13 @@
|
|||
// refresh slower than this, and in many cases will...just need to set an
|
||||
// upper limit to avoid excessive CPU load). An incredibly long comment block
|
||||
// for a single constant, thank you for coming to my TED talk!
|
||||
#define _PM_MAX_REFRESH_HZ 250
|
||||
#define _PM_MAX_REFRESH_HZ 250 ///< Max matrix refresh rate
|
||||
|
||||
// Time (in microseconds) 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
|
||||
#define _PM_ROW_DELAY 8 ///< Delay time between row address line changes (ms)
|
||||
|
||||
// These are the lowest-level functions for issing data to matrices.
|
||||
// There are three versions because it depends on how the six RGB data bits
|
||||
|
|
@ -54,27 +72,42 @@ static void blast_byte(Protomatter_core *core, uint8_t *data);
|
|||
static void blast_word(Protomatter_core *core, uint16_t *data);
|
||||
static void blast_long(Protomatter_core *core, uint32_t *data);
|
||||
|
||||
#define _PM_clearReg(x) \
|
||||
(*(volatile _PM_PORT_TYPE *)((x).clearReg) = \
|
||||
((x).bit)) ///< Clear non-RGB-data-or-clock control line (_PM_pin type)
|
||||
#define _PM_setReg(x) \
|
||||
(*(volatile _PM_PORT_TYPE *)((x).setReg) = \
|
||||
((x).bit)) ///< Set non-RGB-data-or-clock control line (_PM_pin type)
|
||||
|
||||
// Validate and populate vital elements of core structure.
|
||||
// Does NOT allocate core struct -- calling function must provide that.
|
||||
// (In the Arduino C++ library, it’s part of the Protomatter class.)
|
||||
ProtomatterStatus _PM_init(Protomatter_core *core,
|
||||
uint16_t bitWidth, uint8_t bitDepth,
|
||||
uint8_t rgbCount, uint8_t *rgbList,
|
||||
ProtomatterStatus _PM_init(Protomatter_core *core, uint16_t bitWidth,
|
||||
uint8_t bitDepth, uint8_t rgbCount, uint8_t *rgbList,
|
||||
uint8_t addrCount, uint8_t *addrList,
|
||||
uint8_t clockPin, uint8_t latchPin, uint8_t oePin,
|
||||
bool doubleBuffer, void *timer) {
|
||||
if(!core) return PROTOMATTER_ERR_ARG;
|
||||
if (!core)
|
||||
return PROTOMATTER_ERR_ARG;
|
||||
|
||||
if(rgbCount > 5) rgbCount = 5; // Max 5 in parallel (32-bit PORT)
|
||||
if(addrCount > 5) addrCount = 5; // Max 5 address lines (A-E)
|
||||
if (rgbCount > 5)
|
||||
rgbCount = 5; // Max 5 in parallel (32-bit PORT)
|
||||
if (addrCount > 5)
|
||||
addrCount = 5; // Max 5 address lines (A-E)
|
||||
// bitDepth is NOT constrained here, handle in calling function
|
||||
// (varies with implementation, e.g. GFX lib is max 6 bitplanes,
|
||||
// but might be more or less elsewhere)
|
||||
|
||||
#if defined(_PM_TIMER_DEFAULT)
|
||||
// If NULL timer was passed in (the default case for the constructor),
|
||||
// use default value from arch.h. For example, in the Arduino case it's
|
||||
// tied to TC4 specifically.
|
||||
if(timer == NULL) timer = _PM_TIMER_DEFAULT;
|
||||
if (timer == NULL)
|
||||
timer = _PM_TIMER_DEFAULT;
|
||||
#else
|
||||
if (timer == NULL)
|
||||
return PROTOMATTER_ERR_ARG;
|
||||
#endif
|
||||
|
||||
core->timer = timer;
|
||||
core->width = bitWidth; // Total matrix chain length in bits
|
||||
|
|
@ -95,15 +128,15 @@ ProtomatterStatus _PM_init(Protomatter_core *core,
|
|||
// the pin bitmasks.
|
||||
|
||||
rgbCount *= 6; // Convert parallel count to pin count
|
||||
if((core->rgbPins = (uint8_t *)malloc(rgbCount * sizeof(uint8_t)))) {
|
||||
if((core->addr = (_PM_pin *)malloc(addrCount * sizeof(_PM_pin)))) {
|
||||
if ((core->rgbPins = (uint8_t *)_PM_ALLOCATOR(rgbCount * sizeof(uint8_t)))) {
|
||||
if ((core->addr = (_PM_pin *)_PM_ALLOCATOR(addrCount * sizeof(_PM_pin)))) {
|
||||
memcpy(core->rgbPins, rgbList, rgbCount * sizeof(uint8_t));
|
||||
for(uint8_t i=0; i<addrCount; i++) {
|
||||
for (uint8_t i = 0; i < addrCount; i++) {
|
||||
core->addr[i].pin = addrList[i];
|
||||
}
|
||||
return PROTOMATTER_OK;
|
||||
}
|
||||
free(core->rgbPins);
|
||||
_PM_FREE(core->rgbPins);
|
||||
core->rgbPins = NULL;
|
||||
}
|
||||
return PROTOMATTER_ERR_MALLOC;
|
||||
|
|
@ -111,9 +144,10 @@ ProtomatterStatus _PM_init(Protomatter_core *core,
|
|||
|
||||
// Allocate display buffers and populate additional elements.
|
||||
ProtomatterStatus _PM_begin(Protomatter_core *core) {
|
||||
if(!core) return PROTOMATTER_ERR_ARG;
|
||||
if (!core)
|
||||
return PROTOMATTER_ERR_ARG;
|
||||
|
||||
if(!core->rgbPins) { // NULL if copy failed to allocate
|
||||
if (!core->rgbPins) { // NULL if copy failed to allocate
|
||||
return PROTOMATTER_ERR_MALLOC;
|
||||
}
|
||||
|
||||
|
|
@ -133,9 +167,9 @@ ProtomatterStatus _PM_begin(Protomatter_core *core) {
|
|||
uint32_t bitMask = 0;
|
||||
#endif
|
||||
|
||||
for(uint8_t i=0; i<core->parallel * 6; i++) {
|
||||
for (uint8_t i = 0; i < core->parallel * 6; i++) {
|
||||
uint8_t *p2 = (uint8_t *)_PM_portOutRegister(core->rgbPins[i]);
|
||||
if(p2 != port) {
|
||||
if (p2 != port) {
|
||||
return PROTOMATTER_ERR_PINS;
|
||||
}
|
||||
bitMask |= _PM_portBitMask(core->rgbPins[i]);
|
||||
|
|
@ -148,11 +182,15 @@ ProtomatterStatus _PM_begin(Protomatter_core *core) {
|
|||
// register is present) are in the same byte, this can be stored more
|
||||
// compact than if they're spread across a word or long.
|
||||
uint8_t byteMask = 0;
|
||||
if(bitMask & 0xFF000000) byteMask |= 0b1000;
|
||||
if(bitMask & 0x00FF0000) byteMask |= 0b0100;
|
||||
if(bitMask & 0x0000FF00) byteMask |= 0b0010;
|
||||
if(bitMask & 0x000000FF) byteMask |= 0b0001;
|
||||
switch(byteMask) {
|
||||
if (bitMask & 0xFF000000)
|
||||
byteMask |= 0b1000;
|
||||
if (bitMask & 0x00FF0000)
|
||||
byteMask |= 0b0100;
|
||||
if (bitMask & 0x0000FF00)
|
||||
byteMask |= 0b0010;
|
||||
if (bitMask & 0x000000FF)
|
||||
byteMask |= 0b0001;
|
||||
switch (byteMask) {
|
||||
case 0b0001: // If all PORT bits are in the same byte...
|
||||
case 0b0010:
|
||||
case 0b0100:
|
||||
|
|
@ -175,17 +213,19 @@ ProtomatterStatus _PM_begin(Protomatter_core *core) {
|
|||
core->numRowPairs = 1 << core->numAddressLines;
|
||||
uint8_t chunks = (core->width + (_PM_chunkSize - 1)) / _PM_chunkSize;
|
||||
uint16_t columns = chunks * _PM_chunkSize; // Padded matrix width
|
||||
uint32_t screenBytes = columns * core->numRowPairs * core->numPlanes *
|
||||
core->bytesPerElement;
|
||||
uint32_t screenBytes =
|
||||
columns * core->numRowPairs * core->numPlanes * core->bytesPerElement;
|
||||
|
||||
core->bufferSize = screenBytes; // Bytes per matrix buffer (1 or 2)
|
||||
if(core->doubleBuffer) screenBytes *= 2; // Total for matrix buffer(s)
|
||||
if (core->doubleBuffer)
|
||||
screenBytes *= 2; // Total for matrix buffer(s)
|
||||
uint32_t rgbMaskBytes = core->parallel * 6 * core->bytesPerElement;
|
||||
|
||||
// Allocate matrix buffer(s). Don't worry about the return type...
|
||||
// though we might be using words or longs for certain pin configs,
|
||||
// malloc() by definition always aligns to the longest type.
|
||||
if(!(core->screenData = (uint8_t *)malloc(screenBytes + rgbMaskBytes))) {
|
||||
// _PM_ALLOCATOR() by definition always aligns to the longest type.
|
||||
if (!(core->screenData =
|
||||
(uint8_t *)_PM_ALLOCATOR(screenBytes + rgbMaskBytes))) {
|
||||
return PROTOMATTER_ERR_MALLOC;
|
||||
}
|
||||
|
||||
|
|
@ -199,34 +239,33 @@ ProtomatterStatus _PM_begin(Protomatter_core *core) {
|
|||
#endif
|
||||
|
||||
// Figure out clockMask and rgbAndClockMask, clear matrix buffers
|
||||
if(core->bytesPerElement == 1) {
|
||||
if (core->bytesPerElement == 1) {
|
||||
core->portOffset = _PM_byteOffset(core->rgbPins[0]);
|
||||
#if defined(_PM_portToggleRegister)
|
||||
// Clock and rgbAndClockMask are 8-bit values
|
||||
core->clockMask = _PM_portBitMask(core->clockPin) >>
|
||||
(core->portOffset * 8);
|
||||
core->rgbAndClockMask = (bitMask >> (core->portOffset * 8)) |
|
||||
core->clockMask;
|
||||
core->clockMask = _PM_portBitMask(core->clockPin) >> (core->portOffset * 8);
|
||||
core->rgbAndClockMask =
|
||||
(bitMask >> (core->portOffset * 8)) | core->clockMask;
|
||||
memset(core->screenData, core->clockMask, screenBytes);
|
||||
#else
|
||||
// Clock and rgbAndClockMask are 32-bit values
|
||||
core->clockMask = _PM_portBitMask(core->clockPin);
|
||||
core->rgbAndClockMask = bitMask | core->clockMask;
|
||||
#endif
|
||||
for(uint8_t i=0; i<core->parallel * 6; i++) {
|
||||
for (uint8_t i = 0; i < core->parallel * 6; i++) {
|
||||
((uint8_t *)core->rgbMask)[i] = // Pin bitmasks are 8-bit
|
||||
_PM_portBitMask(core->rgbPins[i]) >> (core->portOffset * 8);
|
||||
}
|
||||
} else if(core->bytesPerElement == 2) {
|
||||
} else if (core->bytesPerElement == 2) {
|
||||
core->portOffset = _PM_wordOffset(core->rgbPins[0]);
|
||||
#if defined(_PM_portToggleRegister)
|
||||
// Clock and rgbAndClockMask are 16-bit values
|
||||
core->clockMask = _PM_portBitMask(core->clockPin) >>
|
||||
(core->portOffset * 16);
|
||||
core->rgbAndClockMask = (bitMask >> (core->portOffset * 16)) |
|
||||
core->clockMask;
|
||||
core->clockMask =
|
||||
_PM_portBitMask(core->clockPin) >> (core->portOffset * 16);
|
||||
core->rgbAndClockMask =
|
||||
(bitMask >> (core->portOffset * 16)) | core->clockMask;
|
||||
uint32_t elements = screenBytes / 2;
|
||||
for(uint32_t i=0; i<elements; i++) {
|
||||
for (uint32_t i = 0; i < elements; i++) {
|
||||
((uint16_t *)core->screenData)[i] = core->clockMask;
|
||||
}
|
||||
#else
|
||||
|
|
@ -234,7 +273,7 @@ ProtomatterStatus _PM_begin(Protomatter_core *core) {
|
|||
core->clockMask = _PM_portBitMask(core->clockPin);
|
||||
core->rgbAndClockMask = bitMask | core->clockMask;
|
||||
#endif
|
||||
for(uint8_t i=0; i<core->parallel * 6; i++) {
|
||||
for (uint8_t i = 0; i < core->parallel * 6; i++) {
|
||||
((uint16_t *)core->rgbMask)[i] = // Pin bitmasks are 16-bit
|
||||
_PM_portBitMask(core->rgbPins[i]) >> (core->portOffset * 16);
|
||||
}
|
||||
|
|
@ -244,11 +283,11 @@ ProtomatterStatus _PM_begin(Protomatter_core *core) {
|
|||
core->rgbAndClockMask = bitMask | core->clockMask;
|
||||
#if defined(_PM_portToggleRegister)
|
||||
uint32_t elements = screenBytes / 4;
|
||||
for(uint32_t i=0; i<elements; i++) {
|
||||
for (uint32_t i = 0; i < elements; i++) {
|
||||
((uint32_t *)core->screenData)[i] = core->clockMask;
|
||||
}
|
||||
#endif
|
||||
for(uint8_t i=0; i<core->parallel * 6; i++) {
|
||||
for (uint8_t i = 0; i < core->parallel * 6; i++) {
|
||||
((uint32_t *)core->rgbMask)[i] = // Pin bitmasks are 32-bit
|
||||
_PM_portBitMask(core->rgbPins[i]);
|
||||
}
|
||||
|
|
@ -258,7 +297,7 @@ ProtomatterStatus _PM_begin(Protomatter_core *core) {
|
|||
uint32_t minPeriodPerFrame = _PM_timerFreq / _PM_MAX_REFRESH_HZ;
|
||||
uint32_t minPeriodPerLine = minPeriodPerFrame / core->numRowPairs;
|
||||
core->minPeriod = minPeriodPerLine / ((1 << core->numPlanes) - 1);
|
||||
if(core->minPeriod < _PM_minMinPeriod) {
|
||||
if (core->minPeriod < _PM_minMinPeriod) {
|
||||
core->minPeriod = _PM_minMinPeriod;
|
||||
}
|
||||
// Actual frame rate may be lower than this...it's only an estimate
|
||||
|
|
@ -288,7 +327,7 @@ ProtomatterStatus _PM_begin(Protomatter_core *core) {
|
|||
_PM_pinOutput(core->oe.pin);
|
||||
_PM_pinHigh(core->oe.pin); // Init OE HIGH (disable output)
|
||||
|
||||
for(uint8_t i=0; i<core->parallel * 6; i++) {
|
||||
for (uint8_t i = 0; i < core->parallel * 6; i++) {
|
||||
_PM_pinOutput(core->rgbPins[i]);
|
||||
_PM_pinLow(core->rgbPins[i]);
|
||||
}
|
||||
|
|
@ -296,23 +335,20 @@ ProtomatterStatus _PM_begin(Protomatter_core *core) {
|
|||
core->addrPortToggle = _PM_portToggleRegister(core->addr[0].pin);
|
||||
core->singleAddrPort = 1;
|
||||
#endif
|
||||
for(uint8_t line=0,bit=1; line<core->numAddressLines; line++, bit<<=1) {
|
||||
core->addr[line].setReg =
|
||||
_PM_portSetRegister(core->addr[line].pin);
|
||||
core->addr[line].clearReg =
|
||||
_PM_portClearRegister(core->addr[line].pin);
|
||||
core->addr[line].bit =
|
||||
_PM_portBitMask(core->addr[line].pin);
|
||||
for (uint8_t line = 0, bit = 1; line < core->numAddressLines;
|
||||
line++, bit <<= 1) {
|
||||
core->addr[line].setReg = _PM_portSetRegister(core->addr[line].pin);
|
||||
core->addr[line].clearReg = _PM_portClearRegister(core->addr[line].pin);
|
||||
core->addr[line].bit = _PM_portBitMask(core->addr[line].pin);
|
||||
_PM_pinOutput(core->addr[line].pin);
|
||||
if(core->prevRow & bit) {
|
||||
if (core->prevRow & bit) {
|
||||
_PM_pinHigh(core->addr[line].pin);
|
||||
} else {
|
||||
_PM_pinLow(core->addr[line].pin);
|
||||
}
|
||||
#if defined(_PM_portToggleRegister)
|
||||
// If address pin on different port than addr 0, no singleAddrPort.
|
||||
if(_PM_portToggleRegister(core->addr[line].pin) !=
|
||||
core->addrPortToggle) {
|
||||
if (_PM_portToggleRegister(core->addr[line].pin) != core->addrPortToggle) {
|
||||
core->singleAddrPort = 0;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -335,33 +371,34 @@ ProtomatterStatus _PM_begin(Protomatter_core *core) {
|
|||
// setting OE pin HIGH and writing all-zero data to matrix shift registers,
|
||||
// so it won't halt with lit LEDs.
|
||||
void _PM_stop(Protomatter_core *core) {
|
||||
if((core)) {
|
||||
while(core->swapBuffers); // Wait for any pending buffer swap
|
||||
if ((core)) {
|
||||
while (core->swapBuffers)
|
||||
; // Wait for any pending buffer swap
|
||||
_PM_timerStop(core->timer); // Halt timer
|
||||
*core->oe.setReg = core->oe.bit; // Set OE HIGH (disable output)
|
||||
_PM_setReg(core->oe); // Set OE HIGH (disable output)
|
||||
// So, in PRINCIPLE, setting OE high would be sufficient...
|
||||
// but in case that pin is shared with another function such
|
||||
// as the onloard LED (which pulses during bootloading) let's
|
||||
// also clear out the matrix shift registers for good measure.
|
||||
// Set all RGB pins LOW...
|
||||
for(uint8_t i=0; i<core->parallel * 6; i++) {
|
||||
for (uint8_t i = 0; i < core->parallel * 6; i++) {
|
||||
_PM_pinLow(core->rgbPins[i]);
|
||||
}
|
||||
// Clock out bits (just need to toggle clock with RGBs held low)
|
||||
for(uint32_t i=0; i<core->width; i++) {
|
||||
for (uint32_t i = 0; i < core->width; i++) {
|
||||
_PM_pinHigh(core->clockPin);
|
||||
_PM_clockHoldHigh;
|
||||
_PM_pinLow(core->clockPin);
|
||||
_PM_clockHoldLow;
|
||||
}
|
||||
// Latch data
|
||||
*core->latch.setReg = core->latch.bit;
|
||||
*core->latch.clearReg = core->latch.bit;
|
||||
_PM_setReg(core->latch);
|
||||
_PM_clearReg(core->latch);
|
||||
}
|
||||
}
|
||||
|
||||
void _PM_resume(Protomatter_core *core) {
|
||||
if((core)) {
|
||||
if ((core)) {
|
||||
// Init plane & row to max values so they roll over on 1st interrupt
|
||||
core->plane = core->numPlanes - 1;
|
||||
core->row = core->numRowPairs - 1;
|
||||
|
|
@ -376,73 +413,74 @@ void _PM_resume(Protomatter_core *core) {
|
|||
|
||||
// Free memory associated with core structure. Does NOT dealloc struct.
|
||||
void _PM_free(Protomatter_core *core) {
|
||||
if((core)) {
|
||||
if ((core)) {
|
||||
_PM_stop(core);
|
||||
// TO DO: Set all pins back to inputs here?
|
||||
if(core->screenData) free(core->screenData);
|
||||
if(core->addr) free(core->addr);
|
||||
if(core->rgbPins) {
|
||||
free(core->rgbPins);
|
||||
if (core->screenData)
|
||||
_PM_FREE(core->screenData);
|
||||
if (core->addr)
|
||||
_PM_FREE(core->addr);
|
||||
if (core->rgbPins) {
|
||||
_PM_FREE(core->rgbPins);
|
||||
core->rgbPins = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ISR function (in arch.h) calls this function which it extern'd.
|
||||
void _PM_row_handler(Protomatter_core *core) {
|
||||
|
||||
*core->oe.setReg = core->oe.bit; // Disable LED output
|
||||
_PM_setReg(core->oe); // Disable LED output
|
||||
|
||||
*core->latch.setReg = core->latch.bit; // Latch data from PRIOR pass
|
||||
_PM_setReg(core->latch);
|
||||
// Stop timer, save count value at stop
|
||||
uint32_t elapsed = _PM_timerStop(core->timer);
|
||||
uint8_t prevPlane = core->plane; // Save that plane # for later timing
|
||||
*core->latch.clearReg = core->latch.bit; // (split to add a few cycles)
|
||||
_PM_clearReg(core->latch); // (split to add a few cycles)
|
||||
|
||||
// If plane 0 just finished being displayed (plane 1 was loaded on prior
|
||||
// pass, or there's only one plane...I know, it's confusing), take note
|
||||
// of the elapsed timer value, for subsequent bitplane timing (each
|
||||
// plane period is double the previous). Value is filtered slightly to
|
||||
// avoid jitter.
|
||||
if((prevPlane == 1) || (core->numPlanes == 1)) {
|
||||
if ((prevPlane == 1) || (core->numPlanes == 1)) {
|
||||
core->bitZeroPeriod = ((core->bitZeroPeriod * 7) + elapsed) / 8;
|
||||
if(core->bitZeroPeriod < core->minPeriod) {
|
||||
if (core->bitZeroPeriod < core->minPeriod) {
|
||||
core->bitZeroPeriod = core->minPeriod;
|
||||
}
|
||||
}
|
||||
|
||||
if(prevPlane == 0) { // Plane 0 just finished loading
|
||||
if (prevPlane == 0) { // Plane 0 just finished loading
|
||||
#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(core->singleAddrPort) {
|
||||
if (core->singleAddrPort) {
|
||||
// Make bitmasks of prior and new row bits
|
||||
uint32_t priorBits = 0, newBits = 0;
|
||||
for(uint8_t line=0,bit=1; line<core->numAddressLines;
|
||||
line++, bit<<=1) {
|
||||
if(core->row & bit) {
|
||||
for (uint8_t line = 0, bit = 1; line < core->numAddressLines;
|
||||
line++, bit <<= 1) {
|
||||
if (core->row & bit) {
|
||||
newBits |= core->addr[line].bit;
|
||||
}
|
||||
if(core->prevRow & bit) {
|
||||
if (core->prevRow & bit) {
|
||||
priorBits |= core->addr[line].bit;
|
||||
}
|
||||
}
|
||||
*core->addrPortToggle = newBits ^ priorBits;
|
||||
*(volatile _PM_PORT_TYPE *)core->addrPortToggle = newBits ^ priorBits;
|
||||
_PM_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<core->numAddressLines;
|
||||
line++, bit<<=1) {
|
||||
if((core->row & bit) != (core->prevRow & bit)) {
|
||||
if(core->row & bit) { // Set addr line high
|
||||
*core->addr[line].setReg = core->addr[line].bit;
|
||||
for (uint8_t line = 0, bit = 1; line < core->numAddressLines;
|
||||
line++, bit <<= 1) {
|
||||
if ((core->row & bit) != (core->prevRow & bit)) {
|
||||
if (core->row & bit) { // Set addr line high
|
||||
_PM_setReg(core->addr[line]);
|
||||
} else { // Set addr line low
|
||||
*core->addr[line].clearReg = core->addr[line].bit;
|
||||
_PM_clearReg(core->addr[line]);
|
||||
}
|
||||
_PM_delayMicroseconds(_PM_ROW_DELAY);
|
||||
}
|
||||
|
|
@ -454,12 +492,12 @@ void _PM_row_handler(Protomatter_core *core) {
|
|||
}
|
||||
|
||||
// Advance bitplane index and/or row as necessary
|
||||
if(++core->plane >= core->numPlanes) { // Next data bitplane, or
|
||||
if (++core->plane >= core->numPlanes) { // Next data bitplane, or
|
||||
core->plane = 0; // roll over bitplane to start
|
||||
if(++core->row >= core->numRowPairs) { // Next row, or
|
||||
if (++core->row >= core->numRowPairs) { // Next row, or
|
||||
core->row = 0; // roll over row to start
|
||||
// Switch matrix buffers if due (only if double-buffered)
|
||||
if(core->swapBuffers) {
|
||||
if (core->swapBuffers) {
|
||||
core->activeBuffer = 1 - core->activeBuffer;
|
||||
core->swapBuffers = 0; // Swapped!
|
||||
}
|
||||
|
|
@ -473,19 +511,20 @@ void _PM_row_handler(Protomatter_core *core) {
|
|||
|
||||
// Set timer and enable LED output for data loaded on PRIOR pass:
|
||||
_PM_timerStart(core->timer, core->bitZeroPeriod << prevPlane);
|
||||
*core->oe.clearReg = core->oe.bit; // Enable LED output
|
||||
_PM_clearReg(core->oe); // Enable LED output
|
||||
|
||||
uint32_t elementsPerLine = _PM_chunkSize *
|
||||
((core->width + (_PM_chunkSize - 1)) / _PM_chunkSize);
|
||||
uint32_t elementsPerLine =
|
||||
_PM_chunkSize * ((core->width + (_PM_chunkSize - 1)) / _PM_chunkSize);
|
||||
uint32_t srcOffset = elementsPerLine *
|
||||
(core->numPlanes * core->row + core->plane) * core->bytesPerElement;
|
||||
if(core->doubleBuffer) {
|
||||
(core->numPlanes * core->row + core->plane) *
|
||||
core->bytesPerElement;
|
||||
if (core->doubleBuffer) {
|
||||
srcOffset += core->bufferSize * core->activeBuffer;
|
||||
}
|
||||
|
||||
if(core->bytesPerElement == 1) {
|
||||
if (core->bytesPerElement == 1) {
|
||||
blast_byte(core, (uint8_t *)(core->screenData + srcOffset));
|
||||
} else if(core->bytesPerElement == 2) {
|
||||
} else if (core->bytesPerElement == 2) {
|
||||
blast_word(core, (uint16_t *)(core->screenData + srcOffset));
|
||||
} else {
|
||||
blast_long(core, (uint32_t *)(core->screenData + srcOffset));
|
||||
|
|
@ -505,39 +544,40 @@ void _PM_row_handler(Protomatter_core *core) {
|
|||
// before setting the clock back low. If undefined, nothing goes there.
|
||||
|
||||
#if defined(_PM_portToggleRegister)
|
||||
#define PEW \
|
||||
#define PEW \
|
||||
*toggle = *data++; /* Toggle in new data + toggle clock low */ \
|
||||
_PM_clockHoldLow; \
|
||||
*toggle = clock; /* Toggle clock high */ \
|
||||
_PM_clockHoldHigh;
|
||||
#else
|
||||
#define PEW \
|
||||
#define PEW \
|
||||
*set = *data++; /* Set RGB data high */ \
|
||||
_PM_clockHoldLow; \
|
||||
*set32 = clock; /* Set clock high */ \
|
||||
*set_full = clock; /* Set clock high */ \
|
||||
_PM_clockHoldHigh; \
|
||||
*clear32 = rgbclock; /* Clear RGB data + clock */
|
||||
*clear_full = rgbclock; \
|
||||
/* Clear RGB data + clock */ ///< Bitbang one set of RGB data bits to matrix
|
||||
#endif
|
||||
|
||||
#if _PM_chunkSize == 1
|
||||
#define PEW_UNROLL PEW
|
||||
#define PEW_UNROLL PEW
|
||||
#elif _PM_chunkSize == 8
|
||||
#define PEW_UNROLL PEW PEW PEW PEW PEW PEW PEW PEW
|
||||
#define PEW_UNROLL PEW PEW PEW PEW PEW PEW PEW PEW ///< 8-way PEW unroll
|
||||
#elif _PM_chunkSize == 16
|
||||
#define PEW_UNROLL \
|
||||
#define PEW_UNROLL \
|
||||
PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW
|
||||
#elif _PM_chunkSize == 32
|
||||
#define PEW_UNROLL \
|
||||
PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW \
|
||||
PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW
|
||||
#define PEW_UNROLL \
|
||||
PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW \
|
||||
PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW
|
||||
#elif _PM_chunkSize == 64
|
||||
#define PEW_UNROLL \
|
||||
PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW \
|
||||
PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW \
|
||||
PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW \
|
||||
PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW
|
||||
#define PEW_UNROLL \
|
||||
PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW \
|
||||
PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW \
|
||||
PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW \
|
||||
PEW PEW PEW PEW PEW PEW PEW PEW PEW PEW
|
||||
#else
|
||||
#error "Unimplemented _PM_chunkSize value"
|
||||
#error "Unimplemented _PM_chunkSize value"
|
||||
#endif
|
||||
|
||||
// There are THREE COPIES of the following function -- one each for byte,
|
||||
|
|
@ -552,26 +592,26 @@ static void blast_byte(Protomatter_core *core, uint8_t *data) {
|
|||
// clock are all within the same byte of a PORT register, else we'd be
|
||||
// in the word- or long-blasting functions now. So we just need an
|
||||
// 8-bit pointer to the PORT.
|
||||
volatile uint8_t *toggle = (volatile uint8_t *)core->toggleReg +
|
||||
core->portOffset;
|
||||
volatile uint8_t *toggle =
|
||||
(volatile uint8_t *)core->toggleReg + core->portOffset;
|
||||
#else
|
||||
// No-toggle version is a little different. If here, RGB data is all
|
||||
// in one byte of PORT register, clock can be any bit in 32-bit PORT.
|
||||
volatile uint8_t *set; // For RGB data set
|
||||
volatile uint32_t *set32; // For clock set
|
||||
volatile uint32_t *clear32; // For RGB data + clock clear
|
||||
set = (volatile uint8_t *)core->setReg + portOffset;
|
||||
set32 = (volatile uint32_t *)core->setReg;
|
||||
clear32 = (volatile uint32_t *)core->clearReg;
|
||||
uint32_t rgbclock = core->rgbAndClockMask; // RGB + clock bit
|
||||
volatile _PM_PORT_TYPE *set_full; // For clock set
|
||||
volatile _PM_PORT_TYPE *clear_full; // For RGB data + clock clear
|
||||
set = (volatile uint8_t *)core->setReg + core->portOffset;
|
||||
set_full = (volatile _PM_PORT_TYPE *)core->setReg;
|
||||
clear_full = (volatile _PM_PORT_TYPE *)core->clearReg;
|
||||
_PM_PORT_TYPE rgbclock = core->rgbAndClockMask; // RGB + clock bit
|
||||
#endif
|
||||
uint32_t clock = core->clockMask; // Clock bit
|
||||
_PM_PORT_TYPE clock = core->clockMask; // Clock bit
|
||||
uint8_t chunks = (core->width + (_PM_chunkSize - 1)) / _PM_chunkSize;
|
||||
|
||||
// PORT has already been initialized with RGB data + clock bits
|
||||
// all LOW, so we don't need to initialize that state here.
|
||||
|
||||
while(chunks--) {
|
||||
while (chunks--) {
|
||||
PEW_UNROLL // _PM_chunkSize RGB+clock writes
|
||||
}
|
||||
|
||||
|
|
@ -589,20 +629,20 @@ static void blast_byte(Protomatter_core *core, uint8_t *data) {
|
|||
static void blast_word(Protomatter_core *core, uint16_t *data) {
|
||||
#if defined(_PM_portToggleRegister)
|
||||
// See notes above -- except now 16-bit word in PORT.
|
||||
volatile uint16_t *toggle = (volatile uint16_t *)core->toggleReg +
|
||||
core->portOffset;
|
||||
volatile uint16_t *toggle =
|
||||
(volatile uint16_t *)core->toggleReg + core->portOffset;
|
||||
#else
|
||||
volatile uint16_t *set; // For RGB data set
|
||||
volatile uint32_t *set32; // For clock set
|
||||
volatile uint32_t *clear32; // For RGB data + clock clear
|
||||
volatile _PM_PORT_TYPE *set_full; // For clock set
|
||||
volatile _PM_PORT_TYPE *clear_full; // For RGB data + clock clear
|
||||
set = (volatile uint16_t *)core->setReg + core->portOffset;
|
||||
set32 = (volatile uint32_t *)core->setReg;
|
||||
clear32 = (volatile uint32_t *)core->clearReg;
|
||||
uint32_t rgbclock = core->rgbAndClockMask; // RGB + clock bit
|
||||
set_full = (volatile _PM_PORT_TYPE *)core->setReg;
|
||||
clear_full = (volatile _PM_PORT_TYPE *)core->clearReg;
|
||||
_PM_PORT_TYPE rgbclock = core->rgbAndClockMask; // RGB + clock bit
|
||||
#endif
|
||||
uint32_t clock = core->clockMask; // Clock bit
|
||||
_PM_PORT_TYPE clock = core->clockMask; // Clock bit
|
||||
uint8_t chunks = (core->width + (_PM_chunkSize - 1)) / _PM_chunkSize;
|
||||
while(chunks--) {
|
||||
while (chunks--) {
|
||||
PEW_UNROLL // _PM_chunkSize RGB+clock writes
|
||||
}
|
||||
#if defined(_PM_portToggleRegister)
|
||||
|
|
@ -621,16 +661,16 @@ static void blast_long(Protomatter_core *core, uint32_t *data) {
|
|||
// The optimizer will most likely simplify this; leaving as-is, not
|
||||
// wanting a special case of the PEW macro due to divergence risk.
|
||||
volatile uint32_t *set; // For RGB data set
|
||||
volatile uint32_t *set32; // For clock set
|
||||
volatile uint32_t *clear32; // For RGB data + clock clear
|
||||
volatile _PM_PORT_TYPE *set_full; // For clock set
|
||||
volatile _PM_PORT_TYPE *clear_full; // For RGB data + clock clear
|
||||
set = (volatile uint32_t *)core->setReg;
|
||||
set32 = (volatile uint32_t *)core->setReg;
|
||||
clear32 = (volatile uint32_t *)core->clearReg;
|
||||
uint32_t rgbclock = core->rgbAndClockMask; // RGB + clock bit
|
||||
set_full = (volatile _PM_PORT_TYPE *)core->setReg;
|
||||
clear_full = (volatile _PM_PORT_TYPE *)core->clearReg;
|
||||
_PM_PORT_TYPE rgbclock = core->rgbAndClockMask; // RGB + clock bit
|
||||
#endif
|
||||
uint32_t clock = core->clockMask; // Clock bit
|
||||
_PM_PORT_TYPE clock = core->clockMask; // Clock bit
|
||||
uint8_t chunks = (core->width + (_PM_chunkSize - 1)) / _PM_chunkSize;
|
||||
while(chunks--) {
|
||||
while (chunks--) {
|
||||
PEW_UNROLL // _PM_chunkSize RGB+clock writes
|
||||
}
|
||||
#if defined(_PM_portToggleRegister)
|
||||
|
|
@ -644,7 +684,7 @@ static void blast_long(Protomatter_core *core, uint32_t *data) {
|
|||
// the matrix (since this is difficult to estimate beforehand).
|
||||
uint32_t _PM_getFrameCount(Protomatter_core *core) {
|
||||
uint32_t count = 0;
|
||||
if((core)) {
|
||||
if ((core)) {
|
||||
count = core->frameCount;
|
||||
core->frameCount = 0;
|
||||
}
|
||||
|
|
|
|||
287
core.h
287
core.h
|
|
@ -1,3 +1,19 @@
|
|||
/*!
|
||||
* @file core.h
|
||||
*
|
||||
* Part of Adafruit's Protomatter library for HUB75-style RGB LED matrices.
|
||||
*
|
||||
* Adafruit invests time and resources providing this open source code,
|
||||
* please support Adafruit and open-source hardware by purchasing
|
||||
* products from Adafruit!
|
||||
*
|
||||
* Written by Phil "Paint Your Dragon" Burgess and Jeff Epler for
|
||||
* Adafruit Industries, with contributions from the open source community.
|
||||
*
|
||||
* BSD license, all text here must be included in any redistribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _PROTOMATTER_CORE_H_
|
||||
#define _PROTOMATTER_CORE_H_
|
||||
|
||||
|
|
@ -5,10 +21,10 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// Status type returned by some functions.
|
||||
/** Status type returned by some functions. */
|
||||
typedef enum {
|
||||
PROTOMATTER_OK, // Everything is hunky-dory!
|
||||
PROTOMATTER_ERR_PINS, // Clock and/or data pins on different PORTs
|
||||
|
|
@ -16,92 +32,229 @@ typedef enum {
|
|||
PROTOMATTER_ERR_ARG, // Bad input to function
|
||||
} ProtomatterStatus;
|
||||
|
||||
// Struct for matrix control lines NOT related to RGB data or clock, i.e.
|
||||
// latch, OE and address lines. RGB data and clock ("RGBC") are handled
|
||||
// differently as they have specific requirements (and might use a toggle
|
||||
// register if present). The data conversion functions need bitmasks for
|
||||
// RGB data but do NOT need the set or clear registers, so those items
|
||||
// are also declared as separate things in the core structure that follows.
|
||||
/** Struct for matrix control lines NOT related to RGB data or clock, i.e.
|
||||
latch, OE and address lines. RGB data and clock ("RGBC") are handled
|
||||
differently as they have specific requirements (and might use a toggle
|
||||
register if present). The data conversion functions need bitmasks for
|
||||
RGB data but do NOT need the set or clear registers, so those items are
|
||||
also declared as separate things in the core structure that follows. */
|
||||
typedef struct {
|
||||
volatile uint32_t *setReg; // GPIO bit set register
|
||||
volatile uint32_t *clearReg; // GPIO bit clear register
|
||||
uint32_t bit; // GPIO bitmask
|
||||
uint8_t pin; // Some identifier, e.g. Arduino pin #
|
||||
volatile void *setReg; ///< GPIO bit set register
|
||||
volatile void *clearReg; ///< GPIO bit clear register
|
||||
uint32_t bit; ///< GPIO bitmask
|
||||
uint8_t pin; ///< Some unique ID, e.g. Arduino pin #
|
||||
} _PM_pin;
|
||||
|
||||
// Struct with info about an RGB matrix chain and lots of state and buffer
|
||||
// details for the library. Toggle-related items in this structure MUST be
|
||||
// declared even if the device lacks GPIO bit-toggle registers (i.e. don't
|
||||
// do an ifdef check around these). All hardware-specific details (including
|
||||
// the presence or lack of toggle registers) are isolated to a single
|
||||
// file -- arch.h -- which should ONLY be included by core.c, and ifdef'ing
|
||||
// them would result in differing representations of this structure which
|
||||
// must be shared between the library and calling code. (An alternative is
|
||||
// to put any toggle-specific stuff at the end of the struct with an ifdef
|
||||
// check, but that's just dirty pool and asking for trouble.)
|
||||
/** Struct with info about an RGB matrix chain and lots of state and buffer
|
||||
details for the library. Toggle-related items in this structure MUST be
|
||||
declared even if the device lacks GPIO bit-toggle registers (i.e. don't
|
||||
do an ifdef check around these). All hardware-specific details (including
|
||||
the presence or lack of toggle registers) are isolated to a single
|
||||
file -- arch.h -- which should ONLY be included by core.c, and ifdef'ing
|
||||
them would result in differing representations of this structure which
|
||||
must be shared between the library and calling code. (An alternative is
|
||||
to put any toggle-specific stuff at the end of the struct with an ifdef
|
||||
check, but that's just dirty pool and asking for trouble.) */
|
||||
typedef struct {
|
||||
void *timer; // Arch-specific timer/counter info
|
||||
void *setReg; // RGBC bit set register (cast to use)
|
||||
void *clearReg; // RGBC bit clear register "
|
||||
void *toggleReg; // RGBC bit toggle register "
|
||||
uint8_t *rgbPins; // Array of RGB data pins (mult of 6)
|
||||
void *rgbMask; // PORT bit mask for each RGB pin
|
||||
uint32_t clockMask; // PORT bit mask for RGB clock
|
||||
uint32_t rgbAndClockMask; // PORT bit mask for RGB data + clock
|
||||
volatile uint32_t *addrPortToggle; // See singleAddrPort below
|
||||
void *screenData; // Per-bitplane RGB data for matrix
|
||||
_PM_pin latch; // RGB data latch
|
||||
_PM_pin oe; // !OE (LOW out enable)
|
||||
_PM_pin *addr; // Array of address pins
|
||||
uint32_t bufferSize; // Bytes per matrix buffer
|
||||
uint32_t bitZeroPeriod; // Bitplane 0 timer period
|
||||
uint32_t minPeriod; // Plane 0 timer period for ~250Hz
|
||||
volatile uint32_t frameCount; // For estimating refresh rate
|
||||
uint16_t width; // Matrix chain width in bits
|
||||
uint8_t bytesPerElement; // Using 8, 16 or 32 bits of PORT?
|
||||
uint8_t clockPin; // RGB clock pin identifier
|
||||
uint8_t parallel; // Number of concurrent matrix outs
|
||||
uint8_t numAddressLines; // Number of address line pins
|
||||
uint8_t portOffset; // Active 8- or 16-bit pos. in PORT
|
||||
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
|
||||
void *timer; ///< Arch-specific timer/counter info
|
||||
void *setReg; ///< RGBC bit set register (cast to use)
|
||||
void *clearReg; ///< RGBC bit clear register "
|
||||
void *toggleReg; ///< RGBC bit toggle register "
|
||||
uint8_t *rgbPins; ///< Array of RGB data pins (mult of 6)
|
||||
void *rgbMask; ///< PORT bit mask for each RGB pin
|
||||
uint32_t clockMask; ///< PORT bit mask for RGB clock
|
||||
uint32_t rgbAndClockMask; ///< PORT bit mask for RGB data + clock
|
||||
volatile void *addrPortToggle; ///< See singleAddrPort below
|
||||
void *screenData; ///< Per-bitplane RGB data for matrix
|
||||
_PM_pin latch; ///< RGB data latch
|
||||
_PM_pin oe; ///< !OE (LOW out enable)
|
||||
_PM_pin *addr; ///< Array of address pins
|
||||
uint32_t bufferSize; ///< Bytes per matrix buffer
|
||||
uint32_t bitZeroPeriod; ///< Bitplane 0 timer period
|
||||
uint32_t minPeriod; ///< Plane 0 timer period for ~250Hz
|
||||
volatile uint32_t frameCount; ///< For estimating refresh rate
|
||||
uint16_t width; ///< Matrix chain width in bits
|
||||
uint8_t bytesPerElement; ///< Using 8, 16 or 32 bits of PORT?
|
||||
uint8_t clockPin; ///< RGB clock pin identifier
|
||||
uint8_t parallel; ///< Number of concurrent matrix outs
|
||||
uint8_t numAddressLines; ///< Number of address line pins
|
||||
uint8_t portOffset; ///< Active 8- or 16-bit pos. in PORT
|
||||
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
|
||||
} Protomatter_core;
|
||||
|
||||
// Protomatter core function prototypes. Environment-specific code (like the
|
||||
// Adafruit_Protomatter class for Arduino) calls on these underlying things,
|
||||
// and has to provide a few extras of its own (interrupt handlers and such).
|
||||
// User code shouldn't need to invoke any of them directly.
|
||||
extern ProtomatterStatus _PM_init(Protomatter_core *core,
|
||||
uint16_t bitWidth, uint8_t bitDepth,
|
||||
uint8_t rgbCount, uint8_t *rgbList,
|
||||
uint8_t addrCount, uint8_t *addrList,
|
||||
uint8_t clockPin, uint8_t latchPin, uint8_t oePin,
|
||||
|
||||
/*!
|
||||
@brief Initialize values in Protomatter_core structure.
|
||||
@param core Pointer to Protomatter_core structure.
|
||||
@param bitWidth Total width of RGB matrix chain, in pixels.
|
||||
Usu. some multiple of 32, but maybe exceptions.
|
||||
@param bitDepth Color "depth" in bitplanes, determines range of
|
||||
shades of red, green and blue. e.g. passing 4
|
||||
bits = 16 shades ea. R,G,B = 16x16x16 = 4096
|
||||
colors.
|
||||
@param rgbCount Number of "sets" of RGB data pins, each set
|
||||
containing 6 pins (2 ea. R,G,B). Typically 1,
|
||||
indicating a single matrix (or matrix chain).
|
||||
In theory (but not yet extensively tested),
|
||||
multiple sets of pins can be driven in parallel,
|
||||
up to 5 on some devices (if the hardware design
|
||||
provides all those bits on one PORT).
|
||||
@param rgbList A uint8_t array of pins (values are platform-
|
||||
dependent), 6X the prior rgbCount value,
|
||||
corresponding to the 6 output color bits for a
|
||||
matrix (or chain). Order is upper-half red, green,
|
||||
blue, lower-half red, green blue (repeat for each
|
||||
add'l chain). All the RGB pins (plus the clock pin
|
||||
below on some architectures) MUST be on the same
|
||||
PORT register. It's recommended (but not required)
|
||||
that all RGB pins (and clock depending on arch) be
|
||||
within the same byte of a PORT (but do not need to
|
||||
be sequential or contiguous within that byte) for
|
||||
more efficient RAM utilization. For two concurrent
|
||||
chains, same principle but 16-bit word.
|
||||
@param addrCount Number of row address lines required of matrix.
|
||||
Total pixel height is then 2 x 2^addrCount, e.g.
|
||||
32-pixel-tall matrices have 4 row address lines.
|
||||
@param addrList A uint8_t array of pins (platform-dependent pin
|
||||
numbering), one per row address line.
|
||||
@param clockPin RGB clock pin (platform-dependent pin #).
|
||||
@param latchPin RGB data latch pin (platform-dependent pin #).
|
||||
@param oePin Output enable pin (platform-dependent pin #),
|
||||
active low.
|
||||
@param doubleBuffer If true, two matrix buffers are allocated,
|
||||
so changing display contents doesn't introduce
|
||||
artifacts mid-conversion. Requires ~2X RAM.
|
||||
@param timer Pointer to timer peripheral or timer-related
|
||||
struct (architecture-dependent), or NULL to
|
||||
use a default timer ID (also arch-dependent).
|
||||
@return A ProtomatterStatus status, one of:
|
||||
PROTOMATTER_OK if everything is good.
|
||||
PROTOMATTER_ERR_PINS if data and/or clock pins are split across
|
||||
different PORTs.
|
||||
PROTOMATTER_ERR_MALLOC if insufficient RAM to allocate display
|
||||
memory.
|
||||
PROTOMATTER_ERR_ARG if a bad value (core or timer pointer) was
|
||||
passed in.
|
||||
*/
|
||||
extern ProtomatterStatus _PM_init(Protomatter_core *core, uint16_t bitWidth,
|
||||
uint8_t bitDepth, uint8_t rgbCount,
|
||||
uint8_t *rgbList, uint8_t addrCount,
|
||||
uint8_t *addrList, uint8_t clockPin,
|
||||
uint8_t latchPin, uint8_t oePin,
|
||||
bool doubleBuffer, void *timer);
|
||||
|
||||
/*!
|
||||
@brief Allocate display buffers and populate additional elements of a
|
||||
Protomatter matrix.
|
||||
@param core Pointer to Protomatter_core structure.
|
||||
@return A ProtomatterStatus status, one of:
|
||||
PROTOMATTER_OK if everything is good.
|
||||
PROTOMATTER_ERR_PINS if data and/or clock pins are split across
|
||||
different PORTs.
|
||||
PROTOMATTER_ERR_MALLOC if insufficient RAM to allocate display
|
||||
memory.
|
||||
PROTOMATTER_ERR_ARG if a bad value.
|
||||
*/
|
||||
extern ProtomatterStatus _PM_begin(Protomatter_core *core);
|
||||
|
||||
/*!
|
||||
@brief Disable (but do not deallocate) a Protomatter matrix. Disables
|
||||
matrix by setting OE pin HIGH and writing all-zero data to
|
||||
matrix shift registers, so it won't halt with lit LEDs.
|
||||
@param core Pointer to Protomatter_core structure.
|
||||
*/
|
||||
extern void _PM_stop(Protomatter_core *core);
|
||||
|
||||
/*!
|
||||
@brief Start or restart a matrix. Initialize counters, configure and
|
||||
start timer.
|
||||
@param core Pointer to Protomatter_core structure.
|
||||
*/
|
||||
extern void _PM_resume(Protomatter_core *core);
|
||||
|
||||
/*!
|
||||
@brief Deallocate memory associated with Protomatter_core structure
|
||||
(e.g. screen data, pin lists for data and rows). Does not
|
||||
deallocate the structure itself.
|
||||
@param core Pointer to Protomatter_core structure.
|
||||
*/
|
||||
extern void _PM_free(Protomatter_core *core);
|
||||
|
||||
/*!
|
||||
@brief Matrix "row handler" that's called by the timer interrupt.
|
||||
Handles row address lines and issuing data to matrix.
|
||||
@param core Pointer to Protomatter_core structure.
|
||||
*/
|
||||
extern void _PM_row_handler(Protomatter_core *core);
|
||||
|
||||
/*!
|
||||
@brief Returns current value of frame counter and resets its value to
|
||||
zero. Two calls to this, timed one second apart (or use math with
|
||||
other intervals), can be used to get a rough frames-per-second
|
||||
value for the matrix (since this is difficult to estimate
|
||||
beforehand).
|
||||
@param core Pointer to Protomatter_core structure.
|
||||
@return Frame count since previous call to function, as a uint32_t.
|
||||
*/
|
||||
extern uint32_t _PM_getFrameCount(Protomatter_core *core);
|
||||
|
||||
/*!
|
||||
@brief Start (or restart) a timer/counter peripheral.
|
||||
@param tptr Pointer to timer/counter peripheral OR a struct
|
||||
encapsulating information about a timer/counter
|
||||
periph (architecture-dependent).
|
||||
@param period Timer 'top' / rollover value.
|
||||
*/
|
||||
extern void _PM_timerStart(void *tptr, uint32_t period);
|
||||
|
||||
/*!
|
||||
@brief Stop timer/counter peripheral.
|
||||
@param tptr Pointer to timer/counter peripheral OR a struct
|
||||
encapsulating information about a timer/counter
|
||||
periph (architecture-dependent).
|
||||
@return Counter value when timer was stopped.
|
||||
*/
|
||||
extern uint32_t _PM_timerStop(void *tptr);
|
||||
|
||||
/*!
|
||||
@brief Query a timer/counter peripheral's current count.
|
||||
@param tptr Pointer to timer/counter peripheral OR a struct
|
||||
encapsulating information about a timer/counter
|
||||
periph (architecture-dependent).
|
||||
@return Counter value.
|
||||
*/
|
||||
extern uint32_t _PM_timerGetCount(void *tptr);
|
||||
|
||||
#if defined(ARDUINO)
|
||||
extern void _PM_convert_565_byte(Protomatter_core *core,
|
||||
uint16_t *source, uint16_t width);
|
||||
extern void _PM_convert_565_word(Protomatter_core *core,
|
||||
uint16_t *source, uint16_t width);
|
||||
extern void _PM_convert_565_long(Protomatter_core *core,
|
||||
uint16_t *source, uint16_t width);
|
||||
#endif // ARDUINO
|
||||
/*!
|
||||
@brief Converts image data from GFX16 canvas to the matrices weird
|
||||
internal format.
|
||||
@param core Pointer to Protomatter_core structure.
|
||||
@param source Pointer to source image data (see Adafruit_GFX 16-bit
|
||||
canvas type for format).
|
||||
@param width Width of canvas in pixels, as this may be different than
|
||||
the matrix pixel width due to row padding.
|
||||
*/
|
||||
extern void _PM_convert_565(Protomatter_core *core, uint16_t *source,
|
||||
uint16_t width);
|
||||
|
||||
/*!
|
||||
@brief Pauses until the next vertical blank to avoid 'tearing' animation
|
||||
(if display is double-buffered). If single-buffered, has no effect.
|
||||
@param core Pointer to Protomatter_core structure.
|
||||
*/
|
||||
extern void _PM_swapbuffer_maybe(Protomatter_core *core);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
|
|
|||
|
|
@ -41,6 +41,16 @@ 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
|
||||
|
||||
RGB Matrix FeatherWing:
|
||||
R1 D6 A A5
|
||||
G1 D5 B A4
|
||||
|
|
@ -55,6 +65,7 @@ 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.
|
||||
RGB+clock are on different PORTs on nRF52840.
|
||||
*/
|
||||
|
||||
#if defined(__SAMD51__)
|
||||
|
|
@ -64,12 +75,18 @@ byte of PORT bits.
|
|||
uint8_t clockPin = 13;
|
||||
uint8_t latchPin = 0;
|
||||
uint8_t oePin = 1;
|
||||
#else // SAMD21
|
||||
#elif defined(_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;
|
||||
#elif defined(NRF52_SERIES)
|
||||
uint8_t rgbPins[] = {6, 11, A0, A1, A4, A5};
|
||||
uint8_t addrPins[] = {5, 9, 10, 13};
|
||||
uint8_t clockPin = 12;
|
||||
uint8_t latchPin = A2;
|
||||
uint8_t oePin = A3;
|
||||
#endif
|
||||
|
||||
// Last arg here enables double-buffering
|
||||
|
|
|
|||
|
|
@ -41,6 +41,16 @@ 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
|
||||
|
||||
RGB Matrix FeatherWing:
|
||||
R1 D6 A A5
|
||||
G1 D5 B A4
|
||||
|
|
@ -55,6 +65,7 @@ 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.
|
||||
RGB+clock are on different PORTs on nRF52840.
|
||||
*/
|
||||
|
||||
#if defined(__SAMD51__)
|
||||
|
|
@ -64,12 +75,18 @@ byte of PORT bits.
|
|||
uint8_t clockPin = 13;
|
||||
uint8_t latchPin = 0;
|
||||
uint8_t oePin = 1;
|
||||
#else // SAMD21
|
||||
#elif defined(_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;
|
||||
#elif defined(NRF52_SERIES)
|
||||
uint8_t rgbPins[] = {6, 11, A0, A1, A4, A5};
|
||||
uint8_t addrPins[] = {5, 9, 10, 13};
|
||||
uint8_t clockPin = 12;
|
||||
uint8_t latchPin = A2;
|
||||
uint8_t oePin = A3;
|
||||
#endif
|
||||
|
||||
Adafruit_Protomatter matrix(
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
name=Adafruit Protomatter
|
||||
version=0.0.0
|
||||
version=1.0.0
|
||||
author=Adafruit
|
||||
maintainer=Adafruit <info@adafruit.com>
|
||||
sentence=This is a library for the Adafruit RGB LED matrix.
|
||||
paragraph=RGB LED matrix.
|
||||
category=Display
|
||||
url=https://github.com/adafruit/Adafruit_protomatter
|
||||
architectures=*
|
||||
architectures=samd,nrf52,stm32
|
||||
depends=Adafruit GFX Library
|
||||
|
|
|
|||
Loading…
Reference in a new issue