Compare commits

...

15 commits

Author SHA1 Message Date
Dan Halbert
f83bac7e42
Merge pull request #86 from adafruit/idf5.5
Update for ESP-IDF 5.5 compatibility
2025-08-25 19:35:14 -04:00
Scott Shawcroft
9c45cfb0e4
Remove extra stuff from refactor 2025-08-25 12:07:12 -07:00
Dan Halbert
3614319ecd
Merge pull request #77 from chockenberry/master
Updated simple sketch to use correct values for pin mappings.
2025-08-20 13:54:44 -04:00
Scott Shawcroft
dbd84325c7
More formatting 2025-08-15 10:23:46 -07:00
Scott Shawcroft
c8ad602b45
clang format 2025-08-15 10:08:12 -07:00
Scott Shawcroft
1037d3f080
Tweak IOMUX setting 2025-08-15 09:59:10 -07:00
Scott Shawcroft
d55e3bf833
Mark version 1.7.0 2024-09-23 11:02:26 -07:00
Dan Halbert
0bd9873153
Merge pull request #82 from adafruit/STATIC-fix
STATIC -> static in stm32.h
2024-09-09 19:52:35 -04:00
Dan Halbert
2156ca0407 STATIC -> static 2024-09-09 19:44:17 -04:00
Scott Shawcroft
fb9903f97d
Merge pull request #80 from tannewt/rp2350
Enable RP2350 with same code as RP2040
2024-08-12 13:45:30 -07:00
Scott Shawcroft
282920f9ce
clang format 2024-08-12 13:26:21 -07:00
Scott Shawcroft
267d78d6cf
Enable RP2350 with same code as RP2040 2024-08-09 15:34:40 -07:00
Craig Hockenberry
386371d8fd Updated simple sketch to use correct values for pin mappings.
In reference to: https://forums.adafruit.com/viewtopic.php?p=1013458#p1013458
2024-04-28 13:15:14 -07:00
ladyada
ca4f8764be add c6 2024-04-24 11:31:51 -04:00
cffac25915
Merge pull request #75 from adafruit/updates-for-arduino-churn
Update for arduino esp32 3.0.0-rc1
2024-04-16 08:32:13 -05:00
13 changed files with 117 additions and 229 deletions

View file

@ -38,6 +38,13 @@ supported boards. Notes have been moved to the bottom of the code.
uint8_t clockPin = 13; uint8_t clockPin = 13;
uint8_t latchPin = 0; uint8_t latchPin = 0;
uint8_t oePin = 1; uint8_t oePin = 1;
#elif defined(ARDUINO_ADAFRUIT_FEATHER_ESP32C6) // Feather ESP32-C6
// not featherwing compatible, but can 'hand wire' if desired
uint8_t rgbPins[] = {6, A3, A1, A0, A2, 0};
uint8_t addrPins[] = {8, 5, 15, 7};
uint8_t clockPin = 14;
uint8_t latchPin = RX;
uint8_t oePin = TX;
#elif defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2) // Feather ESP32-S2 #elif defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2) // Feather ESP32-S2
// M0/M4/RP2040 Matrix FeatherWing compatible: // M0/M4/RP2040 Matrix FeatherWing compatible:
uint8_t rgbPins[] = {6, 5, 9, 11, 10, 12}; uint8_t rgbPins[] = {6, 5, 9, 11, 10, 12};
@ -59,11 +66,11 @@ supported boards. Notes have been moved to the bottom of the code.
uint8_t latchPin = 0; uint8_t latchPin = 0;
uint8_t oePin = 1; uint8_t oePin = 1;
#elif defined(_SAMD21_) // Feather M0 variants #elif defined(_SAMD21_) // Feather M0 variants
uint8_t rgbPins[] = {6, 7, 10, 11, 12, 13}; uint8_t rgbPins[] = {6, 5, 9, 11, 10, 12};
uint8_t addrPins[] = {0, 1, 2, 3}; uint8_t addrPins[] = {A5, A4, A3, A2};
uint8_t clockPin = SDA; uint8_t clockPin = 13;
uint8_t latchPin = 4; uint8_t latchPin = 0;
uint8_t oePin = 5; uint8_t oePin = 1;
#elif defined(NRF52_SERIES) // Special nRF52840 FeatherWing pinout #elif defined(NRF52_SERIES) // Special nRF52840 FeatherWing pinout
uint8_t rgbPins[] = {6, A5, A1, A0, A4, 11}; uint8_t rgbPins[] = {6, A5, A1, A0, A4, 11};
uint8_t addrPins[] = {10, 5, 13, 9}; uint8_t addrPins[] = {10, 5, 13, 9};

View file

@ -1,5 +1,5 @@
name=Adafruit Protomatter name=Adafruit Protomatter
version=1.6.2 version=1.7.0
author=Adafruit author=Adafruit
maintainer=Adafruit <info@adafruit.com> maintainer=Adafruit <info@adafruit.com>
sentence=A library for Adafruit RGB LED matrices. sentence=A library for Adafruit RGB LED matrices.

View file

@ -29,7 +29,7 @@
#define _PM_portSetRegister(pin) (volatile uint32_t *)&GPIO.out_w1ts #define _PM_portSetRegister(pin) (volatile uint32_t *)&GPIO.out_w1ts
#define _PM_portClearRegister(pin) (volatile uint32_t *)&GPIO.out_w1tc #define _PM_portClearRegister(pin) (volatile uint32_t *)&GPIO.out_w1tc
#define _PM_portBitMask(pin) (1U << ((pin)&31)) #define _PM_portBitMask(pin) (1U << ((pin) & 31))
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define _PM_byteOffset(pin) ((pin & 31) / 8) #define _PM_byteOffset(pin) ((pin & 31) / 8)

View file

@ -29,7 +29,7 @@
#define _PM_portSetRegister(pin) (volatile uint32_t *)&GPIO.out_w1ts #define _PM_portSetRegister(pin) (volatile uint32_t *)&GPIO.out_w1ts
#define _PM_portClearRegister(pin) (volatile uint32_t *)&GPIO.out_w1tc #define _PM_portClearRegister(pin) (volatile uint32_t *)&GPIO.out_w1tc
#define _PM_portBitMask(pin) (1U << ((pin)&31)) #define _PM_portBitMask(pin) (1U << ((pin) & 31))
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define _PM_byteOffset(pin) ((pin & 31) / 8) #define _PM_byteOffset(pin) ((pin & 31) / 8)

View file

@ -129,12 +129,10 @@ IRAM_ATTR static void blast_long(Protomatter_core *core, uint32_t *data) {}
static void pinmux(int8_t pin, uint8_t signal) { static void pinmux(int8_t pin, uint8_t signal) {
esp_rom_gpio_connect_out_signal(pin, signal, false, false); esp_rom_gpio_connect_out_signal(pin, signal, false, false);
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[pin], PIN_FUNC_GPIO); PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[pin], PIN_FUNC_GPIO);
gpio_set_drive_capability((gpio_num_t)pin, GPIO_DRIVE_STRENGTH); gpio_set_drive_capability((gpio_num_t)pin, GPIO_DRIVE_STRENGTH);
} }
#if defined(ARDUINO) // COMPILING FOR ARDUINO ------------------------------
// LCD_CAM requires a complete replacement of the "blast" functions in order // LCD_CAM requires a complete replacement of the "blast" functions in order
// to use the DMA-based peripheral. // to use the DMA-based peripheral.
#define _PM_CUSTOM_BLAST // Disable blast_*() functions in core.c #define _PM_CUSTOM_BLAST // Disable blast_*() functions in core.c
@ -162,11 +160,23 @@ IRAM_ATTR static void blast_byte(Protomatter_core *core, uint8_t *data) {
// Timer was cleared to 0 before calling blast_byte(), so this // Timer was cleared to 0 before calling blast_byte(), so this
// is the state of the timer immediately after DMA started: // is the state of the timer immediately after DMA started:
#if defined(ARDUINO)
dmaSetupTime = (uint32_t)timerRead((hw_timer_t *)core->timer); dmaSetupTime = (uint32_t)timerRead((hw_timer_t *)core->timer);
#elif defined(CIRCUITPY)
uint64_t value;
#if (ESP_IDF_VERSION_MAJOR == 5)
gptimer_handle_t timer = (gptimer_handle_t)core->timer;
gptimer_get_raw_count(timer, &value);
#else
timer_index_t *timer = (timer_index_t *)core->timer;
timer_get_counter_value(timer->group, timer->idx, &value);
#endif
dmaSetupTime = (uint32_t)value;
#endif
// See notes near top of this file for what's done with this info. // See notes near top of this file for what's done with this info.
} }
void _PM_timerInit(Protomatter_core *core) { static void _PM_timerInit(Protomatter_core *core) {
// On S3, initialize the LCD_CAM peripheral and DMA. // On S3, initialize the LCD_CAM peripheral and DMA.
// LCD_CAM isn't enabled by default -- MUST begin with this: // LCD_CAM isn't enabled by default -- MUST begin with this:
@ -242,157 +252,9 @@ void _PM_timerInit(Protomatter_core *core) {
desc.next = NULL; desc.next = NULL;
// Alloc DMA channel & connect it to LCD periph // Alloc DMA channel & connect it to LCD periph
gdma_channel_alloc_config_t dma_chan_config = { #if defined(CIRCUITPY)
.sibling_chan = NULL,
.direction = GDMA_CHANNEL_DIRECTION_TX,
.flags = {.reserve_sibling = 0}};
esp_err_t ret = gdma_new_channel(&dma_chan_config, &dma_chan);
(void)ret;
gdma_connect(dma_chan, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_LCD, 0));
gdma_strategy_config_t strategy_config = {.owner_check = false,
.auto_update_desc = false};
gdma_apply_strategy(dma_chan, &strategy_config);
gdma_transfer_ability_t ability = {
.sram_trans_align = 0,
.psram_trans_align = 0,
};
gdma_set_transfer_ability(dma_chan, &ability);
gdma_start(dma_chan, (intptr_t)&desc);
// Enable TRANS_DONE interrupt. Note that we do NOT require nor install
// an interrupt service routine, but DO need to enable the TRANS_DONE
// flag to make the LCD DMA transfer work.
LCD_CAM.lc_dma_int_ena.val |= LCD_LL_EVENT_TRANS_DONE & 0x03;
_PM_esp32commonTimerInit(core); // In esp32-common.h
}
#elif defined(CIRCUITPY) // COMPILING FOR CIRCUITPYTHON --------------------
// LCD_CAM requires a complete replacement of the "blast" functions in order
// to use the DMA-based peripheral.
#define _PM_CUSTOM_BLAST // Disable blast_*() functions in core.c
IRAM_ATTR static void blast_byte(Protomatter_core *core, uint8_t *data) {
// Reset LCD DOUT parameters each time (required).
// IN PRINCIPLE, cyclelen should be chainBits-1 (resulting in chainBits
// cycles). But due to the required dummy phases at start of transfer,
// extend by 1; set to chainBits, issue chainBits+1 cycles.
LCD_CAM.lcd_user.lcd_dout_cyclelen = core->chainBits;
LCD_CAM.lcd_user.lcd_dout = 1;
LCD_CAM.lcd_user.lcd_update = 1;
// Reset LCD TX FIFO each time, else we see old data. When doing this,
// it's REQUIRED in the setup code to enable at least one dummy pulse,
// else the PCLK & data are randomly misaligned by 1-2 clocks!
LCD_CAM.lcd_misc.lcd_afifo_reset = 1;
// Partially re-init descriptor each time (required)
desc.dw0.size = desc.dw0.length = core->chainBits;
desc.buffer = data;
gdma_start(dma_chan, (intptr_t)&desc);
esp_rom_delay_us(1); // Necessary before starting xfer
LCD_CAM.lcd_user.lcd_start = 1; // Begin LCD DMA xfer
// Timer was cleared to 0 before calling blast_byte(), so this
// is the state of the timer immediately after DMA started:
uint64_t value;
#if (ESP_IDF_VERSION_MAJOR == 5)
gptimer_handle_t timer = (gptimer_handle_t)core->timer;
gptimer_get_raw_count(timer, &value);
#else
timer_index_t *timer = (timer_index_t *)core->timer;
timer_get_counter_value(timer->group, timer->idx, &value);
#endif
dmaSetupTime = (uint32_t)value;
// See notes near top of this file for what's done with this info.
}
static void _PM_timerInit(Protomatter_core *core) {
// TO DO: adapt this function for any CircuitPython-specific changes.
// If none are required, this function can be deleted and the version
// above can be moved before the ARDUIO/CIRCUITPY checks. If minimal
// changes, consider a single _PM_timerInit() implementation with
// ARDUINO/CIRCUITPY checks inside. It's all good.
// On S3, initialize the LCD_CAM peripheral and DMA.
// LCD_CAM isn't enabled by default -- MUST begin with this:
periph_module_enable(PERIPH_LCD_CAM_MODULE);
periph_module_reset(PERIPH_LCD_CAM_MODULE);
// Reset LCD bus
LCD_CAM.lcd_user.lcd_reset = 1;
esp_rom_delay_us(100);
// Configure LCD clock
LCD_CAM.lcd_clock.clk_en = 1; // Enable clock
LCD_CAM.lcd_clock.lcd_clk_sel = 3; // PLL160M source
LCD_CAM.lcd_clock.lcd_clkm_div_a = 1; // 1/1 fractional divide,
LCD_CAM.lcd_clock.lcd_clkm_div_b = 1; // plus prescale below yields...
#if LCD_CLK_PRESCALE == 8
LCD_CAM.lcd_clock.lcd_clkm_div_num = 7; // 1:8 prescale (20 MHz CLK)
#elif LCD_CLK_PRESCALE == 9
LCD_CAM.lcd_clock.lcd_clkm_div_num = 8; // 1:9 prescale (17.8 MHz CLK)
#else
LCD_CAM.lcd_clock.lcd_clkm_div_num = 9; // 1:10 prescale (16 MHz CLK)
#endif
LCD_CAM.lcd_clock.lcd_ck_out_edge = 0; // PCLK low in first half of cycle
LCD_CAM.lcd_clock.lcd_ck_idle_edge = 0; // PCLK low idle
LCD_CAM.lcd_clock.lcd_clk_equ_sysclk = 1; // PCLK = CLK (ignore CLKCNT_N)
// Configure frame format. Some of these could probably be skipped and
// use defaults, but being verbose for posterity...
LCD_CAM.lcd_ctrl.lcd_rgb_mode_en = 0; // i8080 mode (not RGB)
LCD_CAM.lcd_rgb_yuv.lcd_conv_bypass = 0; // Disable RGB/YUV converter
LCD_CAM.lcd_misc.lcd_next_frame_en = 0; // Do NOT auto-frame
LCD_CAM.lcd_data_dout_mode.val = 0; // No data delays
LCD_CAM.lcd_user.lcd_always_out_en = 0; // Only when requested
LCD_CAM.lcd_user.lcd_8bits_order = 0; // Do not swap bytes
LCD_CAM.lcd_user.lcd_bit_order = 0; // Do not reverse bit order
LCD_CAM.lcd_user.lcd_2byte_en = 0; // 8-bit data mode
// MUST enable at least one dummy phase at start of output, else clock and
// data are randomly misaligned by 1-2 cycles following required TX FIFO
// reset in blast_byte(). One phase MOSTLY works but sparkles a tiny bit
// (as in still very occasionally misaligned by 1 cycle). Two seems ideal;
// no sparkle. Since HUB75 is just a shift register, the extra clock ticks
// are harmless and the zero-data shifts off end of the chain.
LCD_CAM.lcd_user.lcd_dummy = 1; // Enable dummy phase(s) @ LCD start
LCD_CAM.lcd_user.lcd_dummy_cyclelen = 1; // 2 dummy phases
LCD_CAM.lcd_user.lcd_cmd = 0; // No command at LCD start
LCD_CAM.lcd_user.lcd_cmd_2_cycle_en = 0;
LCD_CAM.lcd_user.lcd_update = 1;
// Configure signal pins. IN THEORY this could be expanded to support
// 2 parallel chains, but the rest of the LCD & DMA setup is not currently
// written for that, so it's limited to a single chain for now.
const uint8_t signal[] = {LCD_DATA_OUT0_IDX, LCD_DATA_OUT1_IDX,
LCD_DATA_OUT2_IDX, LCD_DATA_OUT3_IDX,
LCD_DATA_OUT4_IDX, LCD_DATA_OUT5_IDX};
for (int i = 0; i < 6; i++)
pinmux(core->rgbPins[i], signal[i]);
pinmux(core->clockPin, LCD_PCLK_IDX);
gpio_set_drive_capability(core->latch.pin, GPIO_DRIVE_STRENGTH);
gpio_set_drive_capability(core->oe.pin, GPIO_DRIVE_STRENGTH);
for (uint8_t i = 0; i < core->numAddressLines; i++) {
gpio_set_drive_capability(core->addr[i].pin, GPIO_DRIVE_STRENGTH);
}
// Disable LCD_CAM interrupts, clear any pending interrupt
LCD_CAM.lc_dma_int_ena.val &= ~LCD_LL_EVENT_TRANS_DONE;
LCD_CAM.lc_dma_int_clr.val = 0x03;
// Set up DMA TX descriptor
desc.dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA;
desc.dw0.suc_eof = 1;
desc.dw0.size = desc.dw0.length = core->chainBits;
desc.buffer = core->screenData;
desc.next = NULL;
// Alloc DMA channel & connect it to LCD periph
if (dma_chan == NULL) { if (dma_chan == NULL) {
#endif
gdma_channel_alloc_config_t dma_chan_config = { gdma_channel_alloc_config_t dma_chan_config = {
.sibling_chan = NULL, .sibling_chan = NULL,
.direction = GDMA_CHANNEL_DIRECTION_TX, .direction = GDMA_CHANNEL_DIRECTION_TX,
@ -407,7 +269,9 @@ static void _PM_timerInit(Protomatter_core *core) {
.psram_trans_align = 0, .psram_trans_align = 0,
}; };
gdma_set_transfer_ability(dma_chan, &ability); gdma_set_transfer_ability(dma_chan, &ability);
#if defined(CIRCUITPY)
} }
#endif
gdma_start(dma_chan, (intptr_t)&desc); gdma_start(dma_chan, (intptr_t)&desc);
// Enable TRANS_DONE interrupt. Note that we do NOT require nor install // Enable TRANS_DONE interrupt. Note that we do NOT require nor install
@ -418,6 +282,4 @@ static void _PM_timerInit(Protomatter_core *core) {
_PM_esp32commonTimerInit(core); // In esp32-common.h _PM_esp32commonTimerInit(core); // In esp32-common.h
} }
#endif // END CIRCUITPYTHON ------------------------------------------------
#endif // END ESP32S3 #endif // END ESP32S3

View file

@ -32,7 +32,7 @@
#define _PM_portClearRegister(pin) \ #define _PM_portClearRegister(pin) \
(volatile uint32_t *)((pin < 32) ? &GPIO.out_w1tc : &GPIO.out1_w1tc.val) (volatile uint32_t *)((pin < 32) ? &GPIO.out_w1tc : &GPIO.out1_w1tc.val)
#define _PM_portBitMask(pin) (1U << ((pin)&31)) #define _PM_portBitMask(pin) (1U << ((pin) & 31))
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define _PM_byteOffset(pin) ((pin & 31) / 8) #define _PM_byteOffset(pin) ((pin & 31) / 8)

View file

@ -99,7 +99,7 @@ volatile uint32_t *_PM_portClearRegister(uint32_t pin) {
#define _PM_pinInput(pin) nrf_gpio_cfg_input(pin) #define _PM_pinInput(pin) nrf_gpio_cfg_input(pin)
#define _PM_pinHigh(pin) nrf_gpio_pin_set(pin) #define _PM_pinHigh(pin) nrf_gpio_pin_set(pin)
#define _PM_pinLow(pin) nrf_gpio_pin_clear(pin) #define _PM_pinLow(pin) nrf_gpio_pin_clear(pin)
#define _PM_portBitMask(pin) (1u << ((pin)&31)) #define _PM_portBitMask(pin) (1u << ((pin) & 31))
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define _PM_byteOffset(pin) ((pin & 31) / 8) #define _PM_byteOffset(pin) ((pin & 31) / 8)
@ -142,19 +142,19 @@ void _PM_timerInit(Protomatter_core *core) {
IRQn_Type IRQn; // Interrupt number IRQn_Type IRQn; // Interrupt number
} timer[] = { } timer[] = {
#if defined(NRF_TIMER0) #if defined(NRF_TIMER0)
{NRF_TIMER0, TIMER0_IRQn}, {NRF_TIMER0, TIMER0_IRQn},
#endif #endif
#if defined(NRF_TIMER1) #if defined(NRF_TIMER1)
{NRF_TIMER1, TIMER1_IRQn}, {NRF_TIMER1, TIMER1_IRQn},
#endif #endif
#if defined(NRF_TIMER2) #if defined(NRF_TIMER2)
{NRF_TIMER2, TIMER2_IRQn}, {NRF_TIMER2, TIMER2_IRQn},
#endif #endif
#if defined(NRF_TIMER3) #if defined(NRF_TIMER3)
{NRF_TIMER3, TIMER3_IRQn}, {NRF_TIMER3, TIMER3_IRQn},
#endif #endif
#if defined(NRF_TIMER4) #if defined(NRF_TIMER4)
{NRF_TIMER4, TIMER4_IRQn}, {NRF_TIMER4, TIMER4_IRQn},
#endif #endif
}; };
#define NUM_TIMERS (sizeof timer / sizeof timer[0]) #define NUM_TIMERS (sizeof timer / sizeof timer[0])

View file

@ -26,7 +26,8 @@
#pragma once #pragma once
#if defined(ARDUINO_ARCH_RP2040) || defined(PICO_BOARD) || defined(__RP2040__) #if defined(ARDUINO_ARCH_RP2040) || defined(PICO_BOARD) || \
defined(__RP2040__) || defined(__RP2350__)
#include "../../hardware_pwm/include/hardware/pwm.h" #include "../../hardware_pwm/include/hardware/pwm.h"
#include "hardware/irq.h" #include "hardware/irq.h"
@ -104,9 +105,14 @@ void _PM_timerInit(Protomatter_core *core) {
#elif defined(CIRCUITPY) // COMPILING FOR CIRCUITPYTHON -------------------- #elif defined(CIRCUITPY) // COMPILING FOR CIRCUITPYTHON --------------------
#if !defined(F_CPU) // Not sure if CircuitPython build defines this #if !defined(F_CPU) // Not sure if CircuitPython build defines this
#ifdef __RP2040__
#define F_CPU 125000000 // Standard RP2040 clock speed #define F_CPU 125000000 // Standard RP2040 clock speed
#endif #endif
#ifdef __RP2350__
#define F_CPU 150000000 // Standard RP2350 clock speed
#endif
#endif
// 'pin' here is GPXX # // 'pin' here is GPXX #
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__

View file

@ -64,7 +64,7 @@ void _PM_IRQ_HANDLER(void) {
#define _PM_pinInput(pin) gpio_set_pin_direction(pin, GPIO_DIRECTION_IN) #define _PM_pinInput(pin) gpio_set_pin_direction(pin, GPIO_DIRECTION_IN)
#define _PM_pinHigh(pin) gpio_set_pin_level(pin, 1) #define _PM_pinHigh(pin) gpio_set_pin_level(pin, 1)
#define _PM_pinLow(pin) gpio_set_pin_level(pin, 0) #define _PM_pinLow(pin) gpio_set_pin_level(pin, 0)
#define _PM_portBitMask(pin) (1u << ((pin)&31)) #define _PM_portBitMask(pin) (1u << ((pin) & 31))
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define _PM_byteOffset(pin) ((pin & 31) / 8) #define _PM_byteOffset(pin) ((pin & 31) / 8)

View file

@ -51,19 +51,19 @@ void _PM_timerInit(Protomatter_core *core) {
uint8_t GCM_ID; // GCLK selection ID uint8_t GCM_ID; // GCLK selection ID
} timer[] = { } timer[] = {
#if defined(TC0) #if defined(TC0)
{TC0, TC0_IRQn, GCM_TCC0_TCC1}, {TC0, TC0_IRQn, GCM_TCC0_TCC1},
#endif #endif
#if defined(TC1) #if defined(TC1)
{TC1, TC1_IRQn, GCM_TCC0_TCC1}, {TC1, TC1_IRQn, GCM_TCC0_TCC1},
#endif #endif
#if defined(TC2) #if defined(TC2)
{TC2, TC2_IRQn, GCM_TCC2_TC3}, {TC2, TC2_IRQn, GCM_TCC2_TC3},
#endif #endif
#if defined(TC3) #if defined(TC3)
{TC3, TC3_IRQn, GCM_TCC2_TC3}, {TC3, TC3_IRQn, GCM_TCC2_TC3},
#endif #endif
#if defined(TC4) #if defined(TC4)
{TC4, TC4_IRQn, GCM_TC4_TC5}, {TC4, TC4_IRQn, GCM_TC4_TC5},
#endif #endif
}; };
#define NUM_TIMERS (sizeof timer / sizeof timer[0]) #define NUM_TIMERS (sizeof timer / sizeof timer[0])

View file

@ -78,43 +78,43 @@ void _PM_timerInit(Protomatter_core *core) {
uint8_t GCLK_ID; // Peripheral channel # for clock source uint8_t GCLK_ID; // Peripheral channel # for clock source
} timer[] = { } timer[] = {
#if defined(TC0) #if defined(TC0)
{TC0, TC0_IRQn, TC0_GCLK_ID}, {TC0, TC0_IRQn, TC0_GCLK_ID},
#endif #endif
#if defined(TC1) #if defined(TC1)
{TC1, TC1_IRQn, TC1_GCLK_ID}, {TC1, TC1_IRQn, TC1_GCLK_ID},
#endif #endif
#if defined(TC2) #if defined(TC2)
{TC2, TC2_IRQn, TC2_GCLK_ID}, {TC2, TC2_IRQn, TC2_GCLK_ID},
#endif #endif
#if defined(TC3) #if defined(TC3)
{TC3, TC3_IRQn, TC3_GCLK_ID}, {TC3, TC3_IRQn, TC3_GCLK_ID},
#endif #endif
#if defined(TC4) #if defined(TC4)
{TC4, TC4_IRQn, TC4_GCLK_ID}, {TC4, TC4_IRQn, TC4_GCLK_ID},
#endif #endif
#if defined(TC5) #if defined(TC5)
{TC5, TC5_IRQn, TC5_GCLK_ID}, {TC5, TC5_IRQn, TC5_GCLK_ID},
#endif #endif
#if defined(TC6) #if defined(TC6)
{TC6, TC6_IRQn, TC6_GCLK_ID}, {TC6, TC6_IRQn, TC6_GCLK_ID},
#endif #endif
#if defined(TC7) #if defined(TC7)
{TC7, TC7_IRQn, TC7_GCLK_ID}, {TC7, TC7_IRQn, TC7_GCLK_ID},
#endif #endif
#if defined(TC8) #if defined(TC8)
{TC8, TC8_IRQn, TC8_GCLK_ID}, {TC8, TC8_IRQn, TC8_GCLK_ID},
#endif #endif
#if defined(TC9) #if defined(TC9)
{TC9, TC9_IRQn, TC9_GCLK_ID}, {TC9, TC9_IRQn, TC9_GCLK_ID},
#endif #endif
#if defined(TC10) #if defined(TC10)
{TC10, TC10_IRQn, TC10_GCLK_ID}, {TC10, TC10_IRQn, TC10_GCLK_ID},
#endif #endif
#if defined(TC11) #if defined(TC11)
{TC11, TC11_IRQn, TC11_GCLK_ID}, {TC11, TC11_IRQn, TC11_GCLK_ID},
#endif #endif
#if defined(TC12) #if defined(TC12)
{TC12, TC12_IRQn, TC12_GCLK_ID}, {TC12, TC12_IRQn, TC12_GCLK_ID},
#endif #endif
}; };
#define NUM_TIMERS (sizeof timer / sizeof timer[0]) #define NUM_TIMERS (sizeof timer / sizeof timer[0])

View file

@ -28,7 +28,7 @@
#include "timers.h" #include "timers.h"
#undef _PM_portBitMask #undef _PM_portBitMask
#define _PM_portBitMask(pin) (1u << ((pin)&15)) #define _PM_portBitMask(pin) (1u << ((pin) & 15))
#define _PM_byteOffset(pin) ((pin & 15) / 8) #define _PM_byteOffset(pin) ((pin & 15) / 8)
#define _PM_wordOffset(pin) ((pin & 15) / 16) #define _PM_wordOffset(pin) ((pin & 15) / 16)
@ -83,7 +83,7 @@ volatile uint16_t *_PM_portClearRegister(uint32_t pin) {
// TODO: this is no longer true, should it change? // TODO: this is no longer true, should it change?
void *_PM_protoPtr = NULL; void *_PM_protoPtr = NULL;
STATIC TIM_HandleTypeDef tim_handle; static TIM_HandleTypeDef tim_handle;
// Timer interrupt service routine // Timer interrupt service routine
void _PM_IRQ_HANDLER(void) { void _PM_IRQ_HANDLER(void) {

View file

@ -95,12 +95,13 @@ ProtomatterStatus _PM_init(Protomatter_core *core, uint16_t bitWidth,
uint8_t addrCount, uint8_t *addrList, uint8_t addrCount, uint8_t *addrList,
uint8_t clockPin, uint8_t latchPin, uint8_t oePin, uint8_t clockPin, uint8_t latchPin, uint8_t oePin,
bool doubleBuffer, int8_t tile, void *timer) { bool doubleBuffer, int8_t tile, void *timer) {
if (!core) if (!core) {
return PROTOMATTER_ERR_ARG; return PROTOMATTER_ERR_ARG;
}
// bitDepth is NOT constrained here, handle in calling function // bitDepth is NOT constrained here, handle in calling function
// (varies with implementation, e.g. GFX lib is max 6 bitplanes, // (varies with implementation, e.g. GFX lib is max 6 bitplanes,
// but might be more or less elsewhere) // but might be more or less elsewhere)
#if defined(_PM_bytesPerElement) #if defined(_PM_bytesPerElement)
#if _PM_bytesPerElement == 1 #if _PM_bytesPerElement == 1
if (rgbCount > 1) if (rgbCount > 1)
@ -780,9 +781,9 @@ IRAM_ATTR static void blast_word(Protomatter_core *core, uint16_t *data) {
volatile uint16_t *toggle = volatile uint16_t *toggle =
(volatile uint16_t *)core->toggleReg + core->portOffset; (volatile uint16_t *)core->toggleReg + core->portOffset;
#else #else
volatile uint16_t *set; // For RGB data set volatile uint16_t *set; // For RGB data set
volatile _PM_PORT_TYPE *set_full; // For clock set volatile _PM_PORT_TYPE *set_full; // For clock set
volatile _PM_PORT_TYPE *clear_full; // For RGB data + clock clear volatile _PM_PORT_TYPE *clear_full; // For RGB data + clock clear
set = (volatile uint16_t *)core->setReg + core->portOffset; set = (volatile uint16_t *)core->setReg + core->portOffset;
set_full = (volatile _PM_PORT_TYPE *)core->setReg; set_full = (volatile _PM_PORT_TYPE *)core->setReg;
clear_full = (volatile _PM_PORT_TYPE *)core->clearReg; clear_full = (volatile _PM_PORT_TYPE *)core->clearReg;
@ -829,7 +830,7 @@ IRAM_ATTR static void blast_long(Protomatter_core *core, uint32_t *data) {
// Note in this case two copies exist of the PORT set register. // Note in this case two copies exist of the PORT set register.
// The optimizer will most likely simplify this; leaving as-is, not // The optimizer will most likely simplify this; leaving as-is, not
// wanting a special case of the PEW macro due to divergence risk. // wanting a special case of the PEW macro due to divergence risk.
volatile uint32_t *set; // For RGB data set volatile uint32_t *set; // For RGB data set
#if !defined(_PM_STRICT_32BIT_IO) #if !defined(_PM_STRICT_32BIT_IO)
volatile _PM_PORT_TYPE *set_full; // For clock set volatile _PM_PORT_TYPE *set_full; // For clock set
set_full = (volatile _PM_PORT_TYPE *)core->setReg; set_full = (volatile _PM_PORT_TYPE *)core->setReg;
@ -1074,7 +1075,7 @@ __attribute__((noinline)) void _PM_convert_565_byte(Protomatter_core *core,
*d2++ = result; *d2++ = result;
#endif #endif
} // end x } // end x
} // end tile } // end tile
greenBit <<= 1; greenBit <<= 1;
if (plane || (core->numPlanes < 6)) { if (plane || (core->numPlanes < 6)) {
@ -1100,8 +1101,8 @@ __attribute__((noinline)) void _PM_convert_565_byte(Protomatter_core *core,
dest[-pad] &= ~clockMask; // Negative index is legal & intentional dest[-pad] &= ~clockMask; // Negative index is legal & intentional
#endif #endif
dest += bitplaneSize; // Advance one scanline in dest buffer dest += bitplaneSize; // Advance one scanline in dest buffer
} // end plane } // end plane
} // end row } // end row
} }
// Corresponding function for word output -- either 12 RGB bits (2 parallel // Corresponding function for word output -- either 12 RGB bits (2 parallel
@ -1199,20 +1200,26 @@ void _PM_convert_565_word(Protomatter_core *core, uint16_t *source,
uint16_t upperRGB = upperSrc[srcIdx]; // Pixel in upper half uint16_t upperRGB = upperSrc[srcIdx]; // Pixel in upper half
uint16_t lowerRGB = lowerSrc[srcIdx]; // Pixel in lower half uint16_t lowerRGB = lowerSrc[srcIdx]; // Pixel in lower half
uint16_t result = 0; uint16_t result = 0;
if (upperRGB & redBit) if (upperRGB & redBit) {
result |= pinMask[0]; result |= pinMask[0];
if (upperRGB & greenBit) }
if (upperRGB & greenBit) {
result |= pinMask[1]; result |= pinMask[1];
if (upperRGB & blueBit) }
if (upperRGB & blueBit) {
result |= pinMask[2]; result |= pinMask[2];
if (lowerRGB & redBit) }
if (lowerRGB & redBit) {
result |= pinMask[3]; result |= pinMask[3];
if (lowerRGB & greenBit) }
if (lowerRGB & greenBit) {
result |= pinMask[4]; result |= pinMask[4];
if (lowerRGB & blueBit) }
if (lowerRGB & blueBit) {
result |= pinMask[5]; result |= pinMask[5];
// Main difference here vs byte converter is each chain }
// ORs new bits into place (vs single-pass overwrite). // Main difference here vs byte converter is each chain
// ORs new bits into place (vs single-pass overwrite).
// #if defined(_PM_portToggleRegister) // #if defined(_PM_portToggleRegister)
#if defined(_PM_USE_TOGGLE_FORMAT) #if defined(_PM_USE_TOGGLE_FORMAT)
*d2++ |= result ^ prior; // Bitwise OR *d2++ |= result ^ prior; // Bitwise OR
@ -1221,7 +1228,7 @@ void _PM_convert_565_word(Protomatter_core *core, uint16_t *source,
*d2++ |= result; // Bitwise OR *d2++ |= result; // Bitwise OR
#endif #endif
} // end x } // end x
} // end tile } // end tile
greenBit <<= 1; greenBit <<= 1;
if (plane || (core->numPlanes < 6)) { if (plane || (core->numPlanes < 6)) {
redBit <<= 1; redBit <<= 1;
@ -1231,9 +1238,9 @@ void _PM_convert_565_word(Protomatter_core *core, uint16_t *source,
blueBit = 0b0000000000000001; blueBit = 0b0000000000000001;
} }
dest += bitplaneSize; // Advance one scanline in dest buffer dest += bitplaneSize; // Advance one scanline in dest buffer
} // end plane } // end plane
} // end row } // end row
pinMask += 6; // Next chain's RGB pin masks pinMask += 6; // Next chain's RGB pin masks
} }
} }
@ -1323,20 +1330,26 @@ void _PM_convert_565_long(Protomatter_core *core, uint16_t *source,
uint16_t upperRGB = upperSrc[srcIdx]; // Pixel in upper half uint16_t upperRGB = upperSrc[srcIdx]; // Pixel in upper half
uint16_t lowerRGB = lowerSrc[srcIdx]; // Pixel in lower half uint16_t lowerRGB = lowerSrc[srcIdx]; // Pixel in lower half
uint32_t result = 0; uint32_t result = 0;
if (upperRGB & redBit) if (upperRGB & redBit) {
result |= pinMask[0]; result |= pinMask[0];
if (upperRGB & greenBit) }
if (upperRGB & greenBit) {
result |= pinMask[1]; result |= pinMask[1];
if (upperRGB & blueBit) }
if (upperRGB & blueBit) {
result |= pinMask[2]; result |= pinMask[2];
if (lowerRGB & redBit) }
if (lowerRGB & redBit) {
result |= pinMask[3]; result |= pinMask[3];
if (lowerRGB & greenBit) }
if (lowerRGB & greenBit) {
result |= pinMask[4]; result |= pinMask[4];
if (lowerRGB & blueBit) }
if (lowerRGB & blueBit) {
result |= pinMask[5]; result |= pinMask[5];
// Main difference here vs byte converter is each chain }
// ORs new bits into place (vs single-pass overwrite). // Main difference here vs byte converter is each chain
// ORs new bits into place (vs single-pass overwrite).
// #if defined(_PM_portToggleRegister) // #if defined(_PM_portToggleRegister)
#if defined(_PM_USE_TOGGLE_FORMAT) #if defined(_PM_USE_TOGGLE_FORMAT)
*d2++ |= result ^ prior; // Bitwise OR *d2++ |= result ^ prior; // Bitwise OR
@ -1345,7 +1358,7 @@ void _PM_convert_565_long(Protomatter_core *core, uint16_t *source,
*d2++ |= result; // Bitwise OR *d2++ |= result; // Bitwise OR
#endif #endif
} // end x } // end x
} // end tile } // end tile
greenBit <<= 1; greenBit <<= 1;
if (plane || (core->numPlanes < 6)) { if (plane || (core->numPlanes < 6)) {
redBit <<= 1; redBit <<= 1;
@ -1355,9 +1368,9 @@ void _PM_convert_565_long(Protomatter_core *core, uint16_t *source,
blueBit = 0b0000000000000001; blueBit = 0b0000000000000001;
} }
dest += bitplaneSize; // Advance one scanline in dest buffer dest += bitplaneSize; // Advance one scanline in dest buffer
} // end plane } // end plane
} // end row } // end row
pinMask += 6; // Next chain's RGB pin masks pinMask += 6; // Next chain's RGB pin masks
} }
} }