Skip to content

Commit 0ddc032

Browse files
authored
Merge pull request #10844 from ppsx/rm690b0
QSPI bus driver and Waveshare ESP32-S3 Touch AMOLED 2.41" board support
2 parents 89a726e + 6472aad commit 0ddc032

File tree

41 files changed

+1311
-10
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1311
-10
lines changed

locale/circuitpython.pot

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ msgid "%q contains duplicate pins"
9999
msgstr ""
100100

101101
#: ports/atmel-samd/common-hal/sdioio/SDCard.c
102+
#: ports/espressif/common-hal/qspibus/QSPIBus.c
102103
msgid "%q failure: %d"
103104
msgstr ""
104105

@@ -856,6 +857,7 @@ msgid "Coordinate arrays types have different sizes"
856857
msgstr ""
857858

858859
#: shared-module/usb/core/Device.c
860+
#: ports/espressif/common-hal/qspibus/QSPIBus.c
859861
msgid "Could not allocate DMA capable buffer"
860862
msgstr ""
861863

@@ -958,6 +960,7 @@ msgstr ""
958960

959961
#: ports/espressif/common-hal/busio/SPI.c
960962
#: ports/espressif/common-hal/canio/CAN.c
963+
#: ports/espressif/common-hal/qspibus/QSPIBus.c
961964
msgid "ESP-IDF memory allocation failed"
962965
msgstr ""
963966

@@ -1239,6 +1242,7 @@ msgstr ""
12391242
msgid "Internal define error"
12401243
msgstr ""
12411244

1245+
#: ports/espressif/common-hal/qspibus/QSPIBus.c
12421246
#: shared-bindings/pwmio/PWMOut.c supervisor/shared/settings.c
12431247
msgid "Internal error"
12441248
msgstr ""
@@ -1514,6 +1518,7 @@ msgstr ""
15141518
#: ports/stm/common-hal/busio/UART.c shared-bindings/fourwire/FourWire.c
15151519
#: shared-bindings/i2cdisplaybus/I2CDisplayBus.c
15161520
#: shared-bindings/paralleldisplaybus/ParallelBus.c
1521+
#: shared-bindings/qspibus/QSPIBus.c
15171522
#: shared-module/bitbangio/SPI.c
15181523
msgid "No %q pin"
15191524
msgstr ""
@@ -1743,6 +1748,7 @@ msgid "Operation or feature not supported"
17431748
msgstr ""
17441749

17451750
#: ports/espressif/common-hal/espidf/__init__.c
1751+
#: ports/espressif/common-hal/qspibus/QSPIBus.c
17461752
msgid "Operation timed out"
17471753
msgstr ""
17481754

ports/espressif/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,9 @@ endif
780780
ifneq ($(CIRCUITPY_PARALLELDISPLAYBUS),0)
781781
ESP_IDF_COMPONENTS_LINK += esp_lcd
782782
endif
783+
ifneq ($(CIRCUITPY_QSPIBUS),0)
784+
ESP_IDF_COMPONENTS_LINK += esp_lcd
785+
endif
783786
ifneq ($(CIRCUITPY_USB_DEVICE),0)
784787
ESP_IDF_COMPONENTS_LINK += usb
785788
endif
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// This file is part of the CircuitPython project: https://circuitpython.org
2+
// SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha
3+
// SPDX-License-Identifier: MIT
4+
5+
#include "supervisor/board.h"
6+
#include "mpconfigboard.h"
7+
#include "shared-bindings/microcontroller/Pin.h"
8+
#include "shared-bindings/digitalio/DigitalInOut.h"
9+
10+
#include "shared-bindings/qspibus/QSPIBus.h"
11+
#include "shared-bindings/busdisplay/BusDisplay.h"
12+
#include "shared-module/displayio/__init__.h"
13+
#include "shared-module/displayio/mipi_constants.h"
14+
15+
// RM690B0 AMOLED initialization sequence.
16+
// Format: command byte, length | 0x80 (if delay), data bytes..., [delay ms]
17+
// Based on vendor recommendations, tested with Waveshare 2.41" AMOLED panel.
18+
// Non-const to match upstream common_hal_busdisplay_busdisplay_construct signature.
19+
static uint8_t display_init_sequence[] = {
20+
// Page select and configuration
21+
0xFE, 0x01, 0x20, // Enter user command mode
22+
0x26, 0x01, 0x0A, // Bias setting
23+
0x24, 0x01, 0x80, // Source output control
24+
0xFE, 0x01, 0x13, // Page 13
25+
0xEB, 0x01, 0x0E, // Vendor command
26+
0xFE, 0x01, 0x00, // Return to page 0
27+
// Display configuration
28+
0x3A, 0x01, 0x55, // COLMOD: 16-bit RGB565
29+
0xC2, 0x81, 0x00, 0x0A, // Vendor command + 10ms delay
30+
0x35, 0x00, // Tearing effect line on (no data)
31+
0x51, 0x81, 0x00, 0x0A, // Brightness control + 10ms delay
32+
// Power on
33+
0x11, 0x80, 0x50, // Sleep out + 80ms delay
34+
// Display window (MV=1: CASET→rows, RASET→cols)
35+
0x2A, 0x04, 0x00, 0x10, 0x01, 0xD1, // CASET: 16..465 (450px + 16 offset)
36+
0x2B, 0x04, 0x00, 0x00, 0x02, 0x57, // RASET: 0..599 (600px)
37+
// Enable display
38+
0x29, 0x80, 0x0A, // Display on + 10ms delay
39+
// Memory access: MV=1, ML=1 for landscape
40+
0x36, 0x81, 0x30, 0x0A, // MADCTL + 10ms delay
41+
// Brightness
42+
0x51, 0x01, 0xFF, // Set brightness to maximum
43+
};
44+
45+
void board_init(void) {
46+
// 0. Enable display power before any bus/display init.
47+
digitalio_digitalinout_obj_t power_pin;
48+
power_pin.base.type = &digitalio_digitalinout_type;
49+
common_hal_digitalio_digitalinout_construct(&power_pin, CIRCUITPY_LCD_POWER);
50+
common_hal_digitalio_digitalinout_set_value(&power_pin, true);
51+
common_hal_digitalio_digitalinout_never_reset(&power_pin);
52+
// Allow power rail to settle before reset/init.
53+
mp_hal_delay_ms(200);
54+
55+
// 1. Allocate and construct QSPI bus
56+
qspibus_qspibus_obj_t *bus = &allocate_display_bus_or_raise()->qspi_bus;
57+
bus->base.type = &qspibus_qspibus_type;
58+
59+
common_hal_qspibus_qspibus_construct(bus,
60+
CIRCUITPY_LCD_CLK, // clock
61+
CIRCUITPY_LCD_D0, // data0
62+
CIRCUITPY_LCD_D1, // data1
63+
CIRCUITPY_LCD_D2, // data2
64+
CIRCUITPY_LCD_D3, // data3
65+
CIRCUITPY_LCD_CS, // cs
66+
NULL, // dcx (not used, QSPI uses encoded commands)
67+
CIRCUITPY_LCD_RESET, // reset
68+
40000000); // 40 MHz
69+
70+
// 2. Allocate and construct BusDisplay with RM690B0 init sequence.
71+
// Physical panel: 450 cols × 600 rows.
72+
// MADCTL MV=1 swaps row/col → logical 600×450 landscape.
73+
busdisplay_busdisplay_obj_t *display = &allocate_display_or_raise()->display;
74+
display->base.type = &busdisplay_busdisplay_type;
75+
76+
common_hal_busdisplay_busdisplay_construct(display,
77+
bus,
78+
600, // width (logical, after MV=1 swap)
79+
450, // height (logical, after MV=1 swap)
80+
0, // colstart
81+
16, // rowstart (physical row offset)
82+
0, // rotation
83+
16, // color_depth (RGB565)
84+
false, // grayscale
85+
false, // pixels_in_byte_share_row
86+
1, // bytes_per_cell
87+
false, // reverse_pixels_in_byte
88+
false, // reverse_bytes_in_word
89+
MIPI_COMMAND_SET_COLUMN_ADDRESS, // set_column_command
90+
MIPI_COMMAND_SET_PAGE_ADDRESS, // set_row_command
91+
MIPI_COMMAND_WRITE_MEMORY_START, // write_ram_command
92+
display_init_sequence,
93+
sizeof(display_init_sequence),
94+
NULL, // backlight_pin (AMOLED — no backlight GPIO)
95+
0x51, // brightness_command
96+
1.0f, // brightness
97+
false, // single_byte_bounds
98+
false, // data_as_commands
99+
true, // auto_refresh
100+
60, // native_frames_per_second
101+
true, // backlight_on_high
102+
false, // SH1107_addressing
103+
50000); // backlight_pwm_frequency
104+
}
105+
106+
// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// This file is part of the CircuitPython project: https://circuitpython.org
2+
// SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha
3+
// SPDX-License-Identifier: MIT
4+
5+
#pragma once
6+
7+
#define MICROPY_HW_BOARD_NAME "Waveshare ESP32-S3-Touch-AMOLED-2.41"
8+
#define MICROPY_HW_MCU_NAME "ESP32S3"
9+
10+
// I2C bus - Disabled on boot to avoid conflicts. User must manually initialize I2C.
11+
#define CIRCUITPY_BOARD_I2C (0)
12+
#define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO48, .sda = &pin_GPIO47}}
13+
14+
// Display refresh buffer: 8192 bytes = 2048 uint32_t words on stack.
15+
// ESP32-S3 main task stack is 24KB; verified safe with this board.
16+
#define CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE (8192)
17+
18+
// AMOLED Display (displayio + qspibus path) - initialized in board_init()
19+
#define CIRCUITPY_BOARD_DISPLAY (1)
20+
#define CIRCUITPY_LCD_CS (&pin_GPIO9)
21+
#define CIRCUITPY_LCD_CLK (&pin_GPIO10)
22+
#define CIRCUITPY_LCD_D0 (&pin_GPIO11)
23+
#define CIRCUITPY_LCD_D1 (&pin_GPIO12)
24+
#define CIRCUITPY_LCD_D2 (&pin_GPIO13)
25+
#define CIRCUITPY_LCD_D3 (&pin_GPIO14)
26+
#define CIRCUITPY_LCD_RESET (&pin_GPIO21)
27+
#define CIRCUITPY_LCD_POWER (&pin_GPIO16)
28+
29+
// No default SPI bus — SD card uses SDIO, display uses QSPI.
30+
#define CIRCUITPY_BOARD_SPI (0)
31+
32+
// Default UART bus
33+
#define DEFAULT_UART_BUS_RX (&pin_GPIO44)
34+
#define DEFAULT_UART_BUS_TX (&pin_GPIO43)
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# This file is part of the CircuitPython project: https://circuitpython.org
2+
# SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha
3+
# SPDX-License-Identifier: MIT
4+
5+
USB_VID = 0x303A
6+
USB_PID = 0x8278
7+
USB_MANUFACTURER = "Waveshare"
8+
USB_PRODUCT = "ESP32-S3-Touch-AMOLED-2.41"
9+
10+
IDF_TARGET = esp32s3
11+
12+
# Flash configuration - 16MB QSPI Flash
13+
CIRCUITPY_ESP_FLASH_SIZE = 16MB
14+
CIRCUITPY_ESP_FLASH_MODE = qio
15+
CIRCUITPY_ESP_FLASH_FREQ = 80m
16+
17+
# PSRAM configuration - 8MB Octal PSRAM
18+
CIRCUITPY_ESP_PSRAM_SIZE = 8MB
19+
CIRCUITPY_ESP_PSRAM_MODE = opi
20+
CIRCUITPY_ESP_PSRAM_FREQ = 80m
21+
22+
OPTIMIZATION_FLAGS = -Os
23+
24+
# QSPI bus for RM690B0 AMOLED display
25+
CIRCUITPY_QSPIBUS = 1
26+
CIRCUITPY_PARALLELDISPLAYBUS = 0
27+
28+
# No camera on this board
29+
CIRCUITPY_ESPCAMERA = 0
30+
31+
# Capacitive touch not available; board uses I2C touch controller
32+
CIRCUITPY_TOUCHIO = 0
33+
34+
# SD card via SDMMC interface
35+
CIRCUITPY_SDIOIO = 1
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// This file is part of the CircuitPython project: https://circuitpython.org
2+
// SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha
3+
// SPDX-License-Identifier: MIT
4+
5+
#include "py/obj.h"
6+
#include "py/mphal.h"
7+
#include "shared-bindings/board/__init__.h"
8+
#include "shared-bindings/microcontroller/Pin.h"
9+
#include "shared-module/displayio/__init__.h"
10+
11+
static const mp_rom_map_elem_t board_module_globals_table[] = {
12+
CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS
13+
14+
// =================================================================
15+
// ONBOARD PERIPHERALS - Functional Names
16+
// =================================================================
17+
18+
// Boot/Control/Battery/Display Power
19+
// NOTE: GPIO16 is shared between battery control circuitry and LCD power
20+
// (see CIRCUITPY_QSPIBUS_PANEL_POWER_PIN in mpconfigboard.h).
21+
{ MP_ROM_QSTR(MP_QSTR_BOOT), MP_ROM_PTR(&pin_GPIO0) },
22+
{ MP_ROM_QSTR(MP_QSTR_KEY_BAT), MP_ROM_PTR(&pin_GPIO15) },
23+
{ MP_ROM_QSTR(MP_QSTR_BAT_CONTROL), MP_ROM_PTR(&pin_GPIO16) },
24+
{ MP_ROM_QSTR(MP_QSTR_LCD_POWER), MP_ROM_PTR(&pin_GPIO16) },
25+
{ MP_ROM_QSTR(MP_QSTR_BAT_ADC), MP_ROM_PTR(&pin_GPIO17) },
26+
27+
// I2C Bus (shared by Touch, RTC, IMU, IO Expander)
28+
// NOTE: board.I2C auto-initialization is disabled (CIRCUITPY_BOARD_I2C=0)
29+
// to avoid boot conflicts. Users must manually create I2C bus:
30+
// i2c = busio.I2C(board.SCL, board.SDA)
31+
{ MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO47) },
32+
{ MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO48) },
33+
34+
// Touch Panel (FT6336U on I2C)
35+
// NOTE: TP_INT is routed through the TCA9554 IO expander (EXIO2),
36+
// not a direct ESP32-S3 GPIO — no TOUCH_INT alias is possible here.
37+
{ MP_ROM_QSTR(MP_QSTR_TP_SDA), MP_ROM_PTR(&pin_GPIO47) },
38+
{ MP_ROM_QSTR(MP_QSTR_TP_SCL), MP_ROM_PTR(&pin_GPIO48) },
39+
{ MP_ROM_QSTR(MP_QSTR_TP_RESET), MP_ROM_PTR(&pin_GPIO3) },
40+
{ MP_ROM_QSTR(MP_QSTR_TOUCH_SDA), MP_ROM_PTR(&pin_GPIO47) },
41+
{ MP_ROM_QSTR(MP_QSTR_TOUCH_SCL), MP_ROM_PTR(&pin_GPIO48) },
42+
{ MP_ROM_QSTR(MP_QSTR_TOUCH_RST), MP_ROM_PTR(&pin_GPIO3) },
43+
44+
// RTC (PCF85063 on I2C)
45+
{ MP_ROM_QSTR(MP_QSTR_RTC_SDA), MP_ROM_PTR(&pin_GPIO47) },
46+
{ MP_ROM_QSTR(MP_QSTR_RTC_SCL), MP_ROM_PTR(&pin_GPIO48) },
47+
48+
// IMU (QMI8658 on I2C)
49+
{ MP_ROM_QSTR(MP_QSTR_IMU_SDA), MP_ROM_PTR(&pin_GPIO47) },
50+
{ MP_ROM_QSTR(MP_QSTR_IMU_SCL), MP_ROM_PTR(&pin_GPIO48) },
51+
52+
// I/O Expander (TCA9554 on I2C)
53+
{ MP_ROM_QSTR(MP_QSTR_EXIO_SDA), MP_ROM_PTR(&pin_GPIO47) },
54+
{ MP_ROM_QSTR(MP_QSTR_EXIO_SCL), MP_ROM_PTR(&pin_GPIO48) },
55+
56+
// USB
57+
{ MP_ROM_QSTR(MP_QSTR_USB_D_N), MP_ROM_PTR(&pin_GPIO19) },
58+
{ MP_ROM_QSTR(MP_QSTR_USB_D_P), MP_ROM_PTR(&pin_GPIO20) },
59+
60+
// UART
61+
{ MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) },
62+
{ MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) },
63+
64+
// QSPI Display (RM690B0) - canonical generic LCD aliases.
65+
{ MP_ROM_QSTR(MP_QSTR_LCD_CS), MP_ROM_PTR(&pin_GPIO9) },
66+
{ MP_ROM_QSTR(MP_QSTR_LCD_CLK), MP_ROM_PTR(&pin_GPIO10) },
67+
{ MP_ROM_QSTR(MP_QSTR_LCD_D0), MP_ROM_PTR(&pin_GPIO11) },
68+
{ MP_ROM_QSTR(MP_QSTR_LCD_D1), MP_ROM_PTR(&pin_GPIO12) },
69+
{ MP_ROM_QSTR(MP_QSTR_LCD_D2), MP_ROM_PTR(&pin_GPIO13) },
70+
{ MP_ROM_QSTR(MP_QSTR_LCD_D3), MP_ROM_PTR(&pin_GPIO14) },
71+
{ MP_ROM_QSTR(MP_QSTR_LCD_RESET), MP_ROM_PTR(&pin_GPIO21) },
72+
73+
// Display Aliases
74+
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_CS), MP_ROM_PTR(&pin_GPIO9) },
75+
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_SCK), MP_ROM_PTR(&pin_GPIO10) },
76+
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_D0), MP_ROM_PTR(&pin_GPIO11) },
77+
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_D1), MP_ROM_PTR(&pin_GPIO12) },
78+
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_D2), MP_ROM_PTR(&pin_GPIO13) },
79+
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_D3), MP_ROM_PTR(&pin_GPIO14) },
80+
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_RST), MP_ROM_PTR(&pin_GPIO21) },
81+
82+
// SD Card (SDIO / SDMMC)
83+
{ MP_ROM_QSTR(MP_QSTR_SDIO_CLK), MP_ROM_PTR(&pin_GPIO4) },
84+
{ MP_ROM_QSTR(MP_QSTR_SDIO_CMD), MP_ROM_PTR(&pin_GPIO5) },
85+
{ MP_ROM_QSTR(MP_QSTR_SDIO_D0), MP_ROM_PTR(&pin_GPIO6) },
86+
{ MP_ROM_QSTR(MP_QSTR_SDIO_D3), MP_ROM_PTR(&pin_GPIO2) },
87+
88+
// =================================================================
89+
// GENERAL PURPOSE I/O (IOxx - Espressif Convention)
90+
// =================================================================
91+
// Only pins NOT dedicated to onboard peripherals are exposed here.
92+
// Use functional names above for dedicated pins (e.g., SDA, SD_CS).
93+
94+
{ MP_ROM_QSTR(MP_QSTR_IO0), MP_ROM_PTR(&pin_GPIO0) }, // BOOT button (available when not holding BOOT)
95+
{ MP_ROM_QSTR(MP_QSTR_IO1), MP_ROM_PTR(&pin_GPIO1) }, // Available
96+
{ MP_ROM_QSTR(MP_QSTR_IO3), MP_ROM_PTR(&pin_GPIO3) }, // TP_RESET (available if touch not used)
97+
{ MP_ROM_QSTR(MP_QSTR_IO7), MP_ROM_PTR(&pin_GPIO7) }, // Available
98+
{ MP_ROM_QSTR(MP_QSTR_IO8), MP_ROM_PTR(&pin_GPIO8) }, // Available
99+
{ MP_ROM_QSTR(MP_QSTR_IO18), MP_ROM_PTR(&pin_GPIO18) }, // Available
100+
{ MP_ROM_QSTR(MP_QSTR_IO40), MP_ROM_PTR(&pin_GPIO40) }, // Available
101+
{ MP_ROM_QSTR(MP_QSTR_IO41), MP_ROM_PTR(&pin_GPIO41) }, // Available
102+
{ MP_ROM_QSTR(MP_QSTR_IO42), MP_ROM_PTR(&pin_GPIO42) }, // Available
103+
{ MP_ROM_QSTR(MP_QSTR_IO45), MP_ROM_PTR(&pin_GPIO45) }, // Available
104+
{ MP_ROM_QSTR(MP_QSTR_IO46), MP_ROM_PTR(&pin_GPIO46) }, // Available
105+
106+
// Board display (initialized in board_init)
107+
{ MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display) },
108+
};
109+
MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#
2+
# Configuration file for the Waveshare ESP32-S3 Touch AMOLED 2.41
3+
#

0 commit comments

Comments
 (0)