diff --git a/Kconfig b/Kconfig index f692e59358..d4701dc7cf 100755 --- a/Kconfig +++ b/Kconfig @@ -214,6 +214,14 @@ menu "Camera configuration" Maximum value of DMA buffer Larger values may fail to allocate due to insufficient contiguous memory blocks, and smaller value may cause DMA interrupt to be too frequent. + config CAMERA_PSRAM_DMA + bool "Enable PSRAM DMA mode by default" + depends on IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + default n + help + Enable DMA transfers directly from PSRAM on supported targets + (ESP32-S2 and ESP32-S3) by default. + choice CAMERA_JPEG_MODE_FRAME_SIZE_OPTION prompt "JPEG mode frame size option" default CAMERA_JPEG_MODE_FRAME_SIZE_AUTO diff --git a/README.md b/README.md index 774bf423a7..ea71806286 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,8 @@ This repository hosts ESP32 series Soc compatible driver for image sensors. Addi - Using YUV or RGB puts a lot of strain on the chip because writing to PSRAM is not particularly fast. The result is that image data might be missing. This is particularly true if WiFi is enabled. If you need RGB data, it is recommended that JPEG is captured and then turned into RGB using `fmt2rgb888` or `fmt2bmp`/`frame2bmp`. - When 1 frame buffer is used, the driver will wait for the current frame to finish (VSYNC) and start I2S DMA. After the frame is acquired, I2S will be stopped and the frame buffer returned to the application. This approach gives more control over the system, but results in longer time to get the frame. - When 2 or more frame bufers are used, I2S is running in continuous mode and each frame is pushed to a queue that the application can access. This approach puts more strain on the CPU/Memory, but allows for double the frame rate. Please use only with JPEG. +- The Kconfig option `CONFIG_CAMERA_PSRAM_DMA` enables PSRAM DMA mode on ESP32-S2 and ESP32-S3 devices. This flag defaults to false. +- You can switch PSRAM DMA mode at runtime using `esp_camera_set_psram_mode()`. ## Installation Instructions @@ -135,7 +137,7 @@ static camera_config_t camera_config = { .pin_href = CAM_PIN_HREF, .pin_pclk = CAM_PIN_PCLK, - .xclk_freq_hz = 20000000,//EXPERIMENTAL: Set to 16MHz on ESP32-S2 or ESP32-S3 to enable EDMA mode + .xclk_freq_hz = 20000000, .ledc_timer = LEDC_TIMER_0, .ledc_channel = LEDC_CHANNEL_0, diff --git a/driver/cam_hal.c b/driver/cam_hal.c index 815810343f..a15cacdffb 100644 --- a/driver/cam_hal.c +++ b/driver/cam_hal.c @@ -16,6 +16,7 @@ #include #include #include "esp_heap_caps.h" +#include "freertos/FreeRTOS.h" #include "ll_cam.h" #include "cam_hal.h" @@ -41,6 +42,14 @@ static const char *TAG = "cam_hal"; static cam_obj_t *cam_obj = NULL; +#if defined(CONFIG_CAMERA_PSRAM_DMA) +#define CAMERA_PSRAM_DMA_ENABLED CONFIG_CAMERA_PSRAM_DMA +#else +#define CAMERA_PSRAM_DMA_ENABLED 0 +#endif + +static volatile bool g_psram_dma_mode = CAMERA_PSRAM_DMA_ENABLED; +static portMUX_TYPE g_psram_dma_lock = portMUX_INITIALIZER_UNLOCKED; /* At top of cam_hal.c – one switch for noisy ISR prints */ #ifndef CAM_LOG_SPAM_EVERY_FRAME @@ -411,11 +420,12 @@ esp_err_t cam_config(const camera_config_t *config, framesize_t frame_size, uint CAM_CHECK_GOTO(ret == ESP_OK, "ll_cam_set_sample_mode failed", err); cam_obj->jpeg_mode = config->pixel_format == PIXFORMAT_JPEG; -#if CONFIG_IDF_TARGET_ESP32 - cam_obj->psram_mode = false; +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 + cam_obj->psram_mode = g_psram_dma_mode; #else - cam_obj->psram_mode = (config->xclk_freq_hz == 16000000); + cam_obj->psram_mode = false; #endif + ESP_LOGI(TAG, "PSRAM DMA mode %s", cam_obj->psram_mode ? "enabled" : "disabled"); cam_obj->frame_cnt = config->fb_count; cam_obj->width = resolution[frame_size].width; cam_obj->height = resolution[frame_size].height; @@ -612,3 +622,15 @@ bool cam_get_available_frames(void) { return 0 < uxQueueMessagesWaiting(cam_obj->frame_buffer_queue); } + +void cam_set_psram_mode(bool enable) +{ + portENTER_CRITICAL(&g_psram_dma_lock); + g_psram_dma_mode = enable; + portEXIT_CRITICAL(&g_psram_dma_lock); +} + +bool cam_get_psram_mode(void) +{ + return g_psram_dma_mode; +} diff --git a/driver/esp_camera.c b/driver/esp_camera.c index 54a8cfc490..6fb7a98024 100644 --- a/driver/esp_camera.c +++ b/driver/esp_camera.c @@ -95,6 +95,7 @@ typedef struct { static const char *CAMERA_SENSOR_NVS_KEY = "sensor"; static const char *CAMERA_PIXFORMAT_NVS_KEY = "pixformat"; static camera_state_t *s_state = NULL; +static camera_config_t s_saved_config; #if CONFIG_IDF_TARGET_ESP32S3 // LCD_CAM module of ESP32-S3 will generate xclk #define CAMERA_ENABLE_OUT_CLOCK(v) @@ -298,6 +299,7 @@ static pixformat_t get_output_data_format(camera_conv_mode_t conv_mode) esp_err_t esp_camera_init(const camera_config_t *config) { esp_err_t err; + s_saved_config = *config; err = cam_init(config); if (err != ESP_OK) { ESP_LOGE(TAG, "Camera init failed with error 0x%x", err); @@ -517,3 +519,32 @@ bool esp_camera_available_frames(void) } return cam_get_available_frames(); } + +esp_err_t esp_camera_reconfigure(const camera_config_t *config) +{ + if (!config) { + return ESP_ERR_INVALID_ARG; + } + if (s_state) { + esp_err_t err = esp_camera_deinit(); + if (err != ESP_OK) { + return err; + } + } + s_saved_config = *config; + return esp_camera_init(&s_saved_config); +} + +esp_err_t esp_camera_set_psram_mode(bool enable) +{ + cam_set_psram_mode(enable); + if (!s_state) { + return ESP_ERR_INVALID_STATE; + } + return esp_camera_reconfigure(&s_saved_config); +} + +bool esp_camera_get_psram_mode(void) +{ + return cam_get_psram_mode(); +} diff --git a/driver/include/esp_camera.h b/driver/include/esp_camera.h index 01af844d79..fa2f24cda9 100755 --- a/driver/include/esp_camera.h +++ b/driver/include/esp_camera.h @@ -138,7 +138,7 @@ typedef struct { int pin_href; /*!< GPIO pin for camera HREF line */ int pin_pclk; /*!< GPIO pin for camera PCLK line */ - int xclk_freq_hz; /*!< Frequency of XCLK signal, in Hz. EXPERIMENTAL: Set to 16MHz on ESP32-S2 or ESP32-S3 to enable EDMA mode */ + int xclk_freq_hz; /*!< Frequency of XCLK signal, in Hz. */ ledc_timer_t ledc_timer; /*!< LEDC timer to be used for generating XCLK */ ledc_channel_t ledc_channel; /*!< LEDC channel to be used for generating XCLK */ @@ -245,6 +245,35 @@ void esp_camera_return_all(void); */ bool esp_camera_available_frames(void); +/** + * @brief Enable or disable PSRAM DMA mode at runtime. + * + * @param enable True to enable PSRAM DMA mode, false to disable it. + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if the camera is not initialized + * - Propagated error from reinitialization on failure + */ +esp_err_t esp_camera_set_psram_mode(bool enable); + +/** + * @brief Reinitialize the camera with a new configuration. + * + * @param config Updated camera configuration structure + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if config is NULL + * - Propagated error from deinit or init if they fail + */ +esp_err_t esp_camera_reconfigure(const camera_config_t *config); + +/** + * @brief Get current PSRAM DMA mode state. + * + * @return True if PSRAM DMA is enabled, false otherwise. + */ +bool esp_camera_get_psram_mode(void); + #ifdef __cplusplus } diff --git a/driver/private_include/cam_hal.h b/driver/private_include/cam_hal.h index b6f812ffce..8751c53e2c 100644 --- a/driver/private_include/cam_hal.h +++ b/driver/private_include/cam_hal.h @@ -59,6 +59,9 @@ void cam_give_all(void); bool cam_get_available_frames(void); +void cam_set_psram_mode(bool enable); +bool cam_get_psram_mode(void); + #ifdef __cplusplus } #endif diff --git a/test/test_camera.c b/test/test_camera.c index c78dd1474b..5a9db9caf7 100644 --- a/test/test_camera.c +++ b/test/test_camera.c @@ -144,7 +144,6 @@ static esp_err_t init_camera(uint32_t xclk_freq_hz, pixformat_t pixel_format, fr .pin_href = HREF_GPIO_NUM, .pin_pclk = PCLK_GPIO_NUM, - //EXPERIMENTAL: Set to 16MHz on ESP32-S2 or ESP32-S3 to enable EDMA mode .xclk_freq_hz = xclk_freq_hz, .ledc_timer = LEDC_TIMER_0, .ledc_channel = LEDC_CHANNEL_0,