Skip to content

feat: add PSRAM-DMA flag + runtime API for ESP32-S2/S3; remove 16MHz heuristic #774

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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,

Expand Down
28 changes: 25 additions & 3 deletions driver/cam_hal.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <string.h>
#include <stdalign.h>
#include "esp_heap_caps.h"
#include "freertos/FreeRTOS.h"
#include "ll_cam.h"
#include "cam_hal.h"

Expand All @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
31 changes: 31 additions & 0 deletions driver/esp_camera.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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();
}
31 changes: 30 additions & 1 deletion driver/include/esp_camera.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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
}
Expand Down
3 changes: 3 additions & 0 deletions driver/private_include/cam_hal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
1 change: 0 additions & 1 deletion test/test_camera.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down