feat(cam): add dvp example for ESP32S3-EYE

This commit is contained in:
gaoxu
2025-06-10 11:42:10 +08:00
parent a0578c231d
commit 041a0fa9ee
14 changed files with 513 additions and 1 deletions

View File

@@ -117,6 +117,7 @@ esp_err_t esp_cam_ctlr_dvp_dma_init(esp_cam_ctlr_dvp_dma_t *dma, uint32_t burst_
ESP_LOGD(TAG, "alignment_size: %d, dma->desc_count: %d, dma->desc_size: %d", alignment_size, dma->desc_count, dma->desc_size); ESP_LOGD(TAG, "alignment_size: %d, dma->desc_count: %d, dma->desc_size: %d", alignment_size, dma->desc_count, dma->desc_size);
dma->desc = heap_caps_aligned_alloc(alignment_size, dma->desc_size, DVP_GDMA_DESC_ALLOC_CAPS); dma->desc = heap_caps_aligned_alloc(alignment_size, dma->desc_size, DVP_GDMA_DESC_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(dma->desc, ESP_ERR_NO_MEM, fail1, TAG, "no mem for DVP DMA descriptor"); ESP_GOTO_ON_FALSE(dma->desc, ESP_ERR_NO_MEM, fail1, TAG, "no mem for DVP DMA descriptor");
return ESP_OK; return ESP_OK;

View File

@@ -46,6 +46,17 @@ examples/peripherals/camera/dvp_isp_dsi:
- esp_lcd - esp_lcd
- esp_driver_cam - esp_driver_cam
examples/peripherals/camera/dvp_spi_lcd:
disable:
- if: SOC_LCDCAM_CAM_SUPPORTED != 1
disable_test:
- if: IDF_TARGET == "esp32s3"
temporary: true
reason: lack of runners
depends_components:
- esp_lcd
- esp_driver_cam
examples/peripherals/camera/mipi_isp_dsi: examples/peripherals/camera/mipi_isp_dsi:
disable: disable:
- if: SOC_MIPI_CSI_SUPPORTED != 1 or SOC_MIPI_DSI_SUPPORTED != 1 - if: SOC_MIPI_CSI_SUPPORTED != 1 or SOC_MIPI_DSI_SUPPORTED != 1

View File

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

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_spi_lcd)

View File

@@ -0,0 +1,139 @@
| Supported Targets | ESP32-P4 | ESP32-S3 |
| ----------------- | -------- | -------- |
# DVP Camera display via LCD example
## Overview
This example demonstrates how to use the esp_driver_cam component to capture DVP camera sensor signals and display it via LCD interface. This example will auto-detect camera sensors via [ESP camera sensor driver](https://components.espressif.com/components/espressif/esp_cam_sensor) and capture camera sensor signals via DVP interface and display it via LCD interface.
## Usage
The subsections below give only absolutely necessary information. For full steps to configure ESP-IDF and use it to build and run projects, see [ESP-IDF Getting Started](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html#get-started).
### Hardware Required
- ESP32S3 devkit with OV2640 camera sensor and ST7789 LCD screen
- or an ESP32S3-EYE dev-kit
You can also connect camera sensors and LCD screens from other vendors to the ESP chip, you can find corresponding camera or LCD drivers from [ESP Component Registry](https://components.espressif.com), or design your own customized drivers.
GND GND
┌────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────┐
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ ┌───────────────┴─────────────┴──────────────────┐ │
│ │ │ ┌──────────┴───────────┐
│ │ │ LCD MOSI │ │
│ │ ├───────────────────────────┤ │
┌───────────┴─────────┐ │ │ │ │
│ │ │ │ LCD CLK │ │
│ │ │ ├───────────────────────────┤ │
│ │ XCLK │ ESP_CHIP │ │ │
│ DVP Camera ├──────────────────────┤ │ LCD CS │ LCD Screen │
│ │ │ ├───────────────────────────┤ │
│ │ D0~7 │ │ │ │
│ ├──────────────────────┤ │ LCD DC │ │
│ │ │ ├───────────────────────────┤ │
│ │ PCLK │ │ │ │
│ ├──────────────────────┤ │ LCD BACKLIGHT │ │
│ │ │ ├───────────────────────────┤ │
│ │ VSYNC │ │ │ │
│ ├──────────────────────┤ │ │ │
│ │ │ │ │ │
│ │ DE (HREF) │ │ │ │
│ ├──────────────────────┤ │ └──────────────────────┘
│ │ │ │
└───────┬──┬──────────┘ │ │
│ │ I2C SCL │ │
│ └─────────────────────────────────┤ │
│ I2C SDA │ │
└────────────────────────────────────┤ │
└────────────────────────────────────────────────┘
### Set Chip Target
First of all, your target must be supported by both:
- **By your ESP-IDF version**: For the full list of supported targets, run:
```
idf.py --list-targets
```
- **By this example**: For the full list of supported targets, refer to the supported targets table at the top of this README.
After you make sure that your target is supported, go to your example project directory and [set the chip target](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/tools/idf-py.html#select-the-target-chip-set-target):
```
idf.py set-target <target>
```
For example, to set esp32-S3 as the chip target, run:
```
idf.py set-target esp32s3
```
### Configure the Project
For information about Kconfig options, see [Project Configuration](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/kconfig.html) > _Name of relevant section(s)_.
To conveniently check or modify Kconfig options for this example in a project configuration menu, run:
```
idf.py menuconfig
```
```
Set CONFIG_CAMERA_OV2640 to y
```
Available options for the camera sensor output horizontal/vertical resolution can be seen in ``menuconfig`` > ``Example Configuration``.
### Build and Flash
Execute the following command to build the project, flash it to your development board, and run the monitor tool to view the serial output:
```
idf.py build flash monitor
```
This command can be reduced to `idf.py flash monitor`.
If the above command fails, check the log on the serial monitor which usually provides information on the possible cause of the issue.
To exit the serial monitor, use `Ctrl` + `]`.
## Example Output
If you see the following console output, your example should be running correctly:
```
I (1481) main_task: Calling app_main()
I (278) dvp_spi_lcd: Init SPI bus
I (278) dvp_spi_lcd: New panel IO SPI
I (278) dvp_spi_lcd: New ST7789 panel
I (278) dvp_spi_lcd: Reset and init panel
I (408) dvp_spi_lcd: Turn on display
I (408) dvp_spi_lcd: Screen lit up now!
```
## Reference
- Link to the ESP-IDF feature's API reference, for example [ESP-IDF: Camera Controller Driver](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/camera_driver.html)
- [ESP-IDF Getting Started](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html#get-started)
- [Project Configuration](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/kconfig.html) (Kconfig Options)

View File

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

View File

@@ -0,0 +1,33 @@
menu "Example Configuration"
choice EXAMPLE_CAM_HRES
bool "Set camera horizontal resolution"
default EXAMPLE_CAM_HRES_240
config EXAMPLE_CAM_HRES_640
bool "640"
config EXAMPLE_CAM_HRES_240
bool "240"
endchoice
config EXAMPLE_CAM_HRES
int
default 640 if EXAMPLE_CAM_HRES_640
default 240 if EXAMPLE_CAM_HRES_240
choice EXAMPLE_CAM_VRES
bool "Set camera vertical resolution"
default EXAMPLE_CAM_VRES_480 if EXAMPLE_CAM_HRES_640
default EXAMPLE_CAM_VRES_240 if EXAMPLE_CAM_HRES_240
config EXAMPLE_CAM_VRES_480
bool "480"
config EXAMPLE_CAM_VRES_240
bool "240"
endchoice
config EXAMPLE_CAM_VRES
int
default 480 if EXAMPLE_CAM_VRES_480
default 240 if EXAMPLE_CAM_VRES_240
endmenu

View File

@@ -0,0 +1,226 @@
/*
* 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_panel_ops.h"
#include "esp_cache.h"
#include "driver/i2c_master.h"
#include "esp_cam_ctlr.h"
#include "esp_cam_ctlr_dvp.h"
#include "example_config.h"
#include "driver/ledc.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h"
#include "example_sensor_init.h"
static const char *TAG = "dvp_spi_lcd";
#define BUFFER_SIZE (CONFIG_EXAMPLE_CAM_HRES * CONFIG_EXAMPLE_CAM_VRES * EXAMPLE_RGB565_BITS_PER_PIXEL / 8)
typedef struct {
esp_lcd_panel_handle_t panel_hdl;
esp_cam_ctlr_trans_t cam_trans;
} example_cam_context_t;
static bool s_camera_get_new_vb(esp_cam_ctlr_handle_t handle, esp_cam_ctlr_trans_t *trans, void *user_data);
static bool s_camera_get_finished_trans(esp_cam_ctlr_handle_t handle, esp_cam_ctlr_trans_t *trans, void *user_data);
static void lcd_display_init(esp_lcd_panel_handle_t *lcd_panel_hdl, esp_lcd_panel_io_handle_t lcd_io_hdl)
{
esp_lcd_panel_handle_t panel_handle = NULL;
//----------LEDC initialization------------//
const ledc_timer_config_t lcd_timer = {
.speed_mode = LEDC_LOW_SPEED_MODE,
.duty_resolution = LEDC_TIMER_10_BIT,
.timer_num = EXAMPLE_LEDC_LCD_BACKLIGHT,
.freq_hz = 5000,
.clk_cfg = LEDC_AUTO_CLK
};
ESP_ERROR_CHECK(ledc_timer_config(&lcd_timer));
const ledc_channel_config_t lcd_channel = {
.gpio_num = EXAMPLE_LCD_BACKLIGHT,
.speed_mode = LEDC_LOW_SPEED_MODE,
.channel = LEDC_CHANNEL_0,
.timer_sel = EXAMPLE_LEDC_LCD_BACKLIGHT,
.intr_type = LEDC_INTR_DISABLE,
.duty = 0,
.hpoint = 0,
.flags.output_invert = true,
};
ESP_ERROR_CHECK(ledc_channel_config(&lcd_channel));
//----------SPI initialization------------//
ESP_LOGI(TAG, "Init SPI bus");
const spi_bus_config_t bus_cfg = {
.sclk_io_num = EXAMPLE_LCD_SPI_CLK,
.mosi_io_num = EXAMPLE_LCD_SPI_MOSI,
.miso_io_num = GPIO_NUM_NC,
.quadwp_io_num = GPIO_NUM_NC,
.quadhd_io_num = GPIO_NUM_NC,
.max_transfer_sz = BUFFER_SIZE,
};
ESP_ERROR_CHECK(spi_bus_initialize(EXAMPLE_LCD_SPI_NUM, &bus_cfg, SPI_DMA_CH_AUTO));
//----------Panel IO initialization------------//
ESP_LOGI(TAG, "New panel IO SPI");
const esp_lcd_panel_io_spi_config_t io_cfg = {
.dc_gpio_num = EXAMPLE_LCD_DC,
.cs_gpio_num = EXAMPLE_LCD_SPI_CS,
.pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
.lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS,
.lcd_param_bits = EXAMPLE_LCD_PARAM_BITS,
.spi_mode = 2,
.trans_queue_depth = 10,
};
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi(
(esp_lcd_spi_bus_handle_t)EXAMPLE_LCD_SPI_NUM,
&io_cfg,
&lcd_io_hdl
));
//----------ST7789 Panel initialization------------//
ESP_LOGI(TAG, "New ST7789 panel");
const esp_lcd_panel_dev_config_t panel_dev_cfg = {
.reset_gpio_num = EXAMPLE_LCD_RST,
.color_space = ESP_LCD_COLOR_SPACE_RGB,
.bits_per_pixel = EXAMPLE_RGB565_BITS_PER_PIXEL,
};
ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(lcd_io_hdl, &panel_dev_cfg, &panel_handle));
ESP_LOGI(TAG, "Reset and init panel");
esp_lcd_panel_reset(panel_handle);
esp_lcd_panel_init(panel_handle);
esp_lcd_panel_invert_color(panel_handle, true);
ESP_LOGI(TAG, "Turn on display");
esp_lcd_panel_disp_on_off(panel_handle, true);
const int brightness = 100;
uint32_t duty = (1023 * brightness) / 100;
ESP_ERROR_CHECK(ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, duty));
ESP_ERROR_CHECK(ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0));
*lcd_panel_hdl = panel_handle;
}
void app_main(void)
{
esp_err_t ret = ESP_FAIL;
esp_lcd_panel_handle_t lcd_panel_hdl = NULL;
esp_lcd_panel_io_handle_t lcd_io_hdl = NULL;
size_t cam_buffer_size = CONFIG_EXAMPLE_CAM_HRES * CONFIG_EXAMPLE_CAM_VRES * EXAMPLE_RGB565_BITS_PER_PIXEL / 8;
void *cam_buffer = heap_caps_malloc(cam_buffer_size, EXAMPLE_DVP_CAM_BUF_ALLOC_CAPS);
if (!cam_buffer) {
ESP_LOGE(TAG, "no mem for cam_buffer");
return;
}
lcd_display_init(&lcd_panel_hdl, lcd_io_hdl);
//----------CAM Controller Init------------//
esp_cam_ctlr_handle_t cam_handle = NULL;
esp_cam_ctlr_dvp_pin_config_t pin_cfg = {
.data_width = EXAMPLE_DVP_CAM_DATA_WIDTH,
.data_io = {
EXAMPLE_DVP_CAM_D0_IO,
EXAMPLE_DVP_CAM_D1_IO,
EXAMPLE_DVP_CAM_D2_IO,
EXAMPLE_DVP_CAM_D3_IO,
EXAMPLE_DVP_CAM_D4_IO,
EXAMPLE_DVP_CAM_D5_IO,
EXAMPLE_DVP_CAM_D6_IO,
EXAMPLE_DVP_CAM_D7_IO,
},
.vsync_io = EXAMPLE_DVP_CAM_VSYNC_IO,
.de_io = EXAMPLE_DVP_CAM_DE_IO,
.pclk_io = EXAMPLE_DVP_CAM_PCLK_IO,
.xclk_io = EXAMPLE_DVP_CAM_XCLK_IO,
};
esp_cam_ctlr_dvp_config_t dvp_config = {
.ctlr_id = 0,
.clk_src = CAM_CLK_SRC_DEFAULT,
.h_res = CONFIG_EXAMPLE_CAM_HRES,
.v_res = CONFIG_EXAMPLE_CAM_VRES,
.input_data_color_type = CAM_CTLR_COLOR_RGB565,
.dma_burst_size = 64,
.pin = &pin_cfg,
.bk_buffer_dis = 1,
.xclk_freq = EXAMPLE_DVP_CAM_XCLK_FREQ_HZ,
};
ret = esp_cam_new_dvp_ctlr(&dvp_config, &cam_handle);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "dvp init fail[%d]", ret);
return;
}
//--------Camera Sensor and SCCB Init-----------//
example_sensor_config_t cam_sensor_config = {
.i2c_port_num = I2C_NUM_0,
.i2c_sda_io_num = EXAMPLE_DVP_CAM_SCCB_SDA_IO,
.i2c_scl_io_num = EXAMPLE_DVP_CAM_SCCB_SCL_IO,
.port = ESP_CAM_SENSOR_DVP,
.format_name = EXAMPLE_CAM_FORMAT,
};
example_sensor_handle_t sensor_handle = {
.sccb_handle = NULL,
.i2c_bus_handle = NULL,
};
example_sensor_init(&cam_sensor_config, &sensor_handle);
//--------Register Camera Callbacks----------//
example_cam_context_t cam_ctx = {
.panel_hdl = lcd_panel_hdl,
.cam_trans = {
.buffer = cam_buffer,
.buflen = cam_buffer_size,
}
};
esp_cam_ctlr_evt_cbs_t cbs = {
.on_get_new_trans = s_camera_get_new_vb,
.on_trans_finished = s_camera_get_finished_trans,
};
if (esp_cam_ctlr_register_event_callbacks(cam_handle, &cbs, &cam_ctx) != ESP_OK) {
ESP_LOGE(TAG, "ops register fail");
return;
}
//--------Enable and start Camera Controller----------//
ESP_ERROR_CHECK(esp_cam_ctlr_enable(cam_handle));
if (esp_cam_ctlr_start(cam_handle) != ESP_OK) {
ESP_LOGE(TAG, "Driver start fail");
return;
}
while (1) {
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
static bool s_camera_get_new_vb(esp_cam_ctlr_handle_t handle, esp_cam_ctlr_trans_t *trans, void *user_data)
{
example_cam_context_t *ctx = (example_cam_context_t *)user_data;
*trans = ctx->cam_trans;
return false;
}
static bool s_camera_get_finished_trans(esp_cam_ctlr_handle_t handle, esp_cam_ctlr_trans_t *trans, void *user_data)
{
example_cam_context_t *ctx = (example_cam_context_t *)user_data;
esp_lcd_panel_draw_bitmap(ctx->panel_hdl, 0, 0, CONFIG_EXAMPLE_CAM_HRES, CONFIG_EXAMPLE_CAM_VRES, trans->buffer);
return false;
}

View File

@@ -0,0 +1,71 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "sdkconfig.h"
#ifdef __cplusplus
extern "C" {
#endif
//----------CAM Config------------//
#define EXAMPLE_RGB565_BITS_PER_PIXEL 16
#define EXAMPLE_DVP_CAM_SCCB_SCL_IO (5)
#define EXAMPLE_DVP_CAM_SCCB_SDA_IO (4)
#define EXAMPLE_DVP_CAM_XCLK_FREQ_HZ (20000000)
#define EXAMPLE_DVP_CAM_DATA_WIDTH (8)
#define EXAMPLE_DVP_CAM_D0_IO (11)
#define EXAMPLE_DVP_CAM_D1_IO (9)
#define EXAMPLE_DVP_CAM_D2_IO (8)
#define EXAMPLE_DVP_CAM_D3_IO (10)
#define EXAMPLE_DVP_CAM_D4_IO (12)
#define EXAMPLE_DVP_CAM_D5_IO (18)
#define EXAMPLE_DVP_CAM_D6_IO (17)
#define EXAMPLE_DVP_CAM_D7_IO (16)
#define EXAMPLE_DVP_CAM_XCLK_IO (15)
#define EXAMPLE_DVP_CAM_PCLK_IO (13)
#define EXAMPLE_DVP_CAM_DE_IO (7)
#define EXAMPLE_DVP_CAM_VSYNC_IO (6)
#define EXAMPLE_DVP_CAM_HSYNC_IO (-1)
#if CONFIG_SPIRAM
#define EXAMPLE_DVP_CAM_BUF_ALLOC_CAPS (MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA)
#else
#define EXAMPLE_DVP_CAM_BUF_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA)
#endif
#define EXAMPLE_CAM_FORMAT "DVP_8bit_20Minput_RGB565_240x240_25fps" // ov2640
#ifndef EXAMPLE_CAM_FORMAT
#error "Unsupported camera format! Please adjust EXAMPLE_CAM_HRES and EXAMPLE_CAM_VRES in menuconfig"
#endif
//----------LCD Config------------//
#define EXAMPLE_LEDC_DVP_XCLK (LEDC_TIMER_0)
#define EXAMPLE_LEDC_LCD_BACKLIGHT (LEDC_TIMER_1)
#define EXAMPLE_LCD_SPI_NUM (SPI3_HOST)
#define EXAMPLE_LCD_CMD_BITS (8)
#define EXAMPLE_LCD_PARAM_BITS (8)
/* LCD Display */
#define EXAMPLE_LCD_SPI_MOSI (GPIO_NUM_47)
#define EXAMPLE_LCD_SPI_CLK (GPIO_NUM_21)
#define EXAMPLE_LCD_SPI_CS (GPIO_NUM_44)
#define EXAMPLE_LCD_DC (GPIO_NUM_43)
#define EXAMPLE_LCD_RST (GPIO_NUM_NC)
#define EXAMPLE_LCD_BACKLIGHT (GPIO_NUM_48)
#define EXAMPLE_LCD_PIXEL_CLOCK_HZ (80 * 1000 * 1000)
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,3 @@
dependencies:
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_spi_lcd_p4(dut: Dut) -> None:
dut.expect_exact('Calling app_main()')

View File

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

View File

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

View File

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