From f9eddaae303d69f3f737e083b5ad8bcc9c72b8a5 Mon Sep 17 00:00:00 2001 From: Alvaro Figueroa Date: Wed, 2 Sep 2026 10:12:05 -0600 Subject: [PATCH] Adding support for muzi works base-duo board --- ports/nordic/boards/muzi_base_duo/board.c | 9 ++ .../boards/muzi_base_duo/mpconfigboard.h | 53 +++++++ .../boards/muzi_base_duo/mpconfigboard.mk | 11 ++ ports/nordic/boards/muzi_base_duo/pins.c | 134 ++++++++++++++++++ 4 files changed, 207 insertions(+) create mode 100644 ports/nordic/boards/muzi_base_duo/board.c create mode 100644 ports/nordic/boards/muzi_base_duo/mpconfigboard.h create mode 100644 ports/nordic/boards/muzi_base_duo/mpconfigboard.mk create mode 100644 ports/nordic/boards/muzi_base_duo/pins.c diff --git a/ports/nordic/boards/muzi_base_duo/board.c b/ports/nordic/boards/muzi_base_duo/board.c new file mode 100644 index 00000000000..43816ec6093 --- /dev/null +++ b/ports/nordic/boards/muzi_base_duo/board.c @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Álvaro Figueroa +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/nordic/boards/muzi_base_duo/mpconfigboard.h b/ports/nordic/boards/muzi_base_duo/mpconfigboard.h new file mode 100644 index 00000000000..8a7913a3c1c --- /dev/null +++ b/ports/nordic/boards/muzi_base_duo/mpconfigboard.h @@ -0,0 +1,53 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Álvaro Figueroa +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "nrfx/hal/nrf_gpio.h" + +#define MICROPY_HW_BOARD_NAME "muzi works Base Duo" +#define MICROPY_HW_MCU_NAME "nRF52840" + +// Blue LED. Cathode is driven by the MCU, anode sits on 3V3, so this is active low. +#define MICROPY_HW_LED_STATUS (&pin_P1_04) +#define MICROPY_HW_LED_STATUS_INVERTED (1) + +// Y1 32.768 kHz crystal on P0.00 / P0.01. +#define BOARD_HAS_32KHZ_XTAL (1) + +// User button, active low with a 100k pull-up. +#define CIRCUITPY_BOOT_BUTTON (&pin_P0_10) + +#if QSPI_FLASH_FILESYSTEM +#define MICROPY_QSPI_DATA0 NRF_GPIO_PIN_MAP(0, 30) +#define MICROPY_QSPI_DATA1 NRF_GPIO_PIN_MAP(0, 29) +#define MICROPY_QSPI_DATA2 NRF_GPIO_PIN_MAP(0, 28) +#define MICROPY_QSPI_DATA3 NRF_GPIO_PIN_MAP(0, 2) +#define MICROPY_QSPI_SCK NRF_GPIO_PIN_MAP(0, 3) +#define MICROPY_QSPI_CS NRF_GPIO_PIN_MAP(0, 26) +#endif + +#define CIRCUITPY_INTERNAL_NVM_SIZE (4096) + +#define BOARD_FLASH_SIZE (FLASH_SIZE - 0x4000 - CIRCUITPY_INTERNAL_NVM_SIZE) + +// 0: Qwiic / STEMMA QT and the castellated I2C pads. +// 1: the OLED / display header pads. +#define CIRCUITPY_BOARD_I2C (2) +#define CIRCUITPY_BOARD_I2C_PIN { \ + {.scl = &pin_P0_06, .sda = &pin_P0_04}, \ + {.scl = &pin_P0_25, .sda = &pin_P0_24}, \ +} + +// The only SPI bus is the module-internal one going to the LR1121. +#define CIRCUITPY_BOARD_SPI (1) +#define CIRCUITPY_BOARD_SPI_PIN { \ + {.clock = &pin_P1_13, .mosi = &pin_P1_14, .miso = &pin_P1_15}, \ +} + +// Castellated UART. Net names on the silkscreen are from the peripheral's point of view. +#define DEFAULT_UART_BUS_TX (&pin_P0_19) +#define DEFAULT_UART_BUS_RX (&pin_P0_20) diff --git a/ports/nordic/boards/muzi_base_duo/mpconfigboard.mk b/ports/nordic/boards/muzi_base_duo/mpconfigboard.mk new file mode 100644 index 00000000000..0871287ba22 --- /dev/null +++ b/ports/nordic/boards/muzi_base_duo/mpconfigboard.mk @@ -0,0 +1,11 @@ +USB_VID = 0x239A +USB_PID = 0x0081 +USB_PRODUCT = "Base Duo" +USB_MANUFACTURER = "muzi works" + +MCU_CHIP = nrf52840 + +QSPI_FLASH_FILESYSTEM = 1 +# Rev01 ships a W25Q128JV; the Meshtastic BASE variant documents a W25Q32JV. +# List both families and let the JEDEC ID decide at runtime. +EXTERNAL_FLASH_DEVICES = "W25Q128JVxQ, W25Q64JVxQ, W25Q32JVxQ, W25Q32JVxM" diff --git a/ports/nordic/boards/muzi_base_duo/pins.c b/ports/nordic/boards/muzi_base_duo/pins.c new file mode 100644 index 00000000000..4c069042217 --- /dev/null +++ b/ports/nordic/boards/muzi_base_duo/pins.c @@ -0,0 +1,134 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Álvaro Figueroa +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" + +// Instance 0 is the Qwiic / STEMMA QT connector, instance 1 is the display header. +CIRCUITPY_BOARD_BUS_SINGLETON(stemma_i2c, i2c, 0) +CIRCUITPY_BOARD_BUS_SINGLETON(display_i2c, i2c, 1) + +// The LR1121 hangs off the only SPI bus, inside the nRFLR1121 module. +CIRCUITPY_BOARD_BUS_SINGLETON(lora_spi, spi, 0) + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + // Castellated I/O, bottom edge (JC4-JC9, JC13). + // The silkscreen names in parentheses are the trackball positions muzi uses + // on its own carrier boards. + { MP_ROM_QSTR(MP_QSTR_IO1), MP_ROM_PTR(&pin_P1_05) }, + { MP_ROM_QSTR(MP_QSTR_BTN_L), MP_ROM_PTR(&pin_P1_05) }, + { MP_ROM_QSTR(MP_QSTR_P1_05), MP_ROM_PTR(&pin_P1_05) }, + + { MP_ROM_QSTR(MP_QSTR_IO2), MP_ROM_PTR(&pin_P0_21) }, + { MP_ROM_QSTR(MP_QSTR_BTN_U), MP_ROM_PTR(&pin_P0_21) }, + { MP_ROM_QSTR(MP_QSTR_P0_21), MP_ROM_PTR(&pin_P0_21) }, + + { MP_ROM_QSTR(MP_QSTR_IO3), MP_ROM_PTR(&pin_P0_17) }, + { MP_ROM_QSTR(MP_QSTR_BTN_D), MP_ROM_PTR(&pin_P0_17) }, + { MP_ROM_QSTR(MP_QSTR_P0_17), MP_ROM_PTR(&pin_P0_17) }, + + { MP_ROM_QSTR(MP_QSTR_IO4), MP_ROM_PTR(&pin_P0_16) }, + { MP_ROM_QSTR(MP_QSTR_BTN_R), MP_ROM_PTR(&pin_P0_16) }, + { MP_ROM_QSTR(MP_QSTR_P0_16), MP_ROM_PTR(&pin_P0_16) }, + + { MP_ROM_QSTR(MP_QSTR_IO5), MP_ROM_PTR(&pin_P0_15) }, + { MP_ROM_QSTR(MP_QSTR_BTN_B), MP_ROM_PTR(&pin_P0_15) }, + { MP_ROM_QSTR(MP_QSTR_P0_15), MP_ROM_PTR(&pin_P0_15) }, + + { MP_ROM_QSTR(MP_QSTR_IO6), MP_ROM_PTR(&pin_P1_00) }, + { MP_ROM_QSTR(MP_QSTR_P1_00), MP_ROM_PTR(&pin_P1_00) }, + + // Castellated I/O, top edge (JC19, JC20). Labelled SWITCH 1 / SWITCH 2. + { MP_ROM_QSTR(MP_QSTR_IO7), MP_ROM_PTR(&pin_P1_09) }, + { MP_ROM_QSTR(MP_QSTR_SWITCH1), MP_ROM_PTR(&pin_P1_09) }, + { MP_ROM_QSTR(MP_QSTR_P1_09), MP_ROM_PTR(&pin_P1_09) }, + + { MP_ROM_QSTR(MP_QSTR_IO8), MP_ROM_PTR(&pin_P0_12) }, + { MP_ROM_QSTR(MP_QSTR_SWITCH2), MP_ROM_PTR(&pin_P0_12) }, + { MP_ROM_QSTR(MP_QSTR_P0_12), MP_ROM_PTR(&pin_P0_12) }, + + // On-board user button (JC6). Active low, 100k pull-up. Also NFC2. + { MP_ROM_QSTR(MP_QSTR_BUTTON), MP_ROM_PTR(&pin_P0_10) }, + { MP_ROM_QSTR(MP_QSTR_USER_BTN), MP_ROM_PTR(&pin_P0_10) }, + { MP_ROM_QSTR(MP_QSTR_P0_10), MP_ROM_PTR(&pin_P0_10) }, + + // Qwiic / STEMMA QT and the JC10 / JC11 castellations. 5.1k pull-ups on board. + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_P0_04) }, + { MP_ROM_QSTR(MP_QSTR_P0_04), MP_ROM_PTR(&pin_P0_04) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_P0_06) }, + { MP_ROM_QSTR(MP_QSTR_P0_06), MP_ROM_PTR(&pin_P0_06) }, + + // Display header (JC23-JC25). Second I2C bus, also with 5.1k pull-ups. + { MP_ROM_QSTR(MP_QSTR_OLED_SDA), MP_ROM_PTR(&pin_P0_24) }, + { MP_ROM_QSTR(MP_QSTR_P0_24), MP_ROM_PTR(&pin_P0_24) }, + { MP_ROM_QSTR(MP_QSTR_OLED_SCL), MP_ROM_PTR(&pin_P0_25) }, + { MP_ROM_QSTR(MP_QSTR_P0_25), MP_ROM_PTR(&pin_P0_25) }, + { MP_ROM_QSTR(MP_QSTR_OLED_12V_EN), MP_ROM_PTR(&pin_P0_23) }, + { MP_ROM_QSTR(MP_QSTR_P0_23), MP_ROM_PTR(&pin_P0_23) }, + + // Castellated UART (JC17 / JC18). The silkscreen labels these from the + // point of view of the attached peripheral, so JC17 (UART_GPS_RX) is our TX. + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_P0_19) }, + { MP_ROM_QSTR(MP_QSTR_P0_19), MP_ROM_PTR(&pin_P0_19) }, + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_P0_20) }, + { MP_ROM_QSTR(MP_QSTR_P0_20), MP_ROM_PTR(&pin_P0_20) }, + + // Switched 3V3 rails brought out on JC14 / JC15, 500 mA each. + { MP_ROM_QSTR(MP_QSTR_PWR_IO1_EN), MP_ROM_PTR(&pin_P0_22) }, + { MP_ROM_QSTR(MP_QSTR_BUZZER_EN), MP_ROM_PTR(&pin_P0_22) }, + { MP_ROM_QSTR(MP_QSTR_P0_22), MP_ROM_PTR(&pin_P0_22) }, + { MP_ROM_QSTR(MP_QSTR_PWR_IO2_EN), MP_ROM_PTR(&pin_P1_01) }, + { MP_ROM_QSTR(MP_QSTR_GPS_EN), MP_ROM_PTR(&pin_P1_01) }, + { MP_ROM_QSTR(MP_QSTR_P1_01), MP_ROM_PTR(&pin_P1_01) }, + + // Semtech LR1121 dual-band LoRa radio. Wired inside the nRFLR1121 module, + // except for IRQ which leaves the module and comes back in on P1.08. + { MP_ROM_QSTR(MP_QSTR_LORA_SCK), MP_ROM_PTR(&pin_P1_13) }, + { MP_ROM_QSTR(MP_QSTR_P1_13), MP_ROM_PTR(&pin_P1_13) }, + { MP_ROM_QSTR(MP_QSTR_LORA_MOSI), MP_ROM_PTR(&pin_P1_14) }, + { MP_ROM_QSTR(MP_QSTR_P1_14), MP_ROM_PTR(&pin_P1_14) }, + { MP_ROM_QSTR(MP_QSTR_LORA_MISO), MP_ROM_PTR(&pin_P1_15) }, + { MP_ROM_QSTR(MP_QSTR_P1_15), MP_ROM_PTR(&pin_P1_15) }, + { MP_ROM_QSTR(MP_QSTR_LORA_CS), MP_ROM_PTR(&pin_P1_12) }, + { MP_ROM_QSTR(MP_QSTR_P1_12), MP_ROM_PTR(&pin_P1_12) }, + { MP_ROM_QSTR(MP_QSTR_LORA_BUSY), MP_ROM_PTR(&pin_P1_11) }, + { MP_ROM_QSTR(MP_QSTR_P1_11), MP_ROM_PTR(&pin_P1_11) }, + { MP_ROM_QSTR(MP_QSTR_LORA_RESET), MP_ROM_PTR(&pin_P1_10) }, + { MP_ROM_QSTR(MP_QSTR_P1_10), MP_ROM_PTR(&pin_P1_10) }, + { MP_ROM_QSTR(MP_QSTR_LORA_IRQ), MP_ROM_PTR(&pin_P1_08) }, + { MP_ROM_QSTR(MP_QSTR_LORA_DIO9), MP_ROM_PTR(&pin_P1_08) }, + { MP_ROM_QSTR(MP_QSTR_P1_08), MP_ROM_PTR(&pin_P1_08) }, + + // Status LEDs. Anodes are tied to 3V3, so drive these low to light them. + { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_P1_04) }, + { MP_ROM_QSTR(MP_QSTR_BLUE_LED), MP_ROM_PTR(&pin_P1_04) }, + { MP_ROM_QSTR(MP_QSTR_P1_04), MP_ROM_PTR(&pin_P1_04) }, + { MP_ROM_QSTR(MP_QSTR_GREEN_LED), MP_ROM_PTR(&pin_P1_03) }, + { MP_ROM_QSTR(MP_QSTR_P1_03), MP_ROM_PTR(&pin_P1_03) }, + + // BQ25185 charger status. CHARGE_STATUS also sinks the red LED, so it reads + // low while charging. Both are open-drain outputs of the charger: inputs only. + { MP_ROM_QSTR(MP_QSTR_CHARGE_STATUS), MP_ROM_PTR(&pin_P1_02) }, + { MP_ROM_QSTR(MP_QSTR_P1_02), MP_ROM_PTR(&pin_P1_02) }, + { MP_ROM_QSTR(MP_QSTR_CHARGE_STAT1), MP_ROM_PTR(&pin_P0_27) }, + { MP_ROM_QSTR(MP_QSTR_P0_27), MP_ROM_PTR(&pin_P0_27) }, + + // Battery sense on AIN7, through an 806k / 1.5M divider. + // VBAT = analog_in.value / 65535 * 3.3 * 1.5373 + { MP_ROM_QSTR(MP_QSTR_VOLTAGE_MONITOR), MP_ROM_PTR(&pin_P0_31) }, + { MP_ROM_QSTR(MP_QSTR_BATTERY), MP_ROM_PTR(&pin_P0_31) }, + { MP_ROM_QSTR(MP_QSTR_P0_31), MP_ROM_PTR(&pin_P0_31) }, + + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_STEMMA_I2C), MP_ROM_PTR(&board_stemma_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_I2C), MP_ROM_PTR(&board_display_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, + { MP_ROM_QSTR(MP_QSTR_LORA_SPI), MP_ROM_PTR(&board_lora_spi_obj) }, + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, +}; + +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table);