From 786908208924e3f7aae1ee7fedc06339c9e7f19c Mon Sep 17 00:00:00 2001 From: Liu Zhongwei Date: Fri, 1 Nov 2024 16:59:08 +0800 Subject: [PATCH] feat(ports): add esp32 board ESP32_S3_BOX_3 --- .gitignore | 5 + ports/esp32/CMakeLists.txt | 6 +- ports/esp32/Makefile | 9 + ports/esp32/boards/ESP32_S3_BOX_3/board.json | 24 + ports/esp32/boards/ESP32_S3_BOX_3/board.md | 9 + .../boards/ESP32_S3_BOX_3/mpconfigboard.cmake | 9 + .../boards/ESP32_S3_BOX_3/mpconfigboard.h | 11 + .../ESP32_S3_BOX_3/partitions-16MiB.csv | 7 + .../boards/ESP32_S3_BOX_3/sdkconfig.base | 139 +++++ .../boards/ESP32_S3_BOX_3/sdkconfig.board | 16 + ports/esp32/esp32_common.cmake | 5 + ports/esp32/main_esp32s3_box3/CMakeLists.txt | 13 + .../esp-box-3/.component_hash | 1 + .../esp-box-3/CMakeLists.txt | 13 + .../esp32/main_esp32s3_box3/esp-box-3/Kconfig | 99 ++++ .../esp32/main_esp32s3_box3/esp-box-3/LICENSE | 202 +++++++ .../main_esp32s3_box3/esp-box-3/README.md | 30 ++ .../main_esp32s3_box3/esp-box-3/esp-box-3.c | 500 ++++++++++++++++++ .../esp-box-3/esp-box-3_idf4.c | 75 +++ .../esp-box-3/esp-box-3_idf5.c | 95 ++++ .../esp-box-3/idf_component.yml | 29 + .../esp-box-3/include/bsp/display.h | 123 +++++ .../esp-box-3/include/bsp/esp-box-3.h | 308 +++++++++++ .../esp-box-3/include/bsp/esp-bsp.h | 8 + .../esp-box-3/include/bsp/touch.h | 51 ++ .../esp32/main_esp32s3_box3/esp-box-3/pic.png | Bin 0 -> 146873 bytes .../esp-box-3/priv_include/bsp_err_check.h | 58 ++ .../main_esp32s3_box3/esp32_common.cmake | 244 +++++++++ ports/esp32/main_esp32s3_box3/how_to_build.md | 5 + .../esp32/main_esp32s3_box3/idf_component.yml | 9 + ports/esp32/main_esp32s3_box3/linker.lf | 1 + ports/esp32/main_esp32s3_box3/lvgl_port.c | 181 +++++++ ports/esp32/main_esp32s3_box3/lvgl_port.h | 22 + ports/esp32/main_esp32s3_box3/main.c | 296 +++++++++++ 34 files changed, 2602 insertions(+), 1 deletion(-) create mode 100644 ports/esp32/boards/ESP32_S3_BOX_3/board.json create mode 100644 ports/esp32/boards/ESP32_S3_BOX_3/board.md create mode 100644 ports/esp32/boards/ESP32_S3_BOX_3/mpconfigboard.cmake create mode 100644 ports/esp32/boards/ESP32_S3_BOX_3/mpconfigboard.h create mode 100644 ports/esp32/boards/ESP32_S3_BOX_3/partitions-16MiB.csv create mode 100644 ports/esp32/boards/ESP32_S3_BOX_3/sdkconfig.base create mode 100644 ports/esp32/boards/ESP32_S3_BOX_3/sdkconfig.board create mode 100644 ports/esp32/main_esp32s3_box3/CMakeLists.txt create mode 100644 ports/esp32/main_esp32s3_box3/esp-box-3/.component_hash create mode 100644 ports/esp32/main_esp32s3_box3/esp-box-3/CMakeLists.txt create mode 100644 ports/esp32/main_esp32s3_box3/esp-box-3/Kconfig create mode 100644 ports/esp32/main_esp32s3_box3/esp-box-3/LICENSE create mode 100644 ports/esp32/main_esp32s3_box3/esp-box-3/README.md create mode 100644 ports/esp32/main_esp32s3_box3/esp-box-3/esp-box-3.c create mode 100644 ports/esp32/main_esp32s3_box3/esp-box-3/esp-box-3_idf4.c create mode 100644 ports/esp32/main_esp32s3_box3/esp-box-3/esp-box-3_idf5.c create mode 100644 ports/esp32/main_esp32s3_box3/esp-box-3/idf_component.yml create mode 100644 ports/esp32/main_esp32s3_box3/esp-box-3/include/bsp/display.h create mode 100644 ports/esp32/main_esp32s3_box3/esp-box-3/include/bsp/esp-box-3.h create mode 100644 ports/esp32/main_esp32s3_box3/esp-box-3/include/bsp/esp-bsp.h create mode 100644 ports/esp32/main_esp32s3_box3/esp-box-3/include/bsp/touch.h create mode 100644 ports/esp32/main_esp32s3_box3/esp-box-3/pic.png create mode 100644 ports/esp32/main_esp32s3_box3/esp-box-3/priv_include/bsp_err_check.h create mode 100644 ports/esp32/main_esp32s3_box3/esp32_common.cmake create mode 100644 ports/esp32/main_esp32s3_box3/how_to_build.md create mode 100644 ports/esp32/main_esp32s3_box3/idf_component.yml create mode 100644 ports/esp32/main_esp32s3_box3/linker.lf create mode 100644 ports/esp32/main_esp32s3_box3/lvgl_port.c create mode 100644 ports/esp32/main_esp32s3_box3/lvgl_port.h create mode 100644 ports/esp32/main_esp32s3_box3/main.c diff --git a/.gitignore b/.gitignore index 2d20cb18970e..a2bf5a3f3633 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,8 @@ user.props # MacOS desktop metadata files .DS_Store + +# lvgl +**/**/lv_binding_micropython/* +lextab.py +yacctab.py diff --git a/ports/esp32/CMakeLists.txt b/ports/esp32/CMakeLists.txt index 3fe083002557..2216f87c4cbe 100644 --- a/ports/esp32/CMakeLists.txt +++ b/ports/esp32/CMakeLists.txt @@ -57,7 +57,11 @@ set(SDKCONFIG_DEFAULTS ${CMAKE_BINARY_DIR}/sdkconfig.combined) include($ENV{IDF_PATH}/tools/cmake/project.cmake) # Set the location of the main component for the project (one per target). -list(APPEND EXTRA_COMPONENT_DIRS main_${IDF_TARGET}) +if (MAIN_VARIANT) + list(APPEND EXTRA_COMPONENT_DIRS main_${IDF_TARGET}_${MAIN_VARIANT}) +else() + list(APPEND EXTRA_COMPONENT_DIRS main_${IDF_TARGET}) +endif() # Enable the panic handler wrapper idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=esp_panic_handler" APPEND) diff --git a/ports/esp32/Makefile b/ports/esp32/Makefile index 3df2af4716fd..f986830f8728 100644 --- a/ports/esp32/Makefile +++ b/ports/esp32/Makefile @@ -28,6 +28,10 @@ else BUILD ?= build-$(BOARD) endif +ifneq ($(MAIN_VARIANT),) +BUILD := $(BUILD)-$(MAIN_VARIANT) +endif + # Device serial settings. PORT ?= /dev/ttyUSB0 BAUD ?= 460800 @@ -52,6 +56,10 @@ ifdef BOARD_VARIANT IDFPY_FLAGS += -D MICROPY_BOARD_VARIANT=$(BOARD_VARIANT) endif +ifdef MAIN_VARIANT + IDFPY_FLAGS += -D MAIN_VARIANT=$(MAIN_VARIANT) +endif + ifdef MICROPY_PREVIEW_VERSION_2 IDFPY_FLAGS += -D MICROPY_PREVIEW_VERSION_2=1 endif @@ -63,6 +71,7 @@ define RUN_IDF_PY endef all: + echo $(IDFPY_FLAGS) idf.py $(IDFPY_FLAGS) -B $(BUILD) build || (echo -e $(HELP_BUILD_ERROR); false) @$(PYTHON) makeimg.py \ $(BUILD)/sdkconfig \ diff --git a/ports/esp32/boards/ESP32_S3_BOX_3/board.json b/ports/esp32/boards/ESP32_S3_BOX_3/board.json new file mode 100644 index 000000000000..c9794dba8613 --- /dev/null +++ b/ports/esp32/boards/ESP32_S3_BOX_3/board.json @@ -0,0 +1,24 @@ +{ + "deploy": [ + "../deploy_s3.md" + ], + "docs": "", + "features": [ + "BLE", + "External Flash", + "External RAM", + "WiFi" + ], + "images": [ + "generic_s3.jpg" + ], + "mcu": "esp32s3", + "product": "ESP32-S3", + "thumbnail": "", + "url": "https://www.espressif.com/en/products/modules", + "vendor": "Espressif", + "variants": { + "SPIRAM_OCT": "Support for Octal-SPIRAM", + "FLASH_4M": "4MiB flash" + } +} diff --git a/ports/esp32/boards/ESP32_S3_BOX_3/board.md b/ports/esp32/boards/ESP32_S3_BOX_3/board.md new file mode 100644 index 000000000000..16930c9193c5 --- /dev/null +++ b/ports/esp32/boards/ESP32_S3_BOX_3/board.md @@ -0,0 +1,9 @@ +The following files are firmware that should work on most ESP32-S3-based +boards with 4/8MiB of flash, including WROOM and MINI modules. + +This firmware supports configurations with and without SPIRAM (also known as +PSRAM) and will auto-detect a connected SPIRAM chip at startup and allocate +the MicroPython heap accordingly. However if your board has Octal SPIRAM, then +use the "spiram-oct" variant. + +If your board has 4MiB flash (including ESP32-S3FH4R2 based ones with embedded flash), then use the "flash-4m" build. diff --git a/ports/esp32/boards/ESP32_S3_BOX_3/mpconfigboard.cmake b/ports/esp32/boards/ESP32_S3_BOX_3/mpconfigboard.cmake new file mode 100644 index 000000000000..a5499322561c --- /dev/null +++ b/ports/esp32/boards/ESP32_S3_BOX_3/mpconfigboard.cmake @@ -0,0 +1,9 @@ +set(IDF_TARGET esp32s3) + +set(SDKCONFIG_DEFAULTS + ${SDKCONFIG_IDF_VERSION_SPECIFIC} + boards/sdkconfig.usb + boards/sdkconfig.ble + boards/ESP32_S3_BOX_3/sdkconfig.base + boards/ESP32_S3_BOX_3/sdkconfig.board +) diff --git a/ports/esp32/boards/ESP32_S3_BOX_3/mpconfigboard.h b/ports/esp32/boards/ESP32_S3_BOX_3/mpconfigboard.h new file mode 100644 index 000000000000..c46388b05303 --- /dev/null +++ b/ports/esp32/boards/ESP32_S3_BOX_3/mpconfigboard.h @@ -0,0 +1,11 @@ +#ifndef MICROPY_HW_BOARD_NAME +// Can be set by mpconfigboard.cmake. +#define MICROPY_HW_BOARD_NAME "ESP32-S3-BOX-3" +#endif +#define MICROPY_HW_MCU_NAME "ESP32S3" + +// Enable UART REPL for modules that have an external USB-UART and don't use native USB. +#define MICROPY_HW_ENABLE_UART_REPL (1) + +#define MICROPY_HW_I2C0_SCL (9) +#define MICROPY_HW_I2C0_SDA (8) diff --git a/ports/esp32/boards/ESP32_S3_BOX_3/partitions-16MiB.csv b/ports/esp32/boards/ESP32_S3_BOX_3/partitions-16MiB.csv new file mode 100644 index 000000000000..8bba9d58f6f5 --- /dev/null +++ b/ports/esp32/boards/ESP32_S3_BOX_3/partitions-16MiB.csv @@ -0,0 +1,7 @@ +# Notes: the offset of the partition table itself is set in +# $IDF_PATH/components/partition_table/Kconfig.projbuild. +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x6000, +phy_init, data, phy, , 0x1000, +factory, app, factory, , 9M, +vfs, data, fat, , 6M, diff --git a/ports/esp32/boards/ESP32_S3_BOX_3/sdkconfig.base b/ports/esp32/boards/ESP32_S3_BOX_3/sdkconfig.base new file mode 100644 index 000000000000..c7179b6125a1 --- /dev/null +++ b/ports/esp32/boards/ESP32_S3_BOX_3/sdkconfig.base @@ -0,0 +1,139 @@ +# MicroPython on ESP32, ESP IDF configuration +# The following options override the defaults + +CONFIG_IDF_FIRMWARE_CHIP_ID=0x0000 + +# Compiler options: use -O2 and disable assertions to improve performance +CONFIG_COMPILER_OPTIMIZATION_PERF=y +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE=y + +# Application manager +CONFIG_APP_EXCLUDE_PROJECT_VER_VAR=y +CONFIG_APP_EXCLUDE_PROJECT_NAME_VAR=y + +# Bootloader config +CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y +CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP=y +CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y + +# Change default log level to "ERROR" (instead of "INFO") +CONFIG_LOG_DEFAULT_LEVEL_ERROR=y + +# Set the maximum included log level higher than the default, +# so esp.osdebug() can enable more logging at runtime. +# +# To increase the max log verbosity to Debug or Verbose instead, comment +# CONFIG_LOG_MAXIMUM_LEVEL_INFO=y and uncomment one of the other settings. +# +# If not needed, the next line can be commented entirely to save binary size. +CONFIG_LOG_MAXIMUM_LEVEL_INFO=y +#CONFIG_LOG_MAXIMUM_LEVEL_DEBUG=y +#CONFIG_LOG_MAXIMUM_LEVEL_VERBOSE=y + +# Main XTAL Config +# Only on: ESP32 +CONFIG_XTAL_FREQ_AUTO=y + +# ESP System Settings +# Only on: ESP32, ESP32S3 +CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0=n +CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1=n + +# Power Management +CONFIG_PM_ENABLE=y + +# Memory protection +# This is required to allow allocating IRAM +CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=n + +# FreeRTOS +CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=2 +CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y +CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP=y + +# UDP +CONFIG_LWIP_PPP_SUPPORT=y +CONFIG_LWIP_PPP_PAP_SUPPORT=y +CONFIG_LWIP_PPP_CHAP_SUPPORT=y + +# SSL +# Use 4kiB output buffer instead of default 16kiB +CONFIG_MBEDTLS_ASYMMETRIC_CONTENT_LEN=y +CONFIG_MBEDTLS_HAVE_TIME_DATE=y +CONFIG_MBEDTLS_PLATFORM_TIME_ALT=y +CONFIG_MBEDTLS_HAVE_TIME=y + +# Disable ALPN support as it's not implemented in MicroPython +CONFIG_MBEDTLS_SSL_ALPN=n + +# Disable slow or unused EC curves +CONFIG_MBEDTLS_ECP_DP_BP256R1_ENABLED=n +CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED=n +CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED=n +CONFIG_MBEDTLS_ECP_DP_CURVE25519_ENABLED=n + +# Disable certificate bundle as it's not implemented in MicroPython +CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=n +CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=n + +# Allow mbedTLS to allocate from PSRAM or internal memory +# +# (The ESP-IDF default is internal-only, partly for physical security to prevent +# possible information leakage from unencrypted PSRAM contents on the original +# ESP32 - no PSRAM encryption on that chip. MicroPython doesn't support flash +# encryption and is already storing the Python heap in PSRAM so this isn't a +# significant factor in overall physical security.) +CONFIG_MBEDTLS_DEFAULT_MEM_ALLOC=y + +# ULP coprocessor support +# Only on: ESP32, ESP32S2, ESP32S3 +CONFIG_ULP_COPROC_ENABLED=y +CONFIG_ULP_COPROC_TYPE_FSM=y +CONFIG_ULP_COPROC_RESERVE_MEM=2040 + +# For cmake build +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-4MiB.csv" + +# To reduce iRAM usage +CONFIG_ESP32_WIFI_IRAM_OPT=n +CONFIG_ESP32_WIFI_RX_IRAM_OPT=n +CONFIG_SPI_MASTER_ISR_IN_IRAM=n +CONFIG_SPI_SLAVE_ISR_IN_IRAM=n +CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=n +CONFIG_PERIPH_CTRL_FUNC_IN_IRAM=n + +# Legacy ADC Calibration Configuration +# Only on: ESP32 +CONFIG_ADC_CAL_EFUSE_TP_ENABLE=y +CONFIG_ADC_CAL_EFUSE_VREF_ENABLE=y +CONFIG_ADC_CAL_LUT_ENABLE=y + +# UART Configuration +CONFIG_UART_ISR_IN_IRAM=y + +# IDF 5 deprecated +CONFIG_ADC_SUPPRESS_DEPRECATE_WARN=y +CONFIG_RMT_SUPPRESS_DEPRECATE_WARN=y + +CONFIG_ETH_USE_SPI_ETHERNET=y +CONFIG_ETH_SPI_ETHERNET_W5500=y +CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL=y +CONFIG_ETH_SPI_ETHERNET_DM9051=y + +# Using newlib "nano" formatting saves size on SoCs where "nano" formatting +# functions are in ROM. Note some newer chips (c2,c6) have "full" newlib +# formatting in ROM instead and should override this, check +# ESP_ROM_HAS_NEWLIB_NANO_FORMAT. +CONFIG_NEWLIB_NANO_FORMAT=y + +# IRAM/DRAM split protection is a memory protection feature on some parts +# that support SOC_CPU_IDRAM_SPLIT_USING_PMP, eg. C2, C5, C6, H2 +# Due to limitations in the PMP system this feature breaks native emitters +# so is disabled by default. +CONFIG_ESP_SYSTEM_PMP_IDRAM_SPLIT=n + +# Further limit total sockets in TIME-WAIT when there are many short-lived +# connections. +CONFIG_LWIP_MAX_ACTIVE_TCP=12 diff --git a/ports/esp32/boards/ESP32_S3_BOX_3/sdkconfig.board b/ports/esp32/boards/ESP32_S3_BOX_3/sdkconfig.board new file mode 100644 index 000000000000..1a5c838a77c1 --- /dev/null +++ b/ports/esp32/boards/ESP32_S3_BOX_3/sdkconfig.board @@ -0,0 +1,16 @@ +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="boards/ESP32_S3_BOX_3/partitions-16MiB.csv" +CONFIG_COMPILER_OPTIMIZATION_PERF=y +CONFIG_SPIRAM=y +CONFIG_SPIRAM_MODE_OCT=y +CONFIG_SPIRAM_FETCH_INSTRUCTIONS=y +CONFIG_SPIRAM_RODATA=y +CONFIG_SPIRAM_SPEED_80M=y +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y +CONFIG_ESP_CONSOLE_UART_CUSTOM=y +CONFIG_ESP_CONSOLE_UART_BAUDRATE=2000000 +CONFIG_FREERTOS_HZ=1000 +CONFIG_ESP_BROOKESIA_MEMORY_USE_CUSTOM=y +CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y diff --git a/ports/esp32/esp32_common.cmake b/ports/esp32/esp32_common.cmake index e928fb439da5..35e370f4558b 100644 --- a/ports/esp32/esp32_common.cmake +++ b/ports/esp32/esp32_common.cmake @@ -21,6 +21,11 @@ if(NOT CMAKE_BUILD_EARLY_EXPANSION) include(${MICROPY_DIR}/py/usermod.cmake) include(${MICROPY_DIR}/extmod/extmod.cmake) + + idf_build_get_property(component_targets __COMPONENT_TARGETS) + string(REPLACE "___idf_lvgl" "" component_targets "${component_targets}") + idf_build_set_property(__COMPONENT_TARGETS "${component_targets}") + message("Component targets: ${component_targets}") endif() list(APPEND MICROPY_QSTRDEFS_PORT diff --git a/ports/esp32/main_esp32s3_box3/CMakeLists.txt b/ports/esp32/main_esp32s3_box3/CMakeLists.txt new file mode 100644 index 000000000000..b0c3048025ad --- /dev/null +++ b/ports/esp32/main_esp32s3_box3/CMakeLists.txt @@ -0,0 +1,13 @@ +# Set location of base MicroPython directory. +if(NOT MICROPY_DIR) + get_filename_component(MICROPY_DIR ${CMAKE_CURRENT_LIST_DIR}/../../.. ABSOLUTE) +endif() + +# Set location of the ESP32 port directory. +if(NOT MICROPY_PORT_DIR) + get_filename_component(MICROPY_PORT_DIR ${MICROPY_DIR}/ports/esp32 ABSOLUTE) +endif() + +set(MICROPY_PY_TINYUSB ON) + +include(${CMAKE_CURRENT_LIST_DIR}/esp32_common.cmake) diff --git a/ports/esp32/main_esp32s3_box3/esp-box-3/.component_hash b/ports/esp32/main_esp32s3_box3/esp-box-3/.component_hash new file mode 100644 index 000000000000..6eac47da7ffe --- /dev/null +++ b/ports/esp32/main_esp32s3_box3/esp-box-3/.component_hash @@ -0,0 +1 @@ +9afc6dd09f779dfc9f7b1b15f129b5dbed6235a20c5991a4d30304c8705a48f4 \ No newline at end of file diff --git a/ports/esp32/main_esp32s3_box3/esp-box-3/CMakeLists.txt b/ports/esp32/main_esp32s3_box3/esp-box-3/CMakeLists.txt new file mode 100644 index 000000000000..c103d05a42af --- /dev/null +++ b/ports/esp32/main_esp32s3_box3/esp-box-3/CMakeLists.txt @@ -0,0 +1,13 @@ +#IDF version is less than IDF5.0 +if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_LESS "5.0") + set(SRC_VER "esp-box-3_idf4.c") +else() + set(SRC_VER "esp-box-3_idf5.c") +endif() + +idf_component_register( + SRCS "esp-box-3.c" ${SRC_VER} + INCLUDE_DIRS "include" + PRIV_INCLUDE_DIRS "priv_include" + REQUIRES driver spiffs esp_lcd +) diff --git a/ports/esp32/main_esp32s3_box3/esp-box-3/Kconfig b/ports/esp32/main_esp32s3_box3/esp-box-3/Kconfig new file mode 100644 index 000000000000..be779930f849 --- /dev/null +++ b/ports/esp32/main_esp32s3_box3/esp-box-3/Kconfig @@ -0,0 +1,99 @@ +menu "Board Support Package(ESP-BOX-3)" + + config BSP_ERROR_CHECK + bool "Enable error check in BSP" + default y + help + Error check assert the application before returning the error code. + + menu "I2C" + config BSP_I2C_NUM + int "I2C peripheral index" + default 1 + range 0 1 + help + ESP32S3 has two I2C peripherals, pick the one you want to use. + + config BSP_I2C_FAST_MODE + bool "Enable I2C fast mode" + default y + help + I2C has two speed modes: normal (100kHz) and fast (400kHz). + + config BSP_I2C_CLK_SPEED_HZ + int + default 400000 if BSP_I2C_FAST_MODE + default 100000 + endmenu + + menu "SPIFFS - Virtual File System" + config BSP_SPIFFS_FORMAT_ON_MOUNT_FAIL + bool "Format SPIFFS if mounting fails" + default n + help + Format SPIFFS if it fails to mount the filesystem. + + config BSP_SPIFFS_MOUNT_POINT + string "SPIFFS mount point" + default "/spiffs" + help + Mount point of SPIFFS in the Virtual File System. + + config BSP_SPIFFS_PARTITION_LABEL + string "Partition label of SPIFFS" + default "storage" + help + Partition label which stores SPIFFS. + + config BSP_SPIFFS_MAX_FILES + int "Max files supported for SPIFFS VFS" + default 5 + help + Supported max files for SPIFFS in the Virtual File System. + endmenu + + menu "SD card - Virtual File System" + config BSP_SD_FORMAT_ON_MOUNT_FAIL + bool "Format SD card if mounting fails" + default n + help + The SDMMC host will format (FAT) the SD card if it fails to mount the filesystem. + + config BSP_SD_MOUNT_POINT + string "SD card mount point" + default "/sdcard" + help + Mount point of the SD card in the Virtual File System + + endmenu + + menu "Display" + config BSP_DISPLAY_BRIGHTNESS_LEDC_CH + int "LEDC channel index" + default 1 + range 0 7 + help + LEDC channel is used to generate PWM signal that controls display brightness. + Set LEDC index that should be used. + + config BSP_LCD_DRAW_BUF_HEIGHT + int "LCD framebuf height" + default 100 + range 10 240 + help + Framebuf is used for lvgl rendering output. + + config BSP_LCD_DRAW_BUF_DOUBLE + bool "LCD double framebuf" + default n + help + Whether to enable double framebuf. + endmenu + + config BSP_I2S_NUM + int "I2S peripheral index" + default 1 + range 0 1 + help + ESP32S3 has two I2S peripherals, pick the one you want to use. +endmenu diff --git a/ports/esp32/main_esp32s3_box3/esp-box-3/LICENSE b/ports/esp32/main_esp32s3_box3/esp-box-3/LICENSE new file mode 100644 index 000000000000..d64569567334 --- /dev/null +++ b/ports/esp32/main_esp32s3_box3/esp-box-3/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/ports/esp32/main_esp32s3_box3/esp-box-3/README.md b/ports/esp32/main_esp32s3_box3/esp-box-3/README.md new file mode 100644 index 000000000000..a0346e61e226 --- /dev/null +++ b/ports/esp32/main_esp32s3_box3/esp-box-3/README.md @@ -0,0 +1,30 @@ +# BSP: ESP-BOX-3 + +[![Component Registry](https://components.espressif.com/components/espressif/esp-box-3/badge.svg)](https://components.espressif.com/components/espressif/esp-box-3) + +### Overview + +ESP32-S3-BOX-3 is an AI voice development kit that is based on Espressif’s ESP32-S3 Wi-Fi + Bluetooth 5 (LE) SoC, with AI capabilities. In addition to ESP32-S3’s 512KB SRAM, + +ESP32-S3-BOX-3 comes with 16MB of QSPI flash and 16MB of Octal PSRAM. ESP32-S3-BOX-3 is also equipped with a variety of peripherals, such as a 2.4-inch display with a 320x240 resolution, a capacitive touch screen, a dual microphone, a speaker, and two Pmod™-compatible headers which allow for the extensibility of the hardware. + +ESP32-S3-BOX-3 also uses a Type-C USB connector that provides 5 V of power input, while also supporting serial and JTAG debugging, as well as a programming interface; all through the same connector. + +* [Hardware Reference](https://github.com/espressif/esp-box/tree/master/hardware) + +![image](pic.png) + + +### Capabilities and dependencies +| Capability | Available | Component |Version| +|-------------|------------------|----------------------------------------------------------------------------------------------------------|-------| +| DISPLAY |:heavy_check_mark:| [espressif/esp_lcd_ili9341](https://components.espressif.com/components/espressif/esp_lcd_ili9341) | ^1 | +| LVGL_PORT |:heavy_check_mark:| [espressif/esp_lvgl_port](https://components.espressif.com/components/espressif/esp_lvgl_port) | ^2 | +| TOUCH |:heavy_check_mark:|[espressif/esp_lcd_touch_gt911](https://components.espressif.com/components/espressif/esp_lcd_touch_gt911)| ^1 | +| BUTTONS |:heavy_check_mark:| [espressif/button](https://components.espressif.com/components/espressif/button) | >=2.5 | +| AUDIO |:heavy_check_mark:| [espressif/esp_codec_dev](https://components.espressif.com/components/espressif/esp_codec_dev) |^1,<1.2| +|AUDIO_SPEAKER|:heavy_check_mark:| | | +| AUDIO_MIC |:heavy_check_mark:| | | +| SDCARD |:heavy_check_mark:| idf |>=4.4.5| +| IMU |:heavy_check_mark:| [espressif/icm42670](https://components.espressif.com/components/espressif/icm42670) | ^1 | + diff --git a/ports/esp32/main_esp32s3_box3/esp-box-3/esp-box-3.c b/ports/esp32/main_esp32s3_box3/esp-box-3/esp-box-3.c new file mode 100644 index 000000000000..e0e88f6042a4 --- /dev/null +++ b/ports/esp32/main_esp32s3_box3/esp-box-3/esp-box-3.c @@ -0,0 +1,500 @@ +/* + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "driver/gpio.h" +#include "driver/ledc.h" +#include "driver/spi_master.h" +#include "esp_err.h" +#include "esp_log.h" +#include "esp_check.h" +#include "esp_spiffs.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_panel_vendor.h" +#include "esp_lcd_panel_ops.h" + +// #include "iot_button.h" +#include "bsp/esp-box-3.h" +#include "bsp/display.h" +#include "bsp/touch.h" +#include "esp_lcd_touch_tt21100.h" +#include "esp_lcd_touch_gt911.h" +#include "esp_lcd_ili9341.h" +#include "bsp_err_check.h" +#include "esp_codec_dev_defaults.h" + +static const char *TAG = "ESP-BOX-3"; + +/** @cond */ +// _Static_assert(CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0, "Touch buttons must be supported for this BSP"); +/** @endcond */ + +static const ili9341_lcd_init_cmd_t vendor_specific_init[] = { + {0xC8, (uint8_t []){0xFF, 0x93, 0x42}, 3, 0}, + {0xC0, (uint8_t []){0x0E, 0x0E}, 2, 0}, + {0xC5, (uint8_t []){0xD0}, 1, 0}, + {0xC1, (uint8_t []){0x02}, 1, 0}, + {0xB4, (uint8_t []){0x02}, 1, 0}, + {0xE0, (uint8_t []){0x00, 0x03, 0x08, 0x06, 0x13, 0x09, 0x39, 0x39, 0x48, 0x02, 0x0a, 0x08, 0x17, 0x17, 0x0F}, 15, 0}, + {0xE1, (uint8_t []){0x00, 0x28, 0x29, 0x01, 0x0d, 0x03, 0x3f, 0x33, 0x52, 0x04, 0x0f, 0x0e, 0x37, 0x38, 0x0F}, 15, 0}, + + {0xB1, (uint8_t []){00, 0x1B}, 2, 0}, + {0x36, (uint8_t []){0x08}, 1, 0}, + {0x3A, (uint8_t []){0x55}, 1, 0}, + {0xB7, (uint8_t []){0x06}, 1, 0}, + + {0x11, (uint8_t []){0}, 0x80, 0}, + {0x29, (uint8_t []){0}, 0x80, 0}, + + {0, (uint8_t []){0}, 0xff, 0}, +}; + +static esp_lcd_touch_handle_t tp; // LCD touch handle +static esp_lcd_panel_handle_t panel_handle = NULL; + +static bool i2c_initialized = false; + +// // This is just a wrapper to get function signature for espressif/button API callback +// static uint8_t bsp_get_main_button(void *param); +// static esp_err_t bsp_init_main_button(void *param); + +// static const button_config_t bsp_button_config[BSP_BUTTON_NUM] = { +// { +// .type = BUTTON_TYPE_GPIO, +// .gpio_button_config.gpio_num = BSP_BUTTON_CONFIG_IO, +// .gpio_button_config.active_level = 0, +// }, +// { +// .type = BUTTON_TYPE_GPIO, +// .gpio_button_config.gpio_num = BSP_BUTTON_MUTE_IO, +// .gpio_button_config.active_level = 0, +// }, +// { +// .type = BUTTON_TYPE_CUSTOM, +// .custom_button_config.button_custom_init = bsp_init_main_button, +// .custom_button_config.button_custom_get_key_value = bsp_get_main_button, +// .custom_button_config.button_custom_deinit = NULL, +// .custom_button_config.active_level = 1, +// .custom_button_config.priv = (void *) BSP_BUTTON_MAIN, +// } +// }; + +esp_err_t bsp_i2c_init(void) +{ + /* I2C was initialized before */ + if (i2c_initialized) { + return ESP_OK; + } + + const i2c_config_t i2c_conf = { + .mode = I2C_MODE_MASTER, + .sda_io_num = BSP_I2C_SDA, + .sda_pullup_en = GPIO_PULLUP_DISABLE, + .scl_io_num = BSP_I2C_SCL, + .scl_pullup_en = GPIO_PULLUP_DISABLE, + .master.clk_speed = CONFIG_BSP_I2C_CLK_SPEED_HZ + }; + BSP_ERROR_CHECK_RETURN_ERR(i2c_param_config(BSP_I2C_NUM, &i2c_conf)); + BSP_ERROR_CHECK_RETURN_ERR(i2c_driver_install(BSP_I2C_NUM, i2c_conf.mode, 0, 0, 0)); + + i2c_initialized = true; + + return ESP_OK; +} + +esp_err_t bsp_i2c_deinit(void) +{ + BSP_ERROR_CHECK_RETURN_ERR(i2c_driver_delete(BSP_I2C_NUM)); + i2c_initialized = false; + return ESP_OK; +} + +static esp_err_t bsp_i2c_device_probe(uint8_t addr) +{ + esp_err_t ret = ESP_ERR_NOT_FOUND; + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (addr << 1) | I2C_MASTER_WRITE, true); + i2c_master_stop(cmd); + if (i2c_master_cmd_begin(BSP_I2C_NUM, cmd, 1000) == ESP_OK) { + ret = ESP_OK; + } + i2c_cmd_link_delete(cmd); + return ret; +} + +esp_err_t bsp_spiffs_mount(void) +{ + esp_vfs_spiffs_conf_t conf = { + .base_path = CONFIG_BSP_SPIFFS_MOUNT_POINT, + .partition_label = CONFIG_BSP_SPIFFS_PARTITION_LABEL, + .max_files = CONFIG_BSP_SPIFFS_MAX_FILES, +#ifdef CONFIG_BSP_SPIFFS_FORMAT_ON_MOUNT_FAIL + .format_if_mount_failed = true, +#else + .format_if_mount_failed = false, +#endif + }; + + esp_err_t ret_val = esp_vfs_spiffs_register(&conf); + + BSP_ERROR_CHECK_RETURN_ERR(ret_val); + + size_t total = 0, used = 0; + ret_val = esp_spiffs_info(conf.partition_label, &total, &used); + if (ret_val != ESP_OK) { + ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret_val)); + } else { + ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used); + } + + return ret_val; +} + +esp_err_t bsp_spiffs_unmount(void) +{ + return esp_vfs_spiffs_unregister(CONFIG_BSP_SPIFFS_PARTITION_LABEL); +} + +esp_codec_dev_handle_t bsp_audio_codec_speaker_init(void) +{ + const audio_codec_data_if_t *i2s_data_if = bsp_audio_get_codec_itf(); + if (i2s_data_if == NULL) { + /* Initilize I2C */ + BSP_ERROR_CHECK_RETURN_ERR(bsp_i2c_init()); + /* Configure I2S peripheral and Power Amplifier */ + BSP_ERROR_CHECK_RETURN_ERR(bsp_audio_init(NULL)); + i2s_data_if = bsp_audio_get_codec_itf(); + } + assert(i2s_data_if); + + const audio_codec_gpio_if_t *gpio_if = audio_codec_new_gpio(); + + audio_codec_i2c_cfg_t i2c_cfg = { + .port = BSP_I2C_NUM, + .addr = ES8311_CODEC_DEFAULT_ADDR, + }; + const audio_codec_ctrl_if_t *i2c_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg); + BSP_NULL_CHECK(i2c_ctrl_if, NULL); + + esp_codec_dev_hw_gain_t gain = { + .pa_voltage = 5.0, + .codec_dac_voltage = 3.3, + }; + + es8311_codec_cfg_t es8311_cfg = { + .ctrl_if = i2c_ctrl_if, + .gpio_if = gpio_if, + .codec_mode = ESP_CODEC_DEV_WORK_MODE_DAC, + .pa_pin = BSP_POWER_AMP_IO, + .pa_reverted = false, + .master_mode = false, + .use_mclk = true, + .digital_mic = false, + .invert_mclk = false, + .invert_sclk = false, + .hw_gain = gain, + }; + const audio_codec_if_t *es8311_dev = es8311_codec_new(&es8311_cfg); + BSP_NULL_CHECK(es8311_dev, NULL); + + esp_codec_dev_cfg_t codec_dev_cfg = { + .dev_type = ESP_CODEC_DEV_TYPE_OUT, + .codec_if = es8311_dev, + .data_if = i2s_data_if, + }; + return esp_codec_dev_new(&codec_dev_cfg); +} + +esp_codec_dev_handle_t bsp_audio_codec_microphone_init(void) +{ + const audio_codec_data_if_t *i2s_data_if = bsp_audio_get_codec_itf(); + if (i2s_data_if == NULL) { + /* Initilize I2C */ + BSP_ERROR_CHECK_RETURN_ERR(bsp_i2c_init()); + /* Configure I2S peripheral and Power Amplifier */ + BSP_ERROR_CHECK_RETURN_ERR(bsp_audio_init(NULL)); + i2s_data_if = bsp_audio_get_codec_itf(); + } + assert(i2s_data_if); + + audio_codec_i2c_cfg_t i2c_cfg = { + .port = BSP_I2C_NUM, + .addr = ES7210_CODEC_DEFAULT_ADDR, + }; + const audio_codec_ctrl_if_t *i2c_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg); + BSP_NULL_CHECK(i2c_ctrl_if, NULL); + + es7210_codec_cfg_t es7210_cfg = { + .ctrl_if = i2c_ctrl_if, + }; + const audio_codec_if_t *es7210_dev = es7210_codec_new(&es7210_cfg); + BSP_NULL_CHECK(es7210_dev, NULL); + + esp_codec_dev_cfg_t codec_es7210_dev_cfg = { + .dev_type = ESP_CODEC_DEV_TYPE_IN, + .codec_if = es7210_dev, + .data_if = i2s_data_if, + }; + return esp_codec_dev_new(&codec_es7210_dev_cfg); +} + +// Bit number used to represent command and parameter +#define LCD_CMD_BITS 8 +#define LCD_PARAM_BITS 8 +#define LCD_LEDC_CH CONFIG_BSP_DISPLAY_BRIGHTNESS_LEDC_CH + +esp_err_t bsp_display_brightness_init(void) +{ + // Setup LEDC peripheral for PWM backlight control + const ledc_channel_config_t LCD_backlight_channel = { + .gpio_num = BSP_LCD_BACKLIGHT, + .speed_mode = LEDC_LOW_SPEED_MODE, + .channel = LCD_LEDC_CH, + .intr_type = LEDC_INTR_DISABLE, + .timer_sel = 1, + .duty = 0, + .hpoint = 0 + }; + const ledc_timer_config_t LCD_backlight_timer = { + .speed_mode = LEDC_LOW_SPEED_MODE, + .duty_resolution = LEDC_TIMER_10_BIT, + .timer_num = 1, + .freq_hz = 5000, + .clk_cfg = LEDC_AUTO_CLK + }; + + BSP_ERROR_CHECK_RETURN_ERR(ledc_timer_config(&LCD_backlight_timer)); + BSP_ERROR_CHECK_RETURN_ERR(ledc_channel_config(&LCD_backlight_channel)); + + return ESP_OK; +} + +esp_err_t bsp_display_brightness_set(int brightness_percent) +{ + if (brightness_percent > 100) { + brightness_percent = 100; + } + if (brightness_percent < 0) { + brightness_percent = 0; + } + + ESP_LOGI(TAG, "Setting LCD backlight: %d%%", brightness_percent); + uint32_t duty_cycle = (1023 * brightness_percent) / 100; // LEDC resolution set to 10bits, thus: 100% = 1023 + BSP_ERROR_CHECK_RETURN_ERR(ledc_set_duty(LEDC_LOW_SPEED_MODE, LCD_LEDC_CH, duty_cycle)); + BSP_ERROR_CHECK_RETURN_ERR(ledc_update_duty(LEDC_LOW_SPEED_MODE, LCD_LEDC_CH)); + + return ESP_OK; +} + +esp_err_t bsp_display_backlight_off(void) +{ + return bsp_display_brightness_set(0); +} + +esp_err_t bsp_display_backlight_on(void) +{ + return bsp_display_brightness_set(100); +} + +static esp_err_t bsp_lcd_enter_sleep(void) +{ + assert(panel_handle); + return esp_lcd_panel_disp_on_off(panel_handle, false); +} + +static esp_err_t bsp_lcd_exit_sleep(void) +{ + assert(panel_handle); + return esp_lcd_panel_disp_on_off(panel_handle, true); +} + +esp_err_t bsp_display_new(const bsp_display_config_t *config, esp_lcd_panel_handle_t *ret_panel, esp_lcd_panel_io_handle_t *ret_io) +{ + esp_err_t ret = ESP_OK; + assert(config != NULL && config->max_transfer_sz > 0); + + ESP_RETURN_ON_ERROR(bsp_display_brightness_init(), TAG, "Brightness init failed"); + + /* Initilize I2C */ + BSP_ERROR_CHECK_RETURN_ERR(bsp_i2c_init()); + + ESP_LOGD(TAG, "Initialize SPI bus"); + const spi_bus_config_t buscfg = { + .sclk_io_num = BSP_LCD_PCLK, + .mosi_io_num = BSP_LCD_DATA0, + .miso_io_num = GPIO_NUM_NC, + .quadwp_io_num = GPIO_NUM_NC, + .quadhd_io_num = GPIO_NUM_NC, + .max_transfer_sz = config->max_transfer_sz, + }; + ESP_RETURN_ON_ERROR(spi_bus_initialize(BSP_LCD_SPI_NUM, &buscfg, SPI_DMA_CH_AUTO), TAG, "SPI init failed"); + + ESP_LOGD(TAG, "Install panel IO"); + const esp_lcd_panel_io_spi_config_t io_config = { + .dc_gpio_num = BSP_LCD_DC, + .cs_gpio_num = BSP_LCD_CS, + .pclk_hz = BSP_LCD_PIXEL_CLOCK_HZ, + .lcd_cmd_bits = LCD_CMD_BITS, + .lcd_param_bits = LCD_PARAM_BITS, + .spi_mode = 0, + .trans_queue_depth = 10, + }; + ESP_GOTO_ON_ERROR(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)BSP_LCD_SPI_NUM, &io_config, ret_io), err, TAG, "New panel IO failed"); + + ESP_LOGD(TAG, "Install LCD driver"); + const ili9341_vendor_config_t vendor_config = { + .init_cmds = &vendor_specific_init[0], + .init_cmds_size = sizeof(vendor_specific_init) / sizeof(ili9341_lcd_init_cmd_t), + }; + + esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = BSP_LCD_RST, // Shared with Touch reset + .flags.reset_active_high = 1, + .color_space = BSP_LCD_COLOR_SPACE, + .bits_per_pixel = BSP_LCD_BITS_PER_PIXEL, + }; + + if (ESP_OK == bsp_i2c_device_probe(ESP_LCD_TOUCH_IO_I2C_TT21100_ADDRESS)) { + ESP_GOTO_ON_ERROR(esp_lcd_new_panel_st7789(*ret_io, (const esp_lcd_panel_dev_config_t *)&panel_config, ret_panel), err, TAG, "New panel failed"); + } else { + panel_config.vendor_config = (void *)&vendor_config; + ESP_GOTO_ON_ERROR(esp_lcd_new_panel_ili9341(*ret_io, (const esp_lcd_panel_dev_config_t *)&panel_config, ret_panel), err, TAG, "New panel failed"); + } + + esp_lcd_panel_reset(*ret_panel); + esp_lcd_panel_init(*ret_panel); + esp_lcd_panel_mirror(*ret_panel, true, true); + return ret; + +err: + if (*ret_panel) { + esp_lcd_panel_del(*ret_panel); + } + if (*ret_io) { + esp_lcd_panel_io_del(*ret_io); + } + spi_bus_free(BSP_LCD_SPI_NUM); + return ret; +} + +__attribute__((weak)) esp_err_t esp_lcd_touch_enter_sleep(esp_lcd_touch_handle_t tp) +{ + ESP_LOGE(TAG, "Sleep mode not supported!"); + return ESP_FAIL; +} + +__attribute__((weak)) esp_err_t esp_lcd_touch_exit_sleep(esp_lcd_touch_handle_t tp) +{ + ESP_LOGE(TAG, "Sleep mode not supported!"); + return ESP_FAIL; +} + +static esp_err_t bsp_touch_enter_sleep(void) +{ + assert(tp); + return esp_lcd_touch_enter_sleep(tp); +} + +static esp_err_t bsp_touch_exit_sleep(void) +{ + assert(tp); + return esp_lcd_touch_exit_sleep(tp); +} + +esp_err_t bsp_touch_new(const bsp_touch_config_t *config, esp_lcd_touch_handle_t *ret_touch) +{ + /* Initilize I2C */ + BSP_ERROR_CHECK_RETURN_ERR(bsp_i2c_init()); + + /* Initialize touch */ + esp_lcd_touch_config_t tp_cfg = { + .x_max = BSP_LCD_H_RES, + .y_max = BSP_LCD_V_RES, + .rst_gpio_num = GPIO_NUM_NC, // Shared with LCD reset + .int_gpio_num = BSP_LCD_TOUCH_INT, + .levels = { + .reset = 0, + .interrupt = 0, + }, + .flags = { + .swap_xy = 0, + .mirror_x = 0, + .mirror_y = 0, + }, + }; + esp_lcd_panel_io_handle_t tp_io_handle = NULL; + esp_lcd_panel_io_i2c_config_t tp_io_config; + + if (ESP_OK == bsp_i2c_device_probe(ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS)) { + esp_lcd_panel_io_i2c_config_t config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG(); + memcpy(&tp_io_config, &config, sizeof(config)); + } else if (ESP_OK == bsp_i2c_device_probe(ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS_BACKUP)) { + esp_lcd_panel_io_i2c_config_t config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG(); + config.dev_addr = ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS_BACKUP; + memcpy(&tp_io_config, &config, sizeof(config)); + } else if (ESP_OK == bsp_i2c_device_probe(ESP_LCD_TOUCH_IO_I2C_TT21100_ADDRESS)) { + esp_lcd_panel_io_i2c_config_t config = ESP_LCD_TOUCH_IO_I2C_TT21100_CONFIG(); + memcpy(&tp_io_config, &config, sizeof(config)); + tp_cfg.flags.mirror_x = 1; + } else { + ESP_LOGE(TAG, "Touch not found"); + return ESP_ERR_NOT_FOUND; + } + + ESP_RETURN_ON_ERROR(esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)BSP_I2C_NUM, &tp_io_config, &tp_io_handle), TAG, ""); + if (ESP_LCD_TOUCH_IO_I2C_TT21100_ADDRESS == tp_io_config.dev_addr) { + ESP_RETURN_ON_ERROR(esp_lcd_touch_new_i2c_tt21100(tp_io_handle, &tp_cfg, ret_touch), TAG, "New tt21100 failed"); + } else { + ESP_RETURN_ON_ERROR(esp_lcd_touch_new_i2c_gt911(tp_io_handle, &tp_cfg, ret_touch), TAG, "New gt911 failed"); + } + + return ESP_OK; +} + +// static uint8_t bsp_get_main_button(void *param) +// { +// assert(tp); +// #if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0) +// uint8_t home_btn_val = 0x00; +// esp_lcd_touch_get_button_state(tp, 0, &home_btn_val); +// return home_btn_val ? true : false; +// #else +// ESP_LOGE(TAG, "Button main is inaccessible"); +// return false; +// #endif +// } + +// static esp_err_t bsp_init_main_button(void *param) +// { +// if (tp == NULL) { +// BSP_ERROR_CHECK_RETURN_ERR(bsp_touch_new(NULL, &tp)); +// } +// return ESP_OK; +// } + +// esp_err_t bsp_iot_button_create(button_handle_t btn_array[], int *btn_cnt, int btn_array_size) +// { +// esp_err_t ret = ESP_OK; +// if ((btn_array_size < BSP_BUTTON_NUM) || +// (btn_array == NULL)) { +// return ESP_ERR_INVALID_ARG; +// } + +// if (btn_cnt) { +// *btn_cnt = 0; +// } +// for (int i = 0; i < BSP_BUTTON_NUM; i++) { +// btn_array[i] = iot_button_create(&bsp_button_config[i]); +// if (btn_array[i] == NULL) { +// ret = ESP_FAIL; +// break; +// } +// if (btn_cnt) { +// (*btn_cnt)++; +// } +// } +// return ret; +// } diff --git a/ports/esp32/main_esp32s3_box3/esp-box-3/esp-box-3_idf4.c b/ports/esp32/main_esp32s3_box3/esp-box-3/esp-box-3_idf4.c new file mode 100644 index 000000000000..0c2448fd585e --- /dev/null +++ b/ports/esp32/main_esp32s3_box3/esp-box-3/esp-box-3_idf4.c @@ -0,0 +1,75 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_err.h" +#include "bsp/esp-box-3.h" +#include "bsp_err_check.h" +#include "esp_codec_dev_defaults.h" + +static const char *TAG = "ESP-BOX-3"; + +/* This configuration is used by default in bsp_audio_init() */ +#define BSP_I2S_DUPLEX_MONO_CFG(_sample_rate) \ + { \ + .mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_RX, \ + .sample_rate = _sample_rate, \ + .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, \ + .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT, \ + .communication_format = I2S_COMM_FORMAT_STAND_I2S, \ + .dma_buf_count = 3, \ + .dma_buf_len = 1024, \ + .use_apll = true, \ + .tx_desc_auto_clear = true, \ + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL2 | ESP_INTR_FLAG_IRAM \ + } + +static const audio_codec_data_if_t *i2s_data_if = NULL; /* Codec data interface */ + +esp_err_t bsp_audio_init(const i2s_config_t *i2s_config) +{ + esp_err_t ret = ESP_FAIL; + + if (i2s_data_if != NULL) { + /* Audio was initialized before */ + return ESP_OK; + } + + /* Setup I2S peripheral */ + const i2s_pin_config_t i2s_pin_config = { + .mck_io_num = BSP_I2S_MCLK, + .bck_io_num = BSP_I2S_SCLK, + .ws_io_num = BSP_I2S_LCLK, + .data_out_num = BSP_I2S_DOUT, + .data_in_num = BSP_I2S_DSIN + }; + + /* Setup I2S channels */ + const i2s_config_t std_cfg_default = BSP_I2S_DUPLEX_MONO_CFG(22050); + const i2s_config_t *p_i2s_cfg = &std_cfg_default; + if (i2s_config != NULL) { + p_i2s_cfg = i2s_config; + } + + ESP_ERROR_CHECK(i2s_driver_install(CONFIG_BSP_I2S_NUM, p_i2s_cfg, 0, NULL)); + ESP_GOTO_ON_ERROR(i2s_set_pin(CONFIG_BSP_I2S_NUM, &i2s_pin_config), err, TAG, "I2S set pin failed"); + + audio_codec_i2s_cfg_t i2s_cfg = { + .port = CONFIG_BSP_I2S_NUM, + }; + i2s_data_if = audio_codec_new_i2s_data(&i2s_cfg); + BSP_NULL_CHECK_GOTO(i2s_data_if, err); + + return ESP_OK; + +err: + i2s_driver_uninstall(CONFIG_BSP_I2S_NUM); + return ret; +} + +const audio_codec_data_if_t *bsp_audio_get_codec_itf(void) +{ + return i2s_data_if; +} diff --git a/ports/esp32/main_esp32s3_box3/esp-box-3/esp-box-3_idf5.c b/ports/esp32/main_esp32s3_box3/esp-box-3/esp-box-3_idf5.c new file mode 100644 index 000000000000..52503a7ebec7 --- /dev/null +++ b/ports/esp32/main_esp32s3_box3/esp-box-3/esp-box-3_idf5.c @@ -0,0 +1,95 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_err.h" +#include "bsp/esp-box-3.h" +#include "bsp_err_check.h" +#include "esp_codec_dev_defaults.h" + +static const char *TAG = "ESP-BOX-3"; + +static i2s_chan_handle_t i2s_tx_chan = NULL; +static i2s_chan_handle_t i2s_rx_chan = NULL; +static const audio_codec_data_if_t *i2s_data_if = NULL; /* Codec data interface */ + + +/* Can be used for i2s_std_gpio_config_t and/or i2s_std_config_t initialization */ +#define BSP_I2S_GPIO_CFG \ + { \ + .mclk = BSP_I2S_MCLK, \ + .bclk = BSP_I2S_SCLK, \ + .ws = BSP_I2S_LCLK, \ + .dout = BSP_I2S_DOUT, \ + .din = BSP_I2S_DSIN, \ + .invert_flags = { \ + .mclk_inv = false, \ + .bclk_inv = false, \ + .ws_inv = false, \ + }, \ + } + +/* This configuration is used by default in bsp_audio_init() */ +#define BSP_I2S_DUPLEX_MONO_CFG(_sample_rate) \ + { \ + .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(_sample_rate), \ + .slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO), \ + .gpio_cfg = BSP_I2S_GPIO_CFG, \ + } + +esp_err_t bsp_audio_init(const i2s_std_config_t *i2s_config) +{ + esp_err_t ret = ESP_FAIL; + if (i2s_tx_chan && i2s_rx_chan) { + /* Audio was initialized before */ + return ESP_OK; + } + + /* Setup I2S peripheral */ + i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(CONFIG_BSP_I2S_NUM, I2S_ROLE_MASTER); + chan_cfg.auto_clear = true; // Auto clear the legacy data in the DMA buffer + BSP_ERROR_CHECK_RETURN_ERR(i2s_new_channel(&chan_cfg, &i2s_tx_chan, &i2s_rx_chan)); + + /* Setup I2S channels */ + const i2s_std_config_t std_cfg_default = BSP_I2S_DUPLEX_MONO_CFG(22050); + const i2s_std_config_t *p_i2s_cfg = &std_cfg_default; + if (i2s_config != NULL) { + p_i2s_cfg = i2s_config; + } + + if (i2s_tx_chan != NULL) { + ESP_GOTO_ON_ERROR(i2s_channel_init_std_mode(i2s_tx_chan, p_i2s_cfg), err, TAG, "I2S channel initialization failed"); + ESP_GOTO_ON_ERROR(i2s_channel_enable(i2s_tx_chan), err, TAG, "I2S enabling failed"); + } + if (i2s_rx_chan != NULL) { + ESP_GOTO_ON_ERROR(i2s_channel_init_std_mode(i2s_rx_chan, p_i2s_cfg), err, TAG, "I2S channel initialization failed"); + ESP_GOTO_ON_ERROR(i2s_channel_enable(i2s_rx_chan), err, TAG, "I2S enabling failed"); + } + + audio_codec_i2s_cfg_t i2s_cfg = { + .port = CONFIG_BSP_I2S_NUM, + .rx_handle = i2s_rx_chan, + .tx_handle = i2s_tx_chan, + }; + i2s_data_if = audio_codec_new_i2s_data(&i2s_cfg); + BSP_NULL_CHECK_GOTO(i2s_data_if, err); + + return ESP_OK; + +err: + if (i2s_tx_chan) { + i2s_del_channel(i2s_tx_chan); + } + if (i2s_rx_chan) { + i2s_del_channel(i2s_rx_chan); + } + + return ret; +} + +const audio_codec_data_if_t *bsp_audio_get_codec_itf(void) +{ + return i2s_data_if; +} diff --git a/ports/esp32/main_esp32s3_box3/esp-box-3/idf_component.yml b/ports/esp32/main_esp32s3_box3/esp-box-3/idf_component.yml new file mode 100644 index 000000000000..89361754904e --- /dev/null +++ b/ports/esp32/main_esp32s3_box3/esp-box-3/idf_component.yml @@ -0,0 +1,29 @@ +dependencies: + # button: + # public: true + # version: '>=2.5' + esp_codec_dev: + public: true + version: ^1,<1.2 + esp_lcd_ili9341: ^1 + esp_lcd_touch_gt911: + version: ^1 + public: true + esp_lcd_touch_tt21100: + version: ^1 + public: true + icm42670: + public: true + version: ^1 + idf: '>=4.4.5' +description: Board Support Package (BSP) for ESP32-S3-BOX-3 +repository: git://github.com/espressif/esp-bsp.git +repository_info: + commit_sha: 01fe06ae3a42c096721b42adcbf4741abe8e37f6 + path: bsp/esp-box-3 +tags: +- bsp +targets: +- esp32s3 +url: https://github.com/espressif/esp-bsp/tree/master/bsp/esp-box-3 +version: 1.2.0~2 diff --git a/ports/esp32/main_esp32s3_box3/esp-box-3/include/bsp/display.h b/ports/esp32/main_esp32s3_box3/esp-box-3/include/bsp/display.h new file mode 100644 index 000000000000..262d9bfb5e92 --- /dev/null +++ b/ports/esp32/main_esp32s3_box3/esp-box-3/include/bsp/display.h @@ -0,0 +1,123 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief BSP LCD + * + * This file offers API for basic LCD control. + * It is useful for users who want to use the LCD without the default Graphical Library LVGL. + * + * For standard LCD initialization with LVGL graphical library, you can call all-in-one function bsp_display_start(). + */ + +#pragma once +#include "esp_lcd_types.h" + +/* LCD color formats */ +#define ESP_LCD_COLOR_FORMAT_RGB565 (1) +#define ESP_LCD_COLOR_FORMAT_RGB888 (2) + +/* LCD display color format */ +#define BSP_LCD_COLOR_FORMAT (ESP_LCD_COLOR_FORMAT_RGB565) +/* LCD display color bytes endianess */ +#define BSP_LCD_BIGENDIAN (1) +/* LCD display color bits */ +#define BSP_LCD_BITS_PER_PIXEL (16) +/* LCD display color space */ +#define BSP_LCD_COLOR_SPACE (ESP_LCD_COLOR_SPACE_BGR) +/* LCD definition */ +#define BSP_LCD_H_RES (320) +#define BSP_LCD_V_RES (240) + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief BSP display configuration structure + * + */ +typedef struct { + int max_transfer_sz; /*!< Maximum transfer size, in bytes. */ +} bsp_display_config_t; + +/** + * @brief Create new display panel + * + * For maximum flexibility, this function performs only reset and initialization of the display. + * You must turn on the display explicitly by calling esp_lcd_panel_disp_on_off(). + * The display's backlight is not turned on either. You can use bsp_display_backlight_on/off(), + * bsp_display_brightness_set() (on supported boards) or implement your own backlight control. + * + * If you want to free resources allocated by this function, you can use esp_lcd API, ie.: + * + * \code{.c} + * esp_lcd_panel_del(panel); + * esp_lcd_panel_io_del(io); + * spi_bus_free(spi_num_from_configuration); + * \endcode + * + * @param[in] config display configuration + * @param[out] ret_panel esp_lcd panel handle + * @param[out] ret_io esp_lcd IO handle + * @return + * - ESP_OK On success + * - Else esp_lcd failure + */ +esp_err_t bsp_display_new(const bsp_display_config_t *config, esp_lcd_panel_handle_t *ret_panel, esp_lcd_panel_io_handle_t *ret_io); + +/** + * @brief Initialize display's brightness + * + * Brightness is controlled with PWM signal to a pin controlling backlight. + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t bsp_display_brightness_init(void); + +/** + * @brief Set display's brightness + * + * Brightness is controlled with PWM signal to a pin controlling backlight. + * Brightness must be already initialized by calling bsp_display_brightness_init() or bsp_display_new() + * + * @param[in] brightness_percent Brightness in [%] + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t bsp_display_brightness_set(int brightness_percent); + +/** + * @brief Turn on display backlight + * + * Brightness is controlled with PWM signal to a pin controlling backlight. + * Brightness must be already initialized by calling bsp_display_brightness_init() or bsp_display_new() + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t bsp_display_backlight_on(void); + +/** + * @brief Turn off display backlight + * + * Brightness is controlled with PWM signal to a pin controlling backlight. + * Brightness must be already initialized by calling bsp_display_brightness_init() or bsp_display_new() + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t bsp_display_backlight_off(void); + +#ifdef __cplusplus +} +#endif diff --git a/ports/esp32/main_esp32s3_box3/esp-box-3/include/bsp/esp-box-3.h b/ports/esp32/main_esp32s3_box3/esp-box-3/include/bsp/esp-box-3.h new file mode 100644 index 000000000000..db2711990c7d --- /dev/null +++ b/ports/esp32/main_esp32s3_box3/esp-box-3/include/bsp/esp-box-3.h @@ -0,0 +1,308 @@ +/* + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief ESP BSP: ESP-BOX-3 + */ + +#pragma once + +#include "sdkconfig.h" +#include "driver/gpio.h" +#include "driver/i2c.h" +#include "soc/usb_pins.h" +#include "esp_codec_dev.h" +// #include "iot_button.h" +#include "bsp/display.h" + +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) +#include "driver/i2s.h" +#else +#include "driver/i2s_std.h" +#endif +/************************************************************************************************** + * BSP Capabilities + **************************************************************************************************/ + +#define BSP_CAPS_DISPLAY 1 +#define BSP_CAPS_TOUCH 1 +#define BSP_CAPS_BUTTONS 0 +#define BSP_CAPS_AUDIO 1 +#define BSP_CAPS_AUDIO_SPEAKER 1 +#define BSP_CAPS_AUDIO_MIC 1 +#define BSP_CAPS_SDCARD 0 +#define BSP_CAPS_IMU 1 + +/************************************************************************************************** + * ESP-BOX pinout + **************************************************************************************************/ +/* I2C */ +#define BSP_I2C_SCL (GPIO_NUM_18) +#define BSP_I2C_SDA (GPIO_NUM_8) + +/* Audio */ +#define BSP_I2S_SCLK (GPIO_NUM_17) +#define BSP_I2S_MCLK (GPIO_NUM_2) +#define BSP_I2S_LCLK (GPIO_NUM_45) +#define BSP_I2S_DOUT (GPIO_NUM_15) // To Codec ES8311 +#define BSP_I2S_DSIN (GPIO_NUM_16) // From ADC ES7210 +#define BSP_POWER_AMP_IO (GPIO_NUM_46) +#define BSP_MUTE_STATUS (GPIO_NUM_1) + +/* Display */ +#define BSP_LCD_DATA0 (GPIO_NUM_6) +#define BSP_LCD_PCLK (GPIO_NUM_7) +#define BSP_LCD_CS (GPIO_NUM_5) +#define BSP_LCD_DC (GPIO_NUM_4) +#define BSP_LCD_RST (GPIO_NUM_48) + +#define BSP_LCD_BACKLIGHT (GPIO_NUM_47) +#define BSP_LCD_TOUCH_INT (GPIO_NUM_3) + +/* USB */ +#define BSP_USB_POS USBPHY_DP_NUM +#define BSP_USB_NEG USBPHY_DM_NUM + +// /* Buttons */ +// #define BSP_BUTTON_CONFIG_IO (GPIO_NUM_0) +// #define BSP_BUTTON_MUTE_IO (GPIO_NUM_1) + +/* PMOD */ +/* + * PMOD interface (peripheral module interface) is an open standard defined by Digilent Inc. + * for peripherals used with FPGA or microcontroller development boards. + * + * ESP-BOX contains two double PMOD connectors, protected with ESD protection diodes. + * Power pins are on 3.3V. + * + * Double PMOD Connectors on ESP-BOX-3 dock are labeled as follows: + * |------------| + * | IO1 IO5 | + * | IO2 IO6 | + * | IO3 IO7 | + * | IO4 IO8 | + * |------------| + * | GND GND | + * | 3V3 3V3 | + * |------------| + */ +#define BSP_PMOD1_IO1 GPIO_NUM_42 +#define BSP_PMOD1_IO2 BSP_USB_POS +#define BSP_PMOD1_IO3 GPIO_NUM_39 +#define BSP_PMOD1_IO4 GPIO_NUM_40 // Intended for I2C SCL (pull-up NOT populated) +#define BSP_PMOD1_IO5 GPIO_NUM_21 +#define BSP_PMOD1_IO6 BSP_USB_NEG +#define BSP_PMOD1_IO7 GPIO_NUM_38 +#define BSP_PMOD1_IO8 GPIO_NUM_41 // Intended for I2C SDA (pull-up NOT populated) + +#define BSP_PMOD2_IO1 GPIO_NUM_13 // Intended for SPI2 Q (MISO) +#define BSP_PMOD2_IO2 GPIO_NUM_9 // Intended for SPI2 HD (Hold) +#define BSP_PMOD2_IO3 GPIO_NUM_12 // Intended for SPI2 CLK +#define BSP_PMOD2_IO4 GPIO_NUM_44 // UART0 RX by default +#define BSP_PMOD2_IO5 GPIO_NUM_10 // Intended for SPI2 CS +#define BSP_PMOD2_IO6 GPIO_NUM_14 // Intended for SPI2 WP (Write-protect) +#define BSP_PMOD2_IO7 GPIO_NUM_11 // Intended for SPI2 D (MOSI) +#define BSP_PMOD2_IO8 GPIO_NUM_43 // UART0 TX by defaultf + +// /* Buttons */ +// typedef enum { +// BSP_BUTTON_CONFIG = 0, +// BSP_BUTTON_MUTE, +// BSP_BUTTON_MAIN, +// BSP_BUTTON_NUM +// } bsp_button_t; + +#ifdef __cplusplus +extern "C" { +#endif + +/************************************************************************************************** + * + * I2S audio interface + * + * There are two devices connected to the I2S peripheral: + * - Codec ES8311 for output (playback) path + * - ADC ES7210 for input (recording) path + * + * For speaker initialization use bsp_audio_codec_speaker_init() which is inside initialize I2S with bsp_audio_init(). + * For microphone initialization use bsp_audio_codec_microphone_init() which is inside initialize I2S with bsp_audio_init(). + * After speaker or microphone initialization, use functions from esp_codec_dev for play/record audio. + * Example audio play: + * \code{.c} + * esp_codec_dev_set_out_vol(spk_codec_dev, DEFAULT_VOLUME); + * esp_codec_dev_open(spk_codec_dev, &fs); + * esp_codec_dev_write(spk_codec_dev, wav_bytes, bytes_read_from_spiffs); + * esp_codec_dev_close(spk_codec_dev); + * \endcode + **************************************************************************************************/ + +/** + * @brief Init audio + * + * @note There is no deinit audio function. Users can free audio resources by calling i2s_del_channel() + * @warning The type of i2s_config param is depending on IDF version. + * @param[in] i2s_config I2S configuration. Pass NULL to use default values (Mono, duplex, 16bit, 22050 Hz) + * @return + * - ESP_OK On success + * - ESP_ERR_NOT_SUPPORTED The communication mode is not supported on the current chip + * - ESP_ERR_INVALID_ARG NULL pointer or invalid configuration + * - ESP_ERR_NOT_FOUND No available I2S channel found + * - ESP_ERR_NO_MEM No memory for storing the channel information + * - ESP_ERR_INVALID_STATE This channel has not initialized or already started + */ +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) +esp_err_t bsp_audio_init(const i2s_config_t *i2s_config); +#else +esp_err_t bsp_audio_init(const i2s_std_config_t *i2s_config); +#endif + +/** + * @brief Get codec I2S interface (initialized in bsp_audio_init) + * + * @return + * - Pointer to codec I2S interface handle or NULL when error occurred + */ +const audio_codec_data_if_t *bsp_audio_get_codec_itf(void); + +/** + * @brief Initialize speaker codec device + * + * @return Pointer to codec device handle or NULL when error occurred + */ +esp_codec_dev_handle_t bsp_audio_codec_speaker_init(void); + +/** + * @brief Initialize microphone codec device + * + * @return Pointer to codec device handle or NULL when error occurred + */ +esp_codec_dev_handle_t bsp_audio_codec_microphone_init(void); + +/************************************************************************************************** + * + * I2C interface + * + * There are multiple devices connected to I2C peripheral: + * - Codec ES8311 (configuration only) + * - ADC ES7210 (configuration only) + * - Encryption chip ATECC608A (NOT populated on most boards) + * - LCD Touch controller + * - Inertial Measurement Unit ICM-42607-P + * + * After initialization of I2C, use BSP_I2C_NUM macro when creating I2C devices drivers ie.: + * \code{.c} + * icm42670_handle_t imu = icm42670_create(BSP_I2C_NUM, ICM42670_I2C_ADDRESS); + * \endcode + **************************************************************************************************/ +#define BSP_I2C_NUM CONFIG_BSP_I2C_NUM + +/** + * @brief Init I2C driver + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG I2C parameter error + * - ESP_FAIL I2C driver installation error + * + */ +esp_err_t bsp_i2c_init(void); + +/** + * @brief Deinit I2C driver and free its resources + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG I2C parameter error + * + */ +esp_err_t bsp_i2c_deinit(void); + +/************************************************************************************************** + * + * SPIFFS + * + * After mounting the SPIFFS, it can be accessed with stdio functions ie.: + * \code{.c} + * FILE* f = fopen(BSP_SPIFFS_MOUNT_POINT"/hello.txt", "w"); + * fprintf(f, "Hello World!\n"); + * fclose(f); + * \endcode + **************************************************************************************************/ +#define BSP_SPIFFS_MOUNT_POINT CONFIG_BSP_SPIFFS_MOUNT_POINT + +/** + * @brief Mount SPIFFS to virtual file system + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if esp_vfs_spiffs_register was already called + * - ESP_ERR_NO_MEM if memory can not be allocated + * - ESP_FAIL if partition can not be mounted + * - other error codes + */ +esp_err_t bsp_spiffs_mount(void); + +/** + * @brief Unmount SPIFFS from virtual file system + * + * @return + * - ESP_OK on success + * - ESP_ERR_NOT_FOUND if the partition table does not contain SPIFFS partition with given label + * - ESP_ERR_INVALID_STATE if esp_vfs_spiffs_unregister was already called + * - ESP_ERR_NO_MEM if memory can not be allocated + * - ESP_FAIL if partition can not be mounted + * - other error codes + */ +esp_err_t bsp_spiffs_unmount(void); + +/************************************************************************************************** + * + * LCD interface + * + * ESP-BOX is shipped with 2.4inch ST7789 display controller. + * It features 16-bit colors, 320x240 resolution and capacitive touch controller. + * + * LVGL is used as graphics library. LVGL is NOT thread safe, therefore the user must take LVGL mutex + * by calling bsp_display_lock() before calling and LVGL API (lv_...) and then give the mutex with + * bsp_display_unlock(). + * + * Display's backlight must be enabled explicitly by calling bsp_display_backlight_on() + **************************************************************************************************/ +#define BSP_LCD_PIXEL_CLOCK_HZ (40 * 1000 * 1000) +#define BSP_LCD_SPI_NUM (SPI3_HOST) + +/************************************************************************************************** + * + * Button + * + * There are three buttons on ESP-BOX: + * - Reset: Not programable + * - Config: Controls boot mode during reset. Can be programmed after application starts + * - Mute: This button is wired to Logic Gates and its result is mapped to GPIO_NUM_1 + **************************************************************************************************/ + +/** + * @brief Initialize all buttons + * + * Returned button handlers must be used with espressif/button component API + * + * @note For LCD panel button which is defined as BSP_BUTTON_MAIN, bsp_display_start should + * be called before call this function. + * + * @param[out] btn_array Output button array + * @param[out] btn_cnt Number of button handlers saved to btn_array, can be NULL + * @param[in] btn_array_size Size of output button array. Must be at least BSP_BUTTON_NUM + * @return + * - ESP_OK All buttons initialized + * - ESP_ERR_INVALID_ARG btn_array is too small or NULL + * - ESP_FAIL Underlaying iot_button_create failed + */ +// esp_err_t bsp_iot_button_create(button_handle_t btn_array[], int *btn_cnt, int btn_array_size); + +#ifdef __cplusplus +} +#endif diff --git a/ports/esp32/main_esp32s3_box3/esp-box-3/include/bsp/esp-bsp.h b/ports/esp32/main_esp32s3_box3/esp-box-3/include/bsp/esp-bsp.h new file mode 100644 index 000000000000..ba61076d229d --- /dev/null +++ b/ports/esp32/main_esp32s3_box3/esp-box-3/include/bsp/esp-bsp.h @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once +#include "bsp/esp-box-3.h" diff --git a/ports/esp32/main_esp32s3_box3/esp-box-3/include/bsp/touch.h b/ports/esp32/main_esp32s3_box3/esp-box-3/include/bsp/touch.h new file mode 100644 index 000000000000..3c9857ced589 --- /dev/null +++ b/ports/esp32/main_esp32s3_box3/esp-box-3/include/bsp/touch.h @@ -0,0 +1,51 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief BSP Touchscreen + * + * This file offers API for basic touchscreen initialization. + * It is useful for users who want to use the touchscreen without the default Graphical Library LVGL. + * + * For standard LCD initialization with LVGL graphical library, you can call all-in-one function bsp_display_start(). + */ + +#pragma once +#include "esp_lcd_touch.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief BSP touch configuration structure + * + */ +typedef struct { + void *dummy; /*!< Prepared for future use. */ +} bsp_touch_config_t; + +/** + * @brief Create new touchscreen + * + * If you want to free resources allocated by this function, you can use esp_lcd_touch API, ie.: + * + * \code{.c} + * esp_lcd_touch_del(tp); + * \endcode + * + * @param[in] config touch configuration + * @param[out] ret_touch esp_lcd_touch touchscreen handle + * @return + * - ESP_OK On success + * - Else esp_lcd_touch failure + */ +esp_err_t bsp_touch_new(const bsp_touch_config_t *config, esp_lcd_touch_handle_t *ret_touch); + +#ifdef __cplusplus +} +#endif diff --git a/ports/esp32/main_esp32s3_box3/esp-box-3/pic.png b/ports/esp32/main_esp32s3_box3/esp-box-3/pic.png new file mode 100644 index 0000000000000000000000000000000000000000..87569202b34fc56cb44cf58fa990b3a98fc2aac0 GIT binary patch literal 146873 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ʰSkmmDlY6HsEw2~ziRS8dH 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

Cxzy=5h7P@pKq#!XVv+q~; z?awunQ>pLB-@D(~koK-S$h2y)yUIc%S20n{hfnwWi&$<<c z-V!JcC`^W~JTW2v3Cuwcr7Yg&RdvJeIBlF6o7Z@*KI6K zdEXfy+;kwZA?%6+$YMVS-01o91IZ`b+k{n_C(CHUCjL1fh`?SB66kRngrc;UB8Cd5 zU?E6HI+%9Zy@xD}mTI-?+VkL_B=W`LQYei-DMXpZ#>YF)A3{N>tKF`JY9QFBs#@H8 zWw@Y2eYxh`<%YeWP+ZT=_t2MFf0Ny&_WgJw^Eo&fj<@|_5HSH=S*7O7iVg6od3kf? ziXst55`0SJeEU!i9&njdfJ=j%C~U{3%1;l$_)A*`j?m&nNFY%~*^vxEH-yGyVrO=O zbN{>pIsKF*pm5NajE|L&SsW#jDGuIpd~gRlG*C&2+}i~?AUKF`l%AqHp^^`5B=oAI za*uGe1NV-=|Q+VC~KPZ zAJ$0FB+O0#@(Up}q@09zhxlyTjC1lqqGzpc7h4UqN9E<^L(^-WBO|G;a4Q}d=DGJe zkZi70A?xoY$DgB>={{&w<4%9<)twBR^|7O~%<_aa@AHO-Ubw$2t+=5m!rynK_BIz# ze;>}Q7?hy{6ixcgms5#1ha+y@@XVt}!xoRhb!B=#Lx6?zZ-mlbr$pw7&j8zBoMb;} zi;Mw8|L;-&EI3{8BzqO4(&?TTA=t}s8s+fk(OC9Eu!KRQ9FQ*R1z0}ttDNQIo1y+b zgYH}byycPuwqxDjSk64ngI7mmT9_90IIqEs= z5tB>2Vo{&xG~%jm@82G$HIc8N+Z8nqs2R{FCa>?&_#p>c)ne{LZgtMEbjT|8vbSq6{Fh+d6M3yp$>tQmSHV2W5V+G9qxDua2`bOMIn8nL z@m<;3m(U{y-DlMq3j@W+#7V_329{e@E8A>s>{asEQ>!l3!8L~ zKgb0zwdIUYOaDQLvWQY5cld|-2G}p<`u-&b_zb4BVilXl`T_yFA-Vd2G-OG( z$S!$ZXmVdI;5Ha2-#h>;Y?ZxPY_QMV8&i@d6p1IS;jgV-I4#B{xNzerJontA}9hG|!jBvMe!046bebuBd_x;TL#!3MIbo zLx%ttnIEvHv$I|j{Y;7(`kHq@1_*QLF;w;`EfLil;1#!EM6KK${?2quMd44lsM%a9^i@ zKFCWuT+}1$5Rky#-Q8SwD#&1KXXo{z;q}3KVDKG1&Yw?~+buFY`5nKFAZ+g5J+Rz6 zo#x2mKK)zzAhrO1tbD|y&o*|IqkZUBC7YK>oYbpa1V()2UdL~y0vF=2 z*g*1;>LgJ?H;OL4cr<+5VC)8?X3sHbme(c+*=`tgu^^o{$#?VfHf*E-*p8a6MiKP zxh?p*>G5a!b5b+`2mM?2@cbSJ&6kOrf@ctOVPlgo142#=`NJZGh>3tCL;XiU4-pS0 z)6VwLeDJ07#RjCTpuCc)*3G}~J(wQy)9Eb%j*7)4C7UeOLvEzd2$B&+-c!y2lQ3_e zttr85vR5B^ih%Pog`kqH2^V&-UpHDF0jVT7n=^0P6Ry>PP)dLfjs{fIZ42iPFC9W{ zz~~RFvjE$l*e0y|?7STuh#-fEVGtjIO5AfVuZKW2@pd7UVZlw-!k8wn&;Wb=1a@7+ zK_^d7Pi|ODL9>|ymji3&;SDAf5Ew^mj=#|V^S}!IlG%>7E~zS{j)sRv4q(*-fyR;% z7glbsfBpLK@2e|ZV8*<;95{`c*oI5819D2eSv@2pUQp}vD?{Hse?KN}ID(#Ehxp0aJ%4Qx(taup z&QQAhHTkxvEzG^j#%ETeRVi2(2V~GQWxqrNIL0n@=cI>G4ySiyr_h>|dLC9aCV~SNV?0mX#FMWtJ&npBN0$VmMQ1S6aZrjj$80?$&-NiUWzZ}mx4I~xhi2J~mvCx_6 z*zr#@xdvMae*1x|{I9H$^5dV{gdD5=1C#jFBu7LuPh9W(!Yf0uj3cK1fvPFhzwt)a zU~Tf-g7E@n?er;fV!oF(1|_jy-~#m?BV}Z~4x;}UfOhe<($25dZ)H literal 0 HcmV?d00001 diff --git a/ports/esp32/main_esp32s3_box3/esp-box-3/priv_include/bsp_err_check.h b/ports/esp32/main_esp32s3_box3/esp-box-3/priv_include/bsp_err_check.h new file mode 100644 index 000000000000..e00199943eef --- /dev/null +++ b/ports/esp32/main_esp32s3_box3/esp-box-3/priv_include/bsp_err_check.h @@ -0,0 +1,58 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "esp_check.h" +#include "sdkconfig.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Assert on error, if selected in menuconfig. Otherwise return error code. */ +#if CONFIG_BSP_ERROR_CHECK +#define BSP_ERROR_CHECK_RETURN_ERR(x) ESP_ERROR_CHECK(x) +#define BSP_ERROR_CHECK_RETURN_NULL(x) ESP_ERROR_CHECK(x) +#define BSP_ERROR_CHECK(x, ret) ESP_ERROR_CHECK(x) +#define BSP_NULL_CHECK(x, ret) assert(x) +#define BSP_NULL_CHECK_GOTO(x, goto_tag) assert(x) +#else +#define BSP_ERROR_CHECK_RETURN_ERR(x) do { \ + esp_err_t err_rc_ = (x); \ + if (unlikely(err_rc_ != ESP_OK)) { \ + return err_rc_; \ + } \ + } while(0) + +#define BSP_ERROR_CHECK_RETURN_NULL(x) do { \ + if (unlikely((x) != ESP_OK)) { \ + return NULL; \ + } \ + } while(0) + +#define BSP_NULL_CHECK(x, ret) do { \ + if ((x) == NULL) { \ + return ret; \ + } \ + } while(0) + +#define BSP_ERROR_CHECK(x, ret) do { \ + if (unlikely((x) != ESP_OK)) { \ + return ret; \ + } \ + } while(0) + +#define BSP_NULL_CHECK_GOTO(x, goto_tag) do { \ + if ((x) == NULL) { \ + goto goto_tag; \ + } \ + } while(0) +#endif + +#ifdef __cplusplus +} +#endif diff --git a/ports/esp32/main_esp32s3_box3/esp32_common.cmake b/ports/esp32/main_esp32s3_box3/esp32_common.cmake new file mode 100644 index 000000000000..7c2902599d63 --- /dev/null +++ b/ports/esp32/main_esp32s3_box3/esp32_common.cmake @@ -0,0 +1,244 @@ +# Set location of base MicroPython directory. +if(NOT MICROPY_DIR) + get_filename_component(MICROPY_DIR ${CMAKE_CURRENT_LIST_DIR}/../.. ABSOLUTE) +endif() + +# Set location of the ESP32 port directory. +if(NOT MICROPY_PORT_DIR) + get_filename_component(MICROPY_PORT_DIR ${MICROPY_DIR}/ports/esp32 ABSOLUTE) +endif() + +# Include core source components. +include(${MICROPY_DIR}/py/py.cmake) + +# CMAKE_BUILD_EARLY_EXPANSION is set during the component-discovery phase of +# `idf.py build`, so none of the extmod/usermod (and in reality, most of the +# micropython) rules need to happen. Specifically, you cannot invoke add_library. +if(NOT CMAKE_BUILD_EARLY_EXPANSION) + # Enable extmod components that will be configured by extmod.cmake. + # A board may also have enabled additional components. + set(MICROPY_PY_BTREE ON) + + include(${MICROPY_DIR}/py/usermod.cmake) + include(${MICROPY_DIR}/extmod/extmod.cmake) + + idf_build_get_property(component_targets __COMPONENT_TARGETS) + string(REPLACE "___idf_lvgl" "" component_targets "${component_targets}") + idf_build_set_property(__COMPONENT_TARGETS "${component_targets}") +endif() + +list(APPEND MICROPY_QSTRDEFS_PORT + ${MICROPY_PORT_DIR}/qstrdefsport.h +) + +list(APPEND MICROPY_SOURCE_SHARED + ${MICROPY_DIR}/shared/readline/readline.c + ${MICROPY_DIR}/shared/netutils/netutils.c + ${MICROPY_DIR}/shared/timeutils/timeutils.c + ${MICROPY_DIR}/shared/runtime/interrupt_char.c + ${MICROPY_DIR}/shared/runtime/stdout_helpers.c + ${MICROPY_DIR}/shared/runtime/sys_stdio_mphal.c + ${MICROPY_DIR}/shared/runtime/pyexec.c +) + +list(APPEND MICROPY_SOURCE_LIB + ${MICROPY_DIR}/lib/littlefs/lfs1.c + ${MICROPY_DIR}/lib/littlefs/lfs1_util.c + ${MICROPY_DIR}/lib/littlefs/lfs2.c + ${MICROPY_DIR}/lib/littlefs/lfs2_util.c + ${MICROPY_DIR}/lib/mbedtls_errors/esp32_mbedtls_errors.c + ${MICROPY_DIR}/lib/oofatfs/ff.c + ${MICROPY_DIR}/lib/oofatfs/ffunicode.c +) + +list(APPEND MICROPY_SOURCE_DRIVERS + ${MICROPY_DIR}/drivers/bus/softspi.c + ${MICROPY_DIR}/drivers/dht/dht.c +) + +list(APPEND MICROPY_SOURCE_PORT + panichandler.c + adc.c + ppp_set_auth.c + uart.c + usb.c + usb_serial_jtag.c + gccollect.c + mphalport.c + fatfs_port.c + help.c + machine_bitstream.c + machine_timer.c + machine_pin.c + machine_touchpad.c + machine_dac.c + machine_i2c.c + network_common.c + network_lan.c + network_ppp.c + network_wlan.c + mpnimbleport.c + modsocket.c + modesp.c + esp32_nvs.c + esp32_partition.c + esp32_rmt.c + esp32_ulp.c + modesp32.c + machine_hw_spi.c + mpthreadport.c + machine_rtc.c + machine_sdcard.c + modespnow.c +) +list(TRANSFORM MICROPY_SOURCE_PORT PREPEND ${MICROPY_PORT_DIR}/) +list(APPEND MICROPY_SOURCE_PORT ${CMAKE_BINARY_DIR}/pins.c) + +list(APPEND MAIN_SOURCE_PORT + main.c + lvgl_port.c +) +list(TRANSFORM MAIN_SOURCE_PORT PREPEND ${CMAKE_CURRENT_LIST_DIR}/) + +list(APPEND MICROPY_SOURCE_QSTR + ${MICROPY_SOURCE_PY} + ${MICROPY_SOURCE_EXTMOD} + ${MICROPY_SOURCE_USERMOD} + ${MICROPY_SOURCE_SHARED} + ${MICROPY_SOURCE_LIB} + ${MICROPY_SOURCE_PORT} + ${MICROPY_SOURCE_BOARD} + ${MICROPY_SOURCE_TINYUSB} + ${MAIN_SOURCE_PORT} +) + +list(APPEND IDF_COMPONENTS + app_update + bootloader_support + bt + driver + esp_adc + esp_app_format + esp_common + esp_eth + esp_event + esp_hw_support + esp_netif + esp_partition + esp_pm + esp_psram + esp_ringbuf + esp_rom + esp_system + esp_timer + esp_wifi + freertos + hal + heap + log + lwip + mbedtls + newlib + nvs_flash + sdmmc + soc + spi_flash + ulp + vfs +) + +# Register the main IDF component. +idf_component_register( + SRCS + ${MICROPY_SOURCE_PY} + ${MICROPY_SOURCE_EXTMOD} + ${MICROPY_SOURCE_SHARED} + ${MICROPY_SOURCE_LIB} + ${MICROPY_SOURCE_DRIVERS} + ${MICROPY_SOURCE_PORT} + ${MICROPY_SOURCE_BOARD} + ${MAIN_SOURCE_PORT} + INCLUDE_DIRS + ${MICROPY_INC_CORE} + ${MICROPY_INC_USERMOD} + ${MICROPY_PORT_DIR} + ${MICROPY_BOARD_DIR} + ${CMAKE_BINARY_DIR} + LDFRAGMENTS + linker.lf + REQUIRES + ${IDF_COMPONENTS} +) + +# Set the MicroPython target as the current (main) IDF component target. +set(MICROPY_TARGET ${COMPONENT_TARGET}) + +# Define mpy-cross flags, for use with frozen code. +if(NOT IDF_TARGET STREQUAL "esp32c3") +set(MICROPY_CROSS_FLAGS -march=xtensawin) +endif() + +# Set compile options for this port. +target_compile_definitions(${MICROPY_TARGET} PUBLIC + ${MICROPY_DEF_CORE} + ${MICROPY_DEF_BOARD} + MICROPY_ESP_IDF_4=1 + MICROPY_VFS_FAT=1 + MICROPY_VFS_LFS2=1 + FFCONF_H=\"${MICROPY_OOFATFS_DIR}/ffconf.h\" + LFS1_NO_MALLOC LFS1_NO_DEBUG LFS1_NO_WARN LFS1_NO_ERROR LFS1_NO_ASSERT + LFS2_NO_MALLOC LFS2_NO_DEBUG LFS2_NO_WARN LFS2_NO_ERROR LFS2_NO_ASSERT +) + +# Disable some warnings to keep the build output clean. +target_compile_options(${MICROPY_TARGET} PUBLIC + -Wno-clobbered + -Wno-deprecated-declarations + -Wno-missing-field-initializers +) + +# Additional include directories needed for private NimBLE headers. +target_include_directories(${MICROPY_TARGET} PUBLIC + ${IDF_PATH}/components/bt/host/nimble/nimble +) + +# Add additional extmod and usermod components. +target_link_libraries(${MICROPY_TARGET} micropy_extmod_btree) +target_link_libraries(${MICROPY_TARGET} usermod) + +# Collect all of the include directories and compile definitions for the IDF components, +# including those added by the IDF Component Manager via idf_components.yaml. +foreach(comp ${__COMPONENT_NAMES_RESOLVED}) + micropy_gather_target_properties(__idf_${comp}) + micropy_gather_target_properties(${comp}) +endforeach() + +# Include the main MicroPython cmake rules. +include(${MICROPY_DIR}/py/mkrules.cmake) + +# Generate source files for named pins (requires mkrules.cmake for MICROPY_GENHDR_DIR). + +set(GEN_PINS_PREFIX "${MICROPY_PORT_DIR}/boards/pins_prefix.c") +set(GEN_PINS_MKPINS "${MICROPY_PORT_DIR}/boards/make-pins.py") +set(GEN_PINS_SRC "${CMAKE_BINARY_DIR}/pins.c") +set(GEN_PINS_HDR "${MICROPY_GENHDR_DIR}/pins.h") + +if(EXISTS "${MICROPY_BOARD_DIR}/pins.csv") + set(GEN_PINS_BOARD_CSV "${MICROPY_BOARD_DIR}/pins.csv") + set(GEN_PINS_BOARD_CSV_ARG --board-csv "${GEN_PINS_BOARD_CSV}") +endif() + +target_sources(${MICROPY_TARGET} PRIVATE ${GEN_PINS_HDR}) + +add_custom_command( + OUTPUT ${GEN_PINS_SRC} ${GEN_PINS_HDR} + COMMAND ${Python3_EXECUTABLE} ${GEN_PINS_MKPINS} ${GEN_PINS_BOARD_CSV_ARG} + --prefix ${GEN_PINS_PREFIX} --output-source ${GEN_PINS_SRC} --output-header ${GEN_PINS_HDR} + DEPENDS + ${MICROPY_MPVERSION} + ${GEN_PINS_MKPINS} + ${GEN_PINS_BOARD_CSV} + ${GEN_PINS_PREFIX} + VERBATIM + COMMAND_EXPAND_LISTS +) diff --git a/ports/esp32/main_esp32s3_box3/how_to_build.md b/ports/esp32/main_esp32s3_box3/how_to_build.md new file mode 100644 index 000000000000..9dbf29461db5 --- /dev/null +++ b/ports/esp32/main_esp32s3_box3/how_to_build.md @@ -0,0 +1,5 @@ +## How to Build + +```bash +make BOARD=ESP32_S3_BOX_3 MAIN_VARIANT=box3 USER_C_MODULES=../../../modules/micropython.cmake PORT=/dev/ttyACM0 -C ports/esp32 deploy +``` diff --git a/ports/esp32/main_esp32s3_box3/idf_component.yml b/ports/esp32/main_esp32s3_box3/idf_component.yml new file mode 100644 index 000000000000..07f7066b01d3 --- /dev/null +++ b/ports/esp32/main_esp32s3_box3/idf_component.yml @@ -0,0 +1,9 @@ +## IDF Component Manager Manifest File +dependencies: + espressif/mdns: "~1.1.0" + espressif/esp_tinyusb: "~1.0.0" + espressif/esp-box-3: + version: "*" + override_path: "./esp-box-3" + idf: + version: ">=5.0.4" diff --git a/ports/esp32/main_esp32s3_box3/linker.lf b/ports/esp32/main_esp32s3_box3/linker.lf new file mode 100644 index 000000000000..81d27906be0e --- /dev/null +++ b/ports/esp32/main_esp32s3_box3/linker.lf @@ -0,0 +1 @@ +# Empty linker fragment (no workaround required for S3, see main_esp32/linker.lf). diff --git a/ports/esp32/main_esp32s3_box3/lvgl_port.c b/ports/esp32/main_esp32s3_box3/lvgl_port.c new file mode 100644 index 000000000000..7a23ed27d3de --- /dev/null +++ b/ports/esp32/main_esp32s3_box3/lvgl_port.c @@ -0,0 +1,181 @@ +#include +#include +#include +#include "esp_log.h" +#include "esp_timer.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "py/mpconfig.h" +#include "py/obj.h" +#include "py/runtime.h" +#include "lvgl/lvgl.h" +#include "lvgl_port.h" + +#define LVGL_DRAW_BUF_LINES 20 // number of display lines in each draw buffer +#define LVGL_TICK_PERIOD_MS 2 +#define LVGL_TASK_MAX_DELAY_MS 500 +#define LVGL_TASK_MIN_DELAY_MS 1 +#define LVGL_TASK_STACK_SIZE (6 * 1024) +#define LVGL_TASK_PRIORITY 2 + +static _lock_t lvgl_api_lock; +static const char *TAG = "lvgl_port"; + +bool lvgl_port_notify_lvgl_flush_ready(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx) +{ + lv_display_t *disp = (lv_display_t *)user_ctx; + lv_display_flush_ready(disp); + return false; +} + +/* Rotate display and touch, when rotated screen in LVGL. Called when driver parameters are updated. */ +static void lvgl_port_update_callback(lv_display_t *disp) +{ + esp_lcd_panel_handle_t panel_handle = lv_display_get_user_data(disp); + lv_display_rotation_t rotation = lv_display_get_rotation(disp); + + switch (rotation) { + case LV_DISPLAY_ROTATION_0: + // Rotate LCD display + esp_lcd_panel_swap_xy(panel_handle, false); + esp_lcd_panel_mirror(panel_handle, true, false); + break; + case LV_DISPLAY_ROTATION_90: + // Rotate LCD display + esp_lcd_panel_swap_xy(panel_handle, true); + esp_lcd_panel_mirror(panel_handle, true, true); + break; + case LV_DISPLAY_ROTATION_180: + // Rotate LCD display + esp_lcd_panel_swap_xy(panel_handle, false); + esp_lcd_panel_mirror(panel_handle, false, true); + break; + case LV_DISPLAY_ROTATION_270: + // Rotate LCD display + esp_lcd_panel_swap_xy(panel_handle, true); + esp_lcd_panel_mirror(panel_handle, false, false); + break; + } +} + +static void lvgl_flush_cb(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map) +{ + lvgl_port_update_callback(disp); + esp_lcd_panel_handle_t panel_handle = lv_display_get_user_data(disp); + int offsetx1 = area->x1; + int offsetx2 = area->x2; + int offsety1 = area->y1; + int offsety2 = area->y2; + // because SPI LCD is big-endian, we need to swap the RGB bytes order + lv_draw_sw_rgb565_swap(px_map, (offsetx2 + 1 - offsetx1) * (offsety2 + 1 - offsety1)); + // copy a buffer's content to a specific area of the display + esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, px_map); +} + +static void lvgl_touch_cb(lv_indev_t * indev, lv_indev_data_t * data) +{ + uint16_t touchpad_x[1] = {0}; + uint16_t touchpad_y[1] = {0}; + uint8_t touchpad_cnt = 0; + + esp_lcd_touch_handle_t touch_pad = lv_indev_get_user_data(indev); + esp_lcd_touch_read_data(touch_pad); + /* Get coordinates */ + bool touchpad_pressed = esp_lcd_touch_get_coordinates(touch_pad, touchpad_x, touchpad_y, NULL, &touchpad_cnt, 1); + + if (touchpad_pressed && touchpad_cnt > 0) { + data->point.x = touchpad_x[0]; + data->point.y = touchpad_y[0]; + data->state = LV_INDEV_STATE_PRESSED; + } else { + data->state = LV_INDEV_STATE_RELEASED; + } +} + +static void increase_lvgl_tick(void *arg) +{ + /* Tell LVGL how many milliseconds has elapsed */ + lv_tick_inc(LVGL_TICK_PERIOD_MS); +} + +static mp_obj_t mp_lv_task_handler(mp_obj_t arg) +{ + lv_timer_handler(); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(mp_lv_task_handler_obj, mp_lv_task_handler); + +static void lvgl_port_task(void *arg) +{ + printf("Starting LVGL task\n"); + uint32_t time_till_next_ms = 5; + // uint32_t time_threshold_ms = 1000 / CONFIG_FREERTOS_HZ; + while (1) { + mp_sched_schedule((mp_obj_t)&mp_lv_task_handler_obj, mp_const_none); + // in case of triggering a task watch dog time out + // time_till_next_ms = MAX(time_till_next_ms, time_threshold_ms); + usleep(1000 * time_till_next_ms); + } +} + +lv_display_t *lvgl_port_init(int h_res, int v_res, esp_lcd_panel_handle_t panel_handle, esp_lcd_touch_handle_t tp_handle) +{ + printf("Initialize LVGL library\n"); + lv_init(); + + // create a lvgl display + lv_display_t *display = lv_display_create(h_res, v_res); + + // alloc draw buffers used by LVGL + // it's recommended to choose the size of the draw buffer(s) to be at least 1/10 screen sized + size_t draw_buffer_sz = h_res * LVGL_DRAW_BUF_LINES * sizeof(lv_color16_t); + + void *buf1 = heap_caps_aligned_alloc(4, draw_buffer_sz, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA); + assert(buf1); + void *buf2 = heap_caps_aligned_alloc(4, draw_buffer_sz, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA); + assert(buf2); + // initialize LVGL draw buffers + lv_display_set_buffers(display, buf1, buf2, draw_buffer_sz, LV_DISPLAY_RENDER_MODE_PARTIAL); + // associate the mipi panel handle to the display + lv_display_set_user_data(display, panel_handle); + // set color depth + lv_display_set_color_format(display, LV_COLOR_FORMAT_RGB565); + // set the callback which can copy the rendered image to an area of the display + lv_display_set_flush_cb(display, lvgl_flush_cb); + + printf("Install LVGL tick timer\n"); + // Tick interface for LVGL (using esp_timer to generate 2ms periodic event) + const esp_timer_create_args_t lvgl_tick_timer_args = { + .callback = &increase_lvgl_tick, + .name = "lvgl_tick" + }; + esp_timer_handle_t lvgl_tick_timer = NULL; + ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer)); + ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, LVGL_TICK_PERIOD_MS * 1000)); + + esp_lcd_panel_io_handle_t tp_io_handle = NULL; + + if (tp_handle) { + static lv_indev_t *indev; + indev = lv_indev_create(); // Input device driver (Touch) + lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER); + lv_indev_set_display(indev, display); + lv_indev_set_user_data(indev, tp_handle); + lv_indev_set_read_cb(indev, lvgl_touch_cb); + } + + printf("Create LVGL task\n"); + xTaskCreate(lvgl_port_task, "LVGL", LVGL_TASK_STACK_SIZE, NULL, LVGL_TASK_PRIORITY, NULL); + + return display; +} + +void lvgl_port_lock(void) +{ + _lock_acquire(&lvgl_api_lock); +} + +void lvgl_port_unlock(void) +{ + _lock_release(&lvgl_api_lock); +} diff --git a/ports/esp32/main_esp32s3_box3/lvgl_port.h b/ports/esp32/main_esp32s3_box3/lvgl_port.h new file mode 100644 index 000000000000..b0e286276d11 --- /dev/null +++ b/ports/esp32/main_esp32s3_box3/lvgl_port.h @@ -0,0 +1,22 @@ +#pragma once + +#include "esp_lcd_touch.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_panel_io.h" +#include "lvgl/lvgl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +lv_display_t *lvgl_port_init(int h_res, int v_res, esp_lcd_panel_handle_t panel_handle, esp_lcd_touch_handle_t tp_handle); + +bool lvgl_port_notify_lvgl_flush_ready(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx); + +void lvgl_port_lock(void); + +void lvgl_port_unlock(void); + +#ifdef __cplusplus +} +#endif diff --git a/ports/esp32/main_esp32s3_box3/main.c b/ports/esp32/main_esp32s3_box3/main.c new file mode 100644 index 000000000000..86f0be515173 --- /dev/null +++ b/ports/esp32/main_esp32s3_box3/main.c @@ -0,0 +1,296 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" +#include "nvs_flash.h" +#include "esp_task.h" +#include "esp_event.h" +#include "esp_log.h" +#include "esp_psram.h" + +#include "py/stackctrl.h" +#include "py/nlr.h" +#include "py/compile.h" +#include "py/runtime.h" +#include "py/persistentcode.h" +#include "py/repl.h" +#include "py/gc.h" +#include "py/mphal.h" +#include "shared/readline/readline.h" +#include "shared/runtime/pyexec.h" +#include "shared/timeutils/timeutils.h" +#include "mbedtls/platform_time.h" + +#include "uart.h" +#include "usb.h" +#include "usb_serial_jtag.h" +#include "modmachine.h" +#include "modnetwork.h" + +#if MICROPY_BLUETOOTH_NIMBLE +#include "extmod/modbluetooth.h" +#endif + +#if MICROPY_PY_ESPNOW +#include "modespnow.h" +#endif + +#include "bsp/display.h" +#include "bsp/touch.h" +#include "bsp/esp-bsp.h" +#include "lvgl_port.h" + +// MicroPython runs as a task under FreeRTOS +#define MP_TASK_PRIORITY (ESP_TASK_PRIO_MIN + 1) + +// Set the margin for detecting stack overflow, depending on the CPU architecture. +#if CONFIG_IDF_TARGET_ESP32C3 +#define MP_TASK_STACK_LIMIT_MARGIN (2048) +#else +#define MP_TASK_STACK_LIMIT_MARGIN (1024) +#endif + +static void init_lvgl_port(); + +int vprintf_null(const char *format, va_list ap) { + // do nothing: this is used as a log target during raw repl mode + return 0; +} + +time_t platform_mbedtls_time(time_t *timer) { + // mbedtls_time requires time in seconds from EPOCH 1970 + + struct timeval tv; + gettimeofday(&tv, NULL); + + return tv.tv_sec + TIMEUTILS_SECONDS_1970_TO_2000; +} + +void mp_task(void *pvParameter) { + volatile uint32_t sp = (uint32_t)esp_cpu_get_sp(); + #if MICROPY_PY_THREAD + mp_thread_init(pxTaskGetStackStart(NULL), MICROPY_TASK_STACK_SIZE / sizeof(uintptr_t)); + #endif + #if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG + // usb_serial_jtag_init(); + #elif CONFIG_USB_OTG_SUPPORTED + // usb_init(); + #endif + #if MICROPY_HW_ENABLE_UART_REPL + uart_stdout_init(); + #endif + machine_init(); + + // Configure time function, for mbedtls certificate time validation. + mbedtls_platform_set_time(platform_mbedtls_time); + + esp_err_t err = esp_event_loop_create_default(); + if (err != ESP_OK) { + ESP_LOGE("esp_init", "can't create event loop: 0x%x\n", err); + } + + void *mp_task_heap = MP_PLAT_ALLOC_HEAP(MICROPY_GC_INITIAL_HEAP_SIZE); + if (mp_task_heap == NULL) { + printf("mp_task_heap allocation failed!\n"); + esp_restart(); + } + +soft_reset: + // initialise the stack pointer for the main thread + mp_stack_set_top((void *)sp); + mp_stack_set_limit(MICROPY_TASK_STACK_SIZE - MP_TASK_STACK_LIMIT_MARGIN); + gc_init(mp_task_heap, mp_task_heap + MICROPY_GC_INITIAL_HEAP_SIZE); + mp_init(); + mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_lib)); + readline_init0(); + + MP_STATE_PORT(native_code_pointers) = MP_OBJ_NULL; + + // initialise peripherals + machine_pins_init(); + #if MICROPY_PY_MACHINE_I2S + machine_i2s_init0(); + #endif + + init_lvgl_port(); + + // run boot-up scripts + pyexec_frozen_module("_boot.py", false); + int ret = pyexec_file_if_exists("boot.py"); + if (ret & PYEXEC_FORCED_EXIT) { + goto soft_reset_exit; + } + if (pyexec_mode_kind == PYEXEC_MODE_FRIENDLY_REPL && ret != 0) { + int ret = pyexec_file_if_exists("main.py"); + if (ret & PYEXEC_FORCED_EXIT) { + goto soft_reset_exit; + } + } + + for (;;) { + if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) { + vprintf_like_t vprintf_log = esp_log_set_vprintf(vprintf_null); + if (pyexec_raw_repl() != 0) { + break; + } + esp_log_set_vprintf(vprintf_log); + } else { + if (pyexec_friendly_repl() != 0) { + break; + } + } + } + +soft_reset_exit: + + #if MICROPY_BLUETOOTH_NIMBLE + mp_bluetooth_deinit(); + #endif + + #if MICROPY_PY_ESPNOW + espnow_deinit(mp_const_none); + MP_STATE_PORT(espnow_singleton) = NULL; + #endif + + machine_timer_deinit_all(); + + #if MICROPY_PY_THREAD + mp_thread_deinit(); + #endif + + // Free any native code pointers that point to iRAM. + if (MP_STATE_PORT(native_code_pointers) != MP_OBJ_NULL) { + size_t len; + mp_obj_t *items; + mp_obj_list_get(MP_STATE_PORT(native_code_pointers), &len, &items); + for (size_t i = 0; i < len; ++i) { + heap_caps_free(MP_OBJ_TO_PTR(items[i])); + } + } + + gc_sweep_all(); + + mp_hal_stdout_tx_str("MPY: soft reboot\r\n"); + + // deinitialise peripherals + machine_pwm_deinit_all(); + // TODO: machine_rmt_deinit_all(); + machine_pins_deinit(); + machine_deinit(); + #if MICROPY_PY_SOCKET_EVENTS + socket_events_deinit(); + #endif + + mp_deinit(); + fflush(stdout); + goto soft_reset; +} + +void boardctrl_startup(void) { + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + nvs_flash_erase(); + nvs_flash_init(); + } +} + +void app_main(void) { + // Hook for a board to run code at start up. + // This defaults to initialising NVS. + MICROPY_BOARD_STARTUP(); + + // Create and transfer control to the MicroPython task. + xTaskCreatePinnedToCore(mp_task, "mp_task", MICROPY_TASK_STACK_SIZE / sizeof(StackType_t), NULL, MP_TASK_PRIORITY, &mp_main_task_handle, MP_TASK_COREID); +} + +void nlr_jump_fail(void *val) { + printf("NLR jump failed, val=%p\n", val); + esp_restart(); +} + +void *esp_native_code_commit(void *buf, size_t len, void *reloc) { + len = (len + 3) & ~3; + uint32_t *p = heap_caps_malloc(len, MALLOC_CAP_EXEC); + if (p == NULL) { + m_malloc_fail(len); + } + if (MP_STATE_PORT(native_code_pointers) == MP_OBJ_NULL) { + MP_STATE_PORT(native_code_pointers) = mp_obj_new_list(0, NULL); + } + mp_obj_list_append(MP_STATE_PORT(native_code_pointers), MP_OBJ_TO_PTR(p)); + if (reloc) { + mp_native_relocate(reloc, buf, (uintptr_t)p); + } + memcpy(p, buf, len); + return p; +} + +MP_REGISTER_ROOT_POINTER(mp_obj_t native_code_pointers); + +static void init_lvgl_port() +{ + printf("Turn off LCD backlight\n"); + bsp_display_brightness_init(); + bsp_display_backlight_off(); + + printf("Initialize LCD\n"); + bsp_display_config_t display_cfg = { + .max_transfer_sz = 320 * 240 * 2, + }; + esp_lcd_panel_handle_t panel_handle = NULL; + esp_lcd_panel_io_handle_t io_handle = NULL; + bsp_display_new(&display_cfg, &panel_handle, &io_handle); + + printf("Turn on LCD backlight\n"); + bsp_display_backlight_on(); + + printf("Initialize touch\n"); + bsp_i2c_init(); + bsp_touch_config_t touch_cfg = { + .dummy = NULL, + }; + esp_lcd_touch_handle_t tp_handle = NULL; + bsp_touch_new(&touch_cfg, &tp_handle); + + lv_display_t *disp = lvgl_port_init(BSP_LCD_H_RES, BSP_LCD_V_RES, panel_handle, tp_handle); + + printf("Register io panel event callback for LVGL flush ready notification\n"); + const esp_lcd_panel_io_callbacks_t cbs = { + .on_color_trans_done = lvgl_port_notify_lvgl_flush_ready, + }; + /* Register done callback */ + ESP_ERROR_CHECK(esp_lcd_panel_io_register_event_callbacks(io_handle, &cbs, disp)); +}