mirror of
https://github.com/espressif/esp32-camera.git
synced 2025-05-21 09:06:04 +08:00
329 lines
9.4 KiB
C
329 lines
9.4 KiB
C
// 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 "freertos/semphr.h"
|
|
#include "soc/soc.h"
|
|
#include "soc/gpio_sig_map.h"
|
|
#include "soc/i2s_reg.h"
|
|
#include "soc/i2s_struct.h"
|
|
#include "soc/io_mux_reg.h"
|
|
#include "driver/gpio.h"
|
|
#include "driver/rtc_io.h"
|
|
#include "driver/periph_ctrl.h"
|
|
#include "esp_intr_alloc.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 "camera_common.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 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;
|
|
|
|
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
|
|
|
|
static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out_camera_model)
|
|
{
|
|
*out_camera_model = CAMERA_NONE;
|
|
if (s_state != NULL) {
|
|
return ESP_ERR_INVALID_STATE;
|
|
}
|
|
|
|
s_state = (camera_state_t *) calloc(sizeof(*s_state), 1);
|
|
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_sscb_sda != -1) {
|
|
ESP_LOGD(TAG, "Initializing SSCB");
|
|
SCCB_Init(config->pin_sscb_sda, config->pin_sscb_scl);
|
|
}
|
|
|
|
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) {
|
|
CAMERA_DISABLE_OUT_CLOCK();
|
|
return ESP_ERR_CAMERA_NOT_DETECTED;
|
|
}
|
|
|
|
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
|
|
*/
|
|
sensor_id_t *id = &s_state->sensor.id;
|
|
|
|
if (slv_addr == OV2640_SCCB_ADDR) {
|
|
SCCB_Write(slv_addr, 0xFF, 0x01);//bank sensor
|
|
id->PID = SCCB_Read(slv_addr, REG_PID);
|
|
id->VER = SCCB_Read(slv_addr, REG_VER);
|
|
id->MIDL = SCCB_Read(slv_addr, REG_MIDL);
|
|
id->MIDH = SCCB_Read(slv_addr, REG_MIDH);
|
|
} else if (slv_addr == OV5640_SCCB_ADDR || slv_addr == OV3660_SCCB_ADDR) {
|
|
id->PID = SCCB_Read16(slv_addr, REG16_CHIDH);
|
|
id->VER = SCCB_Read16(slv_addr, REG16_CHIDL);
|
|
} else if (slv_addr == NT99141_SCCB_ADDR) {
|
|
SCCB_Write16(slv_addr, 0x3008, 0x01);//bank sensor
|
|
id->PID = SCCB_Read16(slv_addr, 0x3000);
|
|
id->VER = SCCB_Read16(slv_addr, 0x3001);
|
|
if (config->xclk_freq_hz > 10000000) {
|
|
ESP_LOGE(TAG, "NT99141: only XCLK under 10MHz is supported, and XCLK is now set to 10M");
|
|
s_state->sensor.xclk_freq_hz = 10000000;
|
|
}
|
|
}
|
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
|
ESP_LOGI(TAG, "Camera PID=0x%02x VER=0x%02x MIDL=0x%02x MIDH=0x%02x",
|
|
id->PID, id->VER, id->MIDH, id->MIDL);
|
|
|
|
/**
|
|
* Initialize sensor according to sensor ID
|
|
*/
|
|
switch (id->PID) {
|
|
#if CONFIG_OV2640_SUPPORT
|
|
case OV2640_PID:
|
|
*out_camera_model = CAMERA_OV2640;
|
|
ov2640_init(&s_state->sensor);
|
|
break;
|
|
#endif
|
|
#if CONFIG_OV7725_SUPPORT
|
|
case OV7725_PID:
|
|
*out_camera_model = CAMERA_OV7725;
|
|
ov7725_init(&s_state->sensor);
|
|
break;
|
|
#endif
|
|
#if CONFIG_OV3660_SUPPORT
|
|
case OV3660_PID:
|
|
*out_camera_model = CAMERA_OV3660;
|
|
ov3660_init(&s_state->sensor);
|
|
break;
|
|
#endif
|
|
#if CONFIG_OV5640_SUPPORT
|
|
case OV5640_PID:
|
|
*out_camera_model = CAMERA_OV5640;
|
|
ov5640_init(&s_state->sensor);
|
|
break;
|
|
#endif
|
|
#if CONFIG_OV7670_SUPPORT
|
|
case OV7670_PID:
|
|
*out_camera_model = CAMERA_OV7670;
|
|
ov7670_init(&s_state->sensor);
|
|
break;
|
|
#endif
|
|
#if CONFIG_NT99141_SUPPORT
|
|
case NT99141_PID:
|
|
*out_camera_model = CAMERA_NT99141;
|
|
NT99141_init(&s_state->sensor);
|
|
break;
|
|
#endif
|
|
default:
|
|
id->PID = 0;
|
|
CAMERA_DISABLE_OUT_CLOCK();
|
|
ESP_LOGE(TAG, "Detected camera not supported.");
|
|
return ESP_ERR_CAMERA_NOT_SUPPORTED;
|
|
}
|
|
|
|
ESP_LOGD(TAG, "Doing SW reset of sensor");
|
|
s_state->sensor.reset(&s_state->sensor);
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
|
|
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", err);
|
|
goto fail;
|
|
}
|
|
if (camera_model == CAMERA_OV7725) {
|
|
ESP_LOGI(TAG, "Detected OV7725 camera");
|
|
if (config->pixel_format == PIXFORMAT_JPEG) {
|
|
ESP_LOGE(TAG, "Camera does not support JPEG");
|
|
err = ESP_ERR_CAMERA_NOT_SUPPORTED;
|
|
goto fail;
|
|
}
|
|
} else if (camera_model == CAMERA_OV2640) {
|
|
ESP_LOGI(TAG, "Detected OV2640 camera");
|
|
} else if (camera_model == CAMERA_OV3660) {
|
|
ESP_LOGI(TAG, "Detected OV3660 camera");
|
|
} else if (camera_model == CAMERA_OV5640) {
|
|
ESP_LOGI(TAG, "Detected OV5640 camera");
|
|
} else if (camera_model == CAMERA_OV7670) {
|
|
ESP_LOGI(TAG, "Detected OV7670 camera");
|
|
} else if (camera_model == CAMERA_NT99141) {
|
|
ESP_LOGI(TAG, "Detected NT99141 camera");
|
|
} else {
|
|
ESP_LOGI(TAG, "Camera not supported");
|
|
err = ESP_ERR_CAMERA_NOT_SUPPORTED;
|
|
goto fail;
|
|
}
|
|
|
|
framesize_t frame_size = (framesize_t) config->frame_size;
|
|
pixformat_t pix_format = (pixformat_t) config->pixel_format;
|
|
s_state->sensor.status.framesize = frame_size;
|
|
s_state->sensor.pixformat = pix_format;
|
|
// ESP_LOGD(TAG, "Setting frame size to %dx%d", s_state->width, s_state->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 (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:
|
|
free(s_state);
|
|
s_state = NULL;
|
|
CAMERA_DISABLE_OUT_CLOCK();
|
|
return err;
|
|
}
|
|
|
|
camera_fb_t *esp_camera_fb_get()
|
|
{
|
|
if (s_state == NULL) {
|
|
return NULL;
|
|
}
|
|
camera_fb_t *fb = cam_take();
|
|
//set the frame properties
|
|
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;
|
|
}
|