From 3b875b8a0e4139c45c4a7b180c65864660b7893e Mon Sep 17 00:00:00 2001 From: Chris Boot Date: Mon, 6 Jan 2025 11:10:31 +0000 Subject: [PATCH 1/4] Add the M5Stack M5Stick C Plus2 board --- .../boards/m5stack_stick_c_plus2/board.c | 98 +++++++++++++++++++ .../m5stack_stick_c_plus2/mpconfigboard.h | 28 ++++++ .../m5stack_stick_c_plus2/mpconfigboard.mk | 12 +++ .../boards/m5stack_stick_c_plus2/pins.c | 78 +++++++++++++++ .../boards/m5stack_stick_c_plus2/sdkconfig | 14 +++ 5 files changed, 230 insertions(+) create mode 100644 ports/espressif/boards/m5stack_stick_c_plus2/board.c create mode 100644 ports/espressif/boards/m5stack_stick_c_plus2/mpconfigboard.h create mode 100644 ports/espressif/boards/m5stack_stick_c_plus2/mpconfigboard.mk create mode 100644 ports/espressif/boards/m5stack_stick_c_plus2/pins.c create mode 100644 ports/espressif/boards/m5stack_stick_c_plus2/sdkconfig diff --git a/ports/espressif/boards/m5stack_stick_c_plus2/board.c b/ports/espressif/boards/m5stack_stick_c_plus2/board.c new file mode 100644 index 0000000000000..3dced581c56b9 --- /dev/null +++ b/ports/espressif/boards/m5stack_stick_c_plus2/board.c @@ -0,0 +1,98 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2023 n0xa, 2025 bootc +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" +#include "mpconfigboard.h" +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/busio/I2C.h" +#include "shared-bindings/fourwire/FourWire.h" +#include "shared-module/displayio/__init__.h" +#include "shared-module/displayio/mipi_constants.h" +#include "shared-bindings/board/__init__.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "driver/gpio.h" +#include "common-hal/microcontroller/Pin.h" + +// display init sequence according to adafruit_st7735r.py library +uint8_t display_init_sequence[] = { + 0x01, 0x80, 0x96, // SWRESET and Delay 150ms + 0x11, 0x80, 0xff, // SLPOUT and Delay + 0xb1, 0x03, 0x01, 0x2C, 0x2D, // _FRMCTR1 + 0xb2, 0x03, 0x01, 0x2C, 0x2D, // _FRMCTR2 + 0xb3, 0x06, 0x01, 0x2C, 0x2D, 0x01, 0x2C, 0x2D, // _FRMCTR3 + 0xb4, 0x01, 0x07, // _INVCTR line inversion + 0xc0, 0x03, 0xa2, 0x02, 0x84, // _PWCTR1 GVDD = 4.7V, 1.0uA + 0xc1, 0x01, 0xc5, // _PWCTR2 VGH=14.7V, VGL=-7.35V + 0xc2, 0x02, 0x0a, 0x00, // _PWCTR3 Opamp current small, Boost frequency + 0xc3, 0x02, 0x8a, 0x2a, + 0xc4, 0x02, 0x8a, 0xee, + 0xc5, 0x01, 0x0e, // _VMCTR1 VCOMH = 4V, VOML = -1.1V + 0x36, 0x01, 0xc8, // MADCTL Rotate display + 0x21, 0x00, // _INVON + 0x3a, 0x01, 0x05, // COLMOD - 16bit color + 0xe0, 0x10, 0x02, 0x1c, 0x07, 0x12, 0x37, 0x32, 0x29, 0x2d, 0x29, 0x25, 0x2B, 0x39, 0x00, 0x01, 0x03, 0x10, // _GMCTRP1 Gamma + 0xe1, 0x10, 0x03, 0x1d, 0x07, 0x06, 0x2E, 0x2C, 0x29, 0x2D, 0x2E, 0x2E, 0x37, 0x3F, 0x00, 0x00, 0x02, 0x10, // _GMCTRN1 + 0x13, 0x80, 0x0a, // _NORON + 0x29, 0x80, 0x64 // _DISPON +}; + +static void display_init(void) { + fourwire_fourwire_obj_t *bus = &allocate_display_bus()->fourwire_bus; + busio_spi_obj_t *spi = &bus->inline_bus; + common_hal_busio_spi_construct(spi, &pin_GPIO13, &pin_GPIO15, NULL, false); + common_hal_busio_spi_never_reset(spi); + + bus->base.type = &fourwire_fourwire_type; + + common_hal_fourwire_fourwire_construct( + bus, + spi, + &pin_GPIO14, // DC + &pin_GPIO5, // CS + &pin_GPIO12, // RST + 10000000, // baudrate + 0, // polarity + 0 // phase + ); + + busdisplay_busdisplay_obj_t *display = &allocate_display()->display; + display->base.type = &busdisplay_busdisplay_type; + + common_hal_busdisplay_busdisplay_construct( + display, + bus, + 135, // width (after rotation) + 240, // height (after rotation) + 40, // column start + 52, // row start + 1, // rotation + 16, // color depth + false, // grayscale + false, // pixels in a byte share a row. Only valid for depths < 8 + 1, // bytes per cell. Only valid for depths < 8 + false, // reverse_pixels_in_byte. Only valid for depths < 8 + true, // reverse_pixels_in_word + MIPI_COMMAND_SET_COLUMN_ADDRESS, // set column command + MIPI_COMMAND_SET_PAGE_ADDRESS, // set row command + MIPI_COMMAND_WRITE_MEMORY_START, // write memory command + display_init_sequence, + sizeof(display_init_sequence), + &pin_GPIO27, // backlight pin + NO_BRIGHTNESS_COMMAND, + 1.0f, // brightness + false, // single_byte_bounds + false, // data_as_commands + true, // auto_refresh + 60, // native_frames_per_second + true, // backlight_on_high + false, // SH1107_addressing + 50000 // backlight pwm frequency + ); +} + +void board_init(void) { + display_init(); +} diff --git a/ports/espressif/boards/m5stack_stick_c_plus2/mpconfigboard.h b/ports/espressif/boards/m5stack_stick_c_plus2/mpconfigboard.h new file mode 100644 index 0000000000000..fd11ec3ff4f05 --- /dev/null +++ b/ports/espressif/boards/m5stack_stick_c_plus2/mpconfigboard.h @@ -0,0 +1,28 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2023 n0xa, 2025 bootc +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Micropython setup + +#define MICROPY_HW_BOARD_NAME "M5Stack Stick C Plus2" +#define MICROPY_HW_MCU_NAME "ESP32" + +#define MICROPY_HW_LED_STATUS (&pin_GPIO19) + +#define CIRCUITPY_BOARD_I2C (2) +#define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO22, .sda = &pin_GPIO21}, \ + {.scl = &pin_GPIO33, .sda = &pin_GPIO32}} + +// For entering safe mode +#define CIRCUITPY_BOOT_BUTTON (&pin_GPIO37) + +// Explanation of how a user got into safe mode +#define BOARD_USER_SAFE_MODE_ACTION MP_ERROR_TEXT("You pressed button A at start up.") + +// UART pins attached to the USB-serial converter chip +#define CIRCUITPY_CONSOLE_UART_TX (&pin_GPIO1) +#define CIRCUITPY_CONSOLE_UART_RX (&pin_GPIO3) diff --git a/ports/espressif/boards/m5stack_stick_c_plus2/mpconfigboard.mk b/ports/espressif/boards/m5stack_stick_c_plus2/mpconfigboard.mk new file mode 100644 index 0000000000000..f4e8b363f682d --- /dev/null +++ b/ports/espressif/boards/m5stack_stick_c_plus2/mpconfigboard.mk @@ -0,0 +1,12 @@ +CIRCUITPY_CREATOR_ID = 0x10151015 +CIRCUITPY_CREATION_ID = 0x0032000C + +IDF_TARGET = esp32 + +CIRCUITPY_ESP_FLASH_MODE = qio +CIRCUITPY_ESP_FLASH_FREQ = 80m +CIRCUITPY_ESP_FLASH_SIZE = 8MB + +CIRCUITPY_ESP_PSRAM_MODE = qio +CIRCUITPY_ESP_PSRAM_FREQ = 80m +CIRCUITPY_ESP_PSRAM_SIZE = 2MB diff --git a/ports/espressif/boards/m5stack_stick_c_plus2/pins.c b/ports/espressif/boards/m5stack_stick_c_plus2/pins.c new file mode 100644 index 0000000000000..7459482176576 --- /dev/null +++ b/ports/espressif/boards/m5stack_stick_c_plus2/pins.c @@ -0,0 +1,78 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2023 n0xa, 2025 bootc +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" +#include "shared-module/displayio/__init__.h" + +CIRCUITPY_BOARD_BUS_SINGLETON(grove_i2c, i2c, 1) + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + // Pin port on the top + { MP_ROM_QSTR(MP_QSTR_G26), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_G36), MP_ROM_PTR(&pin_GPIO36) }, // G36/G25 pin + { MP_ROM_QSTR(MP_QSTR_G25), MP_ROM_PTR(&pin_GPIO25) }, // G36/G25 pin + { MP_ROM_QSTR(MP_QSTR_G0), MP_ROM_PTR(&pin_GPIO0) }, // also PDM_MIC_CLK + + // Grove port on the bottom + { MP_ROM_QSTR(MP_QSTR_G32), MP_ROM_PTR(&pin_GPIO32) }, + { MP_ROM_QSTR(MP_QSTR_GROVE_SDA), MP_ROM_PTR(&pin_GPIO32) }, + { MP_ROM_QSTR(MP_QSTR_G33), MP_ROM_PTR(&pin_GPIO33) }, + { MP_ROM_QSTR(MP_QSTR_GROVE_SCL), MP_ROM_PTR(&pin_GPIO33) }, + { MP_ROM_QSTR(MP_QSTR_GROVE_I2C), MP_ROM_PTR(&board_grove_i2c_obj) }, + + // Buttons + { MP_ROM_QSTR(MP_QSTR_G37), MP_ROM_PTR(&pin_GPIO37) }, + { MP_ROM_QSTR(MP_QSTR_BTN_A), MP_ROM_PTR(&pin_GPIO37) }, + { MP_ROM_QSTR(MP_QSTR_G39), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_BTN_B), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_G35), MP_ROM_PTR(&pin_GPIO35) }, + { MP_ROM_QSTR(MP_QSTR_BTN_C), MP_ROM_PTR(&pin_GPIO35) }, + { MP_ROM_QSTR(MP_QSTR_BTN_PWR), MP_ROM_PTR(&pin_GPIO35) }, // also WAKE + + // Buzzer / Speaker + { MP_ROM_QSTR(MP_QSTR_G2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_SPEAKER), MP_ROM_PTR(&pin_GPIO2) }, + + // Red and IR LED (single pin) + { MP_ROM_QSTR(MP_QSTR_G19), MP_ROM_PTR(&pin_GPIO19) }, + { MP_ROM_QSTR(MP_QSTR_IR_LED), MP_ROM_PTR(&pin_GPIO19) }, + { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO19) }, + + // LCD display + { MP_ROM_QSTR(MP_QSTR_LCD_MOSI), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_LCD_CLK), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_LCD_DC), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_LCD_RST), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_LCD_CS), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_LCD_BL), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display)}, + + // Battery voltage sense + { MP_ROM_QSTR(MP_QSTR_G38), MP_ROM_PTR(&pin_GPIO38) }, + { MP_ROM_QSTR(MP_QSTR_BAT_ADC), MP_ROM_PTR(&pin_GPIO38) }, + + // Microphone + { MP_ROM_QSTR(MP_QSTR_PDM_MIC_CLK), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_G34), MP_ROM_PTR(&pin_GPIO34) }, + { MP_ROM_QSTR(MP_QSTR_PDM_MIC_DATA), MP_ROM_PTR(&pin_GPIO34) }, + + // Internal I2C (IMU and RTC) + { MP_ROM_QSTR(MP_QSTR_G21), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_SYS_SDA), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_G22), MP_ROM_PTR(&pin_GPIO22) }, + { MP_ROM_QSTR(MP_QSTR_SYS_SCL), MP_ROM_PTR(&pin_GPIO22) }, + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + + // Sleep/Wake signal + { MP_ROM_QSTR(MP_QSTR_WAKE), MP_ROM_PTR(&pin_GPIO35) }, + + // Power hold + { MP_ROM_QSTR(MP_QSTR_G4), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_HOLD), MP_ROM_PTR(&pin_GPIO4) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/espressif/boards/m5stack_stick_c_plus2/sdkconfig b/ports/espressif/boards/m5stack_stick_c_plus2/sdkconfig new file mode 100644 index 0000000000000..e962866216039 --- /dev/null +++ b/ports/espressif/boards/m5stack_stick_c_plus2/sdkconfig @@ -0,0 +1,14 @@ +# +# Espressif IoT Development Framework Configuration +# +# +# Component config +# +# +# LWIP +# +# end of LWIP + +# end of Component config + +# end of Espressif IoT Development Framework Configuration From bec912d47912698b653a49844feaec7b16784459 Mon Sep 17 00:00:00 2001 From: Chris Boot Date: Mon, 6 Jan 2025 14:38:53 +0000 Subject: [PATCH 2/4] Safe mode and power-on sequencing In order to stay on the board must set HOLD(GPIO4) high early during boot. When running off battery the power-on button must be held for 2s to power things on, but if it's held for too long the board powers off. We need to disable the 1s safe mode timer in order to give the user a long enough window to set the pins up so the button can be released and the board stay powered on. Safe mode can be entered by holding BTN_A during boot. --- .../boards/m5stack_stick_c_plus2/board.c | 39 +++++++++++++++++++ .../m5stack_stick_c_plus2/mpconfigboard.mk | 5 +++ 2 files changed, 44 insertions(+) diff --git a/ports/espressif/boards/m5stack_stick_c_plus2/board.c b/ports/espressif/boards/m5stack_stick_c_plus2/board.c index 3dced581c56b9..a50e38df09e46 100644 --- a/ports/espressif/boards/m5stack_stick_c_plus2/board.c +++ b/ports/espressif/boards/m5stack_stick_c_plus2/board.c @@ -96,3 +96,42 @@ static void display_init(void) { void board_init(void) { display_init(); } + +bool board_requests_safe_mode(void) { + // Enable HOLD early on + config_pin_as_output_with_level(GPIO_NUM_4, true); + + // Change the buttons to inputs + gpio_set_direction(GPIO_NUM_35, GPIO_MODE_INPUT); + gpio_set_pull_mode(GPIO_NUM_35, GPIO_FLOATING); + gpio_set_direction(GPIO_NUM_37, GPIO_MODE_INPUT); + gpio_set_pull_mode(GPIO_NUM_37, GPIO_FLOATING); + gpio_set_direction(GPIO_NUM_39, GPIO_MODE_INPUT); + gpio_set_pull_mode(GPIO_NUM_39, GPIO_FLOATING); + + // Let the pins settle + mp_hal_delay_ms(1); + + // Safe mode if BTN_A is held at boot (logic low) + return gpio_get_level(GPIO_NUM_37) == 0; // BTN_A +} + +bool espressif_board_reset_pin_number(gpio_num_t pin_number) { + switch (pin_number) { + case GPIO_NUM_4: // HOLD + // HOLD(G4) pin must be set high to avoid a power off when battery powered + config_pin_as_output_with_level(pin_number, true); + return true; + + case GPIO_NUM_35: // BTN_C/PWR + case GPIO_NUM_37: // BTN_A + case GPIO_NUM_39: // BTN_B + gpio_set_direction(pin_number, GPIO_MODE_INPUT); + gpio_set_pull_mode(pin_number, GPIO_FLOATING); + return true; + + default: + return false; + } + return false; +} diff --git a/ports/espressif/boards/m5stack_stick_c_plus2/mpconfigboard.mk b/ports/espressif/boards/m5stack_stick_c_plus2/mpconfigboard.mk index f4e8b363f682d..6ab4670cd7699 100644 --- a/ports/espressif/boards/m5stack_stick_c_plus2/mpconfigboard.mk +++ b/ports/espressif/boards/m5stack_stick_c_plus2/mpconfigboard.mk @@ -10,3 +10,8 @@ CIRCUITPY_ESP_FLASH_SIZE = 8MB CIRCUITPY_ESP_PSRAM_MODE = qio CIRCUITPY_ESP_PSRAM_FREQ = 80m CIRCUITPY_ESP_PSRAM_SIZE = 2MB + +# The safe mode wait gets us very close to the 3s time for the board to shut +# down when BTN_C/PWR is held down. We skip the wait and instead enter safe +# mode if BTN_A is held down during boot with no timeout. +CIRCUITPY_SKIP_SAFE_MODE_WAIT = 1 From 84704889bbfb071b52a746ea89a346f3053465af Mon Sep 17 00:00:00 2001 From: Chris Boot Date: Mon, 6 Jan 2025 21:03:41 +0000 Subject: [PATCH 3/4] espressif: add missing call to board_requests_safe_mode() --- ports/espressif/supervisor/port.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ports/espressif/supervisor/port.c b/ports/espressif/supervisor/port.c index 3be63db0f040a..bd49da891252f 100644 --- a/ports/espressif/supervisor/port.c +++ b/ports/espressif/supervisor/port.c @@ -292,6 +292,10 @@ safe_mode_t port_init(void) { break; } + if (board_requests_safe_mode()) { + return SAFE_MODE_USER; + } + return SAFE_MODE_NONE; } From cbab23eb6b6a183abc9cc17213f245e50066eede Mon Sep 17 00:00:00 2001 From: Chris Boot Date: Mon, 6 Jan 2025 23:45:54 +0000 Subject: [PATCH 4/4] Enable CIRCUITPY_AUDIOBUSIO_PDMIN for the built-in mic --- ports/espressif/boards/m5stack_stick_c_plus2/mpconfigboard.mk | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ports/espressif/boards/m5stack_stick_c_plus2/mpconfigboard.mk b/ports/espressif/boards/m5stack_stick_c_plus2/mpconfigboard.mk index 6ab4670cd7699..ae0736c42085c 100644 --- a/ports/espressif/boards/m5stack_stick_c_plus2/mpconfigboard.mk +++ b/ports/espressif/boards/m5stack_stick_c_plus2/mpconfigboard.mk @@ -15,3 +15,6 @@ CIRCUITPY_ESP_PSRAM_SIZE = 2MB # down when BTN_C/PWR is held down. We skip the wait and instead enter safe # mode if BTN_A is held down during boot with no timeout. CIRCUITPY_SKIP_SAFE_MODE_WAIT = 1 + +# Enable PDMIn for the microphone +CIRCUITPY_AUDIOBUSIO_PDMIN = 1