feat(cam): support dvp cam on ESP32-S31

This commit is contained in:
gaoxu
2026-05-14 17:09:49 +08:00
parent 7ff718a988
commit 4e3751b32e
52 changed files with 1040 additions and 786 deletions

View File

@@ -810,6 +810,9 @@ esp_err_t esp_cam_ctlr_dvp_format_conversion(esp_cam_ctlr_handle_t cam_handle,
return ESP_OK;
}
#if !SOC_LCDCAM_CAM_SUPPORT_RGB_YUV_CONV
return ESP_ERR_NOT_SUPPORTED;
#else
#if !CONFIG_ESP32P4_SELECTS_REV_LESS_V3
if (config->src_format == CAM_CTLR_COLOR_YUV420) {
ESP_LOGE(TAG, "YUV420 is not allowed for source format");
@@ -820,6 +823,7 @@ esp_err_t esp_cam_ctlr_dvp_format_conversion(esp_cam_ctlr_handle_t cam_handle,
cam_hal_color_format_convert(&ctlr->hal, config);
return ESP_OK;
#endif
}
/**

View File

@@ -85,6 +85,9 @@ TEST_CASE("TEST esp_cam on ov5647", "[csi][camera][ov5647]")
.i2c_port_num = I2C_NUM_0,
.i2c_sda_io_num = TEST_MIPI_CSI_CAM_SCCB_SDA_IO,
.i2c_scl_io_num = TEST_MIPI_CSI_CAM_SCCB_SCL_IO,
.reset_pin = -1,
.pwdn_pin = -1,
.xclk_pin = -1,
.port = ESP_CAM_SENSOR_MIPI_CSI,
.format_name = TEST_CAM_FORMAT,
};

View File

@@ -5,3 +5,4 @@ CONFIG_IDF_EXPERIMENTAL_FEATURES=y
CONFIG_SPIRAM_SPEED_200M=y
CONFIG_CAMERA_OV5647=y
CONFIG_CAMERA_OV5647_MIPI_RAW8_800X640_50FPS=y

View File

@@ -1,2 +1,2 @@
| Supported Targets | ESP32-P4 | ESP32-S3 |
| ----------------- | -------- | -------- |
| Supported Targets | ESP32-P4 | ESP32-S3 | ESP32-S31 |
| ----------------- | -------- | -------- | --------- |

View File

@@ -3,8 +3,8 @@ set(srcs "test_app_main.c")
if(CONFIG_SOC_LCDCAM_CAM_SUPPORTED)
list(APPEND srcs "test_dvp_driver.c")
endif()
if(CONFIG_IDF_TARGET_ESP32S3)
list(APPEND srcs "test_dvp_s3eye.c")
if(CONFIG_CAMERA_OV2640 AND CONFIG_SOC_LCDCAM_CAM_SUPPORTED)
list(APPEND srcs "test_dvp_ov2640.c")
endif()
set(priv_requires
@@ -14,6 +14,10 @@ set(priv_requires
esp_driver_ledc
)
if(CONFIG_CAMERA_OV2640 AND CONFIG_SOC_LCDCAM_CAM_SUPPORTED)
list(APPEND priv_requires sensor_init)
endif()
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS "."
PRIV_REQUIRES ${priv_requires}

View File

@@ -0,0 +1,130 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "unity.h"
#include "driver/i2c_master.h"
#include "esp_attr.h"
#include "esp_heap_caps.h"
#include "esp_cam_ctlr.h"
#include "esp_cam_ctlr_dvp.h"
#include "example_sensor_init.h"
#include "test_dvp_ov2640_board.h"
static int s_new_trans_count;
static int s_trans_finished_count;
static bool IRAM_ATTR camera_get_new_buffer(esp_cam_ctlr_handle_t handle, esp_cam_ctlr_trans_t *trans, void *user_data)
{
(void)handle;
esp_cam_ctlr_trans_t *new_trans = (esp_cam_ctlr_trans_t *)user_data;
trans->buffer = new_trans->buffer;
trans->buflen = new_trans->buflen;
s_new_trans_count++;
return false;
}
static bool IRAM_ATTR camera_trans_finished(esp_cam_ctlr_handle_t handle, esp_cam_ctlr_trans_t *trans, void *user_data)
{
(void)handle;
(void)trans;
(void)user_data;
s_trans_finished_count++;
return false;
}
TEST_CASE("TEST esp_cam DVP on ov2640", "[dvp][camera][ov2640]")
{
s_new_trans_count = 0;
s_trans_finished_count = 0;
esp_cam_ctlr_dvp_pin_config_t pin_cfg = {
.data_width = TEST_DVP_CAM_DATA_WIDTH,
.data_io = {
TEST_DVP_CAM_D0_IO,
TEST_DVP_CAM_D1_IO,
TEST_DVP_CAM_D2_IO,
TEST_DVP_CAM_D3_IO,
TEST_DVP_CAM_D4_IO,
TEST_DVP_CAM_D5_IO,
TEST_DVP_CAM_D6_IO,
TEST_DVP_CAM_D7_IO,
},
.vsync_io = TEST_DVP_CAM_VSYNC_IO,
.de_io = TEST_DVP_CAM_DE_IO,
.pclk_io = TEST_DVP_CAM_PCLK_IO,
.xclk_io = TEST_DVP_CAM_XCLK_IO,
};
esp_cam_ctlr_dvp_config_t dvp_config = {
.ctlr_id = 0,
.clk_src = CAM_CLK_SRC_DEFAULT,
.h_res = TEST_DVP_CAM_FRAME_WIDTH,
.v_res = TEST_DVP_CAM_FRAME_HEIGHT,
.input_data_color_type = TEST_DVP_CAM_INPUT_COLOR_TYPE,
.output_data_color_type = TEST_DVP_CAM_OUTPUT_COLOR_TYPE,
.dma_burst_size = 64,
.pin = &pin_cfg,
.bk_buffer_dis = 1,
.xclk_freq = TEST_DVP_CAM_XCLK_FREQ_HZ,
.cam_data_width = TEST_DVP_CAM_DATA_WIDTH,
.byte_swap_en = false,
.bit_swap_en = false,
};
esp_cam_ctlr_handle_t cam_handle = NULL;
TEST_ESP_OK(esp_cam_new_dvp_ctlr(&dvp_config, &cam_handle));
size_t frame_buffer_size = TEST_DVP_CAM_FRAME_SIZE_BYTES;
void *frame_buffer = esp_cam_ctlr_alloc_buffer(cam_handle, frame_buffer_size, TEST_DVP_CAM_BUF_ALLOC_CAPS);
TEST_ASSERT_NOT_NULL(frame_buffer);
esp_cam_ctlr_trans_t trans_data = {
.buffer = frame_buffer,
.buflen = frame_buffer_size,
};
example_sensor_handle_t sensor_handle = {
.sccb_handle = NULL,
.i2c_bus_handle = NULL,
};
example_sensor_config_t sensor_config = {
.i2c_port_num = I2C_NUM_0,
.i2c_sda_io_num = TEST_DVP_CAM_SCCB_SDA_IO,
.i2c_scl_io_num = TEST_DVP_CAM_SCCB_SCL_IO,
.reset_pin = TEST_DVP_CAM_RESET_IO,
.pwdn_pin = TEST_DVP_CAM_PWDN_IO,
.xclk_pin = TEST_DVP_CAM_XCLK_IO,
.port = ESP_CAM_SENSOR_DVP,
.format_name = TEST_DVP_CAM_FORMAT_NAME,
};
example_sensor_init(&sensor_config, &sensor_handle);
if (sensor_handle.sccb_handle == NULL || sensor_handle.i2c_bus_handle == NULL) {
TEST_ESP_OK(esp_cam_ctlr_del(cam_handle));
heap_caps_free(frame_buffer);
TEST_FAIL_MESSAGE("failed to detect or initialize ov2640 sensor");
}
esp_cam_ctlr_evt_cbs_t cbs = {
.on_get_new_trans = camera_get_new_buffer,
.on_trans_finished = camera_trans_finished,
};
TEST_ESP_OK(esp_cam_ctlr_register_event_callbacks(cam_handle, &cbs, &trans_data));
TEST_ESP_OK(esp_cam_ctlr_enable(cam_handle));
TEST_ESP_OK(esp_cam_ctlr_start(cam_handle));
vTaskDelay(pdMS_TO_TICKS(1000));
TEST_ESP_OK(esp_cam_ctlr_stop(cam_handle));
// Roughly one transfer per frame is expected for the selected OV2640 format.
TEST_ASSERT_GREATER_OR_EQUAL(TEST_DVP_CAM_EXPECTED_NEW_TRANS_MIN, s_new_trans_count);
TEST_ASSERT_GREATER_OR_EQUAL(TEST_DVP_CAM_EXPECTED_FINISHED_MIN, s_trans_finished_count);
TEST_ESP_OK(esp_cam_ctlr_disable(cam_handle));
TEST_ESP_OK(esp_cam_ctlr_del(cam_handle));
example_sensor_deinit(sensor_handle);
heap_caps_free(frame_buffer);
}

View File

@@ -0,0 +1,134 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "sdkconfig.h"
#include "hal/cam_ctlr_types.h"
#include "esp_heap_caps.h"
#ifdef __cplusplus
extern "C" {
#endif
#define TEST_DVP_CAM_FRAME_BITS_PER_PIXEL 16
#define TEST_DVP_CAM_EXPECTED_NEW_TRANS_MIN 6
#define TEST_DVP_CAM_EXPECTED_FINISHED_MIN 5
#define TEST_DVP_CAM_XCLK_FREQ_HZ 20000000
#define TEST_DVP_CAM_DATA_WIDTH 8
#if CONFIG_IDF_TARGET_ESP32S3
#define TEST_DVP_CAM_FRAME_WIDTH 640
#define TEST_DVP_CAM_FRAME_HEIGHT 480
#define TEST_DVP_CAM_FORMAT_NAME "DVP_8bit_20Minput_YUV422_YUYV_640x480_6fps"
#define TEST_DVP_CAM_INPUT_COLOR_TYPE CAM_CTLR_COLOR_YUV422_YUYV
#define TEST_DVP_CAM_OUTPUT_COLOR_TYPE CAM_CTLR_COLOR_YUV422_YUYV
#define TEST_DVP_CAM_PWDN_IO (-1)
#define TEST_DVP_CAM_RESET_IO (-1)
#define TEST_DVP_CAM_SCCB_SCL_IO 5
#define TEST_DVP_CAM_SCCB_SDA_IO 4
#define TEST_DVP_CAM_D0_IO 11
#define TEST_DVP_CAM_D1_IO 9
#define TEST_DVP_CAM_D2_IO 8
#define TEST_DVP_CAM_D3_IO 10
#define TEST_DVP_CAM_D4_IO 12
#define TEST_DVP_CAM_D5_IO 18
#define TEST_DVP_CAM_D6_IO 17
#define TEST_DVP_CAM_D7_IO 16
#define TEST_DVP_CAM_XCLK_IO 15
#define TEST_DVP_CAM_PCLK_IO 13
#define TEST_DVP_CAM_DE_IO 7
#define TEST_DVP_CAM_VSYNC_IO 6
#elif CONFIG_IDF_TARGET_ESP32P4
#define TEST_DVP_CAM_FRAME_WIDTH 640
#define TEST_DVP_CAM_FRAME_HEIGHT 480
#define TEST_DVP_CAM_FORMAT_NAME "DVP_8bit_20Minput_YUV422_UYVY_640x480_6fps"
#define TEST_DVP_CAM_INPUT_COLOR_TYPE CAM_CTLR_COLOR_YUV422_UYVY
#define TEST_DVP_CAM_OUTPUT_COLOR_TYPE CAM_CTLR_COLOR_YUV422_UYVY
#define TEST_DVP_CAM_PWDN_IO (-1)
#define TEST_DVP_CAM_RESET_IO (-1)
#if !CONFIG_ESP32P4_SELECTS_REV_LESS_V3
#define TEST_DVP_CAM_SCCB_SCL_IO 8
#define TEST_DVP_CAM_SCCB_SDA_IO 7
#define TEST_DVP_CAM_D0_IO 2
#define TEST_DVP_CAM_D1_IO 32
#define TEST_DVP_CAM_D2_IO 33
#define TEST_DVP_CAM_D3_IO 23
#define TEST_DVP_CAM_D4_IO 3
#define TEST_DVP_CAM_D5_IO 6
#define TEST_DVP_CAM_D6_IO 5
#define TEST_DVP_CAM_D7_IO 21
#define TEST_DVP_CAM_XCLK_IO 20
#define TEST_DVP_CAM_PCLK_IO 4
#define TEST_DVP_CAM_DE_IO 22
#define TEST_DVP_CAM_VSYNC_IO 37
#else
#define TEST_DVP_CAM_SCCB_SCL_IO 33
#define TEST_DVP_CAM_SCCB_SDA_IO 32
#define TEST_DVP_CAM_D0_IO 53
#define TEST_DVP_CAM_D1_IO 54
#define TEST_DVP_CAM_D2_IO 26
#define TEST_DVP_CAM_D3_IO 1
#define TEST_DVP_CAM_D4_IO 0
#define TEST_DVP_CAM_D5_IO 45
#define TEST_DVP_CAM_D6_IO 46
#define TEST_DVP_CAM_D7_IO 47
#define TEST_DVP_CAM_XCLK_IO 20
#define TEST_DVP_CAM_PCLK_IO 21
#define TEST_DVP_CAM_DE_IO 22
#define TEST_DVP_CAM_VSYNC_IO 23
#endif
#elif CONFIG_IDF_TARGET_ESP32S31
#define TEST_DVP_CAM_FRAME_WIDTH 640
#define TEST_DVP_CAM_FRAME_HEIGHT 480
#define TEST_DVP_CAM_FORMAT_NAME "DVP_8bit_20Minput_YUV422_YUYV_640x480_6fps"
#define TEST_DVP_CAM_INPUT_COLOR_TYPE CAM_CTLR_COLOR_YUV422_YUYV
#define TEST_DVP_CAM_OUTPUT_COLOR_TYPE CAM_CTLR_COLOR_YUV422_YUYV
#define TEST_DVP_CAM_PWDN_IO 61
#define TEST_DVP_CAM_RESET_IO 60
#define TEST_DVP_CAM_SCCB_SCL_IO 1
#define TEST_DVP_CAM_SCCB_SDA_IO 0
#define TEST_DVP_CAM_D0_IO 46
#define TEST_DVP_CAM_D1_IO 47
#define TEST_DVP_CAM_D2_IO 48
#define TEST_DVP_CAM_D3_IO 49
#define TEST_DVP_CAM_D4_IO 50
#define TEST_DVP_CAM_D5_IO 51
#define TEST_DVP_CAM_D6_IO 52
#define TEST_DVP_CAM_D7_IO 53
#define TEST_DVP_CAM_XCLK_IO 55
#define TEST_DVP_CAM_PCLK_IO 54
#define TEST_DVP_CAM_DE_IO 57
#define TEST_DVP_CAM_VSYNC_IO 56
#endif
#define TEST_DVP_CAM_FRAME_SIZE_BYTES \
(TEST_DVP_CAM_FRAME_WIDTH * TEST_DVP_CAM_FRAME_HEIGHT * TEST_DVP_CAM_FRAME_BITS_PER_PIXEL / 8)
#if CONFIG_SPIRAM
#define TEST_DVP_CAM_BUF_ALLOC_CAPS (MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA)
#else
#define TEST_DVP_CAM_BUF_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA)
#endif
#ifdef __cplusplus
}
#endif

View File

@@ -1,166 +0,0 @@
/*
* 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_cache.h"
#include "driver/i2c_master.h"
#include "esp_cam_ctlr.h"
#include "esp_cam_ctlr_dvp.h"
#include "driver/ledc.h"
#include "example_sensor_init.h"
#include "unity.h"
//----------CAM Config------------//
#define TEST_RGB565_BITS_PER_PIXEL 16
#define TEST_DVP_CAM_SCCB_SCL_IO (5)
#define TEST_DVP_CAM_SCCB_SDA_IO (4)
#define TEST_DVP_CAM_XCLK_FREQ_HZ (20000000)
#define TEST_DVP_CAM_DATA_WIDTH (8)
#define TEST_DVP_CAM_D0_IO (11)
#define TEST_DVP_CAM_D1_IO (9)
#define TEST_DVP_CAM_D2_IO (8)
#define TEST_DVP_CAM_D3_IO (10)
#define TEST_DVP_CAM_D4_IO (12)
#define TEST_DVP_CAM_D5_IO (18)
#define TEST_DVP_CAM_D6_IO (17)
#define TEST_DVP_CAM_D7_IO (16)
#define TEST_DVP_CAM_XCLK_IO (15)
#define TEST_DVP_CAM_PCLK_IO (13)
#define TEST_DVP_CAM_DE_IO (7)
#define TEST_DVP_CAM_VSYNC_IO (6)
#define TEST_DVP_CAM_HSYNC_IO (-1)
#define TEST_DVP_CAM_BUF_ALLOC_CAPS (MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA)
#define TEST_DVP_CAM_H_RES 240
#define TEST_DVP_CAM_V_RES 240
#define TEST_CAM_FORMAT "DVP_8bit_20Minput_RGB565_240x240_25fps"
#define BUFFER_SIZE (TEST_DVP_CAM_H_RES * TEST_DVP_CAM_V_RES * TEST_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);
int count_new_vb = 0;
int count_finished_trans = 0;
TEST_CASE("TEST DVP camera on esp32s3_eye", "[dvp][camera][esp32s3_eye]")
{
//----------CAM Controller Init------------//
esp_cam_ctlr_handle_t cam_handle = NULL;
esp_cam_ctlr_dvp_pin_config_t pin_cfg = {
.data_width = TEST_DVP_CAM_DATA_WIDTH,
.data_io = {
TEST_DVP_CAM_D0_IO,
TEST_DVP_CAM_D1_IO,
TEST_DVP_CAM_D2_IO,
TEST_DVP_CAM_D3_IO,
TEST_DVP_CAM_D4_IO,
TEST_DVP_CAM_D5_IO,
TEST_DVP_CAM_D6_IO,
TEST_DVP_CAM_D7_IO,
},
.vsync_io = TEST_DVP_CAM_VSYNC_IO,
.de_io = TEST_DVP_CAM_DE_IO,
.pclk_io = TEST_DVP_CAM_PCLK_IO,
.xclk_io = TEST_DVP_CAM_XCLK_IO,
};
esp_cam_ctlr_dvp_config_t dvp_config = {
.ctlr_id = 0,
.clk_src = CAM_CLK_SRC_DEFAULT,
.h_res = TEST_DVP_CAM_H_RES,
.v_res = TEST_DVP_CAM_V_RES,
.input_data_color_type = CAM_CTLR_COLOR_RGB565,
.output_data_color_type = CAM_CTLR_COLOR_RGB565,
.dma_burst_size = 64,
.pin = &pin_cfg,
.bk_buffer_dis = 1,
.xclk_freq = TEST_DVP_CAM_XCLK_FREQ_HZ,
};
TEST_ESP_OK(esp_cam_new_dvp_ctlr(&dvp_config, &cam_handle));
//--------Allocate Camera Buffer----------//
size_t cam_buffer_size = TEST_DVP_CAM_H_RES * TEST_DVP_CAM_V_RES * TEST_RGB565_BITS_PER_PIXEL / 8;
void *cam_buffer = NULL;
cam_buffer = esp_cam_ctlr_alloc_buffer(cam_handle, cam_buffer_size, TEST_DVP_CAM_BUF_ALLOC_CAPS);
//--------Camera Sensor and SCCB Init-----------//
example_sensor_config_t cam_sensor_config = {
.i2c_port_num = I2C_NUM_0,
.i2c_sda_io_num = TEST_DVP_CAM_SCCB_SDA_IO,
.i2c_scl_io_num = TEST_DVP_CAM_SCCB_SCL_IO,
.port = ESP_CAM_SENSOR_DVP,
.format_name = TEST_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,
};
TEST_ESP_OK(esp_cam_ctlr_register_event_callbacks(cam_handle, &cbs, &cam_ctx));
TEST_ESP_OK(esp_cam_ctlr_enable(cam_handle));
TEST_ESP_OK(esp_cam_ctlr_start(cam_handle));
vTaskDelay(1000 / portTICK_PERIOD_MS);
//For this format"DVP_8bit_20Minput_RGB565_240x240_25fps", the FPS is 25
TEST_ASSERT_GREATER_OR_EQUAL(25, count_new_vb);
TEST_ASSERT_GREATER_OR_EQUAL(24, count_finished_trans);
TEST_ESP_OK(esp_cam_ctlr_stop(cam_handle));
TEST_ESP_OK(esp_cam_ctlr_disable(cam_handle));
TEST_ESP_OK(esp_cam_ctlr_del(cam_handle));
example_sensor_deinit(sensor_handle);
heap_caps_free(cam_buffer);
}
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;
count_new_vb++;
return false;
}
static bool s_camera_get_finished_trans(esp_cam_ctlr_handle_t handle, esp_cam_ctlr_trans_t *trans, void *user_data)
{
count_finished_trans++;
return false;
}

View File

@@ -1,41 +1,34 @@
# SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-FileCopyrightText: 2024-2026 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
def _skip_board_specific_cases(case_tester) -> None: # type: ignore
for case in case_tester.test_menu:
if 'TEST esp_cam DVP on ov2640' in case.name:
continue
case_tester.run_normal_case(case=case, reset=True)
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
['cache_safe', 'release', 'pm_enable'],
indirect=True,
)
@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_driver(case_tester) -> None: # type: ignore
for case in case_tester.test_menu:
if 'TEST DVP camera on esp32s3_eye' in case.name:
continue
case_tester.run_normal_case(case=case, reset=True)
@idf_parametrize('target', ['esp32p4', 'esp32s31'], indirect=['target'])
def test_dvp(case_tester) -> None: # type: ignore
_skip_board_specific_cases(case_tester)
@pytest.mark.esp32s3_eye
@pytest.mark.parametrize(
'config',
['esp32s3_eye'],
['cache_safe', 'release', 'pm_enable', 'esp32s3_ov2640'],
indirect=True,
)
@idf_parametrize('target', ['esp32s3'], indirect=['target'])
def test_dvp_s3eye(dut: Dut) -> None:
def test_dvp_esp32s3(dut: Dut) -> None:
dut.run_all_single_board_cases()

View File

@@ -0,0 +1,6 @@
CONFIG_IDF_TARGET="esp32p4"
CONFIG_SPIRAM=y
CONFIG_SPIRAM_SPEED_200M=y
CONFIG_IDF_EXPERIMENTAL_FEATURES=y
CONFIG_CAMERA_OV2640=y

View File

@@ -0,0 +1,5 @@
CONFIG_IDF_TARGET="esp32s31"
CONFIG_SPIRAM=y
CONFIG_SPIRAM_SPEED_250M=y
CONFIG_CAMERA_OV2640=y

View File

@@ -1,7 +0,0 @@
CONFIG_IDF_TARGET="esp32s3"
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y
CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y
CONFIG_CAMERA_OV2640=y

View File

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

View File

@@ -1,4 +1,6 @@
CONFIG_IDF_TARGET="esp32s3"
CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y

View File

@@ -0,0 +1,4 @@
CONFIG_IDF_TARGET="esp32s31"
CONFIG_SPIRAM=y
CONFIG_SPIRAM_SPEED_250M=y

View File

@@ -0,0 +1,40 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "hal/cam_periph.h"
#include "soc/gpio_sig_map.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_DATA0_IN_PAD_IN_IDX,
CAM_DATA1_IN_PAD_IN_IDX,
CAM_DATA2_IN_PAD_IN_IDX,
CAM_DATA3_IN_PAD_IN_IDX,
CAM_DATA4_IN_PAD_IN_IDX,
CAM_DATA5_IN_PAD_IN_IDX,
CAM_DATA6_IN_PAD_IN_IDX,
CAM_DATA7_IN_PAD_IN_IDX,
CAM_DATA8_IN_PAD_IN_IDX,
CAM_DATA9_IN_PAD_IN_IDX,
CAM_DATA10_IN_PAD_IN_IDX,
CAM_DATA11_IN_PAD_IN_IDX,
CAM_DATA12_IN_PAD_IN_IDX,
CAM_DATA13_IN_PAD_IN_IDX,
CAM_DATA14_IN_PAD_IN_IDX,
CAM_DATA15_IN_PAD_IN_IDX,
},
.hsync_sig = CAM_H_SYNC_PAD_IN_IDX,
.vsync_sig = CAM_V_SYNC_PAD_IN_IDX,
.pclk_sig = CAM_PCLK_PAD_IN_IDX,
.de_sig = CAM_H_ENABLE_PAD_IN_IDX,
.clk_sig = CAM_CLK_PAD_OUT_IDX
}
}
};

View File

@@ -0,0 +1,583 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdbool.h>
#include "hal/assert.h"
#include "hal/cam_types.h"
#include "hal/misc.h"
#include "soc/hp_sys_clkrst_struct.h"
#include "soc/lcd_cam_struct.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 256 // CAM_CLK = CAM_CLK_S / (N + b/a), the a/b register is 8 bit-width
#define CAM_LL_PERIPH_NUM (1U)
#define CAM_LL_DATA_WIDTH_MAX (16U)
/**
* @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;
// the core clock is a shared clock for both LCD and CAM
HP_SYS_CLKRST.lcdcam_lcdcam_ctrl0.reg_lcdcam_clk_en = en;
HP_SYS_CLKRST.lcdcam_ctrl0.reg_lcdcam_apb_clk_en = en;
HP_SYS_CLKRST.lcdcam_ctrl0.reg_lcdcam_sys_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;
HP_SYS_CLKRST.lcdcam_ctrl0.reg_lcdcam_apb_rst_en = 1;
HP_SYS_CLKRST.lcdcam_ctrl0.reg_lcdcam_apb_rst_en = 0;
HP_SYS_CLKRST.lcdcam_ctrl0.reg_lcdcam_rst_en = 1;
HP_SYS_CLKRST.lcdcam_ctrl0.reg_lcdcam_rst_en = 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;
HP_SYS_CLKRST.lcdcam_cam_ctrl0.reg_cam_clk_en = en;
}
/**
* @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)
{
(void)group_id;
switch (src) {
case CAM_CLK_SRC_XTAL:
HP_SYS_CLKRST.lcdcam_cam_ctrl0.reg_cam_clk_src_sel = 0;
break;
case CAM_CLK_SRC_PLL160M:
HP_SYS_CLKRST.lcdcam_cam_ctrl0.reg_cam_clk_src_sel = 1;
break;
case CAM_CLK_SRC_APLL:
HP_SYS_CLKRST.lcdcam_cam_ctrl0.reg_cam_clk_src_sel = 2;
break;
default:
HP_SYS_CLKRST.lcdcam_cam_ctrl0.reg_cam_clk_src_sel = 3;
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)
{
(void)dev;
switch (HP_SYS_CLKRST.lcdcam_cam_ctrl0.reg_cam_clk_src_sel) {
case 0:
*src = CAM_CLK_SRC_XTAL;
break;
case 1:
*src = CAM_CLK_SRC_PLL160M;
break;
case 2:
*src = CAM_CLK_SRC_APLL;
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)
{
(void)group_id;
HAL_ASSERT(div_num > 0 && div_num <= CAM_LL_CLK_FRAC_DIV_N_MAX);
HAL_ASSERT(div_a >= 0 && div_a < CAM_LL_CLK_FRAC_DIV_AB_MAX);
HAL_ASSERT(div_b >= 0 && div_b < CAM_LL_CLK_FRAC_DIV_AB_MAX);
HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.lcdcam_cam_ctrl0, reg_cam_clk_div_num, div_num - 1);
HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.lcdcam_cam_ctrl0, reg_cam_clk_div_denominator, div_a);
HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.lcdcam_cam_ctrl0, reg_cam_clk_div_numerator, 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_enable = 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 converter mode
*
* @param dev CAM register base address
* @param src_fourcc Source format FourCC
* @param dst_fourcc Destination format FourCC
*/
static inline void cam_ll_set_convert_mode(lcd_cam_dev_t *dev, uint32_t src_fourcc, uint32_t dst_fourcc)
{
bool src_is_yuv = (src_fourcc == ESP_COLOR_FOURCC_OUYY_EVYY ||
src_fourcc == ESP_COLOR_FOURCC_YVYU ||
src_fourcc == ESP_COLOR_FOURCC_YUYV ||
src_fourcc == ESP_COLOR_FOURCC_UYVY ||
src_fourcc == ESP_COLOR_FOURCC_VYUY ||
src_fourcc == ESP_COLOR_FOURCC_YUV);
bool dst_is_yuv = (dst_fourcc == ESP_COLOR_FOURCC_OUYY_EVYY ||
dst_fourcc == ESP_COLOR_FOURCC_YVYU ||
dst_fourcc == ESP_COLOR_FOURCC_YUYV ||
dst_fourcc == ESP_COLOR_FOURCC_UYVY ||
dst_fourcc == ESP_COLOR_FOURCC_VYUY ||
dst_fourcc == ESP_COLOR_FOURCC_YUV);
if (src_is_yuv && dst_is_yuv) {
HAL_ASSERT(src_fourcc != dst_fourcc);
dev->cam_rgb_yuv.cam_conv_trans_mode = 1;
dev->cam_rgb_yuv.cam_conv_yuv_mode = (src_fourcc == ESP_COLOR_FOURCC_OUYY_EVYY) ? 1 : 0;
dev->cam_rgb_yuv.cam_conv_yuv2yuv_mode = (dst_fourcc == ESP_COLOR_FOURCC_OUYY_EVYY) ? 1 : 0;
} else if (src_is_yuv) {
dev->cam_rgb_yuv.cam_conv_trans_mode = 0;
dev->cam_rgb_yuv.cam_conv_yuv2yuv_mode = 3;
dev->cam_rgb_yuv.cam_conv_yuv_mode = (src_fourcc == ESP_COLOR_FOURCC_OUYY_EVYY) ? 1 : 0;
} else if (dst_is_yuv) {
dev->cam_rgb_yuv.cam_conv_trans_mode = 1;
dev->cam_rgb_yuv.cam_conv_yuv2yuv_mode = 3;
dev->cam_rgb_yuv.cam_conv_yuv_mode = (dst_fourcc == ESP_COLOR_FOURCC_OUYY_EVYY) ? 1 : 0;
}
}
/**
* @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)
{
(void)dev;
(void)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_reg = 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_reg = 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 & 0x2c;
} else {
dev->lc_dma_int_ena.val &= ~(mask & 0x2c);
}
}
/// 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 & 0x2c;
}
/**
* @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 & 0x2c;
}
/**
* @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

View File

@@ -11,6 +11,7 @@
#include "esp_attr.h"
#include "soc/soc.h"
#include "soc/reset_reasons.h"
#include "soc/hp_sys_clkrst_struct.h"
#include "soc/lp_clkrst_struct.h"
#include "soc/lp_clkrst_reg.h"
@@ -33,6 +34,29 @@ FORCE_INLINE_ATTR void _clk_gate_ll_rtc_fast_to_lp_periph_en(bool enable)
_clk_gate_ll_rtc_fast_to_lp_periph_en(__VA_ARGS__); \
} while(0)
/**
* @brief Set the default clock gate configuration
*/
static inline void periph_ll_clk_gate_set_default(soc_reset_reason_t rst_reason)
{
if ((rst_reason == RESET_REASON_CHIP_POWER_ON) || (rst_reason == RESET_REASON_CORE_PMU_PWR_DOWN) ||
(rst_reason == RESET_REASON_SYS_BROWN_OUT) || (rst_reason == RESET_REASON_SYS_RWDT) ||
(rst_reason == RESET_REASON_SYS_SUPER_WDT) || (rst_reason == RESET_REASON_CORE_SW) ||
(rst_reason == RESET_REASON_CORE_MWDT0) || (rst_reason == RESET_REASON_CORE_MWDT1) ||
(rst_reason == RESET_REASON_CORE_RWDT) || (rst_reason == RESET_REASON_CORE_PWR_GLITCH) ||
(rst_reason == RESET_REASON_CORE_EFUSE_CRC) || (rst_reason == RESET_REASON_CORE_USB_JTAG) ||
(rst_reason == RESET_REASON_CORE_USB_UART)
) {
/*
* Default lcdcam_lcdcam_ctrl0:
* reg_lcdcam_clk_src_sel: 1 (BBPLL 120 MHz path, shared by LCD and CAM)
* reg_lcdcam_clk_div_num: 1 (divide by 2 → 60 MHz)
*/
HP_SYS_CLKRST.lcdcam_lcdcam_ctrl0.reg_lcdcam_clk_src_sel = 1;
HP_SYS_CLKRST.lcdcam_lcdcam_ctrl0.reg_lcdcam_clk_div_num = 1;
}
}
#ifdef __cplusplus
}
#endif

View File

@@ -71,6 +71,8 @@ typedef enum {
static inline void lcd_ll_enable_bus_clock(int group_id, bool enable)
{
(void)group_id;
// the core clock is a shared clock for both LCD and CAM
HP_SYS_CLKRST.lcdcam_lcdcam_ctrl0.reg_lcdcam_clk_en = enable;
HP_SYS_CLKRST.lcdcam_ctrl0.reg_lcdcam_apb_clk_en = enable;
HP_SYS_CLKRST.lcdcam_ctrl0.reg_lcdcam_sys_clk_en = enable;
}
@@ -112,7 +114,6 @@ static inline void _lcd_ll_reset_register(int group_id)
static inline void lcd_ll_enable_clock(lcd_cam_dev_t *dev, bool en)
{
(void)dev;
HP_SYS_CLKRST.lcdcam_lcdcam_ctrl0.reg_lcdcam_clk_en = en;
HP_SYS_CLKRST.lcdcam_lcd_ctrl0.reg_lcd_clk_en = en;
}

View File

@@ -25,6 +25,7 @@
#include "esp_private/esp_sleep_internal.h"
#include "esp_private/esp_modem_clock.h"
#include "esp_private/periph_ctrl.h"
#include "hal/clk_gate_ll.h"
#include "esp_private/esp_clk.h"
#include "esp_private/esp_pmu.h"
#include "esp_rom_serial_output.h"
@@ -194,4 +195,6 @@ __attribute__((weak)) void esp_perip_clk_init(void)
: MODEM_CLOCK_LPCLK_SRC_RC_SLOW);
modem_clock_select_lp_clock_source(PERIPH_WIFI_MODULE, modem_lpclk_src, 0);
#endif
periph_ll_clk_gate_set_default(esp_rom_get_reset_reason(0));
}

View File

@@ -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
@@ -911,6 +915,10 @@ config SOC_KEY_MANAGER_ECDSA_KEY_DEPLOY
bool
default y
config SOC_LCDCAM_CAM_SUPPORT_RGB_YUV_CONV
bool
default y
config SOC_EFUSE_SECURE_BOOT_KEY_DIGESTS
int
default 3

View File

@@ -179,6 +179,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;
//////////////////////////////////////////////////CAM///////////////////////////////////////////////////////////////////
/**
* @brief Array initializer for all supported clock sources of CAM
*/
#define SOC_CAM_CLKS {SOC_MOD_CLK_PLL_F160M, SOC_MOD_CLK_XTAL, SOC_MOD_CLK_APLL}
/**
* @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_XTAL = SOC_MOD_CLK_XTAL, /*!< Select XTAL as the source clock */
CAM_CLK_SRC_APLL = SOC_MOD_CLK_APLL, /*!< Select APLL as the source clock */
CAM_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the default choice */
} soc_periph_cam_clk_src_t;
//////////////////////////////////////////////////SYSTIMER//////////////////////////////////////////////////////////////
/**

View File

@@ -35,7 +35,7 @@
#define SOC_DMA2D_SUPPORTED 1
#define SOC_GPTIMER_SUPPORTED 1
#define SOC_LCDCAM_SUPPORTED 1
// #define SOC_LCDCAM_CAM_SUPPORTED 1 // TODO: [ESP32S31] IDF-14722
#define SOC_LCDCAM_CAM_SUPPORTED 1
#define SOC_LCDCAM_I80_LCD_SUPPORTED 1
#define SOC_LCDCAM_RGB_LCD_SUPPORTED 1
#define SOC_LCD_I80_SUPPORTED 1
@@ -365,6 +365,10 @@
// TODO: [ESP32S31] IDF-14626
#define SOC_KEY_MANAGER_ECDSA_KEY_DEPLOY 1 /*!< Key manager responsible to deploy ECDSA key */
// #define SOC_KEY_MANAGER_FE_KEY_DEPLOY 1 /*!< Key manager responsible to deploy Flash Encryption key */
/*--------------------------- CAM ---------------------------------*/
#define SOC_LCDCAM_CAM_SUPPORT_RGB_YUV_CONV (1)
/*-------------------------- Secure Boot CAPS----------------------------*/
// TODO: [ESP32S31] IDF-14629
#define SOC_EFUSE_SECURE_BOOT_KEY_DIGESTS 3

View File

@@ -343,6 +343,7 @@ Application Examples
* :example:`peripherals/camera/mipi_isp_dsi` Demonstrates how to use the ``esp_driver_cam`` component to capture signals from a MIPI CSI camera sensor via the ISP module and display them on an LCD screen via a DSI interface.
* :example:`peripherals/camera/dvp_isp_dsi` Demonstrates how to use the ``esp_driver_cam`` component to capture signals from a DVP camera sensor via the ISP module and display them on an LCD screen via a DSI interface.
* :example:`peripherals/camera/dvp_dsi` Demonstrates how to use the ``esp_driver_cam`` component to capture DVP camera sensor data and display it on a MIPI DSI LCD.
API Reference
-------------

View File

@@ -343,6 +343,7 @@ IRAM 安全
* :example:`peripherals/camera/mipi_isp_dsi` 演示了如何使用 ``esp_driver_cam`` 组件从 MIPI CSI 摄像头传感器捕获信号,传入 ISP 模块,并通过 DSI 接口将其显示在 LCD 屏幕上。
* :example:`peripherals/camera/dvp_isp_dsi` 演示了如何使用 ``esp_driver_cam`` 组件从 DVP 摄像头传感器捕获信号,传入 ISP 模块,并通过 DSI 接口将其显示在 LCD 屏幕上。
* :example:`peripherals/camera/dvp_dsi` 演示了如何使用 ``esp_driver_cam`` 组件通过 DVP 接口采集摄像头传感器数据,并在 MIPI DSI LCD 上显示。
API 参考
--------

View File

@@ -73,19 +73,6 @@ examples/peripherals/camera/dvp_isp_dsi:
- esp_driver_dma
- soc
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
- esp_driver_dma
- soc
examples/peripherals/camera/mipi_isp_dsi:
disable:
- if: SOC_MIPI_CSI_SUPPORTED != 1 or SOC_MIPI_DSI_SUPPORTED != 1

View File

@@ -1,12 +1,12 @@
/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "esp_err.h"
@@ -36,9 +36,9 @@ void example_sensor_init(example_sensor_config_t *sensor_config, example_sensor_
//---------------SCCB Init------------------//
esp_cam_sensor_config_t cam_config = {
.reset_pin = -1,
.pwdn_pin = -1,
.xclk_pin = -1,
.reset_pin = sensor_config->reset_pin,
.pwdn_pin = sensor_config->pwdn_pin,
.xclk_pin = sensor_config->xclk_pin,
};
esp_cam_sensor_device_t *cam = NULL;

View File

@@ -1,4 +1,4 @@
dependencies:
espressif/esp_cam_sensor: "^1.2.1"
espressif/esp_cam_sensor: "^2.1.0"
idf:
version: ">=5.3.0"

View File

@@ -28,6 +28,9 @@ typedef struct {
int i2c_port_num; /* SCCB: i2c port */
int i2c_sda_io_num; /* SCCB: i2c SDA IO number */
int i2c_scl_io_num; /* SCCB: i2c SCL IO number */
int reset_pin; /* Sensor: reset GPIO number, -1 if unused */
int pwdn_pin; /* Sensor: power-down GPIO number, -1 if unused */
int xclk_pin; /* Sensor: XCLK GPIO number, -1 if unused */
esp_cam_sensor_port_t port; /* Sensor: interface of the camera sensor */
const char *format_name; /* Sensor: format to be set for the camera sensor */
} example_sensor_config_t;

View File

@@ -132,19 +132,10 @@ If you see the following console output, your example should be running correctl
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
I (1701) sensor_init: fmt[0].name:DVP_8bit_20Minput_RGB565_LE_640x480_6fps
I (1701) sensor_init: fmt[1].name:DVP_8bit_20Minput_YUV422_UYVY_640x480_6fps
I (1711) sensor_init: fmt[2].name:DVP_8bit_20Minput_RGB565_LE_240x240_25fps
I (8291) sensor_init: Format in use:DVP_8bit_20Minput_RGB565_LE_640x480_6fps
```

View File

@@ -125,6 +125,9 @@ void app_main(void)
.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,
.reset_pin = -1,
.pwdn_pin = -1,
.xclk_pin = EXAMPLE_DVP_CAM_XCLK_IO,
.port = ESP_CAM_SENSOR_DVP,
.format_name = EXAMPLE_CAM_FORMAT,
};

View File

@@ -61,13 +61,13 @@ extern "C" {
#if CONFIG_EXAMPLE_CAM_HRES_640
#if CONFIG_EXAMPLE_CAM_VRES_480
#define EXAMPLE_CAM_FORMAT "DVP_8bit_20Minput_RGB565_640x480_6fps"
#define EXAMPLE_CAM_FORMAT "DVP_8bit_20Minput_RGB565_LE_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"
#define EXAMPLE_CAM_FORMAT "DVP_8bit_20Minput_RGB565_LE_240x240_25fps"
#endif
#endif

View File

@@ -1,2 +1,4 @@
CONFIG_CAMERA_OV2640=y
CONFIG_CAMERA_OV2640_DVP_RGB565_640X480_6FPS=y
CONFIG_CAMERA_OV2640_DVP_RGB565_240X240_25FPS=y
CONFIG_CAM_CTRL_SPI_ENABLE=n

View File

@@ -134,17 +134,10 @@ If you see the following console output, your example should be running correctl
I (1509) main_task: Calling app_main()
I (1509) ek79007: version: 1.0.1
I (1549) ov2640: Detected Camera sensor PID=0x26
I (1549) sensor_init: fmt[0].name:DVP_8bit_20Minput_RGB565_640x480_6fps
I (1549) sensor_init: fmt[1].name:DVP_8bit_20Minput_YUV422_640x480_6fps
I (1549) sensor_init: fmt[2].name:DVP_8bit_20Minput_JPEG_640x480_25fps
I (1559) sensor_init: fmt[3].name:DVP_8bit_20Minput_RGB565_240x240_25fps
I (1569) sensor_init: fmt[4].name:DVP_8bit_20Minput_YUV422_240x240_25fps
I (1569) sensor_init: fmt[5].name:DVP_8bit_20Minput_JPEG_320x240_50fps
I (1579) sensor_init: fmt[6].name:DVP_8bit_20Minput_JPEG_1280x720_12fps
I (1589) sensor_init: fmt[7].name:DVP_8bit_20Minput_JPEG_1600x1200_12fps
I (1589) sensor_init: fmt[8].name:DVP_8bit_20Minput_RAW8_800x640_15fps
I (1599) sensor_init: fmt[9].name:DVP_8bit_20Minput_RAW8_800x800_15fps
I (1609) sensor_init: fmt[10].name:DVP_8bit_20Minput_RAW8_1024x600_15fps
I (1549) sensor_init: fmt[0].name:DVP_8bit_20Minput_YUV422_UYVY_640x480_6fps
I (1559) sensor_init: fmt[1].name:DVP_8bit_20Minput_RAW8_800x640_15fps
I (1569) sensor_init: fmt[2].name:DVP_8bit_20Minput_RAW8_800x800_15fps
I (1579) sensor_init: fmt[3].name:DVP_8bit_20Minput_RAW8_1024x600_15fps
I (2609) sensor_init: Format in use:DVP_8bit_20Minput_RAW8_1024x600_15fps
```

View File

@@ -105,6 +105,9 @@ void app_main(void)
.i2c_port_num = I2C_NUM_0,
.i2c_sda_io_num = EXAMPLE_ISP_DVP_CAM_SCCB_SDA_IO,
.i2c_scl_io_num = EXAMPLE_ISP_DVP_CAM_SCCB_SCL_IO,
.reset_pin = -1,
.pwdn_pin = -1,
.xclk_pin = EXAMPLE_ISP_DVP_CAM_XCLK_IO,
.port = ESP_CAM_SENSOR_DVP,
.format_name = EXAMPLE_CAM_FORMAT,
};

View File

@@ -1,4 +1,8 @@
CONFIG_CAMERA_OV2640=y
CONFIG_CAMERA_SC030IOT=y
CONFIG_CAMERA_SC030IOT_DVP_RAW8_640X480_26FPS=y
CONFIG_CAMERA_OV2640_DVP_RAW8_800X640_15FPS=y
CONFIG_CAMERA_OV2640_DVP_RAW8_800X800_15FPS=y
CONFIG_CAMERA_OV2640_DVP_RAW8_1024X600_15FPS=y
CONFIG_EXAMPLE_MIPI_DSI_DISP_USE_DMA2D=y
CONFIG_CAM_CTRL_SPI_ENABLE=n

View File

@@ -1,8 +0,0 @@
# 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.22)
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)

View File

@@ -1,137 +0,0 @@
| 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 <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
- [ESP-IDF: Camera Controller Driver](https://docs.espressif.com/projects/esp-idf/en/latest/esp32p4/api-reference/peripherals/camera_driver.html)

View File

@@ -1,5 +0,0 @@
idf_component_register(SRCS "dvp_spi_lcd_main.c"
INCLUDE_DIRS "."
REQUIRES esp_mm esp_driver_cam esp_driver_i2c esp_lcd
PRIV_REQUIRES esp_driver_ledc
)

View File

@@ -1,44 +0,0 @@
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
choice EXAMPLE_CAM_INPUT_FORMAT
bool "Set camera input format"
default EXAMPLE_CAM_INPUT_FORMAT_YUV422
config EXAMPLE_CAM_INPUT_FORMAT_RGB565
bool "RGB565"
config EXAMPLE_CAM_INPUT_FORMAT_YUV422
bool "YUV422"
endchoice
endmenu

View File

@@ -1,249 +0,0 @@
/*
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hal/cam_ctlr_types.h"
#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_BYTES_PER_PIXEL)
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,
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_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;
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,
#if CONFIG_EXAMPLE_CAM_INPUT_FORMAT_YUV422
.input_data_color_type = CAM_CTLR_COLOR_YUV422_UYVY,
.output_data_color_type = CAM_CTLR_COLOR_RGB565,
#else
.input_data_color_type = CAM_CTLR_COLOR_RGB565,
.output_data_color_type = CAM_CTLR_COLOR_RGB565,
#endif
.conv_std = COLOR_CONV_STD_RGB_YUV_BT601,
.input_range = COLOR_RANGE_LIMIT,
.output_range = COLOR_RANGE_LIMIT,
.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;
}
//--------Allocate Camera Buffer----------//
size_t cam_buffer_size = CONFIG_EXAMPLE_CAM_HRES * CONFIG_EXAMPLE_CAM_VRES * EXAMPLE_RGB565_BYTES_PER_PIXEL;
void *cam_buffer = NULL;
cam_buffer = esp_cam_ctlr_alloc_buffer(cam_handle, cam_buffer_size, EXAMPLE_DVP_CAM_BUF_ALLOC_CAPS);
//--------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 CONFIG_EXAMPLE_CAM_INPUT_FORMAT_YUV422
ESP_LOGI(TAG, "Configure format conversion: YUV422 -> RGB565");
// Configure format conversion
const cam_ctlr_format_conv_config_t conv_cfg = {
.src_format = CAM_CTLR_COLOR_YUV422_UYVY, // Source format: YUV422
.dst_format = CAM_CTLR_COLOR_RGB565, // Destination format: RGB565
.conv_std = COLOR_CONV_STD_RGB_YUV_BT601,
.data_width = 8,
.input_range = COLOR_RANGE_LIMIT,
.output_range = COLOR_RANGE_LIMIT,
};
ESP_ERROR_CHECK(esp_cam_ctlr_format_conversion(cam_handle, &conv_cfg));
#endif
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;
}

View File

@@ -1,76 +0,0 @@
/*
* 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_RGB565_BYTES_PER_PIXEL (EXAMPLE_RGB565_BITS_PER_PIXEL / 8)
#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
#if CONFIG_EXAMPLE_CAM_INPUT_FORMAT_YUV422
#define EXAMPLE_CAM_FORMAT "DVP_8bit_20Minput_YUV422_240x240_25fps" // ov2640
#elif CONFIG_EXAMPLE_CAM_INPUT_FORMAT_RGB565
#define EXAMPLE_CAM_FORMAT "DVP_8bit_20Minput_RGB565_240x240_25fps" // ov2640
#endif
#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

View File

@@ -1,3 +0,0 @@
dependencies:
sensor_init:
path: ${IDF_PATH}/examples/peripherals/camera/common_components/sensor_init

View File

@@ -1,11 +0,0 @@
# 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()')

View File

@@ -1,2 +0,0 @@
CONFIG_CAMERA_OV2640=y
CONFIG_CAM_CTRL_SPI_ENABLE=n

View File

@@ -1,2 +0,0 @@
CONFIG_SPIRAM_SPEED_80M=y
CONFIG_SPIRAM=y

View File

@@ -1,2 +0,0 @@
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y

View File

@@ -74,6 +74,9 @@ void app_main(void)
.i2c_port_num = I2C_NUM_0,
.i2c_sda_io_num = EXAMPLE_MIPI_CSI_CAM_SCCB_SDA_IO,
.i2c_scl_io_num = EXAMPLE_MIPI_CSI_CAM_SCCB_SCL_IO,
.reset_pin = -1,
.pwdn_pin = -1,
.xclk_pin = -1,
.port = ESP_CAM_SENSOR_MIPI_CSI,
.format_name = EXAMPLE_CAM_FORMAT,
};

View File

@@ -1,3 +1,7 @@
CONFIG_CAMERA_SC2336=y
CONFIG_CAMERA_OV5647=y
CONFIG_CAMERA_OV5647_MIPI_RAW8_800X640_50FPS=y
CONFIG_CAMERA_OV5647_MIPI_RAW8_800X800_50FPS=y
CONFIG_CAMERA_OV5647_MIPI_RAW8_800X1280_50FPS=y
CONFIG_CAMERA_SC2336_MIPI_RAW8_1024X600_30FPS=y
CONFIG_CAM_CTRL_SPI_ENABLE=n

View File

@@ -153,6 +153,9 @@ void app_main(void)
.i2c_port_num = I2C_NUM_0,
.i2c_sda_io_num = EXAMPLE_MIPI_CSI_CAM_SCCB_SDA_IO,
.i2c_scl_io_num = EXAMPLE_MIPI_CSI_CAM_SCCB_SCL_IO,
.reset_pin = -1,
.pwdn_pin = -1,
.xclk_pin = -1,
.port = ESP_CAM_SENSOR_MIPI_CSI,
.format_name = EXAMPLE_CAM_FORMAT,
};

View File

@@ -3,5 +3,8 @@ CONFIG_IDF_EXPERIMENTAL_FEATURES=y
CONFIG_SPIRAM_SPEED_200M=y
CONFIG_CAMERA_SC2336=y
CONFIG_CAMERA_OV5647=y
CONFIG_CAMERA_OV5647_MIPI_RAW8_800X640_50FPS=y
CONFIG_CAMERA_OV5647_MIPI_RAW8_800X1280_50FPS=y
CONFIG_CAMERA_SC2336_MIPI_RAW8_1024X600_30FPS=y
CONFIG_CAMERA_OV5647_ENABLE_MOTOR_BY_GPIO0=y
CONFIG_CAM_CTRL_SPI_ENABLE=n

View File

@@ -137,8 +137,6 @@ KNOWN_MISSING = {
'system/ulp/ulp_riscv/gpio_pulse_counter',
'system/unit_test/test',
# TODO IDF-15382: add :example: references for peripherals examples
'peripherals/camera/dvp_dsi',
'peripherals/camera/dvp_spi_lcd',
'peripherals/h264',
'peripherals/i2s/i2s_advance/i2s_usb',
'peripherals/spi_slave/receiver',