Skip to content

Commit 1077c47

Browse files
authored
Merge branch 'master' into increase_stack_size
2 parents 181004c + 307bcc0 commit 1077c47

File tree

6 files changed

+160
-14
lines changed

6 files changed

+160
-14
lines changed

CMakeLists.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET ST
7575
)
7676
endif()
7777

78-
set(priv_requires freertos nvs_flash)
78+
set(priv_requires freertos nvs_flash esp_mm)
7979

8080
set(min_version_for_esp_timer "4.2")
8181
if (idf_version VERSION_GREATER_EQUAL min_version_for_esp_timer)
@@ -91,6 +91,10 @@ if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET ST
9191
list(APPEND srcs driver/sccb.c)
9292
endif()
9393

94+
if (idf_version VERSION_GREATER_EQUAL "6.0")
95+
list(APPEND priv_requires esp_driver_gpio)
96+
endif()
97+
9498
endif()
9599

96100
idf_component_register(

driver/cam_hal.c

Lines changed: 148 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,21 @@
1717
#include <stdalign.h>
1818
#include "esp_heap_caps.h"
1919
#include "freertos/FreeRTOS.h"
20+
#include "freertos/task.h"
2021
#include "ll_cam.h"
2122
#include "cam_hal.h"
2223

2324
#if (ESP_IDF_VERSION_MAJOR == 3) && (ESP_IDF_VERSION_MINOR == 3)
2425
#include "rom/ets_sys.h"
2526
#else
2627
#include "esp_timer.h"
28+
#include "esp_cache.h"
29+
#include "hal/cache_hal.h"
30+
#include "hal/cache_ll.h"
31+
#include "esp_idf_version.h"
32+
#ifndef ESP_CACHE_MSYNC_FLAG_DIR_M2C
33+
#define ESP_CACHE_MSYNC_FLAG_DIR_M2C 0
34+
#endif
2735
#if CONFIG_IDF_TARGET_ESP32
2836
#include "esp32/rom/ets_sys.h" // will be removed in idf v5.0
2937
#elif CONFIG_IDF_TARGET_ESP32S2
@@ -56,6 +64,53 @@ static portMUX_TYPE g_psram_dma_lock = portMUX_INITIALIZER_UNLOCKED;
5664
#define CAM_LOG_SPAM_EVERY_FRAME 0 /* set to 1 to restore old behaviour */
5765
#endif
5866

67+
/* Number of bytes copied to SRAM for SOI validation when capturing
68+
* directly to PSRAM. Tunable to probe more of the frame start if needed. */
69+
#ifndef CAM_SOI_PROBE_BYTES
70+
#define CAM_SOI_PROBE_BYTES 32
71+
#endif
72+
73+
/* Number of bytes copied to SRAM for EOI validation when capturing
74+
* directly to PSRAM. Tunable to probe more of the frame tail if needed. */
75+
#ifndef CAM_EOI_PROBE_BYTES
76+
#define CAM_EOI_PROBE_BYTES 32
77+
#endif
78+
79+
/*
80+
* PSRAM DMA may bypass the CPU cache. Always call esp_cache_msync() on the
81+
* SOI probe region so cached reads see the data written by DMA.
82+
*/
83+
84+
static inline size_t dcache_line_size(void)
85+
{
86+
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0)
87+
/* cache_hal_get_cache_line_size() added extra argument from IDF 5.2 */
88+
return cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA);
89+
#else
90+
/* Older releases only expose the ROM helper, all current targets
91+
* have a 32‑byte DCache line */
92+
return 32;
93+
#endif
94+
}
95+
96+
/*
97+
* Invalidate CPU data cache lines that cover a region in PSRAM which
98+
* has just been written by DMA. This guarantees subsequent CPU reads
99+
* fetch the fresh data from PSRAM rather than stale cache contents.
100+
* Both address and length are aligned to the data cache line size.
101+
*/
102+
static inline void cam_drop_psram_cache(void *addr, size_t len)
103+
{
104+
size_t line = dcache_line_size();
105+
if (line == 0) {
106+
line = 32; /* sane fallback */
107+
}
108+
uintptr_t start = (uintptr_t)addr & ~(line - 1);
109+
size_t sync_len = (len + ((uintptr_t)addr - start) + line - 1) & ~(line - 1);
110+
esp_cache_msync((void *)start, sync_len,
111+
ESP_CACHE_MSYNC_FLAG_DIR_M2C | ESP_CACHE_MSYNC_FLAG_INVALIDATE);
112+
}
113+
59114
/* Throttle repeated warnings printed from tight loops / ISRs.
60115
*
61116
* counter – static DRAM/IRAM uint16_t you pass in
@@ -208,21 +263,67 @@ static void cam_task(void *arg)
208263
&frame_buffer_event->buf[frame_buffer_event->len],
209264
&cam_obj->dma_buffer[(cnt % cam_obj->dma_half_buffer_cnt) * cam_obj->dma_half_buffer_size],
210265
cam_obj->dma_half_buffer_size);
266+
} else {
267+
// stop if the next DMA copy would exceed the framebuffer slot
268+
// size, since we're called only after the copy occurs
269+
// This effectively reduces maximum usable frame buffer size
270+
// by one DMA operation, as we can't predict here, if the next
271+
// cam event will be a VSYNC
272+
if (cnt + 1 >= cam_obj->frame_copy_cnt) {
273+
ESP_CAMERA_ETS_PRINTF(DRAM_STR("cam_hal: DMA overflow\r\n"));
274+
ll_cam_stop(cam_obj);
275+
cam_obj->state = CAM_STATE_IDLE;
276+
continue;
277+
}
211278
}
279+
212280
//Check for JPEG SOI in the first buffer. stop if not found
213-
if (cam_obj->jpeg_mode && cnt == 0 && cam_verify_jpeg_soi(frame_buffer_event->buf, frame_buffer_event->len) != 0) {
214-
ll_cam_stop(cam_obj);
215-
cam_obj->state = CAM_STATE_IDLE;
281+
if (cam_obj->jpeg_mode && cnt == 0) {
282+
if (cam_obj->psram_mode) {
283+
/* dma_half_buffer_size already in BYTES (see ll_cam_memcpy()) */
284+
size_t probe_len = cam_obj->dma_half_buffer_size;
285+
/* clamp to avoid copying past the end of soi_probe */
286+
if (probe_len > CAM_SOI_PROBE_BYTES) {
287+
probe_len = CAM_SOI_PROBE_BYTES;
288+
}
289+
/* Invalidate cache lines for the DMA buffer before probing */
290+
cam_drop_psram_cache(frame_buffer_event->buf, probe_len);
291+
292+
uint8_t soi_probe[CAM_SOI_PROBE_BYTES];
293+
memcpy(soi_probe, frame_buffer_event->buf, probe_len);
294+
int soi_off = cam_verify_jpeg_soi(soi_probe, probe_len);
295+
if (soi_off != 0) {
296+
static uint16_t warn_psram_soi_cnt = 0;
297+
if (soi_off > 0) {
298+
CAM_WARN_THROTTLE(warn_psram_soi_cnt,
299+
"NO-SOI - JPEG start marker not at pos 0 (PSRAM)");
300+
} else {
301+
CAM_WARN_THROTTLE(warn_psram_soi_cnt,
302+
"NO-SOI - JPEG start marker missing (PSRAM)");
303+
}
304+
ll_cam_stop(cam_obj);
305+
cam_obj->state = CAM_STATE_IDLE;
306+
continue;
307+
}
308+
} else {
309+
int soi_off = cam_verify_jpeg_soi(frame_buffer_event->buf, frame_buffer_event->len);
310+
if (soi_off != 0) {
311+
static uint16_t warn_soi_bad_cnt = 0;
312+
if (soi_off > 0) {
313+
CAM_WARN_THROTTLE(warn_soi_bad_cnt,
314+
"NO-SOI - JPEG start marker not at pos 0");
315+
} else {
316+
CAM_WARN_THROTTLE(warn_soi_bad_cnt,
317+
"NO-SOI - JPEG start marker missing");
318+
}
319+
ll_cam_stop(cam_obj);
320+
cam_obj->state = CAM_STATE_IDLE;
321+
continue;
322+
}
323+
}
216324
}
325+
217326
cnt++;
218-
// stop when too many DMA copies occur so the PSRAM
219-
// framebuffer slot doesn't overflow from runaway transfers
220-
if (cnt >= cam_obj->frame_copy_cnt) {
221-
ESP_LOGE(TAG, "DMA overflow");
222-
ll_cam_stop(cam_obj);
223-
cam_obj->state = CAM_STATE_IDLE;
224-
continue;
225-
}
226327

227328
} else if (cam_event == CAM_VSYNC_EVENT) {
228329
//DBG_PIN_SET(1);
@@ -320,6 +421,9 @@ static esp_err_t cam_dma_config(const camera_config_t *config)
320421

321422
cam_obj->dma_node_cnt = (cam_obj->dma_buffer_size) / cam_obj->dma_node_buffer_size; // Number of DMA nodes
322423
cam_obj->frame_copy_cnt = cam_obj->recv_size / cam_obj->dma_half_buffer_size; // Number of interrupted copies, ping-pong copy
424+
if (cam_obj->psram_mode) {
425+
cam_obj->frame_copy_cnt++;
426+
}
323427

324428
ESP_LOGI(TAG, "buffer_size: %d, half_buffer_size: %d, node_buffer_size: %d, node_cnt: %d, total_cnt: %d",
325429
(int) cam_obj->dma_buffer_size, (int) cam_obj->dma_half_buffer_size, (int) cam_obj->dma_node_buffer_size,
@@ -338,6 +442,7 @@ static esp_err_t cam_dma_config(const camera_config_t *config)
338442
if (cam_obj->fb_size < cam_obj->recv_size) {
339443
fb_size = cam_obj->recv_size;
340444
}
445+
fb_size += cam_obj->dma_half_buffer_size;
341446
}
342447

343448
/* Allocate memory for frame buffer */
@@ -589,12 +694,38 @@ camera_fb_t *cam_take(TickType_t timeout)
589694

590695
if (cam_obj->jpeg_mode) {
591696
/* find the end marker for JPEG. Data after that can be discarded */
592-
int offset_e = cam_verify_jpeg_eoi(dma_buffer->buf, dma_buffer->len);
697+
int offset_e = -1;
698+
if (cam_obj->psram_mode) {
699+
size_t probe_len = dma_buffer->len;
700+
if (probe_len > CAM_EOI_PROBE_BYTES) {
701+
probe_len = CAM_EOI_PROBE_BYTES;
702+
}
703+
if (probe_len == 0) {
704+
goto skip_eoi_check;
705+
}
706+
cam_drop_psram_cache(dma_buffer->buf + dma_buffer->len - probe_len, probe_len);
707+
708+
uint8_t eoi_probe[CAM_EOI_PROBE_BYTES];
709+
memcpy(eoi_probe, dma_buffer->buf + dma_buffer->len - probe_len, probe_len);
710+
int off = cam_verify_jpeg_eoi(eoi_probe, probe_len);
711+
if (off >= 0) {
712+
offset_e = dma_buffer->len - probe_len + off;
713+
}
714+
} else {
715+
offset_e = cam_verify_jpeg_eoi(dma_buffer->buf, dma_buffer->len);
716+
}
717+
593718
if (offset_e >= 0) {
594719
dma_buffer->len = offset_e + sizeof(JPEG_EOI_MARKER);
720+
if (cam_obj->psram_mode) {
721+
/* DMA may bypass cache, ensure full frame is visible */
722+
cam_drop_psram_cache(dma_buffer->buf, dma_buffer->len);
723+
}
595724
return dma_buffer;
596725
}
597726

727+
skip_eoi_check:
728+
598729
CAM_WARN_THROTTLE(warn_eoi_miss_cnt,
599730
"NO-EOI - JPEG end marker missing");
600731
cam_give(dma_buffer);
@@ -605,6 +736,11 @@ camera_fb_t *cam_take(TickType_t timeout)
605736
dma_buffer->len = ll_cam_memcpy(cam_obj, dma_buffer->buf, dma_buffer->buf, dma_buffer->len);
606737
}
607738

739+
if (cam_obj->psram_mode) {
740+
/* DMA may bypass cache, ensure full frame is visible to the app */
741+
cam_drop_psram_cache(dma_buffer->buf, dma_buffer->len);
742+
}
743+
608744
return dma_buffer;
609745
}
610746
}

target/esp32/ll_cam.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ static inline int gpio_ll_get_level(gpio_dev_t *hw, int gpio_num)
3939
#endif
4040

4141
#if (ESP_IDF_VERSION_MAJOR >= 5)
42+
#include "driver/gpio.h"
4243
#define GPIO_PIN_INTR_POSEDGE GPIO_INTR_POSEDGE
4344
#define GPIO_PIN_INTR_NEGEDGE GPIO_INTR_NEGEDGE
4445
#define gpio_matrix_in(a,b,c) esp_rom_gpio_connect_in_signal(a,b,c)

target/esp32s2/ll_cam.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#endif
2727

2828
#if (ESP_IDF_VERSION_MAJOR >= 5)
29+
#include "driver/gpio.h"
2930
#define GPIO_PIN_INTR_POSEDGE GPIO_INTR_POSEDGE
3031
#define GPIO_PIN_INTR_NEGEDGE GPIO_INTR_NEGEDGE
3132
#define gpio_matrix_in(a,b,c) esp_rom_gpio_connect_in_signal(a,b,c)

target/esp32s3/ll_cam.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "esp_rom_gpio.h"
2828

2929
#if (ESP_IDF_VERSION_MAJOR >= 5)
30+
#include "driver/gpio.h"
3031
#include "soc/gpio_sig_map.h"
3132
#include "soc/gpio_periph.h"
3233
#include "soc/io_mux_reg.h"

target/xclk.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,10 @@ esp_err_t camera_enable_out_clock(const camera_config_t* config)
5151
ch_conf.gpio_num = config->pin_xclk;
5252
ch_conf.speed_mode = LEDC_LOW_SPEED_MODE;
5353
ch_conf.channel = config->ledc_channel;
54-
ch_conf.intr_type = LEDC_INTR_DISABLE;
54+
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(6,0,0)
55+
// no need to explicitly configure interrupt, handled in the driver (IDF v6.0 and above)
56+
ch_conf.intr_type = LEDC_INTR_DISABLE;
57+
#endif
5558
ch_conf.timer_sel = config->ledc_timer;
5659
ch_conf.duty = 1;
5760
ch_conf.hpoint = 0;

0 commit comments

Comments
 (0)