Init:
void app_camera_main () //app_camera.c
1. setup GPIO 13, 14 s pullup inut
2. setup CONFIG_LED_LEDC_PIN output, 1000Hz by calling ledc_timer_config
3. configure Camera port by calling esp_camera_init(), set xclk=20MHz, fb=2, jpeg
esp_camera_init() //components/esp32-camera/driver/camera.c
camera_probe() //find slave address
camera_enable_out_clock(); //enable output clock
SCCB_Init() //I2C or TWI driver
Set Power down line. pull 10ms high, 10ms low.
Set Reset line, 10ms low, 10ms high
if OV2640 software reset: SCCB_Write(0x30, FF 01, 12 80)
SCCB_Probe() //slave_addr=00~0x7F 查slave地址, 写地址直到回复OK. 每次等1 sec!!若用twi,每次等10ms.
read product ID, REG_PID.REG_VER, REG16_CHIDH, REG16_CHIDL, 决定camera_model
if (pid) call sensor's init function ovxxxx_init()
camera_model = CAMERA_OV7725 not support JPEG
= CAMERA_OV2640 OK
= CAMERA_OV3660 OK
camera_init()
{
s_state->width
s_state->height
fb_size = w*h*(pix_format)
s_state->sampling_mode = SM_0A00_0B00;
s_state->dma_filter = &dma_filter_xxx_highspeed;
if JPEG, "JPEG format is only supported for ov2640 and ov3660"
use dma_filter_jpeg()
i2s_init() //i2s_isr 收I2S data
dma_desc_init()
camera_fb_init(fb_count)
s_state->fb -> first fb_header->buf : frame buffer; fb->next = next rame buffer.
s_state->data_ready = xQueueCreate(16)
s_state->fb_in = xQueueCreate(s_state->config.fb_count, sizeof(camera_fb_t *));
s_state->fb_out = xQueueCreate(1, sizeof(camera_fb_t *));
xTaskCreate(&dma_filter_task, "dma_filter", 4096, NULL, 10, &s_state->dma_filter_task))
gpio_isr_handler_add(s_state->config.pin_vsync, &vsync_isr, NULL);
}
4. configure sensor , calling esp_camera_sensor_get(), set frame size 320x240
How to get camera buffer
camera_fb_t* esp_camera_fb_get()
if (fb_count == 1) use xSemaphoreTake(s_state->frame_ready, 4000ms) 等4秒或semaphor release.
if (fb_count > 1)xQueueReceive(s_state->fb_out, 4000ms) 等fb_out 送来fb
How to return camera frame/
void esp_camera_fb_return(camera_fb_t * fb)
xQueueSend(s_state->fb_in, &fb, portMAX_DELAY);
How frame is captured?
dma_filter_task() //camera.c
{
if(xQueueReceive(s_state->data_ready, &buf_idx, portMAX_DELAY) == pdTRUE) {
if (buf_idx == SIZE_MAX) {
//this is the end of the frame
dma_finish_frame();
–> camera_fb_done()
if fb_count == 1 xSemaphoreGive(s_state->frame_ready);
else {
if(xQueueIsQueueFullFromISR(s_state->fb_out) {检查fb_out queue是否没来拿
拿走清掉旧的 把新的fb 放入fb_out
} else {
已经拿走了 把新的fb 放入 fb_out
}
}
while fb_in 有fb, 把 fb_in 的fb 拿 出来
s_state->fb = next fb
} else {
dma_filter_buffer(buf_idx);//第一次设置fb header. copy s_state->dma_buf[buf_idx] to fb->buf
}
}
static void IRAM_ATTR vsync_isr(void* arg)
{ //完成一个frame
signal_dma_buf_received(&need_yield) //开始或结速 准备 DMA descriptor
}
static void IRAM_ATTR i2s_isr(void* arg)
{ //完成一次DMA
signal_dma_buf_received(&need_yield);
}
signal_dma_buf_received(&need_yield) {
xQueueSendFromISR(s_state->data_ready,&dma_desc_filled) 一个DMA 完成, 把目前DMA descriptor 号传给 queue
}