From 80957ea162c2411af9d64400bc116849d39aaad5 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Fri, 30 Jan 2026 20:31:07 +0000 Subject: [PATCH 01/16] pico: handle GPIOs >= 32 in DBI driver --- 32blit-pico/display/dbi.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/32blit-pico/display/dbi.cpp b/32blit-pico/display/dbi.cpp index ee7ded1a8..62c215cfe 100644 --- a/32blit-pico/display/dbi.cpp +++ b/32blit-pico/display/dbi.cpp @@ -380,6 +380,12 @@ void init_display() { #endif // setup PIO +#if LCD_MOSI_PIN >= 32 || LCD_SCK_PIN >= 32 + // assumes anything else using this PIO can also deal with the base + static_assert(LCD_MOSI_PIN >= 16 && LCD_SCK_PIN >= 16); + pio_set_gpio_base(pio, 16); +#endif + pio_offset = pio_add_program(pio, &dbi_raw_program); pio_double_offset = pio_add_program(pio, &dbi_pixel_double_program); @@ -415,7 +421,7 @@ void init_display() { #ifdef DBI_8BIT // these are really D0/WR - bi_decl_if_func_used(bi_pin_mask_with_name(0xFF << LCD_MOSI_PIN, "Display Data")); + bi_decl_if_func_used(bi_pin_mask_with_name(0xFFull << LCD_MOSI_PIN, "Display Data")); bi_decl_if_func_used(bi_1pin_with_name(LCD_SCK_PIN, "Display WR")); #else bi_decl_if_func_used(bi_1pin_with_name(LCD_MOSI_PIN, "Display TX")); From cddd12f9c5579673086cdd1a4856cda0be1c9877 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Fri, 30 Jan 2026 20:57:31 +0000 Subject: [PATCH 02/16] pico: import tufty2350 board header --- 32blit-pico/board/pimoroni_tufty2350.h | 168 +++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 32blit-pico/board/pimoroni_tufty2350.h diff --git a/32blit-pico/board/pimoroni_tufty2350.h b/32blit-pico/board/pimoroni_tufty2350.h new file mode 100644 index 000000000..dae7c8ec1 --- /dev/null +++ b/32blit-pico/board/pimoroni_tufty2350.h @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2024 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +// ----------------------------------------------------- +// NOTE: THIS HEADER IS ALSO INCLUDED BY ASSEMBLER SO +// SHOULD ONLY CONSIST OF PREPROCESSOR DIRECTIVES +// ----------------------------------------------------- + +// pico_cmake_set PICO_PLATFORM=rp2350 +// pico_cmake_set PICO_CYW43_SUPPORTED = 1 + +#ifndef _BOARDS_PICO2_W_H +#define _BOARDS_PICO2_W_H + + +//#define PICO_PANIC_FUNCTION mp_pico_panic + +// Board config +// RTC = PCF85063e +#define BW_RTC_I2C i2c0 +#define BW_RTC_ADDR (0x51) +#define BW_RTC_I2C_SDA (4) +#define BW_RTC_I2C_SCL (5) + +// Rear white LEDs +#define BW_LED_0 (0) +#define BW_LED_1 (1) +#define BW_LED_2 (2) +#define BW_LED_3 (3) + +// PSRAM +#define BW_PSRAM_CS (8) + +// User inputs +#define BW_SWITCH_A (7) +#define BW_SWITCH_B (9) +#define BW_SWITCH_C (10) +#define BW_SWITCH_UP (11) +#define BW_SWITCH_DOWN (6) + +// This is wired to the RESET (Disk / Sleep / Reset / Power On) +// button and used to determine long press status +#define BW_RESET_SW (14) // No pull, active high? + +// I2C power for talking to RTC +#define BW_SW_POWER_EN (41) + +// Interrupt channels for GPIO wakeup +#define BW_VBUS_DETECT (12) // No pull, active high? +#define BW_RTC_ALARM (13) // Pull up, active low +#define BW_SWITCH_HOME (22) // AKA boot +#define BW_SWITCH_INT (15) // Pull up, active low +#define BW_SWITCH_MASK ((1 << BW_SWITCH_A) | (1 << BW_SWITCH_B) | (1 << BW_SWITCH_C) | (1 << BW_SWITCH_UP) | (1 << BW_SWITCH_DOWN)) + +// 250 MHz - Working, but not tested on enough boards to fully verify. +// ./build/micropython/lib/pico-sdk/src/rp2_common/hardware_clocks/scripts/vcocalc.py --cmake 250 +/* +#define PLL_SYS_REFDIV (1) +#define PLL_SYS_VCO_FREQ_HZ (1500000000) +#define PLL_SYS_POSTDIV1 (6) +#define PLL_SYS_POSTDIV2 (1) +#define SYS_CLK_HZ (250000000) +*/ + +// 200 MHz - Most of our testing was done at 200MHz. +// ./build/micropython/lib/pico-sdk/src/rp2_common/hardware_clocks/scripts/vcocalc.py --cmake 200 +#define PLL_SYS_REFDIV (1) +#define PLL_SYS_VCO_FREQ_HZ (1200000000) +#define PLL_SYS_POSTDIV1 (6) +#define PLL_SYS_POSTDIV2 (1) +#define SYS_CLK_HZ (200000000) + +// Support 250MHz if user manually overclocks +#define CYW43_PIO_CLOCK_DIV_INT 3 + +// --- RP2350 VARIANT --- +// not PICO_RP2350A + +// --- UART --- +// no PICO_DEFAULT_UART +// no PICO_DEFAULT_UART_TX_PIN +// no PICO_DEFAULT_UART_RX_PIN + +// --- LED --- +// no PICO_DEFAULT_LED_PIN - LED is on Wireless chip +// no PICO_DEFAULT_WS2812_PIN + +// --- I2C --- +#ifndef PICO_DEFAULT_I2C +#define PICO_DEFAULT_I2C 0 +#endif +#ifndef PICO_DEFAULT_I2C_SDA_PIN +#define PICO_DEFAULT_I2C_SDA_PIN BW_RTC_I2C_SDA +#endif +#ifndef PICO_DEFAULT_I2C_SCL_PIN +#define PICO_DEFAULT_I2C_SCL_PIN BW_RTC_I2C_SCL +#endif + +// --- SPI --- +// no PICO_DEFAULT_SPI +// no PICO_DEFAULT_SPI_SCK_PIN +// no PICO_DEFAULT_SPI_TX_PIN +// no PICO_DEFAULT_SPI_RX_PIN +// no PICO_DEFAULT_SPI_CSN_PIN + +// --- FLASH --- + +#define PICO_BOOT_STAGE2_CHOOSE_W25Q080 1 + +#ifndef PICO_FLASH_SPI_CLKDIV +#define PICO_FLASH_SPI_CLKDIV 2 +#endif + +// pico_cmake_set_default PICO_FLASH_SIZE_BYTES = (16 * 1024 * 1024) +#ifndef PICO_FLASH_SIZE_BYTES +#define PICO_FLASH_SIZE_BYTES (16 * 1024 * 1024) +#endif + +// --- CYW43 --- + +#ifndef CYW43_WL_GPIO_COUNT +#define CYW43_WL_GPIO_COUNT 3 +#endif + +// pico_cmake_set_default PICO_RP2350_A2_SUPPORTED = 1 +#ifndef PICO_RP2350_A2_SUPPORTED +#define PICO_RP2350_A2_SUPPORTED 1 +#endif + +// cyw43 SPI pins can't be changed at runtime +#ifndef CYW43_PIN_WL_DYNAMIC +#define CYW43_PIN_WL_DYNAMIC 0 +#endif + +// gpio pin to power up the cyw43 chip +#ifndef CYW43_DEFAULT_PIN_WL_REG_ON +#define CYW43_DEFAULT_PIN_WL_REG_ON 23u +#endif + +// gpio pin for spi data out to the cyw43 chip +#ifndef CYW43_DEFAULT_PIN_WL_DATA_OUT +#define CYW43_DEFAULT_PIN_WL_DATA_OUT 24u +#endif + +// gpio pin for spi data in from the cyw43 chip +#ifndef CYW43_DEFAULT_PIN_WL_DATA_IN +#define CYW43_DEFAULT_PIN_WL_DATA_IN 24u +#endif + +// gpio (irq) pin for the irq line from the cyw43 chip +#ifndef CYW43_DEFAULT_PIN_WL_HOST_WAKE +#define CYW43_DEFAULT_PIN_WL_HOST_WAKE 24u +#endif + +// gpio pin for the spi clock line to the cyw43 chip +#ifndef CYW43_DEFAULT_PIN_WL_CLOCK +#define CYW43_DEFAULT_PIN_WL_CLOCK 29u +#endif + +// gpio pin for the spi chip select to the cyw43 chip +#ifndef CYW43_DEFAULT_PIN_WL_CS +#define CYW43_DEFAULT_PIN_WL_CS 25u +#endif + +#endif From 6e188ac6697d676bb3a5b09147c183df8e89cea2 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Sun, 1 Feb 2026 13:17:42 +0000 Subject: [PATCH 03/16] pico: add a board define to the board header --- 32blit-pico/board/pimoroni_tufty2350.h | 1 + 1 file changed, 1 insertion(+) diff --git a/32blit-pico/board/pimoroni_tufty2350.h b/32blit-pico/board/pimoroni_tufty2350.h index dae7c8ec1..3be7b29a4 100644 --- a/32blit-pico/board/pimoroni_tufty2350.h +++ b/32blit-pico/board/pimoroni_tufty2350.h @@ -15,6 +15,7 @@ #ifndef _BOARDS_PICO2_W_H #define _BOARDS_PICO2_W_H +#define PIMORONI_TUFTY2350 //#define PICO_PANIC_FUNCTION mp_pico_panic From eea4542b8ffec0121db0cae7a46f3d27179cdbc3 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Fri, 30 Jan 2026 21:00:46 +0000 Subject: [PATCH 04/16] pico: tufty2350 config --- .../board/pimoroni_tufty2350/config.cmake | 4 ++++ 32blit-pico/board/pimoroni_tufty2350/config.h | 23 +++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 32blit-pico/board/pimoroni_tufty2350/config.cmake create mode 100644 32blit-pico/board/pimoroni_tufty2350/config.h diff --git a/32blit-pico/board/pimoroni_tufty2350/config.cmake b/32blit-pico/board/pimoroni_tufty2350/config.cmake new file mode 100644 index 000000000..59043a1bf --- /dev/null +++ b/32blit-pico/board/pimoroni_tufty2350/config.cmake @@ -0,0 +1,4 @@ +set(BLIT_BOARD_NAME "Tufty 2350") + +blit_driver(display dbi) +blit_driver(input gpio) diff --git a/32blit-pico/board/pimoroni_tufty2350/config.h b/32blit-pico/board/pimoroni_tufty2350/config.h new file mode 100644 index 000000000..9a38add43 --- /dev/null +++ b/32blit-pico/board/pimoroni_tufty2350/config.h @@ -0,0 +1,23 @@ +#pragma once + +#define BUTTON_UP_PIN 11 +#define BUTTON_DOWN_PIN 6 +#define BUTTON_A_PIN 7 +#define BUTTON_B_PIN 9 +#define BUTTON_X_PIN 10 // C +#define BUTTON_HOME_PIN 22 + +#define DISPLAY_ST7789 + +#define DBI_8BIT +#define LCD_ROTATION 270 +#define LCD_CS_PIN 27 +#define LCD_DC_PIN 28 +#define LCD_SCK_PIN 30 // WR +#define LCD_RD_PIN 31 +#define LCD_MOSI_PIN 32 // DB0 +#define LCD_BACKLIGHT_PIN 26 +// #define LCD_VSYNC_PIN +#define LCD_MAX_CLOCK 15000000 + +// there are white LEDs From 6f68bfd44134ef71e20b94fc03f94f2c0c6009d3 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Fri, 30 Jan 2026 22:31:58 +0000 Subject: [PATCH 05/16] pico: set i2c clock in tufty2350 config Makes it easy to switch to qwstpad --- 32blit-pico/board/pimoroni_tufty2350/config.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/32blit-pico/board/pimoroni_tufty2350/config.h b/32blit-pico/board/pimoroni_tufty2350/config.h index 9a38add43..8006e2d99 100644 --- a/32blit-pico/board/pimoroni_tufty2350/config.h +++ b/32blit-pico/board/pimoroni_tufty2350/config.h @@ -21,3 +21,5 @@ #define LCD_MAX_CLOCK 15000000 // there are white LEDs + +#define DEFAULT_I2C_CLOCK 400000 From ccbb4933346132786931607784d3191a928f7213 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Sat, 31 Jan 2026 22:12:25 +0000 Subject: [PATCH 06/16] pico: pull in tufty2350 powman code so I can turn it off --- 32blit-pico/CMakeLists.txt | 6 + 32blit-pico/board/pimoroni_tufty2350/powman.c | 492 ++++++++++++++++++ 32blit-pico/board/pimoroni_tufty2350/powman.h | 58 +++ 3 files changed, 556 insertions(+) create mode 100644 32blit-pico/board/pimoroni_tufty2350/powman.c create mode 100644 32blit-pico/board/pimoroni_tufty2350/powman.h diff --git a/32blit-pico/CMakeLists.txt b/32blit-pico/CMakeLists.txt index a68203059..e81c978c8 100644 --- a/32blit-pico/CMakeLists.txt +++ b/32blit-pico/CMakeLists.txt @@ -63,6 +63,12 @@ target_sources(BlitHalPico INTERFACE ${CMAKE_CURRENT_LIST_DIR}/usb/${BLIT_USB_DRIVER}.cpp ) +# pull in powman code so the thing can be turned off... +if(PICO_BOARD STREQUAL "pimoroni_tufty2350") + list(APPEND BLIT_BOARD_LIBRARIES hardware_powman) + target_sources(BlitHalPico INTERFACE ${CMAKE_CURRENT_LIST_DIR}/board/pimoroni_tufty2350/powman.c) +endif() + if(BLIT_ENABLE_CORE1) list(APPEND BLIT_BOARD_DEFINITIONS ENABLE_CORE1) endif() diff --git a/32blit-pico/board/pimoroni_tufty2350/powman.c b/32blit-pico/board/pimoroni_tufty2350/powman.c new file mode 100644 index 000000000..d0a59d3f1 --- /dev/null +++ b/32blit-pico/board/pimoroni_tufty2350/powman.c @@ -0,0 +1,492 @@ + +#include "powman.h" + +static powman_power_state off_state; +static powman_power_state on_state; +bool powman_wake_with_doubletap; +uint32_t user_button_state = 0; + +// Long press to sleep LED effect +#define LED_PEAK_BRIGHTNESS 150 // Total brightness value per led (this is multiplied by GAMMA) +#define LED_IN_PHASE 30 // The delay between the current and next LED lighting up +#define LED_OUT_PHASE 0 +#define LED_TOTAL (LED_PEAK_BRIGHTNESS + LED_IN_PHASE * 3) // Total bright phase for the LEDs +#define LED_FADEOUT_SPEED 5 // Speedup for fading out +#define LED_GAMMA 1.8f +#define LED_DELAY_MS 5 // Delay between steps +#define LED_ON_DELAY_MS 200 // A short delay before the LEDs start their thing +#define LED_ON_DELAY (LED_ON_DELAY_MS / LED_DELAY_MS) // Convert the above delay in terms of steps + +// This is effectively the sequence order for the swooshy LED effect +const uint led_gpios[4] = {BW_LED_1, BW_LED_2, BW_LED_3, BW_LED_0}; +//const uint led_gpios_poweron[4] = {BW_LED_0, BW_LED_2, BW_LED_3, BW_LED_1}; + +//#define DEBUG + +static inline bool double_tap_flag_is_set(void) { + return powman_hw->chip_reset & POWMAN_CHIP_RESET_DOUBLE_TAP_BITS; +} + +static inline void set_double_tap_flag(void) { + powman_set_bits(&powman_hw->chip_reset, POWMAN_CHIP_RESET_DOUBLE_TAP_BITS); +} + +static inline void clear_double_tap_flag(void) { + powman_clear_bits(&powman_hw->chip_reset, POWMAN_CHIP_RESET_DOUBLE_TAP_BITS); +} + +uint8_t powman_get_wake_reason(void) { + // 0 = chip reset, for the source of the last reset see POWMAN_CHIP_RESET + // 1 = pwrup0 (GPIO interrupt 0) + // 2 = pwrup1 (GPIO interrupt 1) + // 3 = pwrup2 (GPIO interrupt 2) + // 4 = pwrup3 (GPIO interrupt 3) + // 5 = coresight_pwrup + // 6 = alarm_pwrup (timeout or alarm wakeup) + // 7 = powman_wake_with_doubletap + return (powman_hw->last_swcore_pwrup & 0x7f) | (powman_wake_with_doubletap ? POWMAN_DOUBLETAP : 0); +} + +bool powman_wake_reset(void) { + return powman_hw->chip_reset & POWMAN_CHIP_RESET_HAD_RUN_LOW_BITS; +} + +bool powman_wake_watchdog(void) { + return watchdog_caused_reboot(); +} + +uint32_t powman_get_user_switches(void) { + return user_button_state; +} + +void i2c_enable(void) { + gpio_init(BW_SW_POWER_EN); + gpio_set_dir(BW_SW_POWER_EN, GPIO_OUT); + gpio_put(BW_SW_POWER_EN, 1); + + sleep_ms(500); + + i2c_init(BW_RTC_I2C, 400 * 1000); + gpio_set_function(BW_RTC_I2C_SDA, GPIO_FUNC_I2C); + gpio_set_function(BW_RTC_I2C_SCL, GPIO_FUNC_I2C); +} + +void i2c_disable(void) { + gpio_init(BW_SW_POWER_EN); + gpio_init(BW_RTC_I2C_SDA); + gpio_init(BW_RTC_I2C_SCL); +} + +static inline uint8_t pcf85063_get_timer_flag() { + uint8_t buf = 0x01; + i2c_write_blocking(BW_RTC_I2C, BW_RTC_ADDR, &buf, 1, false); + i2c_read_blocking(BW_RTC_I2C, BW_RTC_ADDR, (uint8_t *)&buf, 1, false); + return (buf & 0x08) == 0x08; +} + +static inline void pcf85063_clear_timer_flag() { + uint8_t buf[2] = {0x01, 0b00000000}; // Control_2 + i2c_write_blocking(BW_RTC_I2C, BW_RTC_ADDR, buf, 2, false); +} + +static inline void pcf85063_disable_interrupt() { + // Disable RTC timer interrupt + uint8_t data3[2] = {0x11, 0b00000000}; + i2c_write_blocking(BW_RTC_I2C, 0x51, data3, 2, false); +} + +void pcf85063_wakeup_init(uint8_t period) { + // Set up the RTC to countdown before triggering wake + + // Set default timer frequency to minutes (1/60Hz) + uint8_t timer_mode = 0b00010000; // 0b11 == minutes, 0b10 == seconds + + uint8_t buf[2] = {0}; + + buf[0] = 0x00; // Control_1 + buf[1] = 0b00000000; // Ensure default values + i2c_write_blocking(BW_RTC_I2C, BW_RTC_ADDR, buf, 2, false); + + buf[0] = 0x11; // Timer_mode + buf[1] = timer_mode; // interrupt disable + timer disable + i2c_write_blocking(BW_RTC_I2C, BW_RTC_ADDR, buf, 2, false); + + // Clear any prior interrupt flags + // And ensure a 32738 Hz clockout + pcf85063_clear_timer_flag(); + + // Switch into seconds to time anything 4 minutes and under + //if (period <= 4) { + // period *= 60; + // timer_mode = 0b00010000; // 0b10 == seconds + //} + + buf[0] = 0x10; // Timer_value + buf[1] = period; // Set the timer period (in seconds) + i2c_write_blocking(BW_RTC_I2C, BW_RTC_ADDR, buf, 2, false); + + buf[0] = 0x11; // Timer_mode + buf[1] = timer_mode | 0b00000111; // interrupt enable + timer enable + i2c_write_blocking(BW_RTC_I2C, BW_RTC_ADDR, buf, 2, false); +} + +void powman_init() { + uint64_t abs_time_ms = 1746057600000; // 2025/05/01 - Milliseconds since epoch + + clear_double_tap_flag(); + + // Run everything from pll_usb pll and stop pll_sys + set_sys_clock_48mhz(); + + // Use the 32768 Hz clockout from the RTC to keep time accurately + //clock_configure_gpin(clk_ref, 12, 32.768f * KHZ, 32.768f * KHZ); + //clock_configure_gpin(clk_sys, 12, 32.768f * KHZ, 32.768f * KHZ); + //clock_configure_undivided(clk_peri, 0, CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS, 32.768f * KHZ); + //powman_timer_set_1khz_tick_source_lposc_with_hz(32768); + + // Redundant? - we're using the RTC clock + //powman_timer_set_1khz_tick_source_lposc(); + + // Does this accomplish *anything*? + //pll_deinit(pll_sys); + + for (unsigned i = 0; i < NUM_BANK0_GPIOS; ++i) { + gpio_set_function(i, GPIO_FUNC_SIO); + gpio_set_dir(i, GPIO_IN); + gpio_set_input_enabled(i, false); + switch (i) { + case 8: + gpio_set_pulls(i, true, false); + break; + case BW_RESET_SW: + case BW_SWITCH_HOME: + case 40: + case BW_SW_POWER_EN: + case 42: // Floating + gpio_disable_pulls(i); + break; + case BW_SWITCH_A: + case BW_SWITCH_B: + case BW_SWITCH_C: + case BW_SWITCH_UP: + case BW_SWITCH_DOWN: // Don't mess with the button pulls, must be pulled up + break; + default: // Pull down + gpio_set_pulls(i, false, true); + } + } + + // Unlock the VREG control interface + hw_set_bits(&powman_hw->vreg_ctrl, POWMAN_PASSWORD_BITS | POWMAN_VREG_CTRL_UNLOCK_BITS); + + // Reset usb controller + reset_block_mask(RESETS_RESET_USBCTRL_BITS); + unreset_block_mask_wait_blocking(RESETS_RESET_USBCTRL_BITS); + + // Mux the controller to the onboard usb phy + usb_hw->muxing = USB_USB_MUXING_TO_PHY_BITS | USB_USB_MUXING_SOFTCON_BITS; + + // Initializes the USB peripheral for device mode and enables it. + // Don't need to enable the pull up here. Force VBUS + usb_hw->main_ctrl = USB_MAIN_CTRL_CONTROLLER_EN_BITS; + + // Enable individual controller IRQS here. Processor interrupt enable will be used + // for the global interrupt enable... + // Note: Force VBUS detect cause disconnection not detectable + usb_hw->sie_ctrl = USB_SIE_CTRL_EP0_INT_1BUF_BITS; + usb_hw->inte = USB_INTS_BUFF_STATUS_BITS | USB_INTS_BUS_RESET_BITS | USB_INTS_SETUP_REQ_BITS | + USB_INTS_DEV_SUSPEND_BITS | USB_INTS_DEV_RESUME_FROM_HOST_BITS | USB_INTS_DEV_CONN_DIS_BITS; + + + // Turn off USB PHY and apply pull downs on DP & DM + usb_hw->phy_direct = USB_USBPHY_DIRECT_TX_PD_BITS | USB_USBPHY_DIRECT_RX_PD_BITS | USB_USBPHY_DIRECT_DM_PULLDN_EN_BITS | USB_USBPHY_DIRECT_DP_PULLDN_EN_BITS; + + usb_hw->phy_direct_override = USB_USBPHY_DIRECT_RX_DM_BITS | USB_USBPHY_DIRECT_RX_DP_BITS | USB_USBPHY_DIRECT_RX_DD_BITS | + USB_USBPHY_DIRECT_OVERRIDE_TX_DIFFMODE_OVERRIDE_EN_BITS | USB_USBPHY_DIRECT_OVERRIDE_DM_PULLUP_OVERRIDE_EN_BITS | USB_USBPHY_DIRECT_OVERRIDE_TX_FSSLEW_OVERRIDE_EN_BITS | + USB_USBPHY_DIRECT_OVERRIDE_TX_PD_OVERRIDE_EN_BITS | USB_USBPHY_DIRECT_OVERRIDE_RX_PD_OVERRIDE_EN_BITS | USB_USBPHY_DIRECT_OVERRIDE_TX_DM_OVERRIDE_EN_BITS | + USB_USBPHY_DIRECT_OVERRIDE_TX_DP_OVERRIDE_EN_BITS | USB_USBPHY_DIRECT_OVERRIDE_TX_DM_OE_OVERRIDE_EN_BITS | USB_USBPHY_DIRECT_OVERRIDE_TX_DP_OE_OVERRIDE_EN_BITS | + USB_USBPHY_DIRECT_OVERRIDE_DM_PULLDN_EN_OVERRIDE_EN_BITS | USB_USBPHY_DIRECT_OVERRIDE_DP_PULLDN_EN_OVERRIDE_EN_BITS | USB_USBPHY_DIRECT_OVERRIDE_DP_PULLUP_EN_OVERRIDE_EN_BITS | + USB_USBPHY_DIRECT_OVERRIDE_DM_PULLUP_HISEL_OVERRIDE_EN_BITS | USB_USBPHY_DIRECT_OVERRIDE_DP_PULLUP_HISEL_OVERRIDE_EN_BITS; + + // start powman and set the time + powman_timer_start(); + powman_timer_set_ms(abs_time_ms); + + // Allow power down when debugger connected + powman_set_debug_power_request_ignored(true); + + // Power states + powman_power_state P1_7 = POWMAN_POWER_STATE_NONE; + + powman_power_state P0_3 = POWMAN_POWER_STATE_NONE; + P0_3 = powman_power_state_with_domain_on(P0_3, POWMAN_POWER_DOMAIN_SWITCHED_CORE); + P0_3 = powman_power_state_with_domain_on(P0_3, POWMAN_POWER_DOMAIN_XIP_CACHE); + + off_state = P1_7; + on_state = P0_3; +} + +bool __no_inline_not_in_flash_func(psram_cs1_pullup_check)(void) { + uint32_t intr_stash = save_and_disable_interrupts(); + gpio_init(BW_PSRAM_CS); // Init to SIO / IN + gpio_set_pulls(BW_PSRAM_CS, false, true); // Pull down + sleep_us(100); + bool pin_state = gpio_get(BW_PSRAM_CS) == 1; // Check if pin is strongly pulled up + gpio_set_pulls(BW_PSRAM_CS, false, false); // Disable pulls + gpio_set_function(BW_PSRAM_CS, GPIO_FUNC_XIP_CS1); // Return the CS pin to the correct function + restore_interrupts(intr_stash); + return pin_state; +} + +// Initiate power off +int __no_inline_not_in_flash_func(powman_off)(void) { + // Set power states + bool valid_state = powman_configure_wakeup_state(off_state, on_state); + if (!valid_state) { + return PICO_ERROR_INVALID_STATE; + } + + // reboot to main + powman_hw->boot[0] = 0; + powman_hw->boot[1] = 0; + powman_hw->boot[2] = 0; + powman_hw->boot[3] = 0; + + // Switch to required power state + int rc = powman_set_power_state(off_state); + if (rc != PICO_OK) { + return rc; + } + + // Power down + while (true) __wfi(); +} + +int powman_setup_gpio_wakeup(int hw_wakeup, int gpio, bool edge, bool high, uint64_t timeout_ms) { + gpio_init(gpio); + gpio_set_dir(gpio, false); + gpio_set_input_enabled(gpio, true); + + // Must set pulls here, or our pin may never go into its idle state + gpio_set_pulls(gpio, !high, high); + + // If the pin is currently in a triggered state, wait for idle + absolute_time_t timeout = make_timeout_time_ms(timeout_ms); + if (gpio_get(gpio) == high) { + while(gpio_get(gpio) == high) { + sleep_ms(10); + if(time_reached(timeout)) return -1; + } + } + powman_enable_gpio_wakeup(hw_wakeup, gpio, edge, high); + + return 0; +} + +// Power off until a gpio goes high +int powman_off_until_gpio_high(int gpio, bool edge, bool high, uint64_t timeout_ms) { + powman_init(); + + powman_setup_gpio_wakeup(POWMAN_WAKE_PWRUP0_CH, gpio, edge, high, 1000); + + if (timeout_ms > 0) { + uint64_t ms = powman_timer_get_ms(); + return powman_off_until_time(ms + timeout_ms); + } else { + return powman_off(); + } +} + +// Power off until an absolute time +int powman_off_until_time(uint64_t absolute_time_ms) { + powman_init(); + + // Start powman timer and turn off + powman_enable_alarm_wakeup_at_ms(absolute_time_ms); + return powman_off(); +} + +// Power off for a number of milliseconds +int powman_off_for_ms(uint64_t duration_ms) { + powman_init(); + + uint64_t ms = powman_timer_get_ms(); + return powman_off_until_time(ms + duration_ms); +} + +static inline void setup_gpio(bool buttons_only) { + // Init all button GPIOs + gpio_init_mask(BW_SWITCH_MASK); + gpio_set_dir_in_masked(BW_SWITCH_MASK); + gpio_set_pulls(BW_SWITCH_A, true, false); + gpio_set_pulls(BW_SWITCH_B, true, false); + gpio_set_pulls(BW_SWITCH_C, true, false); + gpio_set_pulls(BW_SWITCH_UP, true, false); + gpio_set_pulls(BW_SWITCH_DOWN, true, false); + + if(buttons_only) { + return; + } + + // Init the home button + gpio_init(BW_SWITCH_HOME); + gpio_set_dir(BW_SWITCH_HOME, GPIO_IN); + gpio_set_pulls(BW_SWITCH_HOME, true, false); + + // Init the button interrupt + gpio_init(BW_SWITCH_INT); + gpio_set_dir(BW_SWITCH_INT, GPIO_IN); + gpio_set_pulls(BW_SWITCH_INT, true, false); + + // Init the RTC interrupt + gpio_init(BW_RTC_ALARM); + gpio_set_dir(BW_RTC_ALARM, GPIO_IN); + gpio_set_pulls(BW_RTC_ALARM, true, false); + + // Init the VBUS detect + gpio_init(BW_VBUS_DETECT); + gpio_set_dir(BW_VBUS_DETECT, GPIO_IN); + gpio_set_pulls(BW_VBUS_DETECT, false, false); + + // Moved to RM2 + // Init the charge status detect + // gpio_init(BW_CHARGE_STAT); + // gpio_set_dir(BW_CHARGE_STAT, GPIO_IN); + // gpio_set_pulls(BW_CHARGE_STAT, true, false); + + // Set up LEDs + gpio_init_mask(0b1111); + gpio_set_dir_out_masked(0b1111); + + // Set up long press detect + gpio_init(BW_RESET_SW); + gpio_set_dir(BW_RESET_SW, GPIO_IN); + gpio_pull_up(BW_RESET_SW); + + // Enable I2C power + gpio_init(BW_SW_POWER_EN); + gpio_set_dir(BW_SW_POWER_EN, GPIO_OUT); + gpio_put(BW_SW_POWER_EN, 1); +} + +// Latch inputs +static inline void latch_inputs(void) { + user_button_state = ~gpio_get_all(); + sleep_ms(5); + user_button_state |= ~gpio_get_all(); +} + +// disable RTC interrupt +static inline void setup_system(void) { + i2c_enable(); + pcf85063_disable_interrupt(); +} + +static int64_t alarm_clear_double_tap(alarm_id_t id, __unused void *user_data) { + clear_double_tap_flag(); + return 0; +} + +void shipping_mode() { + powman_init(); + i2c_disable(); + int rc = powman_off(); + hard_assert(rc == PICO_OK); + hard_assert(false); // should never get here! +} + +void long_press_sleep() { + powman_init(); + + // We must set the pulls on the user buttons or they will not be sufficient + // to trigger the interrupt pin + setup_gpio(true); + + int err; + //(void)powman_setup_gpio_wakeup(POWMAN_WAKE_PWRUP0_CH, BW_VBUS_DETECT, true, true, 1000); + err = powman_setup_gpio_wakeup(POWMAN_WAKE_PWRUP1_CH, BW_RTC_ALARM, true, false, 1000); + //err = powman_setup_gpio_wakeup(POWMAN_WAKE_PWRUP2_CH, BW_RESET_SW, true, true, 1000); + err = powman_setup_gpio_wakeup(POWMAN_WAKE_PWRUP3_CH, BW_SWITCH_INT, true, false, 1000); + (void)err; + + i2c_disable(); + int rc = powman_off(); + hard_assert(rc == PICO_OK); + hard_assert(false); // should never get here! +} + +void handle_long_press() { + pwm_config config = pwm_get_default_config(); + pwm_config_set_clkdiv(&config, clock_get_hz(clk_sys) / 2048.0f); + pwm_config_set_wrap(&config, 1024); + for(unsigned i = 0u; i < 4; i++) { + gpio_set_function(led_gpios[i], GPIO_FUNC_PWM); + pwm_init(pwm_gpio_to_slice_num(led_gpios[i]), &config, true); + } + int br = 0; + while (true) { + // If reset is released (high), cut the long press short + if(gpio_get(BW_RESET_SW)) { + break; + } + int cbr = br < LED_ON_DELAY ? 0 : br - LED_ON_DELAY; + int o = cbr >= LED_TOTAL ? LED_TOTAL - (cbr - LED_TOTAL) * LED_FADEOUT_SPEED : cbr; + int phase = cbr >= LED_TOTAL ? LED_OUT_PHASE : LED_IN_PHASE; + int level = 0; + for(unsigned i = 0u; i < 4; i++) { + int v = fmax(0, fmin(LED_PEAK_BRIGHTNESS, o)); + pwm_set_gpio_level(led_gpios[i], v * LED_GAMMA); + level += v * LED_GAMMA; + o -= phase; + } + // If the LEDs have completed their fade to black + if(cbr > 0 && level == 0) { + clear_double_tap_flag(); + if(!gpio_get(BW_SWITCH_UP) && !gpio_get(BW_SWITCH_DOWN)) { + shipping_mode(); + } else { + long_press_sleep(); + } + break; // unreachable + }; + br++; + sleep_ms(LED_DELAY_MS); + } + for(unsigned i = 0u; i < 4; i++) { + pwm_set_enabled(pwm_gpio_to_slice_num(led_gpios[i]), false); + } + gpio_init_mask(0b1111); + gpio_set_dir_out_masked(0b1111); + gpio_put_masked(0b1111, 0); +} + +static void __attribute__((constructor)) powman_startup(void) { + setup_gpio(false); + latch_inputs(); + + // If we haven't reset via a button press we ought not to delay startup + if (!(powman_hw->chip_reset & POWMAN_CHIP_RESET_HAD_RUN_LOW_BITS) || watchdog_caused_reboot()) { + setup_system(); + //power_on_leds(); + return; + }; + + if (!double_tap_flag_is_set()) { + // Arm, wait, then disarm and continue booting + set_double_tap_flag(); + + add_alarm_in_ms(POWMAN_DOUBLE_RESET_TIMEOUT_MS, alarm_clear_double_tap, NULL, false); + + // If reset is held (low), it could be a long press + if(gpio_get(BW_RESET_SW) == 0) { + handle_long_press(); + } + + setup_system(); + //power_on_leds(); + return; + } + clear_double_tap_flag(); + powman_wake_with_doubletap = true; + setup_system(); +} diff --git a/32blit-pico/board/pimoroni_tufty2350/powman.h b/32blit-pico/board/pimoroni_tufty2350/powman.h new file mode 100644 index 000000000..c163a8dca --- /dev/null +++ b/32blit-pico/board/pimoroni_tufty2350/powman.h @@ -0,0 +1,58 @@ + + +#include +#include +#include +#include "pico/stdio.h" +#include "pico/sync.h" +#include "hardware/gpio.h" +#include "hardware/powman.h" +#include "hardware/watchdog.h" +#include "hardware/clocks.h" +#include "hardware/pll.h" +//#include "hardware/adc.h" +#include "hardware/structs/usb.h" +#include "hardware/structs/xosc.h" +#include "hardware/vreg.h" +#include "hardware/flash.h" +#include "hardware/structs/qmi.h" +#include "hardware/i2c.h" +#include "hardware/resets.h" +#include "hardware/pwm.h" + +// For machine_pin_find +//#include "machine_pin.h" + +// For Blinky teardown +#include "hardware/pio.h" + +#define POWMAN_DOUBLE_RESET_TIMEOUT_MS 1000 +#define POWMAN_LONG_PRESS_TIMEOUT_MS 1000 + +#define POWMAN_WAKE_PWRUP0_CH 0 // WAKE_VBUS_DETECT +#define POWMAN_WAKE_PWRUP1_CH 1 // WAKE_RTC +#define POWMAN_WAKE_PWRUP2_CH 2 // WAKE_USER_SW +#define POWMAN_WAKE_PWRUP3_CH 3 // One of the user buttons + +#define POWMAN_WAKE_RESET 0b00000001 +#define POWMAN_WAKE_PWRUP0 0b00000010 +#define POWMAN_WAKE_PWRUP1 0b00000100 +#define POWMAN_WAKE_PWRUP2 0b00001000 +#define POWMAN_WAKE_PWRUP3 0b00010000 +#define POWMAN_WAKE_CORESI 0b00100000 +#define POWMAN_WAKE_ALARM 0b01000000 +#define POWMAN_DOUBLETAP 0b10000000 + +int powman_off_until_gpio_high(int gpio, bool edge, bool high, uint64_t timeout_ms); +int powman_off_until_time(uint64_t absolute_time_ms); +int powman_off_for_ms(uint64_t duration_ms); +uint8_t powman_get_wake_reason(void); +uint32_t powman_get_user_switches(void); +bool powman_wake_reset(void); +bool powman_wake_watchdog(void); + +bool psram_cs1_pullup_check(void); + +void powman_init(); +int powman_setup_gpio_wakeup(int hw_wakeup, int gpio, bool edge, bool high, uint64_t timeout_ms); +int powman_off(void); From 217edcbce83b8c0a93bdf1a557f2e5a32a127593 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Sat, 31 Jan 2026 22:37:01 +0000 Subject: [PATCH 07/16] pico: strip down powman code a bit Mostly the exported functions, but also the double-tap stuff --- 32blit-pico/board/pimoroni_tufty2350/powman.c | 169 ++---------------- 32blit-pico/board/pimoroni_tufty2350/powman.h | 14 -- 2 files changed, 13 insertions(+), 170 deletions(-) diff --git a/32blit-pico/board/pimoroni_tufty2350/powman.c b/32blit-pico/board/pimoroni_tufty2350/powman.c index d0a59d3f1..367aff7ea 100644 --- a/32blit-pico/board/pimoroni_tufty2350/powman.c +++ b/32blit-pico/board/pimoroni_tufty2350/powman.c @@ -3,8 +3,6 @@ static powman_power_state off_state; static powman_power_state on_state; -bool powman_wake_with_doubletap; -uint32_t user_button_state = 0; // Long press to sleep LED effect #define LED_PEAK_BRIGHTNESS 150 // Total brightness value per led (this is multiplied by GAMMA) @@ -23,43 +21,7 @@ const uint led_gpios[4] = {BW_LED_1, BW_LED_2, BW_LED_3, BW_LED_0}; //#define DEBUG -static inline bool double_tap_flag_is_set(void) { - return powman_hw->chip_reset & POWMAN_CHIP_RESET_DOUBLE_TAP_BITS; -} - -static inline void set_double_tap_flag(void) { - powman_set_bits(&powman_hw->chip_reset, POWMAN_CHIP_RESET_DOUBLE_TAP_BITS); -} - -static inline void clear_double_tap_flag(void) { - powman_clear_bits(&powman_hw->chip_reset, POWMAN_CHIP_RESET_DOUBLE_TAP_BITS); -} - -uint8_t powman_get_wake_reason(void) { - // 0 = chip reset, for the source of the last reset see POWMAN_CHIP_RESET - // 1 = pwrup0 (GPIO interrupt 0) - // 2 = pwrup1 (GPIO interrupt 1) - // 3 = pwrup2 (GPIO interrupt 2) - // 4 = pwrup3 (GPIO interrupt 3) - // 5 = coresight_pwrup - // 6 = alarm_pwrup (timeout or alarm wakeup) - // 7 = powman_wake_with_doubletap - return (powman_hw->last_swcore_pwrup & 0x7f) | (powman_wake_with_doubletap ? POWMAN_DOUBLETAP : 0); -} - -bool powman_wake_reset(void) { - return powman_hw->chip_reset & POWMAN_CHIP_RESET_HAD_RUN_LOW_BITS; -} - -bool powman_wake_watchdog(void) { - return watchdog_caused_reboot(); -} - -uint32_t powman_get_user_switches(void) { - return user_button_state; -} - -void i2c_enable(void) { +static void i2c_enable(void) { gpio_init(BW_SW_POWER_EN); gpio_set_dir(BW_SW_POWER_EN, GPIO_OUT); gpio_put(BW_SW_POWER_EN, 1); @@ -71,7 +33,7 @@ void i2c_enable(void) { gpio_set_function(BW_RTC_I2C_SCL, GPIO_FUNC_I2C); } -void i2c_disable(void) { +static void i2c_disable(void) { gpio_init(BW_SW_POWER_EN); gpio_init(BW_RTC_I2C_SDA); gpio_init(BW_RTC_I2C_SCL); @@ -95,46 +57,9 @@ static inline void pcf85063_disable_interrupt() { i2c_write_blocking(BW_RTC_I2C, 0x51, data3, 2, false); } -void pcf85063_wakeup_init(uint8_t period) { - // Set up the RTC to countdown before triggering wake - - // Set default timer frequency to minutes (1/60Hz) - uint8_t timer_mode = 0b00010000; // 0b11 == minutes, 0b10 == seconds - - uint8_t buf[2] = {0}; - - buf[0] = 0x00; // Control_1 - buf[1] = 0b00000000; // Ensure default values - i2c_write_blocking(BW_RTC_I2C, BW_RTC_ADDR, buf, 2, false); - - buf[0] = 0x11; // Timer_mode - buf[1] = timer_mode; // interrupt disable + timer disable - i2c_write_blocking(BW_RTC_I2C, BW_RTC_ADDR, buf, 2, false); - - // Clear any prior interrupt flags - // And ensure a 32738 Hz clockout - pcf85063_clear_timer_flag(); - - // Switch into seconds to time anything 4 minutes and under - //if (period <= 4) { - // period *= 60; - // timer_mode = 0b00010000; // 0b10 == seconds - //} - - buf[0] = 0x10; // Timer_value - buf[1] = period; // Set the timer period (in seconds) - i2c_write_blocking(BW_RTC_I2C, BW_RTC_ADDR, buf, 2, false); - - buf[0] = 0x11; // Timer_mode - buf[1] = timer_mode | 0b00000111; // interrupt enable + timer enable - i2c_write_blocking(BW_RTC_I2C, BW_RTC_ADDR, buf, 2, false); -} - -void powman_init() { +static void powman_init() { uint64_t abs_time_ms = 1746057600000; // 2025/05/01 - Milliseconds since epoch - clear_double_tap_flag(); - // Run everything from pll_usb pll and stop pll_sys set_sys_clock_48mhz(); @@ -226,20 +151,8 @@ void powman_init() { on_state = P0_3; } -bool __no_inline_not_in_flash_func(psram_cs1_pullup_check)(void) { - uint32_t intr_stash = save_and_disable_interrupts(); - gpio_init(BW_PSRAM_CS); // Init to SIO / IN - gpio_set_pulls(BW_PSRAM_CS, false, true); // Pull down - sleep_us(100); - bool pin_state = gpio_get(BW_PSRAM_CS) == 1; // Check if pin is strongly pulled up - gpio_set_pulls(BW_PSRAM_CS, false, false); // Disable pulls - gpio_set_function(BW_PSRAM_CS, GPIO_FUNC_XIP_CS1); // Return the CS pin to the correct function - restore_interrupts(intr_stash); - return pin_state; -} - // Initiate power off -int __no_inline_not_in_flash_func(powman_off)(void) { +static int __no_inline_not_in_flash_func(powman_off)(void) { // Set power states bool valid_state = powman_configure_wakeup_state(off_state, on_state); if (!valid_state) { @@ -262,7 +175,7 @@ int __no_inline_not_in_flash_func(powman_off)(void) { while (true) __wfi(); } -int powman_setup_gpio_wakeup(int hw_wakeup, int gpio, bool edge, bool high, uint64_t timeout_ms) { +static int powman_setup_gpio_wakeup(int hw_wakeup, int gpio, bool edge, bool high, uint64_t timeout_ms) { gpio_init(gpio); gpio_set_dir(gpio, false); gpio_set_input_enabled(gpio, true); @@ -283,37 +196,6 @@ int powman_setup_gpio_wakeup(int hw_wakeup, int gpio, bool edge, bool high, uint return 0; } -// Power off until a gpio goes high -int powman_off_until_gpio_high(int gpio, bool edge, bool high, uint64_t timeout_ms) { - powman_init(); - - powman_setup_gpio_wakeup(POWMAN_WAKE_PWRUP0_CH, gpio, edge, high, 1000); - - if (timeout_ms > 0) { - uint64_t ms = powman_timer_get_ms(); - return powman_off_until_time(ms + timeout_ms); - } else { - return powman_off(); - } -} - -// Power off until an absolute time -int powman_off_until_time(uint64_t absolute_time_ms) { - powman_init(); - - // Start powman timer and turn off - powman_enable_alarm_wakeup_at_ms(absolute_time_ms); - return powman_off(); -} - -// Power off for a number of milliseconds -int powman_off_for_ms(uint64_t duration_ms) { - powman_init(); - - uint64_t ms = powman_timer_get_ms(); - return powman_off_until_time(ms + duration_ms); -} - static inline void setup_gpio(bool buttons_only) { // Init all button GPIOs gpio_init_mask(BW_SWITCH_MASK); @@ -369,25 +251,13 @@ static inline void setup_gpio(bool buttons_only) { gpio_put(BW_SW_POWER_EN, 1); } -// Latch inputs -static inline void latch_inputs(void) { - user_button_state = ~gpio_get_all(); - sleep_ms(5); - user_button_state |= ~gpio_get_all(); -} - // disable RTC interrupt static inline void setup_system(void) { i2c_enable(); pcf85063_disable_interrupt(); } -static int64_t alarm_clear_double_tap(alarm_id_t id, __unused void *user_data) { - clear_double_tap_flag(); - return 0; -} - -void shipping_mode() { +static void shipping_mode() { powman_init(); i2c_disable(); int rc = powman_off(); @@ -395,7 +265,7 @@ void shipping_mode() { hard_assert(false); // should never get here! } -void long_press_sleep() { +static void long_press_sleep() { powman_init(); // We must set the pulls on the user buttons or they will not be sufficient @@ -415,7 +285,7 @@ void long_press_sleep() { hard_assert(false); // should never get here! } -void handle_long_press() { +static void handle_long_press() { pwm_config config = pwm_get_default_config(); pwm_config_set_clkdiv(&config, clock_get_hz(clk_sys) / 2048.0f); pwm_config_set_wrap(&config, 1024); @@ -441,7 +311,6 @@ void handle_long_press() { } // If the LEDs have completed their fade to black if(cbr > 0 && level == 0) { - clear_double_tap_flag(); if(!gpio_get(BW_SWITCH_UP) && !gpio_get(BW_SWITCH_DOWN)) { shipping_mode(); } else { @@ -462,7 +331,6 @@ void handle_long_press() { static void __attribute__((constructor)) powman_startup(void) { setup_gpio(false); - latch_inputs(); // If we haven't reset via a button press we ought not to delay startup if (!(powman_hw->chip_reset & POWMAN_CHIP_RESET_HAD_RUN_LOW_BITS) || watchdog_caused_reboot()) { @@ -471,22 +339,11 @@ static void __attribute__((constructor)) powman_startup(void) { return; }; - if (!double_tap_flag_is_set()) { - // Arm, wait, then disarm and continue booting - set_double_tap_flag(); - - add_alarm_in_ms(POWMAN_DOUBLE_RESET_TIMEOUT_MS, alarm_clear_double_tap, NULL, false); - - // If reset is held (low), it could be a long press - if(gpio_get(BW_RESET_SW) == 0) { - handle_long_press(); - } - - setup_system(); - //power_on_leds(); - return; + // If reset is held (low), it could be a long press + if(gpio_get(BW_RESET_SW) == 0) { + handle_long_press(); } - clear_double_tap_flag(); - powman_wake_with_doubletap = true; + setup_system(); + //power_on_leds(); } diff --git a/32blit-pico/board/pimoroni_tufty2350/powman.h b/32blit-pico/board/pimoroni_tufty2350/powman.h index c163a8dca..7692ec68a 100644 --- a/32blit-pico/board/pimoroni_tufty2350/powman.h +++ b/32blit-pico/board/pimoroni_tufty2350/powman.h @@ -42,17 +42,3 @@ #define POWMAN_WAKE_CORESI 0b00100000 #define POWMAN_WAKE_ALARM 0b01000000 #define POWMAN_DOUBLETAP 0b10000000 - -int powman_off_until_gpio_high(int gpio, bool edge, bool high, uint64_t timeout_ms); -int powman_off_until_time(uint64_t absolute_time_ms); -int powman_off_for_ms(uint64_t duration_ms); -uint8_t powman_get_wake_reason(void); -uint32_t powman_get_user_switches(void); -bool powman_wake_reset(void); -bool powman_wake_watchdog(void); - -bool psram_cs1_pullup_check(void); - -void powman_init(); -int powman_setup_gpio_wakeup(int hw_wakeup, int gpio, bool edge, bool high, uint64_t timeout_ms); -int powman_off(void); From 99cd70c9034c2b7ba5ec7b97b6c1c45d2a3b9ccf Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Fri, 6 Feb 2026 16:17:10 +0000 Subject: [PATCH 08/16] pico: set tufty2350 usb ids --- 32blit-pico/board/pimoroni_tufty2350/config.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/32blit-pico/board/pimoroni_tufty2350/config.h b/32blit-pico/board/pimoroni_tufty2350/config.h index 8006e2d99..fd0fe2760 100644 --- a/32blit-pico/board/pimoroni_tufty2350/config.h +++ b/32blit-pico/board/pimoroni_tufty2350/config.h @@ -23,3 +23,10 @@ // there are white LEDs #define DEFAULT_I2C_CLOCK 400000 + +#define USB_VENDOR_ID 0x2E8A +#define USB_PRODUCT_ID 0x1101 + +#define USB_VENDOR_STR "Pimoroni" +#define USB_PRODUCT_STR "Tufty 2350" + From c375cdcc1143ff3be53a1e322385b09964857f1a Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Sat, 31 Aug 2024 19:18:23 +0100 Subject: [PATCH 09/16] pico: rename the pixel-double irq handler --- 32blit-pico/display/dbi.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/32blit-pico/display/dbi.cpp b/32blit-pico/display/dbi.cpp index 62c215cfe..175810139 100644 --- a/32blit-pico/display/dbi.cpp +++ b/32blit-pico/display/dbi.cpp @@ -84,7 +84,7 @@ static void pio_wait(PIO pio, uint sm) { } // used for pixel doubling -static void __isr dbi_dma_irq_handler() { +static void __isr double_dma_irq_handler() { if(dma_channel_get_irq0_status(dma_channel)) { dma_channel_acknowledge_irq0(dma_channel); @@ -439,7 +439,7 @@ void init_display() { dma_channel_configure( dma_channel, &config, &pio->txf[pio_sm], frame_buffer, DISPLAY_WIDTH * DISPLAY_HEIGHT, false); - irq_add_shared_handler(DMA_IRQ_0, dbi_dma_irq_handler, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY); + irq_add_shared_handler(DMA_IRQ_0, double_dma_irq_handler, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY); irq_set_enabled(DMA_IRQ_0, true); clear(); From 6f2aaf4af8bfa85ffa5f3d1aaa1ba7117dd2bcdb Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Sun, 1 Sep 2024 17:19:02 +0100 Subject: [PATCH 10/16] pico: optionally transpose data sent to display Avoids diagonal tearing from letting the display do it --- 32blit-pico/config.h | 4 ++ 32blit-pico/display/dbi.cpp | 124 ++++++++++++++++++++++++++++++++++-- 2 files changed, 124 insertions(+), 4 deletions(-) diff --git a/32blit-pico/config.h b/32blit-pico/config.h index 13a627aad..525eb157c 100644 --- a/32blit-pico/config.h +++ b/32blit-pico/config.h @@ -210,6 +210,10 @@ #define LCD_MAX_CLOCK 62500000 #endif +#ifndef LCD_TRANSPOSE +#define LCD_TRANSPOSE 0 +#endif + #ifndef OVERCLOCK_250 #define OVERCLOCK_250 1 #endif diff --git a/32blit-pico/display/dbi.cpp b/32blit-pico/display/dbi.cpp index 175810139..e7b965e5e 100644 --- a/32blit-pico/display/dbi.cpp +++ b/32blit-pico/display/dbi.cpp @@ -68,9 +68,14 @@ static uint16_t *upd_frame_buffer = nullptr; // frame buffer where pixel data is stored static uint16_t *frame_buffer = nullptr; +// used for transpose, two scanlines +static uint32_t temp_buffer[DISPLAY_WIDTH]; + // pixel double scanline counter static volatile int cur_scanline = DISPLAY_HEIGHT; +static irq_handler_t cur_irq_handler = nullptr; + // PIO helpers static void pio_put_byte(PIO pio, uint sm, uint8_t b) { while (pio_sm_is_tx_fifo_full(pio, sm)); @@ -83,6 +88,14 @@ static void pio_wait(PIO pio, uint sm) { while(!(pio->fdebug & stall_mask)); } +static inline void transpose_data(const uint16_t *in, uint16_t *out, int count, int stride) { + auto end = out + count; + do { + *out++ = *in; + in += stride; + } while(out != end); +} + // used for pixel doubling static void __isr double_dma_irq_handler() { if(dma_channel_get_irq0_status(dma_channel)) { @@ -98,6 +111,79 @@ static void __isr double_dma_irq_handler() { } } +static void __isr double_transposed_dma_irq_handler() { + if(dma_channel_get_irq0_status(dma_channel)) { + dma_channel_acknowledge_irq0(dma_channel); + + if(cur_scanline > win_h / 2) + return; + + // num pixels + auto count = cur_scanline == (win_h + 1) / 2 ? win_w / 2 : win_w; + + // start from buffer 0 (to repeat first line) + int palette_buf_idx = cur_scanline & 1; + + dma_channel_set_trans_count(dma_channel, count / 2, false); + dma_channel_set_read_addr(dma_channel, temp_buffer + (win_w / 2) * (palette_buf_idx ^ 1), true); + + // prepare next lines + if(++cur_scanline > win_h / 2) + return; + + auto in = upd_frame_buffer + (cur_scanline - 1); + auto out = (uint16_t *)temp_buffer + palette_buf_idx * win_w; + + transpose_data(in, out, win_w / 2, win_h / 2); + if(cur_scanline != (win_h + 1) / 2) + transpose_data(in + 1, out + win_w / 2, win_w / 2, win_h / 2); + } +} + +static void __isr transposed_dma_irq_handler() { + if(dma_channel_get_irq0_status(dma_channel)) { + dma_channel_acknowledge_irq0(dma_channel); + + if(cur_scanline >= win_h) + return; + + // start from buffer 1 + int palette_buf_idx = cur_scanline & 1; + + dma_channel_set_trans_count(dma_channel, win_w, false); + dma_channel_set_read_addr(dma_channel, temp_buffer + (win_w / 2) * palette_buf_idx, true); + + // prepare next line + if(++cur_scanline >= win_h) + return; + + auto in = upd_frame_buffer + cur_scanline; + auto out = (uint16_t *)temp_buffer + (palette_buf_idx ^ 1) * win_w; + transpose_data(in, out, win_w, win_h); + } +} + +// set DMA irq handler for pixel doubling/palette lookup +static void update_irq_handler() { + irq_handler_t new_handler; + + if(LCD_TRANSPOSE) + new_handler = pixel_double ? double_transposed_dma_irq_handler : transposed_dma_irq_handler; + else + new_handler = double_dma_irq_handler; + + if(cur_irq_handler == new_handler) + return; + + if(cur_irq_handler) + irq_remove_handler(DMA_IRQ_0, cur_irq_handler); + + irq_add_shared_handler(DMA_IRQ_0, new_handler, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY); + cur_irq_handler = new_handler; + + cur_scanline = win_h; // probably switching mode, set to max so that dma_is_busy thinks we're finished +} + static void command(uint8_t command, size_t len = 0, const char *data = nullptr) { pio_wait(pio, pio_sm); @@ -134,6 +220,11 @@ static void command(uint8_t command, size_t len = 0, const char *data = nullptr) } static void set_window(uint16_t x, uint16_t y, uint16_t w, uint16_t h) { + if(LCD_TRANSPOSE) { + std::swap(x, y); + std::swap(w, h); + } + uint32_t cols = __builtin_bswap32((x << 16) | (x + w - 1)); uint32_t rows = __builtin_bswap32((y << 16) | (y + h - 1)); @@ -199,6 +290,10 @@ static void send_init_sequence() { sleep_ms(100); uint8_t madctl = MADCTL::RGB | rotations[LCD_ROTATION / 90]; + + if(LCD_TRANSPOSE) + madctl ^= MADCTL::SWAP_XY; + command(MIPIDCS::SetAddressMode, 1, (char *)&madctl); // setup correct addressing window @@ -267,9 +362,25 @@ static void update() { if(!write_mode) prepare_write(); + upd_frame_buffer = frame_buffer; + + if(LCD_TRANSPOSE) { + // setup first two lines + auto stride = pixel_double ? win_h / 2 : win_h; + auto count = pixel_double ? win_w / 2 : win_w; + transpose_data(frame_buffer, (uint16_t *)temp_buffer, count, stride); + transpose_data(frame_buffer + 1, (uint16_t *)temp_buffer + count, count, stride); + + cur_scanline = 1; + + int dma_count = pixel_double ? win_w / 4 : win_w; + dma_channel_set_trans_count(dma_channel, dma_count, false); + dma_channel_set_read_addr(dma_channel, temp_buffer, true); + return; + } + if(pixel_double) { cur_scanline = 0; - upd_frame_buffer = frame_buffer; dma_channel_set_trans_count(dma_channel, win_w / 4, false); } else dma_channel_set_trans_count(dma_channel, win_w * win_h, false); @@ -294,7 +405,10 @@ static void set_pixel_double(bool pd) { if(write_mode) command(0); - if(pixel_double) { + dma_channel_set_irq0_enabled(dma_channel, false); + update_irq_handler(); + + if(pixel_double || LCD_TRANSPOSE) { dma_channel_acknowledge_irq0(dma_channel); dma_channel_set_irq0_enabled(dma_channel, true); } else @@ -306,12 +420,14 @@ static void clear() { prepare_write(); for(int i = 0; i < win_w * win_h; i++) - pio_sm_put_blocking(pio, pio_sm, 0); + pio_sm_put_blocking(pio, pio_sm,0); } static bool dma_is_busy() { if(pixel_double && cur_scanline <= win_h / 2) return true; + else if(LCD_TRANSPOSE && !pixel_double && cur_scanline < win_h) + return true; return dma_channel_is_busy(dma_channel); } @@ -439,7 +555,7 @@ void init_display() { dma_channel_configure( dma_channel, &config, &pio->txf[pio_sm], frame_buffer, DISPLAY_WIDTH * DISPLAY_HEIGHT, false); - irq_add_shared_handler(DMA_IRQ_0, double_dma_irq_handler, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY); + update_irq_handler(); irq_set_enabled(DMA_IRQ_0, true); clear(); From 0a0f4324c4605bde1097f7e7e8ab8a062bbcdaba Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Sat, 31 Jan 2026 22:13:01 +0000 Subject: [PATCH 11/16] pico: enable vsync and transpose for tufty2350 This needs more wip patches than the basic config --- 32blit-pico/board/pimoroni_tufty2350/config.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/32blit-pico/board/pimoroni_tufty2350/config.h b/32blit-pico/board/pimoroni_tufty2350/config.h index fd0fe2760..f32fd79b6 100644 --- a/32blit-pico/board/pimoroni_tufty2350/config.h +++ b/32blit-pico/board/pimoroni_tufty2350/config.h @@ -17,9 +17,11 @@ #define LCD_RD_PIN 31 #define LCD_MOSI_PIN 32 // DB0 #define LCD_BACKLIGHT_PIN 26 -// #define LCD_VSYNC_PIN +#define LCD_VSYNC_PIN 21 #define LCD_MAX_CLOCK 15000000 +#define LCD_TRANSPOSE 1 + // there are white LEDs #define DEFAULT_I2C_CLOCK 400000 From 8b4108d8defb204f0dd2959e8ebcf9de726e645b Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Tue, 24 Dec 2024 13:00:21 +0000 Subject: [PATCH 12/16] pico: driver-ify LEDs --- 32blit-pico/CMakeLists.txt | 2 +- 32blit-pico/board-config.cmake | 3 +++ 32blit-pico/board/display_pack/config.cmake | 1 + 32blit-pico/board/display_pack_2/config.cmake | 1 + 32blit-pico/board/display_pack_2_8/config.cmake | 1 + 32blit-pico/board/pimoroni_picosystem/config.cmake | 1 + 32blit-pico/led/none.cpp | 7 +++++++ 32blit-pico/{led.cpp => led/pwm.cpp} | 7 ------- 8 files changed, 15 insertions(+), 8 deletions(-) create mode 100644 32blit-pico/led/none.cpp rename 32blit-pico/{led.cpp => led/pwm.cpp} (89%) diff --git a/32blit-pico/CMakeLists.txt b/32blit-pico/CMakeLists.txt index e81c978c8..eee1ed6c3 100644 --- a/32blit-pico/CMakeLists.txt +++ b/32blit-pico/CMakeLists.txt @@ -14,7 +14,6 @@ target_sources(BlitHalPico INTERFACE ${CMAKE_CURRENT_LIST_DIR}/blit_launch.cpp ${CMAKE_CURRENT_LIST_DIR}/display.cpp ${CMAKE_CURRENT_LIST_DIR}/file.cpp - ${CMAKE_CURRENT_LIST_DIR}/led.cpp ${CMAKE_CURRENT_LIST_DIR}/main.cpp ${CMAKE_CURRENT_LIST_DIR}/multiplayer.cpp ${CMAKE_CURRENT_LIST_DIR}/overlay.cpp @@ -59,6 +58,7 @@ target_sources(BlitHalPico INTERFACE ${CMAKE_CURRENT_LIST_DIR}/audio/${BLIT_AUDIO_DRIVER}.cpp ${CMAKE_CURRENT_LIST_DIR}/display/${BLIT_DISPLAY_DRIVER}.cpp ${CMAKE_CURRENT_LIST_DIR}/input/${BLIT_INPUT_DRIVER}.cpp + ${CMAKE_CURRENT_LIST_DIR}/led/${BLIT_LED_DRIVER}.cpp ${CMAKE_CURRENT_LIST_DIR}/storage/${BLIT_STORAGE_DRIVER}.cpp ${CMAKE_CURRENT_LIST_DIR}/usb/${BLIT_USB_DRIVER}.cpp ) diff --git a/32blit-pico/board-config.cmake b/32blit-pico/board-config.cmake index 2bb701c67..2709a9565 100644 --- a/32blit-pico/board-config.cmake +++ b/32blit-pico/board-config.cmake @@ -49,6 +49,9 @@ endif() if(NOT BLIT_INPUT_DRIVER) set(BLIT_INPUT_DRIVER "none") endif() +if(NOT BLIT_LED_DRIVER) + set(BLIT_LED_DRIVER "none") +endif() if(NOT BLIT_STORAGE_DRIVER) set(BLIT_STORAGE_DRIVER "flash") endif() diff --git a/32blit-pico/board/display_pack/config.cmake b/32blit-pico/board/display_pack/config.cmake index d982cc4d4..82bdec6bc 100644 --- a/32blit-pico/board/display_pack/config.cmake +++ b/32blit-pico/board/display_pack/config.cmake @@ -2,3 +2,4 @@ set(BLIT_BOARD_NAME "Display Pack") blit_driver(display dbi) blit_driver(input gpio) +blit_driver(led pwm) diff --git a/32blit-pico/board/display_pack_2/config.cmake b/32blit-pico/board/display_pack_2/config.cmake index bb59ac106..2d3a67a36 100644 --- a/32blit-pico/board/display_pack_2/config.cmake +++ b/32blit-pico/board/display_pack_2/config.cmake @@ -2,3 +2,4 @@ set(BLIT_BOARD_NAME "Display Pack 2.0") blit_driver(display dbi) blit_driver(input gpio) +blit_driver(led pwm) diff --git a/32blit-pico/board/display_pack_2_8/config.cmake b/32blit-pico/board/display_pack_2_8/config.cmake index a7a210eed..38f19c4b1 100644 --- a/32blit-pico/board/display_pack_2_8/config.cmake +++ b/32blit-pico/board/display_pack_2_8/config.cmake @@ -2,3 +2,4 @@ set(BLIT_BOARD_NAME "Display Pack 2.8") blit_driver(display dbi) blit_driver(input gpio) +blit_driver(led pwm) diff --git a/32blit-pico/board/pimoroni_picosystem/config.cmake b/32blit-pico/board/pimoroni_picosystem/config.cmake index ac66d9c78..e6a92a6d2 100644 --- a/32blit-pico/board/pimoroni_picosystem/config.cmake +++ b/32blit-pico/board/pimoroni_picosystem/config.cmake @@ -12,3 +12,4 @@ set(BLIT_BOARD_DEFINITIONS blit_driver(audio beep) blit_driver(display dbi) blit_driver(input gpio) +blit_driver(led pwm) diff --git a/32blit-pico/led/none.cpp b/32blit-pico/led/none.cpp new file mode 100644 index 000000000..1a7d76fdb --- /dev/null +++ b/32blit-pico/led/none.cpp @@ -0,0 +1,7 @@ +#include "led.hpp" + +void init_led() { +} + +void update_led() { +} diff --git a/32blit-pico/led.cpp b/32blit-pico/led/pwm.cpp similarity index 89% rename from 32blit-pico/led.cpp rename to 32blit-pico/led/pwm.cpp index d6f755781..8695d8cd1 100644 --- a/32blit-pico/led.cpp +++ b/32blit-pico/led/pwm.cpp @@ -10,13 +10,9 @@ #include "engine/api_private.hpp" -#if defined(LED_R_PIN) && defined(LED_G_PIN) && defined(LED_B_PIN) static const int led_pins[]{LED_R_PIN, LED_G_PIN, LED_B_PIN}; -#define HAVE_LED -#endif void init_led() { -#ifdef HAVE_LED pwm_config cfg = pwm_get_default_config(); #ifdef LED_INVERTED pwm_config_set_output_polarity(&cfg, true, true); @@ -31,13 +27,11 @@ void init_led() { bi_decl(bi_1pin_with_name(led_pins[0], "Red LED")); bi_decl(bi_1pin_with_name(led_pins[1], "Green LED")); bi_decl(bi_1pin_with_name(led_pins[2], "Blue LED")); -#endif } void update_led() { using namespace blit; -#ifdef HAVE_LED const float gamma = 2.8; uint16_t value = (uint16_t)(std::pow((float)(api_data.LED.r) / 255.0f, gamma) * 65535.0f + 0.5f); pwm_set_gpio_level(led_pins[0], value); @@ -45,5 +39,4 @@ void update_led() { pwm_set_gpio_level(led_pins[1], value); value = (uint16_t)(std::pow((float)(api_data.LED.b) / 255.0f, gamma) * 65535.0f + 0.5f); pwm_set_gpio_level(led_pins[2], value); -#endif } From 9efa939158a162ce4725cdb14fd1b8cceb50cc23 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Mon, 2 Feb 2026 13:49:04 +0000 Subject: [PATCH 13/16] pico: mono LED driver Averages RGB to any number of LEDs. Bit of a stretch though --- 32blit-pico/led/pwm_mono.cpp | 45 ++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 32blit-pico/led/pwm_mono.cpp diff --git a/32blit-pico/led/pwm_mono.cpp b/32blit-pico/led/pwm_mono.cpp new file mode 100644 index 000000000..9f52bf295 --- /dev/null +++ b/32blit-pico/led/pwm_mono.cpp @@ -0,0 +1,45 @@ +#include "led.hpp" + +#include + +#include "hardware/gpio.h" +#include "hardware/pwm.h" +#include "pico/binary_info.h" + +#include "config.h" + +#include "engine/api_private.hpp" + +static const int led_pins[]{LED_MONO_PINS}; + +template +constexpr auto make_mask(Args... args) { + return ((1ull << args) | ...); +} + +void init_led() { + pwm_config cfg = pwm_get_default_config(); +#ifdef LED_INVERTED + pwm_config_set_output_polarity(&cfg, true, true); +#endif + + for(auto &pin : led_pins) { + pwm_set_wrap(pwm_gpio_to_slice_num(pin), 65535); + pwm_init(pwm_gpio_to_slice_num(pin), &cfg, true); + gpio_set_function(pin, GPIO_FUNC_PWM); + } + + bi_decl(bi_pin_mask_with_name(make_mask(LED_MONO_PINS), "LED")); +} + +void update_led() { + using namespace blit; + + const float gamma = 2.8; + + float avg = float(api_data.LED.r + api_data.LED.g + api_data.LED.b) / 3.0f; + uint16_t value = (uint16_t)(std::pow(avg / 255.0f, gamma) * 65535.0f + 0.5f); + + for(auto &pin : led_pins) + pwm_set_gpio_level(pin, value); +} From 2e2812ca7c7b580b52bdfc2de00368baf1955cea Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Mon, 2 Feb 2026 13:50:19 +0000 Subject: [PATCH 14/16] pico: tufty2350 leds --- 32blit-pico/board/pimoroni_tufty2350/config.cmake | 1 + 32blit-pico/board/pimoroni_tufty2350/config.h | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/32blit-pico/board/pimoroni_tufty2350/config.cmake b/32blit-pico/board/pimoroni_tufty2350/config.cmake index 59043a1bf..8f06250a4 100644 --- a/32blit-pico/board/pimoroni_tufty2350/config.cmake +++ b/32blit-pico/board/pimoroni_tufty2350/config.cmake @@ -2,3 +2,4 @@ set(BLIT_BOARD_NAME "Tufty 2350") blit_driver(display dbi) blit_driver(input gpio) +blit_driver(led pwm_mono) diff --git a/32blit-pico/board/pimoroni_tufty2350/config.h b/32blit-pico/board/pimoroni_tufty2350/config.h index f32fd79b6..90f416d71 100644 --- a/32blit-pico/board/pimoroni_tufty2350/config.h +++ b/32blit-pico/board/pimoroni_tufty2350/config.h @@ -22,13 +22,12 @@ #define LCD_TRANSPOSE 1 -// there are white LEDs - #define DEFAULT_I2C_CLOCK 400000 +#define LED_MONO_PINS 0, 1, 2, 3 + #define USB_VENDOR_ID 0x2E8A #define USB_PRODUCT_ID 0x1101 #define USB_VENDOR_STR "Pimoroni" #define USB_PRODUCT_STR "Tufty 2350" - From 100ad834536b50e1da707c664880cc8ee7ffab58 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Thu, 5 Mar 2026 20:36:32 +0000 Subject: [PATCH 15/16] pico: rename palette_buf_idx in DBI transpose code This was copy/paste from the paletted mode code... which is not included here --- 32blit-pico/display/dbi.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/32blit-pico/display/dbi.cpp b/32blit-pico/display/dbi.cpp index e7b965e5e..f45405790 100644 --- a/32blit-pico/display/dbi.cpp +++ b/32blit-pico/display/dbi.cpp @@ -122,17 +122,17 @@ static void __isr double_transposed_dma_irq_handler() { auto count = cur_scanline == (win_h + 1) / 2 ? win_w / 2 : win_w; // start from buffer 0 (to repeat first line) - int palette_buf_idx = cur_scanline & 1; + int temp_buf_index = cur_scanline & 1; dma_channel_set_trans_count(dma_channel, count / 2, false); - dma_channel_set_read_addr(dma_channel, temp_buffer + (win_w / 2) * (palette_buf_idx ^ 1), true); + dma_channel_set_read_addr(dma_channel, temp_buffer + (win_w / 2) * (temp_buf_index ^ 1), true); // prepare next lines if(++cur_scanline > win_h / 2) return; auto in = upd_frame_buffer + (cur_scanline - 1); - auto out = (uint16_t *)temp_buffer + palette_buf_idx * win_w; + auto out = (uint16_t *)temp_buffer + temp_buf_index * win_w; transpose_data(in, out, win_w / 2, win_h / 2); if(cur_scanline != (win_h + 1) / 2) @@ -148,17 +148,17 @@ static void __isr transposed_dma_irq_handler() { return; // start from buffer 1 - int palette_buf_idx = cur_scanline & 1; + int temp_buf_index = cur_scanline & 1; dma_channel_set_trans_count(dma_channel, win_w, false); - dma_channel_set_read_addr(dma_channel, temp_buffer + (win_w / 2) * palette_buf_idx, true); + dma_channel_set_read_addr(dma_channel, temp_buffer + (win_w / 2) * temp_buf_index, true); // prepare next line if(++cur_scanline >= win_h) return; auto in = upd_frame_buffer + cur_scanline; - auto out = (uint16_t *)temp_buffer + (palette_buf_idx ^ 1) * win_w; + auto out = (uint16_t *)temp_buffer + (temp_buf_index ^ 1) * win_w; transpose_data(in, out, win_w, win_h); } } From 161bf9c3e4a968c6e3e6426fb3d8cbfc5edad597 Mon Sep 17 00:00:00 2001 From: Charlie Birks Date: Thu, 5 Mar 2026 20:54:01 +0000 Subject: [PATCH 16/16] Add Tufty 2350 (.blit) build action Now there is a source of RP2350-compatible .blits --- .github/workflows/cmake.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index df4f19ed6..defc26cc1 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -56,6 +56,14 @@ jobs: cmake-args: -D32BLIT_DIR=$GITHUB_WORKSPACE -DPICO_SDK_PATH=$GITHUB_WORKSPACE/pico-sdk -DPICO_BOARD=pimoroni_picovision -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache apt-packages: ccache gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib + - os: ubuntu-22.04 + pico-sdk: true + name: Tufty 2350 (.blit) + cache-key: tufty2350-bl + release-suffix: Tufty2350-blit + cmake-args: -D32BLIT_DIR=$GITHUB_WORKSPACE -DPICO_SDK_PATH=$GITHUB_WORKSPACE/pico-sdk -DPICO_BOARD=pimoroni_tufty2350 -DBLIT_EXECUTABLE_PICO_BLIT=1 -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + apt-packages: ccache gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib python3-setuptools + - os: ubuntu-22.04 name: MinGW cache-key: mingw