|
24 | 24 | #include "rom/ets_sys.h"
|
25 | 25 | #else
|
26 | 26 | #include "esp_timer.h"
|
| 27 | +#include "esp_cache.h" |
| 28 | +#include "hal/cache_hal.h" |
| 29 | +#include "hal/cache_ll.h" |
| 30 | +#include "esp_idf_version.h" |
| 31 | +#ifndef ESP_CACHE_MSYNC_FLAG_DIR_M2C |
| 32 | +#define ESP_CACHE_MSYNC_FLAG_DIR_M2C 0 |
| 33 | +#endif |
27 | 34 | #if CONFIG_IDF_TARGET_ESP32
|
28 | 35 | #include "esp32/rom/ets_sys.h" // will be removed in idf v5.0
|
29 | 36 | #elif CONFIG_IDF_TARGET_ESP32S2
|
@@ -56,6 +63,29 @@ static portMUX_TYPE g_psram_dma_lock = portMUX_INITIALIZER_UNLOCKED;
|
56 | 63 | #define CAM_LOG_SPAM_EVERY_FRAME 0 /* set to 1 to restore old behaviour */
|
57 | 64 | #endif
|
58 | 65 |
|
| 66 | +/* Number of bytes copied to SRAM for SOI validation when capturing |
| 67 | + * directly to PSRAM. Tunable to probe more of the frame start if needed. */ |
| 68 | +#ifndef CAM_SOI_PROBE_BYTES |
| 69 | +#define CAM_SOI_PROBE_BYTES 32 |
| 70 | +#endif |
| 71 | + |
| 72 | +/* |
| 73 | + * PSRAM DMA may bypass the CPU cache. Always call esp_cache_msync() on the |
| 74 | + * SOI probe region so cached reads see the data written by DMA. |
| 75 | + */ |
| 76 | + |
| 77 | +static inline size_t dcache_line_size(void) |
| 78 | +{ |
| 79 | +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0) |
| 80 | + /* cache_hal_get_cache_line_size() added extra argument from IDF 5.2 */ |
| 81 | + return cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA); |
| 82 | +#else |
| 83 | + /* Older releases only expose the ROM helper, all current targets |
| 84 | + * have a 32‑byte DCache line */ |
| 85 | + return 32; |
| 86 | +#endif |
| 87 | +} |
| 88 | + |
59 | 89 | /* Throttle repeated warnings printed from tight loops / ISRs.
|
60 | 90 | *
|
61 | 91 | * counter – static DRAM/IRAM uint16_t you pass in
|
@@ -209,11 +239,61 @@ static void cam_task(void *arg)
|
209 | 239 | &cam_obj->dma_buffer[(cnt % cam_obj->dma_half_buffer_cnt) * cam_obj->dma_half_buffer_size],
|
210 | 240 | cam_obj->dma_half_buffer_size);
|
211 | 241 | }
|
| 242 | + |
212 | 243 | //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; |
| 244 | + if (cam_obj->jpeg_mode && cnt == 0) { |
| 245 | + if (cam_obj->psram_mode) { |
| 246 | + /* dma_half_buffer_size already in BYTES (see ll_cam_memcpy()) */ |
| 247 | + size_t probe_len = cam_obj->dma_half_buffer_size; |
| 248 | + /* clamp to avoid copying past the end of soi_probe */ |
| 249 | + if (probe_len > CAM_SOI_PROBE_BYTES) { |
| 250 | + probe_len = CAM_SOI_PROBE_BYTES; |
| 251 | + } |
| 252 | + /* Invalidate cache lines for the DMA buffer before probing */ |
| 253 | + size_t line = dcache_line_size(); |
| 254 | + if (line == 0) { |
| 255 | + line = 32; /* sane fallback */ |
| 256 | + } |
| 257 | + uintptr_t addr = (uintptr_t)frame_buffer_event->buf; |
| 258 | + uintptr_t start = addr & ~(line - 1); |
| 259 | + size_t sync_len = (probe_len + (addr - start) + line - 1) & ~(line - 1); |
| 260 | + esp_cache_msync((void *)start, sync_len, |
| 261 | + ESP_CACHE_MSYNC_FLAG_DIR_M2C | ESP_CACHE_MSYNC_FLAG_INVALIDATE); |
| 262 | + |
| 263 | + uint8_t soi_probe[CAM_SOI_PROBE_BYTES]; |
| 264 | + memcpy(soi_probe, frame_buffer_event->buf, probe_len); |
| 265 | + int soi_off = cam_verify_jpeg_soi(soi_probe, probe_len); |
| 266 | + if (soi_off != 0) { |
| 267 | + static uint16_t warn_psram_soi_cnt = 0; |
| 268 | + if (soi_off > 0) { |
| 269 | + CAM_WARN_THROTTLE(warn_psram_soi_cnt, |
| 270 | + "NO-SOI - JPEG start marker not at pos 0 (PSRAM)"); |
| 271 | + } else { |
| 272 | + CAM_WARN_THROTTLE(warn_psram_soi_cnt, |
| 273 | + "NO-SOI - JPEG start marker missing (PSRAM)"); |
| 274 | + } |
| 275 | + ll_cam_stop(cam_obj); |
| 276 | + cam_obj->state = CAM_STATE_IDLE; |
| 277 | + continue; |
| 278 | + } |
| 279 | + } else { |
| 280 | + int soi_off = cam_verify_jpeg_soi(frame_buffer_event->buf, frame_buffer_event->len); |
| 281 | + if (soi_off != 0) { |
| 282 | + static uint16_t warn_soi_bad_cnt = 0; |
| 283 | + if (soi_off > 0) { |
| 284 | + CAM_WARN_THROTTLE(warn_soi_bad_cnt, |
| 285 | + "NO-SOI - JPEG start marker not at pos 0"); |
| 286 | + } else { |
| 287 | + CAM_WARN_THROTTLE(warn_soi_bad_cnt, |
| 288 | + "NO-SOI - JPEG start marker missing"); |
| 289 | + } |
| 290 | + ll_cam_stop(cam_obj); |
| 291 | + cam_obj->state = CAM_STATE_IDLE; |
| 292 | + continue; |
| 293 | + } |
| 294 | + } |
216 | 295 | }
|
| 296 | + |
217 | 297 | cnt++;
|
218 | 298 | // stop when too many DMA copies occur so the PSRAM
|
219 | 299 | // framebuffer slot doesn't overflow from runaway transfers
|
|
0 commit comments