mirror of
https://github.com/espressif/esp32-camera.git
synced 2025-07-04 08:03:17 +08:00
add OV5640 driver
This commit is contained in:
@ -6,6 +6,8 @@ set(COMPONENT_SRCS
|
||||
driver/xclk.c
|
||||
sensors/ov2640.c
|
||||
sensors/ov3660.c
|
||||
sensors/ov5640.c
|
||||
sensors/ov7670.c
|
||||
sensors/ov7725.c
|
||||
conversions/yuv.c
|
||||
conversions/to_jpg.cpp
|
||||
|
15
Kconfig
15
Kconfig
@ -5,28 +5,35 @@ config OV2640_SUPPORT
|
||||
default y
|
||||
help
|
||||
Enable this option if you want to use the OV2640.
|
||||
Disable this option to safe memory.
|
||||
Disable this option to save memory.
|
||||
|
||||
config OV7670_SUPPORT
|
||||
bool "OV7670 Support"
|
||||
default y
|
||||
help
|
||||
Enable this option if you want to use the OV7670.
|
||||
Disable this option to safe memory.
|
||||
Disable this option to save memory.
|
||||
|
||||
config OV7725_SUPPORT
|
||||
bool "OV7725 Support"
|
||||
default n
|
||||
help
|
||||
Enable this option if you want to use the OV7725.
|
||||
Disable this option to safe memory.
|
||||
Disable this option to save memory.
|
||||
|
||||
config OV3660_SUPPORT
|
||||
bool "OV3660 Support"
|
||||
default y
|
||||
help
|
||||
Enable this option if you want to use the OV3360.
|
||||
Disable this option to safe memory.
|
||||
Disable this option to save memory.
|
||||
|
||||
config OV5640_SUPPORT
|
||||
bool "OV5640 Support"
|
||||
default y
|
||||
help
|
||||
Enable this option if you want to use the OV5640.
|
||||
Disable this option to save memory.
|
||||
|
||||
config SCCB_HARDWARE_I2C
|
||||
bool "Use hardware I2C1 for SCCB"
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
## General Information
|
||||
|
||||
This repository hosts ESP32 compatible driver for OV2640, OV3660 and OV7670 image sensors. Additionally it provides a few tools, which allow converting the captured frame data to the more common BMP and JPEG formats.
|
||||
This repository hosts ESP32 compatible driver for OV2640, OV3660, OV5640 and OV7670 image sensors. Additionally it provides a few tools, which allow converting the captured frame data to the more common BMP and JPEG formats.
|
||||
|
||||
## Important to Remember
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include "esp_jpg_decode.h"
|
||||
|
||||
#include "esp_system.h"
|
||||
#ifdef ESP_IDF_VERSION_MAJOR // IDF 4+
|
||||
#if ESP_IDF_VERSION_MAJOR >= 4 // IDF 4+
|
||||
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
|
||||
#include "esp32/rom/tjpgd.h"
|
||||
#else
|
||||
|
@ -21,7 +21,7 @@
|
||||
#include "esp_jpg_decode.h"
|
||||
|
||||
#include "esp_system.h"
|
||||
#ifdef ESP_IDF_VERSION_MAJOR // IDF 4+
|
||||
#if ESP_IDF_VERSION_MAJOR >= 4
|
||||
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
|
||||
#include "esp32/spiram.h"
|
||||
#else
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include "yuv.h"
|
||||
|
||||
#include "esp_system.h"
|
||||
#ifdef ESP_IDF_VERSION_MAJOR // IDF 4+
|
||||
#if ESP_IDF_VERSION_MAJOR >= 4 // IDF 4+
|
||||
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
|
||||
#include "esp32/spiram.h"
|
||||
#else
|
||||
|
104
driver/camera.c
104
driver/camera.c
@ -45,6 +45,9 @@
|
||||
#if CONFIG_OV3660_SUPPORT
|
||||
#include "ov3660.h"
|
||||
#endif
|
||||
#if CONFIG_OV5640_SUPPORT
|
||||
#include "ov5640.h"
|
||||
#endif
|
||||
#if CONFIG_OV7670_SUPPORT
|
||||
#include "ov7670.h"
|
||||
#endif
|
||||
@ -55,6 +58,7 @@ typedef enum {
|
||||
CAMERA_OV7725 = 7725,
|
||||
CAMERA_OV2640 = 2640,
|
||||
CAMERA_OV3660 = 3660,
|
||||
CAMERA_OV5640 = 5640,
|
||||
CAMERA_OV7670 = 7670,
|
||||
} camera_model_t;
|
||||
|
||||
@ -84,6 +88,7 @@ typedef struct camera_fb_s {
|
||||
size_t width;
|
||||
size_t height;
|
||||
pixformat_t format;
|
||||
struct timeval timestamp;
|
||||
size_t size;
|
||||
uint8_t ref;
|
||||
uint8_t bad;
|
||||
@ -550,6 +555,7 @@ static void IRAM_ATTR signal_dma_buf_received(bool* need_yield)
|
||||
}
|
||||
//ESP_EARLY_LOGW(TAG, "qsf:%d", s_state->dma_received_count);
|
||||
//ets_printf("qsf:%d\n", s_state->dma_received_count);
|
||||
//ets_printf("qovf\n");
|
||||
}
|
||||
*need_yield = (ret == pdTRUE && higher_priority_task_woken == pdTRUE);
|
||||
}
|
||||
@ -581,6 +587,7 @@ static void IRAM_ATTR vsync_isr(void* arg)
|
||||
if(s_state->dma_filtered_count > 1 || s_state->fb->bad || s_state->config.fb_count > 1) {
|
||||
i2s_stop(&need_yield);
|
||||
}
|
||||
//ets_printf("vs\n");
|
||||
}
|
||||
if(s_state->config.fb_count > 1 || s_state->dma_filtered_count < 2) {
|
||||
I2S0.conf.rx_start = 0;
|
||||
@ -673,6 +680,7 @@ static void IRAM_ATTR dma_finish_frame()
|
||||
if(s_state->config.fb_count == 1) {
|
||||
i2s_start_bus();
|
||||
}
|
||||
//ets_printf("bad\n");
|
||||
} else {
|
||||
s_state->fb->len = s_state->dma_filtered_count * buf_len;
|
||||
if(s_state->fb->len) {
|
||||
@ -699,6 +707,8 @@ static void IRAM_ATTR dma_finish_frame()
|
||||
} else if(s_state->config.fb_count == 1){
|
||||
//frame was empty?
|
||||
i2s_start_bus();
|
||||
} else {
|
||||
//ets_printf("empty\n");
|
||||
}
|
||||
}
|
||||
} else if(s_state->fb->len) {
|
||||
@ -732,15 +742,19 @@ static void IRAM_ATTR dma_filter_buffer(size_t buf_idx)
|
||||
if(s_state->sensor.pixformat == PIXFORMAT_JPEG) {
|
||||
uint32_t sig = *((uint32_t *)s_state->fb->buf) & 0xFFFFFF;
|
||||
if(sig != 0xffd8ff) {
|
||||
//ets_printf("bad header\n");
|
||||
ets_printf("bh 0x%08x\n", sig);
|
||||
s_state->fb->bad = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
//set the frame properties
|
||||
s_state->fb->width = resolution[s_state->sensor.status.framesize][0];
|
||||
s_state->fb->height = resolution[s_state->sensor.status.framesize][1];
|
||||
s_state->fb->width = resolution[s_state->sensor.status.framesize].width;
|
||||
s_state->fb->height = resolution[s_state->sensor.status.framesize].height;
|
||||
s_state->fb->format = s_state->sensor.pixformat;
|
||||
|
||||
uint64_t us = (uint64_t)esp_timer_get_time();
|
||||
s_state->fb->timestamp.tv_sec = us / 1000000UL;
|
||||
s_state->fb->timestamp.tv_usec = us % 1000000UL;
|
||||
}
|
||||
s_state->dma_filtered_count++;
|
||||
}
|
||||
@ -976,13 +990,6 @@ esp_err_t camera_probe(const camera_config_t* config, camera_model_t* out_camera
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
gpio_set_level(config->pin_reset, 1);
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
#if (CONFIG_OV2640_SUPPORT && !CONFIG_OV3660_SUPPORT)
|
||||
} else {
|
||||
//reset OV2640
|
||||
SCCB_Write(0x30, 0xFF, 0x01);//bank sensor
|
||||
SCCB_Write(0x30, 0x12, 0x80);//reset
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
#endif
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Searching for camera address");
|
||||
@ -993,14 +1000,12 @@ esp_err_t camera_probe(const camera_config_t* config, camera_model_t* out_camera
|
||||
camera_disable_out_clock();
|
||||
return ESP_ERR_CAMERA_NOT_DETECTED;
|
||||
}
|
||||
s_state->sensor.slv_addr = slv_addr;
|
||||
s_state->sensor.xclk_freq_hz = config->xclk_freq_hz;
|
||||
|
||||
//s_state->sensor.slv_addr = 0x30;
|
||||
ESP_LOGD(TAG, "Detected camera at address=0x%02x", s_state->sensor.slv_addr);
|
||||
|
||||
//slv_addr = 0x30;
|
||||
ESP_LOGD(TAG, "Detected camera at address=0x%02x", slv_addr);
|
||||
sensor_id_t* id = &s_state->sensor.id;
|
||||
|
||||
#if (CONFIG_OV2640_SUPPORT && CONFIG_OV3660_SUPPORT)
|
||||
#if CONFIG_OV2640_SUPPORT
|
||||
if (slv_addr == 0x30) {
|
||||
ESP_LOGD(TAG, "Resetting OV2640");
|
||||
//camera might be OV2640. try to reset it
|
||||
@ -1011,7 +1016,10 @@ esp_err_t camera_probe(const camera_config_t* config, camera_model_t* out_camera
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_OV3660_SUPPORT
|
||||
s_state->sensor.slv_addr = slv_addr;
|
||||
s_state->sensor.xclk_freq_hz = config->xclk_freq_hz;
|
||||
|
||||
#if (CONFIG_OV3660_SUPPORT || CONFIG_OV5640_SUPPORT)
|
||||
if(s_state->sensor.slv_addr == 0x3c){
|
||||
id->PID = SCCB_Read16(s_state->sensor.slv_addr, REG16_CHIDH);
|
||||
id->VER = SCCB_Read16(s_state->sensor.slv_addr, REG16_CHIDL);
|
||||
@ -1026,7 +1034,8 @@ esp_err_t camera_probe(const camera_config_t* config, camera_model_t* out_camera
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
ESP_LOGD(TAG, "Camera PID=0x%02x VER=0x%02x MIDL=0x%02x MIDH=0x%02x",
|
||||
id->PID, id->VER, id->MIDH, id->MIDL);
|
||||
#if CONFIG_OV3660_SUPPORT
|
||||
|
||||
#if (CONFIG_OV3660_SUPPORT || CONFIG_OV5640_SUPPORT)
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1050,6 +1059,12 @@ esp_err_t camera_probe(const camera_config_t* config, camera_model_t* out_camera
|
||||
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;
|
||||
@ -1082,12 +1097,53 @@ esp_err_t camera_init(const camera_config_t* config)
|
||||
esp_err_t err = ESP_OK;
|
||||
framesize_t frame_size = (framesize_t) config->frame_size;
|
||||
pixformat_t pix_format = (pixformat_t) config->pixel_format;
|
||||
s_state->width = resolution[frame_size][0];
|
||||
s_state->height = resolution[frame_size][1];
|
||||
|
||||
switch (s_state->sensor.id.PID) {
|
||||
#if CONFIG_OV2640_SUPPORT
|
||||
case OV2640_PID:
|
||||
if (frame_size > FRAMESIZE_UXGA) {
|
||||
frame_size = FRAMESIZE_UXGA;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#if CONFIG_OV7725_SUPPORT
|
||||
case OV7725_PID:
|
||||
if (frame_size > FRAMESIZE_VGA) {
|
||||
frame_size = FRAMESIZE_VGA;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#if CONFIG_OV3660_SUPPORT
|
||||
case OV3660_PID:
|
||||
if (frame_size > FRAMESIZE_QXGA) {
|
||||
frame_size = FRAMESIZE_QXGA;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#if CONFIG_OV5640_SUPPORT
|
||||
case OV5640_PID:
|
||||
if (frame_size > FRAMESIZE_QSXGA) {
|
||||
frame_size = FRAMESIZE_QSXGA;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#if CONFIG_OV7670_SUPPORT
|
||||
case OV7725_PID:
|
||||
if (frame_size > FRAMESIZE_VGA) {
|
||||
frame_size = FRAMESIZE_VGA;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return ESP_ERR_CAMERA_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
s_state->width = resolution[frame_size].width;
|
||||
s_state->height = resolution[frame_size].height;
|
||||
|
||||
if (pix_format == PIXFORMAT_GRAYSCALE) {
|
||||
s_state->fb_size = s_state->width * s_state->height;
|
||||
if (s_state->sensor.id.PID == OV3660_PID) {
|
||||
if ((s_state->sensor.id.PID == OV3660_PID) || (s_state->sensor.id.PID == OV5640_PID)) {
|
||||
if (is_hs_mode()) {
|
||||
s_state->sampling_mode = SM_0A00_0B00;
|
||||
s_state->dma_filter = &dma_filter_yuyv_highspeed;
|
||||
@ -1138,8 +1194,8 @@ esp_err_t camera_init(const camera_config_t* config)
|
||||
s_state->in_bytes_per_pixel = 2; // camera sends RGB565
|
||||
s_state->fb_bytes_per_pixel = 3; // frame buffer stores RGB888
|
||||
} else if (pix_format == PIXFORMAT_JPEG) {
|
||||
if (s_state->sensor.id.PID != OV2640_PID && s_state->sensor.id.PID != OV3660_PID) {
|
||||
ESP_LOGE(TAG, "JPEG format is only supported for ov2640 and ov3660");
|
||||
if (s_state->sensor.id.PID != OV2640_PID && s_state->sensor.id.PID != OV3660_PID && s_state->sensor.id.PID != OV5640_PID) {
|
||||
ESP_LOGE(TAG, "JPEG format is only supported for ov2640, ov3660 and ov5640");
|
||||
err = ESP_ERR_NOT_SUPPORTED;
|
||||
goto fail;
|
||||
}
|
||||
@ -1286,6 +1342,8 @@ esp_err_t esp_camera_init(const camera_config_t* config)
|
||||
ESP_LOGD(TAG, "Detected OV2640 camera");
|
||||
} else if (camera_model == CAMERA_OV3660) {
|
||||
ESP_LOGD(TAG, "Detected OV3660 camera");
|
||||
} else if (camera_model == CAMERA_OV5640) {
|
||||
ESP_LOGD(TAG, "Detected OV5640 camera");
|
||||
} else if (camera_model == CAMERA_OV7670) {
|
||||
ESP_LOGD(TAG, "Detected OV7670 camera");
|
||||
if(config->pixel_format == PIXFORMAT_JPEG) {
|
||||
|
@ -115,6 +115,7 @@ typedef struct {
|
||||
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
|
||||
|
@ -9,11 +9,14 @@
|
||||
#ifndef __SENSOR_H__
|
||||
#define __SENSOR_H__
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define OV9650_PID (0x96)
|
||||
#define OV2640_PID (0x26)
|
||||
#define OV7725_PID (0x77)
|
||||
#define OV2640_PID (0x26)
|
||||
#define OV3660_PID (0x36)
|
||||
#define OV5640_PID (0x56)
|
||||
#define OV7670_PID (0x76)
|
||||
|
||||
typedef enum {
|
||||
@ -28,23 +31,39 @@ typedef enum {
|
||||
} pixformat_t;
|
||||
|
||||
typedef enum {
|
||||
FRAMESIZE_96x96, // 96x96
|
||||
FRAMESIZE_96X96, // 96x96
|
||||
FRAMESIZE_QQVGA, // 160x120
|
||||
FRAMESIZE_QQVGA2, // 128x160
|
||||
FRAMESIZE_QCIF, // 176x144
|
||||
FRAMESIZE_HQVGA, // 240x176
|
||||
FRAMESIZE_240x240, // 240x240
|
||||
FRAMESIZE_240X240, // 240x240
|
||||
FRAMESIZE_QVGA, // 320x240
|
||||
FRAMESIZE_CIF, // 400x296
|
||||
FRAMESIZE_HVGA, // 480x320
|
||||
FRAMESIZE_VGA, // 640x480
|
||||
FRAMESIZE_SVGA, // 800x600
|
||||
FRAMESIZE_XGA, // 1024x768
|
||||
FRAMESIZE_HD, // 1280x720
|
||||
FRAMESIZE_SXGA, // 1280x1024
|
||||
FRAMESIZE_UXGA, // 1600x1200
|
||||
FRAMESIZE_QXGA, // 2048*1536
|
||||
FRAMESIZE_FHD, // 1920x1080
|
||||
FRAMESIZE_QXGA, // 2048x1536
|
||||
FRAMESIZE_QHD, // 2560x1440
|
||||
FRAMESIZE_WQXGA, // 2560x1600
|
||||
FRAMESIZE_QSXGA, // 2560x1920
|
||||
FRAMESIZE_INVALID
|
||||
} framesize_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_t;
|
||||
|
||||
typedef enum {
|
||||
GAINCEILING_2X,
|
||||
GAINCEILING_4X,
|
||||
@ -55,6 +74,28 @@ typedef enum {
|
||||
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[];
|
||||
|
||||
typedef struct {
|
||||
uint8_t MIDH;
|
||||
uint8_t MIDL;
|
||||
@ -133,9 +174,12 @@ typedef struct _sensor {
|
||||
|
||||
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 reg4520, int incrementX, int incrementY, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, int reg20, int reg21, int reg4514);
|
||||
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;
|
||||
|
||||
// Resolution table (in camera.c)
|
||||
extern const int resolution[][2];
|
||||
|
||||
#endif /* __SENSOR_H__ */
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "sensor.h"
|
||||
|
||||
#include "esp_system.h"
|
||||
#ifdef ESP_IDF_VERSION_MAJOR // IDF 4+
|
||||
#if ESP_IDF_VERSION_MAJOR >= 4 // IDF 4+
|
||||
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
|
||||
#include "esp32/rom/lldesc.h"
|
||||
#else
|
||||
|
@ -19,6 +19,8 @@
|
||||
static const char* TAG = "sccb";
|
||||
#endif
|
||||
|
||||
//#undef CONFIG_SCCB_HARDWARE_I2C
|
||||
|
||||
#define LITTLETOBIG(x) ((x<<8)|(x>>8))
|
||||
|
||||
#ifdef CONFIG_SCCB_HARDWARE_I2C
|
||||
|
@ -1,17 +1,25 @@
|
||||
#include "sensor.h"
|
||||
|
||||
const int resolution[][2] = {
|
||||
{ 96, 96 }, /* 96x96 */
|
||||
{ 160, 120 }, /* QQVGA */
|
||||
{ 128, 160 }, /* QQVGA2*/
|
||||
{ 176, 144 }, /* QCIF */
|
||||
{ 240, 176 }, /* HQVGA */
|
||||
{ 240, 240 }, /* 240x240 */
|
||||
{ 320, 240 }, /* QVGA */
|
||||
{ 400, 296 }, /* CIF */
|
||||
{ 640, 480 }, /* VGA */
|
||||
{ 800, 600 }, /* SVGA */
|
||||
{ 1024, 768 }, /* XGA */
|
||||
{ 1280, 1024 }, /* SXGA */
|
||||
{ 1600, 1200 }, /* UXGA */
|
||||
{ 2048, 1536 }, /* QXGA */
|
||||
const resolution_info_t resolution[FRAMESIZE_INVALID] = {
|
||||
{ 96, 96, ASPECT_RATIO_1X1 }, /* 96x96 */
|
||||
{ 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 */
|
||||
{ 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 */
|
||||
{ 2048, 1536, ASPECT_RATIO_4X3 }, /* QXGA */
|
||||
// 5MP Sensors
|
||||
{ 2560, 1440, ASPECT_RATIO_16X9 }, /* QHD */
|
||||
{ 2560, 1600, ASPECT_RATIO_16X10 }, /* WQXGA */
|
||||
{ 2560, 1920, ASPECT_RATIO_4X3 }, /* QSXGA */
|
||||
};
|
||||
|
@ -12,19 +12,28 @@
|
||||
static const char* TAG = "camera_xclk";
|
||||
#endif
|
||||
|
||||
esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz)
|
||||
{
|
||||
ledc_timer_config_t timer_conf;
|
||||
timer_conf.duty_resolution = 2;
|
||||
timer_conf.freq_hz = xclk_freq_hz;
|
||||
timer_conf.speed_mode = LEDC_HIGH_SPEED_MODE;
|
||||
#if ESP_IDF_VERSION_MAJOR >= 4
|
||||
timer_conf.clk_cfg = LEDC_AUTO_CLK;
|
||||
#endif
|
||||
timer_conf.timer_num = (ledc_timer_t)ledc_timer;
|
||||
esp_err_t err = ledc_timer_config(&timer_conf);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "ledc_timer_config failed for freq %d, rc=%x", xclk_freq_hz, err);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t camera_enable_out_clock(camera_config_t* config)
|
||||
{
|
||||
periph_module_enable(PERIPH_LEDC_MODULE);
|
||||
|
||||
ledc_timer_config_t timer_conf = {};
|
||||
timer_conf.duty_resolution = 2;
|
||||
timer_conf.freq_hz = config->xclk_freq_hz;
|
||||
timer_conf.speed_mode = LEDC_HIGH_SPEED_MODE;
|
||||
timer_conf.timer_num = config->ledc_timer;
|
||||
#ifdef ESP_IDF_VERSION_MAJOR
|
||||
timer_conf.clk_cfg = LEDC_AUTO_CLK;
|
||||
#endif
|
||||
esp_err_t err = ledc_timer_config(&timer_conf);
|
||||
esp_err_t err = xclk_timer_conf(config->ledc_timer, config->xclk_freq_hz);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "ledc_timer_config failed, rc=%x", err);
|
||||
return err;
|
||||
|
@ -225,8 +225,8 @@ int set_image_size(sensor_t *sensor, uint16_t width, uint16_t height)
|
||||
static int set_framesize(sensor_t *sensor, framesize_t framesize)
|
||||
{
|
||||
int ret = 0;
|
||||
uint16_t w = resolution[framesize][0];
|
||||
uint16_t h = resolution[framesize][1];
|
||||
uint16_t w = resolution[framesize].width;
|
||||
uint16_t h = resolution[framesize].height;
|
||||
const uint8_t (*regs)[2];
|
||||
|
||||
sensor->status.framesize = framesize;
|
||||
|
122
sensors/ov3660.c
122
sensors/ov3660.c
@ -124,7 +124,7 @@ static int write_addr_reg(uint8_t slv_addr, const uint16_t reg, uint16_t x_value
|
||||
|
||||
#define write_reg_bits(slv_addr, reg, mask, enable) set_reg_bits(slv_addr, reg, 0, mask, enable?mask:0)
|
||||
|
||||
int calc_sysclk(int xclk, bool pll_bypass, int pll_multiplier, int pll_sys_div, int pll_pre_div, bool pll_root_2x, int pll_seld5, bool pclk_manual, int pclk_div)
|
||||
static int calc_sysclk(int xclk, bool pll_bypass, int pll_multiplier, int pll_sys_div, int pll_pre_div, bool pll_root_2x, int pll_seld5, bool pclk_manual, int pclk_div)
|
||||
{
|
||||
const int pll_pre_div2x_map[] = { 2, 3, 4, 6 };//values are multiplied by two to avoid floats
|
||||
const int pll_seld52x_map[] = { 2, 2, 4, 5 };
|
||||
@ -246,18 +246,24 @@ static int set_image_options(sensor_t *sensor)
|
||||
uint8_t reg4514 = 0;
|
||||
uint8_t reg4514_test = 0;
|
||||
|
||||
uint16_t w = resolution[sensor->status.framesize].width;
|
||||
uint16_t h = resolution[sensor->status.framesize].height;
|
||||
aspect_ratio_t ratio = resolution[sensor->status.framesize].aspect_ratio;
|
||||
ratio_settings_t settings = ratio_table[ratio];
|
||||
bool bining = (w <= (settings.max_width / 2) && h <= (settings.max_height / 2));
|
||||
|
||||
// compression
|
||||
if (sensor->pixformat == PIXFORMAT_JPEG) {
|
||||
reg21 |= 0x20;
|
||||
}
|
||||
|
||||
// binning
|
||||
if (sensor->status.framesize > FRAMESIZE_SVGA) {
|
||||
reg20 |= 0x40;
|
||||
} else {
|
||||
if (bining) {
|
||||
reg20 |= 0x01;
|
||||
reg21 |= 0x01;
|
||||
reg4514_test |= 4;
|
||||
} else {
|
||||
reg20 |= 0x40;
|
||||
}
|
||||
|
||||
// V-Flip
|
||||
@ -293,7 +299,7 @@ static int set_image_options(sensor_t *sensor)
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Set Image Options: Compression: %u, Binning: %u, V-Flip: %u, H-Mirror: %u, Reg-4514: 0x%02x",
|
||||
sensor->pixformat == PIXFORMAT_JPEG, sensor->status.framesize <= FRAMESIZE_SVGA, sensor->status.vflip, sensor->status.hmirror, reg4514);
|
||||
sensor->pixformat == PIXFORMAT_JPEG, bining, sensor->status.vflip, sensor->status.hmirror, reg4514);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -303,51 +309,48 @@ static int set_framesize(sensor_t *sensor, framesize_t framesize)
|
||||
framesize_t old_framesize = sensor->status.framesize;
|
||||
sensor->status.framesize = framesize;
|
||||
|
||||
if(framesize >= FRAMESIZE_INVALID){
|
||||
if(framesize > FRAMESIZE_QXGA){
|
||||
ESP_LOGE(TAG, "Invalid framesize: %u", framesize);
|
||||
return -1;
|
||||
}
|
||||
uint16_t w = resolution[framesize][0];
|
||||
uint16_t h = resolution[framesize][1];
|
||||
uint16_t w = resolution[framesize].width;
|
||||
uint16_t h = resolution[framesize].height;
|
||||
aspect_ratio_t ratio = resolution[sensor->status.framesize].aspect_ratio;
|
||||
ratio_settings_t settings = ratio_table[ratio];
|
||||
bool bining = (w <= (settings.max_width / 2) && h <= (settings.max_height / 2));
|
||||
|
||||
if (framesize > FRAMESIZE_SVGA) {
|
||||
ret = write_reg(sensor->slv_addr, 0x4520, 0xb0)
|
||||
|| write_reg(sensor->slv_addr, X_INCREMENT, 0x11)//odd:1, even: 1
|
||||
|| write_reg(sensor->slv_addr, Y_INCREMENT, 0x11);//odd:1, even: 1
|
||||
} else {
|
||||
if (bining) {
|
||||
ret = write_reg(sensor->slv_addr, 0x4520, 0x0b)
|
||||
|| write_reg(sensor->slv_addr, X_INCREMENT, 0x31)//odd:3, even: 1
|
||||
|| write_reg(sensor->slv_addr, Y_INCREMENT, 0x31);//odd:3, even: 1
|
||||
} else {
|
||||
ret = write_reg(sensor->slv_addr, 0x4520, 0xb0)
|
||||
|| write_reg(sensor->slv_addr, X_INCREMENT, 0x11)//odd:1, even: 1
|
||||
|| write_reg(sensor->slv_addr, Y_INCREMENT, 0x11);//odd:1, even: 1
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = write_addr_reg(sensor->slv_addr, X_ADDR_ST_H, 0, 0)
|
||||
|| write_addr_reg(sensor->slv_addr, X_ADDR_END_H, 2079, 1547)
|
||||
ret = write_addr_reg(sensor->slv_addr, X_ADDR_ST_H, settings.start_x, settings.start_y)
|
||||
|| write_addr_reg(sensor->slv_addr, X_ADDR_END_H, settings.end_x, settings.end_y)
|
||||
|| write_addr_reg(sensor->slv_addr, X_OUTPUT_SIZE_H, w, h);
|
||||
|
||||
if (ret) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (framesize > FRAMESIZE_SVGA) {
|
||||
ret = write_addr_reg(sensor->slv_addr, X_TOTAL_SIZE_H, 2300, 1564)
|
||||
|| write_addr_reg(sensor->slv_addr, X_OFFSET_H, 16, 6);
|
||||
if (bining) {
|
||||
ret = write_addr_reg(sensor->slv_addr, X_TOTAL_SIZE_H, settings.total_x, (settings.total_y / 2) + 1)
|
||||
|| write_addr_reg(sensor->slv_addr, X_OFFSET_H, 8, 2);
|
||||
} else {
|
||||
if (framesize == FRAMESIZE_SVGA) {
|
||||
ret = write_addr_reg(sensor->slv_addr, X_TOTAL_SIZE_H, 2300, 788);
|
||||
} else {
|
||||
ret = write_addr_reg(sensor->slv_addr, X_TOTAL_SIZE_H, 2050, 788);
|
||||
}
|
||||
if (ret == 0) {
|
||||
ret = write_addr_reg(sensor->slv_addr, X_OFFSET_H, 8, 2);
|
||||
}
|
||||
ret = write_addr_reg(sensor->slv_addr, X_TOTAL_SIZE_H, settings.total_x, settings.total_y)
|
||||
|| write_addr_reg(sensor->slv_addr, X_OFFSET_H, 16, 6);
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
ret = write_reg_bits(sensor->slv_addr, ISP_CONTROL_01, 0x20, framesize != FRAMESIZE_QXGA);
|
||||
ret = write_reg_bits(sensor->slv_addr, ISP_CONTROL_01, 0x20, !(w == settings.max_width && h == settings.max_height));
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
@ -880,6 +883,65 @@ static int set_denoise(sensor_t *sensor, int level)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int get_reg(sensor_t *sensor, int reg, int mask){
|
||||
int ret = 0;
|
||||
if(mask > 0xFF){
|
||||
ret = read_reg16(sensor->slv_addr, reg);
|
||||
} else {
|
||||
ret = read_reg(sensor->slv_addr, reg);
|
||||
}
|
||||
if(ret > 0){
|
||||
ret &= mask;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sensor_set_reg(sensor_t *sensor, int reg, int mask, int value){
|
||||
return set_reg_bits(sensor->slv_addr, reg & 0xffff, 0, mask & 0xff, value & 0xff);
|
||||
}
|
||||
|
||||
static int set_res_raw(sensor_t *sensor, int reg4520, int incrementX, int incrementY, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, int reg20, int reg21, int reg4514)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if(outputX > 800 || outputY > 600){
|
||||
reg4520 = 0xb0;
|
||||
incrementX = 0x11;
|
||||
incrementY = 0x11;
|
||||
} else {
|
||||
reg4520 = 0x0b;
|
||||
incrementX = 0x31;
|
||||
incrementY = 0x31;
|
||||
}
|
||||
|
||||
ret = write_reg(sensor->slv_addr, 0x4520, reg4520)
|
||||
|| write_reg(sensor->slv_addr, X_INCREMENT, incrementX)//odd:1, even: 1
|
||||
|| write_reg(sensor->slv_addr, Y_INCREMENT, incrementY)//odd:1, even: 1
|
||||
|| write_addr_reg(sensor->slv_addr, X_ADDR_ST_H, startX, startY)
|
||||
|| write_addr_reg(sensor->slv_addr, X_ADDR_END_H, endX, endY)
|
||||
|| write_addr_reg(sensor->slv_addr, X_OFFSET_H, offsetX, offsetY)
|
||||
|| write_addr_reg(sensor->slv_addr, X_TOTAL_SIZE_H, totalX, totalY)
|
||||
|| write_addr_reg(sensor->slv_addr, X_OUTPUT_SIZE_H, outputX, outputY)
|
||||
|| write_reg_bits(sensor->slv_addr, ISP_CONTROL_01, 0x20, scale);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _set_pll(sensor_t *sensor, int bypass, int multiplier, int sys_div, int root_2x, int pre_div, int seld5, int pclk_manual, int pclk_div)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = set_pll(sensor, bypass > 0, multiplier, sys_div, pre_div, root_2x > 0, seld5, pclk_manual > 0, pclk_div);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz);
|
||||
static int set_xclk(sensor_t *sensor, int timer, int xclk)
|
||||
{
|
||||
int ret = 0;
|
||||
sensor->xclk_freq_hz = xclk * 1000000U;
|
||||
ret = xclk_timer_conf(timer, sensor->xclk_freq_hz);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int init_status(sensor_t *sensor)
|
||||
{
|
||||
sensor->status.brightness = 0;
|
||||
@ -941,5 +1003,11 @@ int ov3660_init(sensor_t *sensor)
|
||||
sensor->set_raw_gma = set_raw_gma_dsp;
|
||||
sensor->set_lenc = set_lenc_dsp;
|
||||
sensor->set_denoise = set_denoise;
|
||||
|
||||
sensor->get_reg = get_reg;
|
||||
sensor->set_reg = sensor_set_reg;
|
||||
sensor->set_res_raw = set_res_raw;
|
||||
sensor->set_pll = _set_pll;
|
||||
sensor->set_xclk = set_xclk;
|
||||
return 0;
|
||||
}
|
||||
|
@ -176,8 +176,8 @@ static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
|
||||
static int set_framesize(sensor_t *sensor, framesize_t framesize)
|
||||
{
|
||||
int ret=0;
|
||||
uint16_t w = resolution[framesize][0];
|
||||
uint16_t h = resolution[framesize][1];
|
||||
uint16_t w = resolution[framesize].width;
|
||||
uint16_t h = resolution[framesize].height;
|
||||
uint8_t reg = SCCB_Read(sensor->slv_addr, COM7);
|
||||
|
||||
sensor->status.framesize = framesize;
|
||||
|
@ -6,10 +6,22 @@
|
||||
#include "esp_attr.h"
|
||||
#include "ov3660_regs.h"
|
||||
|
||||
static const ratio_settings_t ratio_table[] = {
|
||||
// mw, mh, sx, sy, ex, ey, ox, oy, tx, ty
|
||||
{ 2048, 1536, 0, 0, 2079, 1547, 16, 6, 2300, 1564 }, //4x3
|
||||
{ 1920, 1280, 64, 128, 2015, 1419, 16, 6, 2172, 1436 }, //3x2
|
||||
{ 2048, 1280, 0, 128, 2079, 1419, 16, 6, 2300, 1436 }, //16x10
|
||||
{ 1920, 1152, 64, 192, 2015, 1355, 16, 6, 2172, 1372 }, //5x3
|
||||
{ 1920, 1080, 64, 242, 2015, 1333, 16, 6, 2172, 1322 }, //16x9
|
||||
{ 2048, 880, 0, 328, 2079, 1219, 16, 6, 2300, 1236 }, //21x9
|
||||
{ 1920, 1536, 64, 0, 2015, 1547, 16, 6, 2172, 1564 }, //5x4
|
||||
{ 1536, 1536, 256, 0, 1823, 1547, 16, 6, 2044, 1564 } //1x1
|
||||
};
|
||||
|
||||
#define REG_DLY 0xffff
|
||||
#define REGLIST_TAIL 0x0000
|
||||
|
||||
const DRAM_ATTR uint16_t sensor_default_regs[][2] = {
|
||||
static const DRAM_ATTR uint16_t sensor_default_regs[][2] = {
|
||||
{SYSTEM_CTROL0, 0x82}, // software reset
|
||||
{REG_DLY, 10}, // delay 10ms
|
||||
|
||||
@ -131,22 +143,23 @@ const DRAM_ATTR uint16_t sensor_default_regs[][2] = {
|
||||
{0x538a, 0x01},
|
||||
{0x538b, 0x98},
|
||||
|
||||
{0x5481, 0x05},
|
||||
{0x5482, 0x09},
|
||||
{0x5483, 0x10},
|
||||
{0x5484, 0x3a},
|
||||
{0x5485, 0x4c},
|
||||
{0x5486, 0x5a},
|
||||
{0x5487, 0x68},
|
||||
{0x5488, 0x74},
|
||||
{0x5489, 0x80},
|
||||
{0x548a, 0x8e},
|
||||
{0x548b, 0xa4},
|
||||
{0x548c, 0xb4},
|
||||
{0x548d, 0xc8},
|
||||
{0x548e, 0xde},
|
||||
{0x548f, 0xf0},
|
||||
{0x5490, 0x15},
|
||||
{0x5480, 0x01},
|
||||
// {0x5481, 0x05},
|
||||
// {0x5482, 0x09},
|
||||
// {0x5483, 0x10},
|
||||
// {0x5484, 0x3a},
|
||||
// {0x5485, 0x4c},
|
||||
// {0x5486, 0x5a},
|
||||
// {0x5487, 0x68},
|
||||
// {0x5488, 0x74},
|
||||
// {0x5489, 0x80},
|
||||
// {0x548a, 0x8e},
|
||||
// {0x548b, 0xa4},
|
||||
// {0x548c, 0xb4},
|
||||
// {0x548d, 0xc8},
|
||||
// {0x548e, 0xde},
|
||||
// {0x548f, 0xf0},
|
||||
// {0x5490, 0x15},
|
||||
|
||||
{0x5000, 0xa7},
|
||||
{0x5800, 0x0C},
|
||||
@ -247,7 +260,7 @@ const DRAM_ATTR uint16_t sensor_default_regs[][2] = {
|
||||
{REGLIST_TAIL, 0x00}, // tail
|
||||
};
|
||||
|
||||
const DRAM_ATTR uint16_t sensor_fmt_jpeg[][2] = {
|
||||
static const DRAM_ATTR uint16_t sensor_fmt_jpeg[][2] = {
|
||||
{FORMAT_CTRL, 0x00}, // YUV422
|
||||
{FORMAT_CTRL00, 0x30}, // YUYV
|
||||
{0x3002, 0x00},//0x1c to 0x00 !!!
|
||||
@ -256,30 +269,30 @@ const DRAM_ATTR uint16_t sensor_fmt_jpeg[][2] = {
|
||||
{REGLIST_TAIL, 0x00}, // tail
|
||||
};
|
||||
|
||||
const DRAM_ATTR uint16_t sensor_fmt_raw[][2] = {
|
||||
static const DRAM_ATTR uint16_t sensor_fmt_raw[][2] = {
|
||||
{FORMAT_CTRL00, 0x00}, // RAW
|
||||
{REGLIST_TAIL, 0x00}
|
||||
};
|
||||
|
||||
const DRAM_ATTR uint16_t sensor_fmt_grayscale[][2] = {
|
||||
static const DRAM_ATTR uint16_t sensor_fmt_grayscale[][2] = {
|
||||
{FORMAT_CTRL, 0x00}, // YUV422
|
||||
{FORMAT_CTRL00, 0x10}, // Y8
|
||||
{REGLIST_TAIL, 0x00}
|
||||
};
|
||||
|
||||
const DRAM_ATTR uint16_t sensor_fmt_yuv422[][2] = {
|
||||
static const DRAM_ATTR uint16_t sensor_fmt_yuv422[][2] = {
|
||||
{FORMAT_CTRL, 0x00}, // YUV422
|
||||
{FORMAT_CTRL00, 0x30}, // YUYV
|
||||
{REGLIST_TAIL, 0x00}
|
||||
};
|
||||
|
||||
const DRAM_ATTR uint16_t sensor_fmt_rgb565[][2] = {
|
||||
static const DRAM_ATTR uint16_t sensor_fmt_rgb565[][2] = {
|
||||
{FORMAT_CTRL, 0x01}, // RGB
|
||||
{FORMAT_CTRL00, 0x61}, // RGB565 (BGR)
|
||||
{REGLIST_TAIL, 0x00}
|
||||
};
|
||||
|
||||
const DRAM_ATTR uint8_t sensor_saturation_levels[9][11] = {
|
||||
static const DRAM_ATTR uint8_t sensor_saturation_levels[9][11] = {
|
||||
{0x1d, 0x60, 0x03, 0x07, 0x48, 0x4f, 0x4b, 0x40, 0x0b, 0x01, 0x98},//-4
|
||||
{0x1d, 0x60, 0x03, 0x08, 0x54, 0x5c, 0x58, 0x4b, 0x0d, 0x01, 0x98},//-3
|
||||
{0x1d, 0x60, 0x03, 0x0a, 0x60, 0x6a, 0x64, 0x56, 0x0e, 0x01, 0x98},//-2
|
||||
@ -291,7 +304,7 @@ const DRAM_ATTR uint8_t sensor_saturation_levels[9][11] = {
|
||||
{0x1d, 0x60, 0x03, 0x11, 0xa8, 0xb9, 0xaf, 0x96, 0x19, 0x01, 0x98},//+4
|
||||
};
|
||||
|
||||
const DRAM_ATTR uint8_t sensor_special_effects[7][4] = {
|
||||
static const DRAM_ATTR uint8_t sensor_special_effects[7][4] = {
|
||||
{0x06, 0x40, 0x2c, 0x08},//Normal
|
||||
{0x46, 0x40, 0x28, 0x08},//Negative
|
||||
{0x1e, 0x80, 0x80, 0x08},//Grayscale
|
||||
|
Reference in New Issue
Block a user