接前一篇文章:ESP32-S3接OV5640出现“Failed to get the frame on time”以及“NO-SOI”、“NO-EOI”问题的定位过程与最终解决(2)
上一回讲到跟进代码找到问题根源这条路并走不通,至少以目前对于OV5640和ESP32-S3的认知水平无法从源码中分析出根因。当前看来所有路都基本堵死了,到了“山重水复疑无路”的境地。那目前应该怎么办?这就是研发中的一个“窍门”了,直着走不行就绕着走。不是至少还有一个OV5640摄像头是好的嘛,就先用那个往下走,看一下正常拍照时候的流程,在这一过程中,也许就会对于流程有所熟悉,就有回过头来解决当前问题的可能性。
笔者用唯一能够初始化成功的那款OV5640摄像头,写了一个简单的拍照测试程序,代码片段如下:
void app_main(void)
{
……
ESP_LOGI(TAG, "camera init");
camera_init();
vTaskDelay(20 / portTICK_PERIOD_MS);
ESP_LOGI(TAG, "start to take picture");
camera_fb_t *frame_buf = esp_camera_fb_get();
if (!frame_buf)
{
ESP_LOGE(TAG, "camera capture failed");
return;
}
ESP_LOGE(TAG, "camera capture succeeded");
esp_camera_fb_return(frame_buf);
}
实测发现,虽然用这唯一的一款好的摄像头不会出现“Failed to get the frame on time”,但是通过肉眼就能观测到,调用esp_camera_fb_get()获取一帧图像时速度特别慢,3-4秒才能完成。这比前一版产品差了很多,前一版产品很快就会返回了。
这又是为什么呢?这就要跟进一下代码、一探究竟了。
esp_camera_fb_get函数在components/esp32-camera/driver/esp_camera.c中,代码如下:
camera_fb_t *esp_camera_fb_get(void)
{
if (s_state == NULL) {
return NULL;
}
camera_fb_t *fb = cam_take(FB_GET_TIMEOUT);
//set the frame properties
if (fb) {
fb->width = resolution[s_state->sensor.status.framesize].width;
fb->height = resolution[s_state->sensor.status.framesize].height;
fb->format = s_state->sensor.pixformat;
}
return fb;
}
esp_camera_get()的核心函数是cam_take函数,这个cam_take函数可是“老朋友”了,上一回分析问题时就遇到过。这里为了便于理解和回顾,再贴一下其代码,在components/esp32-camera/driver/cam_hal.c中,如下:
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 (dma_buffer) {
if(cam_obj->jpeg_mode){
// find the end marker for JPEG. Data after that can be discarded
int offset_e = cam_verify_jpeg_eoi(dma_buffer->buf, dma_buffer->len);
if (offset_e >= 0) {
// adjust buffer length
dma_buffer->len = offset_e + sizeof(JPEG_EOI_MARKER);
return dma_buffer;
} else {
ESP_LOGW(TAG, "NO-EOI");
cam_give(dma_buffer);
TickType_t ticks_spent = xTaskGetTickCount() - start;
if (ticks_spent >= timeout) {
return NULL; /* We are out of time */
}
return cam_take(timeout - ticks_spent);//recurse!!!!
}
} else if(cam_obj->psram_mode && cam_obj->in_bytes_per_pixel != cam_obj->fb_bytes_per_pixel){
//currently this is used only for YUV to GRAYSCALE
dma_buffer->len = ll_cam_memcpy(cam_obj, dma_buffer->buf, dma_buffer->buf, dma_buffer->len);
}
return dma_buffer;
} else {
ESP_LOGW(TAG, "Failed to get the frame on time!");
}
return NULL;
}
上一回没有对于cam_take函数做过多解析,本回向前推进一点,看一下正常情况中的流程。这其中有一个超时参数timeout,它对用的实参为FB_GET_TIMEOUT。FB_GET_TIMEOUT宏在components/esp32-camera/driver/esp_camera.c中定义,如下:
#define FB_GET_TIMEOUT (4000 / portTICK_PERIOD_MS)
也就是说,超时时间为4000毫秒即4秒。那么成功的情况下,确实是在4秒以内得到了图像。只是理论上应该很快的,因为即使是2560x1440分辨率的情况下,帧率也有15帧,不可能2~3秒才获取到。
这都是乐鑫库中的现成代码了,理论上不会出现这种问题。因此不能怀疑、至少目前不能怀疑是软件代码的问题。
到这里,我们在“山重水复”的情况下,往前走了一点,好像感觉更走到“山穷水尽”的地步了。但笔者反倒有了一丝看到了“柳岸花明”的感觉。笔者在这里把这个过程写得很细,希望读者也能跟随笔者一起体验这个过程,不光是过程,还有当时的心境。
接下来该如何从“山重水复疑无路”到“柳暗花明又一村”?请看下回。