mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-05 05:34:32 +02:00
Merge branch 'feat/lcd_cam_dvp_driver_s3' into 'master'
DVP support and example for ESP32S3 Closes IDF-10475 See merge request espressif/esp-idf!39323
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
menu "ESP-Driver:Camera Controller Configurations"
|
||||
|
||||
depends on SOC_MIPI_CSI_SUPPORTED || SOC_LCDCAM_CAM_SUPPORTED
|
||||
depends on SOC_MIPI_CSI_SUPPORTED || SOC_ISP_DVP_SUPPORTED || SOC_LCDCAM_CAM_SUPPORTED
|
||||
|
||||
config CAM_CTLR_MIPI_CSI_ISR_CACHE_SAFE
|
||||
bool "CSI ISR Cache-Safe"
|
||||
@@ -16,6 +16,7 @@ menu "ESP-Driver:Camera Controller Configurations"
|
||||
|
||||
config CAM_CTLR_ISP_DVP_ISR_CACHE_SAFE # IDF-10093
|
||||
bool "ISP_DVP ISR Cache-Safe"
|
||||
depends on SOC_ISP_DVP_SUPPORTED
|
||||
default n
|
||||
select DW_GDMA_ISR_IRAM_SAFE
|
||||
select DW_GDMA_CTRL_FUNC_IN_IRAM
|
||||
|
@@ -26,12 +26,24 @@
|
||||
#include "../../dvp_share_ctrl.h"
|
||||
|
||||
#ifdef CONFIG_CAM_CTLR_DVP_CAM_ISR_CACHE_SAFE
|
||||
#define CAM_DVP_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
|
||||
#define DVP_CAM_CTLR_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
|
||||
#else
|
||||
#define CAM_DVP_MEM_ALLOC_CAPS (MALLOC_CAP_DEFAULT)
|
||||
#define DVP_CAM_CTLR_ALLOC_CAPS (MALLOC_CAP_DEFAULT)
|
||||
#endif
|
||||
|
||||
#define ALIGN_UP_BY(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
|
||||
#if CONFIG_SPIRAM
|
||||
#define DVP_CAM_BK_BUFFER_ALLOC_CAPS (MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA)
|
||||
#else
|
||||
#define DVP_CAM_BK_BUFFER_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA)
|
||||
#endif
|
||||
|
||||
#if SOC_PERIPH_CLK_CTRL_SHARED
|
||||
#define DVP_CAM_CLK_ATOMIC() PERIPH_RCC_ATOMIC()
|
||||
#else
|
||||
#define DVP_CAM_CLK_ATOMIC()
|
||||
#endif
|
||||
|
||||
#define ALIGN_UP_BY(num, align) ((align) == 0 ? (num) : (((num) + ((align) - 1)) & ~((align) - 1)))
|
||||
|
||||
#define DVP_CAM_CONFIG_INPUT_PIN(pin, sig, inv) \
|
||||
{ \
|
||||
@@ -209,7 +221,6 @@ static uint32_t IRAM_ATTR esp_cam_ctlr_dvp_get_jpeg_size(const uint8_t *buffer,
|
||||
*/
|
||||
static uint32_t IRAM_ATTR esp_cam_ctlr_dvp_get_recved_size(esp_cam_ctlr_dvp_cam_t *ctlr, uint8_t *rx_buffer, uint32_t dma_recv_size)
|
||||
{
|
||||
esp_err_t ret;
|
||||
uint32_t recv_buffer_size;
|
||||
|
||||
if (ctlr->pic_format_jpeg) {
|
||||
@@ -218,8 +229,10 @@ static uint32_t IRAM_ATTR esp_cam_ctlr_dvp_get_recved_size(esp_cam_ctlr_dvp_cam_
|
||||
recv_buffer_size = ctlr->fb_size_in_bytes;
|
||||
}
|
||||
|
||||
ret = esp_cache_msync(rx_buffer, recv_buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
|
||||
if (esp_ptr_external_ram(rx_buffer)) {
|
||||
esp_err_t ret = esp_cache_msync(rx_buffer, recv_buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
|
||||
assert(ret == ESP_OK);
|
||||
}
|
||||
|
||||
if (ctlr->pic_format_jpeg) {
|
||||
recv_buffer_size = esp_cam_ctlr_dvp_get_jpeg_size(rx_buffer, dma_recv_size);
|
||||
@@ -336,7 +349,7 @@ esp_err_t esp_cam_ctlr_dvp_init(int ctlr_id, cam_clock_source_t clk_src, const e
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(esp_clk_tree_enable_src((soc_module_clk_t)clk_src, true));
|
||||
PERIPH_RCC_ATOMIC() {
|
||||
DVP_CAM_CLK_ATOMIC() {
|
||||
cam_ll_enable_clk(ctlr_id, true);
|
||||
cam_ll_select_clk_src(ctlr_id, clk_src);
|
||||
};
|
||||
@@ -367,7 +380,7 @@ esp_err_t esp_cam_ctlr_dvp_output_clock(int ctlr_id, cam_clock_source_t clk_src,
|
||||
ESP_LOGD(TAG, "DVP clock source frequency %" PRIu32 "Hz", src_clk_hz);
|
||||
|
||||
if ((src_clk_hz % xclk_freq) == 0) {
|
||||
PERIPH_RCC_ATOMIC() {
|
||||
DVP_CAM_CLK_ATOMIC() {
|
||||
cam_ll_set_group_clock_coeff(ctlr_id, src_clk_hz / xclk_freq, 0, 0);
|
||||
};
|
||||
|
||||
@@ -390,7 +403,7 @@ esp_err_t esp_cam_ctlr_dvp_deinit(int ctlr_id)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(ctlr_id < CAP_DVP_PERIPH_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid argument: ctlr_id >= %d", CAP_DVP_PERIPH_NUM);
|
||||
|
||||
PERIPH_RCC_ATOMIC() {
|
||||
DVP_CAM_CLK_ATOMIC() {
|
||||
cam_ll_enable_clk(ctlr_id, false);
|
||||
};
|
||||
|
||||
@@ -710,11 +723,11 @@ esp_err_t esp_cam_new_dvp_ctlr(const esp_cam_ctlr_dvp_config_t *config, esp_cam_
|
||||
ESP_RETURN_ON_FALSE(config->external_xtal || config->pin_dont_init || config->pin->xclk_io != GPIO_NUM_NC, ESP_ERR_INVALID_ARG, TAG, "invalid argument: xclk_io is not set");
|
||||
ESP_RETURN_ON_FALSE(config->external_xtal || config->xclk_freq, ESP_ERR_INVALID_ARG, TAG, "invalid argument: xclk_freq is not set");
|
||||
|
||||
ESP_RETURN_ON_ERROR(esp_cache_get_alignment(MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA, &alignment_size), TAG, "failed to get cache alignment");
|
||||
ESP_RETURN_ON_ERROR(esp_cache_get_alignment(DVP_CAM_BK_BUFFER_ALLOC_CAPS, &alignment_size), TAG, "failed to get cache alignment");
|
||||
ESP_RETURN_ON_ERROR(esp_cam_ctlr_dvp_cam_get_frame_size(config, &fb_size_in_bytes), TAG, "invalid argument: input frame pixel format is not supported");
|
||||
ESP_RETURN_ON_ERROR(dvp_shared_ctrl_claim_io_signals(), TAG, "failed to claim io signals");
|
||||
|
||||
esp_cam_ctlr_dvp_cam_t *ctlr = heap_caps_calloc(1, sizeof(esp_cam_ctlr_dvp_cam_t), CAM_DVP_MEM_ALLOC_CAPS);
|
||||
esp_cam_ctlr_dvp_cam_t *ctlr = heap_caps_calloc(1, sizeof(esp_cam_ctlr_dvp_cam_t), DVP_CAM_CTLR_ALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE(ctlr, ESP_ERR_NO_MEM, fail0, TAG, "no mem for CAM DVP controller context");
|
||||
|
||||
ESP_GOTO_ON_ERROR(s_dvp_claim_ctlr(config->ctlr_id, ctlr), fail1, TAG, "no available DVP controller");
|
||||
@@ -722,7 +735,7 @@ esp_err_t esp_cam_new_dvp_ctlr(const esp_cam_ctlr_dvp_config_t *config, esp_cam_
|
||||
ESP_LOGD(TAG, "alignment: 0x%x\n", alignment_size);
|
||||
fb_size_in_bytes = ALIGN_UP_BY(fb_size_in_bytes, alignment_size);
|
||||
if (!config->bk_buffer_dis) {
|
||||
ctlr->backup_buffer = heap_caps_aligned_alloc(alignment_size, fb_size_in_bytes, MALLOC_CAP_SPIRAM);
|
||||
ctlr->backup_buffer = heap_caps_aligned_alloc(alignment_size, fb_size_in_bytes, DVP_CAM_BK_BUFFER_ALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE(ctlr->backup_buffer, ESP_ERR_NO_MEM, fail2, TAG, "no mem for DVP backup buffer");
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -10,9 +10,24 @@
|
||||
#include "esp_cache.h"
|
||||
#include "esp_private/esp_cache_private.h"
|
||||
#include "esp_cam_ctlr_dvp_dma.h"
|
||||
#include "esp_memory_utils.h"
|
||||
|
||||
#define ALIGN_UP_BY(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
|
||||
|
||||
#if defined(SOC_GDMA_TRIG_PERIPH_CAM0_BUS) && (SOC_GDMA_TRIG_PERIPH_CAM0_BUS == SOC_GDMA_BUS_AHB)
|
||||
#define DVP_GDMA_NEW_CHANNEL gdma_new_ahb_channel
|
||||
#define DVP_GDMA_DESC_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA)
|
||||
#elif defined(SOC_GDMA_TRIG_PERIPH_CAM0_BUS) && (SOC_GDMA_TRIG_PERIPH_CAM0_BUS == SOC_GDMA_BUS_AXI)
|
||||
#define DVP_GDMA_NEW_CHANNEL gdma_new_axi_channel
|
||||
#if CONFIG_SPIRAM
|
||||
#define DVP_GDMA_DESC_ALLOC_CAPS (MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA)
|
||||
#else
|
||||
#define DVP_GDMA_DESC_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA)
|
||||
#endif
|
||||
#else
|
||||
#error "Unsupported GDMA bus type for DVP"
|
||||
#endif
|
||||
|
||||
static const char *TAG = "dvp_gdma";
|
||||
|
||||
/**
|
||||
@@ -72,9 +87,9 @@ esp_err_t esp_cam_ctlr_dvp_dma_init(esp_cam_ctlr_dvp_dma_t *dma, uint32_t burst_
|
||||
#endif
|
||||
};
|
||||
|
||||
ESP_RETURN_ON_ERROR(esp_cache_get_alignment(MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA, &alignment_size), TAG, "failed to get cache alignment");
|
||||
ESP_RETURN_ON_ERROR(esp_cache_get_alignment(DVP_GDMA_DESC_ALLOC_CAPS, &alignment_size), TAG, "failed to get cache alignment");
|
||||
|
||||
ESP_RETURN_ON_ERROR(gdma_new_axi_channel(&rx_alloc_config, &dma->dma_chan), TAG, "new channel failed");
|
||||
ESP_RETURN_ON_ERROR(DVP_GDMA_NEW_CHANNEL(&rx_alloc_config, &dma->dma_chan), TAG, "new channel failed");
|
||||
|
||||
ESP_GOTO_ON_ERROR(gdma_connect(dma->dma_chan, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_CAM, 0)), fail0, TAG, "connect failed");
|
||||
|
||||
@@ -90,17 +105,21 @@ esp_err_t esp_cam_ctlr_dvp_dma_init(esp_cam_ctlr_dvp_dma_t *dma, uint32_t burst_
|
||||
.access_ext_mem = true,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(gdma_config_transfer(dma->dma_chan, &transfer_config), fail1, TAG, "set trans ability failed");
|
||||
size_t int_mem_align = 0;
|
||||
size_t ext_mem_align = 0;
|
||||
gdma_get_alignment_constraints(dma->dma_chan, &int_mem_align, &ext_mem_align);
|
||||
|
||||
dma->desc_count = size / ESP_CAM_CTLR_DVP_DMA_DESC_BUFFER_MAX_SIZE;
|
||||
if (size % ESP_CAM_CTLR_DVP_DMA_DESC_BUFFER_MAX_SIZE) {
|
||||
dma->desc_count++;
|
||||
}
|
||||
dma->size = size;
|
||||
|
||||
ESP_LOGD(TAG, "alignment: 0x%x\n", alignment_size);
|
||||
alignment_size = (alignment_size == 0) ? 1 : alignment_size;
|
||||
dma->desc_size = ALIGN_UP_BY(dma->desc_count * sizeof(esp_cam_ctlr_dvp_dma_desc_t), alignment_size);
|
||||
|
||||
dma->desc = heap_caps_aligned_alloc(alignment_size, dma->desc_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA);
|
||||
ESP_LOGD(TAG, "alignment_size: %d, dma->desc_count: %d, dma->desc_size: %d", alignment_size, dma->desc_count, dma->desc_size);
|
||||
dma->desc = heap_caps_aligned_alloc(alignment_size, dma->desc_size, DVP_GDMA_DESC_ALLOC_CAPS);
|
||||
|
||||
ESP_GOTO_ON_FALSE(dma->desc, ESP_ERR_NO_MEM, fail1, TAG, "no mem for DVP DMA descriptor");
|
||||
|
||||
return ESP_OK;
|
||||
@@ -144,15 +163,15 @@ esp_err_t esp_cam_ctlr_dvp_dma_deinit(esp_cam_ctlr_dvp_dma_t *dma)
|
||||
*/
|
||||
esp_err_t IRAM_ATTR esp_cam_ctlr_dvp_dma_start(esp_cam_ctlr_dvp_dma_t *dma, uint8_t *buffer, size_t size)
|
||||
{
|
||||
esp_err_t ret;
|
||||
|
||||
ESP_RETURN_ON_FALSE_ISR(dma, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
|
||||
ESP_RETURN_ON_FALSE_ISR(dma->size >= size, ESP_ERR_INVALID_ARG, TAG, "input buffer size is out of range");
|
||||
|
||||
esp_cam_ctlr_dvp_config_dma_desc(dma->desc, buffer, size);
|
||||
|
||||
ret = esp_cache_msync(dma->desc, dma->desc_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE);
|
||||
if (esp_ptr_external_ram(dma->desc)) {
|
||||
esp_err_t ret = esp_cache_msync(dma->desc, dma->desc_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE);
|
||||
assert(ret == ESP_OK);
|
||||
}
|
||||
|
||||
return gdma_start(dma->dma_chan, (intptr_t)dma->desc);
|
||||
}
|
||||
|
@@ -1,2 +1,2 @@
|
||||
| Supported Targets | ESP32-P4 |
|
||||
| ----------------- | -------- |
|
||||
| Supported Targets | ESP32-P4 | ESP32-S3 |
|
||||
| ----------------- | -------- | -------- |
|
||||
|
@@ -17,7 +17,7 @@ TEST_CASE("TEST DVP driver allocation", "[DVP]")
|
||||
.h_res = 800,
|
||||
.v_res = 640,
|
||||
.input_data_color_type = CAM_CTLR_COLOR_RGB565,
|
||||
.dma_burst_size = 128,
|
||||
.dma_burst_size = 64,
|
||||
.byte_swap_en = false,
|
||||
.pin_dont_init = true,
|
||||
.external_xtal = true,
|
||||
@@ -42,7 +42,7 @@ TEST_CASE("TEST DVP driver allocation with JPEG input", "[DVP]")
|
||||
.clk_src = CAM_CLK_SRC_DEFAULT,
|
||||
.h_res = 800,
|
||||
.v_res = 640,
|
||||
.dma_burst_size = 128,
|
||||
.dma_burst_size = 64,
|
||||
.byte_swap_en = false,
|
||||
.pin_dont_init = true,
|
||||
.pic_format_jpeg = true,
|
||||
@@ -69,7 +69,7 @@ TEST_CASE("TEST DVP driver no backup buffer usage", "[DVP]")
|
||||
.h_res = 800,
|
||||
.v_res = 640,
|
||||
.input_data_color_type = CAM_CTLR_COLOR_RGB565,
|
||||
.dma_burst_size = 128,
|
||||
.dma_burst_size = 64,
|
||||
.byte_swap_en = false,
|
||||
.bk_buffer_dis = true,
|
||||
.pin_dont_init = true,
|
||||
@@ -96,7 +96,7 @@ TEST_CASE("TEST DVP driver intern/extern init", "[DVP]")
|
||||
.h_res = 800,
|
||||
.v_res = 640,
|
||||
.input_data_color_type = CAM_CTLR_COLOR_RGB565,
|
||||
.dma_burst_size = 128,
|
||||
.dma_burst_size = 64,
|
||||
.byte_swap_en = false,
|
||||
.external_xtal = true,
|
||||
};
|
||||
@@ -128,7 +128,7 @@ TEST_CASE("TEST DVP driver intern/extern generate xclk", "[DVP]")
|
||||
.h_res = 800,
|
||||
.v_res = 640,
|
||||
.input_data_color_type = CAM_CTLR_COLOR_RGB565,
|
||||
.dma_burst_size = 128,
|
||||
.dma_burst_size = 64,
|
||||
.byte_swap_en = false,
|
||||
.external_xtal = true,
|
||||
};
|
||||
|
@@ -14,3 +14,14 @@ from pytest_embedded_idf.utils import idf_parametrize
|
||||
@idf_parametrize('target', ['esp32p4'], indirect=['target'])
|
||||
def test_dvp(dut: Dut) -> None:
|
||||
dut.run_all_single_board_cases()
|
||||
|
||||
|
||||
@pytest.mark.octal_psram
|
||||
@pytest.mark.parametrize(
|
||||
'config',
|
||||
['cache_safe', 'release', 'pm_enable'],
|
||||
indirect=True,
|
||||
)
|
||||
@idf_parametrize('target', ['esp32s3'], indirect=['target'])
|
||||
def test_dvp_octal(dut: Dut) -> None:
|
||||
dut.run_all_single_board_cases()
|
||||
|
@@ -4,6 +4,4 @@ CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
|
||||
CONFIG_HAL_ASSERTION_SILENT=y
|
||||
|
||||
CONFIG_CAM_CTLR_MIPI_CSI_ISR_CACHE_SAFE=y
|
||||
CONFIG_CAM_CTLR_ISP_DVP_ISR_CACHE_SAFE=y
|
||||
CONFIG_CAM_CTLR_DVP_CAM_ISR_CACHE_SAFE=y
|
||||
|
@@ -2,4 +2,3 @@
|
||||
# Espressif IoT Development Framework (ESP-IDF) 5.4.0 Project Minimal Configuration
|
||||
#
|
||||
CONFIG_ESP_TASK_WDT_EN=n
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
|
@@ -0,0 +1,4 @@
|
||||
CONFIG_IDF_TARGET="esp32s3"
|
||||
|
||||
CONFIG_SPIRAM=y
|
||||
CONFIG_SPIRAM_MODE_OCT=y
|
@@ -199,7 +199,9 @@ esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lc
|
||||
(uint32_t)lcd_ll_get_interrupt_status_reg(bus->hal.dev),
|
||||
LCD_LL_EVENT_TRANS_DONE, i80_lcd_default_isr_handler, bus, &bus->intr);
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "install interrupt failed");
|
||||
PERIPH_RCC_ATOMIC() {
|
||||
lcd_ll_enable_interrupt(bus->hal.dev, LCD_LL_EVENT_TRANS_DONE, false); // disable all interrupts
|
||||
}
|
||||
lcd_ll_clear_interrupt_status(bus->hal.dev, UINT32_MAX); // clear pending interrupt
|
||||
// install DMA service
|
||||
bus->max_transfer_bytes = bus_config->max_transfer_bytes;
|
||||
@@ -215,8 +217,10 @@ esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lc
|
||||
lcd_ll_set_swizzle_mode(bus->hal.dev, LCD_LL_SWIZZLE_AB2BA);
|
||||
// number of data cycles is controlled by DMA buffer size
|
||||
lcd_ll_enable_output_always_on(bus->hal.dev, true);
|
||||
PERIPH_RCC_ATOMIC() {
|
||||
// enable trans done interrupt
|
||||
lcd_ll_enable_interrupt(bus->hal.dev, LCD_LL_EVENT_TRANS_DONE, true);
|
||||
}
|
||||
// trigger a quick "trans done" event, and wait for the interrupt line goes active
|
||||
// this could ensure we go into ISR handler next time we call `esp_intr_enable`
|
||||
lcd_periph_trigger_quick_trans_done_event(bus);
|
||||
|
@@ -336,7 +336,9 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf
|
||||
(uint32_t)lcd_ll_get_interrupt_status_reg(rgb_panel->hal.dev),
|
||||
LCD_LL_EVENT_VSYNC_END, rgb_lcd_default_isr_handler, rgb_panel, &rgb_panel->intr);
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "install interrupt failed");
|
||||
PERIPH_RCC_ATOMIC() {
|
||||
lcd_ll_enable_interrupt(rgb_panel->hal.dev, LCD_LL_EVENT_VSYNC_END, false); // disable all interrupts
|
||||
}
|
||||
lcd_ll_clear_interrupt_status(rgb_panel->hal.dev, UINT32_MAX); // clear pending interrupt
|
||||
|
||||
// install DMA service
|
||||
@@ -573,8 +575,10 @@ static esp_err_t rgb_panel_init(esp_lcd_panel_t *panel)
|
||||
// in stream mode, after finish one frame, the LCD controller will ask for data automatically from the DMA
|
||||
// DMA should prepare the next frame data within porch region
|
||||
lcd_ll_enable_auto_next_frame(rgb_panel->hal.dev, rgb_panel->flags.stream_mode);
|
||||
PERIPH_RCC_ATOMIC() {
|
||||
// trigger interrupt on the end of frame
|
||||
lcd_ll_enable_interrupt(rgb_panel->hal.dev, LCD_LL_EVENT_VSYNC_END, true);
|
||||
}
|
||||
// enable intr
|
||||
esp_intr_enable(rgb_panel->intr);
|
||||
// start transmission
|
||||
|
@@ -611,6 +611,12 @@ static inline void cam_ll_enable_interrupt(lcd_cam_dev_t *dev, uint32_t mask, bo
|
||||
dev->lc_dma_int_ena.val &= ~(mask & 0x0c);
|
||||
}
|
||||
}
|
||||
/// use a macro to wrap the function, force the caller to use it in a critical section
|
||||
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
|
||||
#define cam_ll_enable_interrupt(...) do { \
|
||||
(void)__DECLARE_RCC_ATOMIC_ENV; \
|
||||
cam_ll_enable_interrupt(__VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
/**
|
||||
* @brief Get interrupt status value
|
||||
|
@@ -749,6 +749,12 @@ static inline void lcd_ll_enable_interrupt(lcd_cam_dev_t *dev, uint32_t mask, bo
|
||||
dev->lc_dma_int_ena.val &= ~(mask & 0x03);
|
||||
}
|
||||
}
|
||||
/// use a macro to wrap the function, force the caller to use it in a critical section
|
||||
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
|
||||
#define lcd_ll_enable_interrupt(...) do { \
|
||||
(void)__DECLARE_RCC_ATOMIC_ENV; \
|
||||
lcd_ll_enable_interrupt(__VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
/**
|
||||
* @brief Get interrupt status value
|
||||
|
621
components/hal/esp32s3/include/hal/cam_ll.h
Normal file
621
components/hal/esp32s3/include/hal/cam_ll.h
Normal file
@@ -0,0 +1,621 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "hal/misc.h"
|
||||
#include "hal/assert.h"
|
||||
#include "soc/lcd_cam_struct.h"
|
||||
#include "soc/system_struct.h"
|
||||
#include "hal/cam_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define CAM_LL_GET_HW(id) (((id) == 0) ? (&LCD_CAM) : NULL)
|
||||
|
||||
#define CAM_LL_CLK_FRAC_DIV_N_MAX 256 // CAM_CLK = CAM_CLK_S / (N + b/a), the N register is 8 bit-width
|
||||
#define CAM_LL_CLK_FRAC_DIV_AB_MAX 64 // CAM_CLK = CAM_CLK_S / (N + b/a), the a/b register is 6 bit-width
|
||||
|
||||
/**
|
||||
* @brief Enable the bus clock for CAM module
|
||||
*
|
||||
* @param group_id Group ID
|
||||
* @param enable true to enable, false to disable
|
||||
*/
|
||||
static inline void cam_ll_enable_bus_clock(int group_id, bool en)
|
||||
{
|
||||
(void)group_id;
|
||||
SYSTEM.perip_clk_en1.lcd_cam_clk_en = en;
|
||||
}
|
||||
/// use a macro to wrap the function, force the caller to use it in a critical section
|
||||
/// the critical section needs to declare the __DECLARE_RCC_RC_ATOMIC_ENV variable in advance
|
||||
#define cam_ll_enable_bus_clock(...) do { \
|
||||
(void)__DECLARE_RCC_RC_ATOMIC_ENV; \
|
||||
cam_ll_enable_bus_clock(__VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
/**
|
||||
* @brief Reset the CAM module
|
||||
*
|
||||
* @param group_id Group ID
|
||||
*/
|
||||
static inline void cam_ll_reset_register(int group_id)
|
||||
{
|
||||
(void)group_id;
|
||||
SYSTEM.perip_rst_en1.lcd_cam_rst = 1;
|
||||
SYSTEM.perip_rst_en1.lcd_cam_rst = 0;
|
||||
}
|
||||
|
||||
/// use a macro to wrap the function, force the caller to use it in a critical section
|
||||
/// the critical section needs to declare the __DECLARE_RCC_RC_ATOMIC_ENV variable in advance
|
||||
#define cam_ll_reset_register(...) do { \
|
||||
(void)__DECLARE_RCC_RC_ATOMIC_ENV; \
|
||||
cam_ll_reset_register(__VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
/**
|
||||
* @brief Enable clock gating
|
||||
*
|
||||
* @param group_id Group ID
|
||||
* @param en True to enable, False to disable
|
||||
*/
|
||||
static inline void cam_ll_enable_clk(int group_id, bool en)
|
||||
{
|
||||
(void)group_id;
|
||||
(void)en;
|
||||
//For compatibility
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Select clock source for CAM peripheral
|
||||
*
|
||||
* @param group_id Group ID
|
||||
* @param src Clock source
|
||||
*/
|
||||
static inline void cam_ll_select_clk_src(int group_id, cam_clock_source_t src)
|
||||
{
|
||||
switch (src) {
|
||||
case CAM_CLK_SRC_XTAL:
|
||||
LCD_CAM.cam_ctrl.cam_clk_sel = 1;
|
||||
break;
|
||||
case CAM_CLK_SRC_PLL240M:
|
||||
LCD_CAM.cam_ctrl.cam_clk_sel = 2;
|
||||
break;
|
||||
case CAM_CLK_SRC_PLL160M:
|
||||
LCD_CAM.cam_ctrl.cam_clk_sel = 3;
|
||||
break;
|
||||
default:
|
||||
// disable LCD clock source
|
||||
LCD_CAM.cam_ctrl.cam_clk_sel = 0;
|
||||
HAL_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the CAM source clock type
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
* @param src The pointer to accept the CAM source clock type
|
||||
*/
|
||||
static inline void cam_ll_get_clk_src(lcd_cam_dev_t *dev, cam_clock_source_t *src)
|
||||
{
|
||||
switch (LCD_CAM.cam_ctrl.cam_clk_sel) {
|
||||
case 1:
|
||||
*src = CAM_CLK_SRC_XTAL;
|
||||
break;
|
||||
case 2:
|
||||
*src = CAM_CLK_SRC_PLL240M;
|
||||
break;
|
||||
case 3:
|
||||
*src = CAM_CLK_SRC_PLL160M;
|
||||
break;
|
||||
default:
|
||||
HAL_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set clock coefficient of CAM peripheral
|
||||
*
|
||||
* @param group_id Group ID
|
||||
* @param div_num Integer part of the divider
|
||||
* @param div_a denominator of the divider
|
||||
* @param div_b numerator of the divider
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void cam_ll_set_group_clock_coeff(int group_id, int div_num, int div_a, int div_b)
|
||||
{
|
||||
HAL_ASSERT(div_num >= 2 && div_num < CAM_LL_CLK_FRAC_DIV_N_MAX);
|
||||
|
||||
LCD_CAM.cam_ctrl.cam_clkm_div_num = div_num;
|
||||
LCD_CAM.cam_ctrl.cam_clkm_div_a = div_a;
|
||||
LCD_CAM.cam_ctrl.cam_clkm_div_b = div_b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable stop signal for CAM peripheral
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
* @param en True to stop when GDMA Rx FIFO is full, False to not stop
|
||||
*/
|
||||
static inline void cam_ll_enable_stop_signal(lcd_cam_dev_t *dev, bool en)
|
||||
{
|
||||
dev->cam_ctrl.cam_stop_en = en;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set vsync filter threshold value
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
* @param value Filter threshold value for CAM_VSYNC_SIGNAL, range [0, 7]
|
||||
*/
|
||||
static inline void cam_ll_set_vsync_filter_thres(lcd_cam_dev_t *dev, uint32_t value)
|
||||
{
|
||||
dev->cam_ctrl.cam_vsync_filter_thres = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable to generate LCD_CAM_CAM_HS_INT
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
* @param en True to enable to generate LCD_CAM_CAM_HS_INT, False to disable
|
||||
*/
|
||||
static inline void cam_ll_enable_hs_line_int(lcd_cam_dev_t *dev, bool en)
|
||||
{
|
||||
dev->cam_ctrl.cam_line_int_en = en;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable CAM_VSYNC to generate in_suc_eof
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
* @param en True to enable CAM_VSYNC to generate in_suc_eof, False to use LCD_CAM_CAM_REC_DATA_BYTELEN to control in_suc_eof
|
||||
*/
|
||||
static inline void cam_ll_enable_vsync_generate_eof(lcd_cam_dev_t *dev, bool en)
|
||||
{
|
||||
dev->cam_ctrl.cam_vs_eof_en = en;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable to swap every two 8-bit input data
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
* @param en True to enable invert, False to disable invert
|
||||
*/
|
||||
static inline void cam_ll_enable_8bits_data_invert(lcd_cam_dev_t *dev, bool en)
|
||||
{
|
||||
dev->cam_rgb_yuv.cam_conv_8bits_data_inv = en;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable YUV-RGB converter
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
* @param en True to enable converter, False to disable converter
|
||||
*/
|
||||
static inline void cam_ll_enable_rgb_yuv_convert(lcd_cam_dev_t *dev, bool en)
|
||||
{
|
||||
dev->cam_rgb_yuv.cam_conv_bypass = en;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set convert data line width
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
* @param width data line width (8 or 16)
|
||||
*/
|
||||
static inline void cam_ll_set_convert_data_width(lcd_cam_dev_t *dev, uint32_t width)
|
||||
{
|
||||
HAL_ASSERT(width == 8 || width == 16);
|
||||
dev->cam_rgb_yuv.cam_conv_mode_8bits_on = (width == 8) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the color range of input data
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
* @param range Color range
|
||||
*/
|
||||
static inline void cam_ll_set_input_color_range(lcd_cam_dev_t *dev, color_range_t range)
|
||||
{
|
||||
if (range == COLOR_RANGE_LIMIT) {
|
||||
dev->cam_rgb_yuv.cam_conv_data_in_mode = 0;
|
||||
} else if (range == COLOR_RANGE_FULL) {
|
||||
dev->cam_rgb_yuv.cam_conv_data_in_mode = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the color range of output data
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
* @param range Color range
|
||||
*/
|
||||
static inline void cam_ll_set_output_color_range(lcd_cam_dev_t *dev, color_range_t range)
|
||||
{
|
||||
if (range == COLOR_RANGE_LIMIT) {
|
||||
dev->cam_rgb_yuv.cam_conv_data_out_mode = 0;
|
||||
} else if (range == COLOR_RANGE_FULL) {
|
||||
dev->cam_rgb_yuv.cam_conv_data_out_mode = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set YUV conversion standard
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
* @param std YUV conversion standard
|
||||
*/
|
||||
static inline void cam_ll_set_yuv_convert_std(lcd_cam_dev_t *dev, color_conv_std_rgb_yuv_t std)
|
||||
{
|
||||
if (std == COLOR_CONV_STD_RGB_YUV_BT601) {
|
||||
dev->cam_rgb_yuv.cam_conv_protocol_mode = 0;
|
||||
} else if (std == COLOR_CONV_STD_RGB_YUV_BT709) {
|
||||
dev->cam_rgb_yuv.cam_conv_protocol_mode = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the converter mode: RGB565 to YUV
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
* @param yuv_sample YUV sample mode
|
||||
*/
|
||||
static inline void cam_ll_set_convert_mode_rgb_to_yuv(lcd_cam_dev_t *dev, color_pixel_yuv_format_t yuv_sample)
|
||||
{
|
||||
dev->cam_rgb_yuv.cam_conv_trans_mode = 1;
|
||||
dev->cam_rgb_yuv.cam_conv_yuv2yuv_mode = 3;
|
||||
switch (yuv_sample) {
|
||||
case COLOR_PIXEL_YUV422:
|
||||
dev->cam_rgb_yuv.cam_conv_yuv_mode = 0;
|
||||
break;
|
||||
case COLOR_PIXEL_YUV420:
|
||||
dev->cam_rgb_yuv.cam_conv_yuv_mode = 1;
|
||||
break;
|
||||
case COLOR_PIXEL_YUV411:
|
||||
dev->cam_rgb_yuv.cam_conv_yuv_mode = 2;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the converter mode: YUV to RGB565
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
* @param yuv_sample YUV sample mode
|
||||
*/
|
||||
static inline void cam_ll_set_convert_mode_yuv_to_rgb(lcd_cam_dev_t *dev, color_pixel_yuv_format_t yuv_sample)
|
||||
{
|
||||
dev->cam_rgb_yuv.cam_conv_trans_mode = 0;
|
||||
dev->cam_rgb_yuv.cam_conv_yuv2yuv_mode = 3;
|
||||
switch (yuv_sample) {
|
||||
case COLOR_PIXEL_YUV422:
|
||||
dev->cam_rgb_yuv.cam_conv_yuv_mode = 0;
|
||||
break;
|
||||
case COLOR_PIXEL_YUV420:
|
||||
dev->cam_rgb_yuv.cam_conv_yuv_mode = 1;
|
||||
break;
|
||||
case COLOR_PIXEL_YUV411:
|
||||
dev->cam_rgb_yuv.cam_conv_yuv_mode = 2;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the converter mode: YUV to YUV
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
* @param src_sample Source YUV sample mode
|
||||
* @param dst_sample Destination YUV sample mode
|
||||
*/
|
||||
static inline void cam_ll_set_convert_mode_yuv_to_yuv(lcd_cam_dev_t *dev, color_pixel_yuv_format_t src_sample, color_pixel_yuv_format_t dst_sample)
|
||||
{
|
||||
HAL_ASSERT(src_sample != dst_sample);
|
||||
dev->cam_rgb_yuv.cam_conv_trans_mode = 1;
|
||||
switch (src_sample) {
|
||||
case COLOR_PIXEL_YUV422:
|
||||
dev->cam_rgb_yuv.cam_conv_yuv_mode = 0;
|
||||
break;
|
||||
case COLOR_PIXEL_YUV420:
|
||||
dev->cam_rgb_yuv.cam_conv_yuv_mode = 1;
|
||||
break;
|
||||
case COLOR_PIXEL_YUV411:
|
||||
dev->cam_rgb_yuv.cam_conv_yuv_mode = 2;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
switch (dst_sample) {
|
||||
case COLOR_PIXEL_YUV422:
|
||||
dev->cam_rgb_yuv.cam_conv_yuv2yuv_mode = 0;
|
||||
break;
|
||||
case COLOR_PIXEL_YUV420:
|
||||
dev->cam_rgb_yuv.cam_conv_yuv2yuv_mode = 1;
|
||||
break;
|
||||
case COLOR_PIXEL_YUV411:
|
||||
dev->cam_rgb_yuv.cam_conv_yuv2yuv_mode = 2;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set camera received data byte length
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
* @param length received data byte length, range [0, 0xFFFF]
|
||||
*/
|
||||
static inline void cam_ll_set_recv_data_bytelen(lcd_cam_dev_t *dev, uint32_t length)
|
||||
{
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->cam_ctrl1, cam_rec_data_bytelen, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set line number to trigger interrupt
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
* @param number line number to trigger hs interrupt, range [0, 0x3F]
|
||||
*/
|
||||
static inline void cam_ll_set_line_int_num(lcd_cam_dev_t *dev, uint32_t number)
|
||||
{
|
||||
dev->cam_ctrl1.cam_line_int_num = number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Whether to invert the input signal CAM_PCLK
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
* @param en True to invert, False to not invert
|
||||
*/
|
||||
static inline void cam_ll_enable_invert_pclk(lcd_cam_dev_t *dev, bool en)
|
||||
{
|
||||
dev->cam_ctrl1.cam_clk_inv = en;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable CAM_VSYNC filter function
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
* @param en True to enable, False to bypass
|
||||
*/
|
||||
static inline void cam_ll_enable_vsync_filter(lcd_cam_dev_t *dev, bool en)
|
||||
{
|
||||
dev->cam_ctrl1.cam_vsync_filter_en = en;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set CAM input data width
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
* @param stride 16: The bit number of input data is 9~16. 8: The bit number of input data is 0~8.
|
||||
*/
|
||||
static inline void cam_ll_set_input_data_width(lcd_cam_dev_t *dev, uint32_t stride)
|
||||
{
|
||||
switch (stride) {
|
||||
case 8:
|
||||
dev->cam_ctrl1.cam_2byte_en = 0;
|
||||
break;
|
||||
case 16:
|
||||
dev->cam_ctrl1.cam_2byte_en = 1;
|
||||
break;
|
||||
default:
|
||||
HAL_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Whether to invert CAM_DE
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
* @param en True to invert, False to not invert
|
||||
*/
|
||||
static inline void cam_ll_enable_invert_de(lcd_cam_dev_t *dev, bool en)
|
||||
{
|
||||
dev->cam_ctrl1.cam_de_inv = en;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Whether to invert CAM_HSYNC
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
* @param en True to invert, False to not invert
|
||||
*/
|
||||
static inline void cam_ll_enable_invert_hsync(lcd_cam_dev_t *dev, bool en)
|
||||
{
|
||||
dev->cam_ctrl1.cam_hsync_inv = en;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Whether to invert CAM_VSYNC
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
* @param en True to invert, False to not invert
|
||||
*/
|
||||
static inline void cam_ll_enable_invert_vsync(lcd_cam_dev_t *dev, bool en)
|
||||
{
|
||||
dev->cam_ctrl1.cam_vsync_inv = en;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable the mode to control the input control signals
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
* @param mode 1: Input control signals are CAM_DE, CAM_HSYNC and CAM_VSYNC;
|
||||
* 0: Input control signals are CAM_DE and CAM_VSYNC. CAM_HSYNC and CAM_DE are all 1 the the same time.
|
||||
*/
|
||||
static inline void cam_ll_set_vh_de_mode(lcd_cam_dev_t *dev, bool enable)
|
||||
{
|
||||
dev->cam_ctrl1.cam_vh_de_mode_en = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the mode of input control signals
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
* @param en The pointer to accept the vh_de mode status. 1: Input control signals are CAM_DE, CAM_HSYNC and CAM_VSYNC;
|
||||
* 0: Input control signals are CAM_DE and CAM_VSYNC. CAM_HSYNC and CAM_DE are all 1 the the same time.
|
||||
*/
|
||||
static inline void cam_ll_get_vh_de_mode(lcd_cam_dev_t *dev, bool *en)
|
||||
{
|
||||
*en = dev->cam_ctrl1.cam_vh_de_mode_en;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the wire width of CAM output
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
* @param width CAM output wire width
|
||||
*/
|
||||
static inline void cam_ll_set_data_wire_width(lcd_cam_dev_t *dev, uint32_t width)
|
||||
{
|
||||
// data line width is same as data stride that set in `cam_ll_set_input_data_width`
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start the CAM transaction
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void cam_ll_start(lcd_cam_dev_t *dev)
|
||||
{
|
||||
dev->cam_ctrl.cam_update = 1;
|
||||
dev->cam_ctrl1.cam_start = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stop the CAM transaction
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void cam_ll_stop(lcd_cam_dev_t *dev)
|
||||
{
|
||||
dev->cam_ctrl1.cam_start = 0;
|
||||
dev->cam_ctrl.cam_update = 1; // self clear
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Whether to reverse the data bit order
|
||||
*
|
||||
* @note It acts before the YUV-RGB converter
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
* @param en True to reverse, False to not reverse
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void cam_ll_reverse_dma_data_bit_order(lcd_cam_dev_t *dev, bool en)
|
||||
{
|
||||
dev->cam_ctrl.cam_bit_order = en;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Whether to swap adjacent two bytes
|
||||
*
|
||||
* @note This acts before the YUV-RGB converter, mainly to change the data endian.
|
||||
* {B1,B0},{B3,B2} => {B0,B1}{B2,B3}
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
* @param en True to swap the byte order, False to not swap
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void cam_ll_swap_dma_data_byte_order(lcd_cam_dev_t *dev, bool en)
|
||||
{
|
||||
dev->cam_ctrl.cam_byte_order = en;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reset camera module
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void cam_ll_reset(lcd_cam_dev_t *dev)
|
||||
{
|
||||
dev->cam_ctrl1.cam_reset = 1; // self clear
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reset Async RX FIFO
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void cam_ll_fifo_reset(lcd_cam_dev_t *dev)
|
||||
{
|
||||
dev->cam_ctrl1.cam_afifo_reset = 1; // self clear
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable/disable interrupt by mask
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
* @param mask Interrupt mask
|
||||
* @param en True to enable interrupt, False to disable interrupt
|
||||
*/
|
||||
static inline void cam_ll_enable_interrupt(lcd_cam_dev_t *dev, uint32_t mask, bool en)
|
||||
{
|
||||
if (en) {
|
||||
dev->lc_dma_int_ena.val |= mask & 0x0c;
|
||||
} else {
|
||||
dev->lc_dma_int_ena.val &= ~(mask & 0x0c);
|
||||
}
|
||||
}
|
||||
/// use a macro to wrap the function, force the caller to use it in a critical section
|
||||
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
|
||||
#define cam_ll_enable_interrupt(...) do { \
|
||||
(void)__DECLARE_RCC_ATOMIC_ENV; \
|
||||
cam_ll_enable_interrupt(__VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
/**
|
||||
* @brief Get interrupt status value
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
* @return Interrupt status value
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline uint32_t cam_ll_get_interrupt_status(lcd_cam_dev_t *dev)
|
||||
{
|
||||
return dev->lc_dma_int_st.val & 0x0c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clear interrupt status by mask
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
* @param mask Interrupt status mask
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void cam_ll_clear_interrupt_status(lcd_cam_dev_t *dev, uint32_t mask)
|
||||
{
|
||||
dev->lc_dma_int_clr.val = mask & 0x0c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get address of interrupt status register address
|
||||
*
|
||||
* @param dev CAM register base address
|
||||
* @return Interrupt status register address
|
||||
*/
|
||||
static inline volatile void *cam_ll_get_interrupt_status_reg(lcd_cam_dev_t *dev)
|
||||
{
|
||||
return &dev->lc_dma_int_st;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -723,6 +723,12 @@ static inline void lcd_ll_enable_interrupt(lcd_cam_dev_t *dev, uint32_t mask, bo
|
||||
dev->lc_dma_int_ena.val &= ~(mask & 0x03);
|
||||
}
|
||||
}
|
||||
/// use a macro to wrap the function, force the caller to use it in a critical section
|
||||
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
|
||||
#define lcd_ll_enable_interrupt(...) do { \
|
||||
(void)__DECLARE_RCC_ATOMIC_ENV; \
|
||||
lcd_ll_enable_interrupt(__VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
/**
|
||||
* @brief Get interrupt status value
|
||||
|
40
components/soc/esp32s3/cam_periph.c
Normal file
40
components/soc/esp32s3/cam_periph.c
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "soc/gpio_sig_map.h"
|
||||
#include "soc/cam_periph.h"
|
||||
|
||||
const cam_signal_conn_t cam_periph_signals = {
|
||||
.buses = {
|
||||
[0] = {
|
||||
.module = PERIPH_LCD_CAM_MODULE,
|
||||
.irq_id = ETS_LCD_CAM_INTR_SOURCE,
|
||||
.data_sigs = {
|
||||
CAM_DATA_IN0_IDX,
|
||||
CAM_DATA_IN1_IDX,
|
||||
CAM_DATA_IN2_IDX,
|
||||
CAM_DATA_IN3_IDX,
|
||||
CAM_DATA_IN4_IDX,
|
||||
CAM_DATA_IN5_IDX,
|
||||
CAM_DATA_IN6_IDX,
|
||||
CAM_DATA_IN7_IDX,
|
||||
CAM_DATA_IN8_IDX,
|
||||
CAM_DATA_IN9_IDX,
|
||||
CAM_DATA_IN10_IDX,
|
||||
CAM_DATA_IN11_IDX,
|
||||
CAM_DATA_IN12_IDX,
|
||||
CAM_DATA_IN13_IDX,
|
||||
CAM_DATA_IN14_IDX,
|
||||
CAM_DATA_IN15_IDX
|
||||
},
|
||||
.hsync_sig = CAM_H_SYNC_IDX,
|
||||
.vsync_sig = CAM_V_SYNC_IDX,
|
||||
.pclk_sig = CAM_PCLK_IDX,
|
||||
.de_sig = CAM_H_ENABLE_IDX,
|
||||
.clk_sig = CAM_CLK_IDX
|
||||
}
|
||||
}
|
||||
};
|
@@ -47,6 +47,10 @@ config SOC_LCDCAM_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_LCDCAM_CAM_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_LCDCAM_I80_LCD_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
@@ -1538,3 +1542,15 @@ config SOC_ULP_HAS_ADC
|
||||
config SOC_PHY_COMBO_MODULE
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_LCDCAM_CAM_SUPPORT_RGB_YUV_CONV
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_LCDCAM_CAM_PERIPH_NUM
|
||||
int
|
||||
default 1
|
||||
|
||||
config SOC_LCDCAM_CAM_DATA_WIDTH_MAX
|
||||
int
|
||||
default 16
|
||||
|
@@ -197,6 +197,23 @@ typedef enum {
|
||||
LCD_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the default choice */
|
||||
} soc_periph_lcd_clk_src_t;
|
||||
|
||||
//////////////////////////////////////////////////LCD///////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* @brief Array initializer for all supported clock sources of CAM
|
||||
*/
|
||||
#define SOC_CAM_CLKS {SOC_MOD_CLK_PLL_F160M, SOC_MOD_CLK_PLL_D2, SOC_MOD_CLK_XTAL}
|
||||
|
||||
/**
|
||||
* @brief Type of CAM clock source
|
||||
*/
|
||||
typedef enum {
|
||||
CAM_CLK_SRC_PLL160M = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the source clock */
|
||||
CAM_CLK_SRC_PLL240M = SOC_MOD_CLK_PLL_D2, /*!< Select PLL_D2 as the source clock */
|
||||
CAM_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL, /*!< Select XTAL as the source clock */
|
||||
CAM_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_D2, /*!< Select PLL_D2 as the default choice */
|
||||
} soc_periph_cam_clk_src_t;
|
||||
|
||||
//////////////////////////////////////////////////RMT///////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
|
@@ -33,6 +33,7 @@
|
||||
#define SOC_AHB_GDMA_SUPPORTED 1
|
||||
#define SOC_GPTIMER_SUPPORTED 1
|
||||
#define SOC_LCDCAM_SUPPORTED 1
|
||||
#define SOC_LCDCAM_CAM_SUPPORTED 1 // support the camera driver based on the LCD_CAM peripheral
|
||||
#define SOC_LCDCAM_I80_LCD_SUPPORTED 1
|
||||
#define SOC_LCDCAM_RGB_LCD_SUPPORTED 1
|
||||
#define SOC_MCPWM_SUPPORTED 1
|
||||
@@ -605,3 +606,8 @@
|
||||
|
||||
/*------------------------------------- PHY CAPS -------------------------------------*/
|
||||
#define SOC_PHY_COMBO_MODULE (1) /*!< Support Wi-Fi and BLE*/
|
||||
|
||||
/*--------------------------- CAM ---------------------------------*/
|
||||
#define SOC_LCDCAM_CAM_SUPPORT_RGB_YUV_CONV (1)
|
||||
#define SOC_LCDCAM_CAM_PERIPH_NUM (1U)
|
||||
#define SOC_LCDCAM_CAM_DATA_WIDTH_MAX (16U)
|
||||
|
@@ -58,6 +58,17 @@ examples/peripherals/camera/dvp_isp_dsi:
|
||||
- esp_lcd
|
||||
- esp_driver_cam
|
||||
|
||||
examples/peripherals/camera/dvp_spi_lcd:
|
||||
disable:
|
||||
- if: SOC_LCDCAM_CAM_SUPPORTED != 1
|
||||
disable_test:
|
||||
- if: IDF_TARGET == "esp32s3"
|
||||
temporary: true
|
||||
reason: lack of runners
|
||||
depends_components:
|
||||
- esp_lcd
|
||||
- esp_driver_cam
|
||||
|
||||
examples/peripherals/camera/mipi_isp_dsi:
|
||||
disable:
|
||||
- if: SOC_MIPI_CSI_SUPPORTED != 1 or SOC_MIPI_DSI_SUPPORTED != 1
|
||||
|
@@ -1,4 +1,4 @@
|
||||
dependencies:
|
||||
espressif/esp_cam_sensor: "^0.6.1"
|
||||
espressif/esp_cam_sensor: "^1.1.0"
|
||||
idf:
|
||||
version: ">=5.3.0"
|
||||
|
8
examples/peripherals/camera/dvp_spi_lcd/CMakeLists.txt
Normal file
8
examples/peripherals/camera/dvp_spi_lcd/CMakeLists.txt
Normal 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)
|
139
examples/peripherals/camera/dvp_spi_lcd/README.md
Normal file
139
examples/peripherals/camera/dvp_spi_lcd/README.md
Normal 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)
|
@@ -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
|
||||
)
|
@@ -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
|
226
examples/peripherals/camera/dvp_spi_lcd/main/dvp_spi_lcd_main.c
Normal file
226
examples/peripherals/camera/dvp_spi_lcd/main/dvp_spi_lcd_main.c
Normal 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;
|
||||
}
|
@@ -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
|
@@ -0,0 +1,3 @@
|
||||
dependencies:
|
||||
sensor_init:
|
||||
path: ${IDF_PATH}/examples/peripherals/camera/common_components/sensor_init
|
@@ -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()')
|
@@ -0,0 +1 @@
|
||||
CONFIG_CAMERA_OV2640=y
|
@@ -0,0 +1,2 @@
|
||||
CONFIG_SPIRAM_SPEED_80M=y
|
||||
CONFIG_SPIRAM=y
|
@@ -0,0 +1,2 @@
|
||||
CONFIG_SPIRAM=y
|
||||
CONFIG_SPIRAM_MODE_OCT=y
|
Reference in New Issue
Block a user