diff --git a/README.md b/README.md index 4bfd6f2..bd5ca4b 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ OpenTyrian with ESP32-P4 and [T-Keyboard S3 Pro](https://github.com/MatheyDeo/Li This is a port of OpenTyrian to the ESP32 platform, originally ported to ESP32 by Gadget Workbench and updated for new hardware with support for [Board Support Packages](https://components.espressif.com/components?q=Board+Support+Package). -The fork was updated to use all the game data directly from flash, as SD cards have a tendency to be unreliable. It now works with ESP-IDF and utilizes the latest [SDL3](https://components.espressif.com/components/georgik/sdl) available at the Espressif Component Registry. +The port works with ESP-IDF and utilizes the latest [SDL3](https://components.espressif.com/components/georgik/sdl) available at the Espressif Component Registry. Game data can be stored either in flash memory or on SD card (ESP32-P4 only). ## Storyline @@ -17,7 +17,7 @@ Tyrian is an arcade-style vertical scrolling shooter. The story is set in 20,031 ## Supported Hardware -- ESP-IDF 5.4+ or later +- ESP-IDF 6.1 or later - **ESP32-P4 boards (16MB Flash):** - [ESP32-P4 Function EV Board](https://components.espressif.com/components/espressif/esp32_p4_function_ev_board_noglib) - 1024x600 RGB display, 32MB PSRAM - [M5Stack Tab5](https://shop.m5stack.com/products/m5stack-tab5-esp32-p4-5-inch-1280-720-mipi-dsi-ips-display-touch-screen-development-board) - 5-inch 1280x720 MIPI-DSI display with touch, 32MB PSRAM @@ -27,6 +27,10 @@ Tyrian is an arcade-style vertical scrolling shooter. The story is set in 20,031 ## ESP32-P4 Features +The ESP32-P4 version includes hardware-accelerated graphics and audio capabilities for optimal performance. + +### Pixel Processing Accelerator (PPA) + The ESP32-P4 version leverages the **Pixel Processing Accelerator (PPA)** for enhanced graphics performance. The PPA is a hardware accelerator specifically designed for image and graphics processing operations, enabling smooth scaling of the original game graphics to full display resolution with minimal CPU overhead. The [PPA peripheral](https://docs.espressif.com/projects/esp-idf/en/stable/esp32p4/api-reference/peripherals/ppa.html) provides hardware acceleration for: @@ -37,28 +41,135 @@ The [PPA peripheral](https://docs.espressif.com/projects/esp-idf/en/stable/esp32 This hardware acceleration ensures OpenTyrian runs smoothly at full display resolution while maintaining responsive gameplay. -## Flash Storage and Partition Tables +### Audio System + +OpenTyrian on ESP32-P4 includes a complete audio implementation supporting all original game sound effects and music. + +**Audio Hardware:** +- ES8311 audio codec with I2S interface +- 22.05kHz sample rate (downsampled from 44.1kHz for memory efficiency) +- Mono output optimized for embedded systems +- TX-only I2S configuration to minimize DMA memory usage + +**Audio Features:** +- All original sound effects (explosions, shooting, pickups, UI sounds) +- OPL/AdLib emulator for music playback +- Real-time audio mixing with proper volume control +- Memory-efficient design using PSRAM for buffers + +**Technical Implementation:** +The audio system uses a custom SDL backend that integrates with the ESP-IDF audio codec framework. Sound samples are loaded from the game data files and mixed in real-time by the audio callback. The system handles: +- Automatic volume initialization (75% music, 100% SFX) +- Codec mute/unmute during initialization to prevent startup artifacts +- NULL pointer safety for completed sounds +- Efficient memory usage with TX-only I2S mode and mono output + +## Game Data Storage + +OpenTyrian supports two methods for storing game data: + +### Storage Options Overview + +| Method | Boards Supported | Capacity | Setup | +|--------|-----------------|----------|-------| +| **Internal Flash** | All boards | 11-12 MB | Automatic (embedded in firmware) | +| **SD Card** | ESP32-P4 only | Up to SD card size | Manual (copy data to SD card) | -OpenTyrian uses custom partition tables optimized for each board architecture to maximize available storage for game assets: +### ESP32-P4 Automatic Fallback -### ESP32-P4 Boards (16MB Flash) +On ESP32-P4 boards, the game automatically detects and uses available game data in this priority order: + +1. **SD Card** (if present with Tyrian data at `/sdcard/tyrian/data/`) +2. **Internal Flash** (fallback if SD card unavailable or missing data) + +This fallback system is completely automatic—no configuration needed. Simply insert an SD card with game data and the game will use it; otherwise, it uses the internal flash storage. + +### Internal Flash Storage + +All boards support game data stored directly in flash memory using LittleFS filesystem. + +#### ESP32-P4 Boards (16MB Flash) - **Partition Table**: `partitions.csv` - **Factory App**: 3MB (main application) - **LittleFS Storage**: 11MB (game data and assets) -- **Configuration**: All game assets loaded directly from flash storage for reliability +- **Mount Point**: `/flash/` -### ESP32-S3 Boards (16MB Flash) +#### ESP32-S3 Boards (16MB Flash) - **Partition Table**: `partitions_esp32s3_16mb.csv` - **Factory App**: 3MB (main application) - **LittleFS Storage**: 12MB (game data and assets) -- **PSRAM Configuration**: Optimized for frame buffers and large data structures +- **Mount Point**: `/flash/` + +Flash storage is embedded during the build process—no additional setup required. + +### SD Card Storage (ESP32-P4 Only) + +ESP32-P4 Function EV Board supports loading game data from SD card, which offers several advantages: + +- **Larger Capacity**: Store additional game mods or custom assets +- **Easy Updates**: Change game data without reflashing firmware +- **Development**: Test new assets quickly during development + +#### Supported Hardware +- ESP32-P4 Function EV Board (SDMMC interface) +- SD card interface pins: GPIO 39-44 (D0-D3, CMD, CLK) -### Storage Architecture -The game data is stored in a LittleFS filesystem containing: -- Original Tyrian game assets (sprites, levels, sounds, music) -- Configuration files and save data -- All assets pre-loaded during build process -- No SD card dependency for maximum reliability +#### SD Card Setup + +1. **Format SD Card** (if needed): + - Filesystem: FAT32 + - Cluster size: Default or 4KB + +2. **Create Directory Structure**: + ``` + /sdcard/ + └── tyrian/ + └── data/ + ├── tyrian1.lvl + ├── tyrian.hdt + ├── voices.snd + └── [all other game files] + ``` + +3. **Copy Game Data**: + - Download Tyrian data files from the official OpenTyrian project + - Extract and copy all files to `/sdcard/tyrian/data/` on the SD card + - Ensure `tyrian1.lvl` exists in the data directory (used for detection) + +4. **Insert SD Card**: + - Power off the board + - Insert SD card into the slot + - Power on—the game will automatically detect and use SD card data + +#### SD Card Behavior + +- **Hot Plugging**: Not supported. Insert SD card before powering on. +- **Card Removal**: Game may crash if SD card is removed during play. +- **Empty/Missing Data**: Falls back to internal flash automatically. +- **Corrupted Card**: Falls back to internal flash with error message. + +#### Verification + +When the game boots, watch the serial output for confirmation: + +``` +OpenTyrian File System Initialization +============================================== + +[1/2] Attempting to mount SD card... +SUCCESS: SD card mounted at /sdcard +Found Tyrian data on SD card at /sdcard/tyrian/data + +>>> Using SD card for game data <<< +``` + +If SD card is not used: +``` +[2/2] Attempting to mount internal flash LittleFS... +SUCCESS: Internal flash LittleFS mounted at /flash + +>>> Using internal flash for game data <<< +``` ## Game Controls @@ -98,7 +209,7 @@ For the quickest installation, use our web-based installer (Chrome/Edge browsers ## Building from Source ### Prerequisites -- ESP-IDF 5.4+ installed and configured +- ESP-IDF 6.1 installed and configured - Git for cloning the repository - USB cable for flashing @@ -197,8 +308,10 @@ SDKCONFIG_DEFAULTS="sdkconfig.defaults.m5stack_core_s3" idf.py -B "build.m5stack - **Display**: 1024x600 RGB LCD - **Storage**: 16MB Flash + 32MB PSRAM (Hex mode) - **PPA Acceleration**: Hardware-accelerated 3x scaling (320x200 → 960x600) +- **Audio**: ES8311 codec with I2S, 22.05kHz mono output, all SFX and music working - **RGB Display**: Direct RGB interface for minimal latency - **USB HID**: Full keyboard and mouse support +- **SD Card**: SDMMC interface with automatic fallback to flash storage - **Partition Table**: `partitions.csv` (3MB app + 11MB storage) #### ESP32-S3-BOX-3 @@ -284,7 +397,7 @@ rm -rf support/ logs/ - Check for proper touch controller initialization in logs **Build errors:** -- Ensure ESP-IDF 5.4+ is installed and properly configured +- Ensure ESP-IDF 6.1 is installed and properly configured - Using espbrew: Check build logs in `./logs/` directory - Using manual builds: Clean specific build directory `rm -rf build.{board_name}` - Check component dependencies are properly resolved @@ -304,6 +417,19 @@ rm -rf support/ logs/ - ESP32-P4 boards use Hex PSRAM mode - Large frame buffers should be allocated in PSRAM, not internal RAM +**SD card issues (ESP32-P4):** +- Game not detecting SD card: Verify `tyrian1.lvl` exists at `/sdcard/tyrian/data/` +- SD card mounting fails: Check SD card is formatted as FAT32 +- Automatic fallback not working: Check boot logs for filesystem initialization messages +- Using wrong mount point: ESP32-P4 uses `/sdcard`, not `/sd` (changed from earlier versions) + +**Audio issues (ESP32-P4):** +- No sound effects: Verify audio codec is initialized in boot logs (look for "ES8311" and "I2S_IF" messages) +- Sound but no music: Music files may not be loaded; check for "audio loaded" message after sound files load +- Distorted audio: Check that I2S_MCLK_MULTIPLE_384 is configured for ES8311 codec at 22.05kHz +- Audio crashes: Ensure audio task stack size is sufficient (12KB recommended for OPL emulator) +- Startup sounds: Audio codec is muted during initialization to prevent artifacts; this is normal behavior + ### Performance Optimization - **M5Stack Tab5**: Hardware scaling provides optimal performance @@ -316,15 +442,17 @@ If you'd like to extend the project by using [T-Keyboard S3 Pro](https://lilygo. ## Acknowledgements -This port is based on the work of the original OpenTyrian project (https://github.com/jkirsons/OpenTyrian) and an ESP32 port by Gadget Workbench, which was initially created for ESP-WROVER and ESP-IDF 4.2. +This port is based on the work of the original OpenTyrian project (https://github.com/jkirsons/OpenTyrian) and an ESP32 port by Gadget Workbench, which was initially created for ESP-WROVER and ESP-IDF 4.2. The current version has been significantly updated to: -- Support ESP-IDF 5.4+ with modern Board Support Packages (ESP-BSP) +- Support ESP-IDF 6.1 with modern Board Support Packages (ESP-BSP) +- Implement complete audio system with ES8311 codec (all SFX and music working) - Utilize SDL3 from the [Espressif Component Registry](https://components.espressif.com/components/georgik/sdl/) - Add M5Stack Tab5 support with optimized landscape orientation and touch input - Implement hardware-accelerated scaling using ESP32-P4 PPA - Include comprehensive USB HID keyboard support -- Provide game data directly from flash storage for reliability +- Provide flexible game data storage (flash and SD card with automatic fallback) +- Optimize memory usage for DMA-constrained ESP32-P4 architecture Special thanks to: - The OpenTyrian development team for the original open-source port diff --git a/components/OpenTyrian/CMakeLists.txt b/components/OpenTyrian/CMakeLists.txt index 7cda760..23a2a90 100644 --- a/components/OpenTyrian/CMakeLists.txt +++ b/components/OpenTyrian/CMakeLists.txt @@ -53,7 +53,7 @@ idf_component_register( "std_support.c" "loudness.c" INCLUDE_DIRS "." - REQUIRES georgik__sdl fatfs littlefs usb usb_host_hid vfs esp_driver_sdspi esp_driver_sdmmc sdmmc + REQUIRES georgik__sdl fatfs littlefs usb usb_host_hid vfs esp_driver_sdspi esp_driver_sdmmc sdmmc georgik__sdl_audio ) # Reduce warning level for now diff --git a/components/OpenTyrian/file.c b/components/OpenTyrian/file.c index f49e12e..03b6ed5 100644 --- a/components/OpenTyrian/file.c +++ b/components/OpenTyrian/file.c @@ -25,18 +25,27 @@ #include #include -// #include "esp_vfs_fat.h" +#include "esp_vfs_fat.h" #include "esp_vfs.h" #include "esp_littlefs.h" #include "driver/sdmmc_host.h" #include "driver/sdspi_host.h" +// Include BSP SD card support for ESP32-P4 Function EV Board +#if CONFIG_SDL_BSP_ESP32_P4_FUNCTION_EV +#include "bsp/esp32_p4_function_ev_board.h" +#endif + #define MODE_SPI 1 #define PIN_NUM_MISO CONFIG_HW_SD_PIN_NUM_MISO #define PIN_NUM_MOSI CONFIG_HW_SD_PIN_NUM_MOSI #define PIN_NUM_CLK CONFIG_HW_SD_PIN_NUM_CLK #define PIN_NUM_CS CONFIG_HW_SD_PIN_NUM_CS +// Track which filesystem is available +static bool sd_card_available = false; +static bool flash_available = false; + const char *custom_data_dir = NULL; static bool init_SD = false; @@ -82,26 +91,103 @@ void listFiles(const char *dirname) { +// Check if Tyrian data exists on SD card +static bool check_sd_card_data(void) { + const char *test_file = BSP_SD_MOUNT_POINT "/tyrian/data/tyrian1.lvl"; + FILE *f = fopen(test_file, "r"); + + if (f) { + fclose(f); + printf("Found Tyrian data on SD card at %s\n", BSP_SD_MOUNT_POINT "/tyrian/data"); + return true; + } + + printf("Tyrian data not found on SD card (checked %s)\n", test_file); + return false; +} + +// Check if Tyrian data exists on internal flash +static bool check_flash_data(void) { + const char *test_file = "/flash/tyrian/data/tyrian1.lvl"; + FILE *f = fopen(test_file, "r"); + + if (f) { + fclose(f); + printf("Found Tyrian data on internal flash at /flash/tyrian/data\n"); + return true; + } + + printf("Tyrian data not found on internal flash (checked %s)\n", test_file); + return false; +} + void SDL_InitFS(void) { - printf("Initialising File System\n"); + printf("==============================================\n"); + printf("OpenTyrian File System Initialization\n"); + printf("==============================================\n"); + +#if CONFIG_SDL_BSP_ESP32_P4_FUNCTION_EV + // Try to mount SD card first (if ESP32-P4 Function EV Board) + printf("\n[1/2] Attempting to mount SD card...\n"); + + esp_err_t sd_err = bsp_sdcard_mount(); + if (sd_err == ESP_OK) { + printf("SUCCESS: SD card mounted at %s\n", BSP_SD_MOUNT_POINT); + + // Check if Tyrian data exists on SD card + if (check_sd_card_data()) { + sd_card_available = true; + printf("\n>>> Using SD card for game data <<<\n"); + init_SD = true; + return; + } else { + printf("INFO: SD card mounted but no Tyrian data found\n"); + // Unmount SD card since we don't need it + bsp_sdcard_unmount(); + } + } else { + printf("INFO: SD card not available or mount failed (error: 0x%x)\n", sd_err); + printf(" This is normal if no SD card is inserted\n"); + } +#else + printf("INFO: SD card support not available for this board\n"); +#endif + + // Fall back to internal flash LittleFS + printf("\n[2/2] Attempting to mount internal flash LittleFS...\n"); - // Define the LittleFS configuration esp_vfs_littlefs_conf_t conf = { - .base_path = "/sd", + .base_path = "/flash", .partition_label = "storage", .format_if_mount_failed = false, .dont_mount = false, }; - // Use the API to mount and possibly format the file system - esp_err_t err = esp_vfs_littlefs_register(&conf); - if (err != ESP_OK) { - printf("Failed to mount or format filesystem\n"); + esp_err_t flash_err = esp_vfs_littlefs_register(&conf); + if (flash_err != ESP_OK) { + printf("ERROR: Failed to mount internal flash filesystem (error: 0x%x)\n", flash_err); + printf("FATAL ERROR: No game data source available!\n"); + printf("Please ensure either:\n"); + printf(" 1. SD card with tyrian/data is inserted, OR\n"); + printf(" 2. Firmware includes LittleFS partition with game data\n"); + return; + } + + printf("SUCCESS: Internal flash LittleFS mounted at /flash\n"); + + // Verify data exists on flash + if (check_flash_data()) { + flash_available = true; + printf("\n>>> Using internal flash for game data <<<\n"); + printf("Listing files in /flash:\n"); + listFiles("/flash"); } else { - printf("Filesystem mounted\n"); - printf("Listing files in /:\n"); - listFiles("/sd"); + printf("ERROR: No Tyrian data found on internal flash!\n"); + printf("Please flash firmware with embedded game data or use SD card\n"); } + + printf("==============================================\n"); + init_SD = true; } void Init_SD() @@ -114,39 +200,56 @@ void Init_SD() // finds the Tyrian data directory const char *data_dir( void ) { - return "/sd/tyrian/data"; + // Return the appropriate path based on which filesystem is available +#if CONFIG_SDL_BSP_ESP32_P4_FUNCTION_EV + if (sd_card_available) { + printf("data_dir: Using SD card path: %s/tyrian/data\n", BSP_SD_MOUNT_POINT); + return BSP_SD_MOUNT_POINT "/tyrian/data"; + } +#endif + + if (flash_available) { + printf("data_dir: Using flash path: /flash/tyrian/data\n"); + return "/flash/tyrian/data"; + } + + // Fallback to original logic for custom data dir or other configurations const char *dirs[] = { - "/sd/tyrian/data", - custom_data_dir, - TYRIAN_DIR, - "data", - ".", +#if CONFIG_SDL_BSP_ESP32_P4_FUNCTION_EV + BSP_SD_MOUNT_POINT "/tyrian/data", // Try SD card first +#endif + "/flash/tyrian/data", // Then try flash + custom_data_dir, // Custom path if set + TYRIAN_DIR, // Default path + "data", // Relative path + ".", // Current directory }; - + static const char *dir = NULL; - + if (dir != NULL) return dir; - + for (uint i = 0; i < COUNTOF(dirs); ++i) { if (dirs[i] == NULL) continue; - + FILE *f = dir_fopen(dirs[i], "tyrian1.lvl", "rb"); if (f) { efclose(f); - + dir = dirs[i]; + printf("data_dir: Found Tyrian data at: %s\n", dir); break; } } - + if (dir == NULL) // data not found dir = ""; - + return dir; } diff --git a/components/OpenTyrian/keyboard.c b/components/OpenTyrian/keyboard.c index b819b45..07ba2ed 100644 --- a/components/OpenTyrian/keyboard.c +++ b/components/OpenTyrian/keyboard.c @@ -28,6 +28,7 @@ #include "SDL3/SDL_keyboard.h" #include "SDL_internal.h" #include "events/SDL_keyboard_c.h" +#include "events/SDL_mouse_c.h" #include #include "freertos/FreeRTOS.h" @@ -36,14 +37,15 @@ #include "freertos/queue.h" #include "esp_err.h" #include "esp_log.h" -#include "usb/usb_host.h" #include "errno.h" #include "driver/gpio.h" +#include "soc/soc_caps.h" +// USB OTG support - always include headers, but will detect at runtime +#include "usb/usb_host.h" #include "usb/hid_host.h" #include "usb/hid_usage_keyboard.h" #include "usb/hid_usage_mouse.h" - #include #include @@ -63,12 +65,15 @@ Uint16 lastmouse_x, lastmouse_y; JE_boolean mouse_pressed[3] = {false, false, false}; Uint16 mouse_x, mouse_y; +// Variables to track if mouse position was reset by game +bool mouse_position_reset = false; + Uint8 keysactive[SDL_SCANCODE_COUNT]; #ifdef NDEBUG bool input_grab_enabled = true; #else -bool input_grab_enabled = false; +bool input_grab_enabled = true; // Always enable mouse input grab for ESP32 port #endif QueueHandle_t app_event_queue = NULL; @@ -414,48 +419,63 @@ static void hid_host_mouse_report_callback(const uint8_t *const data, const int return; } - static int x_pos = 160; // Start at screen center - static int y_pos = 100; // Start at screen center + // Track absolute mouse position ourselves + // Start at center (159, 100) and accumulate displacement + static int tracked_x = 159; + static int tracked_y = 100; - // Calculate absolute position from displacement - x_pos += mouse_report->x_displacement; - y_pos += mouse_report->y_displacement; + // Check if game reset the mouse position + if (mouse_position_reset) { + tracked_x = mouse_x; + tracked_y = mouse_y; + mouse_position_reset = false; + } + + // Update tracked position with displacement + tracked_x += mouse_report->x_displacement; + tracked_y += mouse_report->y_displacement; - // Constrain mouse position to screen bounds (320x200 for Tyrian) - if (x_pos < 0) x_pos = 0; - if (x_pos >= 320) x_pos = 319; - if (y_pos < 0) y_pos = 0; - if (y_pos >= 200) y_pos = 199; + // Constrain to screen bounds (320x200 for Tyrian) + if (tracked_x < 0) tracked_x = 0; + if (tracked_x >= 320) tracked_x = 319; + if (tracked_y < 0) tracked_y = 0; + if (tracked_y >= 200) tracked_y = 199; - // Update OpenTyrian mouse variables - mouse_x = (Uint16)x_pos; - mouse_y = (Uint16)y_pos; + // Update global mouse position directly + mouse_x = (Uint16)tracked_x; + mouse_y = (Uint16)tracked_y; lastmouse_x = mouse_x; lastmouse_y = mouse_y; - // Update button states - uint8_t buttons = 0; - if (mouse_report->buttons.button1) buttons |= 1; // Left button - if (mouse_report->buttons.button2) buttons |= 2; // Right button - if (mouse_report->buttons.button3) buttons |= 4; // Middle button (if present) - - lastmouse_but = buttons; - mousedown = (buttons != 0); - newmouse = true; - - // Update individual button press states - mouse_pressed[0] = mouse_report->buttons.button1; // Left - mouse_pressed[1] = mouse_report->buttons.button2; // Right - mouse_pressed[2] = mouse_report->buttons.button3; // Middle - - hid_print_new_device_report_header(HID_PROTOCOL_MOUSE); - - printf("X: %06d\tY: %06d\t|%c|%c|%c|\r", - x_pos, y_pos, - (mouse_report->buttons.button1 ? 'o' : ' '), - (mouse_report->buttons.button2 ? 'o' : ' '), - (mouse_report->buttons.button3 ? 'o' : ' ')); - fflush(stdout); + // Get registered mouse device for button events + int num_mice; + SDL_MouseID *mouse_ids = SDL_GetMice(&num_mice); + if (num_mice == 0) { + return; + } + SDL_MouseID mouseID = mouse_ids[0]; + + // Send button events to SDL (only on state changes) + // Note: button2 and button3 are swapped - button3 is right, button2 is middle + if (mouse_report->buttons.button1 && !mouse_pressed[0]) + SDL_SendMouseButton(SDL_GetTicks(), NULL, mouseID, SDL_BUTTON_LEFT, true); + if (!mouse_report->buttons.button1 && mouse_pressed[0]) + SDL_SendMouseButton(SDL_GetTicks(), NULL, mouseID, SDL_BUTTON_LEFT, false); + + if (mouse_report->buttons.button3 && !mouse_pressed[1]) + SDL_SendMouseButton(SDL_GetTicks(), NULL, mouseID, SDL_BUTTON_RIGHT, true); + if (!mouse_report->buttons.button3 && mouse_pressed[1]) + SDL_SendMouseButton(SDL_GetTicks(), NULL, mouseID, SDL_BUTTON_RIGHT, false); + + if (mouse_report->buttons.button2 && !mouse_pressed[2]) + SDL_SendMouseButton(SDL_GetTicks(), NULL, mouseID, SDL_BUTTON_MIDDLE, true); + if (!mouse_report->buttons.button2 && mouse_pressed[2]) + SDL_SendMouseButton(SDL_GetTicks(), NULL, mouseID, SDL_BUTTON_MIDDLE, false); + + // Update button state tracking for next callback (swapped to match above) + mouse_pressed[0] = mouse_report->buttons.button1; + mouse_pressed[1] = mouse_report->buttons.button3; + mouse_pressed[2] = mouse_report->buttons.button2; } static void hid_host_generic_report_callback(const uint8_t *const data, const int length) @@ -653,65 +673,77 @@ void* usb_event_handler_thread(void* arg) void init_keyboard(void) { SDL_AddKeyboard(1, "Virtual Keyboard"); + SDL_AddMouse(1, "USB Mouse"); // Register mouse device with SDL newkey = newmouse = false; keydown = mousedown = false; - ESP_LOGI(TAG, "HID Keyboard"); + // Runtime check: Only initialize USB if USB OTG peripheral is available + if (SOC_USB_OTG_PERIPH_NUM > 0) { + ESP_LOGI(TAG, "Initializing USB HID Keyboard"); - // Init BOOT button: Pressing the button simulates app request to exit - const gpio_config_t input_pin = { - .pin_bit_mask = BIT64(APP_QUIT_PIN), - .mode = GPIO_MODE_INPUT, - .pull_up_en = GPIO_PULLUP_ENABLE, - .intr_type = GPIO_INTR_NEGEDGE, - }; - ESP_ERROR_CHECK(gpio_config(&input_pin)); - ESP_ERROR_CHECK(gpio_install_isr_service(ESP_INTR_FLAG_LEVEL1)); - ESP_ERROR_CHECK(gpio_isr_handler_add(APP_QUIT_PIN, gpio_isr_cb, NULL)); - - // Initialize semaphore for synchronization - sem_init(&usb_task_semaphore, 0, 0); - - // Create usb_lib_task thread - pthread_t usb_thread; - pthread_attr_t usb_thread_attr; - pthread_attr_init(&usb_thread_attr); - pthread_attr_setstacksize(&usb_thread_attr, 8912); - - int ret = pthread_create(&usb_thread, &usb_thread_attr, usb_lib_thread, NULL); - if (ret != 0) { - ESP_LOGE(TAG, "Failed to create USB thread: %d", ret); - return; - } + // Init BOOT button: Pressing the button simulates app request to exit + const gpio_config_t input_pin = { + .pin_bit_mask = BIT64(APP_QUIT_PIN), + .mode = GPIO_MODE_INPUT, + .pull_up_en = GPIO_PULLUP_ENABLE, + .intr_type = GPIO_INTR_NEGEDGE, + }; + ESP_ERROR_CHECK(gpio_config(&input_pin)); + ESP_ERROR_CHECK(gpio_install_isr_service(ESP_INTR_FLAG_LEVEL1)); + ESP_ERROR_CHECK(gpio_isr_handler_add(APP_QUIT_PIN, gpio_isr_cb, NULL)); + + // Initialize semaphore for synchronization + sem_init(&usb_task_semaphore, 0, 0); + + // Create usb_lib_task thread + pthread_t usb_thread; + pthread_attr_t usb_thread_attr; + pthread_attr_init(&usb_thread_attr); + pthread_attr_setstacksize(&usb_thread_attr, 8912); + + int ret = pthread_create(&usb_thread, &usb_thread_attr, usb_lib_thread, NULL); + if (ret != 0) { + ESP_LOGE(TAG, "Failed to create USB thread: %d", ret); + return; + } - // Wait for notification from usb_lib_thread to proceed (using semaphore) - sem_wait(&usb_task_semaphore); - - // HID host driver configuration - const hid_host_driver_config_t hid_host_driver_config = { - .create_background_task = false, // No background task, handled manually - .task_priority = 5, // Priority doesn't apply to pthreads - .stack_size = 8912, // Stack size for thread (set manually if needed) - .core_id = 0, - .callback = hid_host_device_callback, - .callback_arg = NULL - }; + // Wait for notification from usb_lib_thread to proceed (using semaphore) + sem_wait(&usb_task_semaphore); - ESP_ERROR_CHECK(hid_host_install(&hid_host_driver_config)); + // HID host driver configuration + const hid_host_driver_config_t hid_host_driver_config = { + .create_background_task = false, // No background task, handled manually + .task_priority = 5, // Priority doesn't apply to pthreads + .stack_size = 8912, // Stack size for thread (set manually if needed) + .core_id = 0, + .callback = hid_host_device_callback, + .callback_arg = NULL + }; - // Create queue (keeping xQueue for event handling if necessary) - app_event_queue = xQueueCreate(10, sizeof(app_event_queue_t)); + esp_err_t err = hid_host_install(&hid_host_driver_config); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to install HID host: %s", esp_err_to_name(err)); + // Don't fail initialization, just log the error + return; + } - ESP_LOGI(TAG, "Waiting for HID Device to be connected"); + // Create queue (keeping xQueue for event handling if necessary) + app_event_queue = xQueueCreate(10, sizeof(app_event_queue_t)); - // Start the HID event handler thread - ret = pthread_create(&usb_event_thread, NULL, usb_event_handler_thread, NULL); - if (ret != 0) { - ESP_LOGE(TAG, "Failed to create HID event handler thread: %d", ret); - return; + ESP_LOGI(TAG, "Waiting for HID Device to be connected"); + + // Start the HID event handler thread + ret = pthread_create(&usb_event_thread, NULL, usb_event_handler_thread, NULL); + if (ret != 0) { + ESP_LOGE(TAG, "Failed to create HID event handler thread: %d", ret); + return; + } + pthread_detach(usb_event_thread); + } else { + ESP_LOGI(TAG, "USB HID Keyboard not supported on this chip (no USB OTG peripheral)"); + ESP_LOGI(TAG, "Keyboard input via SDL only"); } - pthread_detach(usb_event_thread); } void input_grab( bool enable ) @@ -732,6 +764,8 @@ void set_mouse_position( int x, int y ) { mouse_x = x; mouse_y = y; + // Signal to HID callback that position was reset + mouse_position_reset = true; } } @@ -770,6 +804,37 @@ void service_SDL_events(JE_boolean clear_new) keysactive[event.key.scancode] = false; // Update key state break; } + + // Mouse events + case SDL_EVENT_MOUSE_MOTION: { + // Update absolute mouse position from the event + // The game will calculate displacement from center (159, 100) + mouse_x = (Uint16)event.motion.x; + mouse_y = (Uint16)event.motion.y; + lastmouse_x = mouse_x; + lastmouse_y = mouse_y; + break; + } + case SDL_EVENT_MOUSE_BUTTON_DOWN: { + lastmouse_but = event.button.button; + mousedown = true; + newmouse = true; + // Update individual button states + if (event.button.button == SDL_BUTTON_LEFT) mouse_pressed[0] = true; + if (event.button.button == SDL_BUTTON_RIGHT) mouse_pressed[1] = true; + if (event.button.button == SDL_BUTTON_MIDDLE) mouse_pressed[2] = true; + break; + } + case SDL_EVENT_MOUSE_BUTTON_UP: { + lastmouse_but = 0; + mousedown = false; + // Update individual button states + if (event.button.button == SDL_BUTTON_LEFT) mouse_pressed[0] = false; + if (event.button.button == SDL_BUTTON_RIGHT) mouse_pressed[1] = false; + if (event.button.button == SDL_BUTTON_MIDDLE) mouse_pressed[2] = false; + break; + } + case SDL_EVENT_QUIT: // Handle quit event (if needed) break; diff --git a/components/OpenTyrian/loudness.c b/components/OpenTyrian/loudness.c index 4b1a08d..e6d6578 100644 --- a/components/OpenTyrian/loudness.c +++ b/components/OpenTyrian/loudness.c @@ -23,6 +23,9 @@ #include "opentyr.h" #include "params.h" +// ESP32 audio backend +#include "SDL_audio_esp32.h" + float music_volume = 0, sample_volume = 0; float volume = 0; @@ -46,47 +49,44 @@ Uint8 channel_vol[SFX_CHANNELS]; int sound_init_state = false; int freq = 11025 * OUTPUT_QUALITY; -SDL_AudioStream *audio_stream = NULL; - +// Audio callback (will be registered with ESP32 backend) void audio_cb( void *userdata, unsigned char *feedme, int howmuch ); void load_song( unsigned int song_num ); -SDL_AudioDeviceID dev_id; bool init_audio(void) { if (audio_disabled) return false; - /* SDL_AudioSpec ask, got; - - ask.freq = freq; - ask.format = (BYTES_PER_SAMPLE == 2) ? SDL_AUDIO_S16 : SDL_AUDIO_S8; - ask.channels = 1; - - printf("\trequested %d Hz, %d channels\n", ask.freq, ask.channels); + // ESP32-P4 Audio Backend Integration + // Following ESP32-Quake pattern for audio initialization + printf("Initializing ESP32-P4 audio backend...\n"); - // Update to SDL3 OpenAudioDevice - dev_id = SDL_OpenAudioDevice(0, &ask); - if (dev_id == 0) { - fprintf(stderr, "error: failed to initialize SDL audio: %s\n", SDL_GetError()); + // Initialize ESP32 audio hardware (Quake-style) + extern bool SDL_ESP_Audio_Init(void); + if (!SDL_ESP_Audio_Init()) { + fprintf(stderr, "error: failed to initialize ESP32 audio backend\n"); audio_disabled = true; return false; } - printf("\tobtained %d Hz, %d channels\n", got.freq, got.channels); - - // Create Audio Stream - audio_stream = SDL_CreateAudioStream(&ask, &got); - if (!audio_stream) { - fprintf(stderr, "error: failed to create SDL_AudioStream: %s\n", SDL_GetError()); - audio_disabled = true; - return false; - } + printf("\trequested %d Hz, %d channels\n", freq, 1); + printf("\tESP32 audio backend initialized: 44100 Hz, 2 channels (stereo)\n"); + // Initialize OPL/AdLib emulator for music opl_init(); - SDL_PauseAudioDevice(dev_id); // Start playing audio -*/ + // Set initial volume (max volume = 255) + // This is critical - without this, sample_volume stays at 0 and all SFX are silent! + set_volume(192, 255); // Music at 75%, SFX at 100% + + sound_init_state = true; + + // Register audio callback with ESP32 backend + extern void SDL_ESP_RegisterAudioCallback(void (*callback)(void *, uint8_t *, int), void *userdata); + SDL_ESP_RegisterAudioCallback((void (*)(void *, uint8_t *, int))audio_cb, NULL); + + printf("\tAudio system ready\n"); return true; } @@ -97,7 +97,12 @@ IRAM_ATTR void audio_cb(void *user_data, unsigned char *sdl_buffer, int howmuch) static long ct = 0; SAMPLE_TYPE *feedme = (SAMPLE_TYPE *)sdl_buffer; - music_disabled = true; + + // Clear buffer to silence first - prevents garbage data from being played + memset(feedme, 0, howmuch); + + // Music processing (disabled until load_song is implemented) + // music_disabled = true; // REMOVED: This was preventing all audio if (!music_disabled && !music_stopped) { SAMPLE_TYPE *music_pos = feedme; @@ -129,6 +134,10 @@ IRAM_ATTR void audio_cb(void *user_data, unsigned char *sdl_buffer, int howmuch) { for (int ch = 0; ch < SFX_CHANNELS; ch++) { + // Skip this channel if it's not active (NULL pointer check) + if (channel_pos[ch] == NULL || channel_buffer[ch] == NULL) + continue; + volume = sample_volume * (channel_vol[ch] / (float)SFX_CHANNELS); unsigned int qu = ((unsigned)howmuch > channel_len[ch] ? channel_len[ch] : (unsigned)howmuch) / BYTES_PER_SAMPLE; @@ -154,29 +163,19 @@ IRAM_ATTR void audio_cb(void *user_data, unsigned char *sdl_buffer, int howmuch) } } - // Use SDL_AudioStream for conversion - if (audio_stream) { - SDL_PutAudioStreamData(audio_stream, sdl_buffer, howmuch); - int converted_size = SDL_GetAudioStreamData(audio_stream, sdl_buffer, howmuch); - if (converted_size < 0) { - fprintf(stderr, "error: SDL_GetAudioStreamData failed: %s\n", SDL_GetError()); - } - } + // ESP32 backend handles the output directly, no conversion needed + // The codec device expects int16 stereo samples } void deinit_audio(void) { if (audio_disabled) return; -/* - SDL_PauseAudioDevice(dev_id); // Pause audio - - SDL_CloseAudioDevice(dev_id); // Close audio device - if (audio_stream) { - SDL_DestroyAudioStream(audio_stream); // Destroy the audio stream - audio_stream = NULL; - } + // Shutdown ESP32 audio backend + extern void SDL_ESP_Audio_Shutdown(void); + SDL_ESP_Audio_Shutdown(); + // Free audio buffers for (unsigned int i = 0; i < SFX_CHANNELS; i++) { free(channel_buffer[i]); channel_buffer[i] = channel_pos[i] = NULL; @@ -184,7 +183,7 @@ void deinit_audio(void) { } lds_free(); - */ + sound_init_state = false; } @@ -263,28 +262,36 @@ void JE_multiSamplePlay(JE_byte *buffer, JE_word size, JE_byte chan, JE_byte vol { if (audio_disabled || samples_disabled) return; - -// SDL_LockAudio(); - -// free(channel_buffer[chan]); - -// channel_len[chan] = size * BYTES_PER_SAMPLE * SAMPLE_SCALING; -// channel_buffer[chan] = malloc(channel_len[chan]); -// channel_pos[chan] = channel_buffer[chan]; -// channel_vol[chan] = vol + 1; - -// for (int i = 0; i < size; i++) -// { -// for (int ex = 0; ex < SAMPLE_SCALING; ex++) -// { -// #if (BYTES_PER_SAMPLE == 2) -// channel_buffer[chan][(i * SAMPLE_SCALING) + ex] = (Sint8)buffer[i] << 8; -// #else /* BYTES_PER_SAMPLE */ -// channel_buffer[chan][(i * SAMPLE_SCALING) + ex] = (Sint8)buffer[i]; -// #endif /* BYTES_PER_SAMPLE */ -// } -// } - -// SDL_UnlockAudio(); + + // ESP32: No SDL_LockAudio needed - audio task runs continuously + // The audio callback reads these buffers, so we just need to update them atomically + + // Free any existing buffer in this channel + free(channel_buffer[chan]); + + // Calculate buffer size: upsample from SOURCE_QUALITY to OUTPUT_QUALITY + // SAMPLE_SCALING = OUTPUT_QUALITY (e.g., 4x for 11kHz->44kHz) + channel_len[chan] = size * BYTES_PER_SAMPLE * SAMPLE_SCALING; + channel_buffer[chan] = malloc(channel_len[chan]); + channel_pos[chan] = channel_buffer[chan]; + channel_vol[chan] = vol + 1; + + // Convert 8-bit unsigned samples to 16-bit signed and upsample + // Each input byte is replicated SAMPLE_SCALING times + for (int i = 0; i < size; i++) + { + for (int ex = 0; ex < SAMPLE_SCALING; ex++) + { +#if (BYTES_PER_SAMPLE == 2) + // Convert 8-bit unsigned (0-255) to 16-bit signed (-32768 to 32767) + // Shift left by 8 and treat as signed to get full scale + channel_buffer[chan][(i * SAMPLE_SCALING) + ex] = (Sint8)buffer[i] << 8; +#else /* BYTES_PER_SAMPLE */ + channel_buffer[chan][(i * SAMPLE_SCALING) + ex] = (Sint8)buffer[i]; +#endif /* BYTES_PER_SAMPLE */ + } + } + + // ESP32: No SDL_UnlockAudio needed } diff --git a/components/OpenTyrian/nortsong.c b/components/OpenTyrian/nortsong.c index ccaa265..7482548 100644 --- a/components/OpenTyrian/nortsong.c +++ b/components/OpenTyrian/nortsong.c @@ -101,8 +101,9 @@ void wait_delayorinput( JE_boolean keyboard, JE_boolean mouse, JE_boolean joysti void JE_loadSndFile( const char *effects_sndfile, const char *voices_sndfile ) { - notYetLoadedSound = false; - return; + // STUB REMOVED: Actually load sound samples now! + // notYetLoadedSound = false; + // return; JE_byte y, z; JE_word x; JE_longint templ; diff --git a/components/OpenTyrian/video.c b/components/OpenTyrian/video.c index 0f010a4..e12be96 100644 --- a/components/OpenTyrian/video.c +++ b/components/OpenTyrian/video.c @@ -31,9 +31,14 @@ #include // Include for time functions -// Define variables for FPS calculation +// Define variables for FPS calculation and frame limiting static uint32_t frame_count = 0; static uint32_t start_time = 0; +static uint32_t last_frame_time = 0; + +// Original Tyrian was designed for 70 Hz (70 FPS) +#define TARGET_FPS 70 +#define FRAME_TIME_MS (1000 / TARGET_FPS) // ~14.285ms per frame bool fullscreen_enabled = false; @@ -245,6 +250,29 @@ void scale_and_flip(SDL_Surface *src_surface) return; } + // Frame rate limiting: maintain original 70 FPS + uint32_t current_time = SDL_GetTicks(); + + if (last_frame_time == 0) { + // First frame - initialize timing + last_frame_time = current_time; + start_time = current_time; + } else { + // Calculate time elapsed since last frame + uint32_t elapsed = current_time - last_frame_time; + + // If we're running faster than target FPS, delay to maintain 70 FPS + if (elapsed < FRAME_TIME_MS) { + uint32_t delay_time = FRAME_TIME_MS - elapsed; + if (delay_time > 0 && delay_time < 100) { // Sanity check: don't delay excessively + SDL_Delay(delay_time); + } + } + + // Update last frame time (after any delay) + last_frame_time = SDL_GetTicks(); + } + // Convert the SDL_Surface to an SDL_Texture SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, src_surface); if (!texture) { @@ -273,7 +301,7 @@ void scale_and_flip(SDL_Surface *src_surface) // --- FPS Calculation --- frame_count++; // Increment the frame count - uint32_t current_time = SDL_GetTicks(); // Get current time in milliseconds + current_time = SDL_GetTicks(); // Get current time after render if (start_time == 0) { // Initialize the start time for the first frame @@ -284,7 +312,7 @@ void scale_and_flip(SDL_Surface *src_surface) if (elapsed_time >= 5000) { // If 5 seconds have passed float fps = (frame_count / (elapsed_time / 1000.0f)); // Calculate FPS - printf("FPS: %.2f\n", fps); // Print FPS to console + printf("FPS: %.2f (Target: %d FPS)\n", fps, TARGET_FPS); // Print FPS to console // Reset for next interval frame_count = 0; diff --git a/components/espressif__esp32_p4_function_ev_board_noglib/.component_hash b/components/espressif__esp32_p4_function_ev_board_noglib/.component_hash new file mode 100644 index 0000000..758afe8 --- /dev/null +++ b/components/espressif__esp32_p4_function_ev_board_noglib/.component_hash @@ -0,0 +1 @@ +c046ce89af7136847765caf4bf633ceba281beecbbabd1aa756031516e51a40d \ No newline at end of file diff --git a/components/espressif__esp32_p4_function_ev_board_noglib/API.md b/components/espressif__esp32_p4_function_ev_board_noglib/API.md new file mode 100644 index 0000000..b54e7bd --- /dev/null +++ b/components/espressif__esp32_p4_function_ev_board_noglib/API.md @@ -0,0 +1,1543 @@ +# API Reference + +
+ + + + +| :1234: [CAPABILITIES](#1234-capabilities) | :floppy_disk: [SD CARD AND SPIFFS](#floppy_disk-sd-card-and-spiffs) | :musical_note: [AUDIO](#musical_note-audio) | :pager: [DISPLAY AND TOUCH](#pager-display-and-touch) | :electric_plug: [USB](#electric_plug-usb) | :camera: [CAMERA](#camera-camera) | +| :-------------------------: | :-------------------------: | :-------------------------: | :-------------------------: | :-------------------------: | :-------------------------: | + +
+ + + +## Overview + +This document provides an overview of the ESP-BSP (Board Support Package) API as implemented by this board. + +While the ESP-BSP framework defines a unified API shared across multiple boards, this documentation focuses only on the APIs supported by the current board. Any APIs not applicable to this board's hardware are excluded or may not be functional. + +The goal of this document is to make it easier for developers to understand the available APIs and how to use them consistently across different boards. + +## General + +### Pinout + +Each BSP defines a set of macros for default pin assignments used by its hardware peripherals. +These macros allow users to configure or reference standard interfaces like I2C, SPI, LCD, audio, or SD cards easily. + +- I2C: `BSP_I2C_*` +- Display: `BSP_LCD_*` +- Audio I2S: `BSP_I2S_*` +- USB: `BSP_USB_*` +- SD Card (MMC): `BSP_SD_*` +- SD Card (SPI): `BSP_SD_SPI_*` + +> [!NOTE] +> Not all boards support all interfaces. You should always check if the related capability macro (e.g., BSP_CAPS_SDCARD) is defined. + +### I2C + +Some devices included in BSPs (e.g., sensors, displays, audio codecs) communicate via the I2C interface. In many cases, I2C is initialized automatically as part of the device setup. However, you can manually initialize or deinitialize the I2C peripheral using the following API: + +``` +/* Initialize the default I2C bus used by the BSP */ +bsp_i2c_init(); + +... + +/* Deinitialize the I2C bus */ +bsp_i2c_deinit(); +``` + +If you need direct access to the initialized I2C bus (e.g., to communicate with an external peripheral not handled by the BSP), you can retrieve the I2C bus handle: + +``` +i2c_master_bus_handle_t i2c = bsp_i2c_get_handle(); +``` + +> [!NOTE] +> The BSP ensures that I2C initialization is performed only once, even if called multiple times. This helps avoid conflicts when multiple components rely on the same I2C bus. + + +### ADC + +Some devices included in BSPs (such as buttons, battery monitoring, etc.) use the ADC peripheral. In most cases, the ADC is automatically initialized as part of the specific device setup. However, you can manually initialize the ADC using the following API: + +``` +/* Initialize the ADC peripheral */ +bsp_adc_initialize(); +``` + +If you need direct access to the ADC instance (e.g., for custom measurements), you can retrieve the handle: + +``` +adc_oneshot_unit_handle_t adc = bsp_adc_get_handle(); +``` + +> [!NOTE] +> The BSP ensures the ADC is initialized only once, even if `bsp_adc_initialize()` is called multiple times. + +### Features + +Some boards support enabling or disabling specific hardware features (such as LCD, SD card, camera, etc.) to reduce power consumption or manage shared resources. The BSP provides a unified API to control these features: + +``` +/* Enable the LCD feature */ +bsp_feature_enable(BSP_FEATURE_LCD, true); + +/* Disable the speaker to reduce power usage */ +bsp_feature_enable(BSP_FEATURE_SPEAKER, false); +``` + +Supported feature flags (may vary depending on the board): +- `BSP_FEATURE_LCD` - Display module +- `BSP_FEATURE_TOUCH` - Touch controller +- `BSP_FEATURE_SD` - SD card interface +- `BSP_FEATURE_SPEAKER`- Audio speaker +- `BSP_FEATURE_BATTERY` - Battery monitoring +- `BSP_FEATURE_VIBRATION` - Vibration motor + +> [!NOTE] +> Not all BSPs support feature toggling, and some features may not be available or controllable via this API. Always check the BSP header or documentation for supported features. + +> [!TIP] +> Disabling unused features can help reduce power consumption, especially in battery-powered applications. + + + + +## Identification + +Each BSP defines an identifier macro in the form of `BSP_BOARD_*`. + +### Board Name API Reference + + + +## Macros + +| Type | Name | +| ---: | :--- | +| define | [**BSP\_BOARD\_ESP32\_P4\_FUNCTION\_EV\_BOARD**](#define-bsp_board_esp32_p4_function_ev_board)
| + + + + + + + + + +## :1234: Capabilities + +Each BSP defines a set of capability macros that indicate which features are supported. +The list may look like this. +You can use these macros to conditionally compile code depending on feature availability. + +### Capabilities API Reference + + + +## Macros + +| Type | Name | +| ---: | :--- | +| define | [**BSP\_CAPS\_AUDIO**](#define-bsp_caps_audio) 1
| +| define | [**BSP\_CAPS\_AUDIO\_MIC**](#define-bsp_caps_audio_mic) 1
| +| define | [**BSP\_CAPS\_AUDIO\_SPEAKER**](#define-bsp_caps_audio_speaker) 1
| +| define | [**BSP\_CAPS\_BUTTONS**](#define-bsp_caps_buttons) 0
| +| define | [**BSP\_CAPS\_CAMERA**](#define-bsp_caps_camera) 1
| +| define | [**BSP\_CAPS\_DISPLAY**](#define-bsp_caps_display) 1
| +| define | [**BSP\_CAPS\_IMU**](#define-bsp_caps_imu) 0
| +| define | [**BSP\_CAPS\_SDCARD**](#define-bsp_caps_sdcard) 1
| +| define | [**BSP\_CAPS\_TOUCH**](#define-bsp_caps_touch) 1
| + + + + + + + + + + + +### I2C API Reference + + +## Functions + +| Type | Name | +| ---: | :--- | +| esp\_err\_t | [**bsp\_i2c\_deinit**](#function-bsp_i2c_deinit) (void)
_Deinit I2C driver and free its resources._ | +| i2c\_master\_bus\_handle\_t | [**bsp\_i2c\_get\_handle**](#function-bsp_i2c_get_handle) (void)
_Get I2C driver handle._ | +| esp\_err\_t | [**bsp\_i2c\_init**](#function-bsp_i2c_init) (void)
_Init I2C driver._ | + +## Macros + +| Type | Name | +| ---: | :--- | +| define | [**BSP\_I2C\_NUM**](#define-bsp_i2c_num) CONFIG\_BSP\_I2C\_NUM
| +| define | [**BSP\_I2C\_SCL**](#define-bsp_i2c_scl) (GPIO\_NUM\_8)
| +| define | [**BSP\_I2C\_SDA**](#define-bsp_i2c_sda) (GPIO\_NUM\_7)
| + + + +## Functions Documentation + +### function `bsp_i2c_deinit` + +_Deinit I2C driver and free its resources._ +```c +esp_err_t bsp_i2c_deinit ( + void +) +``` + + +**Returns:** + + + +* ESP\_OK On success +* ESP\_ERR\_INVALID\_ARG I2C parameter error +### function `bsp_i2c_get_handle` + +_Get I2C driver handle._ +```c +i2c_master_bus_handle_t bsp_i2c_get_handle ( + void +) +``` + + +**Returns:** + + + +* I2C handle +### function `bsp_i2c_init` + +_Init I2C driver._ +```c +esp_err_t bsp_i2c_init ( + void +) +``` + + +**Returns:** + + + +* ESP\_OK On success +* ESP\_ERR\_INVALID\_ARG I2C parameter error +* ESP\_FAIL I2C driver installation error + + + + + + +## :floppy_disk: SD Card and SPIFFS + +### SPIFFS Initialization / Deinitialization + +Each BSP provides a simple API for mounting and unmounting the SPI Flash File System (SPIFFS). + +``` +/* Mount SPIFFS to the virtual file system */ +bsp_spiffs_mount(); + +/* ... perform file operations ... */ + +/* Unmount SPIFFS from the virtual file system */ +bsp_spiffs_unmount(); +``` + +### SD Card Initialization / Deinitialization + +The BSP offers a flexible API for working with SD cards. In addition to the default mount and unmount functions, you can also use a configuration structure or access preconfigured `host` and `slot` structures. + +Mount with Default Configuration + +``` +/* Mount microSD card to the virtual file system */ +bsp_sdcard_mount(); + +/* ... perform file operations ... */ + +/* Unmount microSD card */ +bsp_sdcard_unmount(); +``` + +Mount with Custom Configuration + +Some BSPs allow selecting between SDMMC and SPI interfaces for the SD card. Use the appropriate API function based on your hardware: +``` +bsp_sdcard_cfg_t cfg = {0}; +/* Mount SD card using SDMMC interface */ +bsp_sdcard_sdmmc_mount(&cfg); +``` + +or + +``` +bsp_sdcard_cfg_t cfg = {0}; +/* Mount SD card using SPI interface */ +bsp_sdcard_sdspi_mount(&cfg) +``` + +> [!NOTE] +> Not all BSPs support both SDMMC and SPI modes. Check the board documentation to see which interfaces are available. +> If an unsupported interface is used, the API will return `ESP_ERR_NOT_SUPPORTED` error. + +### After Mounting + +Once the SD card or SPIFFS is mounted, you can use standard file I/O functions (`fopen`, `fread`, `fwrite`, `fclose`, etc.) provided by ESP-IDF's VFS (Virtual File System). + +To print basic SD card information (after mounting), you can use: +``` +sdmmc_card_t *sdcard = bsp_sdcard_get_handle(); +sdmmc_card_print_info(stdout, sdcard); +``` + +> [!TIP] +> The bsp_sdcard_get_handle() function returns a pointer to the sdmmc_card_t structure, which contains detailed information about the connected SD card. + +### SD Card and SPIFFS API Reference + +## Structures and Types + +| Type | Name | +| ---: | :--- | +| struct | [**bsp\_sdcard\_cfg\_t**](#struct-bsp_sdcard_cfg_t)
_BSP SD card configuration structure._ | + +## Functions + +| Type | Name | +| ---: | :--- | +| sdmmc\_card\_t \* | [**bsp\_sdcard\_get\_handle**](#function-bsp_sdcard_get_handle) (void)
_Get SD card handle._ | +| void | [**bsp\_sdcard\_get\_sdmmc\_host**](#function-bsp_sdcard_get_sdmmc_host) (const int slot, sdmmc\_host\_t \*config)
_Get SD card MMC host config._ | +| void | [**bsp\_sdcard\_get\_sdspi\_host**](#function-bsp_sdcard_get_sdspi_host) (const int slot, sdmmc\_host\_t \*config)
_Get SD card SPI host config._ | +| esp\_err\_t | [**bsp\_sdcard\_mount**](#function-bsp_sdcard_mount) (void)
_Mount microSD card to virtual file system._ | +| void | [**bsp\_sdcard\_sdmmc\_get\_slot**](#function-bsp_sdcard_sdmmc_get_slot) (const int slot, sdmmc\_slot\_config\_t \*config)
_Get SD card MMC slot config._ | +| esp\_err\_t | [**bsp\_sdcard\_sdmmc\_mount**](#function-bsp_sdcard_sdmmc_mount) ([**bsp\_sdcard\_cfg\_t**](#struct-bsp_sdcard_cfg_t) \*cfg)
_Mount microSD card to virtual file system (MMC mode)_ | +| void | [**bsp\_sdcard\_sdspi\_get\_slot**](#function-bsp_sdcard_sdspi_get_slot) (const spi\_host\_device\_t spi\_host, sdspi\_device\_config\_t \*config)
_Get SD card SPI slot config._ | +| esp\_err\_t | [**bsp\_sdcard\_sdspi\_mount**](#function-bsp_sdcard_sdspi_mount) ([**bsp\_sdcard\_cfg\_t**](#struct-bsp_sdcard_cfg_t) \*cfg)
_Mount microSD card to virtual file system (SPI mode)_ | +| esp\_err\_t | [**bsp\_sdcard\_unmount**](#function-bsp_sdcard_unmount) (void)
_Unmount microSD card from virtual file system._ | +| esp\_err\_t | [**bsp\_spiffs\_mount**](#function-bsp_spiffs_mount) (void)
_Mount SPIFFS to virtual file system._ | +| esp\_err\_t | [**bsp\_spiffs\_unmount**](#function-bsp_spiffs_unmount) (void)
_Unmount SPIFFS from virtual file system._ | + +## Macros + +| Type | Name | +| ---: | :--- | +| define | [**BSP\_SDSPI\_HOST**](#define-bsp_sdspi_host) (SDSPI\_DEFAULT\_HOST)
| +| define | [**BSP\_SD\_CLK**](#define-bsp_sd_clk) (GPIO\_NUM\_43)
| +| define | [**BSP\_SD\_CMD**](#define-bsp_sd_cmd) (GPIO\_NUM\_44)
| +| define | [**BSP\_SD\_D0**](#define-bsp_sd_d0) (GPIO\_NUM\_39)
| +| define | [**BSP\_SD\_D1**](#define-bsp_sd_d1) (GPIO\_NUM\_40)
| +| define | [**BSP\_SD\_D2**](#define-bsp_sd_d2) (GPIO\_NUM\_41)
| +| define | [**BSP\_SD\_D3**](#define-bsp_sd_d3) (GPIO\_NUM\_42)
| +| define | [**BSP\_SD\_MOUNT\_POINT**](#define-bsp_sd_mount_point) CONFIG\_BSP\_SD\_MOUNT\_POINT
| +| define | [**BSP\_SD\_SPI\_CLK**](#define-bsp_sd_spi_clk) (GPIO\_NUM\_43)
| +| define | [**BSP\_SD\_SPI\_CS**](#define-bsp_sd_spi_cs) (GPIO\_NUM\_42)
| +| define | [**BSP\_SD\_SPI\_MISO**](#define-bsp_sd_spi_miso) (GPIO\_NUM\_39)
| +| define | [**BSP\_SD\_SPI\_MOSI**](#define-bsp_sd_spi_mosi) (GPIO\_NUM\_44)
| +| define | [**BSP\_SPIFFS\_MOUNT\_POINT**](#define-bsp_spiffs_mount_point) CONFIG\_BSP\_SPIFFS\_MOUNT\_POINT
| + + +## Structures and Types Documentation + +### struct `bsp_sdcard_cfg_t` + +_BSP SD card configuration structure._ + +Variables: + +- sdmmc\_host\_t \* host + +- const esp\_vfs\_fat\_sdmmc\_mount\_config\_t \* mount + +- const sdmmc\_slot\_config\_t \* sdmmc + +- const sdspi\_device\_config\_t \* sdspi + +- union [**bsp\_sdcard\_cfg\_t**](#struct-bsp_sdcard_cfg_t) slot + + +## Functions Documentation + +### function `bsp_sdcard_get_handle` + +_Get SD card handle._ +```c +sdmmc_card_t * bsp_sdcard_get_handle ( + void +) +``` + + +**Returns:** + +SD card handle +### function `bsp_sdcard_get_sdmmc_host` + +_Get SD card MMC host config._ +```c +void bsp_sdcard_get_sdmmc_host ( + const int slot, + sdmmc_host_t *config +) +``` + + +**Parameters:** + + +* `slot` SD card slot +* `config` Structure which will be filled +### function `bsp_sdcard_get_sdspi_host` + +_Get SD card SPI host config._ +```c +void bsp_sdcard_get_sdspi_host ( + const int slot, + sdmmc_host_t *config +) +``` + + +**Parameters:** + + +* `slot` SD card slot +* `config` Structure which will be filled +### function `bsp_sdcard_mount` + +_Mount microSD card to virtual file system._ +```c +esp_err_t bsp_sdcard_mount ( + void +) +``` + + +**Returns:** + + + +* ESP\_OK on success +* ESP\_ERR\_INVALID\_STATE if esp\_vfs\_fat\_sdmmc\_mount was already called +* ESP\_ERR\_NO\_MEM if memory cannot be allocated +* ESP\_FAIL if partition cannot be mounted +* other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers +### function `bsp_sdcard_sdmmc_get_slot` + +_Get SD card MMC slot config._ +```c +void bsp_sdcard_sdmmc_get_slot ( + const int slot, + sdmmc_slot_config_t *config +) +``` + + +**Parameters:** + + +* `slot` SD card slot +* `config` Structure which will be filled +### function `bsp_sdcard_sdmmc_mount` + +_Mount microSD card to virtual file system (MMC mode)_ +```c +esp_err_t bsp_sdcard_sdmmc_mount ( + bsp_sdcard_cfg_t *cfg +) +``` + + +**Parameters:** + + +* `cfg` SD card configuration + + +**Returns:** + + + +* ESP\_OK on success +* ESP\_ERR\_INVALID\_STATE if esp\_vfs\_fat\_sdmmc\_mount was already called +* ESP\_ERR\_NO\_MEM if memory cannot be allocated +* ESP\_FAIL if partition cannot be mounted +* other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers +### function `bsp_sdcard_sdspi_get_slot` + +_Get SD card SPI slot config._ +```c +void bsp_sdcard_sdspi_get_slot ( + const spi_host_device_t spi_host, + sdspi_device_config_t *config +) +``` + + +**Parameters:** + + +* `spi_host` SPI host ID +* `config` Structure which will be filled +### function `bsp_sdcard_sdspi_mount` + +_Mount microSD card to virtual file system (SPI mode)_ +```c +esp_err_t bsp_sdcard_sdspi_mount ( + bsp_sdcard_cfg_t *cfg +) +``` + + +**Parameters:** + + +* `cfg` SD card configuration + + +**Returns:** + + + +* ESP\_OK on success +* ESP\_ERR\_INVALID\_STATE if esp\_vfs\_fat\_sdmmc\_mount was already called +* ESP\_ERR\_NO\_MEM if memory cannot be allocated +* ESP\_FAIL if partition cannot be mounted +* other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers +### function `bsp_sdcard_unmount` + +_Unmount microSD card from virtual file system._ +```c +esp_err_t bsp_sdcard_unmount ( + void +) +``` + + +**Returns:** + + + +* ESP\_OK on success +* ESP\_ERR\_NOT\_FOUND if the partition table does not contain FATFS partition with given label +* ESP\_ERR\_INVALID\_STATE if esp\_vfs\_fat\_spiflash\_mount was already called +* ESP\_ERR\_NO\_MEM if memory can not be allocated +* ESP\_FAIL if partition can not be mounted +* other error codes from wear levelling library, SPI flash driver, or FATFS drivers +### function `bsp_spiffs_mount` + +_Mount SPIFFS to virtual file system._ +```c +esp_err_t bsp_spiffs_mount ( + void +) +``` + + +**Returns:** + + + +* 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 +### function `bsp_spiffs_unmount` + +_Unmount SPIFFS from virtual file system._ +```c +esp_err_t bsp_spiffs_unmount ( + void +) +``` + + +**Returns:** + + + +* 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 + + + + + + +## :musical_note: Audio + +### Initialization + +Before using speaker or microphone features, the audio codec must be initialized. + +``` +/* Initialize the speaker codec */ +esp_codec_dev_handle_t spk_codec_dev = bsp_audio_codec_speaker_init(); + +/* Initialize the microphone codec */ +esp_codec_dev_handle_t mic_codec_dev = bsp_audio_codec_microphone_init(); +``` + +After initialization, the [esp_codec_dev](https://components.espressif.com/components/espressif/esp_codec_dev) API can be used to control playback and recording. + +> [!NOTE] +> Some BSPs may only support playback (speaker) or only input (microphone). Use the capability macros (`BSP_CAPS_AUDIO`, `BSP_CAPS_AUDIO_SPEAKER`, `BSP_CAPS_AUDIO_MIC`) to check supported features. + +### Example of audio usage + +#### Speaker + +Below is an example of audio playback using the speaker (source data not included): + +``` +/* Set volume to 50% */ +esp_codec_dev_set_out_vol(spk_codec_dev, 50); + +/* Define audio format */ +esp_codec_dev_sample_info_t fs = { + .sample_rate = wav_header.sample_rate, + .channel = wav_header.num_channels, + .bits_per_sample = wav_header.bits_per_sample, +}; +/* Open speaker stream */ +esp_codec_dev_open(spk_codec_dev, &fs); + +... +/* Play audio data */ +esp_codec_dev_write(spk_codec_dev, wav_bytes, wav_bytes_len); +... + +/* Close stream when done */ +esp_codec_dev_close(spk_codec_dev); +``` + +> [!TIP] +> Audio data must be in raw PCM format. Use a decoder if playing compressed formats (e.g., WAV, MP3). + +#### Microphone + +Below is an example of recording audio using the microphone (destination buffer not included): + +``` +/* Set input gain (optional) */ +esp_codec_dev_set_in_gain(mic_codec_dev, 42.0); + +/* Define audio format */ +esp_codec_dev_sample_info_t fs = { + .sample_rate = 16000, + .channel = 1, + .bits_per_sample = 16, +}; +/* Open microphone stream */ +esp_codec_dev_open(mic_codec_dev, &fs); + +/* Read recorded data */ +esp_codec_dev_read(mic_codec_dev, recording_buffer, BUFFER_SIZE) + +... + +/* Close stream when done */ +esp_codec_dev_close(mic_codec_dev); +``` + +### Audio API Reference + + +## Functions + +| Type | Name | +| ---: | :--- | +| esp\_codec\_dev\_handle\_t | [**bsp\_audio\_codec\_microphone\_init**](#function-bsp_audio_codec_microphone_init) (void)
_Initialize microphone codec device._ | +| esp\_codec\_dev\_handle\_t | [**bsp\_audio\_codec\_speaker\_init**](#function-bsp_audio_codec_speaker_init) (void)
_Initialize speaker codec device._ | +| esp\_err\_t | [**bsp\_audio\_init**](#function-bsp_audio_init) (const i2s\_std\_config\_t \*i2s\_config)
_Init audio._ | + +## Macros + +| Type | Name | +| ---: | :--- | +| define | [**BSP\_I2S\_DOUT**](#define-bsp_i2s_dout) (GPIO\_NUM\_9)
| +| define | [**BSP\_I2S\_DSIN**](#define-bsp_i2s_dsin) (GPIO\_NUM\_11)
| +| define | [**BSP\_I2S\_LCLK**](#define-bsp_i2s_lclk) (GPIO\_NUM\_10)
| +| define | [**BSP\_I2S\_MCLK**](#define-bsp_i2s_mclk) (GPIO\_NUM\_13)
| +| define | [**BSP\_I2S\_SCLK**](#define-bsp_i2s_sclk) (GPIO\_NUM\_12)
| +| define | [**BSP\_POWER\_AMP\_IO**](#define-bsp_power_amp_io) (GPIO\_NUM\_53)
| + + + +## Functions Documentation + +### function `bsp_audio_codec_microphone_init` + +_Initialize microphone codec device._ +```c +esp_codec_dev_handle_t bsp_audio_codec_microphone_init ( + void +) +``` + + +**Returns:** + +Pointer to codec device handle or NULL when error occurred +### function `bsp_audio_codec_speaker_init` + +_Initialize speaker codec device._ +```c +esp_codec_dev_handle_t bsp_audio_codec_speaker_init ( + void +) +``` + + +**Returns:** + +Pointer to codec device handle or NULL when error occurred +### function `bsp_audio_init` + +_Init audio._ +```c +esp_err_t bsp_audio_init ( + const i2s_std_config_t *i2s_config +) +``` + + +**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. + + + +**Parameters:** + + +* `i2s_config` I2S configuration. Pass NULL to use default values (Mono, duplex, 16bit, 22050 Hz) + + +**Returns:** + + + +* 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 + + + + + + +## :pager: Display and Touch + +### Initialization + +ESP-BSP provides two ways to initialize the **display**, **touch** and **LVGL**. + +Simple method: + +``` +/* Initialize display, touch, and LVGL */ +lv_display_t display = bsp_display_start(); +``` + +Configurable method: + +``` +bsp_display_cfg_t cfg = { + .lvgl_port_cfg = ESP_LVGL_PORT_INIT_CONFIG(), /* See LVGL Port for more info */ + .buffer_size = BSP_LCD_V_RES * BSP_LCD_H_RES, /* Screen buffer size in pixels */ + .double_buffer = true, /* Allocate two buffers if true */ + .flags = { + .buff_dma = true, /* Use DMA-capable LVGL buffer */ + .buff_spiram = false, /* Allocate buffer in PSRAM if true */ + } +}; +cfg.lvgl_port_cfg.task_stack = 10000; /* Example: change LVGL task stack size */ +/* Initialize display, touch, and LVGL */ +lv_display_t display = bsp_display_start_with_config(&cfg); +``` + +After initialization, you can use the [LVGL](https://docs.lvgl.io/master/) API or [LVGL Port](../components/esp_lvgl_port/README.md) API. + +### Initialization without LVGL - NoGLIB BSP + +To initialize the LCD without LVGL, use: + +``` +esp_lcd_panel_handle_t panel_handle; +esp_lcd_panel_io_handle_t io_handle; +const bsp_display_config_t bsp_disp_cfg = { + .max_transfer_sz = (BSP_LCD_H_RES * 100) * sizeof(uint16_t), +}; +BSP_ERROR_CHECK_RETURN_NULL(bsp_display_new(&bsp_disp_cfg, &panel_handle, &io_handle)); +``` + +To initialize the LCD touch without LVGL, use: + +``` +esp_lcd_touch_handle_t tp; +bsp_touch_new(NULL, &tp); +``` + +After initialization, you can use the [ESP-LCD](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/lcd/index.html) API and [ESP-LCD Touch](../components/lcd_touch/README.md) API. + +### Set Brightness + +``` +/* Set display brightness to 100% */ +bsp_display_backlight_on(); + +/* Set display brightness to 0% */ +bsp_display_backlight_off(); + +/* Set display brightness to 50% */ +bsp_display_brightness_set(50); +``` + +> [!NOTE] +> Some boards do not support changing brightness. They return an `ESP_ERR_NOT_SUPPORTED` error. + +### LVGL API Usage (only when initialized with LVGL) + +All LVGL calls must be protected using lock/unlock: + +``` +/* Wait until other tasks finish screen operations */ +bsp_display_lock(0); +... +lv_obj_t * screen = lv_disp_get_scr_act(disp_handle); +lv_obj_t * obj = lv_label_create(screen); +... +/* Unlock after screen operations are done */ +bsp_display_unlock(); +``` + +### Screen rotation (only when initialized with LVGL) + +``` +bsp_display_lock(0); +/* Rotate display to 90 */ +bsp_display_rotate(display, LV_DISPLAY_ROTATION_90); +bsp_display_unlock(); +``` + +> [!NOTE] +> Some LCDs do not support hardware rotation and instead use software rotation, which consumes more memory. + +### Available constants + +Constants like screen resolution, pin configuration, and other options are defined in the BSP header files (`{bsp_name}.h`, `display.h`, `touch.h`). +Below are some of the most relevant predefined constants: + +- `BSP_LCD_H_RES` - Horizontal resolution in pixels +- `BSP_LCD_V_RES` - Vertical resolution in pixels +- `BSP_LCD_SPI_NUM` - SPI bus used by the LCD (if applicable) + + +### Display and Touch API Reference + +## Structures and Types + +| Type | Name | +| ---: | :--- | +| struct | [**bsp\_display\_cfg\_t**](#struct-bsp_display_cfg_t)
_BSP display configuration structure._ | +| struct | [**bsp\_display\_config\_t**](#struct-bsp_display_config_t)
_BSP display configuration structure._ | +| enum | [**bsp\_hdmi\_resolution\_t**](#enum-bsp_hdmi_resolution_t)
_BSP HDMI resolution types._ | +| struct | [**bsp\_lcd\_handles\_t**](#struct-bsp_lcd_handles_t)
_BSP display return handles._ | +| struct | [**bsp\_touch\_config\_t**](#struct-bsp_touch_config_t)
_BSP touch configuration structure._ | + +## Functions + +| Type | Name | +| ---: | :--- | +| esp\_err\_t | [**bsp\_display\_backlight\_off**](#function-bsp_display_backlight_off) (void)
_Turn off display backlight._ | +| esp\_err\_t | [**bsp\_display\_backlight\_on**](#function-bsp_display_backlight_on) (void)
_Turn on display backlight._ | +| esp\_err\_t | [**bsp\_display\_brightness\_deinit**](#function-bsp_display_brightness_deinit) (void)
_Deinitialize display's brightness._ | +| esp\_err\_t | [**bsp\_display\_brightness\_init**](#function-bsp_display_brightness_init) (void)
_Initialize display's brightness._ | +| esp\_err\_t | [**bsp\_display\_brightness\_set**](#function-bsp_display_brightness_set) (int brightness\_percent)
_Set display's brightness._ | +| void | [**bsp\_display\_delete**](#function-bsp_display_delete) (void)
_Delete display panel._ | +| lv\_indev\_t \* | [**bsp\_display\_get\_input\_dev**](#function-bsp_display_get_input_dev) (void)
_Get pointer to input device (touch, buttons, ...)_ | +| bool | [**bsp\_display\_lock**](#function-bsp_display_lock) (uint32\_t timeout\_ms)
_Take LVGL mutex._ | +| esp\_err\_t | [**bsp\_display\_new**](#function-bsp_display_new) (const [**bsp\_display\_config\_t**](#struct-bsp_display_config_t) \*config, esp\_lcd\_panel\_handle\_t \*ret\_panel, esp\_lcd\_panel\_io\_handle\_t \*ret\_io)
_Create new display panel._ | +| esp\_err\_t | [**bsp\_display\_new\_with\_handles**](#function-bsp_display_new_with_handles) (const [**bsp\_display\_config\_t**](#struct-bsp_display_config_t) \*config, [**bsp\_lcd\_handles\_t**](#struct-bsp_lcd_handles_t) \*ret\_handles)
_Create new display panel._ | +| void | [**bsp\_display\_rotate**](#function-bsp_display_rotate) (lv\_display\_t \*disp, lv\_disp\_rotation\_t rotation)
_Rotate screen._ | +| lv\_display\_t \* | [**bsp\_display\_start**](#function-bsp_display_start) (void)
_Initialize display._ | +| lv\_display\_t \* | [**bsp\_display\_start\_with\_config**](#function-bsp_display_start_with_config) (const [**bsp\_display\_cfg\_t**](#struct-bsp_display_cfg_t) \*cfg)
_Initialize display._ | +| void | [**bsp\_display\_stop**](#function-bsp_display_stop) (lv\_display\_t \*display)
_Deinitialize display._ | +| void | [**bsp\_display\_unlock**](#function-bsp_display_unlock) (void)
_Give LVGL mutex._ | +| void | [**bsp\_touch\_delete**](#function-bsp_touch_delete) (void)
_Deinitialize touch._ | +| esp\_err\_t | [**bsp\_touch\_new**](#function-bsp_touch_new) (const [**bsp\_touch\_config\_t**](#struct-bsp_touch_config_t) \*config, esp\_lcd\_touch\_handle\_t \*ret\_touch)
_Create new touchscreen._ | + +## Macros + +| Type | Name | +| ---: | :--- | +| define | [**BSP\_LCD\_BACKLIGHT**](#define-bsp_lcd_backlight) (GPIO\_NUM\_23)
| +| define | [**BSP\_LCD\_BIGENDIAN**](#define-bsp_lcd_bigendian) (0)
| +| define | [**BSP\_LCD\_BITS\_PER\_PIXEL**](#define-bsp_lcd_bits_per_pixel) (16)
| +| define | [**BSP\_LCD\_COLOR\_FORMAT**](#define-bsp_lcd_color_format) (ESP\_LCD\_COLOR\_FORMAT\_RGB565)
| +| define | [**BSP\_LCD\_COLOR\_SPACE**](#define-bsp_lcd_color_space) (LCD\_RGB\_ELEMENT\_ORDER\_RGB)
| +| define | [**BSP\_LCD\_DRAW\_BUFF\_DOUBLE**](#define-bsp_lcd_draw_buff_double) (0)
| +| define | [**BSP\_LCD\_DRAW\_BUFF\_SIZE**](#define-bsp_lcd_draw_buff_size) (BSP\_LCD\_H\_RES \* 50)
| +| define | [**BSP\_LCD\_H\_RES**](#define-bsp_lcd_h_res) (800)
| +| define | [**BSP\_LCD\_MIPI\_DSI\_LANE\_BITRATE\_MBPS**](#define-bsp_lcd_mipi_dsi_lane_bitrate_mbps) (1000)
| +| define | [**BSP\_LCD\_MIPI\_DSI\_LANE\_NUM**](#define-bsp_lcd_mipi_dsi_lane_num) (2)
| +| define | [**BSP\_LCD\_PIXEL\_CLOCK\_MHZ**](#define-bsp_lcd_pixel_clock_mhz) (80)
| +| define | [**BSP\_LCD\_RST**](#define-bsp_lcd_rst) (GPIO\_NUM\_NC)
| +| define | [**BSP\_LCD\_TOUCH\_INT**](#define-bsp_lcd_touch_int) (GPIO\_NUM\_NC)
| +| define | [**BSP\_LCD\_TOUCH\_RST**](#define-bsp_lcd_touch_rst) (GPIO\_NUM\_NC)
| +| define | [**BSP\_LCD\_V\_RES**](#define-bsp_lcd_v_res) (1280)
| +| define | [**BSP\_MIPI\_DSI\_PHY\_PWR\_LDO\_CHAN**](#define-bsp_mipi_dsi_phy_pwr_ldo_chan) (3)
| +| define | [**BSP\_MIPI\_DSI\_PHY\_PWR\_LDO\_VOLTAGE\_MV**](#define-bsp_mipi_dsi_phy_pwr_ldo_voltage_mv) (2500)
| +| define | [**ESP\_LCD\_COLOR\_FORMAT\_RGB565**](#define-esp_lcd_color_format_rgb565) (1)
| +| define | [**ESP\_LCD\_COLOR\_FORMAT\_RGB888**](#define-esp_lcd_color_format_rgb888) (2)
| + + +## Structures and Types Documentation + +### struct `bsp_display_cfg_t` + +_BSP display configuration structure._ + +Variables: + +- unsigned int buff_dma
Allocated LVGL buffer will be DMA capable + +- unsigned int buff_spiram
Allocated LVGL buffer will be in PSRAM + +- uint32\_t buffer_size
Size of the buffer for the screen in pixels + +- bool double_buffer
True, if should be allocated two buffers + +- struct [**bsp\_display\_cfg\_t**](#struct-bsp_display_cfg_t) flags + +- [**bsp\_display\_config\_t**](#struct-bsp_display_config_t) hw_cfg
Display HW configuration + +- lvgl\_port\_cfg\_t lvgl_port_cfg
LVGL port configuration + +- unsigned int sw_rotate
Use software rotation (slower), The feature is unavailable under avoid-tear mode + +### struct `bsp_display_config_t` + +_BSP display configuration structure._ + +Variables: + +- struct [**bsp\_display\_config\_t**](#struct-bsp_display_config_t) dsi_bus + +- [**bsp\_hdmi\_resolution\_t**](#enum-bsp_hdmi_resolution_t) hdmi_resolution
HDMI resolution selection + +- uint32\_t lane_bit_rate_mbps
DSI bus config - lane bit rate + +- mipi\_dsi\_phy\_clock\_source\_t phy_clk_src
DSI bus config - clock source + +### enum `bsp_hdmi_resolution_t` + +_BSP HDMI resolution types._ +```c +enum bsp_hdmi_resolution_t { + BSP_HDMI_RES_NONE = 0, + BSP_HDMI_RES_800x600, + BSP_HDMI_RES_1024x768, + BSP_HDMI_RES_1280x720, + BSP_HDMI_RES_1280x800, + BSP_HDMI_RES_1920x1080 +}; +``` + +### struct `bsp_lcd_handles_t` + +_BSP display return handles._ + +Variables: + +- esp\_lcd\_panel\_handle\_t control
ESP LCD panel (control) handle + +- esp\_lcd\_panel\_io\_handle\_t io
ESP LCD IO handle + +- esp\_lcd\_dsi\_bus\_handle\_t mipi_dsi_bus
MIPI DSI bus handle + +- esp\_lcd\_panel\_handle\_t panel
ESP LCD panel (color) handle + +### struct `bsp_touch_config_t` + +_BSP touch configuration structure._ + +Variables: + +- void \* dummy
Prepared for future use. + + +## Functions Documentation + +### function `bsp_display_backlight_off` + +_Turn off display backlight._ +```c +esp_err_t bsp_display_backlight_off ( + void +) +``` + + +Brightness is controlled with PWM signal to a pin controlling backlight. Brightness must be already initialized by calling [**bsp\_display\_brightness\_init()**](#function-bsp_display_brightness_init) or[**bsp\_display\_new()**](#function-bsp_display_new) + + + +**Returns:** + + + +* ESP\_OK On success +* ESP\_ERR\_INVALID\_ARG Parameter error +### function `bsp_display_backlight_on` + +_Turn on display backlight._ +```c +esp_err_t bsp_display_backlight_on ( + void +) +``` + + +Brightness is controlled with PWM signal to a pin controlling backlight. Brightness must be already initialized by calling [**bsp\_display\_brightness\_init()**](#function-bsp_display_brightness_init) or[**bsp\_display\_new()**](#function-bsp_display_new) + + + +**Returns:** + + + +* ESP\_OK On success +* ESP\_ERR\_INVALID\_ARG Parameter error +### function `bsp_display_brightness_deinit` + +_Deinitialize display's brightness._ +```c +esp_err_t bsp_display_brightness_deinit ( + void +) +``` + +### function `bsp_display_brightness_init` + +_Initialize display's brightness._ +```c +esp_err_t bsp_display_brightness_init ( + void +) +``` + + +Brightness is controlled with PWM signal to a pin controlling backlight. + + + +**Returns:** + + + +* ESP\_OK On success +* ESP\_ERR\_INVALID\_ARG Parameter error +### function `bsp_display_brightness_set` + +_Set display's brightness._ +```c +esp_err_t bsp_display_brightness_set ( + int brightness_percent +) +``` + + +Brightness is controlled with PWM signal to a pin controlling backlight. Brightness must be already initialized by calling [**bsp\_display\_brightness\_init()**](#function-bsp_display_brightness_init) or[**bsp\_display\_new()**](#function-bsp_display_new) + + + +**Parameters:** + + +* `brightness_percent` Brightness in [%] + + +**Returns:** + + + +* ESP\_OK On success +* ESP\_ERR\_INVALID\_ARG Parameter error +### function `bsp_display_delete` + +_Delete display panel._ +```c +void bsp_display_delete ( + void +) +``` + +### function `bsp_display_get_input_dev` + +_Get pointer to input device (touch, buttons, ...)_ +```c +lv_indev_t * bsp_display_get_input_dev ( + void +) +``` + + +**Note:** + +The LVGL input device is initialized in [**bsp\_display\_start()**](#function-bsp_display_start) function. + + + +**Returns:** + +Pointer to LVGL input device or NULL when not initialized +### function `bsp_display_lock` + +_Take LVGL mutex._ +```c +bool bsp_display_lock ( + uint32_t timeout_ms +) +``` + + +**Parameters:** + + +* `timeout_ms` Timeout in [ms]. 0 will block indefinitely. + + +**Returns:** + +true Mutex was taken + + + +**Returns:** + +false Mutex was NOT taken +### function `bsp_display_new` + +_Create new display panel._ +```c +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 +) +``` + + +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()**](#function-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.: + + +````cpp +esp_lcd_panel_del(panel); +esp_lcd_panel_io_del(io); +esp_lcd_del_dsi_bus(mipi_dsi_bus); +```` + + + + + +**Parameters:** + + +* `config` display configuration +* `ret_panel` esp\_lcd panel handle +* `ret_io` esp\_lcd IO handle + + +**Returns:** + + + +* ESP\_OK On success +* Else esp\_lcd failure +### function `bsp_display_new_with_handles` + +_Create new display panel._ +```c +esp_err_t bsp_display_new_with_handles ( + const bsp_display_config_t *config, + bsp_lcd_handles_t *ret_handles +) +``` + + +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()**](#function-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 API: + + +````cpp +bsp_display_delete(); +```` + + + + + +**Parameters:** + + +* `config` display configuration +* `ret_handles` all esp\_lcd handles in one structure + + +**Returns:** + + + +* ESP\_OK On success +* Else esp\_lcd failure +### function `bsp_display_rotate` + +_Rotate screen._ +```c +void bsp_display_rotate ( + lv_display_t *disp, + lv_disp_rotation_t rotation +) +``` + + +Display must be already initialized by calling [**bsp\_display\_start()**](#function-bsp_display_start) + + + +**Parameters:** + + +* `disp` Pointer to LVGL display +* `rotation` Angle of the display rotation +### function `bsp_display_start` + +_Initialize display._ +```c +lv_display_t * bsp_display_start ( + void +) +``` + + +This function initializes MIPI-DSI, display controller and starts LVGL handling task. LCD backlight must be enabled separately by calling [**bsp\_display\_brightness\_set()**](#function-bsp_display_brightness_set) + + + +**Returns:** + +Pointer to LVGL display or NULL when error occured +### function `bsp_display_start_with_config` + +_Initialize display._ +```c +lv_display_t * bsp_display_start_with_config ( + const bsp_display_cfg_t *cfg +) +``` + + +This function initializes MIPI-DSI, display controller and starts LVGL handling task. LCD backlight must be enabled separately by calling [**bsp\_display\_brightness\_set()**](#function-bsp_display_brightness_set) + + + +**Parameters:** + + +* `cfg` display configuration + + +**Returns:** + +Pointer to LVGL display or NULL when error occured +### function `bsp_display_stop` + +_Deinitialize display._ +```c +void bsp_display_stop ( + lv_display_t *display +) +``` + + +This function deinitializes MIPI-DSI, display controller and stops LVGL. + + + +**Parameters:** + + +* `display` Pointer to LVGL display +### function `bsp_display_unlock` + +_Give LVGL mutex._ +```c +void bsp_display_unlock ( + void +) +``` + +### function `bsp_touch_delete` + +_Deinitialize touch._ +```c +void bsp_touch_delete ( + void +) +``` + +### function `bsp_touch_new` + +_Create new touchscreen._ +```c +esp_err_t bsp_touch_new ( + const bsp_touch_config_t *config, + esp_lcd_touch_handle_t *ret_touch +) +``` + + +If you want to free resources allocated by this function, you can use API: + + +````cpp +bsp_touch_delete(); +```` + + + + + +**Parameters:** + + +* `config` touch configuration +* `ret_touch` esp\_lcd\_touch touchscreen handle + + +**Returns:** + + + +* ESP\_OK On success +* Else esp\_lcd\_touch failure + + + + + + +## :electric_plug: USB + +Boards with USB support define macros for USB pins, such as `BSP_USB_POS` and `BSP_USB_NEG`, and may also provide control APIs for enabling or disabling USB functionality. + +``` +/* Initialize USB in device mode and enable power */ +bsp_usb_host_start(BSP_USB_HOST_POWER_MODE_USB_DEV, true); + +... +/* Deinitialize and stop USB */ +bsp_usb_host_stop(); +``` + +> [!NOTE] +> Not all BSPs implement USB support or provide power control. Refer to the board's documentation and the BSP header files for available functions and supported modes. + +For more USB-related APIs and configuration options, check the corresponding BSP header files. + +### USB API Reference + +## Structures and Types + +| Type | Name | +| ---: | :--- | +| enum | [**bsp\_usb\_host\_power\_mode\_t**](#enum-bsp_usb_host_power_mode_t)
_Power modes of USB Host connector._ | +| typedef enum [**bsp\_usb\_host\_power\_mode\_t**](#enum-bsp_usb_host_power_mode_t) | [**bsp\_usb\_host\_power\_mode\_t**](#typedef-bsp_usb_host_power_mode_t)
_Power modes of USB Host connector._ | + +## Functions + +| Type | Name | +| ---: | :--- | +| esp\_err\_t | [**bsp\_usb\_host\_start**](#function-bsp_usb_host_start) ([**bsp\_usb\_host\_power\_mode\_t**](#enum-bsp_usb_host_power_mode_t) mode, bool limit\_500mA)
_Start USB host._ | +| esp\_err\_t | [**bsp\_usb\_host\_stop**](#function-bsp_usb_host_stop) (void)
_Stop USB host._ | + +## Macros + +| Type | Name | +| ---: | :--- | +| define | [**BSP\_USB\_NEG**](#define-bsp_usb_neg) (GPIO\_NUM\_19)
| +| define | [**BSP\_USB\_POS**](#define-bsp_usb_pos) (GPIO\_NUM\_20)
| + + +## Structures and Types Documentation + +### enum `bsp_usb_host_power_mode_t` + +_Power modes of USB Host connector._ +```c +enum bsp_usb_host_power_mode_t { + BSP_USB_HOST_POWER_MODE_USB_DEV +}; +``` + +### typedef `bsp_usb_host_power_mode_t` + +_Power modes of USB Host connector._ +```c +typedef enum bsp_usb_host_power_mode_t bsp_usb_host_power_mode_t; +``` + + +## Functions Documentation + +### function `bsp_usb_host_start` + +_Start USB host._ +```c +esp_err_t bsp_usb_host_start ( + bsp_usb_host_power_mode_t mode, + bool limit_500mA +) +``` + + +This is a one-stop-shop function that will configure the board for USB Host mode and start USB Host library + + + +**Parameters:** + + +* `mode` USB Host connector power mode (Not used on this board) +* `limit_500mA` Limit output current to 500mA (Not used on this board) + + +**Returns:** + + + +* ESP\_OK On success +* ESP\_ERR\_INVALID\_ARG Parameter error +* ESP\_ERR\_NO\_MEM Memory cannot be allocated +### function `bsp_usb_host_stop` + +_Stop USB host._ +```c +esp_err_t bsp_usb_host_stop ( + void +) +``` + + +USB Host lib will be uninstalled and power from connector removed. + + + +**Returns:** + + + +* ESP\_OK On success +* ESP\_ERR\_INVALID\_ARG Parameter error + + + + + + +## :camera: Camera + +The BSP provides a helper function bsp_camera_start() for initializing the on-board camera module. +This function sets up the required I2C bus, video subsystem, and camera clock if necessary. + +### Example Usage + +Camera usage can be quite complex. For a complete example, refer to the [`display_camera_csi`](https://github.com/espressif/esp-bsp/tree/master/examples/display_camera_csi) example in the BSP repository, or to the examples provided in the [`esp_video`](https://github.com/espressif/esp-video-components/tree/master/esp_video) component. + +> [!NOTE] +> Please, do not forget select right camera sensor in `menuconfig` + +### Camera API Reference + +## Structures and Types + +| Type | Name | +| ---: | :--- | +| struct | [**bsp\_camera\_cfg\_t**](#struct-bsp_camera_cfg_t)
_BSP camera configuration structure (for future use)_ | + +## Functions + +| Type | Name | +| ---: | :--- | +| esp\_err\_t | [**bsp\_camera\_start**](#function-bsp_camera_start) (const [**bsp\_camera\_cfg\_t**](#struct-bsp_camera_cfg_t) \*cfg)
_Initialize camera._ | + +## Macros + +| Type | Name | +| ---: | :--- | +| define | [**BSP\_CAMERA\_DEVICE**](#define-bsp_camera_device) (ESP\_VIDEO\_MIPI\_CSI\_DEVICE\_NAME)
| +| define | [**BSP\_CAMERA\_GPIO\_XCLK**](#define-bsp_camera_gpio_xclk) (GPIO\_NUM\_NC)
| +| define | [**BSP\_CAMERA\_ROTATION**](#define-bsp_camera_rotation) (0)
| +| define | [**BSP\_CAMERA\_RST**](#define-bsp_camera_rst) (GPIO\_NUM\_NC)
| + + +## Structures and Types Documentation + +### struct `bsp_camera_cfg_t` + +_BSP camera configuration structure (for future use)_ + +Variables: + +- uint8\_t dummy + + +## Functions Documentation + +### function `bsp_camera_start` + +_Initialize camera._ +```c +esp_err_t bsp_camera_start ( + const bsp_camera_cfg_t *cfg +) +``` + + +Camera sensor initialization. + + + diff --git a/components/espressif__esp32_p4_function_ev_board_noglib/CHECKSUMS.json b/components/espressif__esp32_p4_function_ev_board_noglib/CHECKSUMS.json new file mode 100644 index 0000000..b8f4cec --- /dev/null +++ b/components/espressif__esp32_p4_function_ev_board_noglib/CHECKSUMS.json @@ -0,0 +1 @@ +{"version":"1.0","algorithm":"sha256","created_at":"2025-12-19T03:52:43.446284+00:00","files":[{"path":"API.md","size":42107,"hash":"9e89b7b5e32081cf75f35189676aa31d993eb74ca51506268e5e9871ca3715a4"},{"path":"CMakeLists.txt","size":557,"hash":"fd22d2ab656aa46a327bee55e72f31fb076bd4c14ba36b1fcbfc9db631cc288c"},{"path":"Kconfig","size":5293,"hash":"f42fef51fa52e21a81563fdc43f6814faa0525373f0b28d7d9f283f15a269fcf"},{"path":"LICENSE","size":11358,"hash":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30"},{"path":"README.md","size":11597,"hash":"74cc0e032aed6f42a537cb5c37fed437cd255021a2a771a0f6342867d384b7e9"},{"path":"esp32_p4_function_ev_board.c","size":36378,"hash":"85dcd73fa74be1a8dc7c4f9de12d50c8f8df20bda839012393a613ce4ad618ee"},{"path":"idf_component.yml","size":697,"hash":"ad2daaa82a8f49f8ca0a2e75d1021930f4ee1cf061641f2d438d1a8ae5d2b496"},{"path":"doc/esp32-p4-function-ev-board-annotated-photo-front.png","size":1073234,"hash":"5c027ad92b12778edac777a214f51b036d95385ec200aac8b9c46ed45b937e2c"},{"path":"doc/esp32_p4_function_ev_board.webp","size":648904,"hash":"1ba4346bbe5e58b9523f2e04aed089baa3777c608e7d5bb41e670489db9ce289"},{"path":"priv_include/bsp_err_check.h","size":1620,"hash":"1641bf04cb10e8ab64d4cf66972bb9648c2ac6fc92b7fd71e4391f0fd45d1995"},{"path":"include/bsp/config.h","size":693,"hash":"ec85b0e92b31ce103d35cbd6cac5e5c09a602685fb1c2a088814dda2b7ac1ffd"},{"path":"include/bsp/display.h","size":6695,"hash":"204161247243fdd9eefcc517cab5a2e8e5ede3910509269de423fcc742caa489"},{"path":"include/bsp/esp-bsp.h","size":175,"hash":"4c9377de5ad10bb56fdec5882f683cd5fc402ae25ef7ee2b5bc68b2792d91955"},{"path":"include/bsp/esp32_p4_function_ev_board.h","size":17683,"hash":"5bab165f1979c48158469e9c6a97e737270b26c04b43d7b3b0714c40b0437993"},{"path":"include/bsp/touch.h","size":1315,"hash":"a140b1485797d84acb034e4bee42004e785be9a031650eca3ed634919eee2c87"}]} \ No newline at end of file diff --git a/components/espressif__esp32_p4_function_ev_board_noglib/CMakeLists.txt b/components/espressif__esp32_p4_function_ev_board_noglib/CMakeLists.txt new file mode 100644 index 0000000..b2e4bca --- /dev/null +++ b/components/espressif__esp32_p4_function_ev_board_noglib/CMakeLists.txt @@ -0,0 +1,19 @@ +if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.3") + set(REQ esp_driver_gpio esp_driver_i2s esp_driver_sdmmc esp_driver_sdspi esp_driver_i2c) + set(PRIV_REQ esp_driver_spi esp_driver_ledc) +else() + set(REQ driver) + set(PRIV_REQ "") +endif() + +if(${IDF_VERSION_MAJOR} LESS 6) + list(APPEND PRIV_REQ usb) +endif() + +idf_component_register( + SRCS "esp32_p4_function_ev_board.c" + INCLUDE_DIRS "include" + PRIV_INCLUDE_DIRS "priv_include" + REQUIRES ${REQ} fatfs + PRIV_REQUIRES ${PRIV_REQ} esp_lcd spiffs esp_psram +) diff --git a/components/espressif__esp32_p4_function_ev_board_noglib/Kconfig b/components/espressif__esp32_p4_function_ev_board_noglib/Kconfig new file mode 100644 index 0000000..ee598e9 --- /dev/null +++ b/components/espressif__esp32_p4_function_ev_board_noglib/Kconfig @@ -0,0 +1,163 @@ +menu "Board Support Package (ESP32-P4)" + + 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 + ESP32P4 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 "I2S" + config BSP_I2S_NUM + int "I2S peripheral index" + default 1 + range 0 2 + help + ESP32P4 has three I2S peripherals, pick the one you want to use. + endmenu + + menu "uSD card - Virtual File System" + config BSP_SD_FORMAT_ON_MOUNT_FAIL + bool "Format uSD card if mounting fails" + default n + help + The SDMMC host will format (FAT) the uSD card if it fails to mount the filesystem. + + config BSP_SD_MOUNT_POINT + string "uSD card mount point" + default "/sdcard" + help + Mount point of the uSD card in the Virtual File System + + 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 "Display" + config BSP_LCD_DPI_BUFFER_NUMS + int "Set number of frame buffers" + default 1 + range 1 3 + help + Let DPI LCD driver create a specified number of frame-size buffers. Only when it is set to multiple can the avoiding tearing be turned on. + + config BSP_DISPLAY_LVGL_AVOID_TEAR + bool "Avoid tearing effect" + depends on BSP_LCD_DPI_BUFFER_NUMS > 1 + default "n" + help + Avoid tearing effect through LVGL buffer mode and double frame buffers of RGB LCD. This feature is only available for RGB LCD. + + choice BSP_DISPLAY_LVGL_MODE + depends on BSP_DISPLAY_LVGL_AVOID_TEAR + prompt "Select LVGL buffer mode" + default BSP_DISPLAY_LVGL_FULL_REFRESH + config BSP_DISPLAY_LVGL_FULL_REFRESH + bool "Full refresh" + config BSP_DISPLAY_LVGL_DIRECT_MODE + bool "Direct mode" + endchoice + + 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. + + choice BSP_LCD_COLOR_FORMAT + prompt "Select LCD color format" + default BSP_LCD_COLOR_FORMAT_RGB565 + help + Select the LCD color format RGB565/RGB888. + + config BSP_LCD_COLOR_FORMAT_RGB565 + bool "RGB565" + config BSP_LCD_COLOR_FORMAT_RGB888 + bool "RGB888" + endchoice + + config BSP_LCD_USE_DMA2D + bool "Select whether to use DMA2D" + default "y" + help + Select whether to use DMA2D to draw the bitmap. + + choice BSP_LCD_TYPE + prompt "Select LCD type" + default BSP_LCD_TYPE_1024_600 + help + Select the LCD. + + config BSP_LCD_TYPE_1024_600 + bool "LCD 7-inch 1024x600 - ek79007" + config BSP_LCD_TYPE_1280_800 + bool "LCD 1280x800 - ili9881c" + config BSP_LCD_TYPE_HDMI + bool "HDMI - lt8912b" + endchoice + + choice BSP_LCD_HDMI_RESOLUTION + depends on BSP_LCD_TYPE_HDMI + prompt "Select HDMI resolution" + default BSP_LCD_HDMI_1280x720_60HZ + help + Select the HDMI resolution. + + config BSP_LCD_HDMI_800x600_60HZ + bool "800x600@60HZ" + config BSP_LCD_HDMI_1280x720_60HZ + bool "1280x720@60HZ" + config BSP_LCD_HDMI_1280x800_60HZ + bool "1280x800@60HZ" + config BSP_LCD_HDMI_1920x1080_30HZ + bool "1920x1080@30HZ" + endchoice + + endmenu + +endmenu diff --git a/components/espressif__esp32_p4_function_ev_board_noglib/LICENSE b/components/espressif__esp32_p4_function_ev_board_noglib/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/components/espressif__esp32_p4_function_ev_board_noglib/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/components/espressif__esp32_p4_function_ev_board_noglib/README.md b/components/espressif__esp32_p4_function_ev_board_noglib/README.md new file mode 100644 index 0000000..d68da83 --- /dev/null +++ b/components/espressif__esp32_p4_function_ev_board_noglib/README.md @@ -0,0 +1,126 @@ +> :warning: This is **No Graphical Library version** of esp32_p4_function_ev_board BSP. If you want to use this BSP with LVGL use [esp32_p4_function_ev_board](https://components.espressif.com/components/espressif/esp32_p4_function_ev_board) component. +# BSP: ESP32-P4 Function EV Board + +| [HW Reference](https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32p4/esp32-p4-function-ev-board/user_guide.html) | [HOW TO USE API](API.md) | [EXAMPLES](#compatible-bsp-examples) | [![Component Registry](https://components.espressif.com/components/espressif/esp32_p4_function_ev_board/badge.svg)](https://components.espressif.com/components/espressif/esp32_p4_function_ev_board) | ![maintenance-status](https://img.shields.io/badge/maintenance-actively--developed-brightgreen.svg) | +| --- | --- | --- | --- | -- | + +## Overview + + + +
+ +ESP32-P4-Function-EV-Board is a multimedia development board based on the ESP32-P4 chip. ESP32-P4 chip features a dual-core 400 MHz RISC-V processor and supports up to 32 MB PSRAM. In addition, ESP32-P4 supports USB 2.0 specification, MIPI-CSI/DSI, H264 Encoder, and various other peripherals. With all of its outstanding features, the board is an ideal choice for developing low-cost, high-performance, low-power network-connected audio and video products. + + + +
+ +![](doc/esp32-p4-function-ev-board-annotated-photo-front.png) + +## HW Version + +| HW version | BSP Version | +| :--------: | :---------: | +| V1.0 | ^1 | +| V1.2 | ^2 | +| [V1.4](https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32p4/esp32-p4-function-ev-board/user_guide_v1.4.html) | ^3 | +| [V1.5](https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32p4/esp32-p4-function-ev-board/user_guide.html) | ^3 | + +> [!NOTE] +> For using boards with ESP32-P4 ECO5 (Board v1.5), change `CONFIG_ESP32P4_SELECTS_REV_LESS_V3=n` in `menuconfig`. + +## Configuration + +Configuration in `menuconfig`. + +Selection LCD display `Board Support Package(ESP32-P4) --> Display --> Select LCD type` +- LCD 1280x800 - ili9881c (default) +- LCD 1024x600 - ek79007 +- HDMI - lt8912b + - 800x600@60HZ + - 1280x720@60HZ + - 1280x800@60HZ + - 1920x1080@30HZ + +Selection color format `Board Support Package(ESP32-P4) --> Display --> Select LCD color format` +- RGB565 (default) +- RGB888 + +## HDMI Support + +This BSP supports HDMI converter Lontium LT8912B. Follow these rules for using it with HDMI: +- Use ESP-IDF 5.4 or older (from commit [93fdbf2](https://github.com/espressif/esp-idf/commit/93fdbf25b3ea7e44d1f519ed61050847dcc8a076)) +- Only RGB888 is supported with HDMI +- Use MIPI-DSI to HDMI converter Lontium LT8912B + +## Capabilities and dependencies + +
+ + +| Available | Capability | Controller/Codec | Component | Version | +|------------------|------------------------|--------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------| +|:heavy_check_mark:| :pager: DISPLAY |ek79007, ili9881c, lt8912b|idf
[espressif/esp_lcd_ek79007](https://components.espressif.com/components/espressif/esp_lcd_ek79007)
[espressif/esp_lcd_ili9881c](https://components.espressif.com/components/espressif/esp_lcd_ili9881c)
[espressif/esp_lcd_lt8912b](https://components.espressif.com/components/espressif/esp_lcd_lt8912b)|>=5.4
>=2.0.1,<3.0.0
>=1.0.3,<2.0.0
>=0.1.3,<1.0.0| +|:heavy_check_mark:|:black_circle: LVGL_PORT| | | | +|:heavy_check_mark:| :point_up: TOUCH | gt911 | [espressif/esp_lcd_touch_gt911](https://components.espressif.com/components/espressif/esp_lcd_touch_gt911) | ^1 | +| :x: | :radio_button: BUTTONS | | | | +|:heavy_check_mark:| :musical_note: AUDIO | | [espressif/esp_codec_dev](https://components.espressif.com/components/espressif/esp_codec_dev) | ~1.5 | +|:heavy_check_mark:| :speaker: AUDIO_SPEAKER| es8311 | | | +|:heavy_check_mark:| :microphone: AUDIO_MIC | es8311 | | | +|:heavy_check_mark:| :floppy_disk: SDCARD | | idf | >=5.4 | +| :x: | :video_game: IMU | | | | +|:heavy_check_mark:| :camera: CAMERA | OV5647, SC2336 | | | + + +
+ +## Compatible BSP Examples + +
+ + +| Example | Description | Try with ESP Launchpad | +| ------- | ----------- | ---------------------- | +| [Display Example](https://github.com/espressif/esp-bsp/tree/master/examples/display) | Show an image on the screen with a simple startup animation (LVGL) | [Flash Example](https://espressif.github.io/esp-launchpad/?flashConfigURL=https://espressif.github.io/esp-bsp/config.toml&app=display-) | +| [Camera Example (MIPI-CSI)](https://github.com/espressif/esp-bsp/tree/master/examples/display_camera_csi) | Stream camera (MIPI-CSI) output to display (LVGL) | [Flash Example](https://espressif.github.io/esp-launchpad/?flashConfigURL=https://espressif.github.io/esp-bsp/config.toml&app=display_camera-) | +| [LVGL Benchmark Example](https://github.com/espressif/esp-bsp/tree/master/examples/display_lvgl_benchmark) | Run LVGL benchmark tests | - | +| [LVGL Demos Example](https://github.com/espressif/esp-bsp/tree/master/examples/display_lvgl_demos) | Run the LVGL demo player - all LVGL examples are included (LVGL) | [Flash Example](https://espressif.github.io/esp-launchpad/?flashConfigURL=https://espressif.github.io/esp-bsp/config.toml&app=display_lvgl_demos-) | +| [Display Rotation Example](https://github.com/espressif/esp-bsp/tree/master/examples/display_rotation) | Rotate screen using buttons or an accelerometer (`BSP_CAPS_IMU`, if available) | [Flash Example](https://espressif.github.io/esp-launchpad/?flashConfigURL=https://espressif.github.io/esp-bsp/config.toml&app=display_rotation-) | +| [Display SD card Example](https://github.com/espressif/esp-bsp/tree/master/examples/display_sdcard) | Example of mounting an SD card using SD-MMC/SPI with display interaction. This example is also supported on boards without a display. | [Flash Example](https://espressif.github.io/esp-launchpad/?flashConfigURL=https://espressif.github.io/esp-bsp/config.toml&app=display_sdcard) | +| [USB HID Example](https://github.com/espressif/esp-bsp/tree/master/examples/display_usb_hid) | USB HID demo (keyboard, mouse, or gamepad visualization using LVGL) | - | + + +
+ + + +## LVGL Benchmark + +**DATE:** 07.11.2025 09:03 + +**LVGL version:** 9.4.0 + +| Name | Avg. CPU | Avg. FPS | Avg. time | render time | flush time | +| ---- | :------: | :------: | :-------: | :---------: | :--------: | +| Empty screen | 61% | 88 | 5 | 3 | 2 | +| Moving wallpaper | 89% | 71 | 10 | 7 | 3 | +| Single rectangle | 23% | 88 | 1 | 1 | 0 | +| Multiple rectangles | 39% | 90 | 2 | 2 | 0 | +| Multiple RGB images | 31% | 96 | 2 | 2 | 0 | +| Multiple ARGB images | 58% | 93 | 6 | 6 | 0 | +| Rotated ARGB images | 87% | 61 | 14 | 14 | 0 | +| Multiple labels | 92% | 61 | 12 | 12 | 0 | +| Screen sized text | 99% | 13 | 70 | 67 | 3 | +| Multiple arcs | 98% | 47 | 18 | 16 | 2 | +| Containers | 29% | 87 | 2 | 2 | 0 | +| Containers with overlay | 88% | 28 | 32 | 30 | 2 | +| Containers with opa | 41% | 89 | 4 | 4 | 0 | +| Containers with opa_layer | 62% | 72 | 12 | 12 | 0 | +| Containers with scrolling | 96% | 29 | 30 | 28 | 2 | +| Widgets demo | 99% | 17 | 49 | 48 | 1 | +| All scenes avg. | 68% | 64 | 15 | 15 | 0 | + + + + \ No newline at end of file diff --git a/components/espressif__esp32_p4_function_ev_board_noglib/doc/esp32-p4-function-ev-board-annotated-photo-front.png b/components/espressif__esp32_p4_function_ev_board_noglib/doc/esp32-p4-function-ev-board-annotated-photo-front.png new file mode 100644 index 0000000..3310838 Binary files /dev/null and b/components/espressif__esp32_p4_function_ev_board_noglib/doc/esp32-p4-function-ev-board-annotated-photo-front.png differ diff --git a/components/espressif__esp32_p4_function_ev_board_noglib/doc/esp32_p4_function_ev_board.webp b/components/espressif__esp32_p4_function_ev_board_noglib/doc/esp32_p4_function_ev_board.webp new file mode 100644 index 0000000..53283a0 Binary files /dev/null and b/components/espressif__esp32_p4_function_ev_board_noglib/doc/esp32_p4_function_ev_board.webp differ diff --git a/components/espressif__esp32_p4_function_ev_board_noglib/esp32_p4_function_ev_board.c b/components/espressif__esp32_p4_function_ev_board_noglib/esp32_p4_function_ev_board.c new file mode 100644 index 0000000..20aa1a6 --- /dev/null +++ b/components/espressif__esp32_p4_function_ev_board_noglib/esp32_p4_function_ev_board.c @@ -0,0 +1,1241 @@ +/* + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "sdkconfig.h" +#include "driver/gpio.h" +#include "driver/ledc.h" +#include "esp_err.h" +#include "esp_log.h" +#include "esp_check.h" +#include "esp_spiffs.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_mipi_dsi.h" +#include "esp_ldo_regulator.h" +#include "esp_vfs_fat.h" +#include "usb/usb_host.h" +#include "sd_pwr_ctrl_by_on_chip_ldo.h" + + +#if CONFIG_BSP_LCD_TYPE_1024_600 +#include "esp_lcd_ek79007.h" +#elif CONFIG_BSP_LCD_TYPE_HDMI +#include "esp_lcd_lt8912b.h" +#else +#include "esp_lcd_ili9881c.h" +#endif + +#include "bsp/esp32_p4_function_ev_board.h" +#include "bsp/display.h" +#include "bsp/touch.h" +#include "esp_lcd_touch_gt911.h" +#include "bsp_err_check.h" +#include "esp_codec_dev_defaults.h" +#include "esp_video_device.h" +#include "esp_video_init.h" + +static const char *TAG = "ESP32_P4_EV"; + +#if (BSP_CONFIG_NO_GRAPHIC_LIB == 0) +static lv_indev_t *disp_indev = NULL; +#endif // (BSP_CONFIG_NO_GRAPHIC_LIB == 0) + +static sdmmc_card_t *bsp_sdcard = NULL; // uSD card handle +static bool i2c_initialized = false; +static bool spi_sd_initialized = false; +static sd_pwr_ctrl_handle_t pwr_ctrl_handle = NULL; //SD LDO handle +static TaskHandle_t usb_host_task; // USB Host Library task +#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)) +static i2c_master_bus_handle_t i2c_handle = NULL; // I2C Handle +#endif +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 */ +static bsp_lcd_handles_t disp_handles; +static esp_ldo_channel_handle_t disp_phy_pwr_chan = NULL; +static esp_lcd_touch_handle_t tp = NULL; +static esp_lcd_panel_io_handle_t tp_io_handle = NULL; + +/* 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_extra_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_i2c_init(void) +{ + /* I2C was initialized before */ + if (i2c_initialized) { + return ESP_OK; + } + + i2c_master_bus_config_t i2c_bus_conf = { + .clk_source = I2C_CLK_SRC_DEFAULT, + .sda_io_num = BSP_I2C_SDA, + .scl_io_num = BSP_I2C_SCL, + .i2c_port = BSP_I2C_NUM, + }; + BSP_ERROR_CHECK_RETURN_ERR(i2c_new_master_bus(&i2c_bus_conf, &i2c_handle)); + + i2c_initialized = true; + + return ESP_OK; +} + +esp_err_t bsp_i2c_deinit(void) +{ + if (i2c_initialized && i2c_handle) { + BSP_ERROR_CHECK_RETURN_ERR(i2c_del_master_bus(i2c_handle)); + i2c_initialized = false; + } + return ESP_OK; +} + +i2c_master_bus_handle_t bsp_i2c_get_handle(void) +{ + return i2c_handle; +} + +sdmmc_card_t *bsp_sdcard_get_handle(void) +{ + return bsp_sdcard; +} + +void bsp_sdcard_get_sdmmc_host(const int slot, sdmmc_host_t *config) +{ + assert(config); + + sdmmc_host_t host_config = SDMMC_HOST_DEFAULT(); + host_config.slot = slot; + host_config.max_freq_khz = SDMMC_FREQ_HIGHSPEED; + + memcpy(config, &host_config, sizeof(sdmmc_host_t)); +} + +void bsp_sdcard_get_sdspi_host(const int slot, sdmmc_host_t *config) +{ + assert(config); + + sdmmc_host_t host_config = SDSPI_HOST_DEFAULT(); + host_config.slot = slot; + + memcpy(config, &host_config, sizeof(sdmmc_host_t)); +} + +void bsp_sdcard_sdmmc_get_slot(const int slot, sdmmc_slot_config_t *config) +{ + assert(config); + memset(config, 0, sizeof(sdmmc_slot_config_t)); + + /* SD card is connected to Slot 0 pins. Slot 0 uses IO MUX, so not specifying the pins here */ + config->cd = SDMMC_SLOT_NO_CD; + config->wp = SDMMC_SLOT_NO_WP; + config->width = 4; + config->flags = 0; +} + +void bsp_sdcard_sdspi_get_slot(const spi_host_device_t spi_host, sdspi_device_config_t *config) +{ + assert(config); + memset(config, 0, sizeof(sdspi_device_config_t)); + + config->gpio_cs = BSP_SD_SPI_CS; + config->gpio_cd = SDSPI_SLOT_NO_CD; + config->gpio_wp = SDSPI_SLOT_NO_WP; + config->gpio_int = GPIO_NUM_NC; + config->gpio_wp_polarity = SDSPI_IO_ACTIVE_LOW; + config->host_id = spi_host; +} + +esp_err_t bsp_sdcard_sdmmc_mount(bsp_sdcard_cfg_t *cfg) +{ + sdmmc_host_t sdhost = {0}; + sdmmc_slot_config_t sdslot = {0}; + const esp_vfs_fat_sdmmc_mount_config_t mount_config = { +#ifdef CONFIG_BSP_SD_FORMAT_ON_MOUNT_FAIL + .format_if_mount_failed = true, +#else + .format_if_mount_failed = false, +#endif + .max_files = 5, + .allocation_unit_size = 64 * 1024 + }; + assert(cfg); + + if (!cfg->mount) { + cfg->mount = &mount_config; + } + + if (!cfg->host) { + bsp_sdcard_get_sdmmc_host(SDMMC_HOST_SLOT_0, &sdhost); + cfg->host = &sdhost; + } + + if (!cfg->slot.sdmmc) { + bsp_sdcard_sdmmc_get_slot(SDMMC_HOST_SLOT_0, &sdslot); + cfg->slot.sdmmc = &sdslot; + } + + sd_pwr_ctrl_ldo_config_t ldo_config = { + .ldo_chan_id = 4, + }; + esp_err_t ret = sd_pwr_ctrl_new_on_chip_ldo(&ldo_config, &pwr_ctrl_handle); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to create a new on-chip LDO power control driver"); + return ret; + } + cfg->host->pwr_ctrl_handle = pwr_ctrl_handle; + +#if !CONFIG_FATFS_LONG_FILENAMES + ESP_LOGW(TAG, "Warning: Long filenames on SD card are disabled in menuconfig!"); +#endif + + return esp_vfs_fat_sdmmc_mount(BSP_SD_MOUNT_POINT, cfg->host, cfg->slot.sdmmc, cfg->mount, &bsp_sdcard); +} + +esp_err_t bsp_sdcard_sdspi_mount(bsp_sdcard_cfg_t *cfg) +{ + sdmmc_host_t sdhost = {0}; + sdspi_device_config_t sdslot = {0}; + const esp_vfs_fat_sdmmc_mount_config_t mount_config = { +#ifdef CONFIG_BSP_SD_FORMAT_ON_MOUNT_FAIL + .format_if_mount_failed = true, +#else + .format_if_mount_failed = false, +#endif + .max_files = 5, + .allocation_unit_size = 64 * 1024 + }; + assert(cfg); + + ESP_LOGD(TAG, "Initialize SPI bus"); + const spi_bus_config_t buscfg = { + .sclk_io_num = BSP_SD_SPI_CLK, + .mosi_io_num = BSP_SD_SPI_MOSI, + .miso_io_num = BSP_SD_SPI_MISO, + .quadwp_io_num = GPIO_NUM_NC, + .quadhd_io_num = GPIO_NUM_NC, + .max_transfer_sz = 4000, + }; + ESP_RETURN_ON_ERROR(spi_bus_initialize(BSP_SDSPI_HOST, &buscfg, SDSPI_DEFAULT_DMA), TAG, "SPI init failed"); + spi_sd_initialized = true; + + if (!cfg->mount) { + cfg->mount = &mount_config; + } + + if (!cfg->host) { + bsp_sdcard_get_sdspi_host(SDMMC_HOST_SLOT_0, &sdhost); + cfg->host = &sdhost; + } + + if (!cfg->slot.sdspi) { + bsp_sdcard_sdspi_get_slot(BSP_SDSPI_HOST, &sdslot); + cfg->slot.sdspi = &sdslot; + } + + sd_pwr_ctrl_ldo_config_t ldo_config = { + .ldo_chan_id = 4, + }; + esp_err_t ret = sd_pwr_ctrl_new_on_chip_ldo(&ldo_config, &pwr_ctrl_handle); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to create a new on-chip LDO power control driver"); + return ret; + } + cfg->host->pwr_ctrl_handle = pwr_ctrl_handle; + +#if !CONFIG_FATFS_LONG_FILENAMES + ESP_LOGW(TAG, "Warning: Long filenames on SD card are disabled in menuconfig!"); +#endif + + return esp_vfs_fat_sdspi_mount(BSP_SD_MOUNT_POINT, cfg->host, cfg->slot.sdspi, cfg->mount, &bsp_sdcard); +} + +esp_err_t bsp_sdcard_mount(void) +{ + bsp_sdcard_cfg_t cfg = {0}; + return bsp_sdcard_sdmmc_mount(&cfg); +} + +esp_err_t bsp_sdcard_unmount(void) +{ + esp_err_t ret = ESP_OK; + + if (pwr_ctrl_handle) { + ret |= sd_pwr_ctrl_del_on_chip_ldo(pwr_ctrl_handle); + pwr_ctrl_handle = NULL; + } + + ret |= esp_vfs_fat_sdcard_unmount(BSP_SD_MOUNT_POINT, bsp_sdcard); + bsp_sdcard = NULL; + + if (spi_sd_initialized) { + ret |= spi_bus_free(BSP_SDSPI_HOST); + spi_sd_initialized = false; + } + + 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_err_t bsp_audio_init(const i2s_std_config_t *i2s_config) +{ + 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 + ESP_ERROR_CHECK(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_ERROR_CHECK(i2s_channel_init_std_mode(i2s_tx_chan, p_i2s_cfg)); + ESP_ERROR_CHECK(i2s_channel_enable(i2s_tx_chan)); + } + + if (i2s_rx_chan != NULL) { + ESP_ERROR_CHECK(i2s_channel_init_std_mode(i2s_rx_chan, p_i2s_cfg)); + ESP_ERROR_CHECK(i2s_channel_enable(i2s_rx_chan)); + } + + audio_codec_i2s_cfg_t i2s_cfg = { + .port = CONFIG_BSP_I2S_NUM, + .tx_handle = i2s_tx_chan, + .rx_handle = i2s_rx_chan, + }; + i2s_data_if = audio_codec_new_i2s_data(&i2s_cfg); + + return ESP_OK; +} + +// TX-only audio initialization to save memory (for games that don't need audio input) +esp_err_t bsp_audio_init_tx_only(const i2s_std_config_t *i2s_config) +{ + if (i2s_tx_chan) { + /* Audio was initialized before */ + return ESP_OK; + } + + /* Setup I2S peripheral - TX channel only */ + i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(CONFIG_BSP_I2S_NUM, I2S_ROLE_MASTER); + chan_cfg.auto_clear = true; + ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &i2s_tx_chan, NULL)); // NULL for RX + + /* Setup I2S TX channel - 22.05kHz mono to save DMA memory + * OpenTyrian's 44.1kHz audio will be downsampled to 22.05kHz + * Using mono mode (not stereo) to save 50% of DMA memory */ + i2s_std_config_t tx_cfg_default = { + .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(22050), // 22.05kHz (saves DMA memory) + .slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO), + .gpio_cfg = BSP_I2S_GPIO_CFG, + }; + + const i2s_std_config_t *p_i2s_cfg = &tx_cfg_default; + if (i2s_config != NULL) { + p_i2s_cfg = i2s_config; + } + + ESP_ERROR_CHECK(i2s_channel_init_std_mode(i2s_tx_chan, p_i2s_cfg)); + ESP_ERROR_CHECK(i2s_channel_enable(i2s_tx_chan)); + + audio_codec_i2s_cfg_t i2s_cfg = { + .port = CONFIG_BSP_I2S_NUM, + .tx_handle = i2s_tx_chan, + .rx_handle = NULL, // No RX + }; + i2s_data_if = audio_codec_new_i2s_data(&i2s_cfg); + + return ESP_OK; +} + +esp_codec_dev_handle_t bsp_audio_codec_speaker_init_tx_only(void) +{ + if (i2s_data_if == NULL) { + /* Initialize I2C */ + ESP_ERROR_CHECK(bsp_i2c_init()); + /* Configure I2S TX-only peripheral */ + ESP_ERROR_CHECK(bsp_audio_init_tx_only(NULL)); + } + + 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, + .bus_handle = i2c_handle, + }; + const audio_codec_ctrl_if_t *i2c_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg); + assert(i2c_ctrl_if); + + 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_TYPE_OUT, + .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); + assert(es8311_dev); + + 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_speaker_init(void) +{ + if (i2s_data_if == NULL) { + /* Initilize I2C */ + ESP_ERROR_CHECK(bsp_i2c_init()); + /* Configure I2S peripheral and Power Amplifier */ + ESP_ERROR_CHECK(bsp_audio_init(NULL)); + } + 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, + .bus_handle = i2c_handle, + }; + const audio_codec_ctrl_if_t *i2c_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg); + assert(i2c_ctrl_if); + + 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_TYPE_OUT, + .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); + assert(es8311_dev); + + esp_codec_dev_cfg_t codec_dev_cfg = { + .dev_type = ESP_CODEC_DEV_TYPE_IN_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) +{ + if (i2s_data_if == NULL) { + /* Initilize I2C */ + ESP_ERROR_CHECK(bsp_i2c_init()); + /* Configure I2S peripheral and Power Amplifier */ + ESP_ERROR_CHECK(bsp_audio_init(NULL)); + } + 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, + .bus_handle = i2c_handle, + }; + const audio_codec_ctrl_if_t *i2c_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg); + assert(i2c_ctrl_if); + + 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_BOTH, + .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); + assert(es8311_dev); + + esp_codec_dev_cfg_t codec_es8311_dev_cfg = { + .dev_type = ESP_CODEC_DEV_TYPE_IN, + .codec_if = es8311_dev, + .data_if = i2s_data_if, + }; + return esp_codec_dev_new(&codec_es8311_dev_cfg); +} + +// Bit number used to represent command and parameter +#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_deinit(void) +{ + const ledc_timer_config_t LCD_backlight_timer = { + .speed_mode = LEDC_LOW_SPEED_MODE, + .timer_num = 1, + .deconfigure = 1 + }; + BSP_ERROR_CHECK_RETURN_ERR(ledc_timer_pause(LEDC_LOW_SPEED_MODE, 1)); + BSP_ERROR_CHECK_RETURN_ERR(ledc_timer_config(&LCD_backlight_timer)); + 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_enable_dsi_phy_power(void) +{ +#if BSP_MIPI_DSI_PHY_PWR_LDO_CHAN > 0 + // Turn on the power for MIPI DSI PHY, so it can go from "No Power" state to "Shutdown" state + esp_ldo_channel_config_t ldo_cfg = { + .chan_id = BSP_MIPI_DSI_PHY_PWR_LDO_CHAN, + .voltage_mv = BSP_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV, + }; + ESP_RETURN_ON_ERROR(esp_ldo_acquire_channel(&ldo_cfg, &disp_phy_pwr_chan), TAG, "Acquire LDO channel for DPHY failed"); + ESP_LOGI(TAG, "MIPI DSI PHY Powered on"); +#endif // BSP_MIPI_DSI_PHY_PWR_LDO_CHAN > 0 + + return ESP_OK; +} + +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; + bsp_lcd_handles_t handles; + ret = bsp_display_new_with_handles(config, &handles); + + *ret_panel = handles.panel; + *ret_io = handles.io; + + return ret; +} + +esp_err_t bsp_display_new_with_handles(const bsp_display_config_t *config, bsp_lcd_handles_t *ret_handles) +{ + esp_err_t ret = ESP_OK; + esp_lcd_panel_io_handle_t io = NULL; + esp_lcd_panel_handle_t disp_panel = NULL; + + ESP_RETURN_ON_ERROR(bsp_display_brightness_init(), TAG, "Brightness init failed"); + ESP_RETURN_ON_ERROR(bsp_enable_dsi_phy_power(), TAG, "DSI PHY power failed"); + + /* create MIPI DSI bus first, it will initialize the DSI PHY as well */ + esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL; + esp_lcd_dsi_bus_config_t bus_config = { + .bus_id = 0, + .num_data_lanes = BSP_LCD_MIPI_DSI_LANE_NUM, + .phy_clk_src = config->dsi_bus.phy_clk_src, + .lane_bit_rate_mbps = config->dsi_bus.lane_bit_rate_mbps, + }; + ESP_RETURN_ON_ERROR(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus), TAG, "New DSI bus init failed"); + +#if !CONFIG_BSP_LCD_TYPE_HDMI + if (config->hdmi_resolution != BSP_HDMI_RES_NONE) { + ESP_LOGW(TAG, "Please select HDMI in menuconfig, if you want to use it."); + } + + ESP_LOGI(TAG, "Install MIPI DSI LCD control panel"); + // we use DBI interface to send LCD commands and parameters + esp_lcd_dbi_io_config_t dbi_config = { + .virtual_channel = 0, + .lcd_cmd_bits = 8, // according to the LCD spec + .lcd_param_bits = 8, // according to the LCD spec + }; + ESP_GOTO_ON_ERROR(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &io), err, TAG, "New panel IO failed"); +#endif + +#if CONFIG_BSP_LCD_TYPE_1024_600 + // create EK79007 control panel + ESP_LOGI(TAG, "Install EK79007 LCD control panel"); + +#if CONFIG_BSP_LCD_COLOR_FORMAT_RGB888 + esp_lcd_dpi_panel_config_t dpi_config = EK79007_1024_600_PANEL_60HZ_CONFIG_CF(LCD_COLOR_FMT_RGB888); +#else + esp_lcd_dpi_panel_config_t dpi_config = EK79007_1024_600_PANEL_60HZ_CONFIG_CF(LCD_COLOR_FMT_RGB565); +#endif + dpi_config.num_fbs = CONFIG_BSP_LCD_DPI_BUFFER_NUMS; + +#if CONFIG_BSP_LCD_USE_DMA2D && (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(6, 0, 0)) + dpi_config.flags.use_dma2d = true; +#endif + + ek79007_vendor_config_t vendor_config = { + .mipi_config = { + .dsi_bus = mipi_dsi_bus, + .dpi_config = &dpi_config, + }, + }; + esp_lcd_panel_dev_config_t lcd_dev_config = { + .bits_per_pixel = 16, + .rgb_ele_order = BSP_LCD_COLOR_SPACE, + .reset_gpio_num = BSP_LCD_RST, + .vendor_config = &vendor_config, + }; + ESP_GOTO_ON_ERROR(esp_lcd_new_panel_ek79007(io, &lcd_dev_config, &disp_panel), err, TAG, "New LCD panel EK79007 failed"); + +#if CONFIG_BSP_LCD_USE_DMA2D && (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0)) + ESP_GOTO_ON_ERROR(esp_lcd_dpi_panel_enable_dma2d(disp_panel), err, TAG, "LCD panel enable DMA2D failed"); +#endif + + ESP_GOTO_ON_ERROR(esp_lcd_panel_reset(disp_panel), err, TAG, "LCD panel reset failed"); + ESP_GOTO_ON_ERROR(esp_lcd_panel_init(disp_panel), err, TAG, "LCD panel init failed"); +#elif CONFIG_BSP_LCD_TYPE_1280_800 + // create ILI9881C control panel + ESP_LOGI(TAG, "Install ILI9881C LCD control panel"); +#if CONFIG_BSP_LCD_COLOR_FORMAT_RGB888 + esp_lcd_dpi_panel_config_t dpi_config = ILI9881C_800_1280_PANEL_60HZ_DPI_CONFIG_CF(LCD_COLOR_FMT_RGB888); +#else + esp_lcd_dpi_panel_config_t dpi_config = ILI9881C_800_1280_PANEL_60HZ_DPI_CONFIG_CF(LCD_COLOR_FMT_RGB565); +#endif + dpi_config.num_fbs = CONFIG_BSP_LCD_DPI_BUFFER_NUMS; + +#if CONFIG_BSP_LCD_USE_DMA2D && (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(6, 0, 0)) + dpi_config.flags.use_dma2d = true; +#endif + + ili9881c_vendor_config_t vendor_config = { + .mipi_config = { + .dsi_bus = mipi_dsi_bus, + .dpi_config = &dpi_config, + .lane_num = BSP_LCD_MIPI_DSI_LANE_NUM, + }, + }; + const esp_lcd_panel_dev_config_t lcd_dev_config = { + .reset_gpio_num = BSP_LCD_RST, + .rgb_ele_order = BSP_LCD_COLOR_SPACE, + .bits_per_pixel = 16, + .vendor_config = &vendor_config, + }; + ESP_GOTO_ON_ERROR(esp_lcd_new_panel_ili9881c(io, &lcd_dev_config, &disp_panel), err, TAG, "New LCD panel ILI9881C failed"); + +#if CONFIG_BSP_LCD_USE_DMA2D && (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0)) + ESP_GOTO_ON_ERROR(esp_lcd_dpi_panel_enable_dma2d(disp_panel), err, TAG, "LCD panel enable DMA2D failed"); +#endif + + ESP_GOTO_ON_ERROR(esp_lcd_panel_reset(disp_panel), err, TAG, "LCD panel reset failed"); + ESP_GOTO_ON_ERROR(esp_lcd_panel_init(disp_panel), err, TAG, "LCD panel init failed"); + ESP_GOTO_ON_ERROR(esp_lcd_panel_disp_on_off(disp_panel, true), err, TAG, "LCD panel ON failed"); + +#elif CONFIG_BSP_LCD_TYPE_HDMI + +#if !CONFIG_BSP_LCD_COLOR_FORMAT_RGB888 +#error The color format must be RGB888 in HDMI display type! +#endif + ESP_LOGI(TAG, "Install MIPI DSI HDMI control panel"); + ESP_RETURN_ON_ERROR(bsp_i2c_init(), TAG, "I2C init failed"); + + /* Main IO */ + esp_lcd_panel_io_i2c_config_t io_config = LT8912B_IO_CFG(CONFIG_BSP_I2C_CLK_SPEED_HZ, LT8912B_IO_I2C_MAIN_ADDRESS); + ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c(i2c_handle, &io_config, &io)); + + /* CEC DSI IO */ + esp_lcd_panel_io_handle_t io_cec_dsi = NULL; + esp_lcd_panel_io_i2c_config_t io_config_cec = LT8912B_IO_CFG(CONFIG_BSP_I2C_CLK_SPEED_HZ, LT8912B_IO_I2C_CEC_ADDRESS); + ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c(i2c_handle, &io_config_cec, &io_cec_dsi)); + + /* AVI IO */ + esp_lcd_panel_io_handle_t io_avi = NULL; + esp_lcd_panel_io_i2c_config_t io_config_avi = LT8912B_IO_CFG(CONFIG_BSP_I2C_CLK_SPEED_HZ, LT8912B_IO_I2C_AVI_ADDRESS); + ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c(i2c_handle, &io_config_avi, &io_avi)); + + esp_lcd_dpi_panel_config_t dpi_configs[] = { + LT8912B_800x600_PANEL_60HZ_DPI_CONFIG_WITH_FBS(CONFIG_BSP_LCD_DPI_BUFFER_NUMS), + LT8912B_1024x768_PANEL_60HZ_DPI_CONFIG_WITH_FBS(CONFIG_BSP_LCD_DPI_BUFFER_NUMS), + LT8912B_1280x720_PANEL_60HZ_DPI_CONFIG_WITH_FBS(CONFIG_BSP_LCD_DPI_BUFFER_NUMS), + LT8912B_1280x800_PANEL_60HZ_DPI_CONFIG_WITH_FBS(CONFIG_BSP_LCD_DPI_BUFFER_NUMS), + LT8912B_1920x1080_PANEL_30HZ_DPI_CONFIG_WITH_FBS(CONFIG_BSP_LCD_DPI_BUFFER_NUMS) + }; + +#if CONFIG_BSP_LCD_USE_DMA2D && (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(6, 0, 0)) + for (int i = 0; i < sizeof(dpi_configs) / sizeof(dpi_configs[0]); i++) { + dpi_configs[i].flags.use_dma2d = true; + } +#endif + + const esp_lcd_panel_lt8912b_video_timing_t video_timings[] = { + ESP_LCD_LT8912B_VIDEO_TIMING_800x600_60Hz(), + ESP_LCD_LT8912B_VIDEO_TIMING_1024x768_60Hz(), + ESP_LCD_LT8912B_VIDEO_TIMING_1280x720_60Hz(), + ESP_LCD_LT8912B_VIDEO_TIMING_1280x800_60Hz(), + ESP_LCD_LT8912B_VIDEO_TIMING_1920x1080_30Hz() + }; + lt8912b_vendor_config_t vendor_config = { + .mipi_config = { + .dsi_bus = mipi_dsi_bus, + .lane_num = BSP_LCD_MIPI_DSI_LANE_NUM, + }, + }; + + /* DPI config */ + switch (config->hdmi_resolution) { + case BSP_HDMI_RES_800x600: + ESP_LOGI(TAG, "HDMI configuration for 800x600@60HZ"); + vendor_config.mipi_config.dpi_config = &dpi_configs[0]; + memcpy(&vendor_config.video_timing, &video_timings[0], sizeof(esp_lcd_panel_lt8912b_video_timing_t)); + break; + case BSP_HDMI_RES_1024x768: + ESP_LOGI(TAG, "HDMI configuration for 1024x768@60HZ"); + vendor_config.mipi_config.dpi_config = &dpi_configs[1]; + memcpy(&vendor_config.video_timing, &video_timings[1], sizeof(esp_lcd_panel_lt8912b_video_timing_t)); + break; + case BSP_HDMI_RES_1280x720: + ESP_LOGI(TAG, "HDMI configuration for 1280x720@60HZ"); + vendor_config.mipi_config.dpi_config = &dpi_configs[2]; + memcpy(&vendor_config.video_timing, &video_timings[2], sizeof(esp_lcd_panel_lt8912b_video_timing_t)); + break; + case BSP_HDMI_RES_1280x800: + ESP_LOGI(TAG, "HDMI configuration for 1280x800@60HZ"); + vendor_config.mipi_config.dpi_config = &dpi_configs[3]; + memcpy(&vendor_config.video_timing, &video_timings[3], sizeof(esp_lcd_panel_lt8912b_video_timing_t)); + break; + case BSP_HDMI_RES_1920x1080: + ESP_LOGI(TAG, "HDMI configuration for 1920x1080@30HZ"); + vendor_config.mipi_config.dpi_config = &dpi_configs[4]; + memcpy(&vendor_config.video_timing, &video_timings[4], sizeof(esp_lcd_panel_lt8912b_video_timing_t)); + break; + default: + ESP_LOGE(TAG, "Unsupported display type (%d)", config->hdmi_resolution); + } + + const esp_lcd_panel_dev_config_t panel_config = { + .bits_per_pixel = 24, + .rgb_ele_order = BSP_LCD_COLOR_SPACE, + .reset_gpio_num = BSP_LCD_RST, + .vendor_config = &vendor_config, + }; + const esp_lcd_panel_lt8912b_io_t io_all = { + .main = io, + .cec_dsi = io_cec_dsi, + .avi = io_avi, + }; + ESP_ERROR_CHECK(esp_lcd_new_panel_lt8912b(&io_all, &panel_config, &disp_panel)); + +#if CONFIG_BSP_LCD_USE_DMA2D && (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0)) + ESP_GOTO_ON_ERROR(esp_lcd_dpi_panel_enable_dma2d(disp_panel), err, TAG, "LCD panel enable DMA2D failed"); +#endif + + ESP_GOTO_ON_ERROR(esp_lcd_panel_reset(disp_panel), err, TAG, "LCD panel reset failed"); + ESP_GOTO_ON_ERROR(esp_lcd_panel_init(disp_panel), err, TAG, "LCD panel init failed"); + +#endif //CONFIG_BSP_LCD_TYPE_ + + /* Return all handles */ + ret_handles->io = io; + disp_handles.io = io; +#if CONFIG_BSP_LCD_TYPE_HDMI + ret_handles->io_cec = io_cec_dsi; + disp_handles.io_cec = io_cec_dsi; + ret_handles->io_avi = io_avi; + disp_handles.io_avi = io_avi; +#endif + ret_handles->mipi_dsi_bus = mipi_dsi_bus; + disp_handles.mipi_dsi_bus = mipi_dsi_bus; + ret_handles->panel = disp_panel; + disp_handles.panel = disp_panel; + ret_handles->control = NULL; + disp_handles.control = NULL; + + ESP_LOGI(TAG, "Display initialized"); + + return ret; + +err: + bsp_display_delete(); + return ret; +} + +void bsp_display_delete(void) +{ + if (disp_handles.panel) { + esp_lcd_panel_del(disp_handles.panel); + disp_handles.panel = NULL; + } + if (disp_handles.io) { + esp_lcd_panel_io_del(disp_handles.io); + disp_handles.io = NULL; + } +#if CONFIG_BSP_LCD_TYPE_HDMI + if (disp_handles.io_cec) { + esp_lcd_panel_io_del(disp_handles.io_cec); + disp_handles.io_cec = NULL; + } + if (disp_handles.io_avi) { + esp_lcd_panel_io_del(disp_handles.io_avi); + disp_handles.io_avi = NULL; + } +#endif + if (disp_handles.mipi_dsi_bus) { + esp_lcd_del_dsi_bus(disp_handles.mipi_dsi_bus); + disp_handles.mipi_dsi_bus = NULL; + } + + if (disp_phy_pwr_chan) { + esp_ldo_release_channel(disp_phy_pwr_chan); + disp_phy_pwr_chan = NULL; + } + + bsp_display_brightness_deinit(); +} + +#if !CONFIG_BSP_LCD_TYPE_HDMI +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 */ + const esp_lcd_touch_config_t tp_cfg = { + .x_max = BSP_LCD_H_RES, + .y_max = BSP_LCD_V_RES, + .rst_gpio_num = BSP_LCD_TOUCH_RST, // Shared with LCD reset + .int_gpio_num = BSP_LCD_TOUCH_INT, + .levels = { + .reset = 0, + .interrupt = 0, + }, + .flags = { + .swap_xy = 0, +#if CONFIG_BSP_LCD_TYPE_1024_600 + .mirror_x = 1, + .mirror_y = 1, +#else + .mirror_x = 0, + .mirror_y = 0, +#endif + }, + }; + esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG(); + tp_io_config.scl_speed_hz = CONFIG_BSP_I2C_CLK_SPEED_HZ; + ESP_RETURN_ON_ERROR(esp_lcd_new_panel_io_i2c(i2c_handle, &tp_io_config, &tp_io_handle), TAG, ""); + return esp_lcd_touch_new_i2c_gt911(tp_io_handle, &tp_cfg, ret_touch); +} + +void bsp_touch_delete(void) +{ + if (tp) { + esp_lcd_touch_del(tp); + } + if (tp_io_handle) { + esp_lcd_panel_io_del(tp_io_handle); + tp_io_handle = NULL; + } +} +#endif //!CONFIG_BSP_LCD_TYPE_HDMI + +#if (BSP_CONFIG_NO_GRAPHIC_LIB == 0) +static lv_display_t *bsp_display_lcd_init(const bsp_display_cfg_t *cfg) +{ + assert(cfg != NULL); + BSP_ERROR_CHECK_RETURN_NULL(bsp_display_new_with_handles(&cfg->hw_cfg, &disp_handles)); + + uint32_t display_hres = 0; + uint32_t display_vres = 0; +#if CONFIG_BSP_LCD_TYPE_HDMI + switch (cfg->hw_cfg.hdmi_resolution) { + case BSP_HDMI_RES_800x600: + display_hres = 800; + display_vres = 600; + break; + case BSP_HDMI_RES_1024x768: + display_hres = 1024; + display_vres = 768; + break; + case BSP_HDMI_RES_1280x720: + display_hres = 1280; + display_vres = 720; + break; + case BSP_HDMI_RES_1280x800: + display_hres = 1280; + display_vres = 800; + break; + case BSP_HDMI_RES_1920x1080: + display_hres = 1920; + display_vres = 1080; + break; + default: + ESP_LOGE(TAG, "Unsupported HDMI resolution"); + } +#else + display_hres = BSP_LCD_H_RES; + display_vres = BSP_LCD_V_RES; +#endif + + ESP_LOGI(TAG, "Display resolution %ldx%ld", display_hres, display_vres); + + /* Add LCD screen */ + ESP_LOGD(TAG, "Add LCD screen"); + const lvgl_port_display_cfg_t disp_cfg = { + .io_handle = disp_handles.io, + .panel_handle = disp_handles.panel, + .control_handle = disp_handles.control, + .buffer_size = cfg->buffer_size, + .double_buffer = cfg->double_buffer, + .hres = display_hres, + .vres = display_vres, + .monochrome = false, + /* Rotation values must be same as used in esp_lcd for initial settings of the screen */ + .rotation = { + .swap_xy = false, + .mirror_x = true, + .mirror_y = true, + }, +#if LVGL_VERSION_MAJOR >= 9 +#if CONFIG_BSP_LCD_COLOR_FORMAT_RGB888 + .color_format = LV_COLOR_FORMAT_RGB888, +#else + .color_format = LV_COLOR_FORMAT_RGB565, +#endif +#endif + .flags = { + .buff_dma = cfg->flags.buff_dma, + .buff_spiram = cfg->flags.buff_spiram, +#if LVGL_VERSION_MAJOR >= 9 + .swap_bytes = (BSP_LCD_BIGENDIAN ? true : false), +#endif +#if CONFIG_BSP_DISPLAY_LVGL_AVOID_TEAR + .sw_rotate = false, /* Avoid tearing is not supported for SW rotation */ +#else + .sw_rotate = cfg->flags.sw_rotate, /* Only SW rotation is supported for 90° and 270° */ +#endif +#if CONFIG_BSP_DISPLAY_LVGL_FULL_REFRESH + .full_refresh = true, +#elif CONFIG_BSP_DISPLAY_LVGL_DIRECT_MODE + .direct_mode = true, +#endif + } + }; + + const lvgl_port_display_dsi_cfg_t dpi_cfg = { + .flags = { +#if CONFIG_BSP_DISPLAY_LVGL_AVOID_TEAR + .avoid_tearing = true, +#else + .avoid_tearing = false, +#endif + } + }; + + return lvgl_port_add_disp_dsi(&disp_cfg, &dpi_cfg); +} + +#if !CONFIG_BSP_LCD_TYPE_HDMI +static lv_indev_t *bsp_display_indev_init(lv_display_t *disp) +{ + BSP_ERROR_CHECK_RETURN_NULL(bsp_touch_new(NULL, &tp)); + assert(tp); + + /* Add touch input (for selected screen) */ + const lvgl_port_touch_cfg_t touch_cfg = { + .disp = disp, + .handle = tp, + }; + + return lvgl_port_add_touch(&touch_cfg); +} +#endif //!CONFIG_BSP_LCD_TYPE_HDMI + +lv_display_t *bsp_display_start(void) +{ + bsp_display_cfg_t cfg = { + .lvgl_port_cfg = ESP_LVGL_PORT_INIT_CONFIG(), + .buffer_size = BSP_LCD_DRAW_BUFF_SIZE, + .double_buffer = BSP_LCD_DRAW_BUFF_DOUBLE, + .hw_cfg = { +#if CONFIG_BSP_LCD_TYPE_HDMI +#if CONFIG_BSP_LCD_HDMI_800x600_60HZ + .hdmi_resolution = BSP_HDMI_RES_800x600, +#elif CONFIG_BSP_LCD_HDMI_1280x720_60HZ + .hdmi_resolution = BSP_HDMI_RES_1280x720, +#elif CONFIG_BSP_LCD_HDMI_1280x800_60HZ + .hdmi_resolution = BSP_HDMI_RES_1280x800, +#elif CONFIG_BSP_LCD_HDMI_1920x1080_30HZ + .hdmi_resolution = BSP_HDMI_RES_1920x1080, +#endif +#else + .hdmi_resolution = BSP_HDMI_RES_NONE, +#endif + .dsi_bus = { + .phy_clk_src = MIPI_DSI_PHY_PLLREF_CLK_SRC_DEFAULT, // Use default clock source (XTAL for ESP-IDF 6.1) + .lane_bit_rate_mbps = BSP_LCD_MIPI_DSI_LANE_BITRATE_MBPS, + } + }, + .flags = { +#if CONFIG_BSP_LCD_COLOR_FORMAT_RGB888 + .buff_dma = false, +#else + .buff_dma = true, +#endif + .buff_spiram = false, + .sw_rotate = true, + } + }; + return bsp_display_start_with_config(&cfg); +} + +lv_display_t *bsp_display_start_with_config(const bsp_display_cfg_t *cfg) +{ + lv_display_t *disp; + + assert(cfg != NULL); + BSP_ERROR_CHECK_RETURN_NULL(lvgl_port_init(&cfg->lvgl_port_cfg)); + + BSP_ERROR_CHECK_RETURN_NULL(bsp_display_brightness_init()); + + BSP_NULL_CHECK(disp = bsp_display_lcd_init(cfg), NULL); +#if !CONFIG_BSP_LCD_TYPE_HDMI + BSP_NULL_CHECK(disp_indev = bsp_display_indev_init(disp), NULL); +#endif + return disp; +} + +void bsp_display_stop(lv_display_t *display) +{ + /* Deinit LVGL */ +#if !CONFIG_BSP_LCD_TYPE_HDMI + lvgl_port_remove_touch(disp_indev); +#endif + lvgl_port_remove_disp(display); + lvgl_port_deinit(); + +#if !CONFIG_BSP_LCD_TYPE_HDMI + /* Deinit touch */ + bsp_touch_delete(); +#endif + + /* Deinit display */ + bsp_display_delete(); + + /* Deinit I2C if initialized */ + bsp_i2c_deinit(); +} + +lv_indev_t *bsp_display_get_input_dev(void) +{ + return disp_indev; +} + +void bsp_display_rotate(lv_display_t *disp, lv_disp_rotation_t rotation) +{ + lv_disp_set_rotation(disp, rotation); +} + +bool bsp_display_lock(uint32_t timeout_ms) +{ + return lvgl_port_lock(timeout_ms); +} + +void bsp_display_unlock(void) +{ + lvgl_port_unlock(); +} + +#endif // (BSP_CONFIG_NO_GRAPHIC_LIB == 0) + +static void usb_lib_task(void *arg) +{ + while (1) { + // Start handling system events + uint32_t event_flags; + usb_host_lib_handle_events(portMAX_DELAY, &event_flags); + if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) { + ESP_ERROR_CHECK(usb_host_device_free_all()); + } + if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) { + ESP_LOGI(TAG, "USB: All devices freed"); + // Continue handling USB events to allow device reconnection + // The only way this task can be stopped is by calling bsp_usb_host_stop() + } + } +} + +esp_err_t bsp_usb_host_start(bsp_usb_host_power_mode_t mode, bool limit_500mA) +{ + //Install USB Host driver. Should only be called once in entire application + ESP_LOGI(TAG, "Installing USB Host"); + const usb_host_config_t host_config = { + .skip_phy_setup = false, + .intr_flags = ESP_INTR_FLAG_LEVEL1, + }; + BSP_ERROR_CHECK_RETURN_ERR(usb_host_install(&host_config)); + + // Create a task that will handle USB library events + if (xTaskCreate(usb_lib_task, "usb_lib", 4096, NULL, 10, &usb_host_task) != pdTRUE) { + ESP_LOGE(TAG, "Creating USB host lib task failed"); + abort(); + } + + return ESP_OK; +} + +esp_err_t bsp_usb_host_stop(void) +{ + usb_host_uninstall(); + if (usb_host_task) { + vTaskSuspend(usb_host_task); + vTaskDelete(usb_host_task); + } + return ESP_OK; +} + +esp_err_t bsp_camera_start(const bsp_camera_cfg_t *cfg) +{ + /* Initilize I2C */ + BSP_ERROR_CHECK_RETURN_ERR(bsp_i2c_init()); + + const esp_video_init_csi_config_t base_csi_config = { + .sccb_config = { + .init_sccb = false, + .i2c_handle = i2c_handle, + .freq = 400000, + }, + .reset_pin = BSP_CAMERA_RST, + .pwdn_pin = -1, + }; + + esp_video_init_config_t cam_config = { + .csi = &base_csi_config, + }; + + return esp_video_init(&cam_config); +} diff --git a/components/espressif__esp32_p4_function_ev_board_noglib/idf_component.yml b/components/espressif__esp32_p4_function_ev_board_noglib/idf_component.yml new file mode 100644 index 0000000..4a346e3 --- /dev/null +++ b/components/espressif__esp32_p4_function_ev_board_noglib/idf_component.yml @@ -0,0 +1,30 @@ +dependencies: + esp_codec_dev: + public: true + version: ~1.5 + esp_lcd_ek79007: '>=2.0.1,<3.0.0' + esp_lcd_ili9881c: + version: '>=1.0.3,<2.0.0' + esp_lcd_touch_gt911: + version: ^1 + esp_video: + public: true + version: ~1.4 + espressif/esp_lcd_lt8912b: + version: '>=0.1.3,<1.0.0' + idf: '>=5.4' + usb: + public: true + rules: + - if: idf_version >=6.0 + version: ^1.0.0 +description: Board Support Package (BSP) for ESP32-P4 Function EV Board (preview) + with no graphical library +repository: git://github.com/espressif/esp-bsp.git +tags: +- bsp +- noglib +targets: +- esp32p4 +url: https://github.com/espressif/esp-bsp/tree/master/bsp/esp32_p4_function_ev_board +version: 5.2.0 diff --git a/components/espressif__esp32_p4_function_ev_board_noglib/include/bsp/config.h b/components/espressif__esp32_p4_function_ev_board_noglib/include/bsp/config.h new file mode 100644 index 0000000..d90eee8 --- /dev/null +++ b/components/espressif__esp32_p4_function_ev_board_noglib/include/bsp/config.h @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +/************************************************************************************************** + * BSP configuration + **************************************************************************************************/ +// By default, this BSP is shipped with LVGL graphical library. Enabling this option will exclude it. +// If you want to use BSP without LVGL, select BSP version with 'noglib' suffix. +#if !defined(BSP_CONFIG_NO_GRAPHIC_LIB) // Check if the symbol is not coming from compiler definitions (-D...) +#define BSP_CONFIG_NO_GRAPHIC_LIB (1) +#endif diff --git a/components/espressif__esp32_p4_function_ev_board_noglib/include/bsp/display.h b/components/espressif__esp32_p4_function_ev_board_noglib/include/bsp/display.h new file mode 100644 index 0000000..9897b00 --- /dev/null +++ b/components/espressif__esp32_p4_function_ev_board_noglib/include/bsp/display.h @@ -0,0 +1,213 @@ +/* + * SPDX-FileCopyrightText: 2024-2025 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" +#include "esp_lcd_mipi_dsi.h" +#include "sdkconfig.h" + +/** @defgroup g04_display Display and Touch + * @brief Display BSP API + * @{ + */ + +/* LCD color formats */ +#define ESP_LCD_COLOR_FORMAT_RGB565 (1) +#define ESP_LCD_COLOR_FORMAT_RGB888 (2) + +/* LCD display color format */ +#if CONFIG_BSP_LCD_COLOR_FORMAT_RGB888 +#define BSP_LCD_COLOR_FORMAT (ESP_LCD_COLOR_FORMAT_RGB888) +#else +#define BSP_LCD_COLOR_FORMAT (ESP_LCD_COLOR_FORMAT_RGB565) +#endif +/* LCD display color bytes endianess */ +#define BSP_LCD_BIGENDIAN (0) +/* LCD display color bits */ +#define BSP_LCD_BITS_PER_PIXEL (16) +/* LCD display color space */ +#define BSP_LCD_COLOR_SPACE (LCD_RGB_ELEMENT_ORDER_RGB) + +#if CONFIG_BSP_LCD_TYPE_1024_600 +/* LCD display definition 1024x600 */ +#define BSP_LCD_H_RES (1024) +#define BSP_LCD_V_RES (600) +#else +/* LCD display definition 1280x800 */ +#define BSP_LCD_H_RES (800) +#define BSP_LCD_V_RES (1280) +#endif + +#define BSP_LCD_MIPI_DSI_LANE_NUM (2) // 2 data lanes +#define BSP_LCD_MIPI_DSI_LANE_BITRATE_MBPS (1000) // 1Gbps + +#define BSP_MIPI_DSI_PHY_PWR_LDO_CHAN (3) // LDO_VO3 is connected to VDD_MIPI_DPHY +#define BSP_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV (2500) + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief BSP HDMI resolution types + * + */ +typedef enum { + BSP_HDMI_RES_NONE = 0, + BSP_HDMI_RES_800x600, /*!< 800x600@60HZ */ + BSP_HDMI_RES_1024x768, /*!< 1024x768@60HZ */ + BSP_HDMI_RES_1280x720, /*!< 1280x720@60HZ */ + BSP_HDMI_RES_1280x800, /*!< 1280x800@60HZ */ + BSP_HDMI_RES_1920x1080 /*!< 1920x1080@30HZ */ +} bsp_hdmi_resolution_t; + +/** + * @brief BSP display configuration structure + * + */ +typedef struct { + bsp_hdmi_resolution_t hdmi_resolution; /*!< HDMI resolution selection */ + struct { + mipi_dsi_phy_clock_source_t phy_clk_src; /*!< DSI bus config - clock source */ + uint32_t lane_bit_rate_mbps; /*!< DSI bus config - lane bit rate */ + } dsi_bus; +} bsp_display_config_t; + +/** + * @brief BSP display return handles + * + */ +typedef struct { + esp_lcd_dsi_bus_handle_t mipi_dsi_bus; /*!< MIPI DSI bus handle */ + esp_lcd_panel_io_handle_t io; /*!< ESP LCD IO handle */ +#if CONFIG_BSP_LCD_TYPE_HDMI + esp_lcd_panel_io_handle_t io_cec; /*!< ESP LCD IO (HDMI CEC) handle */ + esp_lcd_panel_io_handle_t io_avi; /*!< ESP LCD IO (HDMI AVI) handle */ +#endif + esp_lcd_panel_handle_t panel; /*!< ESP LCD panel (color) handle */ + esp_lcd_panel_handle_t control; /*!< ESP LCD panel (control) handle */ +} bsp_lcd_handles_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); + * esp_lcd_del_dsi_bus(mipi_dsi_bus); + * \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 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 API: + * + * \code{.c} + * bsp_display_delete(); + * \endcode + * + * @param[in] config display configuration + * @param[out] ret_handles all esp_lcd handles in one structure + * @return + * - ESP_OK On success + * - Else esp_lcd failure + */ +esp_err_t bsp_display_new_with_handles(const bsp_display_config_t *config, bsp_lcd_handles_t *ret_handles); + +/** + * @brief Delete display panel + */ +void bsp_display_delete(void); + +/** + * @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 Deinitialize display's brightness + */ +esp_err_t bsp_display_brightness_deinit(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 + +/** @} */ // end of display diff --git a/components/espressif__esp32_p4_function_ev_board_noglib/include/bsp/esp-bsp.h b/components/espressif__esp32_p4_function_ev_board_noglib/include/bsp/esp-bsp.h new file mode 100644 index 0000000..9ee3521 --- /dev/null +++ b/components/espressif__esp32_p4_function_ev_board_noglib/include/bsp/esp-bsp.h @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once +#include "bsp/esp32_p4_function_ev_board.h" diff --git a/components/espressif__esp32_p4_function_ev_board_noglib/include/bsp/esp32_p4_function_ev_board.h b/components/espressif__esp32_p4_function_ev_board_noglib/include/bsp/esp32_p4_function_ev_board.h new file mode 100644 index 0000000..c4eeabb --- /dev/null +++ b/components/espressif__esp32_p4_function_ev_board_noglib/include/bsp/esp32_p4_function_ev_board.h @@ -0,0 +1,616 @@ +/* + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief ESP BSP: ESP32-P4 Function EV Board + */ + +#pragma once + +#include "sdkconfig.h" +#include "driver/gpio.h" +#include "driver/i2c_master.h" +#include "driver/sdmmc_host.h" +#include "driver/sdspi_host.h" +#include "esp_vfs_fat.h" +#include "driver/i2s_std.h" +#include "bsp/config.h" +#include "bsp/display.h" +#include "esp_codec_dev.h" +#include "sdkconfig.h" + +#if (BSP_CONFIG_NO_GRAPHIC_LIB == 0) +#include "lvgl.h" +#include "esp_lvgl_port.h" +#endif // BSP_CONFIG_NO_GRAPHIC_LIB == 0 + +/************************************************************************************************** + * BSP Board Name + **************************************************************************************************/ + +/** @defgroup boardname Board Name + * @brief BSP Board Name + * @{ + */ +#define BSP_BOARD_ESP32_P4_FUNCTION_EV_BOARD +/** @} */ // end of boardname + +/************************************************************************************************** + * BSP Capabilities + **************************************************************************************************/ + +/** @defgroup capabilities Capabilities + * @brief 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 1 +#define BSP_CAPS_IMU 0 +#define BSP_CAPS_CAMERA 1 +/** @} */ // end of capabilities + +/************************************************************************************************** + * ESP-BOX pinout + **************************************************************************************************/ + +/** @defgroup g01_i2c I2C + * @brief I2C BSP API + * @{ + */ +#define BSP_I2C_SCL (GPIO_NUM_8) +#define BSP_I2C_SDA (GPIO_NUM_7) +/** @} */ // end of i2c + +/** @defgroup g03_audio Audio + * @brief Audio BSP API + * @{ + */ +#define BSP_I2S_SCLK (GPIO_NUM_12) +#define BSP_I2S_MCLK (GPIO_NUM_13) +#define BSP_I2S_LCLK (GPIO_NUM_10) +#define BSP_I2S_DOUT (GPIO_NUM_9) +#define BSP_I2S_DSIN (GPIO_NUM_11) +#define BSP_POWER_AMP_IO (GPIO_NUM_53) +/** @} */ // end of audio + +/** @defgroup g04_display Display and Touch + * @brief Display BSP API + * @{ + */ +#if CONFIG_BSP_LCD_TYPE_1024_600 +#define BSP_LCD_BACKLIGHT (GPIO_NUM_26) +#define BSP_LCD_RST (GPIO_NUM_27) +#define BSP_LCD_TOUCH_RST (GPIO_NUM_NC) +#define BSP_LCD_TOUCH_INT (GPIO_NUM_NC) +#else +#define BSP_LCD_BACKLIGHT (GPIO_NUM_23) +#define BSP_LCD_RST (GPIO_NUM_NC) +#define BSP_LCD_TOUCH_RST (GPIO_NUM_NC) +#define BSP_LCD_TOUCH_INT (GPIO_NUM_NC) +#endif +/** @} */ // end of display + +/** @defgroup g12_camera Camera + * @brief Camera BSP API + * @{ + */ +#define BSP_CAMERA_GPIO_XCLK (GPIO_NUM_NC) +#define BSP_CAMERA_RST (GPIO_NUM_NC) +/** @} */ // end of camera + +/** @defgroup g02_storage SD Card and SPIFFS + * @brief SPIFFS and SD card BSP API + * @{ + */ +/* uSD card MMC */ +#define BSP_SD_D0 (GPIO_NUM_39) +#define BSP_SD_D1 (GPIO_NUM_40) +#define BSP_SD_D2 (GPIO_NUM_41) +#define BSP_SD_D3 (GPIO_NUM_42) +#define BSP_SD_CMD (GPIO_NUM_44) +#define BSP_SD_CLK (GPIO_NUM_43) + +/* uSD card SPI */ +#define BSP_SD_SPI_MISO (GPIO_NUM_39) +#define BSP_SD_SPI_CS (GPIO_NUM_42) +#define BSP_SD_SPI_MOSI (GPIO_NUM_44) +#define BSP_SD_SPI_CLK (GPIO_NUM_43) +/** @} */ // end of storage + +/** @defgroup g07_usb USB + * @brief USB BSP API + * @{ + */ +#define BSP_USB_POS (GPIO_NUM_20) +#define BSP_USB_NEG (GPIO_NUM_19) +/** @} */ // end of usb + +#ifdef __cplusplus +extern "C" { +#endif + +/** \addtogroup g01_i2c + * @{ + */ + +/************************************************************************************************** + * + * I2C interface + * + * There are multiple devices connected to I2C peripheral: + * - Codec ES8311 (configuration only) + * - LCD Touch controller + **************************************************************************************************/ +#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); + +/** + * @brief Get I2C driver handle + * + * @return + * - I2C handle + * + */ +i2c_master_bus_handle_t bsp_i2c_get_handle(void); + +/** @} */ // end of i2c + +/** \addtogroup g03_audio + * @{ + */ + +/************************************************************************************************** + * + * I2S audio interface + * + * There are two devices connected to the I2S peripheral: + * - Codec ES8311 for output(playback) and 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 + */ +esp_err_t bsp_audio_init(const i2s_std_config_t *i2s_config); + +/** + * @brief Initialize I2S audio with TX-only channel (saves memory) + * + * This function initializes only the I2S TX channel for audio output. + * Use this for applications that don't need audio input to save DMA memory. + * + * @param[in] i2s_config I2S configuration (NULL for default 22.05kHz mono) + * @return + * - ESP_OK Initialize I2S successfully + * - ESP_ERR_INVALID_ARG Initialize I2S failed because of invalid argument + * - ESP_ERR_NO_MEM Initialize I2S failed because out of memory + * - ESP_ERR_INVALID_STATE This channel has not initialized or already started + */ +esp_err_t bsp_audio_init_tx_only(const i2s_std_config_t *i2s_config); + +/** + * @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 speaker codec device (TX-only to save memory) + * + * This function initializes only the I2S TX channel, not RX. Use this for applications + * that don't need audio input (microphone) to save DMA memory. + * + * @return Pointer to codec device handle or NULL when error occurred + */ +esp_codec_dev_handle_t bsp_audio_codec_speaker_init_tx_only(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); + +/** @} */ // end of audio + +/** \addtogroup g02_storage + * @{ + */ + +/************************************************************************************************** + * + * 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); + +/************************************************************************************************** + * + * uSD card + * + * After mounting the uSD card, it can be accessed with stdio functions ie.: + * \code{.c} + * FILE* f = fopen(BSP_MOUNT_POINT"/hello.txt", "w"); + * fprintf(f, "Hello %s!\n", bsp_sdcard->cid.name); + * fclose(f); + * \endcode + **************************************************************************************************/ +#define BSP_SD_MOUNT_POINT CONFIG_BSP_SD_MOUNT_POINT +#define BSP_SDSPI_HOST (SDSPI_DEFAULT_HOST) + +/** + * @brief BSP SD card configuration structure + */ +typedef struct { + const esp_vfs_fat_sdmmc_mount_config_t *mount; + sdmmc_host_t *host; + union { + const sdmmc_slot_config_t *sdmmc; + const sdspi_device_config_t *sdspi; + } slot; +} bsp_sdcard_cfg_t; + +/** + * @brief Mount microSD card to virtual file system + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount was already called + * - ESP_ERR_NO_MEM if memory cannot be allocated + * - ESP_FAIL if partition cannot be mounted + * - other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers + */ +esp_err_t bsp_sdcard_mount(void); + +/** + * @brief Unmount microSD card from virtual file system + * + * @return + * - ESP_OK on success + * - ESP_ERR_NOT_FOUND if the partition table does not contain FATFS partition with given label + * - ESP_ERR_INVALID_STATE if esp_vfs_fat_spiflash_mount was already called + * - ESP_ERR_NO_MEM if memory can not be allocated + * - ESP_FAIL if partition can not be mounted + * - other error codes from wear levelling library, SPI flash driver, or FATFS drivers + */ +esp_err_t bsp_sdcard_unmount(void); + +/** + * @brief Get SD card handle + * + * @return SD card handle + */ +sdmmc_card_t *bsp_sdcard_get_handle(void); + +/** + * @brief Get SD card MMC host config + * + * @param slot SD card slot + * @param config Structure which will be filled + */ +void bsp_sdcard_get_sdmmc_host(const int slot, sdmmc_host_t *config); + +/** + * @brief Get SD card SPI host config + * + * @param slot SD card slot + * @param config Structure which will be filled + */ +void bsp_sdcard_get_sdspi_host(const int slot, sdmmc_host_t *config); + +/** + * @brief Get SD card MMC slot config + * + * @param slot SD card slot + * @param config Structure which will be filled + */ +void bsp_sdcard_sdmmc_get_slot(const int slot, sdmmc_slot_config_t *config); + +/** + * @brief Get SD card SPI slot config + * + * @param spi_host SPI host ID + * @param config Structure which will be filled + */ +void bsp_sdcard_sdspi_get_slot(const spi_host_device_t spi_host, sdspi_device_config_t *config); + +/** + * @brief Mount microSD card to virtual file system (MMC mode) + * + * @param cfg SD card configuration + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount was already called + * - ESP_ERR_NO_MEM if memory cannot be allocated + * - ESP_FAIL if partition cannot be mounted + * - other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers + */ +esp_err_t bsp_sdcard_sdmmc_mount(bsp_sdcard_cfg_t *cfg); + +/** + * @brief Mount microSD card to virtual file system (SPI mode) + * + * @param cfg SD card configuration + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount was already called + * - ESP_ERR_NO_MEM if memory cannot be allocated + * - ESP_FAIL if partition cannot be mounted + * - other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers + */ +esp_err_t bsp_sdcard_sdspi_mount(bsp_sdcard_cfg_t *cfg); + +/** @} */ // end of storage + +/** \addtogroup g04_display + * @{ + */ + +/************************************************************************************************** + * + * 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_MHZ (80) + +#if (BSP_CONFIG_NO_GRAPHIC_LIB == 0) + +#define BSP_LCD_DRAW_BUFF_SIZE (BSP_LCD_H_RES * 50) // Frame buffer size in pixels +#define BSP_LCD_DRAW_BUFF_DOUBLE (0) + +/** + * @brief BSP display configuration structure + * + */ +typedef struct { + lvgl_port_cfg_t lvgl_port_cfg; /*!< LVGL port configuration */ + uint32_t buffer_size; /*!< Size of the buffer for the screen in pixels */ + bool double_buffer; /*!< True, if should be allocated two buffers */ + bsp_display_config_t hw_cfg; /*!< Display HW configuration */ + struct { + unsigned int buff_dma: 1; /*!< Allocated LVGL buffer will be DMA capable */ + unsigned int buff_spiram: 1; /*!< Allocated LVGL buffer will be in PSRAM */ + unsigned int sw_rotate: 1; /*!< Use software rotation (slower), The feature is unavailable under avoid-tear mode */ + } flags; +} bsp_display_cfg_t; + +/** + * @brief Initialize display + * + * This function initializes MIPI-DSI, display controller and starts LVGL handling task. + * LCD backlight must be enabled separately by calling bsp_display_brightness_set() + * + * @return Pointer to LVGL display or NULL when error occured + */ +lv_display_t *bsp_display_start(void); + +/** + * @brief Initialize display + * + * This function initializes MIPI-DSI, display controller and starts LVGL handling task. + * LCD backlight must be enabled separately by calling bsp_display_brightness_set() + * + * @param cfg display configuration + * + * @return Pointer to LVGL display or NULL when error occured + */ +lv_display_t *bsp_display_start_with_config(const bsp_display_cfg_t *cfg); + +/** + * @brief Deinitialize display + * + * This function deinitializes MIPI-DSI, display controller and stops LVGL. + * + * @param display Pointer to LVGL display + */ +void bsp_display_stop(lv_display_t *display); + +/** + * @brief Get pointer to input device (touch, buttons, ...) + * + * @note The LVGL input device is initialized in bsp_display_start() function. + * + * @return Pointer to LVGL input device or NULL when not initialized + */ +lv_indev_t *bsp_display_get_input_dev(void); + +/** + * @brief Take LVGL mutex + * + * @param timeout_ms Timeout in [ms]. 0 will block indefinitely. + * @return true Mutex was taken + * @return false Mutex was NOT taken + */ +bool bsp_display_lock(uint32_t timeout_ms); + +/** + * @brief Give LVGL mutex + * + */ +void bsp_display_unlock(void); + +/** + * @brief Rotate screen + * + * Display must be already initialized by calling bsp_display_start() + * + * @param[in] disp Pointer to LVGL display + * @param[in] rotation Angle of the display rotation + */ +void bsp_display_rotate(lv_display_t *disp, lv_disp_rotation_t rotation); +#endif // BSP_CONFIG_NO_GRAPHIC_LIB == 0 + +/** @} */ // end of display + +/** \addtogroup g07_usb + * @{ + */ + +/************************************************************************************************** + * + * USB + * + **************************************************************************************************/ + +/** + * @brief Power modes of USB Host connector + */ +typedef enum bsp_usb_host_power_mode_t { + BSP_USB_HOST_POWER_MODE_USB_DEV, //!< Power from USB DEV port +} bsp_usb_host_power_mode_t; + +/** + * @brief Start USB host + * + * This is a one-stop-shop function that will configure the board for USB Host mode + * and start USB Host library + * + * @param[in] mode USB Host connector power mode (Not used on this board) + * @param[in] limit_500mA Limit output current to 500mA (Not used on this board) + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_NO_MEM Memory cannot be allocated + */ +esp_err_t bsp_usb_host_start(bsp_usb_host_power_mode_t mode, bool limit_500mA); + +/** + * @brief Stop USB host + * + * USB Host lib will be uninstalled and power from connector removed. + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t bsp_usb_host_stop(void); + +/** @} */ // end of usb + +/** @addtogroup g12_camera + * @{ + */ + +/************************************************************************************************** + * + * Camera interface + * Supported camera sensors: OV5647, SC2336 + * More information in display_camera_csi example + * + **************************************************************************************************/ + +#define BSP_CAMERA_DEVICE (ESP_VIDEO_MIPI_CSI_DEVICE_NAME) +#define BSP_CAMERA_ROTATION (0) + +/** + * @brief BSP camera configuration structure (for future use) + * + */ +typedef struct { + uint8_t dummy; +} bsp_camera_cfg_t; + +/** + * @brief Initialize camera + * + * Camera sensor initialization. + */ +esp_err_t bsp_camera_start(const bsp_camera_cfg_t *cfg); + +/** @} */ // end of camera + +#ifdef __cplusplus +} +#endif diff --git a/components/espressif__esp32_p4_function_ev_board_noglib/include/bsp/touch.h b/components/espressif__esp32_p4_function_ev_board_noglib/include/bsp/touch.h new file mode 100644 index 0000000..26f9b11 --- /dev/null +++ b/components/espressif__esp32_p4_function_ev_board_noglib/include/bsp/touch.h @@ -0,0 +1,61 @@ +/* + * SPDX-FileCopyrightText: 2024-2025 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 + +/** \addtogroup g04_display + * @{ + */ + +/** + * @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 API: + * + * \code{.c} + * bsp_touch_delete(); + * \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); + +/** + * @brief Deinitialize touch + */ +void bsp_touch_delete(void); + +/** @} */ // end of display +#ifdef __cplusplus +} +#endif diff --git a/components/espressif__esp32_p4_function_ev_board_noglib/priv_include/bsp_err_check.h b/components/espressif__esp32_p4_function_ev_board_noglib/priv_include/bsp_err_check.h new file mode 100644 index 0000000..cf2f36e --- /dev/null +++ b/components/espressif__esp32_p4_function_ev_board_noglib/priv_include/bsp_err_check.h @@ -0,0 +1,58 @@ +/* + * SPDX-FileCopyrightText: 2024 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/components/georgik__sdl_audio/CMakeLists.txt b/components/georgik__sdl_audio/CMakeLists.txt new file mode 100644 index 0000000..0b5c403 --- /dev/null +++ b/components/georgik__sdl_audio/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS "src/SDL_audio_esp32.c" + INCLUDE_DIRS "include" + REQUIRES esp_codec_dev espressif__esp32_p4_function_ev_board_noglib esp_lcd +) diff --git a/components/georgik__sdl_audio/include/SDL_audio_esp32.h b/components/georgik__sdl_audio/include/SDL_audio_esp32.h new file mode 100644 index 0000000..6826da5 --- /dev/null +++ b/components/georgik__sdl_audio/include/SDL_audio_esp32.h @@ -0,0 +1,53 @@ +/* + * SDL Audio ESP32 Backend + * ESP-IDF wrapper for SDL3 audio on ESP32-P4 + * Based on ESP32-Quake audio implementation + */ + +#pragma once + +#include +#include +#include "esp_err.h" + +// Forward declaration for codec device handle (opaque pointer when BSP unavailable) +#if defined(CONFIG_SDL_BSP_ESP32_P4_FUNCTION_EV) && CONFIG_SDL_BSP_ESP32_P4_FUNCTION_EV +#include "esp_codec_dev.h" +#else +typedef void *esp_codec_dev_handle_t; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// Audio configuration +#define SDL_AUDIO_SAMPLE_RATE 44100 +#define SDL_AUDIO_CHANNELS 2 // Stereo (mono converted to stereo) +#define SDL_AUDIO_BITS_PER_SAMPLE 16 +// Reduced buffer size to save internal DRAM +// Original: 16KB, Reduced to 4KB for minimum viable audio (22.05kHz mono) +#define SDL_AUDIO_BUFFER_SIZE (4*1024) // 4KB DMA buffer + +// Audio device handle (similar to Quake's spk_codec_dev) +extern esp_codec_dev_handle_t sdl_esp_audio_codec_dev; + +// Audio initialization (following Quake's SNDDMA_Init pattern) +bool SDL_ESP_Audio_Init(void); +void SDL_ESP_Audio_Shutdown(void); + +// Audio playback (following Quake's audio_task pattern) +void SDL_ESP_Audio_Write(int16_t *buffer, size_t len); + +// Volume control +void SDL_ESP_Audio_SetVolume(int volume); // 0-100 + +// Check if audio is initialized +bool SDL_ESP_Audio_IsInitialized(void); + +// SDL audio callback registration (called from SDL_OpenAudioDevice) +void SDL_ESP_RegisterAudioCallback(void (*callback)(void *, uint8_t *, int), void *userdata); + +#ifdef __cplusplus +} +#endif diff --git a/components/georgik__sdl_audio/src/SDL_audio_esp32.c b/components/georgik__sdl_audio/src/SDL_audio_esp32.c new file mode 100644 index 0000000..5e2834d --- /dev/null +++ b/components/georgik__sdl_audio/src/SDL_audio_esp32.c @@ -0,0 +1,268 @@ +/* + * SDL Audio ESP32 Backend Implementation + * Based on ESP32-Quake audio implementation + * Reference: /Users/georgik/projects/esp32-quake-hdmi/main/audio.c + */ + +#include "SDL_audio_esp32.h" +#include "esp_log.h" +#include "esp_heap_caps.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include +#include + +// Conditional BSP includes (only available on ESP32-P4 Function EV Board) +#if defined(CONFIG_SDL_BSP_ESP32_P4_FUNCTION_EV) && CONFIG_SDL_BSP_ESP32_P4_FUNCTION_EV +#include "esp_codec_dev.h" +#include "bsp/esp-bsp.h" +#endif + +static const char *TAG = "SDL_audio_esp32"; + +// Audio state (following Quake pattern) +esp_codec_dev_handle_t sdl_esp_audio_codec_dev = NULL; +static bool audio_initialized = false; +static int current_volume = 50; + +// DMA buffer (following Quake's dma_buffer pattern) +#define AUDIO_CHUNK_SIZE (SDL_AUDIO_BUFFER_SIZE / 8) // 512 bytes with 4KB buffer +// Allocate from heap to save DRAM and allow PSRAM usage +static int16_t *audio_mix_buf = NULL; +static int16_t *audio_downsample_buf = NULL; // Downsampled buffer for 22.05kHz output + +// Audio callback from OpenTyrian (will be set by SDL_OpenAudioDevice) +static void (*sdl_audio_callback)(void *userdata, uint8_t *stream, int len) = NULL; +static void *sdl_audio_userdata = NULL; + +// Audio task (following Quake's audio_task pattern) +static void audio_task(void *param) +{ + ESP_LOGI(TAG, "Audio task running (44.1kHz -> 22.05kHz mono)"); + +#if defined(CONFIG_SDL_BSP_ESP32_P4_FUNCTION_EV) && CONFIG_SDL_BSP_ESP32_P4_FUNCTION_EV + if (!sdl_esp_audio_codec_dev) { + ESP_LOGE(TAG, "Codec device not initialized"); + vTaskDelete(NULL); + return; + } +#endif + + // Test tone generator + static uint32_t sample_count = 0; + const uint32_t tone_frequency = 440; // A4 note + const uint32_t sample_rate = 44100; + static int test_tone_counter = 0; + + while (1) { + // Call OpenTyrian's audio callback to get mixed audio + if (sdl_audio_callback && audio_initialized) { + // Call the audio callback - gives us 44.1kHz mono int16 data + sdl_audio_callback(sdl_audio_userdata, (uint8_t *)audio_mix_buf, AUDIO_CHUNK_SIZE); + + // Downsample from 44.1kHz to 22.05kHz (simple 2x decimation: drop every other sample) + size_t samples_44k = AUDIO_CHUNK_SIZE / sizeof(int16_t); + size_t samples_22k = samples_44k / 2; + for (size_t i = 0; i < samples_22k; i++) { + audio_downsample_buf[i] = audio_mix_buf[i * 2]; // Keep every other sample + } + +#if defined(CONFIG_SDL_BSP_ESP32_P4_FUNCTION_EV) && CONFIG_SDL_BSP_ESP32_P4_FUNCTION_EV + // Write downsampled audio to codec (22.05kHz mono) + size_t bytes_22k = samples_22k * sizeof(int16_t); + esp_err_t ret = esp_codec_dev_write( + sdl_esp_audio_codec_dev, + (uint8_t *)audio_downsample_buf, + bytes_22k + ); + + if (ret != ESP_OK) { + ESP_LOGW(TAG, "Audio write failed: %s", esp_err_to_name(ret)); + } +#endif + } else { + // No audio callback yet - write silence instead of test tone + // Don't generate any sound until the callback is registered +#if defined(CONFIG_SDL_BSP_ESP32_P4_FUNCTION_EV) && CONFIG_SDL_BSP_ESP32_P4_FUNCTION_EV + // Write silence (zeros) + size_t samples_22k = AUDIO_CHUNK_SIZE / sizeof(int16_t) / 2; + memset(audio_downsample_buf, 0, samples_22k * sizeof(int16_t)); + size_t bytes_22k = samples_22k * sizeof(int16_t); + esp_codec_dev_write(sdl_esp_audio_codec_dev, (uint8_t *)audio_downsample_buf, bytes_22k); +#endif + } + + // No delay - keep audio pipeline running continuously + // The ESP-BSP example writes continuously without delay + // vTaskDelay(pdMS_TO_TICKS(1)); + } +} + +bool SDL_ESP_Audio_Init(void) +{ + if (audio_initialized) { + ESP_LOGW(TAG, "Audio already initialized"); + return true; + } + +#if defined(CONFIG_SDL_BSP_ESP32_P4_FUNCTION_EV) && CONFIG_SDL_BSP_ESP32_P4_FUNCTION_EV + ESP_LOGI(TAG, "Initializing ESP32 audio (TX-only to save memory)"); + + // Allocate audio mixing buffer from heap (prefer PSRAM) + size_t mix_buf_samples = AUDIO_CHUNK_SIZE / sizeof(int16_t); + audio_mix_buf = heap_caps_malloc(mix_buf_samples * sizeof(int16_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if (!audio_mix_buf) { + ESP_LOGW(TAG, "PSRAM malloc failed for audio mix buffer, trying regular malloc"); + audio_mix_buf = malloc(mix_buf_samples * sizeof(int16_t)); + } + if (!audio_mix_buf) { + ESP_LOGE(TAG, "Failed to allocate audio mix buffer"); + return false; + } + ESP_LOGI(TAG, "Audio mix buffer allocated: %zu bytes", mix_buf_samples * sizeof(int16_t)); + + // Allocate downsampled buffer (half size for 22.05kHz output) + size_t downsample_samples = mix_buf_samples / 2; + audio_downsample_buf = heap_caps_malloc(downsample_samples * sizeof(int16_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if (!audio_downsample_buf) { + ESP_LOGW(TAG, "PSRAM malloc failed for downsample buffer, trying regular malloc"); + audio_downsample_buf = malloc(downsample_samples * sizeof(int16_t)); + } + if (!audio_downsample_buf) { + ESP_LOGE(TAG, "Failed to allocate downsample buffer"); + free(audio_mix_buf); + audio_mix_buf = NULL; + return false; + } + ESP_LOGI(TAG, "Audio downsample buffer allocated: %zu bytes", downsample_samples * sizeof(int16_t)); + + // Use TX-only initialization to save DMA memory + // This allocates only the TX channel, not RX (we don't need audio input) + extern esp_codec_dev_handle_t bsp_audio_codec_speaker_init_tx_only(void); + sdl_esp_audio_codec_dev = bsp_audio_codec_speaker_init_tx_only(); + if (!sdl_esp_audio_codec_dev) { + ESP_LOGE(TAG, "Failed to initialize codec device"); + free(audio_mix_buf); + audio_mix_buf = NULL; + free(audio_downsample_buf); + audio_downsample_buf = NULL; + return false; + } + + // CRITICAL: Mute codec during initialization to prevent garbage sounds + ESP_LOGI(TAG, "Muting codec during initialization..."); + esp_codec_dev_set_out_mute(sdl_esp_audio_codec_dev, 1); + + // Open the codec device for playback (22.05kHz mono) + // MCLK_MULTIPLE_384 is required for ES8311 codec at 22.05kHz + esp_codec_dev_sample_info_t fs = { + .sample_rate = 22050, + .channel = 1, + .bits_per_sample = 16, + .mclk_multiple = I2S_MCLK_MULTIPLE_384, // Required for ES8311 at 22.05kHz + }; + esp_err_t ret = esp_codec_dev_open(sdl_esp_audio_codec_dev, &fs); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to open codec device: %s", esp_err_to_name(ret)); + esp_codec_dev_delete(sdl_esp_audio_codec_dev); + sdl_esp_audio_codec_dev = NULL; + free(audio_mix_buf); + audio_mix_buf = NULL; + free(audio_downsample_buf); + audio_downsample_buf = NULL; + return false; + } + + // Set initial volume (codec is still muted) + esp_codec_dev_set_out_vol(sdl_esp_audio_codec_dev, current_volume); + + ESP_LOGI(TAG, "Audio initialized: 44.1kHz -> 22.05kHz mono (2x downsampling)"); + + // CRITICAL: Unmute codec now that everything is initialized + ESP_LOGI(TAG, "Unmuting codec - audio ready!"); + esp_codec_dev_set_out_mute(sdl_esp_audio_codec_dev, 0); + + // Create audio task with larger stack for OPL emulator + // OPL emulator (adlib_getsample) needs significant stack space + // Increased from 4KB to 12KB to prevent stack overflow + BaseType_t ret_val = xTaskCreatePinnedToCore( + audio_task, + "sdl_audio", + 12 * 1024, // 12KB stack for OPL emulator + audio processing + NULL, + 7, // Priority + NULL, + 1 // Core 1 + ); + + if (ret_val != pdPASS) { + ESP_LOGE(TAG, "Failed to create audio task"); + return false; + } + + audio_initialized = true; + ESP_LOGI(TAG, "ESP32 audio backend ready"); + return true; +#else + ESP_LOGI(TAG, "Audio not supported on this board (BSP not available)"); + return false; +#endif +} + +void SDL_ESP_Audio_Shutdown(void) +{ + if (!audio_initialized) { + return; + } + + ESP_LOGI(TAG, "Shutting down audio"); + audio_initialized = false; + +#if defined(CONFIG_SDL_BSP_ESP32_P4_FUNCTION_EV) && CONFIG_SDL_BSP_ESP32_P4_FUNCTION_EV + if (sdl_esp_audio_codec_dev) { + esp_codec_dev_close(sdl_esp_audio_codec_dev); + esp_codec_dev_delete(sdl_esp_audio_codec_dev); + sdl_esp_audio_codec_dev = NULL; + } +#endif + + // Free audio buffers + if (audio_mix_buf) { + free(audio_mix_buf); + audio_mix_buf = NULL; + } + if (audio_downsample_buf) { + free(audio_downsample_buf); + audio_downsample_buf = NULL; + } + + ESP_LOGI(TAG, "Audio shutdown complete"); +} + +void SDL_ESP_Audio_SetVolume(int volume) +{ + if (volume < 0) volume = 0; + if (volume > 100) volume = 100; + + current_volume = volume; + +#if defined(CONFIG_SDL_BSP_ESP32_P4_FUNCTION_EV) && CONFIG_SDL_BSP_ESP32_P4_FUNCTION_EV + if (sdl_esp_audio_codec_dev && audio_initialized) { + esp_codec_dev_set_out_vol(sdl_esp_audio_codec_dev, volume); + ESP_LOGI(TAG, "Volume set to %d", volume); + } +#endif +} + +bool SDL_ESP_Audio_IsInitialized(void) +{ + return audio_initialized; +} + +// SDL audio callback registration (called from SDL_OpenAudioDevice) +void SDL_ESP_RegisterAudioCallback(void (*callback)(void *, uint8_t *, int), void *userdata) +{ + sdl_audio_callback = callback; + sdl_audio_userdata = userdata; + ESP_LOGI(TAG, "Audio callback registered"); +} diff --git a/components/georgik__sdl_bsp/.clang-format b/components/georgik__sdl_bsp/.clang-format new file mode 100644 index 0000000..fe428d0 --- /dev/null +++ b/components/georgik__sdl_bsp/.clang-format @@ -0,0 +1,36 @@ +--- +# ESP-IDF style clang-format configuration +BasedOnStyle: Google +IndentWidth: 4 +TabWidth: 4 +UseTab: Never +ColumnLimit: 120 +IndentCaseLabels: true +SpaceBeforeParens: Never +AlignAfterOpenBracket: Align +BinPackArguments: false +BinPackParameters: false +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +BreakBeforeBraces: Linux +IndentPPDirectives: AfterHash +SpaceInEmptyParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Auto +PointerAlignment: Right +AlwaysBreakAfterReturnType: None +AlwaysBreakAfterDefinitionReturnType: None +BreakStringLiterals: true +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +SpaceAfterCStyleCast: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeRangeBasedForLoopColon: true +MaxEmptyLinesToKeep: 2 +KeepEmptyLinesAtTheStartOfBlocks: false +SortIncludes: Never diff --git a/components/georgik__sdl_bsp/.component_hash b/components/georgik__sdl_bsp/.component_hash new file mode 100644 index 0000000..bc3b32e --- /dev/null +++ b/components/georgik__sdl_bsp/.component_hash @@ -0,0 +1 @@ +e8de8c48a14c735c1c7b003288c0e629d4cfc327d63e0459e5ff69ae23ce3b13 \ No newline at end of file diff --git a/components/georgik__sdl_bsp/.gitignore b/components/georgik__sdl_bsp/.gitignore new file mode 100644 index 0000000..b570d9e --- /dev/null +++ b/components/georgik__sdl_bsp/.gitignore @@ -0,0 +1,6 @@ +# ESP-IDF build artifacts +build/ + +# ESP-IDF configuration files +sdkconfig +sdkconfig.old \ No newline at end of file diff --git a/components/georgik__sdl_bsp/CMakeLists.txt b/components/georgik__sdl_bsp/CMakeLists.txt new file mode 100644 index 0000000..b1ad310 --- /dev/null +++ b/components/georgik__sdl_bsp/CMakeLists.txt @@ -0,0 +1,92 @@ +cmake_minimum_required(VERSION 3.16) + +# ESP-BSP SDL Abstraction Layer Component +# Kconfig-based BSP selection to avoid header conflicts + +# Conditional source files based on board selection to avoid compilation errors +set(COMPONENT_SRCS "src/esp_bsp_sdl_common.c") + +# Add board-specific sources based on Kconfig selection +if(CONFIG_SDL_BSP_M5_ATOM_S3) + list(APPEND COMPONENT_SRCS "src/boards/esp_bsp_sdl_m5_atom_s3.c") + message(STATUS "ESP-BSP SDL: Including M5 Atom S3 source files") +elseif(CONFIG_SDL_BSP_ESP_BOX_3) + list(APPEND COMPONENT_SRCS "src/boards/esp_bsp_sdl_esp_box_3.c") + message(STATUS "ESP-BSP SDL: Including ESP32-S3-BOX-3 source files") +elseif(CONFIG_SDL_BSP_M5STACK_CORE_S3) + list(APPEND COMPONENT_SRCS "src/boards/esp_bsp_sdl_m5stack_core_s3.c") + message(STATUS "ESP-BSP SDL: Including M5Stack Core S3 source files") +elseif(CONFIG_SDL_BSP_ESP32_P4_FUNCTION_EV) + list(APPEND COMPONENT_SRCS "src/boards/esp_bsp_sdl_esp32_p4_function_ev.c") + message(STATUS "ESP-BSP SDL: Including ESP32-P4 Function EV Board source files") +elseif(CONFIG_SDL_BSP_ESP32_S3_LCD_EV_BOARD) + list(APPEND COMPONENT_SRCS "src/boards/esp_bsp_sdl_esp32_s3_lcd_ev_board.c") + message(STATUS "ESP-BSP SDL: Including ESP32-S3-LCD-EV-Board source files") +elseif(CONFIG_SDL_BSP_M5STACK_TAB5) + list(APPEND COMPONENT_SRCS "src/boards/esp_bsp_sdl_m5stack_tab5.c") + message(STATUS "ESP-BSP SDL: Including M5Stack Tab5 source files") +else() + message(WARNING "ESP-BSP SDL: No board selected in menuconfig!") +endif() + +# Include only the required BSP dependencies - this is the minimum required +set(COMPONENT_REQUIRES "esp_lcd") +set(COMPONENT_PRIV_REQUIRES "espressif__esp_lcd_touch") + +# Conditional BSP selection to avoid symbol conflicts +# Each board BSP is included separately to prevent function name conflicts +if(CONFIG_SDL_BSP_M5_ATOM_S3) + list(APPEND COMPONENT_PRIV_REQUIRES "espressif__m5_atom_s3_noglib") + message(STATUS "ESP-BSP SDL: Including M5 Atom S3 BSP") +elseif(CONFIG_SDL_BSP_ESP_BOX_3) + list(APPEND COMPONENT_PRIV_REQUIRES "espressif__esp-box-3_noglib") + message(STATUS "ESP-BSP SDL: Including ESP32-S3-BOX-3 BSP") +elseif(CONFIG_SDL_BSP_M5STACK_CORE_S3) + list(APPEND COMPONENT_PRIV_REQUIRES "espressif__m5stack_core_s3_noglib") + list(APPEND COMPONENT_PRIV_REQUIRES "espressif__esp_lcd_touch") + message(STATUS "ESP-BSP SDL: Including M5Stack Core S3 BSP") +elseif(CONFIG_SDL_BSP_ESP32_P4_FUNCTION_EV) + list(APPEND COMPONENT_PRIV_REQUIRES "espressif__esp32_p4_function_ev_board_noglib") + list(APPEND COMPONENT_PRIV_REQUIRES "espressif__esp_lcd_touch") + message(STATUS "ESP-BSP SDL: Including ESP32-P4 Function EV Board BSP") +elseif(CONFIG_SDL_BSP_ESP32_S3_LCD_EV_BOARD) + list(APPEND COMPONENT_PRIV_REQUIRES "espressif__esp32_s3_lcd_ev_board_noglib") + list(APPEND COMPONENT_PRIV_REQUIRES "espressif__esp_lcd_touch") + message(STATUS "ESP-BSP SDL: Including ESP32-S3-LCD-EV-Board BSP") +elseif(CONFIG_SDL_BSP_M5STACK_TAB5) + list(APPEND COMPONENT_PRIV_REQUIRES "georgik__m5stack_tab5") + message(STATUS "ESP-BSP SDL: Including M5Stack Tab5 BSP") +else() + message(WARNING "ESP-BSP SDL: No BSP dependency selected!") +endif() + +# This approach ensures: +# 1. All BSP headers are available during compilation +# 2. No dependency resolution timing issues +# 3. Board selection works purely through Kconfig + conditional compilation +# 4. Simple addition of new boards by adding to the list above + +# Register the component +idf_component_register( + SRCS ${COMPONENT_SRCS} + INCLUDE_DIRS "include" + REQUIRES ${COMPONENT_REQUIRES} + PRIV_REQUIRES ${COMPONENT_PRIV_REQUIRES} +) + +# Add board selection messages during build (after CONFIG variables are available) +if(CONFIG_SDL_BSP_M5_ATOM_S3) + message(STATUS "ESP-BSP SDL: Building for M5 Atom S3 (128x128, no PSRAM)") +elseif(CONFIG_SDL_BSP_M5STACK_CORE_S3) + message(STATUS "ESP-BSP SDL: Building for M5Stack CoreS3 (320x240, QUAD PSRAM)") +elseif(CONFIG_SDL_BSP_ESP_BOX_3) + message(STATUS "ESP-BSP SDL: Building for ESP32-S3-BOX-3 (320x240, OCTAL PSRAM)") +elseif(CONFIG_SDL_BSP_ESP32_P4_FUNCTION_EV) + message(STATUS "ESP-BSP SDL: Building for ESP32-P4 Function EV Board (1280x800, 32MB PSRAM)") +elseif(CONFIG_SDL_BSP_ESP32_S3_LCD_EV_BOARD) + message(STATUS "ESP-BSP SDL: Building for ESP32-S3-LCD-EV-Board (800x480, OCTAL PSRAM)") +elseif(CONFIG_SDL_BSP_M5STACK_TAB5) + message(STATUS "ESP-BSP SDL: Building for M5Stack Tab5 (1280x720, 32MB PSRAM, MIPI-DSI)") +else() + message(WARNING "ESP-BSP SDL: No specific board detected in configuration. Check menuconfig.") +endif() diff --git a/components/georgik__sdl_bsp/Kconfig b/components/georgik__sdl_bsp/Kconfig new file mode 100644 index 0000000..f87f32a --- /dev/null +++ b/components/georgik__sdl_bsp/Kconfig @@ -0,0 +1,88 @@ +menu "ESP-BSP SDL Configuration" + + choice SDL_BSP_SELECTION + prompt "Select Target Board" + default SDL_BSP_ESP_BOX_3 + help + Select the target board for your SDL application. + This determines which ESP-BSP will be used and configures + the appropriate display, touch, and other hardware settings. + + config SDL_BSP_ESP_BOX_3 + bool "ESP-Box-3" + help + ESP-Box-3 development board with ESP32-S3, LCD display and touch. + + config SDL_BSP_ESP32_C6_DEVKIT + bool "ESP32-C6 DevKit with ILI9341 Display" + help + ESP32-C6 development board with external ILI9341 LCD display. + + config SDL_BSP_M5STACK_CORE_S3 + bool "M5Stack Core S3" + help + M5Stack Core S3 with ESP32-S3 and built-in display. + + config SDL_BSP_M5_ATOM_S3 + bool "M5 Atom S3" + help + M5 Atom S3 with ESP32-S3 and 240x240 round LCD display. + + config SDL_BSP_ESP32_C3_LCDKIT + bool "ESP32-C3-LCDkit" + help + ESP32-C3-LCDkit development board with LCD display. + + config SDL_BSP_ESP32_P4_FUNCTION_EV + bool "ESP32-P4 Function EV Board" + help + ESP32-P4 Function Evaluation Board with advanced display capabilities. + + config SDL_BSP_ESP32_S3_LCD_EV_BOARD + bool "ESP32-S3-LCD-EV-Board" + help + ESP32-S3-LCD-EV-Board development board for evaluating ESP32-S3 screen interactive applications. + + config SDL_BSP_M5STACK_TAB5 + bool "M5Stack Tab5 (Experimental)" + help + M5Stack Tab5 ESP32-P4 tablet with 5-inch 720×1280 MIPI-DSI display, + GT911 touch controller, and high-performance hardware. + Requires 200MHz PSRAM for proper operation. + + endchoice + + config NAME + string + default "esp-box-3_noglib" if SDL_BSP_ESP_BOX_3 + default "esp-box_noglib" if SDL_BSP_ESP_BOX + default "esp_bsp_generic" if SDL_BSP_ESP32_C6_DEVKIT + default "m5stack_core_s3_noglib" if SDL_BSP_M5STACK_CORE_S3 + default "m5_atom_s3" if SDL_BSP_M5_ATOM_S3 + default "esp32_c3_lcdkit" if SDL_BSP_ESP32_C3_LCDKIT + default "esp32_p4_function_ev_board_noglib" if SDL_BSP_ESP32_P4_FUNCTION_EV + default "esp32_s3_lcd_ev_board_noglib" if SDL_BSP_ESP32_S3_LCD_EV_BOARD + default "m5stack_tab5_noglib" if SDL_BSP_M5STACK_TAB5 + + config SDL_BSP_SDKCONFIG_SUFFIX + string + default "esp-box-3" if SDL_BSP_ESP_BOX_3 + default "esp-box" if SDL_BSP_ESP_BOX + default "esp32_c6_devkit" if SDL_BSP_ESP32_C6_DEVKIT + default "m5stack_core_s3" if SDL_BSP_M5STACK_CORE_S3 + default "m5_atom_s3" if SDL_BSP_M5_ATOM_S3 + default "esp32_c3_lcdkit" if SDL_BSP_ESP32_C3_LCDKIT + default "esp32_p4_function_ev_board" if SDL_BSP_ESP32_P4_FUNCTION_EV + default "esp32_s3_lcd_ev_board" if SDL_BSP_ESP32_S3_LCD_EV_BOARD + default "m5stack_tab5" if SDL_BSP_M5STACK_TAB5 + + config SDL_BSP_TOUCH_ENABLE + bool "Enable Touch Support" + default n + help + Enable touch interface support for boards that have touch capabilities. + This is disabled by default since some boards may have complex touch + initialization that could interfere with the application. + Only enable this if your application needs touch input. + +endmenu diff --git a/components/georgik__sdl_bsp/README.md b/components/georgik__sdl_bsp/README.md new file mode 100644 index 0000000..e8099fd --- /dev/null +++ b/components/georgik__sdl_bsp/README.md @@ -0,0 +1,402 @@ +# ESP-BSP-SDL Abstraction Layer + +This component provides a board-agnostic abstraction layer between SDL and ESP-BSP (Board Support Package). It solves the problem of having to specify board configurations via command-line parameters by moving board selection into ESP-IDF's native Kconfig system. + +## Problem Solved + +**Before**: Developers had to use complex command-line syntax for every build: +```bash +idf.py @boards/esp-box-3.cfg build flash monitor +``` + +**After**: Simple, standard ESP-IDF workflow: +```bash +idf.py menuconfig # Select board once +idf.py build flash monitor # Use for all subsequent builds +``` + +## Architecture + +``` +┌─────────────────┐ +│ SDL Application │ +└─────────┬───────┘ + │ +┌─────────▼───────┐ +│ SDL Component │ (Board-agnostic) +└─────────┬───────┘ + │ +┌─────────▼───────┐ +│ ESP-BSP-SDL │ (Abstraction Layer) +│ Component │ +└─────────┬───────┘ + │ +┌─────────▼───────┐ +│ ESP-BSP │ (Board-specific) +│ Components │ +└─────────────────┘ +``` + +## Key Benefits + +1. **Board Agnostic**: SDL no longer needs to know about specific boards +2. **Clean Separation**: BSP-specific code is isolated in the abstraction layer +3. **Easy Configuration**: Board selection through menuconfig, no command-line args needed +4. **Maintainable**: Adding new boards only requires implementing the abstraction interface +5. **Portable**: The same SDL code works across all supported boards +6. **Flexible**: Easy to switch boards without changing build commands + +## Usage + +### 1. Select Your Board +```bash +idf.py menuconfig +``` +Navigate to: `ESP-BSP SDL Configuration` → `Select Target Board` + +Choose from supported boards: +- **ESP-Box-3** - ESP32-S3 with 320x240 display and touch +- **M5 Atom S3** - Compact ESP32-S3 with 128x128 round display +- **M5Stack Core S3** - ESP32-S3 with 320x240 display and touch +- **ESP32-P4 Function EV Board** - High-performance ESP32-P4 with 1280x800 MIPI-DSI +- **ESP32-S3-LCD-EV-Board** - ESP32-S3 evaluation board with 800x480 RGB display +- **M5Stack Tab5** - ESP32-P4 tablet with 720x1280 MIPI-DSI display +- **ESP32-C6 DevKit** - Generic BSP for ESP32-C6 with external display +- **ESP32-C3-LCDkit** - ESP32-C3 development kit with LCD + +### 2. Build and Flash +```bash +idf.py build flash monitor +``` + +That's it! No need to specify `@boards/BOARD.cfg` anymore. + +### 3. Switching Boards +To switch to a different board: +1. Run `idf.py menuconfig` +2. Change the board selection +3. Run `idf.py build flash monitor` + +The abstraction layer will automatically handle the board-specific configuration. + +## Installation + +### Option 1: ESP Component Registry (Recommended) +```bash +idf.py add-dependency "georgik/sdl_bsp" +``` + +### Option 2: Git Submodule +```bash +cd components +git submodule add https://github.com/georgik/esp-idf-component-SDL_bsp.git georgik__sdl_bsp +``` + +### Option 3: Manual Download +1. Download from GitHub: https://github.com/georgik/esp-idf-component-SDL_bsp +2. Extract to `components/georgik__sdl_bsp/` in your project + +## Quick Start + +### 1. Add to Your Project +```bash +# Add the component +idf.py add-dependency "georgik/sdl_bsp" + +# Configure your board +idf.py menuconfig +``` + +### 2. Select Board in Menuconfig +Navigate to: `ESP-BSP SDL Configuration` → `Select Target Board` + +### 3. Use in Your Code +```c +#include "esp_bsp_sdl.h" + +void app_main(void) +{ + // Initialize display and get configuration + esp_bsp_sdl_display_config_t config; + esp_lcd_panel_handle_t panel_handle; + esp_lcd_panel_io_handle_t panel_io_handle; + + ESP_ERROR_CHECK(esp_bsp_sdl_init(&config, &panel_handle, &panel_io_handle)); + + printf("Display: %dx%d, Touch: %s\n", + config.width, config.height, + config.has_touch ? "Yes" : "No"); + + // Turn on backlight + ESP_ERROR_CHECK(esp_bsp_sdl_backlight_on()); + + // Your SDL code here... +} +``` + +## Implementation Details + +### Component Structure +``` +components/esp_bsp_sdl/ +├── include/ +│ └── esp_bsp_sdl.h # Public API +├── src/ +│ ├── esp_bsp_sdl_common.c # Common implementation +│ └── boards/ # Board-specific implementations +│ ├── esp_bsp_sdl_esp_box_3.c +│ ├── esp_bsp_sdl_esp_box.c +│ └── ... +├── Kconfig # Board selection configuration +└── CMakeLists.txt # Build configuration +``` + +### API Functions +- `esp_bsp_sdl_init()` - Initialize display and touch for selected board +- `esp_bsp_sdl_backlight_on/off()` - Control display backlight +- `esp_bsp_sdl_display_on_off()` - Enable/disable display +- `esp_bsp_sdl_touch_init/read()` - Touch interface (if supported) +- `esp_bsp_sdl_get_board_name()` - Get current board name +- `esp_bsp_sdl_deinit()` - Cleanup resources + +### Board-Specific Implementation +Each supported board has its own implementation file that: +1. Includes the appropriate ESP-BSP headers +2. Implements the abstraction API functions +3. Handles board-specific initialization and configuration +4. Provides display parameters (resolution, pixel format, etc.) +5. Manages touch interface (if available) + +## Adding New Boards + +To add support for a new board: + +1. **Update Kconfig** (`Kconfig`): + ```kconfig + config ESP_BSP_SDL_BOARD_NEW_BOARD + bool "New Board Name" + help + Description of the new board. + ``` + +2. **Update CMakeLists.txt**: + ```cmake + elseif(CONFIG_ESP_BSP_SDL_BOARD_NEW_BOARD) + list(APPEND srcs "src/boards/esp_bsp_sdl_new_board.c") + list(APPEND board_deps "new_board_bsp_component") + ``` + +3. **Create board implementation** (`src/boards/esp_bsp_sdl_new_board.c`): + - Include board-specific BSP headers + - Implement all required abstraction API functions + - Handle board-specific display and touch initialization + +## Migration from Old Approach + +### Old SDL Integration +```c +// Direct BSP includes +#include "bsp/esp-bsp.h" +#include "bsp/display.h" + +// BSP constants used directly +mode.w = BSP_LCD_H_RES; +mode.h = BSP_LCD_V_RES; + +// Direct BSP calls +ESP_ERROR_CHECK(bsp_display_new(&bsp_disp_cfg, &panel_handle, &panel_io_handle)); +ESP_ERROR_CHECK(bsp_display_backlight_on()); +``` + +### New SDL Integration +```c +// Abstraction layer include +#include "esp_bsp_sdl.h" + +// Get configuration through abstraction +esp_bsp_sdl_display_config_t config; +esp_bsp_sdl_init(&config, &panel_handle, &panel_io_handle); +mode.w = config.width; +mode.h = config.height; + +// Abstracted function calls +ESP_ERROR_CHECK(esp_bsp_sdl_backlight_on()); +``` + +## Supported Boards + +Currently implemented boards: +- ✅ **ESP-Box-3** (`esp-box-3_noglib`) - 320x240 ILI9341, Touch, OCTAL PSRAM +- ✅ **M5 Atom S3** (`m5_atom_s3_noglib`) - 128x128 GC9A01, No PSRAM +- ✅ **M5Stack Core S3** (`m5stack_core_s3_noglib`) - 320x240 ILI9341, Touch, QUAD PSRAM +- ✅ **ESP32-P4 Function EV** (`esp32_p4_function_ev_board_noglib`) - 1280x800 MIPI-DSI, Touch, 32MB PSRAM +- ✅ **ESP32-S3-LCD-EV-Board** (`esp32_s3_lcd_ev_board_noglib`) - 800x480 RGB, Touch, OCTAL PSRAM +- ✅ **M5Stack Tab5** (`m5stack_tab5`) - 720x1280 MIPI-DSI (portrait), GT911 Touch, 32MB PSRAM @ 200MHz +- 🚧 **ESP32-C6 DevKit** (`esp_bsp_generic`) - Generic BSP template +- 🚧 **ESP32-C3-LCDkit** (`esp32_c3_lcdkit`) - Template provided + +Legend: ✅ Fully implemented, 🚧 Template provided (needs completion) + +### Board-Specific Features + +| Board | Display | Resolution | Touch | PSRAM | Interface | +|-------|---------|------------|-------|-------|-----------| +| ESP-Box-3 | ILI9341 | 320x240 | FT5x06 | 8MB OCTAL | SPI | +| M5 Atom S3 | GC9A01 | 128x128 | - | - | SPI | +| M5Stack Core S3 | ILI9341 | 320x240 | FT5x06 | 8MB QUAD | SPI | +| ESP32-P4 Function EV | EK9716B | 1280x800 | GT1151 | 32MB | MIPI-DSI | +| ESP32-S3-LCD-EV | RGB Panel | 800x480 | GT1151 | 32MB OCTAL | RGB | +| M5Stack Tab5 | ILI9881C | 720x1280* | GT911 | 32MB @ 200MHz | MIPI-DSI | + +*M5Stack Tab5 uses 1280x720 landscape mode in SDL for better compatibility + +## Dependencies + +The abstraction layer uses **conditional dependencies** to load only the required BSP for your selected board: + +### Automatic Dependency Management +- **Runtime Selection**: Only the selected board's BSP is loaded +- **No Conflicts**: Prevents symbol conflicts between different BSPs +- **Minimal Footprint**: Only necessary components are included + +### Core Dependencies (Always Loaded) +- `esp_lcd` - LCD panel operations +- `espressif__esp_lcd_touch` - Touch interface support + +### Board-Specific Dependencies (Conditional) +| Board | BSP Component | Additional Dependencies | +|-------|---------------|------------------------| +| ESP-Box-3 | `espressif__esp-box-3_noglib` | - | +| M5 Atom S3 | `espressif__m5_atom_s3_noglib` | - | +| M5Stack Core S3 | `espressif__m5stack_core_s3_noglib` | `espressif__esp_lcd_touch` | +| ESP32-P4 Function EV | `espressif__esp32_p4_function_ev_board_noglib` | `espressif__esp_lcd_touch` | +| ESP32-S3-LCD-EV | `espressif__esp32_s3_lcd_ev_board_noglib` | `espressif__esp_lcd_touch` | +| M5Stack Tab5 | `georgik__m5stack_tab5` | - | + +### ESP32-P4 Specific +ESP32-P4 boards automatically include: +- `esp_driver_ppa` - Pixel Processing Accelerator +- MIPI-DSI interface support + +## Critical Configuration Settings + +### ESP-Box-3 Requirements + +For ESP-Box-3 to work properly, these configurations are **mandatory**: + +```ini +# Hardware Requirements +CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y # 8MB flash memory +CONFIG_SPIRAM_MODE_OCT=y # Octal PSRAM mode (NOT quad!) +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y # 240MHz for optimal performance + +# 🔧 CRITICAL DEBUG Setting 🔧 +CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT=y # Halt on panic instead of reboot + # Essential for debugging! +``` + +### Why `CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT=y` is Essential + +**Without this setting**: +- System reboots continuously on panic/crash +- Makes debugging nearly impossible +- You see endless reboot loops + +**With this setting**: +- System halts after printing complete panic information +- Stack traces are fully visible +- Easy to identify crash locations +- Memory issues become debuggable + +This setting is **critical** for development and should always be enabled during the development phase. + +## Touch Support + +The abstraction layer provides optional touch support for compatible boards: + +### Enabling Touch +Touch support is disabled by default to avoid initialization conflicts. Enable it via menuconfig: + +```bash +idf.py menuconfig +``` +Navigate to: `ESP-BSP SDL Configuration` → `Enable Touch Support` + +### Touch-Compatible Boards +- **ESP-Box-3**: FT5x06 capacitive touch controller +- **M5Stack Core S3**: FT5x06 capacitive touch controller +- **ESP32-P4 Function EV**: GT1151 capacitive touch controller +- **ESP32-S3-LCD-EV-Board**: GT1151 capacitive touch controller +- **M5Stack Tab5**: GT911 multi-point capacitive touch controller + +### Using Touch in Your Application +```c +#include "esp_bsp_sdl.h" + +// Initialize touch (if enabled and supported) +if (esp_bsp_sdl_touch_init() == ESP_OK) { + printf("Touch initialized successfully\n"); + + // Read touch data + esp_bsp_sdl_touch_info_t touch_info; + if (esp_bsp_sdl_touch_read(&touch_info) == ESP_OK) { + if (touch_info.pressed) { + printf("Touch at: %d, %d\n", touch_info.x, touch_info.y); + } + } +} +``` + +## M5Stack Tab5 Special Requirements + +The **M5Stack Tab5** is an advanced ESP32-P4 tablet requiring special configuration: + +### Critical PSRAM Configuration +```ini +# MANDATORY for M5Stack Tab5 +CONFIG_SPIRAM=y +CONFIG_SPIRAM_MODE_HEX=y +CONFIG_SPIRAM_SPEED_200M=y # CRITICAL: Must be 200MHz! +CONFIG_SPIRAM_FETCH_INSTRUCTIONS=y +CONFIG_SPIRAM_RODATA=y +``` + +**⚠️ Warning**: M5Stack Tab5 **requires 200MHz PSRAM speed**. Lower speeds (120MHz, 80MHz) will cause instability. + +### Display Orientation +- **Physical orientation**: 720x1280 (portrait) +- **SDL configuration**: 1280x720 (landscape) for better compatibility +- **Touch coordinates**: Automatically rotated to match SDL orientation + +### Features +- **Display**: 5-inch 720×1280 IPS via MIPI-DSI interface +- **Touch**: GT911 multi-point capacitive touch controller +- **Memory**: 32MB PSRAM @ 200MHz, 16MB Flash +- **SoC**: ESP32-P4 dual-core RISC-V +- **Interface**: MIPI-DSI for high-speed display communication + +## Configuration Files + +The old approach required separate configuration files for each board: +- `sdkconfig.defaults.esp-box-3` +- `sdkconfig.defaults.esp32_c6_devkit` +- etc. + +These files are still used internally by the ESP-BSP components, but developers no longer need to manually specify them via command-line arguments. However, the main `sdkconfig.defaults` should contain the critical settings mentioned above. + +## Troubleshooting + +### Build Errors +- Ensure you've selected a board via `idf.py menuconfig` +- Check that the ESP-BSP component for your board is available +- Verify that your target matches the selected board (e.g., ESP32-S3 for ESP-Box-3) + +### Display Issues +- Verify board selection matches your hardware +- Check display connections and power supply +- Review board-specific documentation for pin configurations + +### Touch Not Working +- Ensure your board supports touch (check `has_touch` in display config) +- Verify touch interface initialization in board-specific implementation +- Check touch hardware connections diff --git a/components/georgik__sdl_bsp/idf_component.yml b/components/georgik__sdl_bsp/idf_component.yml new file mode 100644 index 0000000..158aa30 --- /dev/null +++ b/components/georgik__sdl_bsp/idf_component.yml @@ -0,0 +1,53 @@ + +name: sdl_bsp +version: 3.3.0~2 +description: ESP-BSP-SDL Abstraction Layer Component. Board-specific BSP dependencies based on Kconfig selection for SDL3. +dependencies: + # IDF version requirement + idf: + version: '>=5.5.0' + + # Conditional BSP dependencies - only load the BSP for the selected board + espressif/m5_atom_s3_noglib: + version: "~1.0.0" + matches: + - if: "$CONFIG{SDL_BSP_M5_ATOM_S3} == True" + espressif/esp-box-3_noglib: + version: "*" + matches: + - if: "$CONFIG{SDL_BSP_ESP_BOX_3} == True" + espressif/m5stack_core_s3_noglib: + version: "*" + matches: + - if: "$CONFIG{SDL_BSP_M5STACK_CORE_S3} == True" + espressif/esp32_p4_function_ev_board_noglib: + version: "*" + matches: + - if: "$CONFIG{SDL_BSP_ESP32_P4_FUNCTION_EV} == True" + espressif/esp32_s3_lcd_ev_board_noglib: + version: "*" + matches: + - if: "$CONFIG{SDL_BSP_ESP32_S3_LCD_EV_BOARD} == True" + georgik/m5stack_tab5: + version: "feature/m5stack-tab5-sdl3" + git: "https://github.com/georgik/esp-bsp.git" + path: "bsp/m5stack_tab5" + matches: + - if: "$CONFIG{SDL_BSP_M5STACK_TAB5} == True" + + # Touch support - required by some BSPs (esp-box-3, core_s3, etc.) + # Making it unconditional since conditional Kconfig resolution is problematic + espressif/esp_lcd_touch: + version: "*" + require: "private" + + +license: MIT License and the Apache License 2.0 +repository: https://github.com/georgik/esp-idf-component-SDL_bsp +url: https://github.com/georgik/esp-idf-component-SDL_bsp + +exclude: + - build/** + - support/** + - .clang-format + - sdkconfig diff --git a/components/georgik__sdl_bsp/include/esp_bsp_sdl.h b/components/georgik__sdl_bsp/include/esp_bsp_sdl.h new file mode 100644 index 0000000..7923fa9 --- /dev/null +++ b/components/georgik__sdl_bsp/include/esp_bsp_sdl.h @@ -0,0 +1,126 @@ +/** + * @file esp_bsp_sdl.h + * @brief ESP-BSP SDL Abstraction Layer + * + * This component provides a board-agnostic abstraction layer between SDL and ESP-BSP. + * It allows SDL to work with different ESP boards without needing to know the specific + * BSP implementation details. + */ + +#pragma once + +#include "esp_err.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_panel_vendor.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Display configuration structure for SDL abstraction + */ +typedef struct { + int width; /*!< Display width in pixels */ + int height; /*!< Display height in pixels */ + int pixel_format; /*!< SDL pixel format */ + size_t max_transfer_sz; /*!< Maximum transfer size for display operations */ + bool has_touch; /*!< Whether the display has touch capability */ +} esp_bsp_sdl_display_config_t; + +/** + * @brief Touch information structure + */ +typedef struct { + bool pressed; /*!< Touch state */ + int x; /*!< Touch X coordinate */ + int y; /*!< Touch Y coordinate */ +} esp_bsp_sdl_touch_info_t; + +/** + * @brief Board interface function pointer structure (for internal use) + */ +typedef struct { + esp_err_t (*init)(esp_bsp_sdl_display_config_t *config, + esp_lcd_panel_handle_t *panel_handle, + esp_lcd_panel_io_handle_t *panel_io_handle); + esp_err_t (*backlight_on)(void); + esp_err_t (*backlight_off)(void); + esp_err_t (*display_on_off)(bool enable); + esp_err_t (*touch_init)(void); + esp_err_t (*touch_read)(esp_bsp_sdl_touch_info_t *touch_info); + const char *(*get_name)(void); + esp_err_t (*deinit)(void); + const char *board_name; +} esp_bsp_sdl_board_interface_t; + +/** + * @brief Initialize the ESP-BSP SDL abstraction layer + * + * This function initializes the board-specific display and touch interfaces + * based on the selected board configuration. + * + * @param[out] config Display configuration structure to be filled + * @param[out] panel_handle LCD panel handle + * @param[out] panel_io_handle LCD panel IO handle + * @return ESP_OK on success, error code otherwise + */ +esp_err_t esp_bsp_sdl_init(esp_bsp_sdl_display_config_t *config, + esp_lcd_panel_handle_t *panel_handle, + esp_lcd_panel_io_handle_t *panel_io_handle); + +/** + * @brief Turn on display backlight + * + * @return ESP_OK on success, error code otherwise + */ +esp_err_t esp_bsp_sdl_backlight_on(void); + +/** + * @brief Turn off display backlight + * + * @return ESP_OK on success, error code otherwise + */ +esp_err_t esp_bsp_sdl_backlight_off(void); + +/** + * @brief Enable/disable display + * + * @param enable true to enable display, false to disable + * @return ESP_OK on success, error code otherwise + */ +esp_err_t esp_bsp_sdl_display_on_off(bool enable); + +/** + * @brief Initialize touch interface (if available) + * + * @return ESP_OK on success, ESP_ERR_NOT_SUPPORTED if no touch, error code otherwise + */ +esp_err_t esp_bsp_sdl_touch_init(void); + +/** + * @brief Read touch information + * + * @param[out] touch_info Touch information structure to be filled + * @return ESP_OK on success, ESP_ERR_NOT_SUPPORTED if no touch, error code otherwise + */ +esp_err_t esp_bsp_sdl_touch_read(esp_bsp_sdl_touch_info_t *touch_info); + +/** + * @brief Get the selected board name (for debugging/logging) + * + * @return Board name string + */ +const char *esp_bsp_sdl_get_board_name(void); + +/** + * @brief Deinitialize the ESP-BSP SDL abstraction layer + * + * @return ESP_OK on success, error code otherwise + */ +esp_err_t esp_bsp_sdl_deinit(void); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/components/georgik__sdl_bsp/src/boards/esp_bsp_sdl_esp32_p4_function_ev.c b/components/georgik__sdl_bsp/src/boards/esp_bsp_sdl_esp32_p4_function_ev.c new file mode 100644 index 0000000..832000c --- /dev/null +++ b/components/georgik__sdl_bsp/src/boards/esp_bsp_sdl_esp32_p4_function_ev.c @@ -0,0 +1,264 @@ +/** + * @file esp_bsp_sdl_esp32_p4_function_ev.c + * @brief ESP32-P4 Function EV Board specific implementation for ESP-BSP SDL abstraction layer + * Uses official espressif/esp32_p4_function_ev_board_noglib BSP + */ + +#include "esp_bsp_sdl.h" +#include "esp_err.h" +#include "esp_log.h" +#include "sdkconfig.h" + +// Include ESP32-P4 Function EV Board BSP headers - only when this board is selected +#include "bsp/display.h" +#include "bsp/esp32_p4_function_ev_board.h" + +// Forward declarations for touch support to avoid including problematic headers +// The managed BSP component doesn't have esp_lcd_touch dependency, so we include it directly +#include "esp_lcd_touch.h" + +// Define BSP touch types to avoid including bsp/touch.h which causes compilation errors +typedef struct { + void *dummy; /*!< Reserved for future use. */ +} bsp_touch_config_t; + +// Function prototype for bsp_touch_new (from the BSP) +esp_err_t bsp_touch_new(const bsp_touch_config_t *config, esp_lcd_touch_handle_t *ret_touch); + +// SDL pixel format constants - using direct values to avoid SDL dependency +#define SDL_PIXELFORMAT_RGB565 0x15151002u +#define SDL_PIXELFORMAT_RGB888 0x16161804u + +static const char *TAG = "esp_bsp_sdl_esp32_p4_function_ev"; +static esp_lcd_panel_handle_t s_panel_handle = NULL; +static esp_lcd_panel_io_handle_t s_panel_io_handle = NULL; +static esp_lcd_touch_handle_t s_touch_handle = NULL; + +static esp_err_t esp32_p4_function_ev_init(esp_bsp_sdl_display_config_t *config, + esp_lcd_panel_handle_t *panel_handle, + esp_lcd_panel_io_handle_t *panel_io_handle) +{ + ESP_LOGI(TAG, "Initializing ESP32-P4 Function EV Board display using BSP"); + + if(!config || !panel_handle || !panel_io_handle) { + return ESP_ERR_INVALID_ARG; + } + + esp_err_t ret = ESP_OK; + + // Step 1: Fill in display configuration for ESP32-P4 Function EV Board + // Default LCD is 1280x800 ili9881c, but can be configured via menuconfig +#ifdef CONFIG_BSP_LCD_TYPE_1024_600 + config->width = 1024; // EK79007 LCD resolution 1024x600 + config->height = 600; +#elif defined(CONFIG_BSP_LCD_TYPE_1280_800) + config->width = 1280; // ILI9881C LCD resolution 1280x800 + config->height = 800; +#else + config->width = 1280; // Default resolution + config->height = 800; +#endif + + // Set pixel format based on configuration +#ifdef CONFIG_BSP_LCD_RGB888 + config->pixel_format = SDL_PIXELFORMAT_RGB888; + config->max_transfer_sz = (config->width * config->height) * 3; // 3 bytes per pixel for RGB888 +#else + config->pixel_format = SDL_PIXELFORMAT_RGB565; + config->max_transfer_sz = (config->width * config->height) * 2; // 2 bytes per pixel for RGB565 +#endif + +#ifdef CONFIG_SDL_BSP_TOUCH_ENABLE + config->has_touch = BSP_CAPS_TOUCH == 1; +#else + config->has_touch = false; +#endif + + // Step 2: Initialize BSP display using the official BSP + ESP_LOGI(TAG, "Initializing display panel (%dx%d)...", config->width, config->height); + const bsp_display_config_t bsp_disp_cfg = { + .hdmi_resolution = BSP_HDMI_RES_NONE, // Use LCD, not HDMI + .dsi_bus = + { + .phy_clk_src = MIPI_DSI_PHY_PLLREF_CLK_SRC_DEFAULT, // Use XTAL for ESP-IDF 6.1 + .lane_bit_rate_mbps = BSP_LCD_MIPI_DSI_LANE_BITRATE_MBPS, + }, + }; + + ret = bsp_display_new(&bsp_disp_cfg, &s_panel_handle, &s_panel_io_handle); + if(ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to initialize BSP display: %s", esp_err_to_name(ret)); + return ret; + } + + // Step 3: DPI panels don't support disp_on_off, they're always on + ESP_LOGI(TAG, "Display is ready (DPI panels are always on)..."); + + // Step 4: Turn on backlight if supported + ESP_LOGI(TAG, "Turning on backlight..."); + ret = bsp_display_brightness_init(); + if(ret == ESP_OK) { + ret = bsp_display_backlight_on(); + if(ret != ESP_OK) { + ESP_LOGW(TAG, "Failed to turn on backlight: %s", esp_err_to_name(ret)); + // Don't fail initialization if backlight fails + } + } else { + ESP_LOGW(TAG, "Backlight initialization failed: %s", esp_err_to_name(ret)); + // Don't fail initialization if backlight init fails + } + + // Return handles to caller + *panel_handle = s_panel_handle; + *panel_io_handle = s_panel_io_handle; + + ESP_LOGI(TAG, "ESP32-P4 Function EV Board display initialized: %dx%d", config->width, config->height); + + return ESP_OK; +} + +static esp_err_t esp32_p4_function_ev_backlight_on(void) +{ + ESP_LOGI(TAG, "ESP32-P4 Function EV Board: Turning backlight on"); + esp_err_t ret = bsp_display_backlight_on(); + if(ret != ESP_OK) { + ESP_LOGW(TAG, "Backlight control not supported: %s", esp_err_to_name(ret)); + } + return ret; +} + +static esp_err_t esp32_p4_function_ev_backlight_off(void) +{ + ESP_LOGI(TAG, "ESP32-P4 Function EV Board: Turning backlight off"); + esp_err_t ret = bsp_display_backlight_off(); + if(ret != ESP_OK) { + ESP_LOGW(TAG, "Backlight control not supported: %s", esp_err_to_name(ret)); + } + return ret; +} + +static esp_err_t esp32_p4_function_ev_display_on_off(bool enable) +{ + ESP_LOGD(TAG, "%s display (DPI panels are always on)", enable ? "Enabling" : "Disabling"); + // DPI panels don't support disp_on_off, they're always on + return ESP_OK; +} + +static esp_err_t esp32_p4_function_ev_touch_init(void) +{ +#if BSP_CAPS_TOUCH == 1 +#ifdef CONFIG_SDL_BSP_TOUCH_ENABLE + ESP_LOGI(TAG, "Initializing touch interface"); + + const bsp_touch_config_t touch_cfg = { + .dummy = NULL, // Reserved for future use + }; + esp_err_t ret = bsp_touch_new(&touch_cfg, &s_touch_handle); + if(ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to initialize touch: %s", esp_err_to_name(ret)); + return ret; + } + + ESP_LOGI(TAG, "Touch interface initialized successfully"); + return ESP_OK; +#else + ESP_LOGW(TAG, "Touch disabled via CONFIG_SDL_BSP_TOUCH_ENABLE"); + return ESP_ERR_NOT_SUPPORTED; +#endif +#else + ESP_LOGW(TAG, "Touch not supported on this board configuration"); + return ESP_ERR_NOT_SUPPORTED; +#endif +} + +static esp_err_t esp32_p4_function_ev_touch_read(esp_bsp_sdl_touch_info_t *touch_info) +{ +#if BSP_CAPS_TOUCH == 1 +#ifdef CONFIG_SDL_BSP_TOUCH_ENABLE + if(!touch_info) { + return ESP_ERR_INVALID_ARG; + } + + if(!s_touch_handle) { + ESP_LOGW(TAG, "Touch not initialized"); + touch_info->pressed = false; + touch_info->x = 0; + touch_info->y = 0; + return ESP_ERR_INVALID_STATE; + } + + uint16_t touch_x[1] = {0}; + uint16_t touch_y[1] = {0}; + uint16_t touch_strength[1] = {0}; + uint8_t touch_cnt = 0; + + esp_err_t ret = esp_lcd_touch_read_data(s_touch_handle); + if(ret == ESP_OK) { + bool pressed = esp_lcd_touch_get_coordinates(s_touch_handle, touch_x, touch_y, touch_strength, &touch_cnt, 1); + if(pressed && touch_cnt > 0) { + touch_info->pressed = true; + touch_info->x = touch_x[0]; + touch_info->y = touch_y[0]; + } else { + touch_info->pressed = false; + touch_info->x = 0; + touch_info->y = 0; + } + } else { + touch_info->pressed = false; + touch_info->x = 0; + touch_info->y = 0; + } + + return ESP_OK; +#else + // Touch is disabled via configuration + if(touch_info) { + touch_info->pressed = false; + touch_info->x = 0; + touch_info->y = 0; + } + return ESP_ERR_NOT_SUPPORTED; +#endif +#else + return ESP_ERR_NOT_SUPPORTED; +#endif +} + +static const char *esp32_p4_function_ev_get_name(void) +{ + return "ESP32-P4 Function EV Board"; +} + +static esp_err_t esp32_p4_function_ev_deinit(void) +{ + ESP_LOGI(TAG, "Deinitializing ESP32-P4 Function EV Board"); + + // Clean up touch resources + if(s_touch_handle) { + s_touch_handle = NULL; + } + + // Clean up display resources + if(s_panel_handle) { + s_panel_handle = NULL; + } + + if(s_panel_io_handle) { + s_panel_io_handle = NULL; + } + + return ESP_OK; +} + +// ESP32-P4 Function EV Board interface +const esp_bsp_sdl_board_interface_t esp_bsp_sdl_esp32_p4_function_ev_interface = { + .init = esp32_p4_function_ev_init, + .backlight_on = esp32_p4_function_ev_backlight_on, + .backlight_off = esp32_p4_function_ev_backlight_off, + .display_on_off = esp32_p4_function_ev_display_on_off, + .touch_init = esp32_p4_function_ev_touch_init, + .touch_read = esp32_p4_function_ev_touch_read, + .get_name = esp32_p4_function_ev_get_name, + .deinit = esp32_p4_function_ev_deinit, + .board_name = "ESP32-P4 Function EV Board"}; \ No newline at end of file diff --git a/components/georgik__sdl_bsp/src/boards/esp_bsp_sdl_esp32_s3_lcd_ev_board.c b/components/georgik__sdl_bsp/src/boards/esp_bsp_sdl_esp32_s3_lcd_ev_board.c new file mode 100644 index 0000000..8a597fd --- /dev/null +++ b/components/georgik__sdl_bsp/src/boards/esp_bsp_sdl_esp32_s3_lcd_ev_board.c @@ -0,0 +1,215 @@ +/** + * @file esp_bsp_sdl_esp32_s3_lcd_ev_board.c + * @brief ESP32-S3-LCD-EV-Board implementation for ESP-BSP SDL abstraction layer + * Uses official espressif/esp32_s3_lcd_ev_board_noglib BSP + * Supports multiple LCD sub-boards: 480x480 (RGB) and 800x480 (RGB) + */ + +#include "esp_bsp_sdl.h" +#include "esp_err.h" +#include "esp_log.h" +#include "sdkconfig.h" + +// Include ESP32-S3-LCD-EV-Board BSP headers - only when this board is selected +#include "bsp/esp32_s3_lcd_ev_board.h" +#include "bsp/display.h" +#ifdef CONFIG_SDL_BSP_TOUCH_ENABLE +# include "bsp/touch.h" +# include "esp_lcd_touch.h" +#endif + +// SDL pixel format constants - using direct values to avoid SDL dependency +#define SDL_PIXELFORMAT_RGB565 0x15151002u + +static const char *TAG = "esp_bsp_sdl_esp32_s3_lcd_ev_board"; +static esp_lcd_panel_handle_t s_panel_handle = NULL; +static esp_lcd_panel_io_handle_t s_panel_io_handle = NULL; +#ifdef CONFIG_SDL_BSP_TOUCH_ENABLE +static esp_lcd_touch_handle_t s_touch_handle = NULL; +#endif + +static esp_err_t esp32_s3_lcd_ev_board_init(esp_bsp_sdl_display_config_t *config, + esp_lcd_panel_handle_t *panel_handle, + esp_lcd_panel_io_handle_t *panel_io_handle) +{ + ESP_LOGI(TAG, "Initializing ESP32-S3-LCD-EV-Board display using BSP"); + + if(!config || !panel_handle || !panel_io_handle) { + return ESP_ERR_INVALID_ARG; + } + + esp_err_t ret = ESP_OK; + + // Step 1: Fill in display configuration + // Note: ESP32-S3-LCD-EV-Board supports multiple sub-boards with different resolutions + // For now, we'll default to sub-board 3 (800x480) as it's commonly used + uint16_t h_res = bsp_display_get_h_res(); + uint16_t v_res = bsp_display_get_v_res(); + + if(h_res == 0 || v_res == 0) { + // Default to sub-board 3 resolution if BSP hasn't determined it yet + h_res = BSP_LCD_SUB_BOARD_3_H_RES; + v_res = BSP_LCD_SUB_BOARD_3_V_RES; + } + + config->width = h_res; + config->height = v_res; + config->pixel_format = SDL_PIXELFORMAT_RGB565; + config->max_transfer_sz = (h_res * v_res) * sizeof(uint16_t); +#ifdef CONFIG_SDL_BSP_TOUCH_ENABLE + config->has_touch = BSP_CAPS_TOUCH == 1; +#else + config->has_touch = false; +#endif + + ESP_LOGI(TAG, "Configuring display: %dx%d", config->width, config->height); + + // Step 2: Initialize BSP display using the official BSP + ESP_LOGI(TAG, "Initializing display panel..."); + const bsp_display_config_t bsp_disp_cfg = { + .max_transfer_sz = config->max_transfer_sz, + }; + + ret = bsp_display_new(&bsp_disp_cfg, &s_panel_handle, &s_panel_io_handle); + if(ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to initialize BSP display: %s", esp_err_to_name(ret)); + return ret; + } + + // Step 3: Turn on the display + ESP_LOGI(TAG, "Enabling display..."); + ret = esp_lcd_panel_disp_on_off(s_panel_handle, true); + if(ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to turn on display: %s", esp_err_to_name(ret)); + return ret; + } + + // Step 4: Backlight is not controllable on ESP32-S3-LCD-EV-Board (always on) + ESP_LOGI(TAG, "Display backlight is always on (hardware controlled)"); + + // Return handles to caller + *panel_handle = s_panel_handle; + *panel_io_handle = s_panel_io_handle; + + ESP_LOGI(TAG, "ESP32-S3-LCD-EV-Board display initialized: %dx%d", config->width, config->height); + + return ESP_OK; +} + +static esp_err_t esp32_s3_lcd_ev_board_backlight_on(void) +{ + ESP_LOGW(TAG, "ESP32-S3-LCD-EV-Board: Backlight control not supported (always on)"); + return ESP_ERR_NOT_SUPPORTED; +} + +static esp_err_t esp32_s3_lcd_ev_board_backlight_off(void) +{ + ESP_LOGW(TAG, "ESP32-S3-LCD-EV-Board: Backlight control not supported (always on)"); + return ESP_ERR_NOT_SUPPORTED; +} + +static esp_err_t esp32_s3_lcd_ev_board_display_on_off(bool enable) +{ + ESP_LOGD(TAG, "%s display", enable ? "Enabling" : "Disabling"); + if(s_panel_handle) { + return esp_lcd_panel_disp_on_off(s_panel_handle, enable); + } + return ESP_ERR_INVALID_STATE; +} + +static esp_err_t esp32_s3_lcd_ev_board_touch_init(void) +{ +#ifdef CONFIG_SDL_BSP_TOUCH_ENABLE +# if BSP_CAPS_TOUCH == 1 + ESP_LOGI(TAG, "Initializing touch interface"); + + // ESP32-S3-LCD-EV-Board supports touch on sub-boards 2 and 3 + const bsp_touch_config_t touch_cfg = {.dummy = NULL}; + esp_err_t ret = bsp_touch_new(&touch_cfg, &s_touch_handle); + if(ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to initialize touch: %s", esp_err_to_name(ret)); + return ret; + } + + ESP_LOGI(TAG, "Touch interface initialized successfully"); + return ESP_OK; +# else + ESP_LOGW(TAG, "Touch not supported on this board configuration"); + return ESP_ERR_NOT_SUPPORTED; +# endif +#else + ESP_LOGW(TAG, "Touch support disabled in configuration"); + return ESP_ERR_NOT_SUPPORTED; +#endif +} + +static esp_err_t esp32_s3_lcd_ev_board_touch_read(esp_bsp_sdl_touch_info_t *touch_info) +{ +#ifdef CONFIG_SDL_BSP_TOUCH_ENABLE +# if BSP_CAPS_TOUCH == 1 + if(!touch_info || !s_touch_handle) { + return ESP_ERR_INVALID_ARG; + } + + // Read touch data using ESP-LCD touch API + uint16_t touch_x[1] = {0}; + uint16_t touch_y[1] = {0}; + uint8_t touch_cnt = 0; + + // First read the touch data + esp_lcd_touch_read_data(s_touch_handle); + + // Then get the coordinates + bool touched = esp_lcd_touch_get_coordinates(s_touch_handle, touch_x, touch_y, NULL, &touch_cnt, 1); + + touch_info->pressed = touched && (touch_cnt > 0); + touch_info->x = touch_info->pressed ? (int) touch_x[0] : 0; + touch_info->y = touch_info->pressed ? (int) touch_y[0] : 0; + + return ESP_OK; +# else + return ESP_ERR_NOT_SUPPORTED; +# endif +#else + return ESP_ERR_NOT_SUPPORTED; +#endif +} + +static const char *esp32_s3_lcd_ev_board_get_name(void) +{ + return "ESP32-S3-LCD-EV-Board"; +} + +static esp_err_t esp32_s3_lcd_ev_board_deinit(void) +{ + ESP_LOGI(TAG, "Deinitializing ESP32-S3-LCD-EV-Board"); + + // Clean up resources if needed + if(s_panel_handle) { + s_panel_handle = NULL; + } + + if(s_panel_io_handle) { + s_panel_io_handle = NULL; + } + +#ifdef CONFIG_SDL_BSP_TOUCH_ENABLE + if(s_touch_handle) { + s_touch_handle = NULL; + } +#endif + + return ESP_OK; +} + +// ESP32-S3-LCD-EV-Board board interface +const esp_bsp_sdl_board_interface_t esp_bsp_sdl_esp32_s3_lcd_ev_board_interface = { + .init = esp32_s3_lcd_ev_board_init, + .backlight_on = esp32_s3_lcd_ev_board_backlight_on, + .backlight_off = esp32_s3_lcd_ev_board_backlight_off, + .display_on_off = esp32_s3_lcd_ev_board_display_on_off, + .touch_init = esp32_s3_lcd_ev_board_touch_init, + .touch_read = esp32_s3_lcd_ev_board_touch_read, + .get_name = esp32_s3_lcd_ev_board_get_name, + .deinit = esp32_s3_lcd_ev_board_deinit, + .board_name = "ESP32-S3-LCD-EV-Board"}; \ No newline at end of file diff --git a/components/georgik__sdl_bsp/src/boards/esp_bsp_sdl_esp_box_3.c b/components/georgik__sdl_bsp/src/boards/esp_bsp_sdl_esp_box_3.c new file mode 100644 index 0000000..92725d4 --- /dev/null +++ b/components/georgik__sdl_bsp/src/boards/esp_bsp_sdl_esp_box_3.c @@ -0,0 +1,158 @@ +/** + * @file esp_bsp_sdl_esp_box_3.c + * @brief ESP-Box-3 specific implementation for ESP-BSP SDL abstraction layer + */ + +#include "esp_bsp_sdl.h" +#include "esp_err.h" +#include "esp_log.h" +#include "sdkconfig.h" + +// Include ESP-Box-3 BSP headers - only when this board is selected +// #include "esp_box_3_noglib/bsp/display.h" +#include "bsp/display.h" +#include "bsp/esp-bsp.h" +#define BSP_CAPS_TOUCH 0 +#if BSP_CAPS_TOUCH == 1 +# include "bsp/touch.h" +#endif + +// SDL pixel format constants - using direct values to avoid SDL dependency +#define SDL_PIXELFORMAT_RGB565 0x15151002u + +static const char *TAG = "esp_bsp_sdl_esp_box_3"; +static esp_lcd_panel_handle_t s_panel_handle = NULL; +static esp_lcd_panel_io_handle_t s_panel_io_handle = NULL; + +static esp_err_t esp_box_3_init(esp_bsp_sdl_display_config_t *config, + esp_lcd_panel_handle_t *panel_handle, + esp_lcd_panel_io_handle_t *panel_io_handle) +{ + ESP_LOGI(TAG, "Initializing ESP-Box-3 display"); + + if(!config || !panel_handle || !panel_io_handle) { + return ESP_ERR_INVALID_ARG; + } + + // Fill in display configuration for ESP-BOX-3 (320x240) + config->width = 320; // ESP-BOX-3 specific resolution + config->height = 240; // ESP-BOX-3 specific resolution + config->pixel_format = SDL_PIXELFORMAT_RGB565; + config->max_transfer_sz = (320 * 240) * sizeof(uint16_t); + config->has_touch = BSP_CAPS_TOUCH == 1; + + // Initialize BSP display + const bsp_display_config_t bsp_disp_cfg = { + .max_transfer_sz = config->max_transfer_sz, + }; + + esp_err_t ret = bsp_display_new(&bsp_disp_cfg, &s_panel_handle, &s_panel_io_handle); + if(ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to initialize BSP display: %s", esp_err_to_name(ret)); + return ret; + } + + // Return handles to caller + *panel_handle = s_panel_handle; + *panel_io_handle = s_panel_io_handle; + + ESP_LOGI(TAG, "ESP-Box-3 display initialized: %dx%d", config->width, config->height); + + return ESP_OK; +} + +static esp_err_t esp_box_3_backlight_on(void) +{ + ESP_LOGD(TAG, "Turning on backlight"); + return bsp_display_backlight_on(); +} + +static esp_err_t esp_box_3_backlight_off(void) +{ + ESP_LOGD(TAG, "Turning off backlight"); + return bsp_display_backlight_off(); +} + +static esp_err_t esp_box_3_display_on_off(bool enable) +{ + ESP_LOGD(TAG, "%s display", enable ? "Enabling" : "Disabling"); + if(s_panel_handle) { + return esp_lcd_panel_disp_on_off(s_panel_handle, enable); + } + return ESP_ERR_INVALID_STATE; +} + +static esp_err_t esp_box_3_touch_init(void) +{ +#if BSP_CAPS_TOUCH == 1 + ESP_LOGI(TAG, "Initializing touch interface"); + esp_lcd_touch_handle_t touch_handle = NULL; + esp_err_t ret = bsp_touch_new(NULL, &touch_handle); + if(ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to initialize touch: %s", esp_err_to_name(ret)); + return ret; + } + + // TODO: Store touch_handle for later use in esp_bsp_sdl_board_touch_read() + ESP_LOGI(TAG, "Touch interface initialized successfully"); + return ESP_OK; +#else + ESP_LOGW(TAG, "Touch not supported on this board configuration"); + return ESP_ERR_NOT_SUPPORTED; +#endif +} + +static esp_err_t esp_box_3_touch_read(esp_bsp_sdl_touch_info_t *touch_info) +{ +#if BSP_CAPS_TOUCH == 1 + if(!touch_info) { + return ESP_ERR_INVALID_ARG; + } + + // This is a simplified implementation - in a real implementation, + // you would store the touch handle and use it here + touch_info->pressed = false; + touch_info->x = 0; + touch_info->y = 0; + + // TODO: Implement actual touch reading + // esp_lcd_touch_read_data(touch_handle); + + return ESP_OK; +#else + return ESP_ERR_NOT_SUPPORTED; +#endif +} + +static const char *esp_box_3_get_name(void) +{ + return "ESP-Box-3"; +} + +static esp_err_t esp_box_3_deinit(void) +{ + ESP_LOGI(TAG, "Deinitializing ESP-Box-3"); + + // Clean up resources if needed + if(s_panel_handle) { + // Note: BSP typically handles cleanup, but we could add specific cleanup here + s_panel_handle = NULL; + } + + if(s_panel_io_handle) { + s_panel_io_handle = NULL; + } + + return ESP_OK; +} + +// ESP-Box-3 board interface +const esp_bsp_sdl_board_interface_t esp_bsp_sdl_esp_box_3_interface = {.init = esp_box_3_init, + .backlight_on = esp_box_3_backlight_on, + .backlight_off = esp_box_3_backlight_off, + .display_on_off = esp_box_3_display_on_off, + .touch_init = esp_box_3_touch_init, + .touch_read = esp_box_3_touch_read, + .get_name = esp_box_3_get_name, + .deinit = esp_box_3_deinit, + .board_name = "ESP32-S3-BOX-3"}; diff --git a/components/georgik__sdl_bsp/src/boards/esp_bsp_sdl_esp_bsp_devkit.c b/components/georgik__sdl_bsp/src/boards/esp_bsp_sdl_esp_bsp_devkit.c new file mode 100644 index 0000000..3e4f355 --- /dev/null +++ b/components/georgik__sdl_bsp/src/boards/esp_bsp_sdl_esp_bsp_devkit.c @@ -0,0 +1,135 @@ +/** + * @file esp_bsp_sdl_esp_bsp_devkit.c + * @brief ESP BSP DevKit specific implementation for ESP-BSP SDL abstraction layer + * Uses esp_bsp_devkit for basic DevKit support (LEDs, buttons, no display) + * Note: This creates a virtual display for SDL since DevKit BSP has no display + */ + +#include "esp_bsp_devkit.h" +#include "esp_bsp_sdl.h" +#include "esp_err.h" +#include "esp_log.h" +#include "sdkconfig.h" + +// SDL pixel format constants - using direct values to avoid SDL dependency +#define SDL_PIXELFORMAT_RGB565 0x15151002u + +// Virtual display dimensions for DevKit (no physical display) +#define VIRTUAL_DISPLAY_WIDTH 240 +#define VIRTUAL_DISPLAY_HEIGHT 320 + +static const char *TAG = "esp_bsp_sdl_esp_bsp_devkit"; + +esp_err_t esp_bsp_sdl_board_init(esp_bsp_sdl_display_config_t *config, + esp_lcd_panel_handle_t *panel_handle, + esp_lcd_panel_io_handle_t *panel_io_handle) +{ + ESP_LOGI(TAG, "Initializing ESP BSP DevKit (LEDs/Buttons, No Display)"); + + if(!config || !panel_handle || !panel_io_handle) { + return ESP_ERR_INVALID_ARG; + } + + esp_err_t ret = ESP_OK; + + // DevKit BSP has no display - create virtual display configuration for SDL + config->width = VIRTUAL_DISPLAY_WIDTH; + config->height = VIRTUAL_DISPLAY_HEIGHT; + config->pixel_format = SDL_PIXELFORMAT_RGB565; + config->max_transfer_sz = (config->width * config->height) * sizeof(uint16_t); + config->has_touch = false; // DevKit BSP doesn't support touch + + ESP_LOGI(TAG, "Virtual display configured: %dx%d (no physical display)", config->width, config->height); + + // Initialize DevKit BSP features (LEDs, buttons, file systems) + ESP_LOGI(TAG, "Initializing DevKit BSP features..."); + + // Initialize LEDs if configured +#ifdef CONFIG_BSP_DEVKIT_LED_ENABLED + ret = bsp_leds_init(); + if(ret == ESP_OK) { + ESP_LOGI(TAG, "LEDs initialized successfully"); + } else { + ESP_LOGW(TAG, "LED initialization failed: %s", esp_err_to_name(ret)); + } +#endif + + // Initialize buttons if configured +#ifdef CONFIG_BSP_DEVKIT_BUTTON_ENABLED + ret = bsp_iot_button_create(); + if(ret == ESP_OK) { + ESP_LOGI(TAG, "Buttons initialized successfully"); + } else { + ESP_LOGW(TAG, "Button initialization failed: %s", esp_err_to_name(ret)); + } +#endif + + // Initialize file systems if configured +#ifdef CONFIG_BSP_DEVKIT_SPIFFS_ENABLED + ret = bsp_spiffs_mount(); + if(ret == ESP_OK) { + ESP_LOGI(TAG, "SPIFFS mounted successfully"); + } else { + ESP_LOGW(TAG, "SPIFFS mount failed: %s", esp_err_to_name(ret)); + } +#endif + +#ifdef CONFIG_BSP_DEVKIT_USD_ENABLED + // Note: uSD card initialization would be done here if configured + ESP_LOGI(TAG, "uSD card support configured (mount on demand)"); +#endif + + // No physical display handles for DevKit + *panel_handle = NULL; + *panel_io_handle = NULL; + + ESP_LOGI(TAG, "ESP BSP DevKit initialized (virtual display: %dx%d)", config->width, config->height); + + return ESP_OK; +} + +esp_err_t esp_bsp_sdl_board_backlight_on(void) +{ + ESP_LOGW(TAG, "DevKit has no backlight - ignoring backlight_on request"); + return ESP_OK; // Return success since this is expected for DevKit +} + +int esp_bsp_sdl_board_backlight_off(void) +{ + ESP_LOGW(TAG, "DevKit has no backlight - ignoring backlight_off request"); + return ESP_OK; // Return success since this is expected for DevKit +} + +esp_err_t esp_bsp_sdl_board_display_on_off(bool enable) +{ + ESP_LOGD(TAG, "%s virtual display (no physical display on DevKit)", enable ? "Enabling" : "Disabling"); + return ESP_OK; // Always succeed for virtual display +} + +esp_err_t esp_bsp_sdl_board_touch_init(void) +{ + ESP_LOGI(TAG, "DevKit has no touch interface"); + return ESP_ERR_NOT_SUPPORTED; +} + +esp_err_t esp_bsp_sdl_board_touch_read(esp_bsp_sdl_touch_info_t *touch_info) +{ + return ESP_ERR_NOT_SUPPORTED; // DevKit has no touch +} + +const char *esp_bsp_sdl_board_get_name(void) +{ + return "ESP BSP DevKit (LEDs/Buttons)"; +} + +esp_err_t esp_bsp_sdl_board_deinit(void) +{ + ESP_LOGI(TAG, "Deinitializing ESP BSP DevKit"); + + // Clean up DevKit BSP resources if needed +#ifdef CONFIG_BSP_DEVKIT_SPIFFS_ENABLED + bsp_spiffs_unmount(); +#endif + + return ESP_OK; +} \ No newline at end of file diff --git a/components/georgik__sdl_bsp/src/boards/esp_bsp_sdl_esp_bsp_generic.c b/components/georgik__sdl_bsp/src/boards/esp_bsp_sdl_esp_bsp_generic.c new file mode 100644 index 0000000..b0fe914 --- /dev/null +++ b/components/georgik__sdl_bsp/src/boards/esp_bsp_sdl_esp_bsp_generic.c @@ -0,0 +1,175 @@ +/** + * @file esp_bsp_sdl_esp_bsp_generic.c + * @brief ESP BSP Generic specific implementation for ESP-BSP SDL abstraction layer + * Uses esp_bsp_generic for configurable DevKit + Display support + * Supports any ESP32 DevKit with custom display/touch configuration via menuconfig + */ + +#include "esp_bsp_generic.h" +#include "esp_bsp_sdl.h" +#include "esp_err.h" +#include "esp_log.h" +#include "sdkconfig.h" + +// SDL pixel format constants - using direct values to avoid SDL dependency +#define SDL_PIXELFORMAT_RGB565 0x15151002u + +static const char *TAG = "esp_bsp_sdl_esp_bsp_generic"; +static esp_lcd_panel_handle_t s_panel_handle = NULL; +static esp_lcd_panel_io_handle_t s_panel_io_handle = NULL; + +esp_err_t esp_bsp_sdl_board_init(esp_bsp_sdl_display_config_t *config, + esp_lcd_panel_handle_t *panel_handle, + esp_lcd_panel_io_handle_t *panel_io_handle) +{ + ESP_LOGI(TAG, "Initializing ESP BSP Generic (Configurable DevKit)"); + + if(!config || !panel_handle || !panel_io_handle) { + return ESP_ERR_INVALID_ARG; + } + + esp_err_t ret = ESP_OK; + + // Step 1: Get display configuration from BSP Generic + // The actual resolution and settings are configured via menuconfig +#ifdef CONFIG_BSP_GENERIC_DISPLAY_ENABLED + // Read configuration from BSP Generic settings + config->width = CONFIG_BSP_GENERIC_DISPLAY_WIDTH; + config->height = CONFIG_BSP_GENERIC_DISPLAY_HEIGHT; + config->pixel_format = SDL_PIXELFORMAT_RGB565; + config->max_transfer_sz = (config->width * config->height) * sizeof(uint16_t); + config->has_touch = CONFIG_BSP_GENERIC_TOUCH_ENABLED; + + ESP_LOGI(TAG, + "Display configured: %dx%d, Touch: %s", + config->width, + config->height, + config->has_touch ? "enabled" : "disabled"); + + // Step 2: Initialize BSP Generic display + ESP_LOGI(TAG, "Initializing display via BSP Generic..."); + ret = bsp_display_new(NULL, &s_panel_handle, &s_panel_io_handle); + if(ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to initialize BSP Generic display: %s", esp_err_to_name(ret)); + return ret; + } + + // Step 3: Turn on the display + ESP_LOGI(TAG, "Enabling display..."); + ret = esp_lcd_panel_disp_on_off(s_panel_handle, true); + if(ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to turn on display: %s", esp_err_to_name(ret)); + return ret; + } + + // Step 4: Initialize backlight if available + ESP_LOGI(TAG, "Initializing backlight control..."); + ret = bsp_display_brightness_init(); + if(ret == ESP_OK) { + ret = bsp_display_backlight_on(); + if(ret != ESP_OK) { + ESP_LOGW(TAG, "Backlight control failed: %s", esp_err_to_name(ret)); + } + } else { + ESP_LOGI(TAG, "No backlight control configured"); + } + + *panel_handle = s_panel_handle; + *panel_io_handle = s_panel_io_handle; + +#else + // No display configured - this is unusual for SDL applications but supported + ESP_LOGW(TAG, "No display configured in BSP Generic - SDL will use virtual display"); + config->width = 240; // Default fallback size + config->height = 320; + config->pixel_format = SDL_PIXELFORMAT_RGB565; + config->max_transfer_sz = (config->width * config->height) * sizeof(uint16_t); + config->has_touch = false; + + *panel_handle = NULL; + *panel_io_handle = NULL; +#endif + + ESP_LOGI(TAG, "ESP BSP Generic initialized: %dx%d", config->width, config->height); + + return ESP_OK; +} + +esp_err_t esp_bsp_sdl_board_backlight_on(void) +{ + ESP_LOGI(TAG, "Turning backlight on"); + return bsp_display_backlight_on(); +} + +int esp_bsp_sdl_board_backlight_off(void) +{ + ESP_LOGI(TAG, "Turning backlight off"); + return bsp_display_backlight_off(); +} + +esp_err_t esp_bsp_sdl_board_display_on_off(bool enable) +{ + ESP_LOGD(TAG, "%s display", enable ? "Enabling" : "Disabling"); + if(s_panel_handle) { + return esp_lcd_panel_disp_on_off(s_panel_handle, enable); + } + return ESP_ERR_INVALID_STATE; +} + +esp_err_t esp_bsp_sdl_board_touch_init(void) +{ +#ifdef CONFIG_BSP_GENERIC_TOUCH_ENABLED + ESP_LOGI(TAG, "Initializing touch interface"); + + esp_err_t ret = bsp_touch_new(NULL, NULL); + if(ret == ESP_OK) { + ESP_LOGI(TAG, "Touch interface initialized successfully"); + return ESP_OK; + } else { + ESP_LOGW(TAG, "Touch initialization failed: %s", esp_err_to_name(ret)); + return ret; + } +#else + ESP_LOGI(TAG, "Touch not configured in BSP Generic"); + return ESP_ERR_NOT_SUPPORTED; +#endif +} + +esp_err_t esp_bsp_sdl_board_touch_read(esp_bsp_sdl_touch_info_t *touch_info) +{ +#ifdef CONFIG_BSP_GENERIC_TOUCH_ENABLED + if(!touch_info) { + return ESP_ERR_INVALID_ARG; + } + + // TODO: Implement touch reading for BSP Generic + // This would need to be implemented based on the actual touch driver configured + touch_info->pressed = false; + touch_info->x = 0; + touch_info->y = 0; + + return ESP_ERR_NOT_SUPPORTED; // Not yet implemented +#else + return ESP_ERR_NOT_SUPPORTED; +#endif +} + +const char *esp_bsp_sdl_board_get_name(void) +{ + return "ESP BSP Generic (Configurable)"; +} + +esp_err_t esp_bsp_sdl_board_deinit(void) +{ + ESP_LOGI(TAG, "Deinitializing ESP BSP Generic"); + + if(s_panel_handle) { + s_panel_handle = NULL; + } + + if(s_panel_io_handle) { + s_panel_io_handle = NULL; + } + + return ESP_OK; +} \ No newline at end of file diff --git a/components/georgik__sdl_bsp/src/boards/esp_bsp_sdl_m5_atom_s3.c b/components/georgik__sdl_bsp/src/boards/esp_bsp_sdl_m5_atom_s3.c new file mode 100644 index 0000000..e49c9e9 --- /dev/null +++ b/components/georgik__sdl_bsp/src/boards/esp_bsp_sdl_m5_atom_s3.c @@ -0,0 +1,173 @@ +/** + * @file esp_bsp_sdl_m5_atom_s3.c + * @brief M5 Atom S3 specific implementation for ESP-BSP SDL abstraction layer + * Uses official espressif/m5_atom_s3_noglib BSP + */ + +#include "esp_bsp_sdl.h" +#include "esp_err.h" +#include "esp_log.h" +#include "sdkconfig.h" + +// Include M5 Atom S3 BSP headers - only when this board is selected +#include "bsp/display.h" +// #include "bsp/m5_atom_s3.h" +#define BSP_CAPS_TOUCH 0 +// #include "esp_lcd_touch.h" + +// SDL pixel format constants - using direct values to avoid SDL dependency +#define SDL_PIXELFORMAT_RGB565 0x15151002u + +static const char *TAG = "esp_bsp_sdl_m5_atom_s3"; +static esp_lcd_panel_handle_t s_panel_handle = NULL; +static esp_lcd_panel_io_handle_t s_panel_io_handle = NULL; + +static esp_err_t m5_atom_s3_init(esp_bsp_sdl_display_config_t *config, + esp_lcd_panel_handle_t *panel_handle, + esp_lcd_panel_io_handle_t *panel_io_handle) +{ + ESP_LOGI(TAG, "Initializing M5 Atom S3 display using BSP"); + + if(!config || !panel_handle || !panel_io_handle) { + return ESP_ERR_INVALID_ARG; + } + + esp_err_t ret = ESP_OK; + + // Step 1: Fill in display configuration for M5 Atom S3 (128x128) + config->width = 128; // M5 Atom S3 specific resolution + config->height = 128; // M5 Atom S3 specific resolution + config->pixel_format = SDL_PIXELFORMAT_RGB565; + config->max_transfer_sz = (128 * 128) * sizeof(uint16_t); + config->has_touch = BSP_CAPS_TOUCH == 1; + + // Step 2: Initialize backlight PWM control FIRST (M5 Atom S3 requires this) + ESP_LOGI(TAG, "Initializing backlight control..."); + ret = bsp_display_brightness_init(); + if(ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to initialize backlight PWM: %s", esp_err_to_name(ret)); + return ret; + } + + // Step 3: Initialize BSP display using the official BSP + ESP_LOGI(TAG, "Initializing display panel..."); + const bsp_display_config_t bsp_disp_cfg = { + .max_transfer_sz = config->max_transfer_sz, + }; + + ret = bsp_display_new(&bsp_disp_cfg, &s_panel_handle, &s_panel_io_handle); + if(ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to initialize BSP display: %s", esp_err_to_name(ret)); + return ret; + } + + // Step 4: Turn on the display + ESP_LOGI(TAG, "Enabling display..."); + ret = esp_lcd_panel_disp_on_off(s_panel_handle, true); + if(ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to turn on display: %s", esp_err_to_name(ret)); + return ret; + } + + // Step 5: Turn on backlight (M5 Atom S3 specific) + ESP_LOGI(TAG, "Turning on backlight..."); + ret = bsp_display_backlight_on(); + if(ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to turn on backlight: %s", esp_err_to_name(ret)); + return ret; + } + + // Return handles to caller + *panel_handle = s_panel_handle; + *panel_io_handle = s_panel_io_handle; + + ESP_LOGI(TAG, "M5 Atom S3 display initialized: %dx%d", config->width, config->height); + + return ESP_OK; +} + +static esp_err_t m5_atom_s3_backlight_on(void) +{ + ESP_LOGI(TAG, "M5 Atom S3: Turning backlight on"); + return bsp_display_backlight_on(); +} + +static esp_err_t m5_atom_s3_backlight_off(void) +{ + ESP_LOGI(TAG, "M5 Atom S3: Turning backlight off"); + return bsp_display_backlight_off(); +} + +static esp_err_t m5_atom_s3_display_on_off(bool enable) +{ + ESP_LOGD(TAG, "%s display", enable ? "Enabling" : "Disabling"); + if(s_panel_handle) { + return esp_lcd_panel_disp_on_off(s_panel_handle, enable); + } + return ESP_ERR_INVALID_STATE; +} + +static esp_err_t m5_atom_s3_touch_init(void) +{ +#if BSP_CAPS_TOUCH == 1 + ESP_LOGI(TAG, "Initializing touch interface"); + + // TODO: Implement touch initialization if M5 Atom S3 supports touch + // For now, just log that touch is theoretically supported but not implemented + ESP_LOGW(TAG, "Touch support detected in BSP but not yet implemented for M5 Atom S3"); + return ESP_ERR_NOT_SUPPORTED; +#else + ESP_LOGW(TAG, "Touch not supported on this board configuration"); + return ESP_ERR_NOT_SUPPORTED; +#endif +} + +static esp_err_t m5_atom_s3_touch_read(esp_bsp_sdl_touch_info_t *touch_info) +{ +#if BSP_CAPS_TOUCH == 1 + if(!touch_info) { + return ESP_ERR_INVALID_ARG; + } + + // TODO: Implement touch reading if M5 Atom S3 supports touch + touch_info->pressed = false; + touch_info->x = 0; + touch_info->y = 0; + + return ESP_ERR_NOT_SUPPORTED; +#else + return ESP_ERR_NOT_SUPPORTED; +#endif +} + +static const char *m5_atom_s3_get_name(void) +{ + return "M5 Atom S3"; +} + +static esp_err_t m5_atom_s3_deinit(void) +{ + ESP_LOGI(TAG, "Deinitializing M5 Atom S3"); + + // Clean up resources if needed + if(s_panel_handle) { + s_panel_handle = NULL; + } + + if(s_panel_io_handle) { + s_panel_io_handle = NULL; + } + + return ESP_OK; +} + +// M5 Atom S3 board interface +const esp_bsp_sdl_board_interface_t esp_bsp_sdl_m5_atom_s3_interface = {.init = m5_atom_s3_init, + .backlight_on = m5_atom_s3_backlight_on, + .backlight_off = m5_atom_s3_backlight_off, + .display_on_off = m5_atom_s3_display_on_off, + .touch_init = m5_atom_s3_touch_init, + .touch_read = m5_atom_s3_touch_read, + .get_name = m5_atom_s3_get_name, + .deinit = m5_atom_s3_deinit, + .board_name = "M5 Atom S3"}; diff --git a/components/georgik__sdl_bsp/src/boards/esp_bsp_sdl_m5stack_core_s3.c b/components/georgik__sdl_bsp/src/boards/esp_bsp_sdl_m5stack_core_s3.c new file mode 100644 index 0000000..d9377a5 --- /dev/null +++ b/components/georgik__sdl_bsp/src/boards/esp_bsp_sdl_m5stack_core_s3.c @@ -0,0 +1,190 @@ +/** + * @file esp_bsp_sdl_m5stack_core_s3.c + * @brief M5Stack CoreS3 implementation for ESP-BSP SDL abstraction layer + * Uses official espressif/m5stack_core_s3_noglib BSP + */ + +#include "esp_bsp_sdl.h" +#include "esp_err.h" +#include "esp_log.h" +#include "sdkconfig.h" + +// Include M5Stack Core S3 BSP headers - only when this board is selected +#include "bsp/m5stack_core_s3.h" +#include "bsp/touch.h" +#include "esp_lcd_touch.h" + +// SDL pixel format constants - using direct values to avoid SDL dependency +#define SDL_PIXELFORMAT_RGB565 0x15151002u + +static const char *TAG = "esp_bsp_sdl_m5stack_core_s3"; +static esp_lcd_panel_handle_t s_panel_handle = NULL; +static esp_lcd_panel_io_handle_t s_panel_io_handle = NULL; +static esp_lcd_touch_handle_t s_touch_handle = NULL; + +static esp_err_t m5stack_core_s3_init(esp_bsp_sdl_display_config_t *config, + esp_lcd_panel_handle_t *panel_handle, + esp_lcd_panel_io_handle_t *panel_io_handle) +{ + ESP_LOGI(TAG, "Initializing M5Stack Core S3 display using BSP"); + + if(!config || !panel_handle || !panel_io_handle) { + return ESP_ERR_INVALID_ARG; + } + + esp_err_t ret = ESP_OK; + + // Step 1: Fill in display configuration for M5Stack Core S3 (320x240) + config->width = 320; // M5Stack Core S3 specific resolution + config->height = 240; // M5Stack Core S3 specific resolution + config->pixel_format = SDL_PIXELFORMAT_RGB565; + config->max_transfer_sz = (320 * 240) * sizeof(uint16_t); + config->has_touch = BSP_CAPS_TOUCH == 1; + + // Step 2: Initialize backlight PWM control FIRST (M5Stack Core S3 requires this) + ESP_LOGI(TAG, "Initializing backlight control..."); + ret = bsp_display_brightness_init(); + if(ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to initialize backlight PWM: %s", esp_err_to_name(ret)); + return ret; + } + + // Step 3: Initialize BSP display using the official BSP + ESP_LOGI(TAG, "Initializing display panel..."); + const bsp_display_config_t bsp_disp_cfg = { + .max_transfer_sz = config->max_transfer_sz, + }; + + ret = bsp_display_new(&bsp_disp_cfg, &s_panel_handle, &s_panel_io_handle); + if(ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to initialize BSP display: %s", esp_err_to_name(ret)); + return ret; + } + + // Step 4: Turn on the display + ESP_LOGI(TAG, "Enabling display..."); + ret = esp_lcd_panel_disp_on_off(s_panel_handle, true); + if(ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to turn on display: %s", esp_err_to_name(ret)); + return ret; + } + + // Step 5: Turn on backlight (M5Stack Core S3 specific) + ESP_LOGI(TAG, "Turning on backlight..."); + ret = bsp_display_backlight_on(); + if(ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to turn on backlight: %s", esp_err_to_name(ret)); + return ret; + } + + // Return handles to caller + *panel_handle = s_panel_handle; + *panel_io_handle = s_panel_io_handle; + + ESP_LOGI(TAG, "M5Stack Core S3 display initialized: %dx%d", config->width, config->height); + + return ESP_OK; +} + +static esp_err_t m5stack_core_s3_backlight_on(void) +{ + ESP_LOGI(TAG, "M5Stack Core S3: Turning backlight on"); + return bsp_display_backlight_on(); +} + +static esp_err_t m5stack_core_s3_backlight_off(void) +{ + ESP_LOGI(TAG, "M5Stack Core S3: Turning backlight off"); + return bsp_display_backlight_off(); +} + +static esp_err_t m5stack_core_s3_display_on_off(bool enable) +{ + ESP_LOGD(TAG, "%s display", enable ? "Enabling" : "Disabling"); + if(s_panel_handle) { + return esp_lcd_panel_disp_on_off(s_panel_handle, enable); + } + return ESP_ERR_INVALID_STATE; +} + +static esp_err_t m5stack_core_s3_touch_init(void) +{ +#if BSP_CAPS_TOUCH == 1 + ESP_LOGI(TAG, "Initializing touch interface"); + + // M5Stack Core S3 has capacitive touch support + const bsp_touch_config_t touch_cfg = {.dummy = NULL}; + esp_err_t ret = bsp_touch_new(&touch_cfg, &s_touch_handle); + if(ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to initialize touch: %s", esp_err_to_name(ret)); + return ret; + } + + ESP_LOGI(TAG, "Touch interface initialized successfully"); + return ESP_OK; +#else + ESP_LOGW(TAG, "Touch not supported on this board configuration"); + return ESP_ERR_NOT_SUPPORTED; +#endif +} + +static esp_err_t m5stack_core_s3_touch_read(esp_bsp_sdl_touch_info_t *touch_info) +{ +#if BSP_CAPS_TOUCH == 1 + if(!touch_info || !s_touch_handle) { + return ESP_ERR_INVALID_ARG; + } + + // Read touch data using ESP-LCD touch API + uint16_t touch_x[1] = {0}; + uint16_t touch_y[1] = {0}; + uint8_t touch_cnt = 0; + + // First read the touch data + esp_lcd_touch_read_data(s_touch_handle); + + // Then get the coordinates + bool touched = esp_lcd_touch_get_coordinates(s_touch_handle, touch_x, touch_y, NULL, &touch_cnt, 1); + + touch_info->pressed = touched && (touch_cnt > 0); + touch_info->x = touch_info->pressed ? (int) touch_x[0] : 0; + touch_info->y = touch_info->pressed ? (int) touch_y[0] : 0; + + return ESP_OK; +#else + return ESP_ERR_NOT_SUPPORTED; +#endif +} + +static const char *m5stack_core_s3_get_name(void) +{ + return "M5Stack Core S3"; +} + +static esp_err_t m5stack_core_s3_deinit(void) +{ + ESP_LOGI(TAG, "Deinitializing M5Stack Core S3"); + + // Clean up resources if needed + if(s_panel_handle) { + s_panel_handle = NULL; + } + + if(s_panel_io_handle) { + s_panel_io_handle = NULL; + } + + return ESP_OK; +} + +// M5Stack CoreS3 board interface +const esp_bsp_sdl_board_interface_t esp_bsp_sdl_m5stack_core_s3_interface = { + .init = m5stack_core_s3_init, + .backlight_on = m5stack_core_s3_backlight_on, + .backlight_off = m5stack_core_s3_backlight_off, + .display_on_off = m5stack_core_s3_display_on_off, + .touch_init = m5stack_core_s3_touch_init, + .touch_read = m5stack_core_s3_touch_read, + .get_name = m5stack_core_s3_get_name, + .deinit = m5stack_core_s3_deinit, + .board_name = "M5Stack CoreS3"}; diff --git a/components/georgik__sdl_bsp/src/boards/esp_bsp_sdl_m5stack_tab5.c b/components/georgik__sdl_bsp/src/boards/esp_bsp_sdl_m5stack_tab5.c new file mode 100644 index 0000000..822af34 --- /dev/null +++ b/components/georgik__sdl_bsp/src/boards/esp_bsp_sdl_m5stack_tab5.c @@ -0,0 +1,243 @@ +/** + * @file esp_bsp_sdl_m5stack_tab5.c + * @brief M5Stack Tab5 ESP32-P4 specific implementation for ESP-BSP SDL abstraction layer + * Uses M5Stack Tab5 BSP with NOGLIB mode for SDL compatibility + * + * IMPORTANT: M5Stack Tab5 requires 200MHz PSRAM speed for proper operation! + */ + +#include "esp_bsp_sdl.h" +#include "esp_err.h" +#include "esp_log.h" +#include "sdkconfig.h" + +// Include M5Stack Tab5 BSP headers - only when this board is selected +#include "bsp/display.h" +#include "bsp/m5stack_tab5.h" + +#if CONFIG_SDL_BSP_TOUCH_ENABLE +# include "bsp/touch.h" +# include "esp_lcd_touch.h" +#endif + +// SDL pixel format constants - using direct values to avoid SDL dependency +#define SDL_PIXELFORMAT_RGB565 0x15151002u +#define SDL_PIXELFORMAT_RGB888 0x16161804u + +static const char *TAG = "esp_bsp_sdl_m5stack_tab5"; +static esp_lcd_panel_handle_t s_panel_handle = NULL; +static esp_lcd_panel_io_handle_t s_panel_io_handle = NULL; + +#if CONFIG_SDL_BSP_TOUCH_ENABLE +static esp_lcd_touch_handle_t s_touch_handle = NULL; +#endif + +static esp_err_t m5stack_tab5_init(esp_bsp_sdl_display_config_t *config, + esp_lcd_panel_handle_t *panel_handle, + esp_lcd_panel_io_handle_t *panel_io_handle) +{ + ESP_LOGI(TAG, "Initializing M5Stack Tab5 display using BSP"); + ESP_LOGW(TAG, "CRITICAL: Ensure 200MHz PSRAM is configured for proper operation!"); + + if(!config || !panel_handle || !panel_io_handle) { + return ESP_ERR_INVALID_ARG; + } + + esp_err_t ret = ESP_OK; + + // Step 1: Fill in display configuration for M5Stack Tab5 + // Native resolution is 720x1280 (portrait), but we use landscape 1280x720 for SDL + config->width = 1280; // Landscape width (rotated) + config->height = 720; // Landscape height (rotated) + + // M5Stack Tab5 uses RGB565 format for MIPI-DSI + config->pixel_format = SDL_PIXELFORMAT_RGB565; + config->max_transfer_sz = (config->width * config->height) * 2; // 2 bytes per pixel for RGB565 + +#if CONFIG_SDL_BSP_TOUCH_ENABLE + config->has_touch = BSP_CAPS_TOUCH == 1; +#else + config->has_touch = false; +#endif + + // Step 2: Initialize BSP display using the official M5Stack Tab5 BSP + ESP_LOGI(TAG, "Initializing MIPI-DSI display panel (%dx%d)...", config->width, config->height); + + // Use bsp_display_new_with_handles for complete LCD handles + bsp_lcd_handles_t lcd_handles; + const bsp_display_config_t bsp_disp_cfg = { + // M5Stack Tab5 BSP handles the MIPI-DSI configuration internally + }; + + ret = bsp_display_new_with_handles(&bsp_disp_cfg, &lcd_handles); + if(ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to initialize BSP display: %s", esp_err_to_name(ret)); + return ret; + } + + s_panel_handle = lcd_handles.panel; + s_panel_io_handle = lcd_handles.io; + + // Step 3: MIPI-DSI panels are typically always on after initialization + ESP_LOGI(TAG, "MIPI-DSI display is ready..."); + + // Step 4: Turn on backlight if supported + ESP_LOGI(TAG, "Turning on backlight..."); + ret = bsp_display_brightness_init(); + if(ret == ESP_OK) { + ret = bsp_display_backlight_on(); + if(ret != ESP_OK) { + ESP_LOGW(TAG, "Failed to turn on backlight: %s", esp_err_to_name(ret)); + // Don't fail initialization if backlight fails + } + } else { + ESP_LOGW(TAG, "Backlight initialization failed: %s", esp_err_to_name(ret)); + // Don't fail initialization if backlight init fails + } + + // Return handles to caller + *panel_handle = s_panel_handle; + *panel_io_handle = s_panel_io_handle; + + ESP_LOGI(TAG, "M5Stack Tab5 display initialized: %dx%d (landscape mode)", config->width, config->height); + ESP_LOGI(TAG, "Display features: MIPI-DSI, RGB565, %s", config->has_touch ? "Touch enabled" : "Touch disabled"); + + return ESP_OK; +} + +static esp_err_t m5stack_tab5_backlight_on(void) +{ + ESP_LOGI(TAG, "M5Stack Tab5: Turning backlight on"); + esp_err_t ret = bsp_display_backlight_on(); + if(ret != ESP_OK) { + ESP_LOGW(TAG, "Backlight control not supported: %s", esp_err_to_name(ret)); + } + return ret; +} + +static esp_err_t m5stack_tab5_backlight_off(void) +{ + ESP_LOGI(TAG, "M5Stack Tab5: Turning backlight off"); + esp_err_t ret = bsp_display_backlight_off(); + if(ret != ESP_OK) { + ESP_LOGW(TAG, "Backlight control not supported: %s", esp_err_to_name(ret)); + } + return ret; +} + +static esp_err_t m5stack_tab5_display_on_off(bool enable) +{ + ESP_LOGD(TAG, "%s display", enable ? "Enabling" : "Disabling"); + // MIPI-DSI displays are typically always on, but try the BSP function if available + if(s_panel_handle) { + return esp_lcd_panel_disp_on_off(s_panel_handle, enable); + } + return ESP_OK; +} + +static esp_err_t m5stack_tab5_touch_init(void) +{ +#if CONFIG_SDL_BSP_TOUCH_ENABLE + ESP_LOGI(TAG, "Initializing GT911 touch interface"); + + const bsp_touch_config_t touch_cfg = { + // M5Stack Tab5 BSP handles GT911 configuration internally + }; + esp_err_t ret = bsp_touch_new(&touch_cfg, &s_touch_handle); + if(ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to initialize touch: %s", esp_err_to_name(ret)); + return ret; + } + + ESP_LOGI(TAG, "GT911 touch interface initialized successfully"); + return ESP_OK; +#else + ESP_LOGW(TAG, "Touch support disabled via configuration"); + return ESP_ERR_NOT_SUPPORTED; +#endif +} + +static esp_err_t m5stack_tab5_touch_read(esp_bsp_sdl_touch_info_t *touch_info) +{ +#if CONFIG_SDL_BSP_TOUCH_ENABLE + if(!touch_info) { + return ESP_ERR_INVALID_ARG; + } + + if(!s_touch_handle) { + ESP_LOGW(TAG, "Touch not initialized"); + touch_info->pressed = false; + touch_info->x = 0; + touch_info->y = 0; + return ESP_ERR_INVALID_STATE; + } + + uint16_t touch_x[1] = {0}; + uint16_t touch_y[1] = {0}; + uint16_t touch_strength[1] = {0}; + uint8_t touch_cnt = 0; + + esp_err_t ret = esp_lcd_touch_read_data(s_touch_handle); + if(ret == ESP_OK) { + bool pressed = esp_lcd_touch_get_coordinates(s_touch_handle, touch_x, touch_y, touch_strength, &touch_cnt, 1); + if(pressed && touch_cnt > 0) { + touch_info->pressed = true; + // Convert from native portrait 720x1280 to landscape 1280x720 + // Rotate coordinates 90 degrees clockwise + touch_info->x = touch_y[0] * 1280 / 720; // Scale Y to X + touch_info->y = 720 - (touch_x[0] * 720 / 1280); // Scale and flip X to Y + } else { + touch_info->pressed = false; + touch_info->x = 0; + touch_info->y = 0; + } + } else { + touch_info->pressed = false; + touch_info->x = 0; + touch_info->y = 0; + } + + return ESP_OK; +#else + return ESP_ERR_NOT_SUPPORTED; +#endif +} + +static const char *m5stack_tab5_get_name(void) +{ + return "M5Stack Tab5"; +} + +static esp_err_t m5stack_tab5_deinit(void) +{ + ESP_LOGI(TAG, "Deinitializing M5Stack Tab5"); + + // Clean up touch resources +#if CONFIG_SDL_BSP_TOUCH_ENABLE + if(s_touch_handle) { + s_touch_handle = NULL; + } +#endif + + // Clean up display resources + if(s_panel_handle) { + s_panel_handle = NULL; + } + + if(s_panel_io_handle) { + s_panel_io_handle = NULL; + } + + return ESP_OK; +} + +// M5Stack Tab5 board interface +const esp_bsp_sdl_board_interface_t esp_bsp_sdl_m5stack_tab5_interface = {.init = m5stack_tab5_init, + .backlight_on = m5stack_tab5_backlight_on, + .backlight_off = m5stack_tab5_backlight_off, + .display_on_off = m5stack_tab5_display_on_off, + .touch_init = m5stack_tab5_touch_init, + .touch_read = m5stack_tab5_touch_read, + .get_name = m5stack_tab5_get_name, + .deinit = m5stack_tab5_deinit, + .board_name = "M5Stack Tab5"}; \ No newline at end of file diff --git a/components/georgik__sdl_bsp/src/esp_bsp_sdl_common.c b/components/georgik__sdl_bsp/src/esp_bsp_sdl_common.c new file mode 100644 index 0000000..d57623e --- /dev/null +++ b/components/georgik__sdl_bsp/src/esp_bsp_sdl_common.c @@ -0,0 +1,150 @@ +/** + * @file esp_bsp_sdl_common.c + * @brief Runtime board selection for ESP-BSP SDL abstraction layer + */ + +#include "esp_bsp_sdl.h" +#include "esp_log.h" +#include "sdkconfig.h" + +static const char *TAG = "esp_bsp_sdl"; + +// Board interface structure is defined in esp_bsp_sdl.h + +// Conditional forward declarations for board implementations based on what's being compiled +#ifdef CONFIG_SDL_BSP_M5_ATOM_S3 +extern const esp_bsp_sdl_board_interface_t esp_bsp_sdl_m5_atom_s3_interface; +#endif +#ifdef CONFIG_SDL_BSP_ESP_BOX_3 +extern const esp_bsp_sdl_board_interface_t esp_bsp_sdl_esp_box_3_interface; +#endif +#ifdef CONFIG_SDL_BSP_M5STACK_CORE_S3 +extern const esp_bsp_sdl_board_interface_t esp_bsp_sdl_m5stack_core_s3_interface; +#endif +#ifdef CONFIG_SDL_BSP_ESP32_P4_FUNCTION_EV +extern const esp_bsp_sdl_board_interface_t esp_bsp_sdl_esp32_p4_function_ev_interface; +#endif +#ifdef CONFIG_SDL_BSP_ESP32_S3_LCD_EV_BOARD +extern const esp_bsp_sdl_board_interface_t esp_bsp_sdl_esp32_s3_lcd_ev_board_interface; +#endif +#ifdef CONFIG_SDL_BSP_M5STACK_TAB5 +extern const esp_bsp_sdl_board_interface_t esp_bsp_sdl_m5stack_tab5_interface; +#endif + +static const esp_bsp_sdl_board_interface_t *s_current_board = NULL; + +// Runtime board detection based on Kconfig +static const esp_bsp_sdl_board_interface_t *detect_board(void) +{ +#ifdef CONFIG_SDL_BSP_M5_ATOM_S3 + ESP_LOGI(TAG, "Detected board: M5 Atom S3"); + return &esp_bsp_sdl_m5_atom_s3_interface; +#elif CONFIG_SDL_BSP_ESP_BOX_3 + ESP_LOGI(TAG, "Detected board: ESP32-S3-BOX-3"); + return &esp_bsp_sdl_esp_box_3_interface; +#elif CONFIG_SDL_BSP_M5STACK_CORE_S3 + ESP_LOGI(TAG, "Detected board: M5Stack CoreS3"); + return &esp_bsp_sdl_m5stack_core_s3_interface; +#elif CONFIG_SDL_BSP_ESP32_P4_FUNCTION_EV + ESP_LOGI(TAG, "Detected board: ESP32-P4 Function EV Board"); + return &esp_bsp_sdl_esp32_p4_function_ev_interface; +#elif CONFIG_SDL_BSP_ESP32_S3_LCD_EV_BOARD + ESP_LOGI(TAG, "Detected board: ESP32-S3-LCD-EV-Board"); + return &esp_bsp_sdl_esp32_s3_lcd_ev_board_interface; +#elif CONFIG_SDL_BSP_M5STACK_TAB5 + ESP_LOGI(TAG, "Detected board: M5Stack Tab5"); + return &esp_bsp_sdl_m5stack_tab5_interface; +#else + ESP_LOGE(TAG, "No board configuration detected!"); + return NULL; +#endif +} + +esp_err_t esp_bsp_sdl_init(esp_bsp_sdl_display_config_t *config, + esp_lcd_panel_handle_t *panel_handle, + esp_lcd_panel_io_handle_t *panel_io_handle) +{ + ESP_LOGI(TAG, "Initializing ESP-BSP SDL abstraction layer"); + + // Detect and select the board at runtime + s_current_board = detect_board(); + if(!s_current_board) { + ESP_LOGE(TAG, "Failed to detect board configuration"); + return ESP_ERR_NOT_SUPPORTED; + } + + ESP_LOGI(TAG, "Selected board: %s", s_current_board->board_name); + + return s_current_board->init(config, panel_handle, panel_io_handle); +} + +esp_err_t esp_bsp_sdl_backlight_on(void) +{ + if(!s_current_board) { + ESP_LOGE(TAG, "Board not initialized"); + return ESP_ERR_INVALID_STATE; + } + return s_current_board->backlight_on(); +} + +esp_err_t esp_bsp_sdl_backlight_off(void) +{ + if(!s_current_board) { + ESP_LOGE(TAG, "Board not initialized"); + return ESP_ERR_INVALID_STATE; + } + return s_current_board->backlight_off(); +} + +esp_err_t esp_bsp_sdl_display_on_off(bool enable) +{ + if(!s_current_board) { + ESP_LOGE(TAG, "Board not initialized"); + return ESP_ERR_INVALID_STATE; + } + return s_current_board->display_on_off(enable); +} + +esp_err_t esp_bsp_sdl_touch_init(void) +{ + if(!s_current_board) { + ESP_LOGE(TAG, "Board not initialized"); + return ESP_ERR_INVALID_STATE; + } + return s_current_board->touch_init(); +} + +esp_err_t esp_bsp_sdl_touch_read(esp_bsp_sdl_touch_info_t *touch_info) +{ + if(!touch_info) { + return ESP_ERR_INVALID_ARG; + } + + if(!s_current_board) { + ESP_LOGE(TAG, "Board not initialized"); + return ESP_ERR_INVALID_STATE; + } + + return s_current_board->touch_read(touch_info); +} + +const char *esp_bsp_sdl_get_board_name(void) +{ + if(!s_current_board) { + return "Unknown"; + } + return s_current_board->board_name; +} + +esp_err_t esp_bsp_sdl_deinit(void) +{ + ESP_LOGI(TAG, "Deinitializing ESP-BSP SDL abstraction layer"); + + if(!s_current_board) { + return ESP_OK; + } + + esp_err_t ret = s_current_board->deinit(); + s_current_board = NULL; + return ret; +} diff --git a/components/georgik__sdl_bsp/support/format.sh b/components/georgik__sdl_bsp/support/format.sh new file mode 100755 index 0000000..6b23870 --- /dev/null +++ b/components/georgik__sdl_bsp/support/format.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# Format all C and header files in the project using clang-format +# Excludes build directories and other generated files + +set -e + +# Check if clang-format is installed +if ! command -v clang-format &> /dev/null; then + echo "Error: clang-format is not installed." + echo "Install it with: brew install clang-format" + exit 1 +fi + +echo "Formatting C/C++ files with clang-format..." + +# Find and format all C/C++ files, excluding build directories +find . \( -name "*.c" -o -name "*.h" -o -name "*.cpp" -o -name "*.hpp" \) \ + -not -path "./build*" \ + -not -path "./managed_components/*" \ + -not -path "./.git/*" \ + -exec clang-format -i {} \; + +echo "✅ All C/C++ files have been formatted!" \ No newline at end of file diff --git a/main/idf_component.yml b/main/idf_component.yml index 74c6262..541f619 100644 --- a/main/idf_component.yml +++ b/main/idf_component.yml @@ -9,7 +9,7 @@ dependencies: ## Required IDF version idf: - version: '>=6.0' + version: '>=6.1' # BSP - information reguired for SDL espressif/esp-box-3_noglib: diff --git a/sdkconfig.defaults.esp32_p4_function_ev_board b/sdkconfig.defaults.esp32_p4_function_ev_board index b1a293d..5a08fc4 100644 --- a/sdkconfig.defaults.esp32_p4_function_ev_board +++ b/sdkconfig.defaults.esp32_p4_function_ev_board @@ -1,5 +1,5 @@ # This file was generated using idf.py save-defconfig. It can be edited manually. -# Espressif IoT Development Framework (ESP-IDF) 5.4.0 Project Minimal Configuration +# Espressif IoT Development Framework (ESP-IDF) 6.1 Project Minimal Configuration # # Board selection @@ -30,6 +30,9 @@ CONFIG_BSP_LCD_COLOR_FORMAT_RGB565=y CONFIG_BSP_LCD_TYPE_1024_600=y # CONFIG_BSP_LCD_TYPE_1280_800 is not set +# SD Card configuration for ESP32-P4 Function EV Board +CONFIG_BSP_SD_MOUNT_POINT="/sdcard" + # Required for TTF rendering if it happens in main task CONFIG_ESP_MAIN_TASK_STACK_SIZE=32000 @@ -37,6 +40,7 @@ CONFIG_ESP_MAIN_TASK_STACK_SIZE=32000 CONFIG_USB_HOST_HUBS_SUPPORTED=y # ESP32-P4-Ev-Board - v1.4 - with newer P4 this could be removed -CONFIG_ESP32P4_SELECTS_REV_LESS_V3=y -CONFIG_ESP32P4_REV_MIN_100=y +#CONFIG_ESP32P4_SELECTS_REV_LESS_V3=y +#CONFIG_ESP32P4_REV_MIN_100=y +