Merge pull request #30 from espressif/ov3660-support

Ov3660 support
This commit is contained in:
Me No Dev
2019-04-09 14:59:55 +02:00
committed by GitHub
14 changed files with 1905 additions and 40 deletions

View File

@@ -5,6 +5,7 @@ set(COMPONENT_SRCS
driver/twi.c driver/twi.c
driver/xclk.c driver/xclk.c
sensors/ov2640.c sensors/ov2640.c
sensors/ov3660.c
sensors/ov7725.c sensors/ov7725.c
conversions/yuv.c conversions/yuv.c
conversions/to_jpg.cpp conversions/to_jpg.cpp

29
Kconfig
View File

@@ -14,4 +14,33 @@ config OV7725_SUPPORT
Enable this option if you want to use the OV7725. Enable this option if you want to use the OV7725.
Disable this option to safe memory. Disable this option to safe 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.
config SCCB_HARDWARE_I2C
bool "Use hardware I2C1 for SCCB"
default y
help
Enable this option if you want to use hardware I2C to control the camera.
Disable this option to use software I2C.
choice CAMERA_TASK_PINNED_TO_CORE
bool "Camera task pinned to core"
default CAMERA_CORE0
help
Pin the camera handle task to a certain core(0/1). It can also be done automatically choosing NO_AFFINITY.
config CAMERA_CORE0
bool "CORE0"
config CAMERA_CORE1
bool "CORE1"
config CAMERA_NO_AFFINITY
bool "NO_AFFINITY"
endchoice
endmenu endmenu

View File

@@ -205,7 +205,7 @@ bool fmt2jpg(uint8_t *src, size_t src_len, uint16_t width, uint16_t height, pixf
{ {
//todo: allocate proper buffer for holding JPEG data //todo: allocate proper buffer for holding JPEG data
//this should be enough for CIF frame size //this should be enough for CIF frame size
int jpg_buf_len = 24*1024; int jpg_buf_len = 64*1024;
uint8_t * jpg_buf = (uint8_t *)_malloc(jpg_buf_len); uint8_t * jpg_buf = (uint8_t *)_malloc(jpg_buf_len);

View File

@@ -40,12 +40,16 @@
#if CONFIG_OV7725_SUPPORT #if CONFIG_OV7725_SUPPORT
#include "ov7725.h" #include "ov7725.h"
#endif #endif
#if CONFIG_OV3660_SUPPORT
#include "ov3660.h"
#endif
typedef enum { typedef enum {
CAMERA_NONE = 0, CAMERA_NONE = 0,
CAMERA_UNKNOWN = 1, CAMERA_UNKNOWN = 1,
CAMERA_OV7725 = 7725, CAMERA_OV7725 = 7725,
CAMERA_OV2640 = 2640, CAMERA_OV2640 = 2640,
CAMERA_OV3660 = 3660,
} camera_model_t; } camera_model_t;
#define REG_PID 0x0A #define REG_PID 0x0A
@@ -53,6 +57,9 @@ typedef enum {
#define REG_MIDH 0x1C #define REG_MIDH 0x1C
#define REG_MIDL 0x1D #define REG_MIDL 0x1D
#define REG16_CHIDH 0x300A
#define REG16_CHIDL 0x300B
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) #if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
#include "esp32-hal-log.h" #include "esp32-hal-log.h"
#define TAG "" #define TAG ""
@@ -119,7 +126,7 @@ typedef struct {
camera_state_t* s_state = NULL; camera_state_t* s_state = NULL;
static void i2s_init(); static void i2s_init();
static void i2s_run(); static int i2s_run();
static void IRAM_ATTR vsync_isr(void* arg); static void IRAM_ATTR vsync_isr(void* arg);
static void IRAM_ATTR i2s_isr(void* arg); static void IRAM_ATTR i2s_isr(void* arg);
static esp_err_t dma_desc_init(); static esp_err_t dma_desc_init();
@@ -171,20 +178,32 @@ static void vsync_intr_enable()
gpio_set_intr_type(s_state->config.pin_vsync, GPIO_INTR_NEGEDGE); gpio_set_intr_type(s_state->config.pin_vsync, GPIO_INTR_NEGEDGE);
} }
static void skip_frame() static int skip_frame()
{ {
if (s_state == NULL) { if (s_state == NULL) {
return; return -1;
} }
int64_t st_t = esp_timer_get_time();
while (_gpio_get_level(s_state->config.pin_vsync) == 0) { while (_gpio_get_level(s_state->config.pin_vsync) == 0) {
; if((esp_timer_get_time() - st_t) > 1000000LL){
goto timeout;
}
} }
while (_gpio_get_level(s_state->config.pin_vsync) != 0) { while (_gpio_get_level(s_state->config.pin_vsync) != 0) {
; if((esp_timer_get_time() - st_t) > 1000000LL){
goto timeout;
}
} }
while (_gpio_get_level(s_state->config.pin_vsync) == 0) { while (_gpio_get_level(s_state->config.pin_vsync) == 0) {
; if((esp_timer_get_time() - st_t) > 1000000LL){
goto timeout;
}
} }
return 0;
timeout:
ESP_LOGE(TAG, "Timeout waiting for VSYNC");
return -1;
} }
static void camera_fb_deinit() static void camera_fb_deinit()
@@ -446,7 +465,7 @@ static void IRAM_ATTR i2s_start_bus()
} }
} }
static void i2s_run() static int i2s_run()
{ {
for (int i = 0; i < s_state->dma_desc_count; ++i) { for (int i = 0; i < s_state->dma_desc_count; ++i) {
lldesc_t* d = &s_state->dma_desc[i]; lldesc_t* d = &s_state->dma_desc[i];
@@ -467,14 +486,19 @@ static void i2s_run()
vTaskDelay(2); vTaskDelay(2);
} }
// wait for vsync //todo: wait for vsync
ESP_LOGV(TAG, "Waiting for negative edge on VSYNC"); ESP_LOGV(TAG, "Waiting for negative edge on VSYNC");
int64_t st_t = esp_timer_get_time();
while (_gpio_get_level(s_state->config.pin_vsync) != 0) { while (_gpio_get_level(s_state->config.pin_vsync) != 0) {
; if((esp_timer_get_time() - st_t) > 1000000LL){
ESP_LOGE(TAG, "Timeout waiting for VSYNC");
return -1;
}
} }
ESP_LOGV(TAG, "Got VSYNC"); ESP_LOGV(TAG, "Got VSYNC");
i2s_start_bus(); i2s_start_bus();
return 0;
} }
static void IRAM_ATTR i2s_stop_bus() static void IRAM_ATTR i2s_stop_bus()
@@ -818,6 +842,86 @@ static void IRAM_ATTR dma_filter_yuyv_highspeed(const dma_elem_t* src, lldesc_t*
} }
} }
static void IRAM_ATTR dma_filter_rgb888(const dma_elem_t* src, lldesc_t* dma_desc, uint8_t* dst)
{
size_t end = dma_desc->length / sizeof(dma_elem_t) / 4;
uint8_t lb, hb;
for (size_t i = 0; i < end; ++i) {
hb = src[0].sample1;
lb = src[0].sample2;
dst[0] = (lb & 0x1F) << 3;
dst[1] = (hb & 0x07) << 5 | (lb & 0xE0) >> 3;
dst[2] = hb & 0xF8;
hb = src[1].sample1;
lb = src[1].sample2;
dst[3] = (lb & 0x1F) << 3;
dst[4] = (hb & 0x07) << 5 | (lb & 0xE0) >> 3;
dst[5] = hb & 0xF8;
hb = src[2].sample1;
lb = src[2].sample2;
dst[6] = (lb & 0x1F) << 3;
dst[7] = (hb & 0x07) << 5 | (lb & 0xE0) >> 3;
dst[8] = hb & 0xF8;
hb = src[3].sample1;
lb = src[3].sample2;
dst[9] = (lb & 0x1F) << 3;
dst[10] = (hb & 0x07) << 5 | (lb & 0xE0) >> 3;
dst[11] = hb & 0xF8;
src += 4;
dst += 12;
}
}
static void IRAM_ATTR dma_filter_rgb888_highspeed(const dma_elem_t* src, lldesc_t* dma_desc, uint8_t* dst)
{
size_t end = dma_desc->length / sizeof(dma_elem_t) / 8;
uint8_t lb, hb;
for (size_t i = 0; i < end; ++i) {
hb = src[0].sample1;
lb = src[1].sample1;
dst[0] = (lb & 0x1F) << 3;
dst[1] = (hb & 0x07) << 5 | (lb & 0xE0) >> 3;
dst[2] = hb & 0xF8;
hb = src[2].sample1;
lb = src[3].sample1;
dst[3] = (lb & 0x1F) << 3;
dst[4] = (hb & 0x07) << 5 | (lb & 0xE0) >> 3;
dst[5] = hb & 0xF8;
hb = src[4].sample1;
lb = src[5].sample1;
dst[6] = (lb & 0x1F) << 3;
dst[7] = (hb & 0x07) << 5 | (lb & 0xE0) >> 3;
dst[8] = hb & 0xF8;
hb = src[6].sample1;
lb = src[7].sample1;
dst[9] = (lb & 0x1F) << 3;
dst[10] = (hb & 0x07) << 5 | (lb & 0xE0) >> 3;
dst[11] = hb & 0xF8;
src += 8;
dst += 12;
}
if ((dma_desc->length & 0x7) != 0) {
hb = src[0].sample1;
lb = src[1].sample1;
dst[0] = (lb & 0x1F) << 3;
dst[1] = (hb & 0x07) << 5 | (lb & 0xE0) >> 3;
dst[2] = hb & 0xF8;
hb = src[2].sample1;
lb = src[2].sample2;
dst[3] = (lb & 0x1F) << 3;
dst[4] = (hb & 0x07) << 5 | (lb & 0xE0) >> 3;
dst[5] = hb & 0xF8;
}
}
/* /*
* Public Methods * Public Methods
* */ * */
@@ -864,7 +968,7 @@ esp_err_t camera_probe(const camera_config_t* config, camera_model_t* out_camera
vTaskDelay(10 / portTICK_PERIOD_MS); vTaskDelay(10 / portTICK_PERIOD_MS);
gpio_set_level(config->pin_reset, 1); gpio_set_level(config->pin_reset, 1);
vTaskDelay(10 / portTICK_PERIOD_MS); vTaskDelay(10 / portTICK_PERIOD_MS);
#if CONFIG_OV2640_SUPPORT #if (CONFIG_OV2640_SUPPORT && !CONFIG_OV3660_SUPPORT)
} else { } else {
//reset OV2640 //reset OV2640
SCCB_Write(0x30, 0xFF, 0x01);//bank sensor SCCB_Write(0x30, 0xFF, 0x01);//bank sensor
@@ -888,13 +992,25 @@ esp_err_t camera_probe(const camera_config_t* config, camera_model_t* out_camera
ESP_LOGD(TAG, "Detected camera at address=0x%02x", s_state->sensor.slv_addr); ESP_LOGD(TAG, "Detected camera at address=0x%02x", s_state->sensor.slv_addr);
sensor_id_t* id = &s_state->sensor.id; sensor_id_t* id = &s_state->sensor.id;
id->PID = SCCB_Read(s_state->sensor.slv_addr, REG_PID); #if CONFIG_OV3660_SUPPORT
id->VER = SCCB_Read(s_state->sensor.slv_addr, REG_VER); if(s_state->sensor.slv_addr == 0x3c){
id->MIDL = SCCB_Read(s_state->sensor.slv_addr, REG_MIDL); id->PID = SCCB_Read16(s_state->sensor.slv_addr, REG16_CHIDH);
id->MIDH = SCCB_Read(s_state->sensor.slv_addr, REG_MIDH); id->VER = SCCB_Read16(s_state->sensor.slv_addr, REG16_CHIDL);
vTaskDelay(10 / portTICK_PERIOD_MS); vTaskDelay(10 / portTICK_PERIOD_MS);
ESP_LOGD(TAG, "Camera PID=0x%02x VER=0x%02x MIDL=0x%02x MIDH=0x%02x", ESP_LOGD(TAG, "Camera PID=0x%02x VER=0x%02x", id->PID, id->VER);
id->PID, id->VER, id->MIDH, id->MIDL); } else {
#endif
id->PID = SCCB_Read(s_state->sensor.slv_addr, REG_PID);
id->VER = SCCB_Read(s_state->sensor.slv_addr, REG_VER);
id->MIDL = SCCB_Read(s_state->sensor.slv_addr, REG_MIDL);
id->MIDH = SCCB_Read(s_state->sensor.slv_addr, REG_MIDH);
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
}
#endif
switch (id->PID) { switch (id->PID) {
#if CONFIG_OV2640_SUPPORT #if CONFIG_OV2640_SUPPORT
@@ -908,6 +1024,12 @@ esp_err_t camera_probe(const camera_config_t* config, camera_model_t* out_camera
*out_camera_model = CAMERA_OV7725; *out_camera_model = CAMERA_OV7725;
ov7725_init(&s_state->sensor); ov7725_init(&s_state->sensor);
break; break;
#endif
#if CONFIG_OV3660_SUPPORT
case OV3660_PID:
*out_camera_model = CAMERA_OV3660;
ov3660_init(&s_state->sensor);
break;
#endif #endif
default: default:
id->PID = 0; id->PID = 0;
@@ -940,14 +1062,25 @@ esp_err_t camera_init(const camera_config_t* config)
if (pix_format == PIXFORMAT_GRAYSCALE) { if (pix_format == PIXFORMAT_GRAYSCALE) {
s_state->fb_size = s_state->width * s_state->height; s_state->fb_size = s_state->width * s_state->height;
if (is_hs_mode()) { if (s_state->sensor.id.PID == OV3660_PID) {
s_state->sampling_mode = SM_0A00_0B00; if (is_hs_mode()) {
s_state->dma_filter = &dma_filter_grayscale_highspeed; s_state->sampling_mode = SM_0A00_0B00;
s_state->dma_filter = &dma_filter_yuyv_highspeed;
} else {
s_state->sampling_mode = SM_0A0B_0C0D;
s_state->dma_filter = &dma_filter_yuyv;
}
s_state->in_bytes_per_pixel = 1; // camera sends Y8
} else { } else {
s_state->sampling_mode = SM_0A0B_0C0D; if (is_hs_mode()) {
s_state->dma_filter = &dma_filter_grayscale; s_state->sampling_mode = SM_0A00_0B00;
s_state->dma_filter = &dma_filter_grayscale_highspeed;
} else {
s_state->sampling_mode = SM_0A0B_0C0D;
s_state->dma_filter = &dma_filter_grayscale;
}
s_state->in_bytes_per_pixel = 2; // camera sends YU/YV
} }
s_state->in_bytes_per_pixel = 2; // camera sends YUYV
s_state->fb_bytes_per_pixel = 1; // frame buffer stores Y8 s_state->fb_bytes_per_pixel = 1; // frame buffer stores Y8
} else if (pix_format == PIXFORMAT_YUV422 || pix_format == PIXFORMAT_RGB565) { } else if (pix_format == PIXFORMAT_YUV422 || pix_format == PIXFORMAT_RGB565) {
s_state->fb_size = s_state->width * s_state->height * 2; s_state->fb_size = s_state->width * s_state->height * 2;
@@ -958,11 +1091,22 @@ esp_err_t camera_init(const camera_config_t* config)
s_state->sampling_mode = SM_0A0B_0C0D; s_state->sampling_mode = SM_0A0B_0C0D;
s_state->dma_filter = &dma_filter_yuyv; s_state->dma_filter = &dma_filter_yuyv;
} }
s_state->in_bytes_per_pixel = 2; // camera sends YUYV s_state->in_bytes_per_pixel = 2; // camera sends YU/YV
s_state->fb_bytes_per_pixel = 2; // frame buffer stores Y8 s_state->fb_bytes_per_pixel = 2; // frame buffer stores YU/YV/RGB565
} else if (pix_format == PIXFORMAT_RGB888) {
s_state->fb_size = s_state->width * s_state->height * 3;
if (is_hs_mode()) {
s_state->sampling_mode = SM_0A00_0B00;
s_state->dma_filter = &dma_filter_rgb888_highspeed;
} else {
s_state->sampling_mode = SM_0A0B_0C0D;
s_state->dma_filter = &dma_filter_rgb888;
}
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) { } else if (pix_format == PIXFORMAT_JPEG) {
if (s_state->sensor.id.PID != OV2640_PID) { 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"); ESP_LOGE(TAG, "JPEG format is only supported for ov2640 and ov3660");
err = ESP_ERR_NOT_SUPPORTED; err = ESP_ERR_NOT_SUPPORTED;
goto fail; goto fail;
} }
@@ -1032,7 +1176,14 @@ esp_err_t camera_init(const camera_config_t* config)
} }
//ToDo: core affinity? //ToDo: core affinity?
if (!xTaskCreatePinnedToCore(&dma_filter_task, "dma_filter", 4096, NULL, 10, &s_state->dma_filter_task, 1)) { #if CONFIG_CAMERA_CORE0
if (!xTaskCreatePinnedToCore(&dma_filter_task, "dma_filter", 4096, NULL, 10, &s_state->dma_filter_task, 0))
#elif CONFIG_CAMERA_CORE1
if (!xTaskCreatePinnedToCore(&dma_filter_task, "dma_filter", 4096, NULL, 10, &s_state->dma_filter_task, 1))
#else
if (!xTaskCreate(&dma_filter_task, "dma_filter", 4096, NULL, 10, &s_state->dma_filter_task))
#endif
{
ESP_LOGE(TAG, "Failed to create DMA filter task"); ESP_LOGE(TAG, "Failed to create DMA filter task");
err = ESP_ERR_NO_MEM; err = ESP_ERR_NO_MEM;
goto fail; goto fail;
@@ -1063,7 +1214,10 @@ esp_err_t camera_init(const camera_config_t* config)
s_state->sensor.set_lenc(&s_state->sensor, true); s_state->sensor.set_lenc(&s_state->sensor, true);
} }
skip_frame(); if (skip_frame()) {
err = ESP_ERR_CAMERA_FAILED_TO_SET_OUT_FORMAT;
goto fail;
}
//todo: for some reason the first set of the quality does not work. //todo: for some reason the first set of the quality does not work.
if (pix_format == PIXFORMAT_JPEG) { if (pix_format == PIXFORMAT_JPEG) {
(*s_state->sensor.set_quality)(&s_state->sensor, config->jpeg_quality); (*s_state->sensor.set_quality)(&s_state->sensor, config->jpeg_quality);
@@ -1093,6 +1247,8 @@ esp_err_t esp_camera_init(const camera_config_t* config)
} }
} else if (camera_model == CAMERA_OV2640) { } else if (camera_model == CAMERA_OV2640) {
ESP_LOGD(TAG, "Detected OV2640 camera"); ESP_LOGD(TAG, "Detected OV2640 camera");
} else if (camera_model == CAMERA_OV3660) {
ESP_LOGD(TAG, "Detected OV3660 camera");
} else { } else {
ESP_LOGE(TAG, "Camera not supported"); ESP_LOGE(TAG, "Camera not supported");
err = ESP_ERR_CAMERA_NOT_SUPPORTED; err = ESP_ERR_CAMERA_NOT_SUPPORTED;
@@ -1155,7 +1311,9 @@ camera_fb_t* esp_camera_fb_get()
if(s_state->config.fb_count > 1) { if(s_state->config.fb_count > 1) {
ESP_LOGD(TAG, "i2s_run"); ESP_LOGD(TAG, "i2s_run");
} }
i2s_run(); if (i2s_run() != 0) {
return NULL;
}
} }
if(s_state->config.fb_count == 1) { if(s_state->config.fb_count == 1) {
xSemaphoreTake(s_state->frame_ready, portMAX_DELAY); xSemaphoreTake(s_state->frame_ready, portMAX_DELAY);

View File

@@ -120,7 +120,8 @@ typedef struct {
#define ESP_ERR_CAMERA_BASE 0x20000 #define ESP_ERR_CAMERA_BASE 0x20000
#define ESP_ERR_CAMERA_NOT_DETECTED (ESP_ERR_CAMERA_BASE + 1) #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_FRAME_SIZE (ESP_ERR_CAMERA_BASE + 2)
#define ESP_ERR_CAMERA_NOT_SUPPORTED (ESP_ERR_CAMERA_BASE + 3) #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 * @brief Initialize the camera driver

View File

@@ -13,6 +13,7 @@
#define OV9650_PID (0x96) #define OV9650_PID (0x96)
#define OV2640_PID (0x26) #define OV2640_PID (0x26)
#define OV7725_PID (0x77) #define OV7725_PID (0x77)
#define OV3660_PID (0x36)
typedef enum { typedef enum {
PIXFORMAT_RGB565, // 2BPP/RGB565 PIXFORMAT_RGB565, // 2BPP/RGB565
@@ -20,6 +21,9 @@ typedef enum {
PIXFORMAT_GRAYSCALE, // 1BPP/GRAYSCALE PIXFORMAT_GRAYSCALE, // 1BPP/GRAYSCALE
PIXFORMAT_JPEG, // JPEG/COMPRESSED PIXFORMAT_JPEG, // JPEG/COMPRESSED
PIXFORMAT_RGB888, // 3BPP/RGB888 PIXFORMAT_RGB888, // 3BPP/RGB888
PIXFORMAT_RAW, // RAW
PIXFORMAT_RGB444, // 3BP2P/RGB444
PIXFORMAT_RGB555, // 3BP2P/RGB555
} pixformat_t; } pixformat_t;
typedef enum { typedef enum {
@@ -34,6 +38,8 @@ typedef enum {
FRAMESIZE_XGA, // 1024x768 FRAMESIZE_XGA, // 1024x768
FRAMESIZE_SXGA, // 1280x1024 FRAMESIZE_SXGA, // 1280x1024
FRAMESIZE_UXGA, // 1600x1200 FRAMESIZE_UXGA, // 1600x1200
FRAMESIZE_QXGA, // 2048*1536
FRAMESIZE_INVALID
} framesize_t; } framesize_t;
typedef enum { typedef enum {
@@ -59,6 +65,8 @@ typedef struct {
int8_t brightness;//-2 - 2 int8_t brightness;//-2 - 2
int8_t contrast;//-2 - 2 int8_t contrast;//-2 - 2
int8_t saturation;//-2 - 2 int8_t saturation;//-2 - 2
int8_t sharpness;//-2 - 2
uint8_t denoise;
uint8_t special_effect;//0 - 6 uint8_t special_effect;//0 - 6
uint8_t wb_mode;//0 - 4 uint8_t wb_mode;//0 - 4
uint8_t awb; uint8_t awb;
@@ -96,6 +104,8 @@ typedef struct _sensor {
int (*set_contrast) (sensor_t *sensor, int level); int (*set_contrast) (sensor_t *sensor, int level);
int (*set_brightness) (sensor_t *sensor, int level); int (*set_brightness) (sensor_t *sensor, int level);
int (*set_saturation) (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_gainceiling) (sensor_t *sensor, gainceiling_t gainceiling);
int (*set_quality) (sensor_t *sensor, int quality); int (*set_quality) (sensor_t *sensor, int quality);
int (*set_colorbar) (sensor_t *sensor, int enable); int (*set_colorbar) (sensor_t *sensor, int enable);

View File

@@ -13,4 +13,6 @@ int SCCB_Init(int pin_sda, int pin_scl);
uint8_t SCCB_Probe(); uint8_t SCCB_Probe();
uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg); uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg);
uint8_t SCCB_Write(uint8_t slv_addr, uint8_t reg, uint8_t data); uint8_t SCCB_Write(uint8_t slv_addr, uint8_t reg, uint8_t data);
uint8_t SCCB_Read16(uint8_t slv_addr, uint16_t reg);
uint8_t SCCB_Write16(uint8_t slv_addr, uint16_t reg, uint8_t data);
#endif // __SCCB_H__ #endif // __SCCB_H__

View File

@@ -10,7 +10,6 @@
#include <freertos/FreeRTOS.h> #include <freertos/FreeRTOS.h>
#include <freertos/task.h> #include <freertos/task.h>
#include "sccb.h" #include "sccb.h"
#include "twi.h"
#include <stdio.h> #include <stdio.h>
#include "sdkconfig.h" #include "sdkconfig.h"
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) #if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
@@ -20,38 +19,107 @@
static const char* TAG = "sccb"; static const char* TAG = "sccb";
#endif #endif
#define LITTLETOBIG(x) ((x<<8)|(x>>8))
#define SCCB_FREQ (100000) // We don't need fast I2C. 100KHz is fine here. #ifdef CONFIG_SCCB_HARDWARE_I2C
#define TIMEOUT (1000) /* Can't be sure when I2C routines return. Interrupts #include "driver/i2c.h"
while polling hardware may result in unknown delays. */
#define SCCB_FREQ 200000 /*!< 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 */
const int SCCB_I2C_PORT = 1;
static uint8_t ESP_SLAVE_ADDR = 0x3c;
#else
#include "twi.h"
#endif
int SCCB_Init(int pin_sda, int pin_scl) int SCCB_Init(int pin_sda, int pin_scl)
{ {
ESP_LOGI(TAG, "pin_sda %d pin_scl %d\n", pin_sda, pin_scl);
#ifdef CONFIG_SCCB_HARDWARE_I2C
//log_i("SCCB_Init start");
i2c_config_t conf;
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;
i2c_param_config(SCCB_I2C_PORT, &conf);
i2c_driver_install(SCCB_I2C_PORT, conf.mode, 0, 0, 0);
#else
twi_init(pin_sda, pin_scl); twi_init(pin_sda, pin_scl);
#endif
return 0; return 0;
} }
uint8_t SCCB_Probe() uint8_t SCCB_Probe()
{ {
#ifdef CONFIG_SCCB_HARDWARE_I2C
uint8_t slave_addr = 0x0;
while(slave_addr < 0x7f) {
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) {
ESP_SLAVE_ADDR = slave_addr;
return ESP_SLAVE_ADDR;
}
slave_addr++;
}
return ESP_SLAVE_ADDR;
#else
uint8_t reg = 0x00; uint8_t reg = 0x00;
uint8_t slv_addr = 0x00; uint8_t slv_addr = 0x00;
for (uint8_t i=0; i<127; i++) { ESP_LOGI(TAG, "SCCB_Probe start");
for (uint8_t i = 0; i < 127; i++) {
if (twi_writeTo(i, &reg, 1, true) == 0) { if (twi_writeTo(i, &reg, 1, true) == 0) {
slv_addr = i; slv_addr = i;
break; break;
} }
if (i!=126) { if (i!=126) {
vTaskDelay(1 / portTICK_PERIOD_MS); // Necessary for OV7725 camera (not for OV2640). vTaskDelay(10 / portTICK_PERIOD_MS); // Necessary for OV7725 camera (not for OV2640).
} }
} }
return slv_addr; return slv_addr;
#endif
} }
uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg) uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg)
{ {
#ifdef CONFIG_SCCB_HARDWARE_I2C
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, ( ESP_SLAVE_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, ( ESP_SLAVE_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", ESP_SLAVE_ADDR, reg, data, ret);
}
return data;
#else
uint8_t data=0; uint8_t data=0;
int rc = twi_writeTo(slv_addr, &reg, 1, true); int rc = twi_writeTo(slv_addr, &reg, 1, true);
@@ -67,10 +135,26 @@ uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg)
ESP_LOGE(TAG, "SCCB_Read [%02x] failed rc=%d\n", reg, rc); ESP_LOGE(TAG, "SCCB_Read [%02x] failed rc=%d\n", reg, rc);
} }
return data; return data;
#endif
} }
uint8_t SCCB_Write(uint8_t slv_addr, uint8_t reg, uint8_t data) uint8_t SCCB_Write(uint8_t slv_addr, uint8_t reg, uint8_t data)
{ {
#ifdef CONFIG_SCCB_HARDWARE_I2C
esp_err_t ret = ESP_FAIL;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, ( ESP_SLAVE_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", ESP_SLAVE_ADDR, reg, data, ret);
}
return ret == ESP_OK ? 0 : -1;
#else
uint8_t ret=0; uint8_t ret=0;
uint8_t buf[] = {reg, data}; uint8_t buf[] = {reg, data};
@@ -78,7 +162,93 @@ uint8_t SCCB_Write(uint8_t slv_addr, uint8_t reg, uint8_t data)
ret=0xFF; ret=0xFF;
} }
if (ret != 0) { if (ret != 0) {
printf("SCCB_Write [%02x]=%02x failed\n", reg, data); ESP_LOGE(TAG, "SCCB_Write [%02x]=%02x failed\n", reg, data);
} }
return ret; return ret;
#endif
}
uint8_t SCCB_Read16(uint8_t slv_addr, uint16_t reg)
{
#ifdef CONFIG_SCCB_HARDWARE_I2C
uint8_t data=0;
esp_err_t ret = ESP_FAIL;
uint16_t reg_htons = LITTLETOBIG(reg);
uint8_t *reg_u8 = (uint8_t *)&reg_htons;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, ( ESP_SLAVE_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, ( ESP_SLAVE_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;
#else
uint8_t data=0;
uint16_t reg_htons = LITTLETOBIG(reg);
uint8_t *reg_u8 = (uint8_t *)&reg_htons;
uint8_t buf[] = {reg_u8[0], reg_u8[1]};
int rc = twi_writeTo(slv_addr, buf, 2, true);
if (rc != 0) {
data = 0xff;
} else {
rc = twi_readFrom(slv_addr, &data, 1, true);
if (rc != 0) {
data=0xFF;
}
}
if (rc != 0) {
ESP_LOGE(TAG, "R [%04x] fail rc=%d\n", reg, rc);
}
return data;
#endif
}
uint8_t SCCB_Write16(uint8_t slv_addr, uint16_t reg, uint8_t data)
{
static uint16_t i = 0;
#ifdef CONFIG_SCCB_HARDWARE_I2C
esp_err_t ret = ESP_FAIL;
uint16_t reg_htons = LITTLETOBIG(reg);
uint8_t *reg_u8 = (uint8_t *)&reg_htons;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, ( ESP_SLAVE_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;
#else
uint8_t ret=0;
uint16_t reg_htons = LITTLETOBIG(reg);
uint8_t *reg_u8 = (uint8_t *)&reg_htons;
uint8_t buf[] = {reg_u8[0], reg_u8[1], data};
if(twi_writeTo(slv_addr, buf, 3, true) != 0) {
ret = 0xFF;
}
if (ret != 0) {
ESP_LOGE(TAG, "W [%04x]=%02x %d fail\n", reg, data, i++);
}
return ret;
#endif
} }

View File

@@ -11,6 +11,7 @@ const int resolution[][2] = {
{ 1024, 768 }, /* XGA */ { 1024, 768 }, /* XGA */
{ 1280, 1024 }, /* SXGA */ { 1280, 1024 }, /* SXGA */
{ 1600, 1200 }, /* UXGA */ { 1600, 1200 }, /* UXGA */
{ 2048, 1536 }, /* QXGA */
}; };

View File

@@ -471,6 +471,17 @@ static int set_wpc_dsp(sensor_t *sensor, int enable)
return set_reg_bits(sensor, BANK_DSP, CTRL3, 6, 1, enable?1:0); return set_reg_bits(sensor, BANK_DSP, CTRL3, 6, 1, enable?1:0);
} }
//unsupported
static int set_sharpness(sensor_t *sensor, int level)
{
return -1;
}
static int set_denoise(sensor_t *sensor, int level)
{
return -1;
}
static int init_status(sensor_t *sensor){ static int init_status(sensor_t *sensor){
sensor->status.brightness = 0; sensor->status.brightness = 0;
sensor->status.contrast = 0; sensor->status.contrast = 0;
@@ -507,6 +518,9 @@ static int init_status(sensor_t *sensor){
sensor->status.vflip = get_reg_bits(sensor, BANK_SENSOR, REG04, 6, 1); sensor->status.vflip = get_reg_bits(sensor, BANK_SENSOR, REG04, 6, 1);
sensor->status.dcw = get_reg_bits(sensor, BANK_DSP, CTRL2, 5, 1); sensor->status.dcw = get_reg_bits(sensor, BANK_DSP, CTRL2, 5, 1);
sensor->status.colorbar = get_reg_bits(sensor, BANK_SENSOR, COM7, 1, 1); sensor->status.colorbar = get_reg_bits(sensor, BANK_SENSOR, COM7, 1, 1);
sensor->status.sharpness = 0;//not supported
sensor->status.denoise = 0;
return 0; return 0;
} }
@@ -545,6 +559,9 @@ int ov2640_init(sensor_t *sensor)
sensor->set_raw_gma = set_raw_gma_dsp; sensor->set_raw_gma = set_raw_gma_dsp;
sensor->set_lenc = set_lenc_dsp; sensor->set_lenc = set_lenc_dsp;
//not supported
sensor->set_sharpness = set_sharpness;
sensor->set_denoise = set_denoise;
ESP_LOGD(TAG, "OV2640 Attached"); ESP_LOGD(TAG, "OV2640 Attached");
return 0; return 0;
} }

945
sensors/ov3660.c Executable file
View File

@@ -0,0 +1,945 @@
/*
* 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.
*
* OV3660 driver.
*
*/
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "sccb.h"
#include "ov3660.h"
#include "ov3660_regs.h"
#include "ov3660_settings.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.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 = "ov3660";
#endif
//#define REG_DEBUG_ON
static int read_reg(uint8_t slv_addr, const uint16_t reg){
int ret = SCCB_Read16(slv_addr, reg);
#ifdef REG_DEBUG_ON
if (ret < 0) {
ESP_LOGE(TAG, "READ REG 0x%04x FAILED: %d", reg, ret);
}
#endif
return ret;
}
static int check_reg_mask(uint8_t slv_addr, uint16_t reg, uint8_t mask){
return (read_reg(slv_addr, reg) & mask) == mask;
}
static int read_reg16(uint8_t slv_addr, const uint16_t reg){
int ret = 0, ret2 = 0;
ret = read_reg(slv_addr, reg);
if (ret >= 0) {
ret = (ret & 0xFF) << 8;
ret2 = read_reg(slv_addr, reg+1);
if (ret2 < 0) {
ret = ret2;
} else {
ret |= ret2 & 0xFF;
}
}
return ret;
}
static int write_reg(uint8_t slv_addr, const uint16_t reg, uint8_t value){
int ret = 0;
#ifndef REG_DEBUG_ON
ret = SCCB_Write16(slv_addr, reg, value);
#else
int old_value = read_reg(slv_addr, reg);
if (old_value < 0) {
return old_value;
}
if ((uint8_t)old_value != value) {
ESP_LOGI(TAG, "NEW REG 0x%04x: 0x%02x to 0x%02x", reg, (uint8_t)old_value, value);
ret = SCCB_Write16(slv_addr, reg, value);
} else {
ESP_LOGD(TAG, "OLD REG 0x%04x: 0x%02x", reg, (uint8_t)old_value);
ret = SCCB_Write16(slv_addr, reg, value);//maybe not?
}
if (ret < 0) {
ESP_LOGE(TAG, "WRITE REG 0x%04x FAILED: %d", reg, ret);
}
#endif
return ret;
}
static int set_reg_bits(uint8_t slv_addr, uint16_t reg, uint8_t offset, uint8_t mask, uint8_t value)
{
int ret = 0;
uint8_t c_value, new_value;
ret = read_reg(slv_addr, reg);
if(ret < 0) {
return ret;
}
c_value = ret;
new_value = (c_value & ~(mask << offset)) | ((value & mask) << offset);
ret = write_reg(slv_addr, reg, new_value);
return ret;
}
static int write_regs(uint8_t slv_addr, const uint16_t (*regs)[2])
{
int i = 0, ret = 0;
while (!ret && regs[i][0] != REGLIST_TAIL) {
if (regs[i][0] == REG_DLY) {
vTaskDelay(regs[i][1] / portTICK_PERIOD_MS);
} else {
ret = write_reg(slv_addr, regs[i][0], regs[i][1]);
}
i++;
}
return ret;
}
static int write_reg16(uint8_t slv_addr, const uint16_t reg, uint16_t value)
{
if (write_reg(slv_addr, reg, value >> 8) || write_reg(slv_addr, reg + 1, value)) {
return -1;
}
return 0;
}
static int write_addr_reg(uint8_t slv_addr, const uint16_t reg, uint16_t x_value, uint16_t y_value)
{
if (write_reg16(slv_addr, reg, x_value) || write_reg16(slv_addr, reg + 2, y_value)) {
return -1;
}
return 0;
}
#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)
{
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 };
if(!pll_sys_div) {
pll_sys_div = 1;
}
int pll_pre_div2x = pll_pre_div2x_map[pll_pre_div];
int pll_root_div = pll_root_2x?2:1;
int pll_seld52x = pll_seld52x_map[pll_seld5];
int VCO = (xclk / 1000) * pll_multiplier * pll_root_div * 2 / pll_pre_div2x;
int PLLCLK = pll_bypass?(xclk):(VCO * 1000 * 2 / pll_sys_div / pll_seld52x);
int PCLK = PLLCLK / 2 / ((pclk_manual && pclk_div)?pclk_div:1);
int SYSCLK = PLLCLK / 4;
ESP_LOGD(TAG, "Calculated VCO: %d Hz, PLLCLK: %d Hz, SYSCLK: %d Hz, PCLK: %d Hz", VCO*1000, PLLCLK, SYSCLK, PCLK);
return SYSCLK;
}
static int set_pll(sensor_t *sensor, bool bypass, uint8_t multiplier, uint8_t sys_div, uint8_t pre_div, bool root_2x, uint8_t seld5, bool pclk_manual, uint8_t pclk_div){
int ret = 0;
if(multiplier > 31 || sys_div > 15 || pre_div > 3 || pclk_div > 31 || seld5 > 3){
ESP_LOGE(TAG, "Invalid arguments");
return -1;
}
calc_sysclk(sensor->xclk_freq_hz, bypass, multiplier, sys_div, pre_div, root_2x, seld5, pclk_manual, pclk_div);
ret = write_reg(sensor->slv_addr, SC_PLLS_CTRL0, bypass?0x80:0x00);
if (ret == 0) {
ret = write_reg(sensor->slv_addr, SC_PLLS_CTRL1, multiplier & 0x1f);
}
if (ret == 0) {
ret = write_reg(sensor->slv_addr, SC_PLLS_CTRL2, 0x10 | (sys_div & 0x0f));
}
if (ret == 0) {
ret = write_reg(sensor->slv_addr, SC_PLLS_CTRL3, (pre_div & 0x3) << 4 | seld5 | (root_2x?0x40:0x00));
}
if (ret == 0) {
ret = write_reg(sensor->slv_addr, PCLK_RATIO, pclk_div & 0x1f);
}
if (ret == 0) {
ret = write_reg(sensor->slv_addr, VFIFO_CTRL0C, pclk_manual?0x22:0x20);
}
if(ret){
ESP_LOGE(TAG, "set_sensor_pll FAILED!");
}
return ret;
}
static int set_ae_level(sensor_t *sensor, int level);
static int reset(sensor_t *sensor)
{
int ret = 0;
// Software Reset: clear all registers and reset them to their default values
ret = write_reg(sensor->slv_addr, SYSTEM_CTROL0, 0x82);
if(ret){
ESP_LOGE(TAG, "Software Reset FAILED!");
return ret;
}
vTaskDelay(100 / portTICK_PERIOD_MS);
ret = write_regs(sensor->slv_addr, sensor_default_regs);
if (ret == 0) {
ESP_LOGD(TAG, "Camera defaults loaded");
ret = set_ae_level(sensor, 0);
vTaskDelay(100 / portTICK_PERIOD_MS);
}
return ret;
}
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
{
int ret = 0;
const uint16_t (*regs)[2];
switch (pixformat) {
case PIXFORMAT_YUV422:
regs = sensor_fmt_yuv422;
break;
case PIXFORMAT_GRAYSCALE:
regs = sensor_fmt_grayscale;
break;
case PIXFORMAT_RGB565:
case PIXFORMAT_RGB888:
regs = sensor_fmt_rgb565;
break;
case PIXFORMAT_JPEG:
regs = sensor_fmt_jpeg;
break;
case PIXFORMAT_RAW:
regs = sensor_fmt_raw;
break;
default:
ESP_LOGE(TAG, "Unsupported pixformat: %u", pixformat);
return -1;
}
ret = write_regs(sensor->slv_addr, regs);
if(ret == 0) {
sensor->pixformat = pixformat;
ESP_LOGD(TAG, "Set pixformat to: %u", pixformat);
}
return ret;
}
static int set_image_options(sensor_t *sensor)
{
int ret = 0;
uint8_t reg20 = 0;
uint8_t reg21 = 0;
uint8_t reg4514 = 0;
uint8_t reg4514_test = 0;
// compression
if (sensor->pixformat == PIXFORMAT_JPEG) {
reg21 |= 0x20;
}
// binning
if (sensor->status.framesize > FRAMESIZE_SVGA) {
reg20 |= 0x40;
} else {
reg20 |= 0x01;
reg21 |= 0x01;
reg4514_test |= 4;
}
// V-Flip
if (sensor->status.vflip) {
reg20 |= 0x06;
reg4514_test |= 1;
}
// H-Mirror
if (sensor->status.hmirror) {
reg21 |= 0x06;
reg4514_test |= 2;
}
switch (reg4514_test) {
//no binning
case 0: reg4514 = 0x88; break;//normal
case 1: reg4514 = 0x88; break;//v-flip
case 2: reg4514 = 0xbb; break;//h-mirror
case 3: reg4514 = 0xbb; break;//v-flip+h-mirror
//binning
case 4: reg4514 = 0xaa; break;//normal
case 5: reg4514 = 0xbb; break;//v-flip
case 6: reg4514 = 0xbb; break;//h-mirror
case 7: reg4514 = 0xaa; break;//v-flip+h-mirror
}
if(write_reg(sensor->slv_addr, TIMING_TC_REG20, reg20)
|| write_reg(sensor->slv_addr, TIMING_TC_REG21, reg21)
|| write_reg(sensor->slv_addr, 0x4514, reg4514)){
ESP_LOGE(TAG, "Setting Image Options Failed");
ret = -1;
}
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);
return ret;
}
static int set_framesize(sensor_t *sensor, framesize_t framesize)
{
int ret = 0;
framesize_t old_framesize = sensor->status.framesize;
sensor->status.framesize = framesize;
if(framesize >= FRAMESIZE_INVALID){
ESP_LOGE(TAG, "Invalid framesize: %u", framesize);
return -1;
}
uint16_t w = resolution[framesize][0];
uint16_t h = resolution[framesize][1];
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 {
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
}
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)
|| 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);
} 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);
}
}
if (ret == 0) {
ret = write_reg_bits(sensor->slv_addr, ISP_CONTROL_01, 0x20, framesize != FRAMESIZE_QXGA);
}
if (ret == 0) {
ret = set_image_options(sensor);
}
if (ret) {
goto fail;
}
if (sensor->pixformat == PIXFORMAT_JPEG) {
if (framesize == FRAMESIZE_QXGA) {
//40MHz SYSCLK and 10MHz PCLK
ret = set_pll(sensor, false, 24, 1, 3, false, 0, true, 8);
} else {
//50MHz SYSCLK and 10MHz PCLK
ret = set_pll(sensor, false, 30, 1, 3, false, 0, true, 10);
}
} else {
if (framesize > FRAMESIZE_CIF) {
//10MHz SYSCLK and 10MHz PCLK (6.19 FPS)
ret = set_pll(sensor, false, 2, 1, 0, false, 0, true, 2);
} else {
//25MHz SYSCLK and 10MHz PCLK (15.45 FPS)
ret = set_pll(sensor, false, 5, 1, 0, false, 0, true, 5);
}
}
if (ret == 0) {
ESP_LOGD(TAG, "Set framesize to: %ux%u", w, h);
}
return ret;
fail:
sensor->status.framesize = old_framesize;
ESP_LOGE(TAG, "Setting framesize to: %ux%u failed", w, h);
return ret;
}
static int set_hmirror(sensor_t *sensor, int enable)
{
int ret = 0;
sensor->status.hmirror = enable;
ret = set_image_options(sensor);
if (ret == 0) {
ESP_LOGD(TAG, "Set h-mirror to: %d", enable);
}
return ret;
}
static int set_vflip(sensor_t *sensor, int enable)
{
int ret = 0;
sensor->status.vflip = enable;
ret = set_image_options(sensor);
if (ret == 0) {
ESP_LOGD(TAG, "Set v-flip to: %d", enable);
}
return ret;
}
static int set_quality(sensor_t *sensor, int qs)
{
int ret = 0;
ret = write_reg(sensor->slv_addr, COMPRESSION_CTRL07, qs & 0x3f);
if (ret == 0) {
sensor->status.quality = qs;
ESP_LOGD(TAG, "Set quality to: %d", qs);
}
return ret;
}
static int set_colorbar(sensor_t *sensor, int enable)
{
int ret = 0;
ret = write_reg_bits(sensor->slv_addr, PRE_ISP_TEST_SETTING_1, TEST_COLOR_BAR, enable);
if (ret == 0) {
sensor->status.colorbar = enable;
ESP_LOGD(TAG, "Set colorbar to: %d", enable);
}
return ret;
}
static int set_gain_ctrl(sensor_t *sensor, int enable)
{
int ret = 0;
ret = write_reg_bits(sensor->slv_addr, AEC_PK_MANUAL, AEC_PK_MANUAL_AGC_MANUALEN, !enable);
if (ret == 0) {
ESP_LOGD(TAG, "Set gain_ctrl to: %d", enable);
sensor->status.agc = enable;
}
return ret;
}
static int set_exposure_ctrl(sensor_t *sensor, int enable)
{
int ret = 0;
ret = write_reg_bits(sensor->slv_addr, AEC_PK_MANUAL, AEC_PK_MANUAL_AEC_MANUALEN, !enable);
if (ret == 0) {
ESP_LOGD(TAG, "Set exposure_ctrl to: %d", enable);
sensor->status.aec = enable;
}
return ret;
}
static int set_whitebal(sensor_t *sensor, int enable)
{
int ret = 0;
ret = write_reg_bits(sensor->slv_addr, ISP_CONTROL_01, 0x01, enable);
if (ret == 0) {
ESP_LOGD(TAG, "Set awb to: %d", enable);
sensor->status.awb = enable;
}
return ret;
}
//Advanced AWB
static int set_dcw_dsp(sensor_t *sensor, int enable)
{
int ret = 0;
ret = write_reg_bits(sensor->slv_addr, 0x5183, 0x80, !enable);
if (ret == 0) {
ESP_LOGD(TAG, "Set dcw to: %d", enable);
sensor->status.dcw = enable;
}
return ret;
}
//night mode enable
static int set_aec2(sensor_t *sensor, int enable)
{
int ret = 0;
ret = write_reg_bits(sensor->slv_addr, 0x3a00, 0x04, enable);
if (ret == 0) {
ESP_LOGD(TAG, "Set aec2 to: %d", enable);
sensor->status.aec2 = enable;
}
return ret;
}
static int set_bpc_dsp(sensor_t *sensor, int enable)
{
int ret = 0;
ret = write_reg_bits(sensor->slv_addr, 0x5000, 0x04, enable);
if (ret == 0) {
ESP_LOGD(TAG, "Set bpc to: %d", enable);
sensor->status.bpc = enable;
}
return ret;
}
static int set_wpc_dsp(sensor_t *sensor, int enable)
{
int ret = 0;
ret = write_reg_bits(sensor->slv_addr, 0x5000, 0x02, enable);
if (ret == 0) {
ESP_LOGD(TAG, "Set wpc to: %d", enable);
sensor->status.wpc = enable;
}
return ret;
}
//Gamma enable
static int set_raw_gma_dsp(sensor_t *sensor, int enable)
{
int ret = 0;
ret = write_reg_bits(sensor->slv_addr, 0x5000, 0x20, enable);
if (ret == 0) {
ESP_LOGD(TAG, "Set raw_gma to: %d", enable);
sensor->status.raw_gma = enable;
}
return ret;
}
static int set_lenc_dsp(sensor_t *sensor, int enable)
{
int ret = 0;
ret = write_reg_bits(sensor->slv_addr, 0x5000, 0x80, enable);
if (ret == 0) {
ESP_LOGD(TAG, "Set lenc to: %d", enable);
sensor->status.lenc = enable;
}
return ret;
}
static int get_agc_gain(sensor_t *sensor)
{
int ra = read_reg(sensor->slv_addr, 0x350a);
if (ra < 0) {
return 0;
}
int rb = read_reg(sensor->slv_addr, 0x350b);
if (rb < 0) {
return 0;
}
int res = (rb & 0xF0) >> 4 | (ra & 0x03) << 4;
if (rb & 0x0F) {
res += 1;
}
return res;
}
//real gain
static int set_agc_gain(sensor_t *sensor, int gain)
{
int ret = 0;
if(gain < 0) {
gain = 0;
} else if(gain > 64) {
gain = 64;
}
//gain value is 6.4 bits float
//in order to use the max range, we deduct 1/16
int gainv = gain << 4;
if(gainv){
gainv -= 1;
}
ret = write_reg(sensor->slv_addr, 0x350a, gainv >> 8) || write_reg(sensor->slv_addr, 0x350b, gainv & 0xff);
if (ret == 0) {
ESP_LOGD(TAG, "Set agc_gain to: %d", gain);
sensor->status.agc_gain = gain;
}
return ret;
}
static int get_aec_value(sensor_t *sensor)
{
int ra = read_reg(sensor->slv_addr, 0x3500);
if (ra < 0) {
return 0;
}
int rb = read_reg(sensor->slv_addr, 0x3501);
if (rb < 0) {
return 0;
}
int rc = read_reg(sensor->slv_addr, 0x3502);
if (rc < 0) {
return 0;
}
int res = (ra & 0x0F) << 12 | (rb & 0xFF) << 4 | (rc & 0xF0) >> 4;
return res;
}
static int set_aec_value(sensor_t *sensor, int value)
{
int ret = 0, max_val = 0;
max_val = read_reg16(sensor->slv_addr, 0x380e);
if (max_val < 0) {
ESP_LOGE(TAG, "Could not read max aec_value");
return -1;
}
if (value > max_val) {
value =max_val;
}
ret = write_reg(sensor->slv_addr, 0x3500, (value >> 12) & 0x0F)
|| write_reg(sensor->slv_addr, 0x3501, (value >> 4) & 0xFF)
|| write_reg(sensor->slv_addr, 0x3502, (value << 4) & 0xF0);
if (ret == 0) {
ESP_LOGD(TAG, "Set aec_value to: %d / %d", value, max_val);
sensor->status.aec_value = value;
}
return ret;
}
static int set_ae_level(sensor_t *sensor, int level)
{
int ret = 0;
if (level < -5 || level > 5) {
return -1;
}
//good targets are between 5 and 115
int target_level = ((level + 5) * 10) + 5;
int level_high, level_low;
int fast_high, fast_low;
level_low = target_level * 23 / 25; //0.92 (0.46)
level_high = target_level * 27 / 25; //1.08 (2.08)
fast_low = level_low >> 1;
fast_high = level_high << 1;
if(fast_high>255) {
fast_high = 255;
}
ret = write_reg(sensor->slv_addr, 0x3a0f, level_high)
|| write_reg(sensor->slv_addr, 0x3a10, level_low)
|| write_reg(sensor->slv_addr, 0x3a1b, level_high)
|| write_reg(sensor->slv_addr, 0x3a1e, level_low)
|| write_reg(sensor->slv_addr, 0x3a11, fast_high)
|| write_reg(sensor->slv_addr, 0x3a1f, fast_low);
if (ret == 0) {
ESP_LOGD(TAG, "Set ae_level to: %d", level);
sensor->status.ae_level = level;
}
return ret;
}
static int set_wb_mode(sensor_t *sensor, int mode)
{
int ret = 0;
if (mode < 0 || mode > 4) {
return -1;
}
ret = write_reg(sensor->slv_addr, 0x3406, (mode != 0));
if (ret) {
return ret;
}
switch (mode) {
case 1://Sunny
ret = write_reg16(sensor->slv_addr, 0x3400, 0x5e0) //AWB R GAIN
|| write_reg16(sensor->slv_addr, 0x3402, 0x410) //AWB G GAIN
|| write_reg16(sensor->slv_addr, 0x3404, 0x540);//AWB B GAIN
break;
case 2://Cloudy
ret = write_reg16(sensor->slv_addr, 0x3400, 0x650) //AWB R GAIN
|| write_reg16(sensor->slv_addr, 0x3402, 0x410) //AWB G GAIN
|| write_reg16(sensor->slv_addr, 0x3404, 0x4f0);//AWB B GAIN
break;
case 3://Office
ret = write_reg16(sensor->slv_addr, 0x3400, 0x520) //AWB R GAIN
|| write_reg16(sensor->slv_addr, 0x3402, 0x410) //AWB G GAIN
|| write_reg16(sensor->slv_addr, 0x3404, 0x660);//AWB B GAIN
break;
case 4://HOME
ret = write_reg16(sensor->slv_addr, 0x3400, 0x420) //AWB R GAIN
|| write_reg16(sensor->slv_addr, 0x3402, 0x3f0) //AWB G GAIN
|| write_reg16(sensor->slv_addr, 0x3404, 0x710);//AWB B GAIN
break;
default://AUTO
break;
}
if (ret == 0) {
ESP_LOGD(TAG, "Set wb_mode to: %d", mode);
sensor->status.wb_mode = mode;
}
return ret;
}
static int set_awb_gain_dsp(sensor_t *sensor, int enable)
{
int ret = 0;
int old_mode = sensor->status.wb_mode;
int mode = enable?old_mode:0;
ret = set_wb_mode(sensor, mode);
if (ret == 0) {
sensor->status.wb_mode = old_mode;
ESP_LOGD(TAG, "Set awb_gain to: %d", enable);
sensor->status.awb_gain = enable;
}
return ret;
}
static int set_special_effect(sensor_t *sensor, int effect)
{
int ret=0;
if (effect < 0 || effect > 6) {
return -1;
}
uint8_t * regs = (uint8_t *)sensor_special_effects[effect];
ret = write_reg(sensor->slv_addr, 0x5580, regs[0])
|| write_reg(sensor->slv_addr, 0x5583, regs[1])
|| write_reg(sensor->slv_addr, 0x5584, regs[2])
|| write_reg(sensor->slv_addr, 0x5003, regs[3]);
if (ret == 0) {
ESP_LOGD(TAG, "Set special_effect to: %d", effect);
sensor->status.special_effect = effect;
}
return ret;
}
static int set_brightness(sensor_t *sensor, int level)
{
int ret = 0;
uint8_t value = 0;
bool negative = false;
switch (level) {
case 3:
value = 0x30;
break;
case 2:
value = 0x20;
break;
case 1:
value = 0x10;
break;
case -1:
value = 0x10;
negative = true;
break;
case -2:
value = 0x20;
negative = true;
break;
case -3:
value = 0x30;
negative = true;
break;
default: // 0
break;
}
ret = write_reg(sensor->slv_addr, 0x5587, value);
if (ret == 0) {
ret = write_reg_bits(sensor->slv_addr, 0x5588, 0x08, negative);
}
if (ret == 0) {
ESP_LOGD(TAG, "Set brightness to: %d", level);
sensor->status.brightness = level;
}
return ret;
}
static int set_contrast(sensor_t *sensor, int level)
{
int ret = 0;
if(level > 3 || level < -3) {
return -1;
}
ret = write_reg(sensor->slv_addr, 0x5586, (level + 4) << 3);
if (ret == 0) {
ESP_LOGD(TAG, "Set contrast to: %d", level);
sensor->status.contrast = level;
}
return ret;
}
static int set_saturation(sensor_t *sensor, int level)
{
int ret = 0;
if(level > 4 || level < -4) {
return -1;
}
uint8_t * regs = (uint8_t *)sensor_saturation_levels[level+4];
for(int i=0; i<11; i++) {
ret = write_reg(sensor->slv_addr, 0x5381 + i, regs[i]);
if (ret) {
break;
}
}
if (ret == 0) {
ESP_LOGD(TAG, "Set saturation to: %d", level);
sensor->status.saturation = level;
}
return ret;
}
static int set_sharpness(sensor_t *sensor, int level)
{
int ret = 0;
if(level > 3 || level < -3) {
return -1;
}
uint8_t mt_offset_2 = (level + 3) * 8;
uint8_t mt_offset_1 = mt_offset_2 + 1;
ret = write_reg_bits(sensor->slv_addr, 0x5308, 0x40, false)//0x40 means auto
|| write_reg(sensor->slv_addr, 0x5300, 0x10)
|| write_reg(sensor->slv_addr, 0x5301, 0x10)
|| write_reg(sensor->slv_addr, 0x5302, mt_offset_1)
|| write_reg(sensor->slv_addr, 0x5303, mt_offset_2)
|| write_reg(sensor->slv_addr, 0x5309, 0x10)
|| write_reg(sensor->slv_addr, 0x530a, 0x10)
|| write_reg(sensor->slv_addr, 0x530b, 0x04)
|| write_reg(sensor->slv_addr, 0x530c, 0x06);
if (ret == 0) {
ESP_LOGD(TAG, "Set sharpness to: %d", level);
sensor->status.sharpness = level;
}
return ret;
}
static int set_gainceiling(sensor_t *sensor, gainceiling_t level)
{
int ret = 0, l = (int)level;
ret = write_reg(sensor->slv_addr, 0x3A18, (l >> 8) & 3)
|| write_reg(sensor->slv_addr, 0x3A19, l & 0xFF);
if (ret == 0) {
ESP_LOGD(TAG, "Set gainceiling to: %d", l);
sensor->status.gainceiling = l;
}
return ret;
}
static int get_denoise(sensor_t *sensor)
{
if (!check_reg_mask(sensor->slv_addr, 0x5308, 0x10)) {
return 0;
}
return (read_reg(sensor->slv_addr, 0x5306) / 4) + 1;
}
static int set_denoise(sensor_t *sensor, int level)
{
int ret = 0;
if (level < 0 || level > 8) {
return -1;
}
ret = write_reg_bits(sensor->slv_addr, 0x5308, 0x10, level > 0);
if (ret == 0 && level > 0) {
ret = write_reg(sensor->slv_addr, 0x5306, (level - 1) * 4);
}
if (ret == 0) {
ESP_LOGD(TAG, "Set denoise to: %d", level);
sensor->status.denoise = level;
}
return ret;
}
static int init_status(sensor_t *sensor)
{
sensor->status.brightness = 0;
sensor->status.contrast = 0;
sensor->status.saturation = 0;
sensor->status.sharpness = (read_reg(sensor->slv_addr, 0x5303) / 8) - 3;
sensor->status.denoise = get_denoise(sensor);
sensor->status.ae_level = 0;
sensor->status.gainceiling = read_reg16(sensor->slv_addr, 0x3A18) & 0x3FF;
sensor->status.awb = check_reg_mask(sensor->slv_addr, ISP_CONTROL_01, 0x01);
sensor->status.dcw = !check_reg_mask(sensor->slv_addr, 0x5183, 0x80);
sensor->status.agc = !check_reg_mask(sensor->slv_addr, AEC_PK_MANUAL, AEC_PK_MANUAL_AGC_MANUALEN);
sensor->status.aec = !check_reg_mask(sensor->slv_addr, AEC_PK_MANUAL, AEC_PK_MANUAL_AEC_MANUALEN);
sensor->status.hmirror = check_reg_mask(sensor->slv_addr, TIMING_TC_REG21, TIMING_TC_REG21_HMIRROR);
sensor->status.vflip = check_reg_mask(sensor->slv_addr, TIMING_TC_REG20, TIMING_TC_REG20_VFLIP);
sensor->status.colorbar = check_reg_mask(sensor->slv_addr, PRE_ISP_TEST_SETTING_1, TEST_COLOR_BAR);
sensor->status.bpc = check_reg_mask(sensor->slv_addr, 0x5000, 0x04);
sensor->status.wpc = check_reg_mask(sensor->slv_addr, 0x5000, 0x02);
sensor->status.raw_gma = check_reg_mask(sensor->slv_addr, 0x5000, 0x20);
sensor->status.lenc = check_reg_mask(sensor->slv_addr, 0x5000, 0x80);
sensor->status.quality = read_reg(sensor->slv_addr, COMPRESSION_CTRL07) & 0x3f;
sensor->status.special_effect = 0;
sensor->status.wb_mode = 0;
sensor->status.awb_gain = check_reg_mask(sensor->slv_addr, 0x3406, 0x01);
sensor->status.agc_gain = get_agc_gain(sensor);
sensor->status.aec_value = get_aec_value(sensor);
sensor->status.aec2 = check_reg_mask(sensor->slv_addr, 0x3a00, 0x04);
return 0;
}
int ov3660_init(sensor_t *sensor)
{
sensor->reset = reset;
sensor->set_pixformat = set_pixformat;
sensor->set_framesize = set_framesize;
sensor->set_contrast = set_contrast;
sensor->set_brightness = set_brightness;
sensor->set_saturation = set_saturation;
sensor->set_sharpness = set_sharpness;
sensor->set_gainceiling = set_gainceiling;
sensor->set_quality = set_quality;
sensor->set_colorbar = set_colorbar;
sensor->set_gain_ctrl = set_gain_ctrl;
sensor->set_exposure_ctrl = set_exposure_ctrl;
sensor->set_whitebal = set_whitebal;
sensor->set_hmirror = set_hmirror;
sensor->set_vflip = set_vflip;
sensor->init_status = init_status;
sensor->set_aec2 = set_aec2;
sensor->set_aec_value = set_aec_value;
sensor->set_special_effect = set_special_effect;
sensor->set_wb_mode = set_wb_mode;
sensor->set_ae_level = set_ae_level;
sensor->set_dcw = set_dcw_dsp;
sensor->set_bpc = set_bpc_dsp;
sensor->set_wpc = set_wpc_dsp;
sensor->set_awb_gain = set_awb_gain_dsp;
sensor->set_agc_gain = set_agc_gain;
sensor->set_raw_gma = set_raw_gma_dsp;
sensor->set_lenc = set_lenc_dsp;
sensor->set_denoise = set_denoise;
return 0;
}

View File

@@ -0,0 +1,16 @@
/*
* 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.
*
* OV3660 driver.
*
*/
#ifndef __OV3660_H__
#define __OV3660_H__
#include "sensor.h"
int ov3660_init(sensor_t *sensor);
#endif // __OV3660_H__

View File

@@ -0,0 +1,211 @@
/*
* OV3660 register definitions.
*/
#ifndef __OV3660_REG_REGS_H__
#define __OV3660_REG_REGS_H__
/* system control registers */
#define SYSTEM_CTROL0 0x3008 // Bit[7]: Software reset
// Bit[6]: Software power down
// Bit[5]: Reserved
// Bit[4]: SRB clock SYNC enable
// Bit[3]: Isolation suspend select
// Bit[2:0]: Not used
/* output format control registers */
#define FORMAT_CTRL 0x501F // Format select
// Bit[2:0]:
// 000: YUV422
// 001: RGB
// 010: Dither
// 011: RAW after DPC
// 101: RAW after CIP
/* format control registers */
#define FORMAT_CTRL00 0x4300
/* frame control registers */
#define FRAME_CTRL01 0x4201 // Control Passed Frame Number When both ON and OFF number set to 0x00,frame control is in bypass mode
// Bit[7:4]: Not used
// Bit[3:0]: Frame ON number
#define FRAME_CTRL02 0x4202 // Control Masked Frame Number When both ON and OFF number set to 0x00,frame control is in bypass mode
// Bit[7:4]: Not used
// BIT[3:0]: Frame OFF number
/* ISP top control registers */
#define PRE_ISP_TEST_SETTING_1 0x503D // Bit[7]: Test enable
// 0: Test disable
// 1: Color bar enable
// Bit[6]: Rolling
// Bit[5]: Transparent
// Bit[4]: Square black and white
// Bit[3:2]: Color bar style
// 00: Standard 8 color bar
// 01: Gradual change at vertical mode 1
// 10: Gradual change at horizontal
// 11: Gradual change at vertical mode 2
// Bit[1:0]: Test select
// 00: Color bar
// 01: Random data
// 10: Square data
// 11: Black image
//exposure = {0x3500[3:0], 0x3501[7:0], 0x3502[7:0]} / 16 × tROW
/* AEC/AGC control functions */
#define AEC_PK_MANUAL 0x3503 // AEC Manual Mode Control
// Bit[7:6]: Reserved
// Bit[5]: Gain delay option
// Valid when 0x3503[4]=1b0
// 0: Delay one frame latch
// 1: One frame latch
// Bit[4:2]: Reserved
// Bit[1]: AGC manual
// 0: Auto enable
// 1: Manual enable
// Bit[0]: AEC manual
// 0: Auto enable
// 1: Manual enable
//gain = {0x350A[1:0], 0x350B[7:0]} / 16
/* mirror and flip registers */
#define TIMING_TC_REG20 0x3820 // Timing Control Register
// Bit[2:1]: Vertical flip enable
// 00: Normal
// 11: Vertical flip
// Bit[0]: Vertical binning enable
#define TIMING_TC_REG21 0x3821 // Timing Control Register
// Bit[5]: Compression Enable
// Bit[2:1]: Horizontal mirror enable
// 00: Normal
// 11: Horizontal mirror
// Bit[0]: Horizontal binning enable
#define CLOCK_POL_CONTROL 0x4740// Bit[5]: PCLK polarity 0: active low
// 1: active high
// Bit[3]: Gate PCLK under VSYNC
// Bit[2]: Gate PCLK under HREF
// Bit[1]: HREF polarity
// 0: active low
// 1: active high
// Bit[0] VSYNC polarity
// 0: active low
// 1: active high
#define DRIVE_CAPABILITY 0x302c // Bit[7:6]:
// 00: 1x
// 01: 2x
// 10: 3x
// 11: 4x
#define X_ADDR_ST_H 0x3800 //Bit[3:0]: X address start[11:8]
#define X_ADDR_ST_L 0x3801 //Bit[7:0]: X address start[7:0]
#define Y_ADDR_ST_H 0x3802 //Bit[2:0]: Y address start[10:8]
#define Y_ADDR_ST_L 0x3803 //Bit[7:0]: Y address start[7:0]
#define X_ADDR_END_H 0x3804 //Bit[3:0]: X address end[11:8]
#define X_ADDR_END_L 0x3805 //Bit[7:0]:
#define Y_ADDR_END_H 0x3806 //Bit[2:0]: Y address end[10:8]
#define Y_ADDR_END_L 0x3807 //Bit[7:0]:
// Size after scaling
#define X_OUTPUT_SIZE_H 0x3808 //Bit[3:0]: DVP output horizontal width[11:8]
#define X_OUTPUT_SIZE_L 0x3809 //Bit[7:0]:
#define Y_OUTPUT_SIZE_H 0x380a //Bit[2:0]: DVP output vertical height[10:8]
#define Y_OUTPUT_SIZE_L 0x380b //Bit[7:0]:
#define X_TOTAL_SIZE_H 0x380c //Bit[3:0]: Total horizontal size[11:8]
#define X_TOTAL_SIZE_L 0x380d //Bit[7:0]:
#define Y_TOTAL_SIZE_H 0x380e //Bit[7:0]: Total vertical size[15:8]
#define Y_TOTAL_SIZE_L 0x380f //Bit[7:0]:
#define X_OFFSET_H 0x3810 //Bit[3:0]: ISP horizontal offset[11:8]
#define X_OFFSET_L 0x3811 //Bit[7:0]:
#define Y_OFFSET_H 0x3812 //Bit[2:0]: ISP vertical offset[10:8]
#define Y_OFFSET_L 0x3813 //Bit[7:0]:
#define X_INCREMENT 0x3814 //Bit[7:4]: Horizontal odd subsample increment
//Bit[3:0]: Horizontal even subsample increment
#define Y_INCREMENT 0x3815 //Bit[7:4]: Vertical odd subsample increment
//Bit[3:0]: Vertical even subsample increment
// Size before scaling
//#define X_INPUT_SIZE (X_ADDR_END - X_ADDR_ST + 1 - (2 * X_OFFSET))
//#define Y_INPUT_SIZE (Y_ADDR_END - Y_ADDR_ST + 1 - (2 * Y_OFFSET))
#define ISP_CONTROL_01 0x5001 // Bit[5]: Scale enable
// 0: Disable
// 1: Enable
#define SCALE_CTRL_1 0x5601 // Bit[6:4]: HDIV RW
// DCW scale times
// 000: DCW 1 time
// 001: DCW 2 times
// 010: DCW 4 times
// 100: DCW 8 times
// 101: DCW 16 times
// Others: DCW 16 times
// Bit[2:0]: VDIV RW
// DCW scale times
// 000: DCW 1 time
// 001: DCW 2 times
// 010: DCW 4 times
// 100: DCW 8 times
// 101: DCW 16 times
// Others: DCW 16 times
#define SCALE_CTRL_2 0x5602 // X_SCALE High Bits
#define SCALE_CTRL_3 0x5603 // X_SCALE Low Bits
#define SCALE_CTRL_4 0x5604 // Y_SCALE High Bits
#define SCALE_CTRL_5 0x5605 // Y_SCALE Low Bits
#define SCALE_CTRL_6 0x5606 // Bit[3:0]: V Offset
#define PCLK_RATIO 0x3824 // Bit[4:0]: PCLK ratio manual
#define VFIFO_CTRL0C 0x460C // Bit[1]: PCLK manual enable
// 0: Auto
// 1: Manual by PCLK_RATIO
#define VFIFO_X_SIZE_H 0x4602
#define VFIFO_X_SIZE_L 0x4603
#define VFIFO_Y_SIZE_H 0x4604
#define VFIFO_Y_SIZE_L 0x4605
#define SC_PLLS_CTRL0 0x303a // Bit[7]: PLLS bypass
#define SC_PLLS_CTRL1 0x303b // Bit[4:0]: PLLS multiplier
#define SC_PLLS_CTRL2 0x303c // Bit[6:4]: PLLS charge pump control
// Bit[3:0]: PLLS system divider
#define SC_PLLS_CTRL3 0x303d // Bit[5:4]: PLLS pre-divider
// 00: 1
// 01: 1.5
// 10: 2
// 11: 3
// Bit[2]: PLLS root-divider - 1
// Bit[1:0]: PLLS seld5
// 00: 1
// 01: 1
// 10: 2
// 11: 2.5
#define COMPRESSION_CTRL00 0x4400 //
#define COMPRESSION_CTRL01 0x4401 //
#define COMPRESSION_CTRL02 0x4402 //
#define COMPRESSION_CTRL03 0x4403 //
#define COMPRESSION_CTRL04 0x4404 //
#define COMPRESSION_CTRL05 0x4405 //
#define COMPRESSION_CTRL06 0x4406 //
#define COMPRESSION_CTRL07 0x4407 // Bit[5:0]: QS
#define COMPRESSION_ISI_CTRL 0x4408 //
#define COMPRESSION_CTRL09 0x4409 //
#define COMPRESSION_CTRL0a 0x440a //
#define COMPRESSION_CTRL0b 0x440b //
#define COMPRESSION_CTRL0c 0x440c //
#define COMPRESSION_CTRL0d 0x440d //
#define COMPRESSION_CTRL0E 0x440e //
/**
* @brief register value
*/
#define TEST_COLOR_BAR 0xC0 /* Enable Color Bar roling Test */
#define AEC_PK_MANUAL_AGC_MANUALEN 0x02 /* Enable AGC Manual enable */
#define AEC_PK_MANUAL_AEC_MANUALEN 0x01 /* Enable AEC Manual enable */
#define TIMING_TC_REG20_VFLIP 0x06 /* Vertical flip enable */
#define TIMING_TC_REG21_HMIRROR 0x06 /* Horizontal mirror enable */
#endif // __OV3660_REG_REGS_H__

View File

@@ -0,0 +1,304 @@
#ifndef _OV3660_SETTINGS_H_
#define _OV3660_SETTINGS_H_
#include <stdint.h>
#include <stdbool.h>
#include "esp_attr.h"
#include "ov3660_regs.h"
#define REG_DLY 0xffff
#define REGLIST_TAIL 0x0000
const DRAM_ATTR uint16_t sensor_default_regs[][2] = {
{SYSTEM_CTROL0, 0x82}, // software reset
{REG_DLY, 10}, // delay 10ms
{0x3103, 0x13},
{SYSTEM_CTROL0, 0x42},
{0x3017, 0xff},
{0x3018, 0xff},
{DRIVE_CAPABILITY, 0xc3},
{CLOCK_POL_CONTROL, 0x21},
{0x3611, 0x01},
{0x3612, 0x2d},
{0x3032, 0x00},
{0x3614, 0x80},
{0x3618, 0x00},
{0x3619, 0x75},
{0x3622, 0x80},
{0x3623, 0x00},
{0x3624, 0x03},
{0x3630, 0x52},
{0x3632, 0x07},
{0x3633, 0xd2},
{0x3704, 0x80},
{0x3708, 0x66},
{0x3709, 0x12},
{0x370b, 0x12},
{0x3717, 0x00},
{0x371b, 0x60},
{0x371c, 0x00},
{0x3901, 0x13},
{0x3600, 0x08},
{0x3620, 0x43},
{0x3702, 0x20},
{0x3739, 0x48},
{0x3730, 0x20},
{0x370c, 0x0c},
{0x3a18, 0x00},
{0x3a19, 0xf8},
{0x3000, 0x10},
{0x3004, 0xef},
{0x6700, 0x05},
{0x6701, 0x19},
{0x6702, 0xfd},
{0x6703, 0xd1},
{0x6704, 0xff},
{0x6705, 0xff},
{0x3c01, 0x80},
{0x3c00, 0x04},
{0x3a08, 0x00}, {0x3a09, 0x62}, //50Hz Band Width Step (10bit)
{0x3a0e, 0x08}, //50Hz Max Bands in One Frame (6 bit)
{0x3a0a, 0x00}, {0x3a0b, 0x52}, //60Hz Band Width Step (10bit)
{0x3a0d, 0x09}, //60Hz Max Bands in One Frame (6 bit)
{0x3a00, 0x3a},//night mode off
{0x3a14, 0x09},
{0x3a15, 0x30},
{0x3a02, 0x09},
{0x3a03, 0x30},
{COMPRESSION_CTRL0E, 0x08},
{0x4520, 0x0b},
{0x460b, 0x37},
{0x4713, 0x02},
{0x471c, 0xd0},
{0x5086, 0x00},
{0x5002, 0x00},
{0x501f, 0x00},
{SYSTEM_CTROL0, 0x02},
{0x5180, 0xff},
{0x5181, 0xf2},
{0x5182, 0x00},
{0x5183, 0x14},
{0x5184, 0x25},
{0x5185, 0x24},
{0x5186, 0x16},
{0x5187, 0x16},
{0x5188, 0x16},
{0x5189, 0x68},
{0x518a, 0x60},
{0x518b, 0xe0},
{0x518c, 0xb2},
{0x518d, 0x42},
{0x518e, 0x35},
{0x518f, 0x56},
{0x5190, 0x56},
{0x5191, 0xf8},
{0x5192, 0x04},
{0x5193, 0x70},
{0x5194, 0xf0},
{0x5195, 0xf0},
{0x5196, 0x03},
{0x5197, 0x01},
{0x5198, 0x04},
{0x5199, 0x12},
{0x519a, 0x04},
{0x519b, 0x00},
{0x519c, 0x06},
{0x519d, 0x82},
{0x519e, 0x38},
{0x5381, 0x1d},
{0x5382, 0x60},
{0x5383, 0x03},
{0x5384, 0x0c},
{0x5385, 0x78},
{0x5386, 0x84},
{0x5387, 0x7d},
{0x5388, 0x6b},
{0x5389, 0x12},
{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},
{0x5000, 0xa7},
{0x5800, 0x0C},
{0x5801, 0x09},
{0x5802, 0x0C},
{0x5803, 0x0C},
{0x5804, 0x0D},
{0x5805, 0x17},
{0x5806, 0x06},
{0x5807, 0x05},
{0x5808, 0x04},
{0x5809, 0x06},
{0x580a, 0x09},
{0x580b, 0x0E},
{0x580c, 0x05},
{0x580d, 0x01},
{0x580e, 0x01},
{0x580f, 0x01},
{0x5810, 0x05},
{0x5811, 0x0D},
{0x5812, 0x05},
{0x5813, 0x01},
{0x5814, 0x01},
{0x5815, 0x01},
{0x5816, 0x05},
{0x5817, 0x0D},
{0x5818, 0x08},
{0x5819, 0x06},
{0x581a, 0x05},
{0x581b, 0x07},
{0x581c, 0x0B},
{0x581d, 0x0D},
{0x581e, 0x12},
{0x581f, 0x0D},
{0x5820, 0x0E},
{0x5821, 0x10},
{0x5822, 0x10},
{0x5823, 0x1E},
{0x5824, 0x53},
{0x5825, 0x15},
{0x5826, 0x05},
{0x5827, 0x14},
{0x5828, 0x54},
{0x5829, 0x25},
{0x582a, 0x33},
{0x582b, 0x33},
{0x582c, 0x34},
{0x582d, 0x16},
{0x582e, 0x24},
{0x582f, 0x41},
{0x5830, 0x50},
{0x5831, 0x42},
{0x5832, 0x15},
{0x5833, 0x25},
{0x5834, 0x34},
{0x5835, 0x33},
{0x5836, 0x24},
{0x5837, 0x26},
{0x5838, 0x54},
{0x5839, 0x25},
{0x583a, 0x15},
{0x583b, 0x25},
{0x583c, 0x53},
{0x583d, 0xCF},
{0x3a0f, 0x30},
{0x3a10, 0x28},
{0x3a1b, 0x30},
{0x3a1e, 0x28},
{0x3a11, 0x60},
{0x3a1f, 0x14},
{0x5302, 0x28},
{0x5303, 0x20},
{0x5306, 0x1c}, //de-noise offset 1
{0x5307, 0x28}, //de-noise offset 2
{0x4002, 0xc5},
{0x4003, 0x81},
{0x4005, 0x12},
{0x5688, 0x11},
{0x5689, 0x11},
{0x568a, 0x11},
{0x568b, 0x11},
{0x568c, 0x11},
{0x568d, 0x11},
{0x568e, 0x11},
{0x568f, 0x11},
{0x5580, 0x06},
{0x5588, 0x00},
{0x5583, 0x40},
{0x5584, 0x2c},
{ISP_CONTROL_01, 0x83}, // turn color matrix, awb and SDE
{REGLIST_TAIL, 0x00}, // tail
};
const DRAM_ATTR uint16_t sensor_fmt_jpeg[][2] = {
{FORMAT_CTRL, 0x00}, // YUV422
{FORMAT_CTRL00, 0x30}, // YUYV
{0x3002, 0x00},//0x1c to 0x00 !!!
{0x3006, 0xff},//0xc3 to 0xff !!!
{0x471c, 0x50},//0xd0 to 0x50 !!!
{REGLIST_TAIL, 0x00}, // tail
};
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] = {
{FORMAT_CTRL, 0x00}, // YUV422
{FORMAT_CTRL00, 0x10}, // Y8
{REGLIST_TAIL, 0x00}
};
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] = {
{FORMAT_CTRL, 0x01}, // RGB
{FORMAT_CTRL00, 0x61}, // RGB565 (BGR)
{REGLIST_TAIL, 0x00}
};
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
{0x1d, 0x60, 0x03, 0x0b, 0x6c, 0x77, 0x70, 0x60, 0x10, 0x01, 0x98},//-1
{0x1d, 0x60, 0x03, 0x0c, 0x78, 0x84, 0x7d, 0x6b, 0x12, 0x01, 0x98},//0
{0x1d, 0x60, 0x03, 0x0d, 0x84, 0x91, 0x8a, 0x76, 0x14, 0x01, 0x98},//+1
{0x1d, 0x60, 0x03, 0x0e, 0x90, 0x9e, 0x96, 0x80, 0x16, 0x01, 0x98},//+2
{0x1d, 0x60, 0x03, 0x10, 0x9c, 0xac, 0xa2, 0x8b, 0x17, 0x01, 0x98},//+3
{0x1d, 0x60, 0x03, 0x11, 0xa8, 0xb9, 0xaf, 0x96, 0x19, 0x01, 0x98},//+4
};
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
{0x1e, 0x80, 0xc0, 0x08},//Red Tint
{0x1e, 0x60, 0x60, 0x08},//Green Tint
{0x1e, 0xa0, 0x40, 0x08},//Blue Tint
{0x1e, 0x40, 0xa0, 0x08},//Sepia
};
#endif