diff --git a/components/esp_driver_cam/csi/src/esp_cam_ctlr_csi.c b/components/esp_driver_cam/csi/src/esp_cam_ctlr_csi.c index 1136273da0..6c297f1b9c 100644 --- a/components/esp_driver_cam/csi/src/esp_cam_ctlr_csi.c +++ b/components/esp_driver_cam/csi/src/esp_cam_ctlr_csi.c @@ -300,6 +300,7 @@ static bool csi_dma_trans_done_callback(dw_gdma_channel_handle_t chan, const dw_ bool need_yield = false; BaseType_t high_task_woken = pdFALSE; csi_controller_t *ctlr = (csi_controller_t *)user_data; + bool has_new_trans = false; bool use_backup = false; dw_gdma_block_transfer_config_t csi_dma_transfer_config = {}; @@ -328,12 +329,14 @@ static bool csi_dma_trans_done_callback(dw_gdma_channel_handle_t chan, const dw_ use_backup = true; } else { csi_dma_transfer_config.dst.addr = (uint32_t)(new_trans.buffer); + has_new_trans = true; } } else if (xQueueReceiveFromISR(ctlr->trans_que, &new_trans, &high_task_woken) == pdTRUE) { if (!(new_trans.buffer) || new_trans.buflen < ctlr->fb_size_in_bytes) { use_backup = true; } else { csi_dma_transfer_config.dst.addr = (uint32_t)(new_trans.buffer); + has_new_trans = true; } } else if (!ctlr->bk_buffer_dis) { use_backup = true; @@ -344,7 +347,9 @@ static bool csi_dma_trans_done_callback(dw_gdma_channel_handle_t chan, const dw_ new_trans.buflen = ctlr->fb_size_in_bytes; ESP_EARLY_LOGD(TAG, "no new buffer or no long enough new buffer, use driver internal buffer"); csi_dma_transfer_config.dst.addr = (uint32_t)ctlr->backup_buffer; - } else { + } + + if (!has_new_trans) { assert(false && "no new buffer, and no driver internal buffer"); } diff --git a/components/esp_driver_isp/CMakeLists.txt b/components/esp_driver_isp/CMakeLists.txt index a5316bd90e..1ac8cc1ab7 100644 --- a/components/esp_driver_isp/CMakeLists.txt +++ b/components/esp_driver_isp/CMakeLists.txt @@ -9,4 +9,5 @@ endif() idf_component_register(SRCS ${srcs} INCLUDE_DIRS ${public_include} + PRIV_REQUIRES esp_driver_gpio ) diff --git a/components/esp_driver_isp/src/isp_af.c b/components/esp_driver_isp/src/isp_af.c index fa7c067e6c..c534b785cf 100644 --- a/components/esp_driver_isp/src/isp_af.c +++ b/components/esp_driver_isp/src/isp_af.c @@ -241,6 +241,10 @@ esp_err_t esp_isp_af_controller_set_env_detector(isp_af_ctrlr_t af_ctrlr, const isp_ll_af_env_monitor_set_period(af_ctrlr->isp_proc->hal.hw, 0); isp_ll_clear_intr(af_ctrlr->isp_proc->hal.hw, ISP_LL_EVENT_AF_ENV); + isp_ll_af_env_monitor_set_mode(af_ctrlr->isp_proc->hal.hw, ISP_LL_AF_ENV_MONITOR_MODE_ABS); + isp_ll_af_env_monitor_set_period(af_ctrlr->isp_proc->hal.hw, af_ctrlr->config.interval); + isp_ll_enable_intr(af_ctrlr->isp_proc->hal.hw, ISP_LL_EVENT_AF_ENV, true); + return ESP_OK; } @@ -267,7 +271,7 @@ esp_err_t esp_isp_af_env_detector_register_event_callbacks(isp_af_ctrlr_t af_ctr return ESP_OK; } -esp_err_t esp_isp_af_env_detector_set_threshold(isp_af_ctrlr_t af_ctrlr, int definition_thresh, int luminance_thresh) +esp_err_t esp_isp_af_controller_set_env_detector_threshold(isp_af_ctrlr_t af_ctrlr, int definition_thresh, int luminance_thresh) { ESP_RETURN_ON_FALSE_ISR(af_ctrlr, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); ESP_RETURN_ON_FALSE_ISR(af_ctrlr->fsm == ISP_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "detector isn't in enable state"); diff --git a/examples/peripherals/.build-test-rules.yml b/examples/peripherals/.build-test-rules.yml index 174a6caafd..0217624acc 100644 --- a/examples/peripherals/.build-test-rules.yml +++ b/examples/peripherals/.build-test-rules.yml @@ -24,6 +24,13 @@ examples/peripherals/analog_comparator: - esp_driver_gpio - esp_driver_ana_cmpr +examples/peripherals/camera/camera_dsi: + disable: + - if: SOC_MIPI_CSI_SUPPORTED != 1 or SOC_MIPI_DSI_SUPPORTED != 1 + depends_components: + - esp_lcd + - esp_driver_cam + examples/peripherals/dac: disable: - if: SOC_DAC_SUPPORTED != 1 @@ -118,11 +125,11 @@ examples/peripherals/i2s/i2s_recorder: examples/peripherals/isp/auto_focus: disable: - - if: INCLUDE_DEFAULT == 1 - temporary: true - reason: disable build temporarily # TODO: IDF-8895 + - if: SOC_MIPI_CSI_SUPPORTED != 1 or SOC_MIPI_DSI_SUPPORTED != 1 or SOC_ISP_SUPPORTED != 1 depends_components: - esp_driver_isp + - esp_driver_cam + - esp_lcd examples/peripherals/jpeg/jpeg_decode: disable: diff --git a/examples/peripherals/camera/camera_dsi/CMakeLists.txt b/examples/peripherals/camera/camera_dsi/CMakeLists.txt new file mode 100644 index 0000000000..69a158b554 --- /dev/null +++ b/examples/peripherals/camera/camera_dsi/CMakeLists.txt @@ -0,0 +1,6 @@ +# 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) +project(camera_dsi) diff --git a/examples/peripherals/camera/camera_dsi/README.md b/examples/peripherals/camera/camera_dsi/README.md new file mode 100644 index 0000000000..c078a675f3 --- /dev/null +++ b/examples/peripherals/camera/camera_dsi/README.md @@ -0,0 +1,139 @@ +| Supported Targets | ESP32-P4 | +| ----------------- | -------- | + + +# Camera display via DSI example + +## Overview + +This example demonstrates how to use the esp_driver_cam component to capture camera sensor signals 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 + +This example requires: + +- OV5647 camera sensor +- ILI9881C LCD screen +- ESP32P4 devkit + + + GND GND + ┌────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────┐ + │ │ │ │ + │ │ │ │ + │ │ │ │ + │ │ │ │ + │ │ │ │ + │ │ │ │ + │ │ │ │ + │ │ │ │ + │ ┌───────────────┴─────────────┴──────────────────┐ │ + │ │ │ ┌──────────┴───────────┐ + │ │ │ DSI DATA 1P │ │ + │ │ ├───────────────────────────┤ │ + ┌───────────┴─────────┐ CSI DATA 1P │ │ │ │ + │ ├──────────────────────┤ │ DSI DATA 1N │ │ + │ │ │ ├───────────────────────────┤ │ + │ │ CSI DATA 1N │ ESP32-P4 │ │ │ + │ OV5647 ├──────────────────────┤ │ DSI CLK N │ ILI9881C │ + │ │ │ ├───────────────────────────┤ │ + │ │ CSI CLK N │ │ │ │ + │ ├──────────────────────┤ │ DSI CLK P │ │ + │ │ │ ├───────────────────────────┤ │ + │ │ CSI CLK P │ │ │ │ + │ ├──────────────────────┤ │ DSI DATA 0P │ │ + │ │ │ ├───────────────────────────┤ │ + │ │ CSI DATA 0P │ │ │ │ + │ ├──────────────────────┤ │ DSI DATA 0N │ │ + │ │ │ ├───────────────────────────┤ │ + │ │ CSI DATA 0N │ │ │ │ + │ ├──────────────────────┤ │ └──────────────────────┘ + │ │ │ │ + └───────┬──┬──────────┘ │ │ + │ │ I2C SCL │ │ + │ └─────────────────────────────────┤ │ + │ I2C SDA │ │ + └────────────────────────────────────┤ │ + └────────────────────────────────────────────────┘ + + +### Set Chip Target + +First of all, your target must be supported by both: + +- **By your ESP-IDF version**: For the full list of supported targets, run: + ``` + idf.py --list-targets + ``` +- **By this example**: For the full list of supported targets, refer to the supported targets table at the top of this README. + +After you make sure that your target is supported, go to your example project directory and [set the chip target](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/tools/idf-py.html#select-the-target-chip-set-target): + +``` +idf.py set-target +``` + +For example, to set esp32-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_OV5647 to y +``` + + +### 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 (1085) main_task: Calling app_main() +I (1095) ili9881c: ID1: 0x98, ID2: 0x81, ID3: 0x5c +I (1125) gpio: GPIO[31]| InputEn: 1| OutputEn: 1| OpenDrain: 1| Pullup: 1| Pulldown: 0| Intr:0 +I (1125) gpio: GPIO[34]| InputEn: 1| OutputEn: 1| OpenDrain: 1| Pullup: 1| Pulldown: 0| Intr:0 +I (1295) ov5647: Detected Camera sensor PID=0x5647 with index 0 +I (1305) cam_dsi: fmt[0].name:MIPI_2lane_24Minput_RAW8_800x1280_50fps +I (1305) cam_dsi: fmt[1].name:MIPI_2lane_24Minput_RAW8_800x640_50fps +I (1315) cam_dsi: fmt[2].name:MIPI_2lane_24Minput_RAW8_800x800_50fps +I (1355) cam_dsi: Format in use:MIPI_2lane_24Minput_RAW8_800x640_50fps +``` + + +## Reference + +- Link to the ESP-IDF feature's API reference, for example [ESP-IDF: Camera Controller Driver](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/camera_driver.html) +- [ESP-IDF Getting Started](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html#get-started) +- [Project Configuration](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/kconfig.html) (Kconfig Options) diff --git a/examples/peripherals/camera/camera_dsi/main/CMakeLists.txt b/examples/peripherals/camera/camera_dsi/main/CMakeLists.txt new file mode 100644 index 0000000000..8910b7da40 --- /dev/null +++ b/examples/peripherals/camera/camera_dsi/main/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRCS "camera_dsi_main.c" + INCLUDE_DIRS "." + REQUIRES esp_mm esp_driver_isp esp_driver_cam esp_driver_i2c esp_lcd dsi_init + ) diff --git a/examples/peripherals/camera/camera_dsi/main/Kconfig.projbuild b/examples/peripherals/camera/camera_dsi/main/Kconfig.projbuild new file mode 100644 index 0000000000..8117f6ef7b --- /dev/null +++ b/examples/peripherals/camera/camera_dsi/main/Kconfig.projbuild @@ -0,0 +1,41 @@ +menu "Example Configuration" + config EXAMPLE_USED_LDO_CHAN_ID + int "example used LDO channel ID" + default 3 + help + Example used LDO channel ID, you may check datasheet to know more details. + + config EXAMPLE_USED_LDO_VOLTAGE_MV + int "example used LDO voltage in mV" + default 2500 + range 0 3300 + help + Example used LDO voltage, in mV + + choice EXAMPLE_MIPI_CSI_DISP_HRES + bool "Set MIPI CSI horizontal resolution" + default EXAMPLE_MIPI_CSI_HRES_800 + + config EXAMPLE_MIPI_CSI_HRES_800 + bool "800" + endchoice + + config EXAMPLE_MIPI_CSI_DISP_HRES + int + default 800 if EXAMPLE_MIPI_CSI_HRES_800 + + choice EXAMPLE_MIPI_CSI_DISP_VRES + bool "Set MIPI CSI vertical resolution" + default EXAMPLE_MIPI_CSI_VRES_640 + + config EXAMPLE_MIPI_CSI_VRES_640 + bool "640" + config EXAMPLE_MIPI_CSI_VRES_1280 + bool "1280" + endchoice + + config EXAMPLE_MIPI_CSI_DISP_VRES + int + default 640 if EXAMPLE_MIPI_CSI_VRES_640 + default 1280 if EXAMPLE_MIPI_CSI_VRES_1280 +endmenu diff --git a/examples/peripherals/camera/camera_dsi/main/camera_dsi_main.c b/examples/peripherals/camera/camera_dsi/main/camera_dsi_main.c new file mode 100644 index 0000000000..9087aa28fb --- /dev/null +++ b/examples/peripherals/camera/camera_dsi/main/camera_dsi_main.c @@ -0,0 +1,212 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#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_lcd_ili9881c.h" +#include "esp_ldo_regulator.h" +#include "esp_cache.h" +#include "driver/i2c_master.h" +#include "driver/isp.h" +#include "esp_cam_ctlr_csi.h" +#include "esp_cam_ctlr.h" +#include "esp_sccb_intf.h" +#include "esp_sccb_i2c.h" +#include "esp_cam_sensor.h" +#include "ov5647.h" +#include "example_dsi_init.h" +#include "example_config.h" + +static const char *TAG = "cam_dsi"; + +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); + +void app_main(void) +{ + esp_err_t ret = ESP_FAIL; + esp_lcd_panel_handle_t ili9881c_ctrl_panel = 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)); + + /** + * @background + * Sensor use RAW8 + * ISP convert to RGB565 + */ + //---------------DSI Init------------------// + example_dsi_resource_alloc(&ili9881c_ctrl_panel, &mipi_dpi_panel, &frame_buffer); + + //---------------Necessary variable config------------------// + frame_buffer_size = CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES * EXAMPLE_MIPI_DSI_IMAGE_VSIZE * EXAMPLE_RGB565_BITS_PER_PIXEL / 8; + + ESP_LOGD(TAG, "CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES: %d, EXAMPLE_MIPI_DSI_IMAGE_VSIZE: %d, bits per pixel: %d", CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES, EXAMPLE_MIPI_DSI_IMAGE_VSIZE, 8); + ESP_LOGD(TAG, "frame_buffer_size: %zu", frame_buffer_size); + ESP_LOGD(TAG, "frame_buffer: %p", frame_buffer); + + esp_cam_ctlr_trans_t new_trans = { + .buffer = frame_buffer, + .buflen = frame_buffer_size, + }; + + //---------------I2C Init------------------// + i2c_master_bus_config_t i2c_bus_conf = { + .clk_source = I2C_CLK_SRC_DEFAULT, + .sda_io_num = EXAMPLE_MIPI_SCCB_SDA_IO, + .scl_io_num = EXAMPLE_MIPI_SCCB_SCL_IO, + .i2c_port = I2C_NUM_0, + .flags.enable_internal_pullup = true, + }; + i2c_master_bus_handle_t bus_handle = NULL; + ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_conf, &bus_handle)); + + //---------------SCCB Init------------------// + esp_sccb_io_handle_t ov5647_io_handle = NULL; + sccb_i2c_config_t i2c_config = { + .scl_speed_hz = EXAMPLE_MIPI_SCCB_FREQ, + .device_address = EXAMPLE_OV5647_DEV_ADDR, + .dev_addr_length = I2C_ADDR_BIT_LEN_7, + }; + ESP_ERROR_CHECK(sccb_new_i2c_io(bus_handle, &i2c_config, &ov5647_io_handle)); + + //---------------CSI Init------------------// + esp_cam_ctlr_csi_config_t csi_config = { + .ctlr_id = 0, + .h_res = CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES, + .v_res = CONFIG_EXAMPLE_MIPI_CSI_DISP_VRES, + .lane_bit_rate_mbps = EXAMPLE_MIPI_CSI_LANE_BITRATE_MBPS, + .input_data_color_type = MIPI_CSI_COLOR_RAW8, + .output_data_color_type = MIPI_CSI_COLOR_RGB565, + .data_lane_num = 2, + .byte_swap_en = false, + .queue_items = 1, + }; + esp_cam_ctlr_handle_t cam_handle = NULL; + ret = esp_cam_new_csi_ctlr(&csi_config, &cam_handle); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "csi init fail[%d]", ret); + return; + } + + 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, &new_trans) != ESP_OK) { + ESP_LOGE(TAG, "ops register fail"); + return; + } + + ESP_ERROR_CHECK(esp_cam_ctlr_enable(cam_handle)); + + //---------------ISP Init------------------// + isp_proc_handle_t isp_proc = NULL; + esp_isp_processor_cfg_t isp_config = { + .clk_hz = 80 * 1000 * 1000, + .input_data_source = ISP_INPUT_DATA_SOURCE_CSI, + .input_data_color_type = ISP_COLOR_RAW8, + .output_data_color_type = ISP_COLOR_RGB565, + .has_line_start_packet = false, + .has_line_end_packet = false, + .h_res = CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES, + .v_res = CONFIG_EXAMPLE_MIPI_CSI_DISP_VRES, + }; + ESP_ERROR_CHECK(esp_isp_new_processor(&isp_config, &isp_proc)); + ESP_ERROR_CHECK(esp_isp_enable(isp_proc)); + + //---------------DSI Panel Init------------------// + example_dsi_ili9881c_panel_init(ili9881c_ctrl_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); + + if (esp_cam_ctlr_start(cam_handle) != ESP_OK) { + ESP_LOGE(TAG, "Driver start fail"); + return; + } + + esp_cam_sensor_config_t cam_config = { + .sccb_handle = ov5647_io_handle, + .reset_pin = -1, + .pwdn_pin = -1, + .xclk_pin = -1, + .sensor_port = ESP_CAM_SENSOR_MIPI_CSI, + }; + + esp_cam_sensor_device_t *cam = ov5647_detect(&cam_config); + if (!cam) { + ESP_LOGE(TAG, "failed to detect 5647"); + return; + } + + esp_cam_sensor_format_array_t cam_fmt_array = {0}; + esp_cam_sensor_query_format(cam, &cam_fmt_array); + const esp_cam_sensor_format_t *parray = cam_fmt_array.format_array; + for (int i = 0; i < cam_fmt_array.count; i++) { + ESP_LOGI(TAG, "fmt[%d].name:%s", i, parray[i].name); + } + + esp_cam_sensor_format_t *cam_cur_fmt = NULL; + for (int i = 0; i < cam_fmt_array.count; i++) { +#if CONFIG_EXAMPLE_MIPI_CSI_VRES_640 + if (!strcmp(parray[i].name, "MIPI_2lane_24Minput_RAW8_800x640_50fps")) { + cam_cur_fmt = (esp_cam_sensor_format_t *) & (parray[i].name); + } +#else + if (!strcmp(parray[i].name, "MIPI_2lane_24Minput_RAW8_800x1280_50fps")) { + cam_cur_fmt = (esp_cam_sensor_format_t *) & (parray[i].name); + } +#endif + } + + ret = esp_cam_sensor_set_format(cam, (const esp_cam_sensor_format_t *) cam_cur_fmt); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Format set fail"); + } else { + ESP_LOGI(TAG, "Format in use:%s", cam_cur_fmt->name); + } + int enable_flag = 1; + // Set sensor output stream + ret = esp_cam_sensor_ioctl(cam, ESP_CAM_SENSOR_IOC_S_STREAM, &enable_flag); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Start stream fail"); + } + + example_dpi_panel_init(mipi_dpi_panel); + + while (1) { + ESP_ERROR_CHECK(esp_cam_ctlr_receive(cam_handle, &new_trans, ESP_CAM_CTLR_MAX_DELAY)); + } +} + +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 new_trans = *(esp_cam_ctlr_trans_t *)user_data; + trans->buffer = new_trans.buffer; + trans->buflen = new_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; +} diff --git a/examples/peripherals/camera/camera_dsi/main/example_config.h b/examples/peripherals/camera/camera_dsi/main/example_config.h new file mode 100644 index 0000000000..c72a6b02ea --- /dev/null +++ b/examples/peripherals/camera/camera_dsi/main/example_config.h @@ -0,0 +1,23 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#define EXAMPLE_RGB565_BITS_PER_PIXEL 16 +#define EXAMPLE_MIPI_SCCB_FREQ (100000) +#define EXAMPLE_MIPI_SCCB_SCL_IO (8) +#define EXAMPLE_MIPI_SCCB_SDA_IO (7) +#define EXAMPLE_MIPI_IDI_CLOCK_RATE (50000000) +#define EXAMPLE_MIPI_CSI_LANE_BITRATE_MBPS 200 //line_rate = pclk * 4 + +#define EXAMPLE_OV5647_DEV_ADDR 0x36 +#ifdef __cplusplus +} +#endif diff --git a/examples/peripherals/camera/camera_dsi/main/idf_component.yml b/examples/peripherals/camera/camera_dsi/main/idf_component.yml new file mode 100644 index 0000000000..f7121dc8c5 --- /dev/null +++ b/examples/peripherals/camera/camera_dsi/main/idf_component.yml @@ -0,0 +1,7 @@ +dependencies: + espressif/esp_cam_sensor: "^0.2.2" + espressif/esp_lcd_ili9881c: "*" + idf: + version: ">=5.3.0" + dsi_init: + path: ${IDF_PATH}/examples/peripherals/camera/components/dsi_init diff --git a/examples/peripherals/camera/camera_dsi/pytest_camera_dsi.py b/examples/peripherals/camera/camera_dsi/pytest_camera_dsi.py new file mode 100644 index 0000000000..9c234e6217 --- /dev/null +++ b/examples/peripherals/camera/camera_dsi/pytest_camera_dsi.py @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 +import pytest +from pytest_embedded import Dut + + +@pytest.mark.esp32p4 +@pytest.mark.generic +def test_camera_dsi(dut: Dut) -> None: + dut.expect_exact('Calling app_main()') diff --git a/examples/peripherals/camera/camera_dsi/sdkconfig.defaults b/examples/peripherals/camera/camera_dsi/sdkconfig.defaults new file mode 100644 index 0000000000..7c4a698932 --- /dev/null +++ b/examples/peripherals/camera/camera_dsi/sdkconfig.defaults @@ -0,0 +1,4 @@ +CONFIG_SPIRAM=y +CONFIG_IDF_EXPERIMENTAL_FEATURES=y +CONFIG_SPIRAM_SPEED_200M=y +CONFIG_CAMERA_OV5647=y diff --git a/examples/peripherals/camera/components/dsi_init/CMakeLists.txt b/examples/peripherals/camera/components/dsi_init/CMakeLists.txt new file mode 100644 index 0000000000..348bb31726 --- /dev/null +++ b/examples/peripherals/camera/components/dsi_init/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRCS "example_dsi_init.c" + INCLUDE_DIRS "." + REQUIRES esp_lcd + ) diff --git a/examples/peripherals/camera/components/dsi_init/example_dsi_init.c b/examples/peripherals/camera/components/dsi_init/example_dsi_init.c new file mode 100644 index 0000000000..1cf9f3768a --- /dev/null +++ b/examples/peripherals/camera/components/dsi_init/example_dsi_init.c @@ -0,0 +1,75 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include "esp_log.h" +#include "esp_lcd_mipi_dsi.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_ili9881c.h" +#include "example_dsi_init.h" + +void example_dsi_resource_alloc(esp_lcd_panel_handle_t *ili9881c_ctrl_panel, esp_lcd_panel_handle_t *mipi_dpi_panel, void **frame_buffer) +{ + esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL; + esp_lcd_panel_io_handle_t mipi_dbi_io = NULL; + + //---------------DSI resource allocation------------------// + esp_lcd_dsi_bus_config_t bus_config = { + .bus_id = 0, + .num_data_lanes = 2, + .phy_clk_src = MIPI_DSI_PHY_CLK_SRC_DEFAULT, + .lane_bit_rate_mbps = 1000, // 1000 Mbps + }; + ESP_ERROR_CHECK(esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus)); + + esp_lcd_dbi_io_config_t dbi_config = { + .virtual_channel = 0, + .lcd_cmd_bits = 8, + .lcd_param_bits = 8, + }; + ESP_ERROR_CHECK(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io)); + + esp_lcd_panel_dev_config_t lcd_dev_config = { + .bits_per_pixel = 16, + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, + .reset_gpio_num = -1, + }; + ESP_ERROR_CHECK(esp_lcd_new_panel_ili9881c(mipi_dbi_io, &lcd_dev_config, ili9881c_ctrl_panel)); + + esp_lcd_dpi_panel_config_t dpi_config = { + .dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT, + .dpi_clock_freq_mhz = 80, + .virtual_channel = 0, + .pixel_format = LCD_COLOR_PIXEL_FORMAT_RGB565, + .video_timing = { + .h_size = EXAMPLE_MIPI_DSI_IMAGE_HSIZE, + .v_size = EXAMPLE_MIPI_DSI_IMAGE_VSIZE, + .hsync_back_porch = EXAMPLE_MIPI_DSI_IMAGE_HBP, + .hsync_pulse_width = EXAMPLE_MIPI_DSI_IMAGE_HSYNC, + .hsync_front_porch = EXAMPLE_MIPI_DSI_IMAGE_HFP, + .vsync_back_porch = EXAMPLE_MIPI_DSI_IMAGE_VBP, + .vsync_pulse_width = EXAMPLE_MIPI_DSI_IMAGE_VSYNC, + .vsync_front_porch = EXAMPLE_MIPI_DSI_IMAGE_VFP, + }, + }; + ESP_ERROR_CHECK(esp_lcd_new_panel_dpi(mipi_dsi_bus, &dpi_config, mipi_dpi_panel)); + ESP_ERROR_CHECK(esp_lcd_dpi_panel_get_frame_buffer(*mipi_dpi_panel, 1, frame_buffer)); +} + +void example_dsi_ili9881c_panel_init(esp_lcd_panel_handle_t ili9881c_ctrl_panel) +{ + //---------------DSI Panel Init------------------// + ESP_ERROR_CHECK(esp_lcd_panel_reset(ili9881c_ctrl_panel)); + ESP_ERROR_CHECK(esp_lcd_panel_init(ili9881c_ctrl_panel)); + // turn on display + ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(ili9881c_ctrl_panel, true)); +} + +void example_dpi_panel_init(esp_lcd_panel_handle_t mipi_dpi_panel) +{ + //---------------DPI Panel Init------------------// + ESP_ERROR_CHECK(esp_lcd_panel_init(mipi_dpi_panel)); +} diff --git a/examples/peripherals/camera/components/dsi_init/example_dsi_init.h b/examples/peripherals/camera/components/dsi_init/example_dsi_init.h new file mode 100644 index 0000000000..5cffbc3cdf --- /dev/null +++ b/examples/peripherals/camera/components/dsi_init/example_dsi_init.h @@ -0,0 +1,47 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#define EXAMPLE_MIPI_DSI_IMAGE_HSIZE 800 +#define EXAMPLE_MIPI_DSI_IMAGE_VSIZE 1280 +#define EXAMPLE_MIPI_DSI_IMAGE_HSYNC 40 +#define EXAMPLE_MIPI_DSI_IMAGE_HBP 140 +#define EXAMPLE_MIPI_DSI_IMAGE_HFP 40 +#define EXAMPLE_MIPI_DSI_IMAGE_VSYNC 4 +#define EXAMPLE_MIPI_DSI_IMAGE_VBP 16 +#define EXAMPLE_MIPI_DSI_IMAGE_VFP 16 + +/** + * @brief DSI init function + * + * @param[out] ili9881c_ctrl_panel ILI9881C panel handle + * @param[out] mipi_dpi_panel MIPI DPI panel handle + * @param[out] frame_buffer frame buffer + */ +void example_dsi_resource_alloc(esp_lcd_panel_handle_t *ili9881c_ctrl_panel, esp_lcd_panel_handle_t *mipi_dpi_panel, void **frame_buffer); + +/** + * @brief DSI ILI9881C panel init function + * + * @param[in] ili9881c_ctrl_panel ILI9881C panel handle + */ +void example_dsi_ili9881c_panel_init(esp_lcd_panel_handle_t ili9881c_ctrl_panel); + +/** + * @brief DPI panel init function + * + * @param[in] mipi_dpi_panel MIPI DPI panel handle + */ +void example_dpi_panel_init(esp_lcd_panel_handle_t mipi_dpi_panel); + +#ifdef __cplusplus +} +#endif diff --git a/examples/peripherals/camera/components/dsi_init/idf_component.yml b/examples/peripherals/camera/components/dsi_init/idf_component.yml new file mode 100644 index 0000000000..6e0028e6a5 --- /dev/null +++ b/examples/peripherals/camera/components/dsi_init/idf_component.yml @@ -0,0 +1,4 @@ +dependencies: + espressif/esp_lcd_ili9881c: "^0.2.0" + idf: + version: ">=5.3.0" diff --git a/examples/peripherals/isp/auto_focus/CMakeLists.txt b/examples/peripherals/isp/auto_focus/CMakeLists.txt new file mode 100644 index 0000000000..dd9fec70ab --- /dev/null +++ b/examples/peripherals/isp/auto_focus/CMakeLists.txt @@ -0,0 +1,6 @@ +# 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) +project(isp_af_dsi) diff --git a/examples/peripherals/isp/auto_focus/README.md b/examples/peripherals/isp/auto_focus/README.md new file mode 100644 index 0000000000..a38446fd9e --- /dev/null +++ b/examples/peripherals/isp/auto_focus/README.md @@ -0,0 +1,141 @@ +| Supported Targets | ESP32-P4 | +| ----------------- | -------- | + + +# Camera display via DSI example + +## Overview + +This example demonstrates how to use the ISP (image signal processor) to work with esp_driver_cam component. This example will capture camera sensor signals via CSI interface and display it via DSI interface. This example also enables the ISP AF (auto-focus) feature. + +## 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 + +This example requires: + +- OV5647 camera sensor with VCM (Voice Coil Motor). The VCM used in this example is DW9714. +- ILI9881C LCD screen +- ESP32P4 devkit + + + GND GND + ┌────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────┐ + │ │ │ │ + │ │ │ │ + │ │ │ │ + │ │ │ │ + │ │ │ │ + │ │ │ │ + │ │ │ │ + │ │ │ │ + │ ┌───────────────┴─────────────┴──────────────────┐ │ + │ │ │ ┌──────────┴───────────┐ + │ │ │ DSI DATA 1P │ │ + │ │ ├───────────────────────────┤ │ + ┌───────────┴─────────┐ CSI DATA 1P │ │ │ │ + │ ├──────────────────────┤ │ DSI DATA 1N │ │ + │ │ │ ├───────────────────────────┤ │ + │ │ CSI DATA 1N │ ESP32-P4 │ │ │ + │ OV5647 ├──────────────────────┤ │ DSI CLK N │ ILI9881C │ + │ │ │ ├───────────────────────────┤ │ + │ │ CSI CLK N │ │ │ │ + │ ├──────────────────────┤ │ DSI CLK P │ │ + │ │ │ ├───────────────────────────┤ │ + │ │ CSI CLK P │ │ │ │ + │ ├──────────────────────┤ │ DSI DATA 0P │ │ + │ │ │ ├───────────────────────────┤ │ + │ │ CSI DATA 0P │ │ │ │ + │ ├──────────────────────┤ │ DSI DATA 0N │ │ + │ │ │ ├───────────────────────────┤ │ + │ │ CSI DATA 0N │ │ │ │ + │ ├──────────────────────┤ │ └──────────────────────┘ + │ │ │ │ + └───────┬──┬──────────┘ │ │ + │ │ I2C SCL │ │ + │ └─────────────────────────────────┤ │ + │ I2C SDA │ │ + └────────────────────────────────────┤ │ + └────────────────────────────────────────────────┘ + + +### Set Chip Target + +First of all, your target must be supported by both: + +- **By your ESP-IDF version**: For the full list of supported targets, run: + ``` + idf.py --list-targets + ``` +- **By this example**: For the full list of supported targets, refer to the supported targets table at the top of this README. + +After you make sure that your target is supported, go to your example project directory and [set the chip target](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/tools/idf-py.html#select-the-target-chip-set-target): + +``` +idf.py set-target +``` + +For example, to set esp32-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_OV5647 to y +``` + + +### 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 (1085) main_task: Calling app_main() +I (1095) ili9881c: ID1: 0x98, ID2: 0x81, ID3: 0x5c +I (1125) gpio: GPIO[31]| InputEn: 1| OutputEn: 1| OpenDrain: 1| Pullup: 1| Pulldown: 0| Intr:0 +I (1125) gpio: GPIO[34]| InputEn: 1| OutputEn: 1| OpenDrain: 1| Pullup: 1| Pulldown: 0| Intr:0 +I (1295) ov5647: Detected Camera sensor PID=0x5647 with index 0 +I (1305) cam_dsi: fmt[0].name:MIPI_2lane_24Minput_RAW8_800x1280_50fps +I (1305) cam_dsi: fmt[1].name:MIPI_2lane_24Minput_RAW8_800x640_50fps +I (1315) cam_dsi: fmt[2].name:MIPI_2lane_24Minput_RAW8_800x800_50fps +I (1355) cam_dsi: Format in use:MIPI_2lane_24Minput_RAW8_800x640_50fps +``` + +You will also see the screen auto-focus when the screen image changes. + +## Reference + +- Link to the ESP-IDF camera controller driver API reference, [ESP-IDF: Camera Controller Driver](https://docs.espressif.com/projects/esp-idf/en/latest/esp32p4/api-reference/peripherals/camera_driver.html) +- Link to the ESP-IDF ISP driver API reference, [ESP-IDF: Image Signal Processor](https://docs.espressif.com/projects/esp-idf/en/latest/esp32p4/api-reference/peripherals/isp.html) +- [ESP-IDF Getting Started](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html#get-started) +- [Project Configuration](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/kconfig.html) (Kconfig Options) diff --git a/examples/peripherals/isp/auto_focus/components/isp_af_schemes/include/isp_af_scheme.h b/examples/peripherals/isp/auto_focus/components/isp_af_schemes/include/isp_af_scheme.h index 1926c484fd..95164fce3a 100644 --- a/examples/peripherals/isp/auto_focus/components/isp_af_schemes/include/isp_af_scheme.h +++ b/examples/peripherals/isp/auto_focus/components/isp_af_schemes/include/isp_af_scheme.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/examples/peripherals/isp/auto_focus/components/isp_af_schemes/include/isp_af_scheme_sa.h b/examples/peripherals/isp/auto_focus/components/isp_af_schemes/include/isp_af_scheme_sa.h index 44e6a57dec..dd70f17d0c 100644 --- a/examples/peripherals/isp/auto_focus/components/isp_af_schemes/include/isp_af_scheme_sa.h +++ b/examples/peripherals/isp/auto_focus/components/isp_af_schemes/include/isp_af_scheme_sa.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -33,8 +33,9 @@ typedef struct { * @brief Sensor driver API to set sensor focus value * * @param[in] focus_val Camera sensor focus value + * @param[in] arg User arg */ - esp_err_t (*af_sensor_set_focus)(int focus_val); + esp_err_t (*af_sensor_set_focus)(int focus_val, void *arg); } isp_af_sa_scheme_sensor_drv_t; @@ -72,13 +73,14 @@ esp_err_t isp_af_delete_sa_scheme(isp_af_scheme_handle_t scheme); * @param[in] scheme AF scheme handle * @param[in] sensor_drv Sensor driver, see `isp_af_sa_scheme_sensor_drv_t` * @param[in] info Sensor info + * @param[in] arg User arg * * @return * - ESP_OK On success * - ESP_ERR_INVALID_ARG If the combination of arguments is invalid * - ESP_ERR_INVALID_STATE Invalid state */ -esp_err_t isp_af_sa_scheme_register_sensor_driver(isp_af_scheme_handle_t scheme, const isp_af_sa_scheme_sensor_drv_t *sensor_drv, const isp_af_sa_scheme_sensor_info_t *info); +esp_err_t isp_af_sa_scheme_register_sensor_driver(isp_af_scheme_handle_t scheme, const isp_af_sa_scheme_sensor_drv_t *sensor_drv, const isp_af_sa_scheme_sensor_info_t *info, void *arg); #ifdef __cplusplus } diff --git a/examples/peripherals/isp/auto_focus/components/isp_af_schemes/src/isp_af_scheme_sa.c b/examples/peripherals/isp/auto_focus/components/isp_af_schemes/src/isp_af_scheme_sa.c index 516782695d..b7b5502d52 100644 --- a/examples/peripherals/isp/auto_focus/components/isp_af_schemes/src/isp_af_scheme_sa.c +++ b/examples/peripherals/isp/auto_focus/components/isp_af_schemes/src/isp_af_scheme_sa.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -29,6 +29,7 @@ typedef struct { isp_af_sa_scheme_sensor_info_t sensor_info; isp_af_sa_scheme_sensor_drv_t sensor_drv; + void *arg; } af_scheme_context_t; /* ------------------------ Interface Functions --------------------------- */ @@ -78,7 +79,7 @@ esp_err_t isp_af_delete_sa_scheme(isp_af_scheme_handle_t scheme) return ESP_OK; } -esp_err_t isp_af_sa_scheme_register_sensor_driver(isp_af_scheme_handle_t scheme, const isp_af_sa_scheme_sensor_drv_t *sensor_drv, const isp_af_sa_scheme_sensor_info_t *info) +esp_err_t isp_af_sa_scheme_register_sensor_driver(isp_af_scheme_handle_t scheme, const isp_af_sa_scheme_sensor_drv_t *sensor_drv, const isp_af_sa_scheme_sensor_info_t *info, void *arg) { ESP_RETURN_ON_FALSE(scheme, ESP_ERR_INVALID_ARG, TAG, "invalid arg: null pointer"); ESP_RETURN_ON_FALSE(scheme->ctx, ESP_ERR_INVALID_STATE, TAG, "no scheme created yet"); @@ -86,6 +87,7 @@ esp_err_t isp_af_sa_scheme_register_sensor_driver(isp_af_scheme_handle_t scheme, af_scheme_context_t *ctx = scheme->ctx; ctx->sensor_drv.af_sensor_set_focus = sensor_drv->af_sensor_set_focus; ctx->sensor_info.focus_val_max = info->focus_val_max; + ctx->arg = arg; return ESP_OK; } @@ -117,7 +119,7 @@ static esp_err_t s_af_process(void *arg, int *out_definition_thresh, int *out_lu isp_af_result_t result = {}; - ESP_RETURN_ON_ERROR(ctx->sensor_drv.af_sensor_set_focus(0), TAG, "sensor set focus val fail"); + ESP_RETURN_ON_ERROR(ctx->sensor_drv.af_sensor_set_focus(0, ctx->arg), TAG, "sensor set focus val fail"); ESP_LOGV(TAG, "//----------- af start ----------//"); @@ -128,7 +130,7 @@ static esp_err_t s_af_process(void *arg, int *out_definition_thresh, int *out_lu for (int x = 0; x <= ctx->first_approx_cycles; x++) { af_current = af_current_base + x * ctx->first_step_val; - ESP_RETURN_ON_ERROR(ctx->sensor_drv.af_sensor_set_focus(af_current), TAG, "sensor set focus val fail"); + ESP_RETURN_ON_ERROR(ctx->sensor_drv.af_sensor_set_focus(af_current, ctx->arg), TAG, "sensor set focus val fail"); ESP_RETURN_ON_ERROR(esp_isp_af_controller_get_oneshot_result(ctx->af_ctlr, &result), TAG, "get AF result fail"); af_sum = result.definition[0] + result.definition[1] + result.definition[2]; if (af_sum > af_sum_max) { @@ -136,7 +138,7 @@ static esp_err_t s_af_process(void *arg, int *out_definition_thresh, int *out_lu af_current_best = af_current; } - ESP_LOGV(TAG, "af_sum: %"PRId32", af_current: %"PRId32".%"PRId32, af_sum, (int)af_current, (int)((int)(af_current * 1000) % 1000)); + ESP_LOGV(TAG, "af_sum: %d, af_current: %d.%d", af_sum, (int)af_current, (int)((int)(af_current * 1000) % 1000)); } // second search @@ -154,7 +156,7 @@ static esp_err_t s_af_process(void *arg, int *out_definition_thresh, int *out_lu af_current = 0; } - ESP_RETURN_ON_ERROR(ctx->sensor_drv.af_sensor_set_focus(af_current), TAG, "sensor set focus val fail"); + ESP_RETURN_ON_ERROR(ctx->sensor_drv.af_sensor_set_focus(af_current, ctx->arg), TAG, "sensor set focus val fail"); ESP_RETURN_ON_ERROR(esp_isp_af_controller_get_oneshot_result(ctx->af_ctlr, &result), TAG, "get AF result fail"); af_sum = result.definition[0] + result.definition[1] + result.definition[2]; if (af_sum > af_sum_max) { @@ -167,7 +169,7 @@ static esp_err_t s_af_process(void *arg, int *out_definition_thresh, int *out_lu // af done ESP_LOGV(TAG, "//----------- af done ----------//"); ESP_LOGV(TAG, "af_sum_max: %d, af_current_best: %d.%d", af_sum_max, (int)af_current_best, (int)((int)(af_current_best * 1000) % 1000)); - ESP_RETURN_ON_ERROR(ctx->sensor_drv.af_sensor_set_focus(af_current_best), TAG, "sensor set focus val fail"); + ESP_RETURN_ON_ERROR(ctx->sensor_drv.af_sensor_set_focus(af_current_best, ctx->arg), TAG, "sensor set focus val fail"); // update env threshold ESP_LOGV(TAG, "//------- update env threshold -------//"); diff --git a/examples/peripherals/isp/auto_focus/main/CMakeLists.txt b/examples/peripherals/isp/auto_focus/main/CMakeLists.txt new file mode 100644 index 0000000000..60d0b9d605 --- /dev/null +++ b/examples/peripherals/isp/auto_focus/main/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRCS "isp_af_dsi_main.c" + INCLUDE_DIRS "." + REQUIRES esp_mm esp_driver_isp esp_driver_cam esp_driver_i2c esp_lcd dsi_init + ) diff --git a/examples/peripherals/isp/auto_focus/main/Kconfig.projbuild b/examples/peripherals/isp/auto_focus/main/Kconfig.projbuild new file mode 100644 index 0000000000..8117f6ef7b --- /dev/null +++ b/examples/peripherals/isp/auto_focus/main/Kconfig.projbuild @@ -0,0 +1,41 @@ +menu "Example Configuration" + config EXAMPLE_USED_LDO_CHAN_ID + int "example used LDO channel ID" + default 3 + help + Example used LDO channel ID, you may check datasheet to know more details. + + config EXAMPLE_USED_LDO_VOLTAGE_MV + int "example used LDO voltage in mV" + default 2500 + range 0 3300 + help + Example used LDO voltage, in mV + + choice EXAMPLE_MIPI_CSI_DISP_HRES + bool "Set MIPI CSI horizontal resolution" + default EXAMPLE_MIPI_CSI_HRES_800 + + config EXAMPLE_MIPI_CSI_HRES_800 + bool "800" + endchoice + + config EXAMPLE_MIPI_CSI_DISP_HRES + int + default 800 if EXAMPLE_MIPI_CSI_HRES_800 + + choice EXAMPLE_MIPI_CSI_DISP_VRES + bool "Set MIPI CSI vertical resolution" + default EXAMPLE_MIPI_CSI_VRES_640 + + config EXAMPLE_MIPI_CSI_VRES_640 + bool "640" + config EXAMPLE_MIPI_CSI_VRES_1280 + bool "1280" + endchoice + + config EXAMPLE_MIPI_CSI_DISP_VRES + int + default 640 if EXAMPLE_MIPI_CSI_VRES_640 + default 1280 if EXAMPLE_MIPI_CSI_VRES_1280 +endmenu diff --git a/examples/peripherals/isp/auto_focus/main/example_config.h b/examples/peripherals/isp/auto_focus/main/example_config.h new file mode 100644 index 0000000000..628a441f56 --- /dev/null +++ b/examples/peripherals/isp/auto_focus/main/example_config.h @@ -0,0 +1,24 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#define EXAMPLE_RGB565_BITS_PER_PIXEL 16 +#define EXAMPLE_MIPI_SCCB_FREQ (100000) +#define EXAMPLE_MIPI_SCCB_SCL_IO (8) +#define EXAMPLE_MIPI_SCCB_SDA_IO (7) +#define EXAMPLE_MIPI_IDI_CLOCK_RATE (50000000) +#define EXAMPLE_MIPI_CSI_LANE_BITRATE_MBPS 200 //line_rate = pclk * 4 + +#define EXAMPLE_OV5647_DEV_ADDR 0x36 +#define EXAMPLE_DW9714_DEV_ADDR 0xC +#ifdef __cplusplus +} +#endif diff --git a/examples/peripherals/isp/auto_focus/main/idf_component.yml b/examples/peripherals/isp/auto_focus/main/idf_component.yml new file mode 100644 index 0000000000..803bff156a --- /dev/null +++ b/examples/peripherals/isp/auto_focus/main/idf_component.yml @@ -0,0 +1,9 @@ +dependencies: + espressif/esp_cam_sensor: "^0.2.2" + espressif/esp_lcd_ili9881c: "*" + idf: + version: ">=5.3.0" + isp_af_schemes: + path: ${IDF_PATH}/examples/peripherals/isp/auto_focus/components/isp_af_schemes + dsi_init: + path: ${IDF_PATH}/examples/peripherals/camera/components/dsi_init diff --git a/examples/peripherals/isp/auto_focus/main/isp_af_dsi_main.c b/examples/peripherals/isp/auto_focus/main/isp_af_dsi_main.c new file mode 100644 index 0000000000..6cca1612bc --- /dev/null +++ b/examples/peripherals/isp/auto_focus/main/isp_af_dsi_main.c @@ -0,0 +1,357 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "esp_log.h" +#include "esp_heap_caps.h" +#include "esp_lcd_mipi_dsi.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_ili9881c.h" +#include "esp_ldo_regulator.h" +#include "esp_cache.h" +#include "driver/i2c_master.h" +#include "driver/isp.h" +#include "isp_af_scheme_sa.h" +#include "esp_cam_ctlr_csi.h" +#include "esp_cam_ctlr.h" +#include "esp_sccb_intf.h" +#include "esp_sccb_i2c.h" +#include "esp_cam_sensor.h" +#include "ov5647.h" +#include "example_dsi_init.h" +#include "example_config.h" + +static const char *TAG = "isp_dsi"; + +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); + +typedef union { + struct { + uint16_t s : 4; + uint16_t d : 10; + uint16_t flag : 1; + uint16_t pd : 1; + }; + struct { + uint16_t byte2 : 8; + uint16_t byte1 : 8; + }; + uint16_t val; +} dw9714_reg_t; + +static bool IRAM_ATTR s_env_change_cb(isp_af_ctrlr_t af_ctrlr, const esp_isp_af_env_detector_evt_data_t *edata, void *user_data) +{ + BaseType_t mustYield = pdFALSE; + TaskHandle_t task_handle = (TaskHandle_t)user_data; + vTaskNotifyGiveFromISR(task_handle, &mustYield); + + return (mustYield == pdTRUE); +} + +static esp_err_t s_sensor_set_focus_val(int focus_val, void *arg) +{ + esp_sccb_io_handle_t dw9714_io_handle = arg; + + dw9714_reg_t reg = {0}; + reg.d = (uint16_t)((focus_val / 120.0) * 1023.0); + + uint8_t data[2] = {0}; + data[0] = reg.byte1; + data[1] = reg.byte2; + + uint16_t reg_addr = (data[0] << 8) + (data[1]); + uint8_t reg_val = 0; + + esp_err_t ret = esp_sccb_transmit_reg_a16v8(dw9714_io_handle, reg_addr, reg_val); + if (ret != ESP_OK) { + printf("dw9714 esp_sccb_transmit_reg_a16v8 failed\n"); + return ret; + } + + return ESP_OK; +} + +static void af_task(void *arg) +{ + TaskHandle_t task_handle = xTaskGetCurrentTaskHandle(); + + typedef struct af_task_param_t { + isp_proc_handle_t isp_proc; + esp_sccb_io_handle_t dw9714_io_handle; + } af_task_param_t; + + af_task_param_t af_task_param = *(af_task_param_t *)arg; + + /** + * AF window, windows for ISP hardware to record the + * - lunimance + * - definition + * of the current windows + */ + esp_isp_af_config_t af_config = { + .window = { + [0] = { + .top_left_x = (CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES / 2) - 100, + .bottom_right_x = (CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES / 2) + 99, + .top_left_y = (CONFIG_EXAMPLE_MIPI_CSI_DISP_VRES / 2) - 100, + .bottom_right_y = (CONFIG_EXAMPLE_MIPI_CSI_DISP_VRES / 2) + 99, + }, + [1] = { + .top_left_x = (CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES / 2) - 100, + .bottom_right_x = (CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES / 2) + 99, + .top_left_y = (CONFIG_EXAMPLE_MIPI_CSI_DISP_VRES / 2) - 100, + .bottom_right_y = (CONFIG_EXAMPLE_MIPI_CSI_DISP_VRES / 2) + 99, + }, + [2] = { + .top_left_x = (CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES / 2) - 100, + .bottom_right_x = (CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES / 2) + 99, + .top_left_y = (CONFIG_EXAMPLE_MIPI_CSI_DISP_VRES / 2) - 100, + .bottom_right_y = (CONFIG_EXAMPLE_MIPI_CSI_DISP_VRES / 2) + 99, + }, + }, + .edge_thresh = 128, + }; + + isp_af_ctrlr_t af_ctrlr = NULL; + ESP_ERROR_CHECK(esp_isp_new_af_controller(af_task_param.isp_proc, &af_config, &af_ctrlr)); + + esp_isp_af_env_config_t env_config = { + .interval = 10, + }; + ESP_ERROR_CHECK(esp_isp_af_controller_set_env_detector(af_ctrlr, &env_config)); + + esp_isp_af_env_detector_evt_cbs_t cbs = { + .on_env_change = s_env_change_cb, + }; + ESP_ERROR_CHECK(esp_isp_af_env_detector_register_event_callbacks(af_ctrlr, &cbs, task_handle)); + + isp_af_sa_scheme_config_t af_scheme_config = { + .first_step_val = 12, + .first_approx_cycles = 10, + .second_step_val = 2, + .second_approx_cycles = 10, + }; + isp_af_scheme_handle_t af_scheme = NULL; + ESP_ERROR_CHECK(isp_af_create_sa_scheme(af_ctrlr, &af_scheme_config, &af_scheme)); + + isp_af_sa_scheme_sensor_drv_t sensor_driver = { + .af_sensor_set_focus = s_sensor_set_focus_val, + }; + isp_af_sa_scheme_sensor_info_t sensor_info = { + .focus_val_max = 120, + }; + ESP_ERROR_CHECK(isp_af_sa_scheme_register_sensor_driver(af_scheme, &sensor_driver, &sensor_info, af_task_param.dw9714_io_handle)); + + int definition_thresh = 0; + int luminance_thresh = 0; + ESP_ERROR_CHECK(esp_isp_af_controller_enable(af_ctrlr)); + + while (1) { + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + ESP_ERROR_CHECK(isp_af_process(af_scheme, &definition_thresh, &luminance_thresh)); + ESP_ERROR_CHECK(esp_isp_af_controller_set_env_detector_threshold(af_ctrlr, definition_thresh, luminance_thresh)); + } +} + +void app_main(void) +{ + esp_err_t ret = ESP_FAIL; + esp_lcd_panel_handle_t ili9881c_ctrl_panel = 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)); + + /** + * @background + * Sensor use RAW8 + * ISP convert to RGB565 + */ + //---------------DSI Init------------------// + example_dsi_resource_alloc(&ili9881c_ctrl_panel, &mipi_dpi_panel, &frame_buffer); + + //---------------Necessary variable config------------------// + frame_buffer_size = CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES * EXAMPLE_MIPI_DSI_IMAGE_VSIZE * EXAMPLE_RGB565_BITS_PER_PIXEL / 8; + + ESP_LOGD(TAG, "CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES: %d, EXAMPLE_MIPI_DSI_IMAGE_VSIZE: %d, bits per pixel: %d", CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES, EXAMPLE_MIPI_DSI_IMAGE_VSIZE, 8); + ESP_LOGD(TAG, "frame_buffer_size: %zu", frame_buffer_size); + ESP_LOGD(TAG, "frame_buffer: %p", frame_buffer); + + esp_cam_ctlr_trans_t new_trans = { + .buffer = frame_buffer, + .buflen = frame_buffer_size, + }; + + //---------------I2C Init------------------// + i2c_master_bus_config_t i2c_bus_conf = { + .clk_source = I2C_CLK_SRC_DEFAULT, + .sda_io_num = EXAMPLE_MIPI_SCCB_SDA_IO, + .scl_io_num = EXAMPLE_MIPI_SCCB_SCL_IO, + .i2c_port = I2C_NUM_0, + .flags.enable_internal_pullup = true, + }; + i2c_master_bus_handle_t bus_handle = NULL; + ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_conf, &bus_handle)); + + //---------------SCCB Init------------------// + esp_sccb_io_handle_t ov5647_io_handle = NULL; + sccb_i2c_config_t i2c_config = { + .scl_speed_hz = EXAMPLE_MIPI_SCCB_FREQ, + .device_address = EXAMPLE_OV5647_DEV_ADDR, + .dev_addr_length = I2C_ADDR_BIT_LEN_7, + }; + ESP_ERROR_CHECK(sccb_new_i2c_io(bus_handle, &i2c_config, &ov5647_io_handle)); + + esp_sccb_io_handle_t dw9714_io_handle = NULL; + i2c_config.device_address = EXAMPLE_DW9714_DEV_ADDR; + ESP_ERROR_CHECK(sccb_new_i2c_io(bus_handle, &i2c_config, &dw9714_io_handle)); + + //---------------CSI Init------------------// + esp_cam_ctlr_csi_config_t csi_config = { + .ctlr_id = 0, + .h_res = CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES, + .v_res = CONFIG_EXAMPLE_MIPI_CSI_DISP_VRES, + .lane_bit_rate_mbps = EXAMPLE_MIPI_CSI_LANE_BITRATE_MBPS, + .input_data_color_type = MIPI_CSI_COLOR_RAW8, + .output_data_color_type = MIPI_CSI_COLOR_RGB565, + .data_lane_num = 2, + .byte_swap_en = false, + .queue_items = 1, + }; + esp_cam_ctlr_handle_t handle = NULL; + ret = esp_cam_new_csi_ctlr(&csi_config, &handle); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "csi init fail[%d]", ret); + return; + } + + 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(handle, &cbs, &new_trans) != ESP_OK) { + ESP_LOGE(TAG, "ops register fail"); + return; + } + + ESP_ERROR_CHECK(esp_cam_ctlr_enable(handle)); + //---------------ISP Init------------------// + isp_proc_handle_t isp_proc = NULL; + esp_isp_processor_cfg_t isp_config = { + .clk_hz = 80 * 1000 * 1000, + .input_data_source = ISP_INPUT_DATA_SOURCE_CSI, + .input_data_color_type = ISP_COLOR_RAW8, + .output_data_color_type = ISP_COLOR_RGB565, + .has_line_start_packet = false, + .has_line_end_packet = false, + .h_res = CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES, + .v_res = CONFIG_EXAMPLE_MIPI_CSI_DISP_VRES, + }; + ESP_ERROR_CHECK(esp_isp_new_processor(&isp_config, &isp_proc)); + ESP_ERROR_CHECK(esp_isp_enable(isp_proc)); + + typedef struct af_task_param_t { + isp_proc_handle_t isp_proc; + esp_sccb_io_handle_t dw9714_io_handle; + } af_task_param_t; + + af_task_param_t af_task_param = { + .isp_proc = isp_proc, + .dw9714_io_handle = dw9714_io_handle, + }; + xTaskCreatePinnedToCore(af_task, "af_task", 8192, &af_task_param, 5, NULL, 0); + + //---------------DSI Panel Init------------------// + example_dsi_ili9881c_panel_init(ili9881c_ctrl_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); + + if (esp_cam_ctlr_start(handle) != ESP_OK) { + ESP_LOGE(TAG, "Driver start fail"); + return; + } + + esp_cam_sensor_config_t cam_config = { + .sccb_handle = ov5647_io_handle, + .reset_pin = -1, + .pwdn_pin = -1, + .xclk_pin = -1, + .sensor_port = ESP_CAM_SENSOR_MIPI_CSI, + }; + + esp_cam_sensor_device_t *cam = ov5647_detect(&cam_config); + if (!cam) { + ESP_LOGE(TAG, "failed to detect 5647"); + return; + } + + esp_cam_sensor_format_array_t cam_fmt_array = {0}; + esp_cam_sensor_query_format(cam, &cam_fmt_array); + const esp_cam_sensor_format_t *parray = cam_fmt_array.format_array; + for (int i = 0; i < cam_fmt_array.count; i++) { + ESP_LOGI(TAG, "fmt[%d].name:%s", i, parray[i].name); + } + + esp_cam_sensor_format_t *cam_cur_fmt = NULL; + for (int i = 0; i < cam_fmt_array.count; i++) { +#if CONFIG_EXAMPLE_MIPI_CSI_VRES_640 + if (!strcmp(parray[i].name, "MIPI_2lane_24Minput_RAW8_800x640_50fps")) { + cam_cur_fmt = (esp_cam_sensor_format_t *) & (parray[i].name); + break; + } +#else + if (!strcmp(parray[i].name, "MIPI_2lane_24Minput_RAW8_800x1280_50fps")) { + cam_cur_fmt = (esp_cam_sensor_format_t *) & (parray[i].name); + break; + } +#endif + } + + ret = esp_cam_sensor_set_format(cam, (const esp_cam_sensor_format_t *) cam_cur_fmt); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Format set fail"); + } else { + ESP_LOGI(TAG, "Format in use:%s", cam_cur_fmt->name); + } + int enable_flag = 1; + // Set sensor output stream + ret = esp_cam_sensor_ioctl(cam, ESP_CAM_SENSOR_IOC_S_STREAM, &enable_flag); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Start stream fail"); + } + + example_dpi_panel_init(mipi_dpi_panel); + + while (1) { + ESP_ERROR_CHECK(esp_cam_ctlr_receive(handle, &new_trans, ESP_CAM_CTLR_MAX_DELAY)); + } +} + +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 new_trans = *(esp_cam_ctlr_trans_t *)user_data; + trans->buffer = new_trans.buffer; + trans->buflen = new_trans.buflen; + + return false; +} + +bool s_camera_get_finished_trans(esp_cam_ctlr_handle_t handle, esp_cam_ctlr_trans_t *trans, void *user_data) +{ + return false; +} diff --git a/examples/peripherals/isp/auto_focus/sdkconfig.defaults b/examples/peripherals/isp/auto_focus/sdkconfig.defaults new file mode 100644 index 0000000000..7c4a698932 --- /dev/null +++ b/examples/peripherals/isp/auto_focus/sdkconfig.defaults @@ -0,0 +1,4 @@ +CONFIG_SPIRAM=y +CONFIG_IDF_EXPERIMENTAL_FEATURES=y +CONFIG_SPIRAM_SPEED_200M=y +CONFIG_CAMERA_OV5647=y