Merge branch 'camera/add_dvp_example' into 'master'

P4 DVP example and bugfix

See merge request espressif/esp-idf!39308
This commit is contained in:
Gao Xu
2025-06-09 09:57:14 +08:00
17 changed files with 698 additions and 9 deletions

View File

@@ -43,10 +43,12 @@ typedef struct esp_cam_ctlr_dvp_config {
uint32_t bk_buffer_dis : 1; /*!< Disable backup buffer */ uint32_t bk_buffer_dis : 1; /*!< Disable backup buffer */
uint32_t pin_dont_init : 1; /*!< Don't initialize DVP pins if users have called "esp_cam_ctlr_dvp_init" before */ uint32_t pin_dont_init : 1; /*!< Don't initialize DVP pins if users have called "esp_cam_ctlr_dvp_init" before */
uint32_t pic_format_jpeg : 1; /*!< Input picture format is JPEG, if set this flag and "input_data_color_type" will be ignored */ uint32_t pic_format_jpeg : 1; /*!< Input picture format is JPEG, if set this flag and "input_data_color_type" will be ignored */
uint32_t external_xtal : 1; /*!< Using external XTAL, if set, xclk_io and dvp output clock will be ignored */
}; /*!< Boolean Flags */ }; /*!< Boolean Flags */
uint32_t dma_burst_size; /*!< DVP DMA burst transmission block size, set to 0 means to disable the data burst, uint32_t dma_burst_size; /*!< DVP DMA burst transmission block size, set to 0 means to disable the data burst,
other value must be power of 2, e.g., 4/8/16/32/64/128 */ other value must be power of 2, e.g., 4/8/16/32/64/128 */
uint32_t xclk_freq; /*!< DVP output clock frequency in HZ, only valid if `external_xtal` is set to true */
const esp_cam_ctlr_dvp_pin_config_t *pin; /*!< DVP pin configuration, this will be ignored by "esp_cam_new_dvp_ctlr" if "pin_dont_init" is set */ const esp_cam_ctlr_dvp_pin_config_t *pin; /*!< DVP pin configuration, this will be ignored by "esp_cam_new_dvp_ctlr" if "pin_dont_init" is set */
} esp_cam_ctlr_dvp_config_t; } esp_cam_ctlr_dvp_config_t;

View File

@@ -146,8 +146,11 @@ static IRAM_ATTR esp_err_t esp_cam_ctlr_dvp_start_trans(esp_cam_ctlr_dvp_cam_t *
ESP_RETURN_ON_ERROR_ISR(esp_cam_ctlr_dvp_dma_stop(&ctlr->dma), TAG, "failed to stop DMA"); ESP_RETURN_ON_ERROR_ISR(esp_cam_ctlr_dvp_dma_stop(&ctlr->dma), TAG, "failed to stop DMA");
} }
if (ctlr->cbs.on_get_new_trans && ctlr->cbs.on_get_new_trans(&(ctlr->base), &trans, ctlr->cbs_user_data)) { if (ctlr->cbs.on_get_new_trans) {
buffer_ready = true; ctlr->cbs.on_get_new_trans(&(ctlr->base), &trans, ctlr->cbs_user_data);
if (trans.buffer) {
buffer_ready = true;
}
} else if (!ctlr->bk_buffer_dis) { } else if (!ctlr->bk_buffer_dis) {
trans.buffer = ctlr->backup_buffer; trans.buffer = ctlr->backup_buffer;
trans.buflen = ctlr->fb_size_in_bytes; trans.buflen = ctlr->fb_size_in_bytes;
@@ -704,6 +707,9 @@ esp_err_t esp_cam_new_dvp_ctlr(const esp_cam_ctlr_dvp_config_t *config, esp_cam_
ESP_RETURN_ON_FALSE(config && ret_handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument: config or ret_handle is null"); ESP_RETURN_ON_FALSE(config && ret_handle, ESP_ERR_INVALID_ARG, TAG, "invalid argument: config or ret_handle is null");
ESP_RETURN_ON_FALSE(config->ctlr_id < CAP_DVP_PERIPH_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid argument: ctlr_id >= %d", CAP_DVP_PERIPH_NUM); ESP_RETURN_ON_FALSE(config->ctlr_id < CAP_DVP_PERIPH_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid argument: ctlr_id >= %d", CAP_DVP_PERIPH_NUM);
ESP_RETURN_ON_FALSE(config->pin_dont_init || config->pin, ESP_ERR_INVALID_ARG, TAG, "invalid argument: pin_dont_init is unset and pin is null"); ESP_RETURN_ON_FALSE(config->pin_dont_init || config->pin, ESP_ERR_INVALID_ARG, TAG, "invalid argument: pin_dont_init is unset and pin is null");
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(MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA, &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(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_RETURN_ON_ERROR(dvp_shared_ctrl_claim_io_signals(), TAG, "failed to claim io signals");
@@ -740,13 +746,21 @@ esp_err_t esp_cam_new_dvp_ctlr(const esp_cam_ctlr_dvp_config_t *config, esp_cam_
.port = config->ctlr_id, .port = config->ctlr_id,
.byte_swap_en = config->byte_swap_en, .byte_swap_en = config->byte_swap_en,
}; };
cam_hal_init(&ctlr->hal, &cam_hal_config);
if (!config->pin_dont_init) { if (!config->pin_dont_init) {
// Initialzie DVP clock and GPIO internally
ESP_GOTO_ON_ERROR(esp_cam_ctlr_dvp_init(config->ctlr_id, config->clk_src, config->pin), ESP_GOTO_ON_ERROR(esp_cam_ctlr_dvp_init(config->ctlr_id, config->clk_src, config->pin),
fail5, TAG, "failed to initialize clock and GPIO"); fail5, TAG, "failed to initialize clock and GPIO");
} }
if (!config->external_xtal) {
// Generate DVP xtal clock internally
ESP_GOTO_ON_ERROR(esp_cam_ctlr_dvp_output_clock(config->ctlr_id, config->clk_src, config->xclk_freq),
fail5, TAG, "failed to generate xtal clock");
}
cam_hal_init(&ctlr->hal, &cam_hal_config);
ctlr->ctlr_id = config->ctlr_id; ctlr->ctlr_id = config->ctlr_id;
ctlr->fb_size_in_bytes = fb_size_in_bytes; ctlr->fb_size_in_bytes = fb_size_in_bytes;
ctlr->dvp_fsm = ESP_CAM_CTLR_DVP_CAM_FSM_INIT; ctlr->dvp_fsm = ESP_CAM_CTLR_DVP_CAM_FSM_INIT;

View File

@@ -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 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -20,6 +20,7 @@ TEST_CASE("TEST DVP driver allocation", "[DVP]")
.dma_burst_size = 128, .dma_burst_size = 128,
.byte_swap_en = false, .byte_swap_en = false,
.pin_dont_init = true, .pin_dont_init = true,
.external_xtal = true,
}; };
esp_cam_ctlr_handle_t handle = NULL; esp_cam_ctlr_handle_t handle = NULL;
TEST_ESP_OK(esp_cam_new_dvp_ctlr(&dvp_config, &handle)); TEST_ESP_OK(esp_cam_new_dvp_ctlr(&dvp_config, &handle));
@@ -45,6 +46,7 @@ TEST_CASE("TEST DVP driver allocation with JPEG input", "[DVP]")
.byte_swap_en = false, .byte_swap_en = false,
.pin_dont_init = true, .pin_dont_init = true,
.pic_format_jpeg = true, .pic_format_jpeg = true,
.external_xtal = true,
}; };
esp_cam_ctlr_handle_t handle = NULL; esp_cam_ctlr_handle_t handle = NULL;
TEST_ESP_OK(esp_cam_new_dvp_ctlr(&dvp_config, &handle)); TEST_ESP_OK(esp_cam_new_dvp_ctlr(&dvp_config, &handle));
@@ -71,6 +73,7 @@ TEST_CASE("TEST DVP driver no backup buffer usage", "[DVP]")
.byte_swap_en = false, .byte_swap_en = false,
.bk_buffer_dis = true, .bk_buffer_dis = true,
.pin_dont_init = true, .pin_dont_init = true,
.external_xtal = true,
}; };
esp_cam_ctlr_handle_t handle = NULL; esp_cam_ctlr_handle_t handle = NULL;
TEST_ESP_OK(esp_cam_new_dvp_ctlr(&dvp_config, &handle)); TEST_ESP_OK(esp_cam_new_dvp_ctlr(&dvp_config, &handle));
@@ -84,3 +87,87 @@ TEST_CASE("TEST DVP driver no backup buffer usage", "[DVP]")
TEST_ASSERT_EQUAL((dvp_config.h_res * dvp_config.v_res * 2), bk_buffer_len); // out type RGB565 using 2 byte / pixel TEST_ASSERT_EQUAL((dvp_config.h_res * dvp_config.v_res * 2), bk_buffer_len); // out type RGB565 using 2 byte / pixel
TEST_ESP_OK(esp_cam_ctlr_del(handle)); TEST_ESP_OK(esp_cam_ctlr_del(handle));
} }
TEST_CASE("TEST DVP driver intern/extern init", "[DVP]")
{
esp_cam_ctlr_dvp_config_t dvp_config = {
.ctlr_id = 0,
.clk_src = CAM_CLK_SRC_DEFAULT,
.h_res = 800,
.v_res = 640,
.input_data_color_type = CAM_CTLR_COLOR_RGB565,
.dma_burst_size = 128,
.byte_swap_en = false,
.external_xtal = true,
};
esp_cam_ctlr_handle_t handle = NULL;
//Init externally, do not check pin
dvp_config.pin_dont_init = true;
TEST_ESP_OK(esp_cam_new_dvp_ctlr(&dvp_config, &handle));
TEST_ESP_OK(esp_cam_ctlr_del(handle));
//Init internally but not set pin
dvp_config.pin_dont_init = false;
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_cam_new_dvp_ctlr(&dvp_config, &handle));
//Init internally and set pin
esp_cam_ctlr_dvp_pin_config_t pin_cfg = {
.data_width = 8,
};
dvp_config.pin = &pin_cfg;
TEST_ESP_OK(esp_cam_new_dvp_ctlr(&dvp_config, &handle));
TEST_ESP_OK(esp_cam_ctlr_del(handle));
}
TEST_CASE("TEST DVP driver intern/extern generate xclk", "[DVP]")
{
esp_cam_ctlr_dvp_config_t dvp_config = {
.ctlr_id = 0,
.clk_src = CAM_CLK_SRC_DEFAULT,
.h_res = 800,
.v_res = 640,
.input_data_color_type = CAM_CTLR_COLOR_RGB565,
.dma_burst_size = 128,
.byte_swap_en = false,
.external_xtal = true,
};
esp_cam_ctlr_handle_t handle = NULL;
//Init externally, generate xclk externally, check nothing
dvp_config.pin_dont_init = true;
dvp_config.external_xtal = true;
TEST_ESP_OK(esp_cam_new_dvp_ctlr(&dvp_config, &handle));
TEST_ESP_OK(esp_cam_ctlr_del(handle));
//Init externally, generate xclk internally, do not check pin, check xclk_freq
dvp_config.pin_dont_init = true;
dvp_config.external_xtal = false;
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_cam_new_dvp_ctlr(&dvp_config, &handle));
dvp_config.xclk_freq = 20000000;
TEST_ESP_OK(esp_cam_new_dvp_ctlr(&dvp_config, &handle));
TEST_ESP_OK(esp_cam_ctlr_del(handle));
esp_cam_ctlr_dvp_pin_config_t pin_cfg = {
.data_width = 8,
.xclk_io = GPIO_NUM_NC,
};
//Init internally, generate xclk externally, check nothing
dvp_config.pin = &pin_cfg;
dvp_config.pin_dont_init = false;
dvp_config.external_xtal = true;
TEST_ESP_OK(esp_cam_new_dvp_ctlr(&dvp_config, &handle));
TEST_ESP_OK(esp_cam_ctlr_del(handle));
//Init internally, generate xclk internally, check xclk_io and xclk_freq
dvp_config.pin = &pin_cfg;
dvp_config.pin_dont_init = false;
dvp_config.external_xtal = false;
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_cam_new_dvp_ctlr(&dvp_config, &handle));
pin_cfg.xclk_io = 20;
dvp_config.pin = &pin_cfg;
TEST_ESP_OK(esp_cam_new_dvp_ctlr(&dvp_config, &handle));
TEST_ESP_OK(esp_cam_ctlr_del(handle));
}

View File

@@ -10,8 +10,9 @@ Introduction
.. list:: .. list::
: SOC_MIPI_CSI_SUPPORTED : - MIPI Camera Serial Interface (CSI) : SOC_MIPI_CSI_SUPPORTED : - MIPI Camera Serial Interface (MIPI CSI)
: SOC_ISP_DVP_SUPPORTED : - ISP Digital Video Port (ISP DVP) : SOC_ISP_DVP_SUPPORTED : - Digital Video Port through ISP module (ISP DVP)
: SOC_LCDCAM_CAM_SUPPORTED : - Digital Video Port through LCD_CAM module(LCD_CAM DVP)
The camera controller driver is designed for this hardware peripheral. The camera controller driver is designed for this hardware peripheral.
@@ -38,6 +39,14 @@ Resource Allocation
Install Camera Controller Driver Install Camera Controller Driver
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Camera controller driver can be implemented in one of following ways:
.. list::
: SOC_MIPI_CSI_SUPPORTED : - :cpp:func:`esp_cam_new_csi_ctlr`
: SOC_ISP_DVP_SUPPORTED : - :cpp:func:`esp_cam_new_isp_dvp_ctlr`
: SOC_LCDCAM_CAM_SUPPORTED : - :cpp:func:`esp_cam_new_lcd_cam_ctlr`
.. only:: SOC_MIPI_CSI_SUPPORTED .. only:: SOC_MIPI_CSI_SUPPORTED
A camera controller driver can be implemented by the CSI peripheral, which requires the configuration that specified by :cpp:type:`esp_cam_ctlr_csi_config_t`. A camera controller driver can be implemented by the CSI peripheral, which requires the configuration that specified by :cpp:type:`esp_cam_ctlr_csi_config_t`.
@@ -95,6 +104,48 @@ Install Camera Controller Driver
}; };
ESP_ERROR_CHECK(esp_cam_new_isp_dvp_ctlr(isp_proc, &dvp_ctlr_config, &cam_handle)); ESP_ERROR_CHECK(esp_cam_new_isp_dvp_ctlr(isp_proc, &dvp_ctlr_config, &cam_handle));
.. only:: SOC_LCDCAM_CAM_SUPPORTED
A camera controller driver can be implemented by the DVP port of LCD_CAM, which requires the configuration that specified by :cpp:type:`esp_cam_ctlr_dvp_config_t`.
:cpp:member:`esp_cam_ctlr_dvp_config_t::exexternal_xtal`: set this to use externally generated xclk, otherwise the camera driver will generate it internally.
If :cpp:type:`esp_cam_ctlr_lcd_cam_cfg_t` is specified, users can call :cpp:func:`esp_cam_new_lcd_cam_ctlr` to allocate and initialize a DVP camera controller handle. This function will return an DVP camera controller handle if it runs correctly. You can take following code as reference.
.. code:: c
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, // Set XCLK pin to generate XCLK signal
};
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 = 128,
.pin = &pin_cfg,
.bk_buffer_dis = 1,
.xclk_freq = EXAMPLE_DVP_CAM_XCLK_FREQ_HZ,
};
ESP_ERROR_CHECK(esp_cam_new_dvp_ctlr(&dvp_config, &cam_handle));
Uninstall Camera Controller Driver Uninstall Camera Controller Driver
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -10,8 +10,9 @@
.. list:: .. list::
: SOC_MIPI_CSI_SUPPORTED : - MIPI 摄像头串行接口 (CSI) : SOC_MIPI_CSI_SUPPORTED : - MIPI 摄像头串行接口 (MIPI CSI)
: SOC_ISP_DVP_SUPPORTED : - ISP 数字视频端口 (ISP DVP) : SOC_ISP_DVP_SUPPORTED : - ISP的DVP端口 (ISP DVP)
: SOC_LCDCAM_CAM_SUPPORTED : - LCD_CAM的DVP端口 (LCD_CAM DVP)
摄像头控制器驱动程序是为上述硬件外设而设计的。 摄像头控制器驱动程序是为上述硬件外设而设计的。
@@ -38,6 +39,14 @@
安装摄像头控制器驱动程序 安装摄像头控制器驱动程序
~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~
摄像头控制器驱动程序可以通过以下方式之一安装:
.. list::
: SOC_MIPI_CSI_SUPPORTED : - :cpp:func:`esp_cam_new_csi_ctlr`
: SOC_ISP_DVP_SUPPORTED : - :cpp:func:`esp_cam_new_isp_dvp_ctlr`
: SOC_LCDCAM_CAM_SUPPORTED : - :cpp:func:`esp_cam_new_lcd_cam_ctlr`
.. only:: SOC_MIPI_CSI_SUPPORTED .. only:: SOC_MIPI_CSI_SUPPORTED
摄像头控制器驱动程序可以通过 CSI 外设实现,需要应用 :cpp:type:`esp_cam_ctlr_csi_config_t` 指定的配置。 摄像头控制器驱动程序可以通过 CSI 外设实现,需要应用 :cpp:type:`esp_cam_ctlr_csi_config_t` 指定的配置。
@@ -95,6 +104,48 @@
}; };
ESP_ERROR_CHECK(esp_cam_new_isp_dvp_ctlr(isp_proc, &dvp_ctlr_config, &cam_handle)); ESP_ERROR_CHECK(esp_cam_new_isp_dvp_ctlr(isp_proc, &dvp_ctlr_config, &cam_handle));
.. only:: SOC_LCDCAM_CAM_SUPPORTED
摄像头控制器驱动程序可以通过 LCD_CAM外设实现需要应用 :cpp:type:`esp_cam_ctlr_dvp_config_t`:cpp:type:`esp_cam_ctlr_dvp_pin_config_t` 指定的配置。
:cpp:member:`esp_cam_ctlr_dvp_config_t::exexternal_xtal`:使用外部生成的 xclk或者使用驱动内部内部生成的 xclk。
如果指定了 :cpp:type:`esp_cam_ctlr_dvp_config_t` 中的配置,就可以调用 :cpp:func:`esp_cam_new_dvp_ctlr` 来分配和初始化 DVP 摄像头控制器句柄。如果函数运行正确,将返回一个 DVP 摄像头控制器句柄。请参考以下代码。
.. code:: c
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, // Set XCLK pin to generate XCLK signal
};
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 = 128,
.pin = &pin_cfg,
.bk_buffer_dis = 1,
.xclk_freq = EXAMPLE_DVP_CAM_XCLK_FREQ_HZ,
};
ESP_ERROR_CHECK(esp_cam_new_dvp_ctlr(&dvp_config, &cam_handle));
卸载摄像头控制器驱动程序 卸载摄像头控制器驱动程序
~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -44,6 +44,13 @@ examples/peripherals/bitscrambler:
depends_components: depends_components:
- esp_driver_bitscrambler - esp_driver_bitscrambler
examples/peripherals/camera/dvp_dsi:
disable:
- if: SOC_LCDCAM_CAM_SUPPORTED != 1 or SOC_MIPI_DSI_SUPPORTED != 1
depends_components:
- esp_lcd
- esp_driver_cam
examples/peripherals/camera/dvp_isp_dsi: examples/peripherals/camera/dvp_isp_dsi:
disable: disable:
- if: SOC_ISP_DVP_SUPPORTED != 1 or SOC_MIPI_DSI_SUPPORTED != 1 - if: SOC_ISP_DVP_SUPPORTED != 1 or SOC_MIPI_DSI_SUPPORTED != 1

View File

@@ -10,7 +10,7 @@
extern "C" { extern "C" {
#endif #endif
#define EXAMPLE_CAM_SCCB_FREQ (100000) #define EXAMPLE_CAM_SCCB_FREQ (10 * 1000)
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@@ -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_dsi)

View File

@@ -0,0 +1,156 @@
| Supported Targets | ESP32-P4 |
| ----------------- | -------- |
# DVP Camera display via DSI example
## Overview
This example demonstrates how to use the esp_driver_cam component to capture DVP camera sensor signals and display it via DSI 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 DSI 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
- OV2640 camera sensor, or other camera sensors with DVP port that can output RGB565 format color data
- EK79007 LCD screen
- ESP32P4 devkit
**Note:** For EK79007 you will need to connect following pins:
- 5V - 5V
- GND - GND
- RST_LCD - 3V3
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
┌────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────┐
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ ┌───────────────┴─────────────┴──────────────────┐ │
│ │ │ ┌──────────┴───────────┐
│ │ │ DSI DATA 1P │ │
│ │ ├───────────────────────────┤ │
┌───────────┴─────────┐ │ │ │ │
│ │ │ │ DSI DATA 1N │ │
│ │ │ ├───────────────────────────┤ │
│ │ XCLK │ ESP32-P4 │ │ │
│ DVP Camera ├──────────────────────┤ │ DSI CLK N │ LCD Screen │
│ │ │ ├───────────────────────────┤ │
│ │ D0~7 │ │ │ │
│ ├──────────────────────┤ │ DSI CLK P │ │
│ │ │ ├───────────────────────────┤ │
│ │ PCLK │ │ │ │
│ ├──────────────────────┤ │ DSI DATA 0P │ │
│ │ │ ├───────────────────────────┤ │
│ │ VSYNC │ │ │ │
│ ├──────────────────────┤ │ DSI DATA 0N │ │
│ │ │ ├───────────────────────────┤ │
│ │ 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 <target>
```
For example, to set esp32-P4 as the chip target, run:
```
idf.py set-target esp32p4
```
### 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
```
Remember to select the LCD screen model and set corresponding correct horizontal/vertical resolution in ``menuconfig`` > ``Example DSI Configuration``.
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 (1481) ek79007: version: 1.0.2
I (1701) ov2640: Detected Camera sensor PID=0x26
I (1701) sensor_init: fmt[0].name:DVP_8bit_20Minput_RGB565_640x480_6fps
I (1701) sensor_init: fmt[1].name:DVP_8bit_20Minput_YUV422_640x480_6fps
I (1711) sensor_init: fmt[2].name:DVP_8bit_20Minput_JPEG_640x480_25fps
I (1711) sensor_init: fmt[3].name:DVP_8bit_20Minput_RGB565_240x240_25fps
I (1721) sensor_init: fmt[4].name:DVP_8bit_20Minput_YUV422_240x240_25fps
I (1721) sensor_init: fmt[5].name:DVP_8bit_20Minput_JPEG_320x240_50fps
I (1731) sensor_init: fmt[6].name:DVP_8bit_20Minput_JPEG_1280x720_12fps
I (1741) sensor_init: fmt[7].name:DVP_8bit_20Minput_JPEG_1600x1200_12fps
I (1741) sensor_init: fmt[8].name:DVP_8bit_20Minput_RAW8_800x640_30fps
I (1751) sensor_init: fmt[9].name:DVP_8bit_20Minput_RAW8_800x640_15fps
I (1761) sensor_init: fmt[10].name:DVP_8bit_20Minput_RAW8_800x800_15fps
I (1761) sensor_init: fmt[11].name:DVP_8bit_20Minput_RAW8_1024x600_15fps
I (8291) sensor_init: Format in use:DVP_8bit_20Minput_RGB565_640x480_6fps
```
## 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)

View File

@@ -0,0 +1,4 @@
idf_component_register(SRCS "dvp_dsi_main.c"
INCLUDE_DIRS "."
REQUIRES esp_mm esp_driver_cam esp_driver_i2c dsi_init sensor_init
)

View File

@@ -0,0 +1,45 @@
menu "Example Configuration"
config EXAMPLE_USED_LDO_CHAN_ID
int "Occupied channel ID of the LDO to power on the MIPI DSI PHY"
default 3
help
Example used LDO channel ID, you may check datasheet to know more details.
config EXAMPLE_USED_LDO_VOLTAGE_MV
int "Occupied LDO voltage in mV"
default 2500
range 0 3300
help
Example used LDO voltage, in mV
choice EXAMPLE_CAM_HRES
bool "Set camera horizontal resolution"
default EXAMPLE_CAM_HRES_640
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

View File

@@ -0,0 +1,184 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sdkconfig.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "esp_lcd_mipi_dsi.h"
#include "esp_lcd_panel_ops.h"
#include "esp_ldo_regulator.h"
#include "esp_cache.h"
#include "driver/i2c_master.h"
#include "esp_cam_ctlr.h"
#include "esp_cam_ctlr_dvp.h"
#include "example_dsi_init.h"
#include "example_dsi_init_config.h"
#include "example_sensor_init.h"
#include "example_config.h"
static const char *TAG = "dvp_dsi";
typedef struct {
esp_lcd_panel_handle_t panel_hdl;
void *cam_buf;
} display_update_param_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 bool example_display_update_ready(esp_lcd_panel_handle_t panel, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx)
{
display_update_param_t *param = (display_update_param_t *)user_ctx;
esp_lcd_panel_draw_bitmap(param->panel_hdl, 0, 0, CONFIG_EXAMPLE_CAM_HRES, CONFIG_EXAMPLE_CAM_VRES, param->cam_buf);
return false;
}
void app_main(void)
{
esp_err_t ret = ESP_FAIL;
esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL;
esp_lcd_panel_io_handle_t mipi_dbi_io = NULL;
esp_lcd_panel_handle_t mipi_dpi_panel = NULL;
void *frame_buffer = NULL;
size_t frame_buffer_size = 0;
//mipi ldo
esp_ldo_channel_handle_t ldo_mipi_phy = NULL;
esp_ldo_channel_config_t ldo_mipi_phy_config = {
.chan_id = CONFIG_EXAMPLE_USED_LDO_CHAN_ID,
.voltage_mv = CONFIG_EXAMPLE_USED_LDO_VOLTAGE_MV,
};
ESP_ERROR_CHECK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy));
//---------------DSI Init------------------//
example_dsi_resource_alloc(&mipi_dsi_bus, &mipi_dbi_io, &mipi_dpi_panel, &frame_buffer);
//---------------Necessary variable config------------------//
frame_buffer_size = CONFIG_EXAMPLE_MIPI_DSI_DISP_HRES * CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES * EXAMPLE_RGB565_BITS_PER_PIXEL / 8;
ESP_LOGD(TAG, "CONFIG_EXAMPLE_MIPI_DSI_DISP_HRES: %d, CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES: %d, bits per pixel: %d", CONFIG_EXAMPLE_MIPI_DSI_DISP_HRES, CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES, EXAMPLE_RGB565_BITS_PER_PIXEL);
ESP_LOGD(TAG, "frame_buffer_size: %zu", frame_buffer_size);
ESP_LOGD(TAG, "frame_buffer: %p", frame_buffer);
size_t cam_buffer_size = CONFIG_EXAMPLE_CAM_HRES * CONFIG_EXAMPLE_CAM_VRES * EXAMPLE_RGB565_BITS_PER_PIXEL / 8;
void *cam_buffer = heap_caps_calloc(1, cam_buffer_size, MALLOC_CAP_DMA | MALLOC_CAP_SPIRAM);
if (!cam_buffer) {
ESP_LOGE(TAG, "no mem for cam_buffer");
return;
}
esp_cam_ctlr_trans_t cam_trans = {
.buffer = cam_buffer,
.buflen = cam_buffer_size,
};
//----------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, // Set XCLK pin to generate XCLK signal
};
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 = 128,
.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);
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_trans) != ESP_OK) {
ESP_LOGE(TAG, "ops register fail");
return;
}
ESP_ERROR_CHECK(esp_cam_ctlr_enable(cam_handle));
//---------------DPI Reset------------------//
example_dpi_panel_reset(mipi_dpi_panel);
//init to all white
memset(frame_buffer, 0xFF, frame_buffer_size);
esp_cache_msync((void *)frame_buffer, frame_buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
example_dpi_panel_init(mipi_dpi_panel);
if (esp_cam_ctlr_start(cam_handle) != ESP_OK) {
ESP_LOGE(TAG, "Driver start fail");
return;
}
// Register DPI panel event callback for display update ready notification
display_update_param_t display_update_param = {
.panel_hdl = mipi_dpi_panel,
.cam_buf = cam_buffer,
};
esp_lcd_dpi_panel_event_callbacks_t dpi_cbs = {
.on_color_trans_done = example_display_update_ready,
};
ESP_ERROR_CHECK(esp_lcd_dpi_panel_register_event_callbacks(mipi_dpi_panel, &dpi_cbs, &display_update_param));
esp_lcd_panel_draw_bitmap(mipi_dpi_panel, 0, 0, CONFIG_EXAMPLE_CAM_HRES, CONFIG_EXAMPLE_CAM_VRES, cam_buffer);
while (1) {
vTaskDelay(100 / 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)
{
esp_cam_ctlr_trans_t cam_trans = *(esp_cam_ctlr_trans_t *)user_data;
trans->buffer = cam_trans.buffer;
trans->buflen = cam_trans.buflen;
return false;
}
static bool s_camera_get_finished_trans(esp_cam_ctlr_handle_t handle, esp_cam_ctlr_trans_t *trans, void *user_data)
{
return false;
}

View File

@@ -0,0 +1,57 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "sdkconfig.h"
#ifdef __cplusplus
extern "C" {
#endif
#define EXAMPLE_RGB565_BITS_PER_PIXEL (16)
#define EXAMPLE_DVP_CAM_SCCB_SCL_IO (33)
#define EXAMPLE_DVP_CAM_SCCB_SDA_IO (32)
#define EXAMPLE_DVP_CAM_XCLK_FREQ_HZ (20000000)
#define EXAMPLE_DVP_CAM_DATA_WIDTH (8)
#define EXAMPLE_DVP_CAM_D0_IO (53)
#define EXAMPLE_DVP_CAM_D1_IO (54)
#define EXAMPLE_DVP_CAM_D2_IO (26)
#define EXAMPLE_DVP_CAM_D3_IO (1)
#define EXAMPLE_DVP_CAM_D4_IO (0)
#define EXAMPLE_DVP_CAM_D5_IO (45)
#define EXAMPLE_DVP_CAM_D6_IO (46)
#define EXAMPLE_DVP_CAM_D7_IO (47)
#define EXAMPLE_DVP_CAM_XCLK_IO (20)
#define EXAMPLE_DVP_CAM_PCLK_IO (21)
#define EXAMPLE_DVP_CAM_DE_IO (22)
#define EXAMPLE_DVP_CAM_VSYNC_IO (23)
#define EXAMPLE_DVP_CAM_HSYNC_IO (-1)
#if CONFIG_EXAMPLE_CAM_HRES_640
#if CONFIG_EXAMPLE_CAM_VRES_480
#define EXAMPLE_CAM_FORMAT "DVP_8bit_20Minput_RGB565_640x480_6fps"
#endif
#elif CONFIG_EXAMPLE_CAM_HRES_240
#if CONFIG_EXAMPLE_CAM_VRES_240
#define EXAMPLE_CAM_FORMAT "DVP_8bit_20Minput_RGB565_240x240_25fps"
#endif
#endif
#ifndef EXAMPLE_CAM_FORMAT
#error "Unsupported camera format! Please adjust EXAMPLE_CAM_HRES and EXAMPLE_CAM_VRES in menuconfig"
#endif
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,7 @@
dependencies:
idf:
version: ">=5.3.0"
dsi_init:
path: ${IDF_PATH}/examples/peripherals/camera/common_components/dsi_init
sensor_init:
path: ${IDF_PATH}/examples/peripherals/camera/common_components/sensor_init

View File

@@ -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_dsi(dut: Dut) -> None:
dut.expect_exact('Calling app_main()')

View File

@@ -0,0 +1 @@
CONFIG_CAMERA_OV2640=y

View File

@@ -0,0 +1,4 @@
CONFIG_IDF_EXPERIMENTAL_FEATURES=y
CONFIG_SPIRAM=y
CONFIG_SPIRAM_SPEED_200M=y
CONFIG_EXAMPLE_MIPI_DSI_DISP_USE_DMA2D=y