diff --git a/components/esp_driver_cam/Kconfig b/components/esp_driver_cam/Kconfig index e162894c33..378c1d33da 100644 --- a/components/esp_driver_cam/Kconfig +++ b/components/esp_driver_cam/Kconfig @@ -1,6 +1,6 @@ menu "ESP-Driver:Camera Controller Configurations" - depends on SOC_MIPI_CSI_SUPPORTED || SOC_LCDCAM_CAM_SUPPORTED + depends on SOC_MIPI_CSI_SUPPORTED || SOC_ISP_DVP_SUPPORTED || SOC_LCDCAM_CAM_SUPPORTED config CAM_CTLR_MIPI_CSI_ISR_CACHE_SAFE bool "CSI ISR Cache-Safe" @@ -16,6 +16,7 @@ menu "ESP-Driver:Camera Controller Configurations" config CAM_CTLR_ISP_DVP_ISR_CACHE_SAFE # IDF-10093 bool "ISP_DVP ISR Cache-Safe" + depends on SOC_ISP_DVP_SUPPORTED default n select DW_GDMA_ISR_IRAM_SAFE select DW_GDMA_CTRL_FUNC_IN_IRAM diff --git a/components/esp_driver_cam/dvp/src/esp_cam_ctlr_dvp_cam.c b/components/esp_driver_cam/dvp/src/esp_cam_ctlr_dvp_cam.c index 26cdb6a0e2..ea16e83039 100644 --- a/components/esp_driver_cam/dvp/src/esp_cam_ctlr_dvp_cam.c +++ b/components/esp_driver_cam/dvp/src/esp_cam_ctlr_dvp_cam.c @@ -26,12 +26,24 @@ #include "../../dvp_share_ctrl.h" #ifdef CONFIG_CAM_CTLR_DVP_CAM_ISR_CACHE_SAFE -#define CAM_DVP_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT) +#define DVP_CAM_CTLR_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT) #else -#define CAM_DVP_MEM_ALLOC_CAPS (MALLOC_CAP_DEFAULT) +#define DVP_CAM_CTLR_ALLOC_CAPS (MALLOC_CAP_DEFAULT) #endif -#define ALIGN_UP_BY(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) +#if CONFIG_SPIRAM +#define DVP_CAM_BK_BUFFER_ALLOC_CAPS (MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA) +#else +#define DVP_CAM_BK_BUFFER_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA) +#endif + +#if SOC_PERIPH_CLK_CTRL_SHARED +#define DVP_CAM_CLK_ATOMIC() PERIPH_RCC_ATOMIC() +#else +#define DVP_CAM_CLK_ATOMIC() +#endif + +#define ALIGN_UP_BY(num, align) ((align) == 0 ? (num) : (((num) + ((align) - 1)) & ~((align) - 1))) #define DVP_CAM_CONFIG_INPUT_PIN(pin, sig, inv) \ { \ @@ -209,7 +221,6 @@ static uint32_t IRAM_ATTR esp_cam_ctlr_dvp_get_jpeg_size(const uint8_t *buffer, */ static uint32_t IRAM_ATTR esp_cam_ctlr_dvp_get_recved_size(esp_cam_ctlr_dvp_cam_t *ctlr, uint8_t *rx_buffer, uint32_t dma_recv_size) { - esp_err_t ret; uint32_t recv_buffer_size; if (ctlr->pic_format_jpeg) { @@ -218,8 +229,10 @@ static uint32_t IRAM_ATTR esp_cam_ctlr_dvp_get_recved_size(esp_cam_ctlr_dvp_cam_ recv_buffer_size = ctlr->fb_size_in_bytes; } - ret = esp_cache_msync(rx_buffer, recv_buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); - assert(ret == ESP_OK); + if (esp_ptr_external_ram(rx_buffer)) { + esp_err_t ret = esp_cache_msync(rx_buffer, recv_buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); + assert(ret == ESP_OK); + } if (ctlr->pic_format_jpeg) { recv_buffer_size = esp_cam_ctlr_dvp_get_jpeg_size(rx_buffer, dma_recv_size); @@ -336,7 +349,7 @@ esp_err_t esp_cam_ctlr_dvp_init(int ctlr_id, cam_clock_source_t clk_src, const e } ESP_ERROR_CHECK(esp_clk_tree_enable_src((soc_module_clk_t)clk_src, true)); - PERIPH_RCC_ATOMIC() { + DVP_CAM_CLK_ATOMIC() { cam_ll_enable_clk(ctlr_id, true); cam_ll_select_clk_src(ctlr_id, clk_src); }; @@ -367,7 +380,7 @@ esp_err_t esp_cam_ctlr_dvp_output_clock(int ctlr_id, cam_clock_source_t clk_src, ESP_LOGD(TAG, "DVP clock source frequency %" PRIu32 "Hz", src_clk_hz); if ((src_clk_hz % xclk_freq) == 0) { - PERIPH_RCC_ATOMIC() { + DVP_CAM_CLK_ATOMIC() { cam_ll_set_group_clock_coeff(ctlr_id, src_clk_hz / xclk_freq, 0, 0); }; @@ -390,7 +403,7 @@ esp_err_t esp_cam_ctlr_dvp_deinit(int ctlr_id) { ESP_RETURN_ON_FALSE(ctlr_id < CAP_DVP_PERIPH_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid argument: ctlr_id >= %d", CAP_DVP_PERIPH_NUM); - PERIPH_RCC_ATOMIC() { + DVP_CAM_CLK_ATOMIC() { cam_ll_enable_clk(ctlr_id, false); }; @@ -710,11 +723,11 @@ esp_err_t esp_cam_new_dvp_ctlr(const esp_cam_ctlr_dvp_config_t *config, esp_cam_ ESP_RETURN_ON_FALSE(config->external_xtal || config->pin_dont_init || config->pin->xclk_io != GPIO_NUM_NC, ESP_ERR_INVALID_ARG, TAG, "invalid argument: xclk_io is not set"); ESP_RETURN_ON_FALSE(config->external_xtal || config->xclk_freq, ESP_ERR_INVALID_ARG, TAG, "invalid argument: xclk_freq is not set"); - ESP_RETURN_ON_ERROR(esp_cache_get_alignment(MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA, &alignment_size), TAG, "failed to get cache alignment"); + ESP_RETURN_ON_ERROR(esp_cache_get_alignment(DVP_CAM_BK_BUFFER_ALLOC_CAPS, &alignment_size), TAG, "failed to get cache alignment"); ESP_RETURN_ON_ERROR(esp_cam_ctlr_dvp_cam_get_frame_size(config, &fb_size_in_bytes), TAG, "invalid argument: input frame pixel format is not supported"); ESP_RETURN_ON_ERROR(dvp_shared_ctrl_claim_io_signals(), TAG, "failed to claim io signals"); - esp_cam_ctlr_dvp_cam_t *ctlr = heap_caps_calloc(1, sizeof(esp_cam_ctlr_dvp_cam_t), CAM_DVP_MEM_ALLOC_CAPS); + esp_cam_ctlr_dvp_cam_t *ctlr = heap_caps_calloc(1, sizeof(esp_cam_ctlr_dvp_cam_t), DVP_CAM_CTLR_ALLOC_CAPS); ESP_GOTO_ON_FALSE(ctlr, ESP_ERR_NO_MEM, fail0, TAG, "no mem for CAM DVP controller context"); ESP_GOTO_ON_ERROR(s_dvp_claim_ctlr(config->ctlr_id, ctlr), fail1, TAG, "no available DVP controller"); @@ -722,7 +735,7 @@ esp_err_t esp_cam_new_dvp_ctlr(const esp_cam_ctlr_dvp_config_t *config, esp_cam_ ESP_LOGD(TAG, "alignment: 0x%x\n", alignment_size); fb_size_in_bytes = ALIGN_UP_BY(fb_size_in_bytes, alignment_size); if (!config->bk_buffer_dis) { - ctlr->backup_buffer = heap_caps_aligned_alloc(alignment_size, fb_size_in_bytes, MALLOC_CAP_SPIRAM); + ctlr->backup_buffer = heap_caps_aligned_alloc(alignment_size, fb_size_in_bytes, DVP_CAM_BK_BUFFER_ALLOC_CAPS); ESP_GOTO_ON_FALSE(ctlr->backup_buffer, ESP_ERR_NO_MEM, fail2, TAG, "no mem for DVP backup buffer"); } diff --git a/components/esp_driver_cam/dvp/src/esp_cam_ctlr_dvp_gdma.c b/components/esp_driver_cam/dvp/src/esp_cam_ctlr_dvp_gdma.c index 09067083f9..f9d82503cc 100644 --- a/components/esp_driver_cam/dvp/src/esp_cam_ctlr_dvp_gdma.c +++ b/components/esp_driver_cam/dvp/src/esp_cam_ctlr_dvp_gdma.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -10,9 +10,24 @@ #include "esp_cache.h" #include "esp_private/esp_cache_private.h" #include "esp_cam_ctlr_dvp_dma.h" +#include "esp_memory_utils.h" #define ALIGN_UP_BY(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) +#if defined(SOC_GDMA_TRIG_PERIPH_CAM0_BUS) && (SOC_GDMA_TRIG_PERIPH_CAM0_BUS == SOC_GDMA_BUS_AHB) +#define DVP_GDMA_NEW_CHANNEL gdma_new_ahb_channel +#define DVP_GDMA_DESC_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA) +#elif defined(SOC_GDMA_TRIG_PERIPH_CAM0_BUS) && (SOC_GDMA_TRIG_PERIPH_CAM0_BUS == SOC_GDMA_BUS_AXI) +#define DVP_GDMA_NEW_CHANNEL gdma_new_axi_channel +#if CONFIG_SPIRAM +#define DVP_GDMA_DESC_ALLOC_CAPS (MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA) +#else +#define DVP_GDMA_DESC_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA) +#endif +#else +#error "Unsupported GDMA bus type for DVP" +#endif + static const char *TAG = "dvp_gdma"; /** @@ -72,9 +87,9 @@ esp_err_t esp_cam_ctlr_dvp_dma_init(esp_cam_ctlr_dvp_dma_t *dma, uint32_t burst_ #endif }; - ESP_RETURN_ON_ERROR(esp_cache_get_alignment(MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA, &alignment_size), TAG, "failed to get cache alignment"); + ESP_RETURN_ON_ERROR(esp_cache_get_alignment(DVP_GDMA_DESC_ALLOC_CAPS, &alignment_size), TAG, "failed to get cache alignment"); - ESP_RETURN_ON_ERROR(gdma_new_axi_channel(&rx_alloc_config, &dma->dma_chan), TAG, "new channel failed"); + ESP_RETURN_ON_ERROR(DVP_GDMA_NEW_CHANNEL(&rx_alloc_config, &dma->dma_chan), TAG, "new channel failed"); ESP_GOTO_ON_ERROR(gdma_connect(dma->dma_chan, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_CAM, 0)), fail0, TAG, "connect failed"); @@ -90,17 +105,21 @@ esp_err_t esp_cam_ctlr_dvp_dma_init(esp_cam_ctlr_dvp_dma_t *dma, uint32_t burst_ .access_ext_mem = true, }; ESP_GOTO_ON_ERROR(gdma_config_transfer(dma->dma_chan, &transfer_config), fail1, TAG, "set trans ability failed"); + size_t int_mem_align = 0; + size_t ext_mem_align = 0; + gdma_get_alignment_constraints(dma->dma_chan, &int_mem_align, &ext_mem_align); dma->desc_count = size / ESP_CAM_CTLR_DVP_DMA_DESC_BUFFER_MAX_SIZE; if (size % ESP_CAM_CTLR_DVP_DMA_DESC_BUFFER_MAX_SIZE) { dma->desc_count++; } dma->size = size; - - ESP_LOGD(TAG, "alignment: 0x%x\n", alignment_size); + alignment_size = (alignment_size == 0) ? 1 : alignment_size; dma->desc_size = ALIGN_UP_BY(dma->desc_count * sizeof(esp_cam_ctlr_dvp_dma_desc_t), alignment_size); - dma->desc = heap_caps_aligned_alloc(alignment_size, dma->desc_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA); + ESP_LOGD(TAG, "alignment_size: %d, dma->desc_count: %d, dma->desc_size: %d", alignment_size, dma->desc_count, dma->desc_size); + dma->desc = heap_caps_aligned_alloc(alignment_size, dma->desc_size, DVP_GDMA_DESC_ALLOC_CAPS); + ESP_GOTO_ON_FALSE(dma->desc, ESP_ERR_NO_MEM, fail1, TAG, "no mem for DVP DMA descriptor"); return ESP_OK; @@ -144,15 +163,15 @@ esp_err_t esp_cam_ctlr_dvp_dma_deinit(esp_cam_ctlr_dvp_dma_t *dma) */ esp_err_t IRAM_ATTR esp_cam_ctlr_dvp_dma_start(esp_cam_ctlr_dvp_dma_t *dma, uint8_t *buffer, size_t size) { - esp_err_t ret; - ESP_RETURN_ON_FALSE_ISR(dma, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); ESP_RETURN_ON_FALSE_ISR(dma->size >= size, ESP_ERR_INVALID_ARG, TAG, "input buffer size is out of range"); esp_cam_ctlr_dvp_config_dma_desc(dma->desc, buffer, size); - ret = esp_cache_msync(dma->desc, dma->desc_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE); - assert(ret == ESP_OK); + if (esp_ptr_external_ram(dma->desc)) { + esp_err_t ret = esp_cache_msync(dma->desc, dma->desc_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE); + assert(ret == ESP_OK); + } return gdma_start(dma->dma_chan, (intptr_t)dma->desc); } diff --git a/components/esp_driver_cam/test_apps/dvp/README.md b/components/esp_driver_cam/test_apps/dvp/README.md index 909282018f..25fbbd517f 100644 --- a/components/esp_driver_cam/test_apps/dvp/README.md +++ b/components/esp_driver_cam/test_apps/dvp/README.md @@ -1,2 +1,2 @@ -| Supported Targets | ESP32-P4 | -| ----------------- | -------- | +| Supported Targets | ESP32-P4 | ESP32-S3 | +| ----------------- | -------- | -------- | diff --git a/components/esp_driver_cam/test_apps/dvp/main/test_dvp_driver.c b/components/esp_driver_cam/test_apps/dvp/main/test_dvp_driver.c index ec99cc5202..f138f1ee9b 100644 --- a/components/esp_driver_cam/test_apps/dvp/main/test_dvp_driver.c +++ b/components/esp_driver_cam/test_apps/dvp/main/test_dvp_driver.c @@ -17,7 +17,7 @@ TEST_CASE("TEST DVP driver allocation", "[DVP]") .h_res = 800, .v_res = 640, .input_data_color_type = CAM_CTLR_COLOR_RGB565, - .dma_burst_size = 128, + .dma_burst_size = 64, .byte_swap_en = false, .pin_dont_init = true, .external_xtal = true, @@ -42,7 +42,7 @@ TEST_CASE("TEST DVP driver allocation with JPEG input", "[DVP]") .clk_src = CAM_CLK_SRC_DEFAULT, .h_res = 800, .v_res = 640, - .dma_burst_size = 128, + .dma_burst_size = 64, .byte_swap_en = false, .pin_dont_init = true, .pic_format_jpeg = true, @@ -69,7 +69,7 @@ TEST_CASE("TEST DVP driver no backup buffer usage", "[DVP]") .h_res = 800, .v_res = 640, .input_data_color_type = CAM_CTLR_COLOR_RGB565, - .dma_burst_size = 128, + .dma_burst_size = 64, .byte_swap_en = false, .bk_buffer_dis = true, .pin_dont_init = true, @@ -96,7 +96,7 @@ TEST_CASE("TEST DVP driver intern/extern init", "[DVP]") .h_res = 800, .v_res = 640, .input_data_color_type = CAM_CTLR_COLOR_RGB565, - .dma_burst_size = 128, + .dma_burst_size = 64, .byte_swap_en = false, .external_xtal = true, }; @@ -128,7 +128,7 @@ TEST_CASE("TEST DVP driver intern/extern generate xclk", "[DVP]") .h_res = 800, .v_res = 640, .input_data_color_type = CAM_CTLR_COLOR_RGB565, - .dma_burst_size = 128, + .dma_burst_size = 64, .byte_swap_en = false, .external_xtal = true, }; diff --git a/components/esp_driver_cam/test_apps/dvp/pytest_dvp.py b/components/esp_driver_cam/test_apps/dvp/pytest_dvp.py index b7b0f347e1..5ef42e0150 100644 --- a/components/esp_driver_cam/test_apps/dvp/pytest_dvp.py +++ b/components/esp_driver_cam/test_apps/dvp/pytest_dvp.py @@ -14,3 +14,14 @@ from pytest_embedded_idf.utils import idf_parametrize @idf_parametrize('target', ['esp32p4'], indirect=['target']) def test_dvp(dut: Dut) -> None: dut.run_all_single_board_cases() + + +@pytest.mark.octal_psram +@pytest.mark.parametrize( + 'config', + ['cache_safe', 'release', 'pm_enable'], + indirect=True, +) +@idf_parametrize('target', ['esp32s3'], indirect=['target']) +def test_dvp_octal(dut: Dut) -> None: + dut.run_all_single_board_cases() diff --git a/components/esp_driver_cam/test_apps/dvp/sdkconfig.ci.cache_safe b/components/esp_driver_cam/test_apps/dvp/sdkconfig.ci.cache_safe index edd91f201c..97180e808b 100644 --- a/components/esp_driver_cam/test_apps/dvp/sdkconfig.ci.cache_safe +++ b/components/esp_driver_cam/test_apps/dvp/sdkconfig.ci.cache_safe @@ -4,6 +4,4 @@ CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y CONFIG_HAL_ASSERTION_SILENT=y -CONFIG_CAM_CTLR_MIPI_CSI_ISR_CACHE_SAFE=y -CONFIG_CAM_CTLR_ISP_DVP_ISR_CACHE_SAFE=y CONFIG_CAM_CTLR_DVP_CAM_ISR_CACHE_SAFE=y diff --git a/components/esp_driver_cam/test_apps/dvp/sdkconfig.defaults b/components/esp_driver_cam/test_apps/dvp/sdkconfig.defaults index eb6374a370..87397fa945 100644 --- a/components/esp_driver_cam/test_apps/dvp/sdkconfig.defaults +++ b/components/esp_driver_cam/test_apps/dvp/sdkconfig.defaults @@ -2,4 +2,3 @@ # Espressif IoT Development Framework (ESP-IDF) 5.4.0 Project Minimal Configuration # CONFIG_ESP_TASK_WDT_EN=n -CONFIG_FREERTOS_HZ=1000 diff --git a/components/esp_driver_cam/test_apps/dvp/sdkconfig.defaults.esp32s3 b/components/esp_driver_cam/test_apps/dvp/sdkconfig.defaults.esp32s3 new file mode 100644 index 0000000000..15ecad20b0 --- /dev/null +++ b/components/esp_driver_cam/test_apps/dvp/sdkconfig.defaults.esp32s3 @@ -0,0 +1,4 @@ +CONFIG_IDF_TARGET="esp32s3" + +CONFIG_SPIRAM=y +CONFIG_SPIRAM_MODE_OCT=y diff --git a/components/esp_lcd/i80/esp_lcd_panel_io_i80.c b/components/esp_lcd/i80/esp_lcd_panel_io_i80.c index 9babc2792d..23b80924e0 100644 --- a/components/esp_lcd/i80/esp_lcd_panel_io_i80.c +++ b/components/esp_lcd/i80/esp_lcd_panel_io_i80.c @@ -199,7 +199,9 @@ esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lc (uint32_t)lcd_ll_get_interrupt_status_reg(bus->hal.dev), LCD_LL_EVENT_TRANS_DONE, i80_lcd_default_isr_handler, bus, &bus->intr); ESP_GOTO_ON_ERROR(ret, err, TAG, "install interrupt failed"); - lcd_ll_enable_interrupt(bus->hal.dev, LCD_LL_EVENT_TRANS_DONE, false); // disable all interrupts + PERIPH_RCC_ATOMIC() { + lcd_ll_enable_interrupt(bus->hal.dev, LCD_LL_EVENT_TRANS_DONE, false); // disable all interrupts + } lcd_ll_clear_interrupt_status(bus->hal.dev, UINT32_MAX); // clear pending interrupt // install DMA service bus->max_transfer_bytes = bus_config->max_transfer_bytes; @@ -215,8 +217,10 @@ esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lc lcd_ll_set_swizzle_mode(bus->hal.dev, LCD_LL_SWIZZLE_AB2BA); // number of data cycles is controlled by DMA buffer size lcd_ll_enable_output_always_on(bus->hal.dev, true); - // enable trans done interrupt - lcd_ll_enable_interrupt(bus->hal.dev, LCD_LL_EVENT_TRANS_DONE, true); + PERIPH_RCC_ATOMIC() { + // enable trans done interrupt + lcd_ll_enable_interrupt(bus->hal.dev, LCD_LL_EVENT_TRANS_DONE, true); + } // trigger a quick "trans done" event, and wait for the interrupt line goes active // this could ensure we go into ISR handler next time we call `esp_intr_enable` lcd_periph_trigger_quick_trans_done_event(bus); diff --git a/components/esp_lcd/rgb/esp_lcd_panel_rgb.c b/components/esp_lcd/rgb/esp_lcd_panel_rgb.c index 87a334d772..a4e0441f53 100644 --- a/components/esp_lcd/rgb/esp_lcd_panel_rgb.c +++ b/components/esp_lcd/rgb/esp_lcd_panel_rgb.c @@ -336,7 +336,9 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf (uint32_t)lcd_ll_get_interrupt_status_reg(rgb_panel->hal.dev), LCD_LL_EVENT_VSYNC_END, rgb_lcd_default_isr_handler, rgb_panel, &rgb_panel->intr); ESP_GOTO_ON_ERROR(ret, err, TAG, "install interrupt failed"); - lcd_ll_enable_interrupt(rgb_panel->hal.dev, LCD_LL_EVENT_VSYNC_END, false); // disable all interrupts + PERIPH_RCC_ATOMIC() { + lcd_ll_enable_interrupt(rgb_panel->hal.dev, LCD_LL_EVENT_VSYNC_END, false); // disable all interrupts + } lcd_ll_clear_interrupt_status(rgb_panel->hal.dev, UINT32_MAX); // clear pending interrupt // install DMA service @@ -573,8 +575,10 @@ static esp_err_t rgb_panel_init(esp_lcd_panel_t *panel) // in stream mode, after finish one frame, the LCD controller will ask for data automatically from the DMA // DMA should prepare the next frame data within porch region lcd_ll_enable_auto_next_frame(rgb_panel->hal.dev, rgb_panel->flags.stream_mode); - // trigger interrupt on the end of frame - lcd_ll_enable_interrupt(rgb_panel->hal.dev, LCD_LL_EVENT_VSYNC_END, true); + PERIPH_RCC_ATOMIC() { + // trigger interrupt on the end of frame + lcd_ll_enable_interrupt(rgb_panel->hal.dev, LCD_LL_EVENT_VSYNC_END, true); + } // enable intr esp_intr_enable(rgb_panel->intr); // start transmission diff --git a/components/hal/esp32p4/include/hal/cam_ll.h b/components/hal/esp32p4/include/hal/cam_ll.h index c9f15c8945..9fee8b2308 100644 --- a/components/hal/esp32p4/include/hal/cam_ll.h +++ b/components/hal/esp32p4/include/hal/cam_ll.h @@ -611,6 +611,12 @@ static inline void cam_ll_enable_interrupt(lcd_cam_dev_t *dev, uint32_t mask, bo dev->lc_dma_int_ena.val &= ~(mask & 0x0c); } } +/// use a macro to wrap the function, force the caller to use it in a critical section +/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance +#define cam_ll_enable_interrupt(...) do { \ + (void)__DECLARE_RCC_ATOMIC_ENV; \ + cam_ll_enable_interrupt(__VA_ARGS__); \ + } while(0) /** * @brief Get interrupt status value diff --git a/components/hal/esp32p4/include/hal/lcd_ll.h b/components/hal/esp32p4/include/hal/lcd_ll.h index 6fe2897e4f..ab3bc12070 100644 --- a/components/hal/esp32p4/include/hal/lcd_ll.h +++ b/components/hal/esp32p4/include/hal/lcd_ll.h @@ -749,6 +749,12 @@ static inline void lcd_ll_enable_interrupt(lcd_cam_dev_t *dev, uint32_t mask, bo dev->lc_dma_int_ena.val &= ~(mask & 0x03); } } +/// use a macro to wrap the function, force the caller to use it in a critical section +/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance +#define lcd_ll_enable_interrupt(...) do { \ + (void)__DECLARE_RCC_ATOMIC_ENV; \ + lcd_ll_enable_interrupt(__VA_ARGS__); \ + } while(0) /** * @brief Get interrupt status value diff --git a/components/hal/esp32s3/include/hal/cam_ll.h b/components/hal/esp32s3/include/hal/cam_ll.h new file mode 100644 index 0000000000..28b151fa92 --- /dev/null +++ b/components/hal/esp32s3/include/hal/cam_ll.h @@ -0,0 +1,621 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include +#include "hal/misc.h" +#include "hal/assert.h" +#include "soc/lcd_cam_struct.h" +#include "soc/system_struct.h" +#include "hal/cam_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define CAM_LL_GET_HW(id) (((id) == 0) ? (&LCD_CAM) : NULL) + +#define CAM_LL_CLK_FRAC_DIV_N_MAX 256 // CAM_CLK = CAM_CLK_S / (N + b/a), the N register is 8 bit-width +#define CAM_LL_CLK_FRAC_DIV_AB_MAX 64 // CAM_CLK = CAM_CLK_S / (N + b/a), the a/b register is 6 bit-width + +/** + * @brief Enable the bus clock for CAM module + * + * @param group_id Group ID + * @param enable true to enable, false to disable + */ +static inline void cam_ll_enable_bus_clock(int group_id, bool en) +{ + (void)group_id; + SYSTEM.perip_clk_en1.lcd_cam_clk_en = en; +} +/// use a macro to wrap the function, force the caller to use it in a critical section +/// the critical section needs to declare the __DECLARE_RCC_RC_ATOMIC_ENV variable in advance +#define cam_ll_enable_bus_clock(...) do { \ + (void)__DECLARE_RCC_RC_ATOMIC_ENV; \ + cam_ll_enable_bus_clock(__VA_ARGS__); \ + } while(0) + +/** + * @brief Reset the CAM module + * + * @param group_id Group ID + */ +static inline void cam_ll_reset_register(int group_id) +{ + (void)group_id; + SYSTEM.perip_rst_en1.lcd_cam_rst = 1; + SYSTEM.perip_rst_en1.lcd_cam_rst = 0; +} + +/// use a macro to wrap the function, force the caller to use it in a critical section +/// the critical section needs to declare the __DECLARE_RCC_RC_ATOMIC_ENV variable in advance +#define cam_ll_reset_register(...) do { \ + (void)__DECLARE_RCC_RC_ATOMIC_ENV; \ + cam_ll_reset_register(__VA_ARGS__); \ + } while(0) + +/** + * @brief Enable clock gating + * + * @param group_id Group ID + * @param en True to enable, False to disable + */ +static inline void cam_ll_enable_clk(int group_id, bool en) +{ + (void)group_id; + (void)en; + //For compatibility +} + +/** + * @brief Select clock source for CAM peripheral + * + * @param group_id Group ID + * @param src Clock source + */ +static inline void cam_ll_select_clk_src(int group_id, cam_clock_source_t src) +{ + switch (src) { + case CAM_CLK_SRC_XTAL: + LCD_CAM.cam_ctrl.cam_clk_sel = 1; + break; + case CAM_CLK_SRC_PLL240M: + LCD_CAM.cam_ctrl.cam_clk_sel = 2; + break; + case CAM_CLK_SRC_PLL160M: + LCD_CAM.cam_ctrl.cam_clk_sel = 3; + break; + default: + // disable LCD clock source + LCD_CAM.cam_ctrl.cam_clk_sel = 0; + HAL_ASSERT(false); + break; + } +} + +/** + * @brief Get the CAM source clock type + * + * @param dev CAM register base address + * @param src The pointer to accept the CAM source clock type + */ +static inline void cam_ll_get_clk_src(lcd_cam_dev_t *dev, cam_clock_source_t *src) +{ + switch (LCD_CAM.cam_ctrl.cam_clk_sel) { + case 1: + *src = CAM_CLK_SRC_XTAL; + break; + case 2: + *src = CAM_CLK_SRC_PLL240M; + break; + case 3: + *src = CAM_CLK_SRC_PLL160M; + break; + default: + HAL_ASSERT(false); + break; + } +} + +/** + * @brief Set clock coefficient of CAM peripheral + * + * @param group_id Group ID + * @param div_num Integer part of the divider + * @param div_a denominator of the divider + * @param div_b numerator of the divider + */ +__attribute__((always_inline)) +static inline void cam_ll_set_group_clock_coeff(int group_id, int div_num, int div_a, int div_b) +{ + HAL_ASSERT(div_num >= 2 && div_num < CAM_LL_CLK_FRAC_DIV_N_MAX); + + LCD_CAM.cam_ctrl.cam_clkm_div_num = div_num; + LCD_CAM.cam_ctrl.cam_clkm_div_a = div_a; + LCD_CAM.cam_ctrl.cam_clkm_div_b = div_b; +} + +/** + * @brief Enable stop signal for CAM peripheral + * + * @param dev CAM register base address + * @param en True to stop when GDMA Rx FIFO is full, False to not stop + */ +static inline void cam_ll_enable_stop_signal(lcd_cam_dev_t *dev, bool en) +{ + dev->cam_ctrl.cam_stop_en = en; +} + +/** + * @brief Set vsync filter threshold value + * + * @param dev CAM register base address + * @param value Filter threshold value for CAM_VSYNC_SIGNAL, range [0, 7] + */ +static inline void cam_ll_set_vsync_filter_thres(lcd_cam_dev_t *dev, uint32_t value) +{ + dev->cam_ctrl.cam_vsync_filter_thres = value; +} + +/** + * @brief Enable to generate LCD_CAM_CAM_HS_INT + * + * @param dev CAM register base address + * @param en True to enable to generate LCD_CAM_CAM_HS_INT, False to disable + */ +static inline void cam_ll_enable_hs_line_int(lcd_cam_dev_t *dev, bool en) +{ + dev->cam_ctrl.cam_line_int_en = en; +} + +/** + * @brief Enable CAM_VSYNC to generate in_suc_eof + * + * @param dev CAM register base address + * @param en True to enable CAM_VSYNC to generate in_suc_eof, False to use LCD_CAM_CAM_REC_DATA_BYTELEN to control in_suc_eof + */ +static inline void cam_ll_enable_vsync_generate_eof(lcd_cam_dev_t *dev, bool en) +{ + dev->cam_ctrl.cam_vs_eof_en = en; +} + +/** + * @brief Enable to swap every two 8-bit input data + * + * @param dev CAM register base address + * @param en True to enable invert, False to disable invert + */ +static inline void cam_ll_enable_8bits_data_invert(lcd_cam_dev_t *dev, bool en) +{ + dev->cam_rgb_yuv.cam_conv_8bits_data_inv = en; +} + +/** + * @brief Enable YUV-RGB converter + * + * @param dev CAM register base address + * @param en True to enable converter, False to disable converter + */ +static inline void cam_ll_enable_rgb_yuv_convert(lcd_cam_dev_t *dev, bool en) +{ + dev->cam_rgb_yuv.cam_conv_bypass = en; +} + +/** + * @brief Set convert data line width + * + * @param dev CAM register base address + * @param width data line width (8 or 16) + */ +static inline void cam_ll_set_convert_data_width(lcd_cam_dev_t *dev, uint32_t width) +{ + HAL_ASSERT(width == 8 || width == 16); + dev->cam_rgb_yuv.cam_conv_mode_8bits_on = (width == 8) ? 1 : 0; +} + +/** + * @brief Set the color range of input data + * + * @param dev CAM register base address + * @param range Color range + */ +static inline void cam_ll_set_input_color_range(lcd_cam_dev_t *dev, color_range_t range) +{ + if (range == COLOR_RANGE_LIMIT) { + dev->cam_rgb_yuv.cam_conv_data_in_mode = 0; + } else if (range == COLOR_RANGE_FULL) { + dev->cam_rgb_yuv.cam_conv_data_in_mode = 1; + } +} + +/** + * @brief Set the color range of output data + * + * @param dev CAM register base address + * @param range Color range + */ +static inline void cam_ll_set_output_color_range(lcd_cam_dev_t *dev, color_range_t range) +{ + if (range == COLOR_RANGE_LIMIT) { + dev->cam_rgb_yuv.cam_conv_data_out_mode = 0; + } else if (range == COLOR_RANGE_FULL) { + dev->cam_rgb_yuv.cam_conv_data_out_mode = 1; + } +} + +/** + * @brief Set YUV conversion standard + * + * @param dev CAM register base address + * @param std YUV conversion standard + */ +static inline void cam_ll_set_yuv_convert_std(lcd_cam_dev_t *dev, color_conv_std_rgb_yuv_t std) +{ + if (std == COLOR_CONV_STD_RGB_YUV_BT601) { + dev->cam_rgb_yuv.cam_conv_protocol_mode = 0; + } else if (std == COLOR_CONV_STD_RGB_YUV_BT709) { + dev->cam_rgb_yuv.cam_conv_protocol_mode = 1; + } +} + +/** + * @brief Set the converter mode: RGB565 to YUV + * + * @param dev CAM register base address + * @param yuv_sample YUV sample mode + */ +static inline void cam_ll_set_convert_mode_rgb_to_yuv(lcd_cam_dev_t *dev, color_pixel_yuv_format_t yuv_sample) +{ + dev->cam_rgb_yuv.cam_conv_trans_mode = 1; + dev->cam_rgb_yuv.cam_conv_yuv2yuv_mode = 3; + switch (yuv_sample) { + case COLOR_PIXEL_YUV422: + dev->cam_rgb_yuv.cam_conv_yuv_mode = 0; + break; + case COLOR_PIXEL_YUV420: + dev->cam_rgb_yuv.cam_conv_yuv_mode = 1; + break; + case COLOR_PIXEL_YUV411: + dev->cam_rgb_yuv.cam_conv_yuv_mode = 2; + break; + default: + abort(); + } +} + +/** + * @brief Set the converter mode: YUV to RGB565 + * + * @param dev CAM register base address + * @param yuv_sample YUV sample mode + */ +static inline void cam_ll_set_convert_mode_yuv_to_rgb(lcd_cam_dev_t *dev, color_pixel_yuv_format_t yuv_sample) +{ + dev->cam_rgb_yuv.cam_conv_trans_mode = 0; + dev->cam_rgb_yuv.cam_conv_yuv2yuv_mode = 3; + switch (yuv_sample) { + case COLOR_PIXEL_YUV422: + dev->cam_rgb_yuv.cam_conv_yuv_mode = 0; + break; + case COLOR_PIXEL_YUV420: + dev->cam_rgb_yuv.cam_conv_yuv_mode = 1; + break; + case COLOR_PIXEL_YUV411: + dev->cam_rgb_yuv.cam_conv_yuv_mode = 2; + break; + default: + abort(); + } +} + +/** + * @brief Set the converter mode: YUV to YUV + * + * @param dev CAM register base address + * @param src_sample Source YUV sample mode + * @param dst_sample Destination YUV sample mode + */ +static inline void cam_ll_set_convert_mode_yuv_to_yuv(lcd_cam_dev_t *dev, color_pixel_yuv_format_t src_sample, color_pixel_yuv_format_t dst_sample) +{ + HAL_ASSERT(src_sample != dst_sample); + dev->cam_rgb_yuv.cam_conv_trans_mode = 1; + switch (src_sample) { + case COLOR_PIXEL_YUV422: + dev->cam_rgb_yuv.cam_conv_yuv_mode = 0; + break; + case COLOR_PIXEL_YUV420: + dev->cam_rgb_yuv.cam_conv_yuv_mode = 1; + break; + case COLOR_PIXEL_YUV411: + dev->cam_rgb_yuv.cam_conv_yuv_mode = 2; + break; + default: + abort(); + } + switch (dst_sample) { + case COLOR_PIXEL_YUV422: + dev->cam_rgb_yuv.cam_conv_yuv2yuv_mode = 0; + break; + case COLOR_PIXEL_YUV420: + dev->cam_rgb_yuv.cam_conv_yuv2yuv_mode = 1; + break; + case COLOR_PIXEL_YUV411: + dev->cam_rgb_yuv.cam_conv_yuv2yuv_mode = 2; + break; + default: + abort(); + } +} + +/** + * @brief Set camera received data byte length + * + * @param dev CAM register base address + * @param length received data byte length, range [0, 0xFFFF] + */ +static inline void cam_ll_set_recv_data_bytelen(lcd_cam_dev_t *dev, uint32_t length) +{ + HAL_FORCE_MODIFY_U32_REG_FIELD(dev->cam_ctrl1, cam_rec_data_bytelen, length); +} + +/** + * @brief Set line number to trigger interrupt + * + * @param dev CAM register base address + * @param number line number to trigger hs interrupt, range [0, 0x3F] + */ +static inline void cam_ll_set_line_int_num(lcd_cam_dev_t *dev, uint32_t number) +{ + dev->cam_ctrl1.cam_line_int_num = number; +} + +/** + * @brief Whether to invert the input signal CAM_PCLK + * + * @param dev CAM register base address + * @param en True to invert, False to not invert + */ +static inline void cam_ll_enable_invert_pclk(lcd_cam_dev_t *dev, bool en) +{ + dev->cam_ctrl1.cam_clk_inv = en; +} + +/** + * @brief Enable CAM_VSYNC filter function + * + * @param dev CAM register base address + * @param en True to enable, False to bypass + */ +static inline void cam_ll_enable_vsync_filter(lcd_cam_dev_t *dev, bool en) +{ + dev->cam_ctrl1.cam_vsync_filter_en = en; +} + +/** + * @brief Set CAM input data width + * + * @param dev CAM register base address + * @param stride 16: The bit number of input data is 9~16. 8: The bit number of input data is 0~8. + */ +static inline void cam_ll_set_input_data_width(lcd_cam_dev_t *dev, uint32_t stride) +{ + switch (stride) { + case 8: + dev->cam_ctrl1.cam_2byte_en = 0; + break; + case 16: + dev->cam_ctrl1.cam_2byte_en = 1; + break; + default: + HAL_ASSERT(false); + break; + } +} + +/** + * @brief Whether to invert CAM_DE + * + * @param dev CAM register base address + * @param en True to invert, False to not invert + */ +static inline void cam_ll_enable_invert_de(lcd_cam_dev_t *dev, bool en) +{ + dev->cam_ctrl1.cam_de_inv = en; +} + +/** + * @brief Whether to invert CAM_HSYNC + * + * @param dev CAM register base address + * @param en True to invert, False to not invert + */ +static inline void cam_ll_enable_invert_hsync(lcd_cam_dev_t *dev, bool en) +{ + dev->cam_ctrl1.cam_hsync_inv = en; +} + +/** + * @brief Whether to invert CAM_VSYNC + * + * @param dev CAM register base address + * @param en True to invert, False to not invert + */ +static inline void cam_ll_enable_invert_vsync(lcd_cam_dev_t *dev, bool en) +{ + dev->cam_ctrl1.cam_vsync_inv = en; +} + +/** + * @brief Enable the mode to control the input control signals + * + * @param dev CAM register base address + * @param mode 1: Input control signals are CAM_DE, CAM_HSYNC and CAM_VSYNC; + * 0: Input control signals are CAM_DE and CAM_VSYNC. CAM_HSYNC and CAM_DE are all 1 the the same time. + */ +static inline void cam_ll_set_vh_de_mode(lcd_cam_dev_t *dev, bool enable) +{ + dev->cam_ctrl1.cam_vh_de_mode_en = enable; +} + +/** + * @brief Get the mode of input control signals + * + * @param dev CAM register base address + * @param en The pointer to accept the vh_de mode status. 1: Input control signals are CAM_DE, CAM_HSYNC and CAM_VSYNC; + * 0: Input control signals are CAM_DE and CAM_VSYNC. CAM_HSYNC and CAM_DE are all 1 the the same time. + */ +static inline void cam_ll_get_vh_de_mode(lcd_cam_dev_t *dev, bool *en) +{ + *en = dev->cam_ctrl1.cam_vh_de_mode_en; +} + +/** + * @brief Set the wire width of CAM output + * + * @param dev CAM register base address + * @param width CAM output wire width + */ +static inline void cam_ll_set_data_wire_width(lcd_cam_dev_t *dev, uint32_t width) +{ + // data line width is same as data stride that set in `cam_ll_set_input_data_width` +} + +/** + * @brief Start the CAM transaction + * + * @param dev CAM register base address + */ +__attribute__((always_inline)) +static inline void cam_ll_start(lcd_cam_dev_t *dev) +{ + dev->cam_ctrl.cam_update = 1; + dev->cam_ctrl1.cam_start = 1; +} + +/** + * @brief Stop the CAM transaction + * + * @param dev CAM register base address + */ +__attribute__((always_inline)) +static inline void cam_ll_stop(lcd_cam_dev_t *dev) +{ + dev->cam_ctrl1.cam_start = 0; + dev->cam_ctrl.cam_update = 1; // self clear +} + +/** + * @brief Whether to reverse the data bit order + * + * @note It acts before the YUV-RGB converter + * + * @param dev CAM register base address + * @param en True to reverse, False to not reverse + */ +__attribute__((always_inline)) +static inline void cam_ll_reverse_dma_data_bit_order(lcd_cam_dev_t *dev, bool en) +{ + dev->cam_ctrl.cam_bit_order = en; +} + +/** + * @brief Whether to swap adjacent two bytes + * + * @note This acts before the YUV-RGB converter, mainly to change the data endian. + * {B1,B0},{B3,B2} => {B0,B1}{B2,B3} + * + * @param dev CAM register base address + * @param en True to swap the byte order, False to not swap + */ +__attribute__((always_inline)) +static inline void cam_ll_swap_dma_data_byte_order(lcd_cam_dev_t *dev, bool en) +{ + dev->cam_ctrl.cam_byte_order = en; +} + +/** + * @brief Reset camera module + * + * @param dev CAM register base address + */ +__attribute__((always_inline)) +static inline void cam_ll_reset(lcd_cam_dev_t *dev) +{ + dev->cam_ctrl1.cam_reset = 1; // self clear +} + +/** + * @brief Reset Async RX FIFO + * + * @param dev CAM register base address + */ +__attribute__((always_inline)) +static inline void cam_ll_fifo_reset(lcd_cam_dev_t *dev) +{ + dev->cam_ctrl1.cam_afifo_reset = 1; // self clear +} + +/** + * @brief Enable/disable interrupt by mask + * + * @param dev CAM register base address + * @param mask Interrupt mask + * @param en True to enable interrupt, False to disable interrupt + */ +static inline void cam_ll_enable_interrupt(lcd_cam_dev_t *dev, uint32_t mask, bool en) +{ + if (en) { + dev->lc_dma_int_ena.val |= mask & 0x0c; + } else { + dev->lc_dma_int_ena.val &= ~(mask & 0x0c); + } +} +/// use a macro to wrap the function, force the caller to use it in a critical section +/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance +#define cam_ll_enable_interrupt(...) do { \ + (void)__DECLARE_RCC_ATOMIC_ENV; \ + cam_ll_enable_interrupt(__VA_ARGS__); \ + } while(0) + +/** + * @brief Get interrupt status value + * + * @param dev CAM register base address + * @return Interrupt status value + */ +__attribute__((always_inline)) +static inline uint32_t cam_ll_get_interrupt_status(lcd_cam_dev_t *dev) +{ + return dev->lc_dma_int_st.val & 0x0c; +} + +/** + * @brief Clear interrupt status by mask + * + * @param dev CAM register base address + * @param mask Interrupt status mask + */ +__attribute__((always_inline)) +static inline void cam_ll_clear_interrupt_status(lcd_cam_dev_t *dev, uint32_t mask) +{ + dev->lc_dma_int_clr.val = mask & 0x0c; +} + +/** + * @brief Get address of interrupt status register address + * + * @param dev CAM register base address + * @return Interrupt status register address + */ +static inline volatile void *cam_ll_get_interrupt_status_reg(lcd_cam_dev_t *dev) +{ + return &dev->lc_dma_int_st; +} + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/esp32s3/include/hal/lcd_ll.h b/components/hal/esp32s3/include/hal/lcd_ll.h index 77e50183f4..84047c776d 100644 --- a/components/hal/esp32s3/include/hal/lcd_ll.h +++ b/components/hal/esp32s3/include/hal/lcd_ll.h @@ -723,6 +723,12 @@ static inline void lcd_ll_enable_interrupt(lcd_cam_dev_t *dev, uint32_t mask, bo dev->lc_dma_int_ena.val &= ~(mask & 0x03); } } +/// use a macro to wrap the function, force the caller to use it in a critical section +/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance +#define lcd_ll_enable_interrupt(...) do { \ + (void)__DECLARE_RCC_ATOMIC_ENV; \ + lcd_ll_enable_interrupt(__VA_ARGS__); \ + } while(0) /** * @brief Get interrupt status value diff --git a/components/soc/esp32s3/cam_periph.c b/components/soc/esp32s3/cam_periph.c new file mode 100644 index 0000000000..36799a4075 --- /dev/null +++ b/components/soc/esp32s3/cam_periph.c @@ -0,0 +1,40 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/gpio_sig_map.h" +#include "soc/cam_periph.h" + +const cam_signal_conn_t cam_periph_signals = { + .buses = { + [0] = { + .module = PERIPH_LCD_CAM_MODULE, + .irq_id = ETS_LCD_CAM_INTR_SOURCE, + .data_sigs = { + CAM_DATA_IN0_IDX, + CAM_DATA_IN1_IDX, + CAM_DATA_IN2_IDX, + CAM_DATA_IN3_IDX, + CAM_DATA_IN4_IDX, + CAM_DATA_IN5_IDX, + CAM_DATA_IN6_IDX, + CAM_DATA_IN7_IDX, + CAM_DATA_IN8_IDX, + CAM_DATA_IN9_IDX, + CAM_DATA_IN10_IDX, + CAM_DATA_IN11_IDX, + CAM_DATA_IN12_IDX, + CAM_DATA_IN13_IDX, + CAM_DATA_IN14_IDX, + CAM_DATA_IN15_IDX + }, + .hsync_sig = CAM_H_SYNC_IDX, + .vsync_sig = CAM_V_SYNC_IDX, + .pclk_sig = CAM_PCLK_IDX, + .de_sig = CAM_H_ENABLE_IDX, + .clk_sig = CAM_CLK_IDX + } + } +}; diff --git a/components/soc/esp32s3/include/soc/Kconfig.soc_caps.in b/components/soc/esp32s3/include/soc/Kconfig.soc_caps.in index 6f3479aea9..aa81895665 100644 --- a/components/soc/esp32s3/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32s3/include/soc/Kconfig.soc_caps.in @@ -47,6 +47,10 @@ config SOC_LCDCAM_SUPPORTED bool default y +config SOC_LCDCAM_CAM_SUPPORTED + bool + default y + config SOC_LCDCAM_I80_LCD_SUPPORTED bool default y @@ -1538,3 +1542,15 @@ config SOC_ULP_HAS_ADC config SOC_PHY_COMBO_MODULE bool default y + +config SOC_LCDCAM_CAM_SUPPORT_RGB_YUV_CONV + bool + default y + +config SOC_LCDCAM_CAM_PERIPH_NUM + int + default 1 + +config SOC_LCDCAM_CAM_DATA_WIDTH_MAX + int + default 16 diff --git a/components/soc/esp32s3/include/soc/clk_tree_defs.h b/components/soc/esp32s3/include/soc/clk_tree_defs.h index 9636399022..a2a5afae2c 100644 --- a/components/soc/esp32s3/include/soc/clk_tree_defs.h +++ b/components/soc/esp32s3/include/soc/clk_tree_defs.h @@ -197,6 +197,23 @@ typedef enum { LCD_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the default choice */ } soc_periph_lcd_clk_src_t; +//////////////////////////////////////////////////LCD/////////////////////////////////////////////////////////////////// + +/** + * @brief Array initializer for all supported clock sources of CAM + */ +#define SOC_CAM_CLKS {SOC_MOD_CLK_PLL_F160M, SOC_MOD_CLK_PLL_D2, SOC_MOD_CLK_XTAL} + +/** + * @brief Type of CAM clock source + */ +typedef enum { + CAM_CLK_SRC_PLL160M = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the source clock */ + CAM_CLK_SRC_PLL240M = SOC_MOD_CLK_PLL_D2, /*!< Select PLL_D2 as the source clock */ + CAM_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL, /*!< Select XTAL as the source clock */ + CAM_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_D2, /*!< Select PLL_D2 as the default choice */ +} soc_periph_cam_clk_src_t; + //////////////////////////////////////////////////RMT/////////////////////////////////////////////////////////////////// /** diff --git a/components/soc/esp32s3/include/soc/soc_caps.h b/components/soc/esp32s3/include/soc/soc_caps.h index 3280410efe..28ad1055ee 100644 --- a/components/soc/esp32s3/include/soc/soc_caps.h +++ b/components/soc/esp32s3/include/soc/soc_caps.h @@ -33,6 +33,7 @@ #define SOC_AHB_GDMA_SUPPORTED 1 #define SOC_GPTIMER_SUPPORTED 1 #define SOC_LCDCAM_SUPPORTED 1 +#define SOC_LCDCAM_CAM_SUPPORTED 1 // support the camera driver based on the LCD_CAM peripheral #define SOC_LCDCAM_I80_LCD_SUPPORTED 1 #define SOC_LCDCAM_RGB_LCD_SUPPORTED 1 #define SOC_MCPWM_SUPPORTED 1 @@ -605,3 +606,8 @@ /*------------------------------------- PHY CAPS -------------------------------------*/ #define SOC_PHY_COMBO_MODULE (1) /*!< Support Wi-Fi and BLE*/ + +/*--------------------------- CAM ---------------------------------*/ +#define SOC_LCDCAM_CAM_SUPPORT_RGB_YUV_CONV (1) +#define SOC_LCDCAM_CAM_PERIPH_NUM (1U) +#define SOC_LCDCAM_CAM_DATA_WIDTH_MAX (16U) diff --git a/examples/peripherals/.build-test-rules.yml b/examples/peripherals/.build-test-rules.yml index 24b066949d..bb4b9deb25 100644 --- a/examples/peripherals/.build-test-rules.yml +++ b/examples/peripherals/.build-test-rules.yml @@ -58,6 +58,17 @@ examples/peripherals/camera/dvp_isp_dsi: - esp_lcd - esp_driver_cam +examples/peripherals/camera/dvp_spi_lcd: + disable: + - if: SOC_LCDCAM_CAM_SUPPORTED != 1 + disable_test: + - if: IDF_TARGET == "esp32s3" + temporary: true + reason: lack of runners + depends_components: + - esp_lcd + - esp_driver_cam + examples/peripherals/camera/mipi_isp_dsi: disable: - if: SOC_MIPI_CSI_SUPPORTED != 1 or SOC_MIPI_DSI_SUPPORTED != 1 diff --git a/examples/peripherals/camera/common_components/sensor_init/idf_component.yml b/examples/peripherals/camera/common_components/sensor_init/idf_component.yml index 1881aa5e99..a5d6718e18 100644 --- a/examples/peripherals/camera/common_components/sensor_init/idf_component.yml +++ b/examples/peripherals/camera/common_components/sensor_init/idf_component.yml @@ -1,4 +1,4 @@ dependencies: - espressif/esp_cam_sensor: "^0.6.1" + espressif/esp_cam_sensor: "^1.1.0" idf: version: ">=5.3.0" diff --git a/examples/peripherals/camera/dvp_spi_lcd/CMakeLists.txt b/examples/peripherals/camera/dvp_spi_lcd/CMakeLists.txt new file mode 100644 index 0000000000..87b0236748 --- /dev/null +++ b/examples/peripherals/camera/dvp_spi_lcd/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +# "Trim" the build. Include the minimal set of components, main, and anything it depends on. +idf_build_set_property(MINIMAL_BUILD ON) +project(dvp_spi_lcd) diff --git a/examples/peripherals/camera/dvp_spi_lcd/README.md b/examples/peripherals/camera/dvp_spi_lcd/README.md new file mode 100644 index 0000000000..6a69880dc2 --- /dev/null +++ b/examples/peripherals/camera/dvp_spi_lcd/README.md @@ -0,0 +1,139 @@ +| Supported Targets | ESP32-P4 | ESP32-S3 | +| ----------------- | -------- | -------- | + + +# DVP Camera display via LCD example + +## Overview + +This example demonstrates how to use the esp_driver_cam component to capture DVP camera sensor signals and display it via LCD interface. This example will auto-detect camera sensors via [ESP camera sensor driver](https://components.espressif.com/components/espressif/esp_cam_sensor) and capture camera sensor signals via DVP interface and display it via LCD interface. + +## Usage + +The subsections below give only absolutely necessary information. For full steps to configure ESP-IDF and use it to build and run projects, see [ESP-IDF Getting Started](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html#get-started). + + +### Hardware Required + +- ESP32S3 devkit with OV2640 camera sensor and ST7789 LCD screen +- or an ESP32S3-EYE dev-kit + +You can also connect camera sensors and LCD screens from other vendors to the ESP chip, you can find corresponding camera or LCD drivers from [ESP Component Registry](https://components.espressif.com), or design your own customized drivers. + + + GND GND + ┌────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────┐ + │ │ │ │ + │ │ │ │ + │ │ │ │ + │ │ │ │ + │ │ │ │ + │ │ │ │ + │ │ │ │ + │ │ │ │ + │ ┌───────────────┴─────────────┴──────────────────┐ │ + │ │ │ ┌──────────┴───────────┐ + │ │ │ LCD MOSI │ │ + │ │ ├───────────────────────────┤ │ + ┌───────────┴─────────┐ │ │ │ │ + │ │ │ │ LCD CLK │ │ + │ │ │ ├───────────────────────────┤ │ + │ │ XCLK │ ESP_CHIP │ │ │ + │ DVP Camera ├──────────────────────┤ │ LCD CS │ LCD Screen │ + │ │ │ ├───────────────────────────┤ │ + │ │ D0~7 │ │ │ │ + │ ├──────────────────────┤ │ LCD DC │ │ + │ │ │ ├───────────────────────────┤ │ + │ │ PCLK │ │ │ │ + │ ├──────────────────────┤ │ LCD BACKLIGHT │ │ + │ │ │ ├───────────────────────────┤ │ + │ │ VSYNC │ │ │ │ + │ ├──────────────────────┤ │ │ │ + │ │ │ │ │ │ + │ │ DE (HREF) │ │ │ │ + │ ├──────────────────────┤ │ └──────────────────────┘ + │ │ │ │ + └───────┬──┬──────────┘ │ │ + │ │ I2C SCL │ │ + │ └─────────────────────────────────┤ │ + │ I2C SDA │ │ + └────────────────────────────────────┤ │ + └────────────────────────────────────────────────┘ + + +### Set Chip Target + +First of all, your target must be supported by both: + +- **By your ESP-IDF version**: For the full list of supported targets, run: + ``` + idf.py --list-targets + ``` +- **By this example**: For the full list of supported targets, refer to the supported targets table at the top of this README. + +After you make sure that your target is supported, go to your example project directory and [set the chip target](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/tools/idf-py.html#select-the-target-chip-set-target): + +``` +idf.py set-target +``` + +For example, to set esp32-S3 as the chip target, run: + +``` +idf.py set-target esp32s3 +``` + + +### Configure the Project + +For information about Kconfig options, see [Project Configuration](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/kconfig.html) > _Name of relevant section(s)_. + +To conveniently check or modify Kconfig options for this example in a project configuration menu, run: + +``` +idf.py menuconfig +``` + +``` +Set CONFIG_CAMERA_OV2640 to y +``` + +Available options for the camera sensor output horizontal/vertical resolution can be seen in ``menuconfig`` > ``Example Configuration``. + + +### Build and Flash + +Execute the following command to build the project, flash it to your development board, and run the monitor tool to view the serial output: + +``` +idf.py build flash monitor +``` + +This command can be reduced to `idf.py flash monitor`. + +If the above command fails, check the log on the serial monitor which usually provides information on the possible cause of the issue. + +To exit the serial monitor, use `Ctrl` + `]`. + + +## Example Output + +If you see the following console output, your example should be running correctly: + +``` +I (1481) main_task: Calling app_main() +I (278) dvp_spi_lcd: Init SPI bus +I (278) dvp_spi_lcd: New panel IO SPI +I (278) dvp_spi_lcd: New ST7789 panel +I (278) dvp_spi_lcd: Reset and init panel +I (408) dvp_spi_lcd: Turn on display +I (408) dvp_spi_lcd: Screen lit up now! + +``` + + +## Reference + +- Link to the ESP-IDF feature's API reference, for example [ESP-IDF: Camera Controller Driver](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/camera_driver.html) +- [ESP-IDF Getting Started](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html#get-started) +- [Project Configuration](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/kconfig.html) (Kconfig Options) diff --git a/examples/peripherals/camera/dvp_spi_lcd/main/CMakeLists.txt b/examples/peripherals/camera/dvp_spi_lcd/main/CMakeLists.txt new file mode 100644 index 0000000000..e863a9baee --- /dev/null +++ b/examples/peripherals/camera/dvp_spi_lcd/main/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRCS "dvp_spi_lcd_main.c" + INCLUDE_DIRS "." + REQUIRES esp_mm esp_driver_cam esp_driver_i2c esp_lcd sensor_init + ) diff --git a/examples/peripherals/camera/dvp_spi_lcd/main/Kconfig.projbuild b/examples/peripherals/camera/dvp_spi_lcd/main/Kconfig.projbuild new file mode 100644 index 0000000000..d44ca3210b --- /dev/null +++ b/examples/peripherals/camera/dvp_spi_lcd/main/Kconfig.projbuild @@ -0,0 +1,33 @@ +menu "Example Configuration" + + choice EXAMPLE_CAM_HRES + bool "Set camera horizontal resolution" + default EXAMPLE_CAM_HRES_240 + + config EXAMPLE_CAM_HRES_640 + bool "640" + config EXAMPLE_CAM_HRES_240 + bool "240" + endchoice + + config EXAMPLE_CAM_HRES + int + default 640 if EXAMPLE_CAM_HRES_640 + default 240 if EXAMPLE_CAM_HRES_240 + + choice EXAMPLE_CAM_VRES + bool "Set camera vertical resolution" + default EXAMPLE_CAM_VRES_480 if EXAMPLE_CAM_HRES_640 + default EXAMPLE_CAM_VRES_240 if EXAMPLE_CAM_HRES_240 + + config EXAMPLE_CAM_VRES_480 + bool "480" + config EXAMPLE_CAM_VRES_240 + bool "240" + endchoice + + config EXAMPLE_CAM_VRES + int + default 480 if EXAMPLE_CAM_VRES_480 + default 240 if EXAMPLE_CAM_VRES_240 +endmenu diff --git a/examples/peripherals/camera/dvp_spi_lcd/main/dvp_spi_lcd_main.c b/examples/peripherals/camera/dvp_spi_lcd/main/dvp_spi_lcd_main.c new file mode 100644 index 0000000000..ef57a1a2e7 --- /dev/null +++ b/examples/peripherals/camera/dvp_spi_lcd/main/dvp_spi_lcd_main.c @@ -0,0 +1,226 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include "sdkconfig.h" +#include "esp_attr.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "esp_lcd_panel_ops.h" +#include "esp_cache.h" +#include "driver/i2c_master.h" +#include "esp_cam_ctlr.h" +#include "esp_cam_ctlr_dvp.h" +#include "example_config.h" +#include "driver/ledc.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_panel_vendor.h" +#include "esp_lcd_panel_ops.h" +#include "example_sensor_init.h" + +static const char *TAG = "dvp_spi_lcd"; + +#define BUFFER_SIZE (CONFIG_EXAMPLE_CAM_HRES * CONFIG_EXAMPLE_CAM_VRES * EXAMPLE_RGB565_BITS_PER_PIXEL / 8) + +typedef struct { + esp_lcd_panel_handle_t panel_hdl; + esp_cam_ctlr_trans_t cam_trans; +} example_cam_context_t; + +static bool s_camera_get_new_vb(esp_cam_ctlr_handle_t handle, esp_cam_ctlr_trans_t *trans, void *user_data); +static bool s_camera_get_finished_trans(esp_cam_ctlr_handle_t handle, esp_cam_ctlr_trans_t *trans, void *user_data); + +static void lcd_display_init(esp_lcd_panel_handle_t *lcd_panel_hdl, esp_lcd_panel_io_handle_t lcd_io_hdl) +{ + esp_lcd_panel_handle_t panel_handle = NULL; + //----------LEDC initialization------------// + const ledc_timer_config_t lcd_timer = { + .speed_mode = LEDC_LOW_SPEED_MODE, + .duty_resolution = LEDC_TIMER_10_BIT, + .timer_num = EXAMPLE_LEDC_LCD_BACKLIGHT, + .freq_hz = 5000, + .clk_cfg = LEDC_AUTO_CLK + }; + ESP_ERROR_CHECK(ledc_timer_config(&lcd_timer)); + const ledc_channel_config_t lcd_channel = { + .gpio_num = EXAMPLE_LCD_BACKLIGHT, + .speed_mode = LEDC_LOW_SPEED_MODE, + .channel = LEDC_CHANNEL_0, + .timer_sel = EXAMPLE_LEDC_LCD_BACKLIGHT, + .intr_type = LEDC_INTR_DISABLE, + .duty = 0, + .hpoint = 0, + .flags.output_invert = true, + }; + ESP_ERROR_CHECK(ledc_channel_config(&lcd_channel)); + + //----------SPI initialization------------// + ESP_LOGI(TAG, "Init SPI bus"); + const spi_bus_config_t bus_cfg = { + .sclk_io_num = EXAMPLE_LCD_SPI_CLK, + .mosi_io_num = EXAMPLE_LCD_SPI_MOSI, + .miso_io_num = GPIO_NUM_NC, + .quadwp_io_num = GPIO_NUM_NC, + .quadhd_io_num = GPIO_NUM_NC, + .max_transfer_sz = BUFFER_SIZE, + }; + ESP_ERROR_CHECK(spi_bus_initialize(EXAMPLE_LCD_SPI_NUM, &bus_cfg, SPI_DMA_CH_AUTO)); + + //----------Panel IO initialization------------// + ESP_LOGI(TAG, "New panel IO SPI"); + const esp_lcd_panel_io_spi_config_t io_cfg = { + .dc_gpio_num = EXAMPLE_LCD_DC, + .cs_gpio_num = EXAMPLE_LCD_SPI_CS, + .pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ, + .lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS, + .lcd_param_bits = EXAMPLE_LCD_PARAM_BITS, + .spi_mode = 2, + .trans_queue_depth = 10, + }; + ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi( + (esp_lcd_spi_bus_handle_t)EXAMPLE_LCD_SPI_NUM, + &io_cfg, + &lcd_io_hdl + )); + + //----------ST7789 Panel initialization------------// + ESP_LOGI(TAG, "New ST7789 panel"); + const esp_lcd_panel_dev_config_t panel_dev_cfg = { + .reset_gpio_num = EXAMPLE_LCD_RST, + .color_space = ESP_LCD_COLOR_SPACE_RGB, + .bits_per_pixel = EXAMPLE_RGB565_BITS_PER_PIXEL, + }; + ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(lcd_io_hdl, &panel_dev_cfg, &panel_handle)); + + ESP_LOGI(TAG, "Reset and init panel"); + esp_lcd_panel_reset(panel_handle); + esp_lcd_panel_init(panel_handle); + esp_lcd_panel_invert_color(panel_handle, true); + + ESP_LOGI(TAG, "Turn on display"); + esp_lcd_panel_disp_on_off(panel_handle, true); + + const int brightness = 100; + uint32_t duty = (1023 * brightness) / 100; + ESP_ERROR_CHECK(ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, duty)); + ESP_ERROR_CHECK(ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0)); + + *lcd_panel_hdl = panel_handle; +} + +void app_main(void) +{ + esp_err_t ret = ESP_FAIL; + + esp_lcd_panel_handle_t lcd_panel_hdl = NULL; + esp_lcd_panel_io_handle_t lcd_io_hdl = NULL; + + size_t cam_buffer_size = CONFIG_EXAMPLE_CAM_HRES * CONFIG_EXAMPLE_CAM_VRES * EXAMPLE_RGB565_BITS_PER_PIXEL / 8; + void *cam_buffer = heap_caps_malloc(cam_buffer_size, EXAMPLE_DVP_CAM_BUF_ALLOC_CAPS); + if (!cam_buffer) { + ESP_LOGE(TAG, "no mem for cam_buffer"); + return; + } + + lcd_display_init(&lcd_panel_hdl, lcd_io_hdl); + + //----------CAM Controller Init------------// + esp_cam_ctlr_handle_t cam_handle = NULL; + esp_cam_ctlr_dvp_pin_config_t pin_cfg = { + .data_width = EXAMPLE_DVP_CAM_DATA_WIDTH, + .data_io = { + EXAMPLE_DVP_CAM_D0_IO, + EXAMPLE_DVP_CAM_D1_IO, + EXAMPLE_DVP_CAM_D2_IO, + EXAMPLE_DVP_CAM_D3_IO, + EXAMPLE_DVP_CAM_D4_IO, + EXAMPLE_DVP_CAM_D5_IO, + EXAMPLE_DVP_CAM_D6_IO, + EXAMPLE_DVP_CAM_D7_IO, + }, + .vsync_io = EXAMPLE_DVP_CAM_VSYNC_IO, + .de_io = EXAMPLE_DVP_CAM_DE_IO, + .pclk_io = EXAMPLE_DVP_CAM_PCLK_IO, + .xclk_io = EXAMPLE_DVP_CAM_XCLK_IO, + }; + + esp_cam_ctlr_dvp_config_t dvp_config = { + .ctlr_id = 0, + .clk_src = CAM_CLK_SRC_DEFAULT, + .h_res = CONFIG_EXAMPLE_CAM_HRES, + .v_res = CONFIG_EXAMPLE_CAM_VRES, + .input_data_color_type = CAM_CTLR_COLOR_RGB565, + .dma_burst_size = 64, + .pin = &pin_cfg, + .bk_buffer_dis = 1, + .xclk_freq = EXAMPLE_DVP_CAM_XCLK_FREQ_HZ, + }; + + ret = esp_cam_new_dvp_ctlr(&dvp_config, &cam_handle); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "dvp init fail[%d]", ret); + return; + } + + //--------Camera Sensor and SCCB Init-----------// + example_sensor_config_t cam_sensor_config = { + .i2c_port_num = I2C_NUM_0, + .i2c_sda_io_num = EXAMPLE_DVP_CAM_SCCB_SDA_IO, + .i2c_scl_io_num = EXAMPLE_DVP_CAM_SCCB_SCL_IO, + .port = ESP_CAM_SENSOR_DVP, + .format_name = EXAMPLE_CAM_FORMAT, + }; + example_sensor_handle_t sensor_handle = { + .sccb_handle = NULL, + .i2c_bus_handle = NULL, + }; + example_sensor_init(&cam_sensor_config, &sensor_handle); + + //--------Register Camera Callbacks----------// + example_cam_context_t cam_ctx = { + .panel_hdl = lcd_panel_hdl, + .cam_trans = { + .buffer = cam_buffer, + .buflen = cam_buffer_size, + } + }; + + esp_cam_ctlr_evt_cbs_t cbs = { + .on_get_new_trans = s_camera_get_new_vb, + .on_trans_finished = s_camera_get_finished_trans, + }; + if (esp_cam_ctlr_register_event_callbacks(cam_handle, &cbs, &cam_ctx) != ESP_OK) { + ESP_LOGE(TAG, "ops register fail"); + return; + } + + //--------Enable and start Camera Controller----------// + ESP_ERROR_CHECK(esp_cam_ctlr_enable(cam_handle)); + + if (esp_cam_ctlr_start(cam_handle) != ESP_OK) { + ESP_LOGE(TAG, "Driver start fail"); + return; + } + + while (1) { + vTaskDelay(1000 / portTICK_PERIOD_MS); + } +} + +static bool s_camera_get_new_vb(esp_cam_ctlr_handle_t handle, esp_cam_ctlr_trans_t *trans, void *user_data) +{ + example_cam_context_t *ctx = (example_cam_context_t *)user_data; + *trans = ctx->cam_trans; + return false; +} + +static bool s_camera_get_finished_trans(esp_cam_ctlr_handle_t handle, esp_cam_ctlr_trans_t *trans, void *user_data) +{ + example_cam_context_t *ctx = (example_cam_context_t *)user_data; + esp_lcd_panel_draw_bitmap(ctx->panel_hdl, 0, 0, CONFIG_EXAMPLE_CAM_HRES, CONFIG_EXAMPLE_CAM_VRES, trans->buffer); + return false; +} diff --git a/examples/peripherals/camera/dvp_spi_lcd/main/example_config.h b/examples/peripherals/camera/dvp_spi_lcd/main/example_config.h new file mode 100644 index 0000000000..2925ac9542 --- /dev/null +++ b/examples/peripherals/camera/dvp_spi_lcd/main/example_config.h @@ -0,0 +1,71 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "sdkconfig.h" + +#ifdef __cplusplus +extern "C" { +#endif + +//----------CAM Config------------// +#define EXAMPLE_RGB565_BITS_PER_PIXEL 16 + +#define EXAMPLE_DVP_CAM_SCCB_SCL_IO (5) +#define EXAMPLE_DVP_CAM_SCCB_SDA_IO (4) + +#define EXAMPLE_DVP_CAM_XCLK_FREQ_HZ (20000000) + +#define EXAMPLE_DVP_CAM_DATA_WIDTH (8) + +#define EXAMPLE_DVP_CAM_D0_IO (11) +#define EXAMPLE_DVP_CAM_D1_IO (9) +#define EXAMPLE_DVP_CAM_D2_IO (8) +#define EXAMPLE_DVP_CAM_D3_IO (10) +#define EXAMPLE_DVP_CAM_D4_IO (12) +#define EXAMPLE_DVP_CAM_D5_IO (18) +#define EXAMPLE_DVP_CAM_D6_IO (17) +#define EXAMPLE_DVP_CAM_D7_IO (16) + +#define EXAMPLE_DVP_CAM_XCLK_IO (15) +#define EXAMPLE_DVP_CAM_PCLK_IO (13) +#define EXAMPLE_DVP_CAM_DE_IO (7) +#define EXAMPLE_DVP_CAM_VSYNC_IO (6) +#define EXAMPLE_DVP_CAM_HSYNC_IO (-1) + +#if CONFIG_SPIRAM +#define EXAMPLE_DVP_CAM_BUF_ALLOC_CAPS (MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA) +#else +#define EXAMPLE_DVP_CAM_BUF_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA) +#endif + +#define EXAMPLE_CAM_FORMAT "DVP_8bit_20Minput_RGB565_240x240_25fps" // ov2640 + +#ifndef EXAMPLE_CAM_FORMAT +#error "Unsupported camera format! Please adjust EXAMPLE_CAM_HRES and EXAMPLE_CAM_VRES in menuconfig" +#endif + +//----------LCD Config------------// +#define EXAMPLE_LEDC_DVP_XCLK (LEDC_TIMER_0) +#define EXAMPLE_LEDC_LCD_BACKLIGHT (LEDC_TIMER_1) +#define EXAMPLE_LCD_SPI_NUM (SPI3_HOST) +#define EXAMPLE_LCD_CMD_BITS (8) +#define EXAMPLE_LCD_PARAM_BITS (8) + +/* LCD Display */ +#define EXAMPLE_LCD_SPI_MOSI (GPIO_NUM_47) +#define EXAMPLE_LCD_SPI_CLK (GPIO_NUM_21) +#define EXAMPLE_LCD_SPI_CS (GPIO_NUM_44) +#define EXAMPLE_LCD_DC (GPIO_NUM_43) +#define EXAMPLE_LCD_RST (GPIO_NUM_NC) +#define EXAMPLE_LCD_BACKLIGHT (GPIO_NUM_48) + +#define EXAMPLE_LCD_PIXEL_CLOCK_HZ (80 * 1000 * 1000) + +#ifdef __cplusplus +} +#endif diff --git a/examples/peripherals/camera/dvp_spi_lcd/main/idf_component.yml b/examples/peripherals/camera/dvp_spi_lcd/main/idf_component.yml new file mode 100644 index 0000000000..4cd6897719 --- /dev/null +++ b/examples/peripherals/camera/dvp_spi_lcd/main/idf_component.yml @@ -0,0 +1,3 @@ +dependencies: + sensor_init: + path: ${IDF_PATH}/examples/peripherals/camera/common_components/sensor_init diff --git a/examples/peripherals/camera/dvp_spi_lcd/pytest_dvp_spi_lcd.py b/examples/peripherals/camera/dvp_spi_lcd/pytest_dvp_spi_lcd.py new file mode 100644 index 0000000000..7dfd614f40 --- /dev/null +++ b/examples/peripherals/camera/dvp_spi_lcd/pytest_dvp_spi_lcd.py @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 +import pytest +from pytest_embedded import Dut +from pytest_embedded_idf.utils import idf_parametrize + + +@pytest.mark.generic +@idf_parametrize('target', ['esp32p4'], indirect=['target']) +def test_dvp_spi_lcd_p4(dut: Dut) -> None: + dut.expect_exact('Calling app_main()') diff --git a/examples/peripherals/camera/dvp_spi_lcd/sdkconfig.defaults b/examples/peripherals/camera/dvp_spi_lcd/sdkconfig.defaults new file mode 100644 index 0000000000..dc0f8f7a83 --- /dev/null +++ b/examples/peripherals/camera/dvp_spi_lcd/sdkconfig.defaults @@ -0,0 +1 @@ +CONFIG_CAMERA_OV2640=y diff --git a/examples/peripherals/camera/dvp_spi_lcd/sdkconfig.defaults.esp32p4 b/examples/peripherals/camera/dvp_spi_lcd/sdkconfig.defaults.esp32p4 new file mode 100644 index 0000000000..b035427002 --- /dev/null +++ b/examples/peripherals/camera/dvp_spi_lcd/sdkconfig.defaults.esp32p4 @@ -0,0 +1,2 @@ +CONFIG_SPIRAM_SPEED_80M=y +CONFIG_SPIRAM=y diff --git a/examples/peripherals/camera/dvp_spi_lcd/sdkconfig.defaults.esp32s3 b/examples/peripherals/camera/dvp_spi_lcd/sdkconfig.defaults.esp32s3 new file mode 100644 index 0000000000..38bbb5933e --- /dev/null +++ b/examples/peripherals/camera/dvp_spi_lcd/sdkconfig.defaults.esp32s3 @@ -0,0 +1,2 @@ +CONFIG_SPIRAM=y +CONFIG_SPIRAM_MODE_OCT=y