Compare commits
No commits in common. "master" and "updates-for-arduino-churn" have entirely different histories.
master
...
updates-fo
13 changed files with 229 additions and 117 deletions
|
|
@ -38,13 +38,6 @@ 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};
|
||||||
|
|
@ -66,11 +59,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, 5, 9, 11, 10, 12};
|
uint8_t rgbPins[] = {6, 7, 10, 11, 12, 13};
|
||||||
uint8_t addrPins[] = {A5, A4, A3, A2};
|
uint8_t addrPins[] = {0, 1, 2, 3};
|
||||||
uint8_t clockPin = 13;
|
uint8_t clockPin = SDA;
|
||||||
uint8_t latchPin = 0;
|
uint8_t latchPin = 4;
|
||||||
uint8_t oePin = 1;
|
uint8_t oePin = 5;
|
||||||
#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};
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
name=Adafruit Protomatter
|
name=Adafruit Protomatter
|
||||||
version=1.7.0
|
version=1.6.2
|
||||||
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.
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -129,10 +129,12 @@ 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);
|
||||||
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[pin], PIN_FUNC_GPIO);
|
gpio_hal_iomux_func_sel(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
|
||||||
|
|
@ -160,23 +162,11 @@ 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.
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _PM_timerInit(Protomatter_core *core) {
|
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:
|
||||||
|
|
@ -252,9 +242,157 @@ static 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
|
||||||
#if defined(CIRCUITPY)
|
gdma_channel_alloc_config_t dma_chan_config = {
|
||||||
if (dma_chan == NULL) {
|
.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
|
#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) {
|
||||||
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,
|
||||||
|
|
@ -269,9 +407,7 @@ 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
|
||||||
|
|
@ -282,4 +418,6 @@ 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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#if defined(ARDUINO_ARCH_RP2040) || defined(PICO_BOARD) || \
|
#if defined(ARDUINO_ARCH_RP2040) || defined(PICO_BOARD) || defined(__RP2040__)
|
||||||
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"
|
||||||
|
|
@ -106,13 +105,8 @@ 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__
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
39
src/core.c
39
src/core.c
|
|
@ -95,9 +95,8 @@ 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,
|
||||||
|
|
@ -1200,24 +1199,18 @@ 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
|
// Main difference here vs byte converter is each chain
|
||||||
// ORs new bits into place (vs single-pass overwrite).
|
// ORs new bits into place (vs single-pass overwrite).
|
||||||
// #if defined(_PM_portToggleRegister)
|
// #if defined(_PM_portToggleRegister)
|
||||||
|
|
@ -1330,24 +1323,18 @@ 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
|
// Main difference here vs byte converter is each chain
|
||||||
// ORs new bits into place (vs single-pass overwrite).
|
// ORs new bits into place (vs single-pass overwrite).
|
||||||
// #if defined(_PM_portToggleRegister)
|
// #if defined(_PM_portToggleRegister)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue