diff --git a/arch.h b/arch.h index a88487f..0fac7e0 100644 --- a/arch.h +++ b/arch.h @@ -1138,6 +1138,9 @@ uint32_t _PM_timerStop(void *tptr) { return _PM_timerGetCount(tptr); } +#define _PM_clockHoldHigh asm("nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop"); +#define _PM_clockHoldLow asm("nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop"); + #elif defined(CIRCUITPY) // Teensy 4 CircuitPython magic goes here. diff --git a/core.c b/core.c index 738265c..67ba814 100644 --- a/core.c +++ b/core.c @@ -241,7 +241,7 @@ ProtomatterStatus _PM_begin(Protomatter_core *core) { // Figure out clockMask and rgbAndClockMask, clear matrix buffers if (core->bytesPerElement == 1) { core->portOffset = _PM_byteOffset(core->rgbPins[0]); -#if defined(_PM_portToggleRegister) +#if defined(_PM_portToggleRegister) && !defined(_PM_STRICT_32BIT_IO) // Clock and rgbAndClockMask are 8-bit values core->clockMask = _PM_portBitMask(core->clockPin) >> (core->portOffset * 8); core->rgbAndClockMask = @@ -258,7 +258,7 @@ ProtomatterStatus _PM_begin(Protomatter_core *core) { } } else if (core->bytesPerElement == 2) { core->portOffset = _PM_wordOffset(core->rgbPins[0]); -#if defined(_PM_portToggleRegister) +#if defined(_PM_portToggleRegister) && !defined(_PM_STRICT_32BIT_IO) // Clock and rgbAndClockMask are 16-bit values core->clockMask = _PM_portBitMask(core->clockPin) >> (core->portOffset * 16); @@ -563,6 +563,9 @@ IRAM_ATTR void _PM_row_handler(Protomatter_core *core) { // before setting the clock back low. If undefined, nothing goes there. #if !defined(PEW) // arch.h can define a custom PEW if needed (e.g. ESP32) + +#if !defined(_PM_STRICT_32BIT_IO) // Partial access to 32-bit GPIO OK + #if defined(_PM_portToggleRegister) #define PEW \ *toggle = *data++; /* Toggle in new data + toggle clock low */ \ @@ -578,7 +581,28 @@ IRAM_ATTR void _PM_row_handler(Protomatter_core *core) { *clear_full = rgbclock; /* Clear RGB data + clock */ \ ///< Bitbang one set of RGB data bits to matrix #endif -#endif // PEW + +#else // ONLY 32-bit GPIO + +#if defined(_PM_portToggleRegister) +#define PEW \ + *toggle = *data++ << shift; /* Toggle in new data + toggle clock low */ \ + _PM_clockHoldLow; \ + *toggle = clock; /* Toggle clock high */ \ + _PM_clockHoldHigh; +#else +#define PEW \ + *set = *data++ << shift; /* Set RGB data high */ \ + _PM_clockHoldLow; \ + *set = clock; /* Set clock high */ \ + _PM_clockHoldHigh; \ + *clear = rgbclock; /* Clear RGB data + clock */ \ + ///< Bitbang one set of RGB data bits to matrix +#endif + +#endif // end 32-bit GPIO + +#endif // end PEW #if _PM_chunkSize == 1 #define PEW_UNROLL PEW @@ -608,6 +632,8 @@ IRAM_ATTR void _PM_row_handler(Protomatter_core *core) { // three-function maintenance then.) IRAM_ATTR static void blast_byte(Protomatter_core *core, uint8_t *data) { +#if !defined(_PM_STRICT_32BIT_IO) // Partial access to 32-bit GPIO OK + #if defined(_PM_portToggleRegister) // If here, it was established in begin() that the RGB data bits and // clock are all within the same byte of a PORT register, else we'd be @@ -645,9 +671,37 @@ IRAM_ATTR static void blast_byte(Protomatter_core *core, uint8_t *data) { *((volatile uint8_t *)core->clearReg + core->portOffset) = core->rgbAndClockMask; #endif + +#else // ONLY 32-bit GPIO + +#if defined(_PM_portToggleRegister) + volatile _PM_PORT_TYPE *toggle = (volatile _PM_PORT_TYPE *)core->toggleReg; +#else + volatile _PM_PORT_TYPE *set = (volatile _PM_PORT_TYPE *)core->setReg; + volatile _PM_PORT_TYPE *clear = (volatile _PM_PORT_TYPE *)core->clearReg; + _PM_PORT_TYPE rgbclock = core->rgbAndClockMask; // RGB + clock bit +#endif + _PM_PORT_TYPE clock = core->clockMask; // Clock bit + uint8_t shift = core->portOffset * 8; + 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--) { + PEW_UNROLL // _PM_chunkSize RGB+clock writes + } + +#if defined(_PM_portToggleRegister) + *((volatile uint32_t *)core->clearReg) = core->rgbAndClockMask; +#endif + +#endif // 32-bit GPIO } IRAM_ATTR static void blast_word(Protomatter_core *core, uint16_t *data) { +#if !defined(_PM_STRICT_32BIT_IO) // Partial access to 32-bit GPIO OK + #if defined(_PM_portToggleRegister) // See notes above -- except now 16-bit word in PORT. volatile uint16_t *toggle = @@ -671,6 +725,27 @@ IRAM_ATTR static void blast_word(Protomatter_core *core, uint16_t *data) { *((volatile uint16_t *)core->clearReg + core->portOffset) = core->rgbAndClockMask; #endif + +#else // ONLY 32-bit GPIO + +#if defined(_PM_portToggleRegister) + volatile _PM_PORT_TYPE *toggle = (volatile _PM_PORT_TYPE *)core->toggleReg; +#else + volatile _PM_PORT_TYPE *set = (volatile _PM_PORT_TYPE *)core->setReg; + volatile _PM_PORT_TYPE *clear = (volatile _PM_PORT_TYPE *)core->clearReg; + _PM_PORT_TYPE rgbclock = core->rgbAndClockMask; // RGB + clock bit +#endif + _PM_PORT_TYPE clock = core->clockMask; // Clock bit + uint8_t shift = core->portOffset * 16; + uint8_t chunks = (core->width + (_PM_chunkSize - 1)) / _PM_chunkSize; + while (chunks--) { + PEW_UNROLL // _PM_chunkSize RGB+clock writes + } +#if defined(_PM_portToggleRegister) + *((volatile _PM_PORT_TYPE *)core->clearReg) = core->rgbAndClockMask; +#endif + +#endif // 32-bit GPIO } IRAM_ATTR static void blast_long(Protomatter_core *core, uint32_t *data) { @@ -690,6 +765,9 @@ IRAM_ATTR static void blast_long(Protomatter_core *core, uint32_t *data) { _PM_PORT_TYPE rgbclock = core->rgbAndClockMask; // RGB + clock bit #endif _PM_PORT_TYPE clock = core->clockMask; // Clock bit +#if defined(_PM_STRICT_32BIT_IO) + uint8_t shift = 0; +#endif uint8_t chunks = (core->width + (_PM_chunkSize - 1)) / _PM_chunkSize; while (chunks--) { PEW_UNROLL // _PM_chunkSize RGB+clock writes diff --git a/examples/protomatter/protomatter.ino b/examples/protomatter/protomatter.ino index b93f4c5..51947bc 100644 --- a/examples/protomatter/protomatter.ino +++ b/examples/protomatter/protomatter.ino @@ -108,7 +108,7 @@ RGB+clock are on different PORTs on nRF52840. uint8_t latchPin = 32; uint8_t oePin = 33; #elif defined(ARDUINO_TEENSY40) - uint8_t rgbPins[] = {15, 16, 17, 20, 21, 22}; // A1-2, A6-8 (skip SDA, SCL) + uint8_t rgbPins[] = {15, 16, 17, 20, 21, 22}; // A1-A3, A6-A8, skips SDA,SCL uint8_t addrPins[] = {2, 3, 4, 5}; uint8_t clockPin = 23; // A9 uint8_t latchPin = 6;