From ae162f20468be04cf28ba49718ee9644f6f5c547 Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Mon, 22 Jan 2024 18:58:23 +0200 Subject: [PATCH 1/2] Create workaround for DMA failing on ESP32-S3 when WiFi is started Relates to https://github.com/espressif/esp32-camera/issues/620 --- driver/cam_hal.c | 9 +++++ target/esp32s3/ll_cam.c | 70 +++++++++++++++++++++++---------- target/private_include/ll_cam.h | 4 ++ 3 files changed, 62 insertions(+), 21 deletions(-) diff --git a/driver/cam_hal.c b/driver/cam_hal.c index a6e2e1df4e..2b7917176b 100644 --- a/driver/cam_hal.c +++ b/driver/cam_hal.c @@ -474,6 +474,12 @@ camera_fb_t *cam_take(TickType_t timeout) camera_fb_t *dma_buffer = NULL; TickType_t start = xTaskGetTickCount(); xQueueReceive(cam_obj->frame_buffer_queue, (void *)&dma_buffer, timeout); +#if CONFIG_IDF_TARGET_ESP32S3 + if (!dma_buffer) { + ll_cam_dma_reset(cam_obj); + xQueueReceive(cam_obj->frame_buffer_queue, (void *)&dma_buffer, timeout); + } +#endif if (dma_buffer) { if(cam_obj->jpeg_mode){ // find the end marker for JPEG. Data after that can be discarded @@ -498,6 +504,9 @@ camera_fb_t *cam_take(TickType_t timeout) return dma_buffer; } else { ESP_LOGW(TAG, "Failed to get the frame on time!"); +// #if CONFIG_IDF_TARGET_ESP32S3 +// ll_cam_dma_print_state(cam_obj); +// #endif } return NULL; } diff --git a/target/esp32s3/ll_cam.c b/target/esp32s3/ll_cam.c index 515bfe1546..193c1821bc 100644 --- a/target/esp32s3/ll_cam.c +++ b/target/esp32s3/ll_cam.c @@ -39,6 +39,54 @@ static const char *TAG = "s3 ll_cam"; +void ll_cam_dma_print_state(cam_obj_t *cam) +{ + esp_rom_printf("dma_infifo_status[%u] :\n", cam->dma_num); + esp_rom_printf(" infifo_full_l1 : %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.infifo_full_l1); + esp_rom_printf(" infifo_empty_l1 : %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.infifo_empty_l1); + esp_rom_printf(" infifo_full_l2 : %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.infifo_full_l2); + esp_rom_printf(" infifo_empty_l2 : %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.infifo_empty_l2); + esp_rom_printf(" infifo_full_l3 : %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.infifo_full_l3); + esp_rom_printf(" infifo_empty_l3 : %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.infifo_empty_l3); + esp_rom_printf(" infifo_cnt_l1 : %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.infifo_cnt_l1); + esp_rom_printf(" infifo_cnt_l2 : %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.infifo_cnt_l2); + esp_rom_printf(" infifo_cnt_l3 : %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.infifo_cnt_l3); + esp_rom_printf(" in_remain_under_1b_l3: %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.in_remain_under_1b_l3); + esp_rom_printf(" in_remain_under_2b_l3: %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.in_remain_under_2b_l3); + esp_rom_printf(" in_remain_under_3b_l3: %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.in_remain_under_3b_l3); + esp_rom_printf(" in_remain_under_4b_l3: %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.in_remain_under_4b_l3); + esp_rom_printf(" in_buf_hungry : %lu\n", GDMA.channel[cam->dma_num].in.infifo_status.in_buf_hungry); + esp_rom_printf("dma_state[%u] :\n", cam->dma_num); + esp_rom_printf(" dscr_addr : 0x%lx\n", GDMA.channel[cam->dma_num].in.state.dscr_addr); + esp_rom_printf(" in_dscr_state : %lu\n", GDMA.channel[cam->dma_num].in.state.in_dscr_state); + esp_rom_printf(" in_state : %lu\n", GDMA.channel[cam->dma_num].in.state.in_state); +} + +void ll_cam_dma_reset(cam_obj_t *cam) +{ + + GDMA.channel[cam->dma_num].in.int_clr.val = ~0; + GDMA.channel[cam->dma_num].in.int_ena.val = 0; + + GDMA.channel[cam->dma_num].in.conf0.val = 0; + GDMA.channel[cam->dma_num].in.conf0.in_rst = 1; + GDMA.channel[cam->dma_num].in.conf0.in_rst = 0; + + //internal SRAM only + if (!cam->psram_mode) { + GDMA.channel[cam->dma_num].in.conf0.indscr_burst_en = 1; + GDMA.channel[cam->dma_num].in.conf0.in_data_burst_en = 1; + } + + GDMA.channel[cam->dma_num].in.conf1.in_check_owner = 0; + // GDMA.channel[cam->dma_num].in.conf1.in_ext_mem_bk_size = 2; + + GDMA.channel[cam->dma_num].in.peri_sel.sel = 5; + //GDMA.channel[cam->dma_num].in.pri.rx_pri = 1;//rx prio 0-15 + //GDMA.channel[cam->dma_num].in.sram_size.in_size = 6;//This register is used to configure the size of L2 Tx FIFO for Rx channel. 0:16 bytes, 1:24 bytes, 2:32 bytes, 3: 40 bytes, 4: 48 bytes, 5:56 bytes, 6: 64 bytes, 7: 72 bytes, 8: 80 bytes. + //GDMA.channel[cam->dma_num].in.wight.rx_weight = 7;//The weight of Rx channel 0-15 +} + static void IRAM_ATTR ll_cam_vsync_isr(void *arg) { //DBG_PIN_SET(1); @@ -164,27 +212,7 @@ static esp_err_t ll_cam_dma_init(cam_obj_t *cam) REG_SET_BIT(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_DMA_RST); REG_CLR_BIT(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_DMA_RST); } - - GDMA.channel[cam->dma_num].in.int_clr.val = ~0; - GDMA.channel[cam->dma_num].in.int_ena.val = 0; - - GDMA.channel[cam->dma_num].in.conf0.val = 0; - GDMA.channel[cam->dma_num].in.conf0.in_rst = 1; - GDMA.channel[cam->dma_num].in.conf0.in_rst = 0; - - //internal SRAM only - if (!cam->psram_mode) { - GDMA.channel[cam->dma_num].in.conf0.indscr_burst_en = 1; - GDMA.channel[cam->dma_num].in.conf0.in_data_burst_en = 1; - } - - GDMA.channel[cam->dma_num].in.conf1.in_check_owner = 0; - // GDMA.channel[cam->dma_num].in.conf1.in_ext_mem_bk_size = 2; - - GDMA.channel[cam->dma_num].in.peri_sel.sel = 5; - //GDMA.channel[cam->dma_num].in.pri.rx_pri = 1;//rx prio 0-15 - //GDMA.channel[cam->dma_num].in.sram_size.in_size = 6;//This register is used to configure the size of L2 Tx FIFO for Rx channel. 0:16 bytes, 1:24 bytes, 2:32 bytes, 3: 40 bytes, 4: 48 bytes, 5:56 bytes, 6: 64 bytes, 7: 72 bytes, 8: 80 bytes. - //GDMA.channel[cam->dma_num].in.wight.rx_weight = 7;//The weight of Rx channel 0-15 + ll_cam_dma_reset(cam); return ESP_OK; } diff --git a/target/private_include/ll_cam.h b/target/private_include/ll_cam.h index 34c8da36dd..da80a667ef 100644 --- a/target/private_include/ll_cam.h +++ b/target/private_include/ll_cam.h @@ -142,6 +142,10 @@ uint8_t ll_cam_get_dma_align(cam_obj_t *cam); bool ll_cam_dma_sizes(cam_obj_t *cam); size_t ll_cam_memcpy(cam_obj_t *cam, uint8_t *out, const uint8_t *in, size_t len); esp_err_t ll_cam_set_sample_mode(cam_obj_t *cam, pixformat_t pix_format, uint32_t xclk_freq_hz, uint16_t sensor_pid); +#if CONFIG_IDF_TARGET_ESP32S3 +void ll_cam_dma_print_state(cam_obj_t *cam); +void ll_cam_dma_reset(cam_obj_t *cam); +#endif // implemented in cam_hal void ll_cam_send_event(cam_obj_t *cam, cam_event_t cam_event, BaseType_t * HPTaskAwoken); From f6154cfa845a08426be6fa73832733f6d455b4d5 Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Mon, 22 Jan 2024 19:18:03 +0200 Subject: [PATCH 2/2] Add comment explaining the workaround --- driver/cam_hal.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/driver/cam_hal.c b/driver/cam_hal.c index 2b7917176b..356e2cd72c 100644 --- a/driver/cam_hal.c +++ b/driver/cam_hal.c @@ -475,6 +475,10 @@ camera_fb_t *cam_take(TickType_t timeout) TickType_t start = xTaskGetTickCount(); xQueueReceive(cam_obj->frame_buffer_queue, (void *)&dma_buffer, timeout); #if CONFIG_IDF_TARGET_ESP32S3 + // Currently (22.01.2024) there is a bug in ESP-IDF v5.2, that causes + // GDMA to fall into a strange state if it is running while WiFi STA is connecting. + // This code tries to reset GDMA if frame is not received, to try and help with + // this case. It is possible to have some side effects too, though none come to mind if (!dma_buffer) { ll_cam_dma_reset(cam_obj); xQueueReceive(cam_obj->frame_buffer_queue, (void *)&dma_buffer, timeout);