diff --git a/CMakeLists.txt b/CMakeLists.txt index 49d5c1a32f..9d81ab5d1b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ set(COMPONENT_SRCS driver/xclk.c sensors/ov2640.c sensors/ov3660.c + sensors/ov5640.c sensors/ov7725.c conversions/yuv.c conversions/to_jpg.cpp diff --git a/Kconfig b/Kconfig index 207c307ed6..78fc607515 100755 --- a/Kconfig +++ b/Kconfig @@ -5,21 +5,28 @@ 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 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 I2C for SCCB" diff --git a/conversions/esp_jpg_decode.c b/conversions/esp_jpg_decode.c index a37878aade..d42794fcc2 100644 --- a/conversions/esp_jpg_decode.c +++ b/conversions/esp_jpg_decode.c @@ -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 diff --git a/conversions/to_jpg.cpp b/conversions/to_jpg.cpp index 812ec575e2..f8987a84e2 100644 --- a/conversions/to_jpg.cpp +++ b/conversions/to_jpg.cpp @@ -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 diff --git a/driver/camera.c b/driver/camera.c index ab040fd2da..089c0c532c 100755 --- a/driver/camera.c +++ b/driver/camera.c @@ -45,6 +45,9 @@ #if CONFIG_OV3660_SUPPORT #include "ov3660.h" #endif +#if CONFIG_OV5640_SUPPORT +#include "ov5640.h" +#endif typedef enum { CAMERA_NONE = 0, @@ -52,6 +55,7 @@ typedef enum { CAMERA_OV7725 = 7725, CAMERA_OV2640 = 2640, CAMERA_OV3660 = 3660, + CAMERA_OV5640 = 5640, } camera_model_t; #define REG_PID 0x0A @@ -80,6 +84,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; @@ -546,6 +551,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); } @@ -577,6 +583,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; @@ -669,6 +676,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) { @@ -695,6 +703,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) { @@ -728,15 +738,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++; } @@ -972,13 +986,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"); @@ -989,15 +996,13 @@ 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) - if (s_state->sensor.slv_addr == 0x30) { +#if CONFIG_OV2640_SUPPORT + if (slv_addr == 0x30) { ESP_LOGD(TAG, "Resetting OV2640"); //camera might be OV2640. try to reset it SCCB_Write(0x30, 0xFF, 0x01);//bank sensor @@ -1007,7 +1012,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); @@ -1022,7 +1030,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 @@ -1045,6 +1054,12 @@ esp_err_t camera_probe(const camera_config_t* config, camera_model_t* out_camera *out_camera_model = CAMERA_OV3660; ov3660_init(&s_state->sensor); break; +#endif +#if CONFIG_OV5640_SUPPORT + case OV5640_PID: + *out_camera_model = CAMERA_OV5640; + ov5640_init(&s_state->sensor); + break; #endif default: id->PID = 0; @@ -1072,12 +1087,46 @@ 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 + 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; @@ -1120,8 +1169,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; } @@ -1268,6 +1317,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 { ESP_LOGE(TAG, "Camera not supported"); err = ESP_ERR_CAMERA_NOT_SUPPORTED; diff --git a/driver/include/esp_camera.h b/driver/include/esp_camera.h index d7034f444d..b062dd42e7 100755 --- a/driver/include/esp_camera.h +++ b/driver/include/esp_camera.h @@ -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 diff --git a/driver/include/sensor.h b/driver/include/sensor.h index e8b4e0cc0e..3ea7e2ce7e 100755 --- a/driver/include/sensor.h +++ b/driver/include/sensor.h @@ -9,11 +9,13 @@ #ifndef __SENSOR_H__ #define __SENSOR_H__ #include +#include #define OV9650_PID (0x96) -#define OV2640_PID (0x26) #define OV7725_PID (0x77) +#define OV2640_PID (0x26) #define OV3660_PID (0x36) +#define OV5640_PID (0x56) typedef enum { PIXFORMAT_RGB565, // 2BPP/RGB565 @@ -27,23 +29,45 @@ 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 + // 3MP Sensors + FRAMESIZE_FHD, // 1920x1080 + FRAMESIZE_P_HD, // 720x1280 + FRAMESIZE_P_3MP, // 864x1536 + FRAMESIZE_QXGA, // 2048x1536 + // 5MP Sensors + FRAMESIZE_QHD, // 2560x1440 + FRAMESIZE_WQXGA, // 2560x1600 + FRAMESIZE_P_FHD, // 1080x1920 + FRAMESIZE_QSXGA, // 2560x1920 FRAMESIZE_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_9X16 +} aspect_ratio_t; + typedef enum { GAINCEILING_2X, GAINCEILING_4X, @@ -54,6 +78,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; @@ -63,6 +109,8 @@ typedef struct { typedef struct { framesize_t framesize;//0 - 10 + bool scale; + bool binning; uint8_t quality;//0 - 63 int8_t brightness;//-2 - 2 int8_t contrast;//-2 - 2 @@ -132,9 +180,12 @@ typedef struct _sensor { int (*set_raw_gma) (sensor_t *sensor, int enable); int (*set_lenc) (sensor_t *sensor, int enable); -} sensor_t; -// Resolution table (in camera.c) -extern const int resolution[][2]; + int (*get_reg) (sensor_t *sensor, int reg, int mask); + int (*set_reg) (sensor_t *sensor, int reg, int mask, int value); + int (*set_res_raw) (sensor_t *sensor, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, bool binning); + int (*set_pll) (sensor_t *sensor, int bypass, int mul, int sys, int root, int pre, int seld5, int pclken, int pclk); + int (*set_xclk) (sensor_t *sensor, int timer, int xclk); +} sensor_t; #endif /* __SENSOR_H__ */ diff --git a/driver/private_include/camera_common.h b/driver/private_include/camera_common.h index f31d045673..8a2db5ceda 100755 --- a/driver/private_include/camera_common.h +++ b/driver/private_include/camera_common.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 diff --git a/driver/sccb.c b/driver/sccb.c index aa57abc540..30e725c053 100755 --- a/driver/sccb.c +++ b/driver/sccb.c @@ -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 diff --git a/driver/sensor.c b/driver/sensor.c index ef0b005860..2e6d111020 100644 --- a/driver/sensor.c +++ b/driver/sensor.c @@ -1,17 +1,28 @@ +#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 */ + { 720, 1280, ASPECT_RATIO_9X16 }, /* Portrait HD */ + { 864, 1536, ASPECT_RATIO_9X16 }, /* Portrait 3MP */ + { 2048, 1536, ASPECT_RATIO_4X3 }, /* QXGA */ + // 5MP Sensors + { 2560, 1440, ASPECT_RATIO_16X9 }, /* QHD */ + { 2560, 1600, ASPECT_RATIO_16X10 }, /* WQXGA */ + { 1088, 1920, ASPECT_RATIO_9X16 }, /* Portrait FHD */ + { 2560, 1920, ASPECT_RATIO_4X3 }, /* QSXGA */ }; diff --git a/driver/xclk.c b/driver/xclk.c index 19a8ebb93b..f58747d5f2 100755 --- a/driver/xclk.c +++ b/driver/xclk.c @@ -12,19 +12,28 @@ static const char* TAG = "camera_xclk"; #endif -esp_err_t camera_enable_out_clock(camera_config_t* config) +esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz) { - periph_module_enable(PERIPH_LEDC_MODULE); - - ledc_timer_config_t timer_conf = {}; + ledc_timer_config_t timer_conf; timer_conf.duty_resolution = 2; - timer_conf.freq_hz = config->xclk_freq_hz; + timer_conf.freq_hz = xclk_freq_hz; timer_conf.speed_mode = LEDC_HIGH_SPEED_MODE; - timer_conf.timer_num = config->ledc_timer; -#ifdef ESP_IDF_VERSION_MAJOR +#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); + + 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; diff --git a/sensors/ov2640.c b/sensors/ov2640.c index 71cfa7e8ab..811023c061 100755 --- a/sensors/ov2640.c +++ b/sensors/ov2640.c @@ -133,133 +133,100 @@ static int set_pixformat(sensor_t *sensor, pixformat_t pixformat) return ret; } -//Functions are not needed currently -#if 0 -//Set the sensor output window -int set_output_window(sensor_t *sensor, uint16_t x, uint16_t y, uint16_t width, uint16_t height) -{ +static int set_window(sensor_t *sensor, ov2640_sensor_mode_t mode, int offset_x, int offset_y, int max_x, int max_y, int w, int h){ int ret = 0; - uint16_t endx, endy; - uint8_t com1, reg32; - - endy = y + height / 2; - com1 = read_reg(sensor, BANK_SENSOR, COM1); - WRITE_REG_OR_RETURN(BANK_SENSOR, COM1, (com1 & 0XF0) | (((endy & 0X03) << 2) | (y & 0X03))); - WRITE_REG_OR_RETURN(BANK_SENSOR, VSTART, y >> 2); - WRITE_REG_OR_RETURN(BANK_SENSOR, VSTOP, endy >> 2); - - endx = x + width / 2; - reg32 = read_reg(sensor, BANK_SENSOR, REG32); - WRITE_REG_OR_RETURN(BANK_SENSOR, REG32, (reg32 & 0XC0) | (((endx & 0X07) << 3) | (x & 0X07))); - WRITE_REG_OR_RETURN(BANK_SENSOR, HSTART, x >> 3); - WRITE_REG_OR_RETURN(BANK_SENSOR, HSTOP, endx >> 3); - - return ret; -} - -// Set the image output size (final output resolution) -int set_output_size(sensor_t *sensor, uint16_t width, uint16_t height) -{ - int ret = 0; - uint16_t h, w; - - if(width % 4) { - return -1; - } - if(height % 4 ) { - return -2; + const uint8_t (*regs)[2]; + ov2640_clk_t c; + c.reserved = 0; + + max_x /= 4; + max_y /= 4; + w /= 4; + h /= 4; + uint8_t win_regs[][2] = { + {BANK_SEL, BANK_DSP}, + {HSIZE, max_x & 0xFF}, + {VSIZE, max_y & 0xFF}, + {XOFFL, offset_x & 0xFF}, + {YOFFL, offset_y & 0xFF}, + {VHYX, ((max_y >> 1) & 0X80) | ((offset_y >> 4) & 0X70) | ((max_x >> 5) & 0X08) | ((offset_y >> 8) & 0X07)}, + {TEST, (max_x >> 2) & 0X80}, + {ZMOW, (w)&0xFF}, + {ZMOH, (h)&0xFF}, + {ZMHH, ((h>>6)&0x04)|((w>>8)&0x03)}, + {0, 0} + }; + + c.pclk_auto = 0; + c.pclk_div = 8; + c.clk_2x = 0; + c.clk_div = 0; + + if(sensor->pixformat != PIXFORMAT_JPEG){ + c.pclk_auto = 1; + c.clk_div = 7; } - w = width / 4; - h = height / 4; - //WRITE_REG_OR_RETURN(BANK_DSP, RESET, RESET_DVP); - WRITE_REG_OR_RETURN(BANK_DSP, ZMOW, w & 0XFF); - WRITE_REG_OR_RETURN(BANK_DSP, ZMOH, h & 0XFF); - WRITE_REG_OR_RETURN(BANK_DSP, ZMHH, ((w >> 8) & 0X03) | ((h >> 6) & 0X04)); - //WRITE_REG_OR_RETURN(BANK_DSP, RESET, 0X00); - - return ret; -} - -//Set the image window size >= output size -int set_window_size(sensor_t *sensor, uint16_t x, uint16_t y, uint16_t width, uint16_t height) -{ - int ret = 0; - uint16_t w, h; - - if(width % 4) { - return -1; - } - if(height % 4) { - return -2; + if (mode == OV2640_MODE_CIF) { + regs = ov2640_settings_to_cif; + if(sensor->pixformat != PIXFORMAT_JPEG){ + c.clk_div = 3; + } + } else if (mode == OV2640_MODE_SVGA) { + regs = ov2640_settings_to_svga; + } else { + regs = ov2640_settings_to_uxga; + c.pclk_div = 12; } - w = width / 4; - h = height / 4; - //WRITE_REG_OR_RETURN(BANK_DSP, RESET, RESET_DVP); - WRITE_REG_OR_RETURN(BANK_DSP, HSIZE, w & 0XFF); - WRITE_REG_OR_RETURN(BANK_DSP, VSIZE, h & 0XFF); - WRITE_REG_OR_RETURN(BANK_DSP, XOFFL, x & 0XFF); - WRITE_REG_OR_RETURN(BANK_DSP, YOFFL, y & 0XFF); - WRITE_REG_OR_RETURN(BANK_DSP, VHYX, ((h >> 1) & 0X80) | ((y >> 4) & 0X70) | ((w >> 5) & 0X08) | ((x >> 8) & 0X07)); - WRITE_REG_OR_RETURN(BANK_DSP, TEST, (w >> 2) & 0X80); - //WRITE_REG_OR_RETURN(BANK_DSP, RESET, 0X00); - - return ret; -} + WRITE_REG_OR_RETURN(BANK_DSP, R_BYPASS, R_BYPASS_DSP_BYPAS); + WRITE_REGS_OR_RETURN(regs); + WRITE_REGS_OR_RETURN(win_regs); + WRITE_REG_OR_RETURN(BANK_SENSOR, CLKRC, c.clk); + WRITE_REG_OR_RETURN(BANK_DSP, R_DVP_SP, c.pclk); + WRITE_REG_OR_RETURN(BANK_DSP, R_BYPASS, R_BYPASS_DSP_EN); -//Set the sensor resolution (UXGA, SVGA, CIF) -int set_image_size(sensor_t *sensor, uint16_t width, uint16_t height) -{ - int ret = 0; - //WRITE_REG_OR_RETURN(BANK_DSP, RESET, RESET_DVP); - WRITE_REG_OR_RETURN(BANK_DSP, HSIZE8, (width >> 3) & 0XFF); - WRITE_REG_OR_RETURN(BANK_DSP, VSIZE8, (height >> 3) & 0XFF); - WRITE_REG_OR_RETURN(BANK_DSP, SIZEL, ((width & 0X07) << 3) | ((width >> 4) & 0X80) | (height & 0X07)); - //WRITE_REG_OR_RETURN(BANK_DSP, RESET, 0X00); + vTaskDelay(10 / portTICK_PERIOD_MS); + //required when changing resolution + set_pixformat(sensor, sensor->pixformat); return ret; } -#endif 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]; - const uint8_t (*regs)[2]; + uint16_t w = resolution[framesize].width; + uint16_t h = resolution[framesize].height; + aspect_ratio_t ratio = resolution[framesize].aspect_ratio; + uint16_t max_x = ratio_table[ratio].max_x; + uint16_t max_y = ratio_table[ratio].max_y; + uint16_t offset_x = ratio_table[ratio].offset_x; + uint16_t offset_y = ratio_table[ratio].offset_y; + ov2640_sensor_mode_t mode = OV2640_MODE_UXGA; sensor->status.framesize = framesize; - if (framesize <= FRAMESIZE_CIF) { - regs = ov2640_settings_to_cif; - } else if (framesize <= FRAMESIZE_SVGA) { - regs = ov2640_settings_to_svga; - } else { - regs = ov2640_settings_to_uxga; - } - WRITE_REG_OR_RETURN(BANK_DSP, R_BYPASS, R_BYPASS_DSP_BYPAS); - WRITE_REGS_OR_RETURN(regs); - if (sensor->pixformat == PIXFORMAT_JPEG && sensor->xclk_freq_hz == 10000000) { - if (framesize <= FRAMESIZE_CIF) { - WRITE_REG_OR_RETURN(BANK_SENSOR, CLKRC, CLKRC_2X_CIF); - } else if (framesize <= FRAMESIZE_SVGA) { - WRITE_REG_OR_RETURN(BANK_SENSOR, CLKRC, CLKRC_2X_SVGA); - } else { - WRITE_REG_OR_RETURN(BANK_SENSOR, CLKRC, CLKRC_2X_UXGA); + + if (framesize <= FRAMESIZE_CIF) { + mode = OV2640_MODE_CIF; + max_x /= 4; + max_y /= 4; + offset_x /= 4; + offset_y /= 4; + if(max_y > 296){ + max_y = 296; } + } else if (framesize <= FRAMESIZE_SVGA) { + mode = OV2640_MODE_SVGA; + max_x /= 2; + max_y /= 2; + offset_x /= 2; + offset_y /= 2; } - WRITE_REG_OR_RETURN(BANK_DSP, ZMOW, (w>>2)&0xFF); // OUTW[7:0] (real/4) - WRITE_REG_OR_RETURN(BANK_DSP, ZMOH, (h>>2)&0xFF); // OUTH[7:0] (real/4) - WRITE_REG_OR_RETURN(BANK_DSP, ZMHH, ((h>>8)&0x04)|((w>>10)&0x03)); // OUTH[8]/OUTW[9:8] - WRITE_REG_OR_RETURN(BANK_DSP, RESET, 0x00); - WRITE_REG_OR_RETURN(BANK_DSP, R_BYPASS, R_BYPASS_DSP_EN); - - vTaskDelay(10 / portTICK_PERIOD_MS); - //required when changing resolution - set_pixformat(sensor, sensor->pixformat); + ret = set_window(sensor, mode, offset_x, offset_y, max_x, max_y, w, h); return ret; } @@ -482,6 +449,46 @@ static int set_denoise(sensor_t *sensor, int level) return -1; } +static int get_reg(sensor_t *sensor, int reg, int mask) +{ + int ret = read_reg(sensor, (reg >> 8) & 0x01, reg & 0xFF); + if(ret > 0){ + ret &= mask; + } + return ret; +} + +static int set_reg(sensor_t *sensor, int reg, int mask, int value) +{ + int ret = 0; + ret = read_reg(sensor, (reg >> 8) & 0x01, reg & 0xFF); + if(ret < 0){ + return ret; + } + value = (ret & ~mask) | (value & mask); + ret = write_reg(sensor, (reg >> 8) & 0x01, reg & 0xFF, value); + return ret; +} + +static int set_res_raw(sensor_t *sensor, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, bool binning) +{ + return set_window(sensor, (ov2640_sensor_mode_t)startX, offsetX, offsetY, totalX, totalY, outputX, outputY); +} + +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) +{ + return -1; +} + +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; sensor->status.contrast = 0; @@ -562,6 +569,12 @@ int ov2640_init(sensor_t *sensor) //not supported sensor->set_sharpness = set_sharpness; sensor->set_denoise = set_denoise; + + sensor->get_reg = get_reg; + sensor->set_reg = set_reg; + sensor->set_res_raw = set_res_raw; + sensor->set_pll = _set_pll; + sensor->set_xclk = set_xclk; ESP_LOGD(TAG, "OV2640 Attached"); return 0; } diff --git a/sensors/ov3660.c b/sensors/ov3660.c index f59011dd59..723ec5c66f 100755 --- a/sensors/ov3660.c +++ b/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 }; @@ -252,12 +252,12 @@ static int set_image_options(sensor_t *sensor) } // binning - if (sensor->status.framesize > FRAMESIZE_SVGA) { - reg20 |= 0x40; - } else { + if (sensor->status.binning) { reg20 |= 0x01; reg21 |= 0x01; reg4514_test |= 4; + } else { + reg20 |= 0x40; } // V-Flip @@ -292,8 +292,18 @@ static int set_image_options(sensor_t *sensor) ret = -1; } + if (sensor->status.binning) { + 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 + } + 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, sensor->status.binning, sensor->status.vflip, sensor->status.hmirror, reg4514); return ret; } @@ -303,51 +313,37 @@ 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]; - - 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 - } + 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]; - if (ret) { - goto fail; - } + sensor->status.binning = (w <= (settings.max_width / 2) && h <= (settings.max_height / 2)); + sensor->status.scale = !((w == settings.max_width && h == settings.max_height) + || (w == (settings.max_width / 2) && h == (settings.max_height / 2))); - 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 (sensor->status.binning) { + 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, sensor->status.scale); } if (ret == 0) { @@ -880,6 +876,92 @@ 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, ret2 = 0; + if(mask > 0xFF){ + ret = read_reg16(sensor->slv_addr, reg); + if(ret >= 0 && mask > 0xFFFF){ + ret2 = read_reg(sensor->slv_addr, reg+2); + if(ret2 >= 0){ + ret = (ret << 8) | ret2 ; + } else { + ret = ret2; + } + } + } else { + ret = read_reg(sensor->slv_addr, reg); + } + if(ret > 0){ + ret &= mask; + } + return ret; +} + +static int set_reg(sensor_t *sensor, int reg, int mask, int value) +{ + int ret = 0, ret2 = 0; + if(mask > 0xFF){ + ret = read_reg16(sensor->slv_addr, reg); + if(ret >= 0 && mask > 0xFFFF){ + ret2 = read_reg(sensor->slv_addr, reg+2); + if(ret2 >= 0){ + ret = (ret << 8) | ret2 ; + } else { + ret = ret2; + } + } + } else { + ret = read_reg(sensor->slv_addr, reg); + } + if(ret < 0){ + return ret; + } + value = (ret & ~mask) | (value & mask); + if(mask > 0xFFFF){ + ret = write_reg16(sensor->slv_addr, reg, value >> 8); + if(ret >= 0){ + ret = write_reg(sensor->slv_addr, reg+2, value & 0xFF); + } + } else if(mask > 0xFF){ + ret = write_reg16(sensor->slv_addr, reg, value); + } else { + ret = write_reg(sensor->slv_addr, reg, value); + } + return ret; +} + +static int set_res_raw(sensor_t *sensor, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, bool binning) +{ + int ret = 0; + ret = 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); + if(!ret){ + sensor->status.scale = scale; + sensor->status.binning = binning; + ret = set_image_options(sensor); + } + 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) +{ + return set_pll(sensor, bypass > 0, multiplier, sys_div, pre_div, root_2x > 0, seld5, pclk_manual > 0, pclk_div); +} + +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 +1023,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 = set_reg; + sensor->set_res_raw = set_res_raw; + sensor->set_pll = _set_pll; + sensor->set_xclk = set_xclk; return 0; } diff --git a/sensors/ov5640.c b/sensors/ov5640.c new file mode 100755 index 0000000000..e7adcf4603 --- /dev/null +++ b/sensors/ov5640.c @@ -0,0 +1,1105 @@ +/* + * This file is part of the OpenMV project. + * Copyright (c) 2013/2014 Ibrahim Abdelkader + * This work is licensed under the MIT license, see the file LICENSE for details. + * + * OV3660 driver. + * + */ +#include +#include +#include +#include "sccb.h" +#include "ov5640.h" +#include "ov5640_regs.h" +#include "ov5640_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 = "ov5640"; +#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 void dump_reg(sensor_t *sensor, const uint16_t reg){ +// int v = SCCB_Read16(sensor->slv_addr, reg); +// if(v < 0){ +// ets_printf(" 0x%04x: FAIL[%d]\n", reg, v); +// } else { +// ets_printf(" 0x%04x: 0x%02X\n", reg, v); +// } +//} +// +//static void dump_range(sensor_t *sensor, const char * name, const uint16_t start_reg, const uint16_t end_reg){ +// ets_printf("%s: 0x%04x - 0x%04X\n", name, start_reg, end_reg); +// for(uint16_t reg = start_reg; reg <= end_reg; reg++){ +// dump_reg(sensor, reg); +// } +//} +// +//static void dump_regs(sensor_t *sensor){ +//// dump_range(sensor, "All Regs", 0x3000, 0x6100); +//// dump_range(sensor, "system and IO pad control", 0x3000, 0x3052); +//// dump_range(sensor, "SCCB control", 0x3100, 0x3108); +//// dump_range(sensor, "SRB control", 0x3200, 0x3211); +//// dump_range(sensor, "AWB gain control", 0x3400, 0x3406); +//// dump_range(sensor, "AEC/AGC control", 0x3500, 0x350D); +//// dump_range(sensor, "VCM control", 0x3600, 0x3606); +//// dump_range(sensor, "timing control", 0x3800, 0x3821); +//// dump_range(sensor, "AEC/AGC power down domain control", 0x3A00, 0x3A25); +//// dump_range(sensor, "strobe control", 0x3B00, 0x3B0C); +//// dump_range(sensor, "50/60Hz detector control", 0x3C00, 0x3C1E); +//// dump_range(sensor, "OTP control", 0x3D00, 0x3D21); +//// dump_range(sensor, "MC control", 0x3F00, 0x3F0D); +//// dump_range(sensor, "BLC control", 0x4000, 0x4033); +//// dump_range(sensor, "frame control", 0x4201, 0x4202); +//// dump_range(sensor, "format control", 0x4300, 0x430D); +//// dump_range(sensor, "JPEG control", 0x4400, 0x4431); +//// dump_range(sensor, "VFIFO control", 0x4600, 0x460D); +//// dump_range(sensor, "DVP control", 0x4709, 0x4745); +//// dump_range(sensor, "MIPI control", 0x4800, 0x4837); +//// dump_range(sensor, "ISP frame control", 0x4901, 0x4902); +//// dump_range(sensor, "ISP top control", 0x5000, 0x5063); +//// dump_range(sensor, "AWB control", 0x5180, 0x51D0); +//// dump_range(sensor, "CIP control", 0x5300, 0x530F); +//// dump_range(sensor, "CMX control", 0x5380, 0x538B); +//// dump_range(sensor, "gamma control", 0x5480, 0x5490); +//// dump_range(sensor, "SDE control", 0x5580, 0x558C); +//// dump_range(sensor, "scale control", 0x5600, 0x5606); +//// dump_range(sensor, "AVG control", 0x5680, 0x56A2); +//// dump_range(sensor, "LENC control", 0x5800, 0x5849); +//// dump_range(sensor, "AFC control", 0x6000, 0x603F); +//} + +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) + +static int calc_sysclk(int xclk, bool pll_bypass, int pll_multiplier, int pll_sys_div, int pre_div, bool root_2x, int pclk_root_div, bool pclk_manual, int pclk_div) +{ + const float pll_pre_div2x_map[] = { 1, 1, 2, 3, 4, 1.5, 6, 2.5, 8}; + const int pll_pclk_root_div_map[] = { 1, 2, 4, 8 }; + + if(!pll_sys_div) { + pll_sys_div = 1; + } + + float pll_pre_div = pll_pre_div2x_map[pre_div]; + unsigned int root_2x_div = root_2x?2:1; + unsigned int pll_pclk_root_div = pll_pclk_root_div_map[pclk_root_div]; + + unsigned int REFIN = xclk / pll_pre_div; + + unsigned int VCO = REFIN * pll_multiplier / root_2x_div; + + unsigned int PLL_CLK = pll_bypass?(xclk):(VCO / pll_sys_div * 2 / 5);//5 here is 10bit mode / 2, for 8bit it should be 4 (reg 0x3034) + + unsigned int PCLK = PLL_CLK / pll_pclk_root_div / ((pclk_manual && pclk_div)?pclk_div:2); + + unsigned int SYSCLK = PLL_CLK / 4; + + ESP_LOGD(TAG, "Calculated XVCLK: %d Hz, REFIN: %u Hz, VCO: %u Hz, PLL_CLK: %u Hz, SYSCLK: %u Hz, PCLK: %u Hz", xclk, REFIN, VCO, PLL_CLK, 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 pclk_root_div, bool pclk_manual, uint8_t pclk_div){ + int ret = 0; + if(multiplier > 252 || multiplier < 4 || sys_div > 15 || pre_div > 8 || pclk_div > 31 || pclk_root_div > 3){ + ESP_LOGE(TAG, "Invalid arguments"); + return -1; + } + if(multiplier > 127){ + multiplier &= 0xFE;//only even integers above 127 + } + + calc_sysclk(sensor->xclk_freq_hz, bypass, multiplier, sys_div, pre_div, root_2x, pclk_root_div, pclk_manual, pclk_div); + + ret = write_reg(sensor->slv_addr, 0x3039, bypass?0x80:0x00); + if (ret == 0) { + ret = write_reg(sensor->slv_addr, 0x3034, 0x1A);//10bit mode + } + if (ret == 0) { + ret = write_reg(sensor->slv_addr, 0x3035, 0x01 | ((sys_div & 0x0f) << 4)); + } + if (ret == 0) { + ret = write_reg(sensor->slv_addr, 0x3036, multiplier & 0xff); + } + if (ret == 0) { + ret = write_reg(sensor->slv_addr, 0x3037, (pre_div & 0xf) | (root_2x?0x10:0x00)); + } + if (ret == 0) { + ret = write_reg(sensor->slv_addr, 0x3108, (pclk_root_div & 0x3) << 4 | 0x06); + } + if (ret == 0) { + ret = write_reg(sensor->slv_addr, 0x3824, pclk_div & 0x1f); + } + if (ret == 0) { + ret = write_reg(sensor->slv_addr, 0x460C, pclk_manual?0x22:0x20); + } + if (ret == 0) { + ret = write_reg(sensor->slv_addr, 0x3103, 0x13);// system clock from pll, bit[1] + } + 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) +{ + //dump_regs(sensor); + vTaskDelay(100 / portTICK_PERIOD_MS); + 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"); + vTaskDelay(100 / portTICK_PERIOD_MS); + //write_regs(sensor->slv_addr, sensor_regs_awb0); + //write_regs(sensor->slv_addr, sensor_regs_gamma1); + } + 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.binning) { + 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 = 0x00; break;//v-flip + case 2: reg4514 = 0xbb; break;//h-mirror + case 3: reg4514 = 0x00; 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"); + return -1; + } + + if (!sensor->status.binning) { + ret = write_reg(sensor->slv_addr, 0x4520, 0x10) + || 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 + } + + 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.binning, 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_QSXGA){ + ESP_LOGE(TAG, "Invalid framesize: %u", framesize); + return -1; + } + uint16_t w = resolution[framesize].width; + uint16_t h = resolution[framesize].height; + aspect_ratio_t ratio = resolution[framesize].aspect_ratio; + ratio_settings_t settings = ratio_table[ratio]; + + sensor->status.binning = (w <= (settings.max_width / 2) && h <= (settings.max_height / 2)); + sensor->status.scale = !((w == settings.max_width && h == settings.max_height) + || (w == (settings.max_width / 2) && h == (settings.max_height / 2))); + + 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 (!sensor->status.binning) { + 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, settings.offset_x, settings.offset_y); + } else { + if (w > 920) { + ret = write_addr_reg(sensor->slv_addr, X_TOTAL_SIZE_H, settings.total_x - 200, settings.total_y / 2); + } else { + ret = write_addr_reg(sensor->slv_addr, X_TOTAL_SIZE_H, 2060, settings.total_y / 2); + } + if (ret == 0) { + ret = write_addr_reg(sensor->slv_addr, X_OFFSET_H, settings.offset_x / 2, settings.offset_y / 2); + } + } + + if (ret == 0) { + ret = write_reg_bits(sensor->slv_addr, ISP_CONTROL_01, 0x20, sensor->status.scale); + } + + if (ret == 0) { + ret = set_image_options(sensor); + } + + if (ret) { + goto fail; + } + + if (sensor->pixformat == PIXFORMAT_JPEG) { + //10MHz PCLK + uint8_t sys_mul = 200; + if(framesize < FRAMESIZE_QVGA){ + sys_mul = 160; + } else if(framesize < FRAMESIZE_XGA){ + sys_mul = 180; + } + ret = set_pll(sensor, false, sys_mul, 4, 2, false, 2, true, 4); + } else { + ret = set_pll(sensor, false, 10, 1, 1, false, 1, true, 4); + } + + 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 get_reg(sensor_t *sensor, int reg, int mask) +{ + int ret = 0, ret2 = 0; + if(mask > 0xFF){ + ret = read_reg16(sensor->slv_addr, reg); + if(ret >= 0 && mask > 0xFFFF){ + ret2 = read_reg(sensor->slv_addr, reg+2); + if(ret2 >= 0){ + ret = (ret << 8) | ret2 ; + } else { + ret = ret2; + } + } + } else { + ret = read_reg(sensor->slv_addr, reg); + } + if(ret > 0){ + ret &= mask; + } + return ret; +} + +static int set_reg(sensor_t *sensor, int reg, int mask, int value) +{ + int ret = 0, ret2 = 0; + if(mask > 0xFF){ + ret = read_reg16(sensor->slv_addr, reg); + if(ret >= 0 && mask > 0xFFFF){ + ret2 = read_reg(sensor->slv_addr, reg+2); + if(ret2 >= 0){ + ret = (ret << 8) | ret2 ; + } else { + ret = ret2; + } + } + } else { + ret = read_reg(sensor->slv_addr, reg); + } + if(ret < 0){ + return ret; + } + value = (ret & ~mask) | (value & mask); + if(mask > 0xFFFF){ + ret = write_reg16(sensor->slv_addr, reg, value >> 8); + if(ret >= 0){ + ret = write_reg(sensor->slv_addr, reg+2, value & 0xFF); + } + } else if(mask > 0xFF){ + ret = write_reg16(sensor->slv_addr, reg, value); + } else { + ret = write_reg(sensor->slv_addr, reg, value); + } + return ret; +} + +static int set_res_raw(sensor_t *sensor, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, bool binning) +{ + int ret = 0; + ret = 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); + if(!ret){ + sensor->status.scale = scale; + sensor->status.binning = binning; + ret = set_image_options(sensor); + } + 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; + 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 ov5640_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; + + sensor->get_reg = get_reg; + sensor->set_reg = set_reg; + sensor->set_res_raw = set_res_raw; + sensor->set_pll = _set_pll; + sensor->set_xclk = set_xclk; + return 0; +} diff --git a/sensors/ov7725.c b/sensors/ov7725.c index 78175aad1f..b4e67109f9 100755 --- a/sensors/ov7725.c +++ b/sensors/ov7725.c @@ -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; diff --git a/sensors/private_include/ov2640_settings.h b/sensors/private_include/ov2640_settings.h index d415d20491..f151f0a428 100644 --- a/sensors/private_include/ov2640_settings.h +++ b/sensors/private_include/ov2640_settings.h @@ -19,6 +19,48 @@ #include "esp_attr.h" #include "ov2640_regs.h" +typedef enum { + OV2640_MODE_UXGA, OV2640_MODE_SVGA, OV2640_MODE_CIF, OV2640_MODE_MAX +} ov2640_sensor_mode_t; + +typedef struct { + union { + struct { + uint8_t pclk_div:7; + uint8_t pclk_auto:1; + }; + uint8_t pclk; + }; + union { + struct { + uint8_t clk_div:6; + uint8_t reserved:1; + uint8_t clk_2x:1; + }; + uint8_t clk; + }; +} ov2640_clk_t; + +typedef struct { + uint16_t offset_x; + uint16_t offset_y; + uint16_t max_x; + uint16_t max_y; +} ov2640_ratio_settings_t; + +static const DRAM_ATTR ov2640_ratio_settings_t ratio_table[] = { + // ox, oy, mx, my + { 0, 0, 1600, 1200 }, //4x3 + { 8, 72, 1584, 1056 }, //3x2 + { 0, 100, 1600, 1000 }, //16x10 + { 0, 120, 1600, 960 }, //5x3 + { 0, 150, 1600, 900 }, //16x9 + { 2, 258, 1596, 684 }, //21x9 + { 50, 0, 1500, 1200 }, //5x4 + { 200, 0, 1200, 1200 }, //1x1 + { 462, 0, 676, 1200 } //9x16 +}; + // 30fps@24MHz const DRAM_ATTR uint8_t ov2640_settings_cif[][2] = { {BANK_SEL, BANK_DSP}, @@ -193,7 +235,7 @@ const DRAM_ATTR uint8_t ov2640_settings_to_cif[][2] = { {VSTART, 0x00}, {VSTOP, 0x25}, - {CLKRC, 0x01}, + //{CLKRC, 0x00}, {BD50, 0xca}, {BD60, 0xa8}, {0x5a, 0x23}, @@ -228,7 +270,7 @@ const DRAM_ATTR uint8_t ov2640_settings_to_cif[][2] = { {CTRL2, CTRL2_DCW_EN | 0x1D}, {CTRLI, CTRLI_LP_DP | 0x00}, - {R_DVP_SP, 0x82}, + //{R_DVP_SP, 0x08}, {0, 0} }; @@ -244,7 +286,7 @@ const DRAM_ATTR uint8_t ov2640_settings_to_svga[][2] = { {VSTART, 0x00}, {VSTOP, 0x4b}, - {CLKRC, 0x01}, + //{CLKRC, 0x00}, {0x37, 0xc0}, {BD50, 0xca}, {BD60, 0xa8}, @@ -281,7 +323,7 @@ const DRAM_ATTR uint8_t ov2640_settings_to_svga[][2] = { {CTRL2, CTRL2_DCW_EN | 0x1D}, {CTRLI, CTRLI_LP_DP | 0x00}, - {R_DVP_SP, 0x80}, + //{R_DVP_SP, 0x08}, {0, 0} }; @@ -297,7 +339,7 @@ const DRAM_ATTR uint8_t ov2640_settings_to_uxga[][2] = { {VSTART, 0x01}, {VSTOP, 0x97}, - {CLKRC, 0x01}, + //{CLKRC, 0x00}, {0x3d, 0x34}, {BD50, 0xbb}, {BD60, 0x9c}, @@ -333,7 +375,7 @@ const DRAM_ATTR uint8_t ov2640_settings_to_uxga[][2] = { {CTRL2, CTRL2_DCW_EN | 0x1d}, {CTRLI, 0x00}, - {R_DVP_SP, 0x82}, + //{R_DVP_SP, 0x06}, {0, 0} }; @@ -348,7 +390,6 @@ const DRAM_ATTR uint8_t ov2640_settings_jpeg3[][2] = { {0xDF, 0x80}, {0x33, 0x80}, {0x3C, 0x10}, - {R_DVP_SP, 0x04}, {0xEB, 0x30}, {0xDD, 0x7F}, {RESET, 0x00}, diff --git a/sensors/private_include/ov3660_settings.h b/sensors/private_include/ov3660_settings.h index 3b20ad49dd..97c4e03b66 100644 --- a/sensors/private_include/ov3660_settings.h +++ b/sensors/private_include/ov3660_settings.h @@ -6,10 +6,23 @@ #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 + { 864, 1536, 592, 0, 1487, 1547, 16, 6, 2044, 1564 } //9x16 +}; + #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 +144,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 +261,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 +270,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 +305,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 diff --git a/sensors/private_include/ov5640.h b/sensors/private_include/ov5640.h new file mode 100755 index 0000000000..7b572ad340 --- /dev/null +++ b/sensors/private_include/ov5640.h @@ -0,0 +1,9 @@ + +#ifndef __OV5640_H__ +#define __OV5640_H__ + +#include "sensor.h" + +int ov5640_init(sensor_t *sensor); + +#endif // __OV5640_H__ diff --git a/sensors/private_include/ov5640_regs.h b/sensors/private_include/ov5640_regs.h new file mode 100755 index 0000000000..c28d80f5bb --- /dev/null +++ b/sensors/private_include/ov5640_regs.h @@ -0,0 +1,213 @@ +/* + * OV5640 register definitions. + */ +#ifndef __OV5640_REG_REGS_H__ +#define __OV5640_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 + +#define DRIVE_CAPABILITY 0x302c // Bit[7:6]: + // 00: 1x + // 01: 2x + // 10: 3x + // 11: 4x + +#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 + +/* 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]=1’b0 + // 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 + + +#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)) + +/* 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 PCLK_RATIO 0x3824 // Bit[4:0]: PCLK ratio manual + +/* 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 + +/* format control registers */ +#define FORMAT_CTRL00 0x4300 + +#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 ISP_CONTROL_01 0x5001 // Bit[5]: Scale enable + // 0: Disable + // 1: Enable + +/* 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 + +/* 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 + +#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 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 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__ diff --git a/sensors/private_include/ov5640_settings.h b/sensors/private_include/ov5640_settings.h new file mode 100644 index 0000000000..fec7d679f5 --- /dev/null +++ b/sensors/private_include/ov5640_settings.h @@ -0,0 +1,334 @@ +#ifndef _OV5640_SETTINGS_H_ +#define _OV5640_SETTINGS_H_ + +#include +#include +#include "esp_attr.h" +#include "ov5640_regs.h" + +static const ratio_settings_t ratio_table[] = { + // mw, mh, sx, sy, ex, ey, ox, oy, tx, ty + { 2560, 1920, 0, 0, 2623, 1951, 32, 16, 2844, 1968 }, //4x3 + { 2560, 1704, 0, 110, 2623, 1843, 32, 16, 2844, 1752 }, //3x2 + { 2560, 1600, 0, 160, 2623, 1791, 32, 16, 2844, 1648 }, //16x10 + { 2560, 1536, 0, 192, 2623, 1759, 32, 16, 2844, 1584 }, //5x3 + { 2560, 1440, 0, 240, 2623, 1711, 32, 16, 2844, 1488 }, //16x9 + { 2560, 1080, 0, 420, 2623, 1531, 32, 16, 2844, 1128 }, //21x9 + { 2400, 1920, 80, 0, 2543, 1951, 32, 16, 2684, 1968 }, //5x4 + { 1920, 1920, 320, 0, 2543, 1951, 32, 16, 2684, 1968 }, //1x1 + { 1088, 1920, 736, 0, 1887, 1951, 32, 16, 1884, 1968 } //9x16 +}; + +#define REG_DLY 0xffff +#define REGLIST_TAIL 0x0000 + +static const DRAM_ATTR uint16_t sensor_default_regs[][2] = { + {SYSTEM_CTROL0, 0x82}, // software reset + {REG_DLY, 10}, // delay 10ms + {SYSTEM_CTROL0, 0x42}, // power down + + //enable pll + {0x3103, 0x13}, + + //io direction + {0x3017, 0xff}, + {0x3018, 0xff}, + + {DRIVE_CAPABILITY, 0xc3}, + {CLOCK_POL_CONTROL, 0x21}, + + {0x4713, 0x02},//jpg mode select + + {ISP_CONTROL_01, 0x83}, // turn color matrix, awb and SDE + + //sys reset + {0x3000, 0x00}, + {0x3002, 0x1c}, + + //clock enable + {0x3004, 0xff}, + {0x3006, 0xc3}, + + //isp control + {0x5000, 0xa7}, + {ISP_CONTROL_01, 0xa3},//+scaling? + {0x5003, 0x08},//special_effect + + //unknown + {0x370c, 0x02},//!!IMPORTANT + {0x3634, 0x40},//!!IMPORTANT + + //AEC/AGC + {0x3a02, 0x03}, + {0x3a03, 0xd8}, + {0x3a08, 0x01}, + {0x3a09, 0x27}, + {0x3a0a, 0x00}, + {0x3a0b, 0xf6}, + {0x3a0d, 0x04}, + {0x3a0e, 0x03}, + {0x3a0f, 0x30},//ae_level + {0x3a10, 0x28},//ae_level + {0x3a11, 0x60},//ae_level + {0x3a13, 0x43}, + {0x3a14, 0x03}, + {0x3a15, 0xd8}, + {0x3a18, 0x00},//gainceiling + {0x3a19, 0xf8},//gainceiling + {0x3a1b, 0x30},//ae_level + {0x3a1e, 0x26},//ae_level + {0x3a1f, 0x14},//ae_level + + //vcm debug + {0x3600, 0x08}, + {0x3601, 0x33}, + + //50/60Hz + {0x3c01, 0xa4}, + {0x3c04, 0x28}, + {0x3c05, 0x98}, + {0x3c06, 0x00}, + {0x3c07, 0x08}, + {0x3c08, 0x00}, + {0x3c09, 0x1c}, + {0x3c0a, 0x9c}, + {0x3c0b, 0x40}, + + {0x460c, 0x22},//disable jpeg footer + + //BLC + {0x4001, 0x02}, + {0x4004, 0x02}, + + //AWB + {0x5180, 0xff}, + {0x5181, 0xf2}, + {0x5182, 0x00}, + {0x5183, 0x14}, + {0x5184, 0x25}, + {0x5185, 0x24}, + {0x5186, 0x09}, + {0x5187, 0x09}, + {0x5188, 0x09}, + {0x5189, 0x75}, + {0x518a, 0x54}, + {0x518b, 0xe0}, + {0x518c, 0xb2}, + {0x518d, 0x42}, + {0x518e, 0x3d}, + {0x518f, 0x56}, + {0x5190, 0x46}, + {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}, + + //color matrix (Saturation) + {0x5381, 0x1e}, + {0x5382, 0x5b}, + {0x5383, 0x08}, + {0x5384, 0x0a}, + {0x5385, 0x7e}, + {0x5386, 0x88}, + {0x5387, 0x7c}, + {0x5388, 0x6c}, + {0x5389, 0x10}, + {0x538a, 0x01}, + {0x538b, 0x98}, + + //CIP control (Sharpness) + {0x5300, 0x10},//sharpness + {0x5301, 0x10},//sharpness + {0x5302, 0x18},//sharpness + {0x5303, 0x19},//sharpness + {0x5304, 0x10}, + {0x5305, 0x10}, + {0x5306, 0x08},//denoise + {0x5307, 0x16}, + {0x5308, 0x40}, + {0x5309, 0x10},//sharpness + {0x530a, 0x10},//sharpness + {0x530b, 0x04},//sharpness + {0x530c, 0x06},//sharpness + + //GAMMA + {0x5480, 0x01}, + {0x5481, 0x00}, + {0x5482, 0x1e}, + {0x5483, 0x3b}, + {0x5484, 0x58}, + {0x5485, 0x66}, + {0x5486, 0x71}, + {0x5487, 0x7d}, + {0x5488, 0x83}, + {0x5489, 0x8f}, + {0x548a, 0x98}, + {0x548b, 0xa6}, + {0x548c, 0xb8}, + {0x548d, 0xca}, + {0x548e, 0xd7}, + {0x548f, 0xe3}, + {0x5490, 0x1d}, + + //Special Digital Effects (SDE) (UV adjust) + {0x5580, 0x06},//enable brightness and contrast + {0x5583, 0x40},//special_effect + {0x5584, 0x10},//special_effect + {0x5586, 0x20},//contrast + {0x5587, 0x00},//brightness + {0x5588, 0x00},//brightness + {0x5589, 0x10}, + {0x558a, 0x00}, + {0x558b, 0xf8}, + {0x501d, 0x40},// enable manual offset of contrast + + //power on + {0x3008, 0x02}, + + //50Hz + {0x3c00, 0x04}, + + {REG_DLY, 300}, + {REGLIST_TAIL, 0x00}, // tail +}; + +static 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 +}; + +static const DRAM_ATTR uint16_t sensor_fmt_raw[][2] = { + {FORMAT_CTRL, 0x03}, // RAW (DPC) + {FORMAT_CTRL00, 0x00}, // RAW + {REGLIST_TAIL, 0x00} +}; + +static const DRAM_ATTR uint16_t sensor_fmt_grayscale[][2] = { + {FORMAT_CTRL, 0x00}, // YUV422 + {FORMAT_CTRL00, 0x10}, // Y8 + {REGLIST_TAIL, 0x00} +}; + +static const DRAM_ATTR uint16_t sensor_fmt_yuv422[][2] = { + {FORMAT_CTRL, 0x00}, // YUV422 + {FORMAT_CTRL00, 0x30}, // YUYV + {REGLIST_TAIL, 0x00} +}; + +static const DRAM_ATTR uint16_t sensor_fmt_rgb565[][2] = { + {FORMAT_CTRL, 0x01}, // RGB + {FORMAT_CTRL00, 0x61}, // RGB565 (BGR) + {REGLIST_TAIL, 0x00} +}; + +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 + {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 +}; + +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 + {0x1e, 0x80, 0xc0, 0x08},//Red Tint + {0x1e, 0x60, 0x60, 0x08},//Green Tint + {0x1e, 0xa0, 0x40, 0x08},//Blue Tint + {0x1e, 0x40, 0xa0, 0x08},//Sepia +}; + +static const DRAM_ATTR uint16_t sensor_regs_gamma0[][2] = { + {0x5480, 0x01}, + {0x5481, 0x08}, + {0x5482, 0x14}, + {0x5483, 0x28}, + {0x5484, 0x51}, + {0x5485, 0x65}, + {0x5486, 0x71}, + {0x5487, 0x7d}, + {0x5488, 0x87}, + {0x5489, 0x91}, + {0x548a, 0x9a}, + {0x548b, 0xaa}, + {0x548c, 0xb8}, + {0x548d, 0xcd}, + {0x548e, 0xdd}, + {0x548f, 0xea}, + {0x5490, 0x1d} +}; + +static const DRAM_ATTR uint16_t sensor_regs_gamma1[][2] = { + {0x5480, 0x1}, + {0x5481, 0x0}, + {0x5482, 0x1e}, + {0x5483, 0x3b}, + {0x5484, 0x58}, + {0x5485, 0x66}, + {0x5486, 0x71}, + {0x5487, 0x7d}, + {0x5488, 0x83}, + {0x5489, 0x8f}, + {0x548a, 0x98}, + {0x548b, 0xa6}, + {0x548c, 0xb8}, + {0x548d, 0xca}, + {0x548e, 0xd7}, + {0x548f, 0xe3}, + {0x5490, 0x1d} +}; + +static const DRAM_ATTR uint16_t sensor_regs_awb0[][2] = { + {0x5180, 0xff}, + {0x5181, 0xf2}, + {0x5182, 0x00}, + {0x5183, 0x14}, + {0x5184, 0x25}, + {0x5185, 0x24}, + {0x5186, 0x09}, + {0x5187, 0x09}, + {0x5188, 0x09}, + {0x5189, 0x75}, + {0x518a, 0x54}, + {0x518b, 0xe0}, + {0x518c, 0xb2}, + {0x518d, 0x42}, + {0x518e, 0x3d}, + {0x518f, 0x56}, + {0x5190, 0x46}, + {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} +}; + +#endif