init
This commit is contained in:
537
managed_components/espressif__esp32-camera/driver/cam_hal.c
Normal file
537
managed_components/espressif__esp32-camera/driver/cam_hal.c
Normal file
@@ -0,0 +1,537 @@
|
||||
// Copyright 2010-2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdalign.h>
|
||||
#include "esp_heap_caps.h"
|
||||
#include "ll_cam.h"
|
||||
#include "cam_hal.h"
|
||||
|
||||
#if (ESP_IDF_VERSION_MAJOR == 3) && (ESP_IDF_VERSION_MINOR == 3)
|
||||
#include "rom/ets_sys.h"
|
||||
#else
|
||||
#include "esp_timer.h"
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#include "esp32/rom/ets_sys.h" // will be removed in idf v5.0
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#include "esp32s2/rom/ets_sys.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#include "esp32s3/rom/ets_sys.h"
|
||||
#endif
|
||||
#endif // ESP_IDF_VERSION_MAJOR
|
||||
#define ESP_CAMERA_ETS_PRINTF ets_printf
|
||||
|
||||
#if CONFIG_CAMERA_TASK_STACK_SIZE
|
||||
#define CAM_TASK_STACK CONFIG_CAMERA_TASK_STACK_SIZE
|
||||
#else
|
||||
#define CAM_TASK_STACK (2*1024)
|
||||
#endif
|
||||
|
||||
static const char *TAG = "cam_hal";
|
||||
static cam_obj_t *cam_obj = NULL;
|
||||
|
||||
static const uint32_t JPEG_SOI_MARKER = 0xFFD8FF; // written in little-endian for esp32
|
||||
static const uint16_t JPEG_EOI_MARKER = 0xD9FF; // written in little-endian for esp32
|
||||
|
||||
static int cam_verify_jpeg_soi(const uint8_t *inbuf, uint32_t length)
|
||||
{
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
if (memcmp(&inbuf[i], &JPEG_SOI_MARKER, 3) == 0) {
|
||||
//ESP_LOGW(TAG, "SOI: %d", (int) i);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
ESP_LOGW(TAG, "NO-SOI");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int cam_verify_jpeg_eoi(const uint8_t *inbuf, uint32_t length)
|
||||
{
|
||||
int offset = -1;
|
||||
uint8_t *dptr = (uint8_t *)inbuf + length - 2;
|
||||
while (dptr > inbuf) {
|
||||
if (memcmp(dptr, &JPEG_EOI_MARKER, 2) == 0) {
|
||||
offset = dptr - inbuf;
|
||||
//ESP_LOGW(TAG, "EOI: %d", length - (offset + 2));
|
||||
return offset;
|
||||
}
|
||||
dptr--;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static bool cam_get_next_frame(int * frame_pos)
|
||||
{
|
||||
if(!cam_obj->frames[*frame_pos].en){
|
||||
for (int x = 0; x < cam_obj->frame_cnt; x++) {
|
||||
if (cam_obj->frames[x].en) {
|
||||
*frame_pos = x;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool cam_start_frame(int * frame_pos)
|
||||
{
|
||||
if (cam_get_next_frame(frame_pos)) {
|
||||
if(ll_cam_start(cam_obj, *frame_pos)){
|
||||
// Vsync the frame manually
|
||||
ll_cam_do_vsync(cam_obj);
|
||||
uint64_t us = (uint64_t)esp_timer_get_time();
|
||||
cam_obj->frames[*frame_pos].fb.timestamp.tv_sec = us / 1000000UL;
|
||||
cam_obj->frames[*frame_pos].fb.timestamp.tv_usec = us % 1000000UL;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void IRAM_ATTR ll_cam_send_event(cam_obj_t *cam, cam_event_t cam_event, BaseType_t * HPTaskAwoken)
|
||||
{
|
||||
if (xQueueSendFromISR(cam->event_queue, (void *)&cam_event, HPTaskAwoken) != pdTRUE) {
|
||||
ll_cam_stop(cam);
|
||||
cam->state = CAM_STATE_IDLE;
|
||||
ESP_CAMERA_ETS_PRINTF(DRAM_STR("cam_hal: EV-%s-OVF\r\n"), cam_event==CAM_IN_SUC_EOF_EVENT ? DRAM_STR("EOF") : DRAM_STR("VSYNC"));
|
||||
}
|
||||
}
|
||||
|
||||
//Copy fram from DMA dma_buffer to fram dma_buffer
|
||||
static void cam_task(void *arg)
|
||||
{
|
||||
int cnt = 0;
|
||||
int frame_pos = 0;
|
||||
cam_obj->state = CAM_STATE_IDLE;
|
||||
cam_event_t cam_event = 0;
|
||||
|
||||
xQueueReset(cam_obj->event_queue);
|
||||
|
||||
while (1) {
|
||||
xQueueReceive(cam_obj->event_queue, (void *)&cam_event, portMAX_DELAY);
|
||||
DBG_PIN_SET(1);
|
||||
switch (cam_obj->state) {
|
||||
|
||||
case CAM_STATE_IDLE: {
|
||||
if (cam_event == CAM_VSYNC_EVENT) {
|
||||
//DBG_PIN_SET(1);
|
||||
if(cam_start_frame(&frame_pos)){
|
||||
cam_obj->frames[frame_pos].fb.len = 0;
|
||||
cam_obj->state = CAM_STATE_READ_BUF;
|
||||
}
|
||||
cnt = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CAM_STATE_READ_BUF: {
|
||||
camera_fb_t * frame_buffer_event = &cam_obj->frames[frame_pos].fb;
|
||||
size_t pixels_per_dma = (cam_obj->dma_half_buffer_size * cam_obj->fb_bytes_per_pixel) / (cam_obj->dma_bytes_per_item * cam_obj->in_bytes_per_pixel);
|
||||
|
||||
if (cam_event == CAM_IN_SUC_EOF_EVENT) {
|
||||
if(!cam_obj->psram_mode){
|
||||
if (cam_obj->fb_size < (frame_buffer_event->len + pixels_per_dma)) {
|
||||
ESP_LOGW(TAG, "FB-OVF");
|
||||
ll_cam_stop(cam_obj);
|
||||
DBG_PIN_SET(0);
|
||||
continue;
|
||||
}
|
||||
frame_buffer_event->len += ll_cam_memcpy(cam_obj,
|
||||
&frame_buffer_event->buf[frame_buffer_event->len],
|
||||
&cam_obj->dma_buffer[(cnt % cam_obj->dma_half_buffer_cnt) * cam_obj->dma_half_buffer_size],
|
||||
cam_obj->dma_half_buffer_size);
|
||||
}
|
||||
//Check for JPEG SOI in the first buffer. stop if not found
|
||||
if (cam_obj->jpeg_mode && cnt == 0 && cam_verify_jpeg_soi(frame_buffer_event->buf, frame_buffer_event->len) != 0) {
|
||||
ll_cam_stop(cam_obj);
|
||||
cam_obj->state = CAM_STATE_IDLE;
|
||||
}
|
||||
cnt++;
|
||||
|
||||
} else if (cam_event == CAM_VSYNC_EVENT) {
|
||||
//DBG_PIN_SET(1);
|
||||
ll_cam_stop(cam_obj);
|
||||
|
||||
if (cnt || !cam_obj->jpeg_mode || cam_obj->psram_mode) {
|
||||
if (cam_obj->jpeg_mode) {
|
||||
if (!cam_obj->psram_mode) {
|
||||
if (cam_obj->fb_size < (frame_buffer_event->len + pixels_per_dma)) {
|
||||
ESP_LOGW(TAG, "FB-OVF");
|
||||
cnt--;
|
||||
} else {
|
||||
frame_buffer_event->len += ll_cam_memcpy(cam_obj,
|
||||
&frame_buffer_event->buf[frame_buffer_event->len],
|
||||
&cam_obj->dma_buffer[(cnt % cam_obj->dma_half_buffer_cnt) * cam_obj->dma_half_buffer_size],
|
||||
cam_obj->dma_half_buffer_size);
|
||||
}
|
||||
}
|
||||
cnt++;
|
||||
}
|
||||
|
||||
cam_obj->frames[frame_pos].en = 0;
|
||||
|
||||
if (cam_obj->psram_mode) {
|
||||
if (cam_obj->jpeg_mode) {
|
||||
frame_buffer_event->len = cnt * cam_obj->dma_half_buffer_size;
|
||||
} else {
|
||||
frame_buffer_event->len = cam_obj->recv_size;
|
||||
}
|
||||
} else if (!cam_obj->jpeg_mode) {
|
||||
if (frame_buffer_event->len != cam_obj->fb_size) {
|
||||
cam_obj->frames[frame_pos].en = 1;
|
||||
ESP_LOGE(TAG, "FB-SIZE: %u != %u", frame_buffer_event->len, (unsigned) cam_obj->fb_size);
|
||||
}
|
||||
}
|
||||
//send frame
|
||||
if(!cam_obj->frames[frame_pos].en && xQueueSend(cam_obj->frame_buffer_queue, (void *)&frame_buffer_event, 0) != pdTRUE) {
|
||||
//pop frame buffer from the queue
|
||||
camera_fb_t * fb2 = NULL;
|
||||
if(xQueueReceive(cam_obj->frame_buffer_queue, &fb2, 0) == pdTRUE) {
|
||||
//push the new frame to the end of the queue
|
||||
if (xQueueSend(cam_obj->frame_buffer_queue, (void *)&frame_buffer_event, 0) != pdTRUE) {
|
||||
cam_obj->frames[frame_pos].en = 1;
|
||||
ESP_LOGE(TAG, "FBQ-SND");
|
||||
}
|
||||
//free the popped buffer
|
||||
cam_give(fb2);
|
||||
} else {
|
||||
//queue is full and we could not pop a frame from it
|
||||
cam_obj->frames[frame_pos].en = 1;
|
||||
ESP_LOGE(TAG, "FBQ-RCV");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!cam_start_frame(&frame_pos)){
|
||||
cam_obj->state = CAM_STATE_IDLE;
|
||||
} else {
|
||||
cam_obj->frames[frame_pos].fb.len = 0;
|
||||
}
|
||||
cnt = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
DBG_PIN_SET(0);
|
||||
}
|
||||
}
|
||||
|
||||
static lldesc_t * allocate_dma_descriptors(uint32_t count, uint16_t size, uint8_t * buffer)
|
||||
{
|
||||
lldesc_t *dma = (lldesc_t *)heap_caps_malloc(count * sizeof(lldesc_t), MALLOC_CAP_DMA);
|
||||
if (dma == NULL) {
|
||||
return dma;
|
||||
}
|
||||
|
||||
for (int x = 0; x < count; x++) {
|
||||
dma[x].size = size;
|
||||
dma[x].length = 0;
|
||||
dma[x].sosf = 0;
|
||||
dma[x].eof = 0;
|
||||
dma[x].owner = 1;
|
||||
dma[x].buf = (buffer + size * x);
|
||||
dma[x].empty = (uint32_t)&dma[(x + 1) % count];
|
||||
}
|
||||
return dma;
|
||||
}
|
||||
|
||||
static esp_err_t cam_dma_config(const camera_config_t *config)
|
||||
{
|
||||
bool ret = ll_cam_dma_sizes(cam_obj);
|
||||
if (0 == ret) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
cam_obj->dma_node_cnt = (cam_obj->dma_buffer_size) / cam_obj->dma_node_buffer_size; // Number of DMA nodes
|
||||
cam_obj->frame_copy_cnt = cam_obj->recv_size / cam_obj->dma_half_buffer_size; // Number of interrupted copies, ping-pong copy
|
||||
|
||||
ESP_LOGI(TAG, "buffer_size: %d, half_buffer_size: %d, node_buffer_size: %d, node_cnt: %d, total_cnt: %d",
|
||||
(int) cam_obj->dma_buffer_size, (int) cam_obj->dma_half_buffer_size, (int) cam_obj->dma_node_buffer_size,
|
||||
(int) cam_obj->dma_node_cnt, (int) cam_obj->frame_copy_cnt);
|
||||
|
||||
cam_obj->dma_buffer = NULL;
|
||||
cam_obj->dma = NULL;
|
||||
|
||||
cam_obj->frames = (cam_frame_t *)heap_caps_aligned_calloc(alignof(cam_frame_t), 1, cam_obj->frame_cnt * sizeof(cam_frame_t), MALLOC_CAP_DEFAULT);
|
||||
CAM_CHECK(cam_obj->frames != NULL, "frames malloc failed", ESP_FAIL);
|
||||
|
||||
uint8_t dma_align = 0;
|
||||
size_t fb_size = cam_obj->fb_size;
|
||||
if (cam_obj->psram_mode) {
|
||||
dma_align = ll_cam_get_dma_align(cam_obj);
|
||||
if (cam_obj->fb_size < cam_obj->recv_size) {
|
||||
fb_size = cam_obj->recv_size;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate memory for frame buffer */
|
||||
size_t alloc_size = fb_size * sizeof(uint8_t) + dma_align;
|
||||
uint32_t _caps = MALLOC_CAP_8BIT;
|
||||
if (CAMERA_FB_IN_DRAM == config->fb_location) {
|
||||
_caps |= MALLOC_CAP_INTERNAL;
|
||||
} else {
|
||||
_caps |= MALLOC_CAP_SPIRAM;
|
||||
}
|
||||
for (int x = 0; x < cam_obj->frame_cnt; x++) {
|
||||
cam_obj->frames[x].dma = NULL;
|
||||
cam_obj->frames[x].fb_offset = 0;
|
||||
cam_obj->frames[x].en = 0;
|
||||
ESP_LOGI(TAG, "Allocating %d Byte frame buffer in %s", alloc_size, _caps & MALLOC_CAP_SPIRAM ? "PSRAM" : "OnBoard RAM");
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0)
|
||||
// In IDF v4.2 and earlier, memory returned by heap_caps_aligned_alloc must be freed using heap_caps_aligned_free.
|
||||
// And heap_caps_aligned_free is deprecated on v4.3.
|
||||
cam_obj->frames[x].fb.buf = (uint8_t *)heap_caps_aligned_alloc(16, alloc_size, _caps);
|
||||
#else
|
||||
cam_obj->frames[x].fb.buf = (uint8_t *)heap_caps_malloc(alloc_size, _caps);
|
||||
#endif
|
||||
CAM_CHECK(cam_obj->frames[x].fb.buf != NULL, "frame buffer malloc failed", ESP_FAIL);
|
||||
if (cam_obj->psram_mode) {
|
||||
//align PSRAM buffer. TODO: save the offset so proper address can be freed later
|
||||
cam_obj->frames[x].fb_offset = dma_align - ((uint32_t)cam_obj->frames[x].fb.buf & (dma_align - 1));
|
||||
cam_obj->frames[x].fb.buf += cam_obj->frames[x].fb_offset;
|
||||
ESP_LOGI(TAG, "Frame[%d]: Offset: %u, Addr: 0x%08X", x, cam_obj->frames[x].fb_offset, (unsigned) cam_obj->frames[x].fb.buf);
|
||||
cam_obj->frames[x].dma = allocate_dma_descriptors(cam_obj->dma_node_cnt, cam_obj->dma_node_buffer_size, cam_obj->frames[x].fb.buf);
|
||||
CAM_CHECK(cam_obj->frames[x].dma != NULL, "frame dma malloc failed", ESP_FAIL);
|
||||
}
|
||||
cam_obj->frames[x].en = 1;
|
||||
}
|
||||
|
||||
if (!cam_obj->psram_mode) {
|
||||
cam_obj->dma_buffer = (uint8_t *)heap_caps_malloc(cam_obj->dma_buffer_size * sizeof(uint8_t), MALLOC_CAP_DMA);
|
||||
if(NULL == cam_obj->dma_buffer) {
|
||||
ESP_LOGE(TAG,"%s(%d): DMA buffer %d Byte malloc failed, the current largest free block:%d Byte", __FUNCTION__, __LINE__,
|
||||
(int) cam_obj->dma_buffer_size, (int) heap_caps_get_largest_free_block(MALLOC_CAP_DMA));
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
cam_obj->dma = allocate_dma_descriptors(cam_obj->dma_node_cnt, cam_obj->dma_node_buffer_size, cam_obj->dma_buffer);
|
||||
CAM_CHECK(cam_obj->dma != NULL, "dma malloc failed", ESP_FAIL);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t cam_init(const camera_config_t *config)
|
||||
{
|
||||
CAM_CHECK(NULL != config, "config pointer is invalid", ESP_ERR_INVALID_ARG);
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
cam_obj = (cam_obj_t *)heap_caps_calloc(1, sizeof(cam_obj_t), MALLOC_CAP_DMA);
|
||||
CAM_CHECK(NULL != cam_obj, "lcd_cam object malloc error", ESP_ERR_NO_MEM);
|
||||
|
||||
cam_obj->swap_data = 0;
|
||||
cam_obj->vsync_pin = config->pin_vsync;
|
||||
cam_obj->vsync_invert = true;
|
||||
|
||||
ll_cam_set_pin(cam_obj, config);
|
||||
ret = ll_cam_config(cam_obj, config);
|
||||
CAM_CHECK_GOTO(ret == ESP_OK, "ll_cam initialize failed", err);
|
||||
|
||||
#if CAMERA_DBG_PIN_ENABLE
|
||||
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[DBG_PIN_NUM], PIN_FUNC_GPIO);
|
||||
gpio_set_direction(DBG_PIN_NUM, GPIO_MODE_OUTPUT);
|
||||
gpio_set_pull_mode(DBG_PIN_NUM, GPIO_FLOATING);
|
||||
#endif
|
||||
|
||||
ESP_LOGI(TAG, "cam init ok");
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
free(cam_obj);
|
||||
cam_obj = NULL;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t cam_config(const camera_config_t *config, framesize_t frame_size, uint16_t sensor_pid)
|
||||
{
|
||||
CAM_CHECK(NULL != config, "config pointer is invalid", ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
ret = ll_cam_set_sample_mode(cam_obj, (pixformat_t)config->pixel_format, config->xclk_freq_hz, sensor_pid);
|
||||
CAM_CHECK_GOTO(ret == ESP_OK, "ll_cam_set_sample_mode failed", err);
|
||||
|
||||
cam_obj->jpeg_mode = config->pixel_format == PIXFORMAT_JPEG;
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
cam_obj->psram_mode = false;
|
||||
#else
|
||||
cam_obj->psram_mode = (config->xclk_freq_hz == 16000000);
|
||||
#endif
|
||||
cam_obj->frame_cnt = config->fb_count;
|
||||
cam_obj->width = resolution[frame_size].width;
|
||||
cam_obj->height = resolution[frame_size].height;
|
||||
|
||||
if(cam_obj->jpeg_mode){
|
||||
#ifdef CONFIG_CAMERA_JPEG_MODE_FRAME_SIZE_AUTO
|
||||
cam_obj->recv_size = cam_obj->width * cam_obj->height / 5;
|
||||
#else
|
||||
cam_obj->recv_size = CONFIG_CAMERA_JPEG_MODE_FRAME_SIZE;
|
||||
#endif
|
||||
cam_obj->fb_size = cam_obj->recv_size;
|
||||
} else {
|
||||
cam_obj->recv_size = cam_obj->width * cam_obj->height * cam_obj->in_bytes_per_pixel;
|
||||
cam_obj->fb_size = cam_obj->width * cam_obj->height * cam_obj->fb_bytes_per_pixel;
|
||||
}
|
||||
|
||||
ret = cam_dma_config(config);
|
||||
CAM_CHECK_GOTO(ret == ESP_OK, "cam_dma_config failed", err);
|
||||
|
||||
size_t queue_size = cam_obj->dma_half_buffer_cnt - 1;
|
||||
if (queue_size == 0) {
|
||||
queue_size = 1;
|
||||
}
|
||||
cam_obj->event_queue = xQueueCreate(queue_size, sizeof(cam_event_t));
|
||||
CAM_CHECK_GOTO(cam_obj->event_queue != NULL, "event_queue create failed", err);
|
||||
|
||||
size_t frame_buffer_queue_len = cam_obj->frame_cnt;
|
||||
if (config->grab_mode == CAMERA_GRAB_LATEST && cam_obj->frame_cnt > 1) {
|
||||
frame_buffer_queue_len = cam_obj->frame_cnt - 1;
|
||||
}
|
||||
cam_obj->frame_buffer_queue = xQueueCreate(frame_buffer_queue_len, sizeof(camera_fb_t*));
|
||||
CAM_CHECK_GOTO(cam_obj->frame_buffer_queue != NULL, "frame_buffer_queue create failed", err);
|
||||
|
||||
ret = ll_cam_init_isr(cam_obj);
|
||||
CAM_CHECK_GOTO(ret == ESP_OK, "cam intr alloc failed", err);
|
||||
|
||||
|
||||
#if CONFIG_CAMERA_CORE0
|
||||
xTaskCreatePinnedToCore(cam_task, "cam_task", CAM_TASK_STACK, NULL, configMAX_PRIORITIES - 2, &cam_obj->task_handle, 0);
|
||||
#elif CONFIG_CAMERA_CORE1
|
||||
xTaskCreatePinnedToCore(cam_task, "cam_task", CAM_TASK_STACK, NULL, configMAX_PRIORITIES - 2, &cam_obj->task_handle, 1);
|
||||
#else
|
||||
xTaskCreate(cam_task, "cam_task", CAM_TASK_STACK, NULL, configMAX_PRIORITIES - 2, &cam_obj->task_handle);
|
||||
#endif
|
||||
|
||||
ESP_LOGI(TAG, "cam config ok");
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
cam_deinit();
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t cam_deinit(void)
|
||||
{
|
||||
if (!cam_obj) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
cam_stop();
|
||||
if (cam_obj->task_handle) {
|
||||
vTaskDelete(cam_obj->task_handle);
|
||||
}
|
||||
if (cam_obj->event_queue) {
|
||||
vQueueDelete(cam_obj->event_queue);
|
||||
}
|
||||
if (cam_obj->frame_buffer_queue) {
|
||||
vQueueDelete(cam_obj->frame_buffer_queue);
|
||||
}
|
||||
|
||||
ll_cam_deinit(cam_obj);
|
||||
|
||||
if (cam_obj->dma) {
|
||||
free(cam_obj->dma);
|
||||
}
|
||||
if (cam_obj->dma_buffer) {
|
||||
free(cam_obj->dma_buffer);
|
||||
}
|
||||
if (cam_obj->frames) {
|
||||
for (int x = 0; x < cam_obj->frame_cnt; x++) {
|
||||
free(cam_obj->frames[x].fb.buf - cam_obj->frames[x].fb_offset);
|
||||
if (cam_obj->frames[x].dma) {
|
||||
free(cam_obj->frames[x].dma);
|
||||
}
|
||||
}
|
||||
free(cam_obj->frames);
|
||||
}
|
||||
|
||||
free(cam_obj);
|
||||
cam_obj = NULL;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void cam_stop(void)
|
||||
{
|
||||
ll_cam_vsync_intr_enable(cam_obj, false);
|
||||
ll_cam_stop(cam_obj);
|
||||
}
|
||||
|
||||
void cam_start(void)
|
||||
{
|
||||
ll_cam_vsync_intr_enable(cam_obj, true);
|
||||
}
|
||||
|
||||
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
|
||||
// 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);
|
||||
}
|
||||
#endif
|
||||
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!");
|
||||
// #if CONFIG_IDF_TARGET_ESP32S3
|
||||
// ll_cam_dma_print_state(cam_obj);
|
||||
// #endif
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void cam_give(camera_fb_t *dma_buffer)
|
||||
{
|
||||
for (int x = 0; x < cam_obj->frame_cnt; x++) {
|
||||
if (&cam_obj->frames[x].fb == dma_buffer) {
|
||||
cam_obj->frames[x].en = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cam_give_all(void) {
|
||||
for (int x = 0; x < cam_obj->frame_cnt; x++) {
|
||||
cam_obj->frames[x].en = 1;
|
||||
}
|
||||
}
|
492
managed_components/espressif__esp32-camera/driver/esp_camera.c
Normal file
492
managed_components/espressif__esp32-camera/driver/esp_camera.c
Normal file
@@ -0,0 +1,492 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "time.h"
|
||||
#include "sys/time.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_system.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "nvs.h"
|
||||
#include "sensor.h"
|
||||
#include "sccb.h"
|
||||
#include "cam_hal.h"
|
||||
#include "esp_camera.h"
|
||||
#include "xclk.h"
|
||||
#if CONFIG_OV2640_SUPPORT
|
||||
#include "ov2640.h"
|
||||
#endif
|
||||
#if CONFIG_OV7725_SUPPORT
|
||||
#include "ov7725.h"
|
||||
#endif
|
||||
#if CONFIG_OV3660_SUPPORT
|
||||
#include "ov3660.h"
|
||||
#endif
|
||||
#if CONFIG_OV5640_SUPPORT
|
||||
#include "ov5640.h"
|
||||
#endif
|
||||
#if CONFIG_NT99141_SUPPORT
|
||||
#include "nt99141.h"
|
||||
#endif
|
||||
#if CONFIG_OV7670_SUPPORT
|
||||
#include "ov7670.h"
|
||||
#endif
|
||||
#if CONFIG_GC2145_SUPPORT
|
||||
#include "gc2145.h"
|
||||
#endif
|
||||
#if CONFIG_GC032A_SUPPORT
|
||||
#include "gc032a.h"
|
||||
#endif
|
||||
#if CONFIG_GC0308_SUPPORT
|
||||
#include "gc0308.h"
|
||||
#endif
|
||||
#if CONFIG_BF3005_SUPPORT
|
||||
#include "bf3005.h"
|
||||
#endif
|
||||
#if CONFIG_BF20A6_SUPPORT
|
||||
#include "bf20a6.h"
|
||||
#endif
|
||||
#if CONFIG_SC101IOT_SUPPORT
|
||||
#include "sc101iot.h"
|
||||
#endif
|
||||
#if CONFIG_SC030IOT_SUPPORT
|
||||
#include "sc030iot.h"
|
||||
#endif
|
||||
#if CONFIG_SC031GS_SUPPORT
|
||||
#include "sc031gs.h"
|
||||
#endif
|
||||
#if CONFIG_MEGA_CCM_SUPPORT
|
||||
#include "mega_ccm.h"
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#define TAG ""
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char *TAG = "camera";
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
sensor_t sensor;
|
||||
camera_fb_t fb;
|
||||
} camera_state_t;
|
||||
|
||||
static const char *CAMERA_SENSOR_NVS_KEY = "sensor";
|
||||
static const char *CAMERA_PIXFORMAT_NVS_KEY = "pixformat";
|
||||
static camera_state_t *s_state = NULL;
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32S3 // LCD_CAM module of ESP32-S3 will generate xclk
|
||||
#define CAMERA_ENABLE_OUT_CLOCK(v)
|
||||
#define CAMERA_DISABLE_OUT_CLOCK()
|
||||
#else
|
||||
#define CAMERA_ENABLE_OUT_CLOCK(v) camera_enable_out_clock((v))
|
||||
#define CAMERA_DISABLE_OUT_CLOCK() camera_disable_out_clock()
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
int (*detect)(int slv_addr, sensor_id_t *id);
|
||||
int (*init)(sensor_t *sensor);
|
||||
} sensor_func_t;
|
||||
|
||||
static const sensor_func_t g_sensors[] = {
|
||||
#if CONFIG_OV7725_SUPPORT
|
||||
{ov7725_detect, ov7725_init},
|
||||
#endif
|
||||
#if CONFIG_OV7670_SUPPORT
|
||||
{ov7670_detect, ov7670_init},
|
||||
#endif
|
||||
#if CONFIG_OV2640_SUPPORT
|
||||
{ov2640_detect, ov2640_init},
|
||||
#endif
|
||||
#if CONFIG_OV3660_SUPPORT
|
||||
{ov3660_detect, ov3660_init},
|
||||
#endif
|
||||
#if CONFIG_OV5640_SUPPORT
|
||||
{ov5640_detect, ov5640_init},
|
||||
#endif
|
||||
#if CONFIG_NT99141_SUPPORT
|
||||
{nt99141_detect, nt99141_init},
|
||||
#endif
|
||||
#if CONFIG_GC2145_SUPPORT
|
||||
{gc2145_detect, gc2145_init},
|
||||
#endif
|
||||
#if CONFIG_GC032A_SUPPORT
|
||||
{gc032a_detect, gc032a_init},
|
||||
#endif
|
||||
#if CONFIG_GC0308_SUPPORT
|
||||
{gc0308_detect, gc0308_init},
|
||||
#endif
|
||||
#if CONFIG_BF3005_SUPPORT
|
||||
{bf3005_detect, bf3005_init},
|
||||
#endif
|
||||
#if CONFIG_BF20A6_SUPPORT
|
||||
{bf20a6_detect, bf20a6_init},
|
||||
#endif
|
||||
#if CONFIG_SC101IOT_SUPPORT
|
||||
{sc101iot_detect, sc101iot_init},
|
||||
#endif
|
||||
#if CONFIG_SC030IOT_SUPPORT
|
||||
{sc030iot_detect, sc030iot_init},
|
||||
#endif
|
||||
#if CONFIG_SC031GS_SUPPORT
|
||||
{sc031gs_detect, sc031gs_init},
|
||||
#endif
|
||||
#if CONFIG_MEGA_CCM_SUPPORT
|
||||
{mega_ccm_detect, mega_ccm_init},
|
||||
#endif
|
||||
};
|
||||
|
||||
static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out_camera_model)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
*out_camera_model = CAMERA_NONE;
|
||||
if (s_state != NULL) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
s_state = (camera_state_t *) calloc(1, sizeof(camera_state_t));
|
||||
if (!s_state) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
if (config->pin_xclk >= 0) {
|
||||
ESP_LOGD(TAG, "Enabling XCLK output");
|
||||
CAMERA_ENABLE_OUT_CLOCK(config);
|
||||
}
|
||||
|
||||
if (config->pin_sccb_sda != -1) {
|
||||
ESP_LOGD(TAG, "Initializing SCCB");
|
||||
ret = SCCB_Init(config->pin_sccb_sda, config->pin_sccb_scl);
|
||||
} else {
|
||||
ESP_LOGD(TAG, "Using existing I2C port");
|
||||
ret = SCCB_Use_Port(config->sccb_i2c_port);
|
||||
}
|
||||
|
||||
if(ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "sccb init err");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (config->pin_pwdn >= 0) {
|
||||
ESP_LOGD(TAG, "Resetting camera by power down line");
|
||||
gpio_config_t conf = { 0 };
|
||||
conf.pin_bit_mask = 1LL << config->pin_pwdn;
|
||||
conf.mode = GPIO_MODE_OUTPUT;
|
||||
gpio_config(&conf);
|
||||
|
||||
// carefull, logic is inverted compared to reset pin
|
||||
gpio_set_level(config->pin_pwdn, 1);
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
gpio_set_level(config->pin_pwdn, 0);
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
if (config->pin_reset >= 0) {
|
||||
ESP_LOGD(TAG, "Resetting camera");
|
||||
gpio_config_t conf = { 0 };
|
||||
conf.pin_bit_mask = 1LL << config->pin_reset;
|
||||
conf.mode = GPIO_MODE_OUTPUT;
|
||||
gpio_config(&conf);
|
||||
|
||||
gpio_set_level(config->pin_reset, 0);
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
gpio_set_level(config->pin_reset, 1);
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Searching for camera address");
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
|
||||
uint8_t slv_addr = SCCB_Probe();
|
||||
|
||||
if (slv_addr == 0) {
|
||||
ret = ESP_ERR_NOT_FOUND;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Detected camera at address=0x%02x", slv_addr);
|
||||
s_state->sensor.slv_addr = slv_addr;
|
||||
s_state->sensor.xclk_freq_hz = config->xclk_freq_hz;
|
||||
|
||||
/**
|
||||
* Read sensor ID and then initialize sensor
|
||||
* Attention: Some sensors have the same SCCB address. Therefore, several attempts may be made in the detection process
|
||||
*/
|
||||
sensor_id_t *id = &s_state->sensor.id;
|
||||
for (size_t i = 0; i < sizeof(g_sensors) / sizeof(sensor_func_t); i++) {
|
||||
if (g_sensors[i].detect(slv_addr, id)) {
|
||||
camera_sensor_info_t *info = esp_camera_sensor_get_info(id);
|
||||
if (NULL != info) {
|
||||
*out_camera_model = info->model;
|
||||
ESP_LOGI(TAG, "Detected %s camera", info->name);
|
||||
g_sensors[i].init(&s_state->sensor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (CAMERA_NONE == *out_camera_model) { //If no supported sensors are detected
|
||||
ESP_LOGE(TAG, "Detected camera not supported.");
|
||||
ret = ESP_ERR_NOT_SUPPORTED;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Camera PID=0x%02x VER=0x%02x MIDL=0x%02x MIDH=0x%02x",
|
||||
id->PID, id->VER, id->MIDH, id->MIDL);
|
||||
|
||||
ESP_LOGD(TAG, "Doing SW reset of sensor");
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
|
||||
return s_state->sensor.reset(&s_state->sensor);
|
||||
err :
|
||||
CAMERA_DISABLE_OUT_CLOCK();
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if CONFIG_CAMERA_CONVERTER_ENABLED
|
||||
static pixformat_t get_output_data_format(camera_conv_mode_t conv_mode)
|
||||
{
|
||||
pixformat_t format = PIXFORMAT_RGB565;
|
||||
switch (conv_mode) {
|
||||
case YUV422_TO_YUV420:
|
||||
format = PIXFORMAT_YUV420;
|
||||
break;
|
||||
case YUV422_TO_RGB565: // default format is RGB565
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ESP_LOGD(TAG, "Convert to %d format enabled", format);
|
||||
return format;
|
||||
}
|
||||
#endif
|
||||
|
||||
esp_err_t esp_camera_init(const camera_config_t *config)
|
||||
{
|
||||
esp_err_t err;
|
||||
err = cam_init(config);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Camera init failed with error 0x%x", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
camera_model_t camera_model = CAMERA_NONE;
|
||||
err = camera_probe(config, &camera_model);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Camera probe failed with error 0x%x(%s)", err, esp_err_to_name(err));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
framesize_t frame_size = (framesize_t) config->frame_size;
|
||||
pixformat_t pix_format = (pixformat_t) config->pixel_format;
|
||||
|
||||
if (PIXFORMAT_JPEG == pix_format && (!camera_sensor[camera_model].support_jpeg)) {
|
||||
ESP_LOGE(TAG, "JPEG format is not supported on this sensor");
|
||||
err = ESP_ERR_NOT_SUPPORTED;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (frame_size > camera_sensor[camera_model].max_size) {
|
||||
ESP_LOGW(TAG, "The frame size exceeds the maximum for this sensor, it will be forced to the maximum possible value");
|
||||
frame_size = camera_sensor[camera_model].max_size;
|
||||
}
|
||||
|
||||
err = cam_config(config, frame_size, s_state->sensor.id.PID);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Camera config failed with error 0x%x", err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
s_state->sensor.status.framesize = frame_size;
|
||||
s_state->sensor.pixformat = pix_format;
|
||||
|
||||
ESP_LOGD(TAG, "Setting frame size to %dx%d", resolution[frame_size].width, resolution[frame_size].height);
|
||||
if (s_state->sensor.set_framesize(&s_state->sensor, frame_size) != 0) {
|
||||
ESP_LOGE(TAG, "Failed to set frame size");
|
||||
err = ESP_ERR_CAMERA_FAILED_TO_SET_FRAME_SIZE;
|
||||
goto fail;
|
||||
}
|
||||
s_state->sensor.set_pixformat(&s_state->sensor, pix_format);
|
||||
#if CONFIG_CAMERA_CONVERTER_ENABLED
|
||||
if(config->conv_mode) {
|
||||
s_state->sensor.pixformat = get_output_data_format(config->conv_mode); // If conversion enabled, change the out data format by conversion mode
|
||||
}
|
||||
#endif
|
||||
|
||||
if (s_state->sensor.id.PID == OV2640_PID) {
|
||||
s_state->sensor.set_gainceiling(&s_state->sensor, GAINCEILING_2X);
|
||||
s_state->sensor.set_bpc(&s_state->sensor, false);
|
||||
s_state->sensor.set_wpc(&s_state->sensor, true);
|
||||
s_state->sensor.set_lenc(&s_state->sensor, true);
|
||||
}
|
||||
|
||||
if (pix_format == PIXFORMAT_JPEG) {
|
||||
s_state->sensor.set_quality(&s_state->sensor, config->jpeg_quality);
|
||||
}
|
||||
s_state->sensor.init_status(&s_state->sensor);
|
||||
|
||||
cam_start();
|
||||
|
||||
return ESP_OK;
|
||||
|
||||
fail:
|
||||
esp_camera_deinit();
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_camera_deinit()
|
||||
{
|
||||
esp_err_t ret = cam_deinit();
|
||||
CAMERA_DISABLE_OUT_CLOCK();
|
||||
if (s_state) {
|
||||
SCCB_Deinit();
|
||||
|
||||
free(s_state);
|
||||
s_state = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define FB_GET_TIMEOUT (4000 / portTICK_PERIOD_MS)
|
||||
|
||||
camera_fb_t *esp_camera_fb_get()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
void esp_camera_fb_return(camera_fb_t *fb)
|
||||
{
|
||||
if (s_state == NULL) {
|
||||
return;
|
||||
}
|
||||
cam_give(fb);
|
||||
}
|
||||
|
||||
sensor_t *esp_camera_sensor_get()
|
||||
{
|
||||
if (s_state == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return &s_state->sensor;
|
||||
}
|
||||
|
||||
esp_err_t esp_camera_save_to_nvs(const char *key)
|
||||
{
|
||||
#if ESP_IDF_VERSION_MAJOR > 3
|
||||
nvs_handle_t handle;
|
||||
#else
|
||||
nvs_handle handle;
|
||||
#endif
|
||||
esp_err_t ret = nvs_open(key, NVS_READWRITE, &handle);
|
||||
|
||||
if (ret == ESP_OK) {
|
||||
sensor_t *s = esp_camera_sensor_get();
|
||||
if (s != NULL) {
|
||||
ret = nvs_set_blob(handle, CAMERA_SENSOR_NVS_KEY, &s->status, sizeof(camera_status_t));
|
||||
if (ret == ESP_OK) {
|
||||
uint8_t pf = s->pixformat;
|
||||
ret = nvs_set_u8(handle, CAMERA_PIXFORMAT_NVS_KEY, pf);
|
||||
}
|
||||
return ret;
|
||||
} else {
|
||||
return ESP_ERR_CAMERA_NOT_DETECTED;
|
||||
}
|
||||
nvs_close(handle);
|
||||
return ret;
|
||||
} else {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t esp_camera_load_from_nvs(const char *key)
|
||||
{
|
||||
#if ESP_IDF_VERSION_MAJOR > 3
|
||||
nvs_handle_t handle;
|
||||
#else
|
||||
nvs_handle handle;
|
||||
#endif
|
||||
uint8_t pf;
|
||||
|
||||
esp_err_t ret = nvs_open(key, NVS_READWRITE, &handle);
|
||||
|
||||
if (ret == ESP_OK) {
|
||||
sensor_t *s = esp_camera_sensor_get();
|
||||
camera_status_t st;
|
||||
if (s != NULL) {
|
||||
size_t size = sizeof(camera_status_t);
|
||||
ret = nvs_get_blob(handle, CAMERA_SENSOR_NVS_KEY, &st, &size);
|
||||
if (ret == ESP_OK) {
|
||||
s->set_ae_level(s, st.ae_level);
|
||||
s->set_aec2(s, st.aec2);
|
||||
s->set_aec_value(s, st.aec_value);
|
||||
s->set_agc_gain(s, st.agc_gain);
|
||||
s->set_awb_gain(s, st.awb_gain);
|
||||
s->set_bpc(s, st.bpc);
|
||||
s->set_brightness(s, st.brightness);
|
||||
s->set_colorbar(s, st.colorbar);
|
||||
s->set_contrast(s, st.contrast);
|
||||
s->set_dcw(s, st.dcw);
|
||||
s->set_denoise(s, st.denoise);
|
||||
s->set_exposure_ctrl(s, st.aec);
|
||||
s->set_framesize(s, st.framesize);
|
||||
s->set_gain_ctrl(s, st.agc);
|
||||
s->set_gainceiling(s, st.gainceiling);
|
||||
s->set_hmirror(s, st.hmirror);
|
||||
s->set_lenc(s, st.lenc);
|
||||
s->set_quality(s, st.quality);
|
||||
s->set_raw_gma(s, st.raw_gma);
|
||||
s->set_saturation(s, st.saturation);
|
||||
s->set_sharpness(s, st.sharpness);
|
||||
s->set_special_effect(s, st.special_effect);
|
||||
s->set_vflip(s, st.vflip);
|
||||
s->set_wb_mode(s, st.wb_mode);
|
||||
s->set_whitebal(s, st.awb);
|
||||
s->set_wpc(s, st.wpc);
|
||||
}
|
||||
ret = nvs_get_u8(handle, CAMERA_PIXFORMAT_NVS_KEY, &pf);
|
||||
if (ret == ESP_OK) {
|
||||
s->set_pixformat(s, pf);
|
||||
}
|
||||
} else {
|
||||
return ESP_ERR_CAMERA_NOT_DETECTED;
|
||||
}
|
||||
nvs_close(handle);
|
||||
return ret;
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Error (%d) opening nvs key \"%s\"", ret, key);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
void esp_camera_return_all(void) {
|
||||
if (s_state == NULL) {
|
||||
return;
|
||||
}
|
||||
cam_give_all();
|
||||
}
|
||||
|
@@ -0,0 +1,251 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
/*
|
||||
* Example Use
|
||||
*
|
||||
static camera_config_t camera_example_config = {
|
||||
.pin_pwdn = PIN_PWDN,
|
||||
.pin_reset = PIN_RESET,
|
||||
.pin_xclk = PIN_XCLK,
|
||||
.pin_sccb_sda = PIN_SIOD,
|
||||
.pin_sccb_scl = PIN_SIOC,
|
||||
.pin_d7 = PIN_D7,
|
||||
.pin_d6 = PIN_D6,
|
||||
.pin_d5 = PIN_D5,
|
||||
.pin_d4 = PIN_D4,
|
||||
.pin_d3 = PIN_D3,
|
||||
.pin_d2 = PIN_D2,
|
||||
.pin_d1 = PIN_D1,
|
||||
.pin_d0 = PIN_D0,
|
||||
.pin_vsync = PIN_VSYNC,
|
||||
.pin_href = PIN_HREF,
|
||||
.pin_pclk = PIN_PCLK,
|
||||
|
||||
.xclk_freq_hz = 20000000,
|
||||
.ledc_timer = LEDC_TIMER_0,
|
||||
.ledc_channel = LEDC_CHANNEL_0,
|
||||
.pixel_format = PIXFORMAT_JPEG,
|
||||
.frame_size = FRAMESIZE_SVGA,
|
||||
.jpeg_quality = 10,
|
||||
.fb_count = 2,
|
||||
.grab_mode = CAMERA_GRAB_WHEN_EMPTY
|
||||
};
|
||||
|
||||
esp_err_t camera_example_init(){
|
||||
return esp_camera_init(&camera_example_config);
|
||||
}
|
||||
|
||||
esp_err_t camera_example_capture(){
|
||||
//capture a frame
|
||||
camera_fb_t * fb = esp_camera_fb_get();
|
||||
if (!fb) {
|
||||
ESP_LOGE(TAG, "Frame buffer could not be acquired");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
//replace this with your own function
|
||||
display_image(fb->width, fb->height, fb->pixformat, fb->buf, fb->len);
|
||||
|
||||
//return the frame buffer back to be reused
|
||||
esp_camera_fb_return(fb);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "driver/ledc.h"
|
||||
#include "sensor.h"
|
||||
#include "sys/time.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
/**
|
||||
* @brief define for if chip supports camera
|
||||
*/
|
||||
#define ESP_CAMERA_SUPPORTED (CONFIG_IDF_TARGET_ESP32 | CONFIG_IDF_TARGET_ESP32S3 | \
|
||||
CONFIG_IDF_TARGET_ESP32S2)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Configuration structure for camera initialization
|
||||
*/
|
||||
typedef enum {
|
||||
CAMERA_GRAB_WHEN_EMPTY, /*!< Fills buffers when they are empty. Less resources but first 'fb_count' frames might be old */
|
||||
CAMERA_GRAB_LATEST /*!< Except when 1 frame buffer is used, queue will always contain the last 'fb_count' frames */
|
||||
} camera_grab_mode_t;
|
||||
|
||||
/**
|
||||
* @brief Camera frame buffer location
|
||||
*/
|
||||
typedef enum {
|
||||
CAMERA_FB_IN_PSRAM, /*!< Frame buffer is placed in external PSRAM */
|
||||
CAMERA_FB_IN_DRAM /*!< Frame buffer is placed in internal DRAM */
|
||||
} camera_fb_location_t;
|
||||
|
||||
#if CONFIG_CAMERA_CONVERTER_ENABLED
|
||||
/**
|
||||
* @brief Camera RGB\YUV conversion mode
|
||||
*/
|
||||
typedef enum {
|
||||
CONV_DISABLE,
|
||||
RGB565_TO_YUV422,
|
||||
|
||||
YUV422_TO_RGB565,
|
||||
YUV422_TO_YUV420
|
||||
} camera_conv_mode_t;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Configuration structure for camera initialization
|
||||
*/
|
||||
typedef struct {
|
||||
int pin_pwdn; /*!< GPIO pin for camera power down line */
|
||||
int pin_reset; /*!< GPIO pin for camera reset line */
|
||||
int pin_xclk; /*!< GPIO pin for camera XCLK line */
|
||||
union {
|
||||
int pin_sccb_sda; /*!< GPIO pin for camera SDA line */
|
||||
int pin_sscb_sda __attribute__((deprecated("please use pin_sccb_sda instead"))); /*!< GPIO pin for camera SDA line (legacy name) */
|
||||
};
|
||||
union {
|
||||
int pin_sccb_scl; /*!< GPIO pin for camera SCL line */
|
||||
int pin_sscb_scl __attribute__((deprecated("please use pin_sccb_scl instead"))); /*!< GPIO pin for camera SCL line (legacy name) */
|
||||
};
|
||||
int pin_d7; /*!< GPIO pin for camera D7 line */
|
||||
int pin_d6; /*!< GPIO pin for camera D6 line */
|
||||
int pin_d5; /*!< GPIO pin for camera D5 line */
|
||||
int pin_d4; /*!< GPIO pin for camera D4 line */
|
||||
int pin_d3; /*!< GPIO pin for camera D3 line */
|
||||
int pin_d2; /*!< GPIO pin for camera D2 line */
|
||||
int pin_d1; /*!< GPIO pin for camera D1 line */
|
||||
int pin_d0; /*!< GPIO pin for camera D0 line */
|
||||
int pin_vsync; /*!< GPIO pin for camera VSYNC line */
|
||||
int pin_href; /*!< GPIO pin for camera HREF line */
|
||||
int pin_pclk; /*!< GPIO pin for camera PCLK line */
|
||||
|
||||
int xclk_freq_hz; /*!< Frequency of XCLK signal, in Hz. EXPERIMENTAL: Set to 16MHz on ESP32-S2 or ESP32-S3 to enable EDMA mode */
|
||||
|
||||
ledc_timer_t ledc_timer; /*!< LEDC timer to be used for generating XCLK */
|
||||
ledc_channel_t ledc_channel; /*!< LEDC channel to be used for generating XCLK */
|
||||
|
||||
pixformat_t pixel_format; /*!< Format of the pixel data: PIXFORMAT_ + YUV422|GRAYSCALE|RGB565|JPEG */
|
||||
framesize_t frame_size; /*!< Size of the output image: FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA */
|
||||
|
||||
int jpeg_quality; /*!< Quality of JPEG output. 0-63 lower means higher quality */
|
||||
size_t fb_count; /*!< Number of frame buffers to be allocated. If more than one, then each frame will be acquired (double speed) */
|
||||
camera_fb_location_t fb_location; /*!< The location where the frame buffer will be allocated */
|
||||
camera_grab_mode_t grab_mode; /*!< When buffers should be filled */
|
||||
#if CONFIG_CAMERA_CONVERTER_ENABLED
|
||||
camera_conv_mode_t conv_mode; /*!< RGB<->YUV Conversion mode */
|
||||
#endif
|
||||
|
||||
int sccb_i2c_port; /*!< If pin_sccb_sda is -1, use the already configured I2C bus by number */
|
||||
} camera_config_t;
|
||||
|
||||
/**
|
||||
* @brief Data structure of camera frame buffer
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t * buf; /*!< Pointer to the pixel data */
|
||||
size_t len; /*!< Length of the buffer in bytes */
|
||||
size_t width; /*!< Width of the buffer in pixels */
|
||||
size_t height; /*!< Height of the buffer in pixels */
|
||||
pixformat_t format; /*!< Format of the pixel data */
|
||||
struct timeval timestamp; /*!< Timestamp since boot of the first DMA buffer of the frame */
|
||||
} camera_fb_t;
|
||||
|
||||
#define ESP_ERR_CAMERA_BASE 0x20000
|
||||
#define ESP_ERR_CAMERA_NOT_DETECTED (ESP_ERR_CAMERA_BASE + 1)
|
||||
#define ESP_ERR_CAMERA_FAILED_TO_SET_FRAME_SIZE (ESP_ERR_CAMERA_BASE + 2)
|
||||
#define ESP_ERR_CAMERA_FAILED_TO_SET_OUT_FORMAT (ESP_ERR_CAMERA_BASE + 3)
|
||||
#define ESP_ERR_CAMERA_NOT_SUPPORTED (ESP_ERR_CAMERA_BASE + 4)
|
||||
|
||||
/**
|
||||
* @brief Initialize the camera driver
|
||||
*
|
||||
* @note call camera_probe before calling this function
|
||||
*
|
||||
* This function detects and configures camera over I2C interface,
|
||||
* allocates framebuffer and DMA buffers,
|
||||
* initializes parallel I2S input, and sets up DMA descriptors.
|
||||
*
|
||||
* Currently this function can only be called once and there is
|
||||
* no way to de-initialize this module.
|
||||
*
|
||||
* @param config Camera configuration parameters
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_camera_init(const camera_config_t* config);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize the camera driver
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if the driver hasn't been initialized yet
|
||||
*/
|
||||
esp_err_t esp_camera_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief Obtain pointer to a frame buffer.
|
||||
*
|
||||
* @return pointer to the frame buffer
|
||||
*/
|
||||
camera_fb_t* esp_camera_fb_get(void);
|
||||
|
||||
/**
|
||||
* @brief Return the frame buffer to be reused again.
|
||||
*
|
||||
* @param fb Pointer to the frame buffer
|
||||
*/
|
||||
void esp_camera_fb_return(camera_fb_t * fb);
|
||||
|
||||
/**
|
||||
* @brief Get a pointer to the image sensor control structure
|
||||
*
|
||||
* @return pointer to the sensor
|
||||
*/
|
||||
sensor_t * esp_camera_sensor_get(void);
|
||||
|
||||
/**
|
||||
* @brief Save camera settings to non-volatile-storage (NVS)
|
||||
*
|
||||
* @param key A unique nvs key name for the camera settings
|
||||
*/
|
||||
esp_err_t esp_camera_save_to_nvs(const char *key);
|
||||
|
||||
/**
|
||||
* @brief Load camera settings from non-volatile-storage (NVS)
|
||||
*
|
||||
* @param key A unique nvs key name for the camera settings
|
||||
*/
|
||||
esp_err_t esp_camera_load_from_nvs(const char *key);
|
||||
|
||||
/**
|
||||
* @brief Return all frame buffers to be reused again.
|
||||
*/
|
||||
void esp_camera_return_all(void);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "img_converters.h"
|
||||
|
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
* This file is part of the OpenMV project.
|
||||
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
|
||||
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||
*
|
||||
* Sensor abstraction layer.
|
||||
*
|
||||
*/
|
||||
#ifndef __SENSOR_H__
|
||||
#define __SENSOR_H__
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
OV9650_PID = 0x96,
|
||||
OV7725_PID = 0x77,
|
||||
OV2640_PID = 0x26,
|
||||
OV3660_PID = 0x3660,
|
||||
OV5640_PID = 0x5640,
|
||||
OV7670_PID = 0x76,
|
||||
NT99141_PID = 0x1410,
|
||||
GC2145_PID = 0x2145,
|
||||
GC032A_PID = 0x232a,
|
||||
GC0308_PID = 0x9b,
|
||||
BF3005_PID = 0x30,
|
||||
BF20A6_PID = 0x20a6,
|
||||
SC101IOT_PID = 0xda4a,
|
||||
SC030IOT_PID = 0x9a46,
|
||||
SC031GS_PID = 0x0031,
|
||||
MEGA_CCM_PID =0x039E,
|
||||
} camera_pid_t;
|
||||
|
||||
typedef enum {
|
||||
CAMERA_OV7725,
|
||||
CAMERA_OV2640,
|
||||
CAMERA_OV3660,
|
||||
CAMERA_OV5640,
|
||||
CAMERA_OV7670,
|
||||
CAMERA_NT99141,
|
||||
CAMERA_GC2145,
|
||||
CAMERA_GC032A,
|
||||
CAMERA_GC0308,
|
||||
CAMERA_BF3005,
|
||||
CAMERA_BF20A6,
|
||||
CAMERA_SC101IOT,
|
||||
CAMERA_SC030IOT,
|
||||
CAMERA_SC031GS,
|
||||
CAMERA_MEGA_CCM,
|
||||
CAMERA_MODEL_MAX,
|
||||
CAMERA_NONE,
|
||||
} camera_model_t;
|
||||
|
||||
typedef enum {
|
||||
OV2640_SCCB_ADDR = 0x30,// 0x60 >> 1
|
||||
OV5640_SCCB_ADDR = 0x3C,// 0x78 >> 1
|
||||
OV3660_SCCB_ADDR = 0x3C,// 0x78 >> 1
|
||||
OV7725_SCCB_ADDR = 0x21,// 0x42 >> 1
|
||||
OV7670_SCCB_ADDR = 0x21,// 0x42 >> 1
|
||||
NT99141_SCCB_ADDR = 0x2A,// 0x54 >> 1
|
||||
GC2145_SCCB_ADDR = 0x3C,// 0x78 >> 1
|
||||
GC032A_SCCB_ADDR = 0x21,// 0x42 >> 1
|
||||
GC0308_SCCB_ADDR = 0x21,// 0x42 >> 1
|
||||
BF3005_SCCB_ADDR = 0x6E,
|
||||
BF20A6_SCCB_ADDR = 0x6E,
|
||||
SC101IOT_SCCB_ADDR = 0x68,// 0xd0 >> 1
|
||||
SC030IOT_SCCB_ADDR = 0x68,// 0xd0 >> 1
|
||||
SC031GS_SCCB_ADDR = 0x30,
|
||||
MEGA_CCM_SCCB_ADDR = 0x1F, // 0x3E >> 1
|
||||
} camera_sccb_addr_t;
|
||||
|
||||
typedef enum {
|
||||
PIXFORMAT_RGB565, // 2BPP/RGB565
|
||||
PIXFORMAT_YUV422, // 2BPP/YUV422
|
||||
PIXFORMAT_YUV420, // 1.5BPP/YUV420
|
||||
PIXFORMAT_GRAYSCALE, // 1BPP/GRAYSCALE
|
||||
PIXFORMAT_JPEG, // JPEG/COMPRESSED
|
||||
PIXFORMAT_RGB888, // 3BPP/RGB888
|
||||
PIXFORMAT_RAW, // RAW
|
||||
PIXFORMAT_RGB444, // 3BP2P/RGB444
|
||||
PIXFORMAT_RGB555, // 3BP2P/RGB555
|
||||
} pixformat_t;
|
||||
|
||||
typedef enum {
|
||||
FRAMESIZE_96X96, // 96x96
|
||||
FRAMESIZE_QQVGA, // 160x120
|
||||
FRAMESIZE_128X128, // 128x128
|
||||
FRAMESIZE_QCIF, // 176x144
|
||||
FRAMESIZE_HQVGA, // 240x176
|
||||
FRAMESIZE_240X240, // 240x240
|
||||
FRAMESIZE_QVGA, // 320x240
|
||||
FRAMESIZE_320X320, // 320x320
|
||||
FRAMESIZE_CIF, // 400x296
|
||||
FRAMESIZE_HVGA, // 480x320
|
||||
FRAMESIZE_VGA, // 640x480
|
||||
FRAMESIZE_SVGA, // 800x600
|
||||
FRAMESIZE_XGA, // 1024x768
|
||||
FRAMESIZE_HD, // 1280x720
|
||||
FRAMESIZE_SXGA, // 1280x1024
|
||||
FRAMESIZE_UXGA, // 1600x1200
|
||||
// 3MP Sensors
|
||||
FRAMESIZE_FHD, // 1920x1080
|
||||
FRAMESIZE_P_HD, // 720x1280
|
||||
FRAMESIZE_P_3MP, // 864x1536
|
||||
FRAMESIZE_QXGA, // 2048x1536
|
||||
// 5MP Sensors
|
||||
FRAMESIZE_QHD, // 2560x1440
|
||||
FRAMESIZE_WQXGA, // 2560x1600
|
||||
FRAMESIZE_P_FHD, // 1080x1920
|
||||
FRAMESIZE_QSXGA, // 2560x1920
|
||||
FRAMESIZE_5MP, // 2592x1944
|
||||
FRAMESIZE_INVALID
|
||||
} framesize_t;
|
||||
|
||||
typedef struct {
|
||||
const camera_model_t model;
|
||||
const char *name;
|
||||
const camera_sccb_addr_t sccb_addr;
|
||||
const camera_pid_t pid;
|
||||
const framesize_t max_size;
|
||||
const bool support_jpeg;
|
||||
} camera_sensor_info_t;
|
||||
|
||||
typedef enum {
|
||||
ASPECT_RATIO_4X3,
|
||||
ASPECT_RATIO_3X2,
|
||||
ASPECT_RATIO_16X10,
|
||||
ASPECT_RATIO_5X3,
|
||||
ASPECT_RATIO_16X9,
|
||||
ASPECT_RATIO_21X9,
|
||||
ASPECT_RATIO_5X4,
|
||||
ASPECT_RATIO_1X1,
|
||||
ASPECT_RATIO_9X16
|
||||
} aspect_ratio_t;
|
||||
|
||||
typedef enum {
|
||||
GAINCEILING_2X,
|
||||
GAINCEILING_4X,
|
||||
GAINCEILING_8X,
|
||||
GAINCEILING_16X,
|
||||
GAINCEILING_32X,
|
||||
GAINCEILING_64X,
|
||||
GAINCEILING_128X,
|
||||
} gainceiling_t;
|
||||
|
||||
typedef struct {
|
||||
uint16_t max_width;
|
||||
uint16_t max_height;
|
||||
uint16_t start_x;
|
||||
uint16_t start_y;
|
||||
uint16_t end_x;
|
||||
uint16_t end_y;
|
||||
uint16_t offset_x;
|
||||
uint16_t offset_y;
|
||||
uint16_t total_x;
|
||||
uint16_t total_y;
|
||||
} ratio_settings_t;
|
||||
|
||||
typedef struct {
|
||||
const uint16_t width;
|
||||
const uint16_t height;
|
||||
const aspect_ratio_t aspect_ratio;
|
||||
} resolution_info_t;
|
||||
|
||||
// Resolution table (in sensor.c)
|
||||
extern const resolution_info_t resolution[];
|
||||
// camera sensor table (in sensor.c)
|
||||
extern const camera_sensor_info_t camera_sensor[];
|
||||
|
||||
typedef struct {
|
||||
uint8_t MIDH;
|
||||
uint8_t MIDL;
|
||||
uint16_t PID;
|
||||
uint8_t VER;
|
||||
} sensor_id_t;
|
||||
|
||||
typedef struct {
|
||||
framesize_t framesize;//0 - 10
|
||||
bool scale;
|
||||
bool binning;
|
||||
uint8_t quality;//0 - 63
|
||||
int8_t brightness;//-2 - 2
|
||||
int8_t contrast;//-2 - 2
|
||||
int8_t saturation;//-2 - 2
|
||||
int8_t sharpness;//-2 - 2
|
||||
uint8_t denoise;
|
||||
uint8_t special_effect;//0 - 6
|
||||
uint8_t wb_mode;//0 - 4
|
||||
uint8_t awb;
|
||||
uint8_t awb_gain;
|
||||
uint8_t aec;
|
||||
uint8_t aec2;
|
||||
int8_t ae_level;//-2 - 2
|
||||
uint16_t aec_value;//0 - 1200
|
||||
uint8_t agc;
|
||||
uint8_t agc_gain;//0 - 30
|
||||
uint8_t gainceiling;//0 - 6
|
||||
uint8_t bpc;
|
||||
uint8_t wpc;
|
||||
uint8_t raw_gma;
|
||||
uint8_t lenc;
|
||||
uint8_t hmirror;
|
||||
uint8_t vflip;
|
||||
uint8_t dcw;
|
||||
uint8_t colorbar;
|
||||
} camera_status_t;
|
||||
|
||||
typedef struct _sensor sensor_t;
|
||||
typedef struct _sensor {
|
||||
sensor_id_t id; // Sensor ID.
|
||||
uint8_t slv_addr; // Sensor I2C slave address.
|
||||
pixformat_t pixformat;
|
||||
camera_status_t status;
|
||||
int xclk_freq_hz;
|
||||
|
||||
// Sensor function pointers
|
||||
int (*init_status) (sensor_t *sensor);
|
||||
int (*reset) (sensor_t *sensor); // Reset the configuration of the sensor, and return ESP_OK if reset is successful
|
||||
int (*set_pixformat) (sensor_t *sensor, pixformat_t pixformat);
|
||||
int (*set_framesize) (sensor_t *sensor, framesize_t framesize);
|
||||
int (*set_contrast) (sensor_t *sensor, int level);
|
||||
int (*set_brightness) (sensor_t *sensor, int level);
|
||||
int (*set_saturation) (sensor_t *sensor, int level);
|
||||
int (*set_sharpness) (sensor_t *sensor, int level);
|
||||
int (*set_denoise) (sensor_t *sensor, int level);
|
||||
int (*set_gainceiling) (sensor_t *sensor, gainceiling_t gainceiling);
|
||||
int (*set_quality) (sensor_t *sensor, int quality);
|
||||
int (*set_colorbar) (sensor_t *sensor, int enable);
|
||||
int (*set_whitebal) (sensor_t *sensor, int enable);
|
||||
int (*set_gain_ctrl) (sensor_t *sensor, int enable);
|
||||
int (*set_exposure_ctrl) (sensor_t *sensor, int enable);
|
||||
int (*set_hmirror) (sensor_t *sensor, int enable);
|
||||
int (*set_vflip) (sensor_t *sensor, int enable);
|
||||
|
||||
int (*set_aec2) (sensor_t *sensor, int enable);
|
||||
int (*set_awb_gain) (sensor_t *sensor, int enable);
|
||||
int (*set_agc_gain) (sensor_t *sensor, int gain);
|
||||
int (*set_aec_value) (sensor_t *sensor, int gain);
|
||||
|
||||
int (*set_special_effect) (sensor_t *sensor, int effect);
|
||||
int (*set_wb_mode) (sensor_t *sensor, int mode);
|
||||
int (*set_ae_level) (sensor_t *sensor, int level);
|
||||
|
||||
int (*set_dcw) (sensor_t *sensor, int enable);
|
||||
int (*set_bpc) (sensor_t *sensor, int enable);
|
||||
int (*set_wpc) (sensor_t *sensor, int enable);
|
||||
|
||||
int (*set_raw_gma) (sensor_t *sensor, int enable);
|
||||
int (*set_lenc) (sensor_t *sensor, int enable);
|
||||
|
||||
int (*get_reg) (sensor_t *sensor, int reg, int mask);
|
||||
int (*set_reg) (sensor_t *sensor, int reg, int mask, int value);
|
||||
int (*set_res_raw) (sensor_t *sensor, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, bool binning);
|
||||
int (*set_pll) (sensor_t *sensor, int bypass, int mul, int sys, int root, int pre, int seld5, int pclken, int pclk);
|
||||
int (*set_xclk) (sensor_t *sensor, int timer, int xclk);
|
||||
} sensor_t;
|
||||
|
||||
camera_sensor_info_t *esp_camera_sensor_get_info(sensor_id_t *id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __SENSOR_H__ */
|
@@ -0,0 +1,62 @@
|
||||
// Copyright 2010-2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_camera.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Uninitialize the lcd_cam module
|
||||
*
|
||||
* @param handle Provide handle pointer to release resources
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Uninitialize fail
|
||||
*/
|
||||
esp_err_t cam_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief Initialize the lcd_cam module
|
||||
*
|
||||
* @param config Configurations - see lcd_cam_config_t struct
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
* - ESP_ERR_NO_MEM No memory to initialize lcd_cam
|
||||
* - ESP_FAIL Initialize fail
|
||||
*/
|
||||
esp_err_t cam_init(const camera_config_t *config);
|
||||
|
||||
esp_err_t cam_config(const camera_config_t *config, framesize_t frame_size, uint16_t sensor_pid);
|
||||
|
||||
void cam_stop(void);
|
||||
|
||||
void cam_start(void);
|
||||
|
||||
camera_fb_t *cam_take(TickType_t timeout);
|
||||
|
||||
void cam_give(camera_fb_t *dma_buffer);
|
||||
|
||||
void cam_give_all(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* This file is part of the OpenMV project.
|
||||
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
|
||||
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||
*
|
||||
* SCCB (I2C like) driver.
|
||||
*
|
||||
*/
|
||||
#ifndef __SCCB_H__
|
||||
#define __SCCB_H__
|
||||
#include <stdint.h>
|
||||
int SCCB_Init(int pin_sda, int pin_scl);
|
||||
int SCCB_Use_Port(int sccb_i2c_port);
|
||||
int SCCB_Deinit(void);
|
||||
uint8_t SCCB_Probe(void);
|
||||
uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg);
|
||||
int SCCB_Write(uint8_t slv_addr, uint8_t reg, uint8_t data);
|
||||
uint8_t SCCB_Read16(uint8_t slv_addr, uint16_t reg);
|
||||
int SCCB_Write16(uint8_t slv_addr, uint16_t reg, uint8_t data);
|
||||
uint16_t SCCB_Read_Addr16_Val16(uint8_t slv_addr, uint16_t reg);
|
||||
int SCCB_Write_Addr16_Val16(uint8_t slv_addr, uint16_t reg, uint16_t data);
|
||||
#endif // __SCCB_H__
|
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "esp_system.h"
|
||||
#include "esp_camera.h"
|
||||
|
||||
esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz);
|
||||
|
||||
esp_err_t camera_enable_out_clock(const camera_config_t *config);
|
||||
|
||||
void camera_disable_out_clock(void);
|
347
managed_components/espressif__esp32-camera/driver/sccb-ng.c
Normal file
347
managed_components/espressif__esp32-camera/driver/sccb-ng.c
Normal file
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
* This file is part of the OpenMV project.
|
||||
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
|
||||
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||
*
|
||||
* SCCB (I2C like) driver with the new esp-idf I2C API.
|
||||
*
|
||||
*/
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include "sccb.h"
|
||||
#include "sensor.h"
|
||||
#include <stdio.h>
|
||||
#include "sdkconfig.h"
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char *TAG = "sccb-ng";
|
||||
#endif
|
||||
|
||||
#define LITTLETOBIG(x) ((x << 8) | (x >> 8))
|
||||
|
||||
#include "esp_private/i2c_platform.h"
|
||||
#include "driver/i2c_master.h"
|
||||
#include "driver/i2c_types.h"
|
||||
|
||||
// support IDF 5.x
|
||||
#ifndef portTICK_RATE_MS
|
||||
#define portTICK_RATE_MS portTICK_PERIOD_MS
|
||||
#endif
|
||||
|
||||
#define TIMEOUT_MS 1000 /*!< I2C timeout duration */
|
||||
#define SCCB_FREQ CONFIG_SCCB_CLK_FREQ /*!< I2C master frequency */
|
||||
#if CONFIG_SCCB_HARDWARE_I2C_PORT1
|
||||
const int SCCB_I2C_PORT_DEFAULT = 1;
|
||||
#else
|
||||
const int SCCB_I2C_PORT_DEFAULT = 0;
|
||||
#endif
|
||||
|
||||
#define MAX_DEVICES UINT8_MAX-1
|
||||
|
||||
/*
|
||||
The legacy I2C driver used addresses to differentiate between devices, whereas the new driver uses
|
||||
i2c_master_dev_handle_t structs which are registed to the bus.
|
||||
To avoid re-writing all camera dependant code, we simply translate the devices address to the corresponding
|
||||
device_handle. This keeps all interfaces to the drivers identical.
|
||||
To perform this conversion the following local struct is used.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
i2c_master_dev_handle_t dev_handle;
|
||||
uint16_t address;
|
||||
} device_t;
|
||||
|
||||
static device_t devices[MAX_DEVICES];
|
||||
static uint8_t device_count = 0;
|
||||
static int sccb_i2c_port;
|
||||
static bool sccb_owns_i2c_port;
|
||||
|
||||
i2c_master_dev_handle_t *get_handle_from_address(uint8_t slv_addr)
|
||||
{
|
||||
for (uint8_t i = 0; i < device_count; i++)
|
||||
{
|
||||
|
||||
if (slv_addr == devices[i].address)
|
||||
{
|
||||
return &(devices[i].dev_handle);
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGE(TAG, "Device with address %02x not found", slv_addr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int SCCB_Install_Device(uint8_t slv_addr)
|
||||
{
|
||||
esp_err_t ret;
|
||||
i2c_master_bus_handle_t bus_handle;
|
||||
|
||||
if (device_count > MAX_DEVICES)
|
||||
{
|
||||
ESP_LOGE(TAG, "cannot add more than %d devices", MAX_DEVICES);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ret = i2c_master_get_bus_handle(sccb_i2c_port, &bus_handle);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "failed to get SCCB I2C Bus handle for port %d", sccb_i2c_port);
|
||||
return ret;
|
||||
}
|
||||
|
||||
i2c_device_config_t dev_cfg = {
|
||||
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
|
||||
.device_address = slv_addr, // not yet set
|
||||
.scl_speed_hz = SCCB_FREQ,
|
||||
};
|
||||
|
||||
ret = i2c_master_bus_add_device(bus_handle, &dev_cfg, &(devices[device_count].dev_handle));
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "failed to install SCCB I2C device: %s", esp_err_to_name(ret));
|
||||
return -1;
|
||||
}
|
||||
|
||||
devices[device_count].address = slv_addr;
|
||||
device_count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SCCB_Init(int pin_sda, int pin_scl)
|
||||
{
|
||||
ESP_LOGI(TAG, "pin_sda %d pin_scl %d", pin_sda, pin_scl);
|
||||
// i2c_config_t conf;
|
||||
esp_err_t ret;
|
||||
|
||||
sccb_i2c_port = SCCB_I2C_PORT_DEFAULT;
|
||||
sccb_owns_i2c_port = true;
|
||||
ESP_LOGI(TAG, "sccb_i2c_port=%d", sccb_i2c_port);
|
||||
|
||||
i2c_master_bus_config_t i2c_mst_config = {
|
||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||
.i2c_port = SCCB_I2C_PORT_DEFAULT,
|
||||
.scl_io_num = pin_scl,
|
||||
.sda_io_num = pin_sda,
|
||||
.glitch_ignore_cnt = 7,
|
||||
.flags.enable_internal_pullup = 1};
|
||||
|
||||
i2c_master_bus_handle_t bus_handle;
|
||||
ret = i2c_new_master_bus(&i2c_mst_config, &bus_handle);
|
||||
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "failed to install SCCB I2C master bus on port %d: %s", sccb_i2c_port, esp_err_to_name(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
int SCCB_Use_Port(int i2c_num)
|
||||
{ // sccb use an already initialized I2C port
|
||||
if (sccb_owns_i2c_port)
|
||||
{
|
||||
SCCB_Deinit();
|
||||
}
|
||||
if (i2c_num < 0 || i2c_num > I2C_NUM_MAX)
|
||||
{
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
sccb_i2c_port = i2c_num;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
int SCCB_Deinit(void)
|
||||
{
|
||||
esp_err_t ret;
|
||||
|
||||
for (uint8_t i = 0; i < device_count; i++)
|
||||
{
|
||||
ret = i2c_master_bus_rm_device(devices[i].dev_handle);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "failed to remove SCCB I2C Device");
|
||||
return ret;
|
||||
}
|
||||
|
||||
devices[i].dev_handle = NULL;
|
||||
devices[i].address = 0;
|
||||
}
|
||||
device_count = 0;
|
||||
|
||||
if (!sccb_owns_i2c_port)
|
||||
{
|
||||
return ESP_OK;
|
||||
}
|
||||
sccb_owns_i2c_port = false;
|
||||
|
||||
i2c_master_bus_handle_t bus_handle;
|
||||
ret = i2c_master_get_bus_handle(sccb_i2c_port, &bus_handle);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "failed to get SCCB I2C Bus handle for port %d", sccb_i2c_port);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = i2c_del_master_bus(bus_handle);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "failed to get delete SCCB I2C Master Bus at port %d", sccb_i2c_port);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
uint8_t SCCB_Probe(void)
|
||||
{
|
||||
uint8_t slave_addr = 0x0;
|
||||
esp_err_t ret;
|
||||
i2c_master_bus_handle_t bus_handle;
|
||||
|
||||
ret = i2c_master_get_bus_handle(sccb_i2c_port, &bus_handle);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "failed to get SCCB I2C Bus handle for port %d", sccb_i2c_port);
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < CAMERA_MODEL_MAX; i++)
|
||||
{
|
||||
if (slave_addr == camera_sensor[i].sccb_addr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
slave_addr = camera_sensor[i].sccb_addr;
|
||||
|
||||
ret = i2c_master_probe(bus_handle, slave_addr, TIMEOUT_MS);
|
||||
|
||||
if (ret == ESP_OK)
|
||||
{
|
||||
if (SCCB_Install_Device(slave_addr) != 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return slave_addr;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg)
|
||||
{
|
||||
i2c_master_dev_handle_t dev_handle = *(get_handle_from_address(slv_addr));
|
||||
|
||||
uint8_t tx_buffer[1];
|
||||
uint8_t rx_buffer[1];
|
||||
|
||||
tx_buffer[0] = reg;
|
||||
|
||||
esp_err_t ret = i2c_master_transmit_receive(dev_handle, tx_buffer, 1, rx_buffer, 1, TIMEOUT_MS);
|
||||
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "SCCB_Read Failed addr:0x%02x, reg:0x%02x, data:0x%02x, ret:%d", slv_addr, reg, rx_buffer[0], ret);
|
||||
}
|
||||
|
||||
return rx_buffer[0];
|
||||
}
|
||||
|
||||
int SCCB_Write(uint8_t slv_addr, uint8_t reg, uint8_t data)
|
||||
{
|
||||
i2c_master_dev_handle_t dev_handle = *(get_handle_from_address(slv_addr));
|
||||
|
||||
uint8_t tx_buffer[2];
|
||||
tx_buffer[0] = reg;
|
||||
tx_buffer[1] = data;
|
||||
|
||||
esp_err_t ret = i2c_master_transmit(dev_handle, tx_buffer, 2, TIMEOUT_MS);
|
||||
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "SCCB_Write Failed addr:0x%02x, reg:0x%02x, data:0x%02x, ret:%d", slv_addr, reg, data, ret);
|
||||
}
|
||||
|
||||
return ret == ESP_OK ? 0 : -1;
|
||||
}
|
||||
|
||||
uint8_t SCCB_Read16(uint8_t slv_addr, uint16_t reg)
|
||||
{
|
||||
i2c_master_dev_handle_t dev_handle = *(get_handle_from_address(slv_addr));
|
||||
|
||||
uint8_t rx_buffer[1];
|
||||
|
||||
uint16_t reg_htons = LITTLETOBIG(reg);
|
||||
uint8_t *reg_u8 = (uint8_t *)®_htons;
|
||||
|
||||
esp_err_t ret = i2c_master_transmit_receive(dev_handle, reg_u8, 2, rx_buffer, 1, TIMEOUT_MS);
|
||||
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "W [%04x]=%02x fail\n", reg, rx_buffer[0]);
|
||||
}
|
||||
|
||||
return rx_buffer[0];
|
||||
}
|
||||
|
||||
int SCCB_Write16(uint8_t slv_addr, uint16_t reg, uint8_t data)
|
||||
{
|
||||
i2c_master_dev_handle_t dev_handle = *(get_handle_from_address(slv_addr));
|
||||
|
||||
uint8_t tx_buffer[3];
|
||||
tx_buffer[0] = reg >> 8;
|
||||
tx_buffer[1] = reg & 0x00ff;
|
||||
tx_buffer[2] = data;
|
||||
|
||||
esp_err_t ret = i2c_master_transmit(dev_handle, tx_buffer, 3, TIMEOUT_MS);
|
||||
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "W [%04x]=%02x fail\n", reg, data);
|
||||
}
|
||||
return ret == ESP_OK ? 0 : -1;
|
||||
}
|
||||
|
||||
uint16_t SCCB_Read_Addr16_Val16(uint8_t slv_addr, uint16_t reg)
|
||||
{
|
||||
i2c_master_dev_handle_t dev_handle = *(get_handle_from_address(slv_addr));
|
||||
|
||||
uint8_t rx_buffer[2];
|
||||
|
||||
uint16_t reg_htons = LITTLETOBIG(reg);
|
||||
uint8_t *reg_u8 = (uint8_t *)®_htons;
|
||||
|
||||
esp_err_t ret = i2c_master_transmit_receive(dev_handle, reg_u8, 2, rx_buffer, 2, TIMEOUT_MS);
|
||||
uint16_t data = ((uint16_t)rx_buffer[0] << 8) | (uint16_t)rx_buffer[1];
|
||||
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "W [%04x]=%02x fail\n", reg, data);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
int SCCB_Write_Addr16_Val16(uint8_t slv_addr, uint16_t reg, uint16_t data)
|
||||
{
|
||||
i2c_master_dev_handle_t dev_handle = *(get_handle_from_address(slv_addr));
|
||||
|
||||
uint8_t tx_buffer[4];
|
||||
tx_buffer[0] = reg >> 8;
|
||||
tx_buffer[1] = reg & 0x00ff;
|
||||
tx_buffer[2] = data >> 8;
|
||||
tx_buffer[3] = data & 0x00ff;
|
||||
|
||||
esp_err_t ret = i2c_master_transmit(dev_handle, tx_buffer, 4, TIMEOUT_MS);
|
||||
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "W [%04x]=%02x fail\n", reg, data);
|
||||
}
|
||||
return ret == ESP_OK ? 0 : -1;
|
||||
}
|
261
managed_components/espressif__esp32-camera/driver/sccb.c
Normal file
261
managed_components/espressif__esp32-camera/driver/sccb.c
Normal file
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* This file is part of the OpenMV project.
|
||||
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
|
||||
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||
*
|
||||
* SCCB (I2C like) driver.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include "sccb.h"
|
||||
#include "sensor.h"
|
||||
#include <stdio.h>
|
||||
#include "sdkconfig.h"
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char* TAG = "sccb";
|
||||
#endif
|
||||
|
||||
#define LITTLETOBIG(x) ((x<<8)|(x>>8))
|
||||
|
||||
#include "driver/i2c.h"
|
||||
|
||||
// support IDF 5.x
|
||||
#ifndef portTICK_RATE_MS
|
||||
#define portTICK_RATE_MS portTICK_PERIOD_MS
|
||||
#endif
|
||||
|
||||
#define SCCB_FREQ CONFIG_SCCB_CLK_FREQ /*!< I2C master frequency*/
|
||||
#define WRITE_BIT I2C_MASTER_WRITE /*!< I2C master write */
|
||||
#define READ_BIT I2C_MASTER_READ /*!< I2C master read */
|
||||
#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/
|
||||
#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
|
||||
#define ACK_VAL 0x0 /*!< I2C ack value */
|
||||
#define NACK_VAL 0x1 /*!< I2C nack value */
|
||||
#if CONFIG_SCCB_HARDWARE_I2C_PORT1
|
||||
const int SCCB_I2C_PORT_DEFAULT = 1;
|
||||
#else
|
||||
const int SCCB_I2C_PORT_DEFAULT = 0;
|
||||
#endif
|
||||
|
||||
static int sccb_i2c_port;
|
||||
static bool sccb_owns_i2c_port;
|
||||
|
||||
int SCCB_Init(int pin_sda, int pin_scl)
|
||||
{
|
||||
ESP_LOGI(TAG, "pin_sda %d pin_scl %d", pin_sda, pin_scl);
|
||||
i2c_config_t conf;
|
||||
esp_err_t ret;
|
||||
|
||||
memset(&conf, 0, sizeof(i2c_config_t));
|
||||
|
||||
sccb_i2c_port = SCCB_I2C_PORT_DEFAULT;
|
||||
sccb_owns_i2c_port = true;
|
||||
ESP_LOGI(TAG, "sccb_i2c_port=%d", sccb_i2c_port);
|
||||
|
||||
conf.mode = I2C_MODE_MASTER;
|
||||
conf.sda_io_num = pin_sda;
|
||||
conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
|
||||
conf.scl_io_num = pin_scl;
|
||||
conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
|
||||
conf.master.clk_speed = SCCB_FREQ;
|
||||
|
||||
if ((ret = i2c_param_config(sccb_i2c_port, &conf)) != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return i2c_driver_install(sccb_i2c_port, conf.mode, 0, 0, 0);
|
||||
}
|
||||
|
||||
int SCCB_Use_Port(int i2c_num) { // sccb use an already initialized I2C port
|
||||
if (sccb_owns_i2c_port) {
|
||||
SCCB_Deinit();
|
||||
}
|
||||
if (i2c_num < 0 || i2c_num > I2C_NUM_MAX) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
sccb_i2c_port = i2c_num;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
int SCCB_Deinit(void)
|
||||
{
|
||||
if (!sccb_owns_i2c_port) {
|
||||
return ESP_OK;
|
||||
}
|
||||
sccb_owns_i2c_port = false;
|
||||
return i2c_driver_delete(sccb_i2c_port);
|
||||
}
|
||||
|
||||
uint8_t SCCB_Probe(void)
|
||||
{
|
||||
uint8_t slave_addr = 0x0;
|
||||
|
||||
for (size_t i = 0; i < CAMERA_MODEL_MAX; i++) {
|
||||
if (slave_addr == camera_sensor[i].sccb_addr) {
|
||||
continue;
|
||||
}
|
||||
slave_addr = camera_sensor[i].sccb_addr;
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, ( slave_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN);
|
||||
i2c_master_stop(cmd);
|
||||
esp_err_t ret = i2c_master_cmd_begin(sccb_i2c_port, cmd, 1000 / portTICK_RATE_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
if( ret == ESP_OK) {
|
||||
return slave_addr;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg)
|
||||
{
|
||||
uint8_t data=0;
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, ( slv_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN);
|
||||
i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
|
||||
i2c_master_stop(cmd);
|
||||
ret = i2c_master_cmd_begin(sccb_i2c_port, cmd, 1000 / portTICK_RATE_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
if(ret != ESP_OK) return -1;
|
||||
cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, ( slv_addr << 1 ) | READ_BIT, ACK_CHECK_EN);
|
||||
i2c_master_read_byte(cmd, &data, NACK_VAL);
|
||||
i2c_master_stop(cmd);
|
||||
ret = i2c_master_cmd_begin(sccb_i2c_port, cmd, 1000 / portTICK_RATE_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
if(ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "SCCB_Read Failed addr:0x%02x, reg:0x%02x, data:0x%02x, ret:%d", slv_addr, reg, data, ret);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
int SCCB_Write(uint8_t slv_addr, uint8_t reg, uint8_t data)
|
||||
{
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, ( slv_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN);
|
||||
i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
|
||||
i2c_master_write_byte(cmd, data, ACK_CHECK_EN);
|
||||
i2c_master_stop(cmd);
|
||||
ret = i2c_master_cmd_begin(sccb_i2c_port, cmd, 1000 / portTICK_RATE_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
if(ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "SCCB_Write Failed addr:0x%02x, reg:0x%02x, data:0x%02x, ret:%d", slv_addr, reg, data, ret);
|
||||
}
|
||||
return ret == ESP_OK ? 0 : -1;
|
||||
}
|
||||
|
||||
uint8_t SCCB_Read16(uint8_t slv_addr, uint16_t reg)
|
||||
{
|
||||
uint8_t data=0;
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
uint16_t reg_htons = LITTLETOBIG(reg);
|
||||
uint8_t *reg_u8 = (uint8_t *)®_htons;
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, ( slv_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN);
|
||||
i2c_master_write_byte(cmd, reg_u8[0], ACK_CHECK_EN);
|
||||
i2c_master_write_byte(cmd, reg_u8[1], ACK_CHECK_EN);
|
||||
i2c_master_stop(cmd);
|
||||
ret = i2c_master_cmd_begin(sccb_i2c_port, cmd, 1000 / portTICK_RATE_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
if(ret != ESP_OK) return -1;
|
||||
cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, ( slv_addr << 1 ) | READ_BIT, ACK_CHECK_EN);
|
||||
i2c_master_read_byte(cmd, &data, NACK_VAL);
|
||||
i2c_master_stop(cmd);
|
||||
ret = i2c_master_cmd_begin(sccb_i2c_port, cmd, 1000 / portTICK_RATE_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
if(ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "W [%04x]=%02x fail\n", reg, data);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
int SCCB_Write16(uint8_t slv_addr, uint16_t reg, uint8_t data)
|
||||
{
|
||||
static uint16_t i = 0;
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
uint16_t reg_htons = LITTLETOBIG(reg);
|
||||
uint8_t *reg_u8 = (uint8_t *)®_htons;
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, ( slv_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN);
|
||||
i2c_master_write_byte(cmd, reg_u8[0], ACK_CHECK_EN);
|
||||
i2c_master_write_byte(cmd, reg_u8[1], ACK_CHECK_EN);
|
||||
i2c_master_write_byte(cmd, data, ACK_CHECK_EN);
|
||||
i2c_master_stop(cmd);
|
||||
ret = i2c_master_cmd_begin(sccb_i2c_port, cmd, 1000 / portTICK_RATE_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
if(ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "W [%04x]=%02x %d fail\n", reg, data, i++);
|
||||
}
|
||||
return ret == ESP_OK ? 0 : -1;
|
||||
}
|
||||
|
||||
uint16_t SCCB_Read_Addr16_Val16(uint8_t slv_addr, uint16_t reg)
|
||||
{
|
||||
uint16_t data = 0;
|
||||
uint8_t *data_u8 = (uint8_t *)&data;
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
uint16_t reg_htons = LITTLETOBIG(reg);
|
||||
uint8_t *reg_u8 = (uint8_t *)®_htons;
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, ( slv_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN);
|
||||
i2c_master_write_byte(cmd, reg_u8[0], ACK_CHECK_EN);
|
||||
i2c_master_write_byte(cmd, reg_u8[1], ACK_CHECK_EN);
|
||||
i2c_master_stop(cmd);
|
||||
ret = i2c_master_cmd_begin(sccb_i2c_port, cmd, 1000 / portTICK_RATE_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
if(ret != ESP_OK) return -1;
|
||||
|
||||
cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, ( slv_addr << 1 ) | READ_BIT, ACK_CHECK_EN);
|
||||
i2c_master_read_byte(cmd, &data_u8[1], ACK_VAL);
|
||||
i2c_master_read_byte(cmd, &data_u8[0], NACK_VAL);
|
||||
i2c_master_stop(cmd);
|
||||
ret = i2c_master_cmd_begin(sccb_i2c_port, cmd, 1000 / portTICK_RATE_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
if(ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "W [%04x]=%04x fail\n", reg, data);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
int SCCB_Write_Addr16_Val16(uint8_t slv_addr, uint16_t reg, uint16_t data)
|
||||
{
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
uint16_t reg_htons = LITTLETOBIG(reg);
|
||||
uint8_t *reg_u8 = (uint8_t *)®_htons;
|
||||
uint16_t data_htons = LITTLETOBIG(data);
|
||||
uint8_t *data_u8 = (uint8_t *)&data_htons;
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, ( slv_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN);
|
||||
i2c_master_write_byte(cmd, reg_u8[0], ACK_CHECK_EN);
|
||||
i2c_master_write_byte(cmd, reg_u8[1], ACK_CHECK_EN);
|
||||
i2c_master_write_byte(cmd, data_u8[0], ACK_CHECK_EN);
|
||||
i2c_master_write_byte(cmd, data_u8[1], ACK_CHECK_EN);
|
||||
i2c_master_stop(cmd);
|
||||
ret = i2c_master_cmd_begin(sccb_i2c_port, cmd, 1000 / portTICK_RATE_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
if(ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "W [%04x]=%04x fail\n", reg, data);
|
||||
}
|
||||
return ret == ESP_OK ? 0 : -1;
|
||||
}
|
61
managed_components/espressif__esp32-camera/driver/sensor.c
Normal file
61
managed_components/espressif__esp32-camera/driver/sensor.c
Normal file
@@ -0,0 +1,61 @@
|
||||
#include <stdio.h>
|
||||
#include "sensor.h"
|
||||
|
||||
const camera_sensor_info_t camera_sensor[CAMERA_MODEL_MAX] = {
|
||||
// The sequence must be consistent with camera_model_t
|
||||
{CAMERA_OV7725, "OV7725", OV7725_SCCB_ADDR, OV7725_PID, FRAMESIZE_VGA, false},
|
||||
{CAMERA_OV2640, "OV2640", OV2640_SCCB_ADDR, OV2640_PID, FRAMESIZE_UXGA, true},
|
||||
{CAMERA_OV3660, "OV3660", OV3660_SCCB_ADDR, OV3660_PID, FRAMESIZE_QXGA, true},
|
||||
{CAMERA_OV5640, "OV5640", OV5640_SCCB_ADDR, OV5640_PID, FRAMESIZE_QSXGA, true},
|
||||
{CAMERA_OV7670, "OV7670", OV7670_SCCB_ADDR, OV7670_PID, FRAMESIZE_VGA, false},
|
||||
{CAMERA_NT99141, "NT99141", NT99141_SCCB_ADDR, NT99141_PID, FRAMESIZE_HD, true},
|
||||
{CAMERA_GC2145, "GC2145", GC2145_SCCB_ADDR, GC2145_PID, FRAMESIZE_UXGA, false},
|
||||
{CAMERA_GC032A, "GC032A", GC032A_SCCB_ADDR, GC032A_PID, FRAMESIZE_VGA, false},
|
||||
{CAMERA_GC0308, "GC0308", GC0308_SCCB_ADDR, GC0308_PID, FRAMESIZE_VGA, false},
|
||||
{CAMERA_BF3005, "BF3005", BF3005_SCCB_ADDR, BF3005_PID, FRAMESIZE_VGA, false},
|
||||
{CAMERA_BF20A6, "BF20A6", BF20A6_SCCB_ADDR, BF20A6_PID, FRAMESIZE_VGA, false},
|
||||
{CAMERA_SC101IOT, "SC101IOT", SC101IOT_SCCB_ADDR, SC101IOT_PID, FRAMESIZE_HD, false},
|
||||
{CAMERA_SC030IOT, "SC030IOT", SC030IOT_SCCB_ADDR, SC030IOT_PID, FRAMESIZE_VGA, false},
|
||||
{CAMERA_SC031GS, "SC031GS", SC031GS_SCCB_ADDR, SC031GS_PID, FRAMESIZE_VGA, false},
|
||||
{CAMERA_MEGA_CCM, "MEGA_CCM", MEGA_CCM_SCCB_ADDR, MEGA_CCM_PID, FRAMESIZE_5MP, true},
|
||||
};
|
||||
|
||||
const resolution_info_t resolution[FRAMESIZE_INVALID] = {
|
||||
{ 96, 96, ASPECT_RATIO_1X1 }, /* 96x96 */
|
||||
{ 128, 128, ASPECT_RATIO_1X1 }, /* 128x128 */
|
||||
{ 160, 120, ASPECT_RATIO_4X3 }, /* QQVGA */
|
||||
{ 176, 144, ASPECT_RATIO_5X4 }, /* QCIF */
|
||||
{ 240, 176, ASPECT_RATIO_4X3 }, /* HQVGA */
|
||||
{ 240, 240, ASPECT_RATIO_1X1 }, /* 240x240 */
|
||||
{ 320, 240, ASPECT_RATIO_4X3 }, /* QVGA */
|
||||
{ 320, 320, ASPECT_RATIO_1X1 }, /* 320x320 */
|
||||
{ 400, 296, ASPECT_RATIO_4X3 }, /* CIF */
|
||||
{ 480, 320, ASPECT_RATIO_3X2 }, /* HVGA */
|
||||
{ 640, 480, ASPECT_RATIO_4X3 }, /* VGA */
|
||||
{ 800, 600, ASPECT_RATIO_4X3 }, /* SVGA */
|
||||
{ 1024, 768, ASPECT_RATIO_4X3 }, /* XGA */
|
||||
{ 1280, 720, ASPECT_RATIO_16X9 }, /* HD */
|
||||
{ 1280, 1024, ASPECT_RATIO_5X4 }, /* SXGA */
|
||||
{ 1600, 1200, ASPECT_RATIO_4X3 }, /* UXGA */
|
||||
// 3MP Sensors
|
||||
{ 1920, 1080, ASPECT_RATIO_16X9 }, /* FHD */
|
||||
{ 720, 1280, ASPECT_RATIO_9X16 }, /* Portrait HD */
|
||||
{ 864, 1536, ASPECT_RATIO_9X16 }, /* Portrait 3MP */
|
||||
{ 2048, 1536, ASPECT_RATIO_4X3 }, /* QXGA */
|
||||
// 5MP Sensors
|
||||
{ 2560, 1440, ASPECT_RATIO_16X9 }, /* QHD */
|
||||
{ 2560, 1600, ASPECT_RATIO_16X10 }, /* WQXGA */
|
||||
{ 1088, 1920, ASPECT_RATIO_9X16 }, /* Portrait FHD */
|
||||
{ 2560, 1920, ASPECT_RATIO_4X3 }, /* QSXGA */
|
||||
{ 2592, 1944, ASPECT_RATIO_4X3 }, /* 5MP */
|
||||
};
|
||||
|
||||
camera_sensor_info_t *esp_camera_sensor_get_info(sensor_id_t *id)
|
||||
{
|
||||
for (int i = 0; i < CAMERA_MODEL_MAX; i++) {
|
||||
if (id->PID == camera_sensor[i].pid) {
|
||||
return (camera_sensor_info_t *)&camera_sensor[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
Reference in New Issue
Block a user