diff --git a/esp32-cam-rtos-allframes/camera.c b/esp32-cam-rtos-allframes/camera.c index 0fc85f6..f0f3a2e 100644 --- a/esp32-cam-rtos-allframes/camera.c +++ b/esp32-cam-rtos-allframes/camera.c @@ -48,6 +48,12 @@ #if CONFIG_OV5640_SUPPORT #include "ov5640.h" #endif +#if CONFIG_NT99141_SUPPORT +#include "nt99141.h" +#endif +#if CONFIG_OV7670_SUPPORT +#include "ov7670.h" +#endif typedef enum { CAMERA_NONE = 0, @@ -56,6 +62,8 @@ typedef enum { CAMERA_OV2640 = 2640, CAMERA_OV3660 = 3660, CAMERA_OV5640 = 5640, + CAMERA_OV7670 = 7670, + CAMERA_NT99141 = 9141, } camera_model_t; #define REG_PID 0x0A @@ -369,12 +377,10 @@ static inline void IRAM_ATTR i2s_conf_reset() } } -static void i2s_init() +static void i2s_gpio_init(const camera_config_t* config) { - camera_config_t* config = &s_state->config; - // Configure input GPIOs - gpio_num_t pins[] = { + const gpio_num_t pins[] = { config->pin_d7, config->pin_d6, config->pin_d5, @@ -391,15 +397,21 @@ static void i2s_init() .mode = GPIO_MODE_INPUT, .pull_up_en = GPIO_PULLUP_ENABLE, .pull_down_en = GPIO_PULLDOWN_DISABLE, - .intr_type = GPIO_INTR_DISABLE + .intr_type = GPIO_INTR_DISABLE, + .pin_bit_mask = 0LL }; for (int i = 0; i < sizeof(pins) / sizeof(gpio_num_t); ++i) { if (rtc_gpio_is_valid_gpio(pins[i])) { rtc_gpio_deinit(pins[i]); } - conf.pin_bit_mask = 1LL << pins[i]; - gpio_config(&conf); + conf.pin_bit_mask |= 1LL << pins[i]; } + gpio_config(&conf); +} + +static void i2s_init() +{ + camera_config_t* config = &s_state->config; // Route input GPIOs to I2S peripheral using GPIO matrix gpio_matrix_in(config->pin_d0, I2S0I_DATA_IN0_IDX, false); @@ -738,7 +750,7 @@ 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("bh 0x%08x\n", sig); + ESP_LOGD(TAG,"unexpected JPEG signature 0x%08x\n", sig); s_state->fb->bad = 1; return; } @@ -955,11 +967,15 @@ esp_err_t camera_probe(const camera_config_t* config, camera_model_t* out_camera return ESP_ERR_NO_MEM; } - ESP_LOGD(TAG, "Enabling XCLK output"); - camera_enable_out_clock(config); + if(config->pin_xclk >= 0) { + ESP_LOGD(TAG, "Enabling XCLK output"); + camera_enable_out_clock(config); + } - ESP_LOGD(TAG, "Initializing SSCB"); - SCCB_Init(config->pin_sscb_sda, config->pin_sscb_scl); + if (config->pin_sscb_sda != -1) { + ESP_LOGD(TAG, "Initializing SSCB"); + SCCB_Init(config->pin_sscb_sda, config->pin_sscb_scl); + } if(config->pin_pwdn >= 0) { ESP_LOGD(TAG, "Resetting camera by power down line"); @@ -1011,16 +1027,33 @@ esp_err_t camera_probe(const camera_config_t* config, camera_model_t* out_camera slv_addr = SCCB_Probe(); } #endif +#if CONFIG_NT99141_SUPPORT + if (slv_addr == 0x2a) + { + ESP_LOGD(TAG, "Resetting NT99141"); + SCCB_Write16(0x2a, 0x3008, 0x01);//bank sensor + } +#endif 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 (CONFIG_OV3660_SUPPORT || CONFIG_OV5640_SUPPORT || CONFIG_NT99141_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); vTaskDelay(10 / portTICK_PERIOD_MS); ESP_LOGD(TAG, "Camera PID=0x%02x VER=0x%02x", id->PID, id->VER); + } else if(s_state->sensor.slv_addr == 0x2a){ + id->PID = SCCB_Read16(s_state->sensor.slv_addr, 0x3000); + id->VER = SCCB_Read16(s_state->sensor.slv_addr, 0x3001); + vTaskDelay(10 / portTICK_PERIOD_MS); + ESP_LOGD(TAG, "Camera PID=0x%02x VER=0x%02x", id->PID, id->VER); + if(config->xclk_freq_hz > 10000000) + { + ESP_LOGE(TAG, "NT99141: only XCLK under 10MHz is supported, and XCLK is now set to 10M"); + s_state->sensor.xclk_freq_hz = 10000000; + } } else { #endif id->PID = SCCB_Read(s_state->sensor.slv_addr, REG_PID); @@ -1031,7 +1064,7 @@ esp_err_t camera_probe(const camera_config_t* config, camera_model_t* out_camera 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 || CONFIG_OV5640_SUPPORT) +#if (CONFIG_OV3660_SUPPORT || CONFIG_OV5640_SUPPORT || CONFIG_NT99141_SUPPORT) } #endif @@ -1060,6 +1093,18 @@ esp_err_t camera_probe(const camera_config_t* config, camera_model_t* out_camera *out_camera_model = CAMERA_OV5640; ov5640_init(&s_state->sensor); break; +#endif +#if CONFIG_OV7670_SUPPORT + case OV7670_PID: + *out_camera_model = CAMERA_OV7670; + ov7670_init(&s_state->sensor); + break; +#endif +#if CONFIG_NT99141_SUPPORT + case NT99141_PID: + *out_camera_model = CAMERA_NT99141; + NT99141_init(&s_state->sensor); + break; #endif default: id->PID = 0; @@ -1116,6 +1161,20 @@ esp_err_t camera_init(const camera_config_t* config) frame_size = FRAMESIZE_QSXGA; } break; +#endif +#if CONFIG_OV7670_SUPPORT + case OV7670_PID: + if (frame_size > FRAMESIZE_VGA) { + frame_size = FRAMESIZE_VGA; + } + break; +#endif +#if CONFIG_NT99141_SUPPORT + case NT99141_PID: + if (frame_size > FRAMESIZE_HD) { + frame_size = FRAMESIZE_HD; + } + break; #endif default: return ESP_ERR_CAMERA_NOT_SUPPORTED; @@ -1126,7 +1185,7 @@ esp_err_t camera_init(const camera_config_t* config) if (pix_format == PIXFORMAT_GRAYSCALE) { s_state->fb_size = s_state->width * s_state->height; - if (s_state->sensor.id.PID == OV3660_PID || s_state->sensor.id.PID == OV5640_PID) { + if (s_state->sensor.id.PID == OV3660_PID || s_state->sensor.id.PID == OV5640_PID || s_state->sensor.id.PID == NT99141_PID) { if (is_hs_mode()) { s_state->sampling_mode = SM_0A00_0B00; s_state->dma_filter = &dma_filter_yuyv_highspeed; @@ -1147,20 +1206,28 @@ esp_err_t camera_init(const camera_config_t* config) } s_state->fb_bytes_per_pixel = 1; // frame buffer stores Y8 } else if (pix_format == PIXFORMAT_YUV422 || pix_format == PIXFORMAT_RGB565) { - s_state->fb_size = s_state->width * s_state->height * 2; - if (is_hs_mode() && s_state->sensor.id.PID != OV7725_PID) { - 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 = 2; // camera sends YU/YV - s_state->fb_bytes_per_pixel = 2; // frame buffer stores YU/YV/RGB565 + s_state->fb_size = s_state->width * s_state->height * 2; + if (is_hs_mode() && s_state->sensor.id.PID != OV7725_PID) { + if(s_state->sensor.id.PID == OV7670_PID) { + s_state->sampling_mode = SM_0A0B_0B0C; + }else{ + 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 = 2; // camera sends YU/YV + 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; + if(s_state->sensor.id.PID == OV7670_PID) { + s_state->sampling_mode = SM_0A0B_0B0C; + }else{ + s_state->sampling_mode = SM_0A00_0B00; + } s_state->dma_filter = &dma_filter_rgb888_highspeed; } else { s_state->sampling_mode = SM_0A0B_0C0D; @@ -1169,7 +1236,7 @@ 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 && s_state->sensor.id.PID != OV5640_PID) { + if (s_state->sensor.id.PID != OV2640_PID && s_state->sensor.id.PID != OV3660_PID && s_state->sensor.id.PID != OV5640_PID && s_state->sensor.id.PID != NT99141_PID) { ESP_LOGE(TAG, "JPEG format is only supported for ov2640, ov3660 and ov5640"); err = ESP_ERR_NOT_SUPPORTED; goto fail; @@ -1306,6 +1373,7 @@ fail: esp_err_t esp_camera_init(const camera_config_t* config) { camera_model_t camera_model = CAMERA_NONE; + i2s_gpio_init(config); esp_err_t err = camera_probe(config, &camera_model); if (err != ESP_OK) { ESP_LOGE(TAG, "Camera probe failed with error 0x%x", err); @@ -1324,6 +1392,10 @@ esp_err_t esp_camera_init(const camera_config_t* config) ESP_LOGI(TAG, "Detected OV3660 camera"); } else if (camera_model == CAMERA_OV5640) { ESP_LOGI(TAG, "Detected OV5640 camera"); + } else if (camera_model == CAMERA_OV7670) { + ESP_LOGI(TAG, "Detected OV7670 camera"); + } else if (camera_model == CAMERA_NT99141) { + ESP_LOGI(TAG, "Detected NT99141 camera"); } else { ESP_LOGI(TAG, "Camera not supported"); err = ESP_ERR_CAMERA_NOT_SUPPORTED; @@ -1370,9 +1442,12 @@ esp_err_t esp_camera_deinit() } dma_desc_deinit(); camera_fb_deinit(); + + if(s_state->config.pin_xclk >= 0) { + camera_disable_out_clock(); + } free(s_state); s_state = NULL; - camera_disable_out_clock(); periph_module_disable(PERIPH_I2S0_MODULE); return ESP_OK; } @@ -1430,11 +1505,11 @@ sensor_t * esp_camera_sensor_get() esp_err_t esp_camera_save_to_nvs(const char *key) { -#if ESP_IDF_VERSION_MAJOR > 3 - nvs_handle_t handle; -#else +//#if ESP_IDF_VERSION_MAJOR > 3 +// nvs_handle_t handle; +//#else nvs_handle handle; -#endif +//#endif esp_err_t ret = nvs_open(key,NVS_READWRITE,&handle); if (ret == ESP_OK) { @@ -1458,11 +1533,11 @@ esp_err_t esp_camera_save_to_nvs(const char *key) esp_err_t esp_camera_load_from_nvs(const char *key) { -#if ESP_IDF_VERSION_MAJOR > 3 - nvs_handle_t handle; -#else +//#if ESP_IDF_VERSION_MAJOR > 3 +// nvs_handle_t handle; +//#else nvs_handle handle; -#endif +//#endif uint8_t pf; esp_err_t ret = nvs_open(key,NVS_READWRITE,&handle); diff --git a/esp32-cam-rtos-allframes/esp32-cam-rtos-allframes.ino b/esp32-cam-rtos-allframes/esp32-cam-rtos-allframes.ino index c7e0d50..df59ac7 100644 --- a/esp32-cam-rtos-allframes/esp32-cam-rtos-allframes.ino +++ b/esp32-cam-rtos-allframes/esp32-cam-rtos-allframes.ino @@ -421,6 +421,8 @@ void setup() ESP.restart(); } + sensor_t* s = esp_camera_sensor_get(); + s->set_vflip(s, true); // Configure and connect to WiFi IPAddress ip; diff --git a/esp32-cam-rtos-allframes/esp32-camera-master.zip b/esp32-cam-rtos-allframes/esp32-camera-master.zip deleted file mode 100644 index e8118ae..0000000 Binary files a/esp32-cam-rtos-allframes/esp32-camera-master.zip and /dev/null differ diff --git a/esp32-cam-rtos-allframes/img_converters.h b/esp32-cam-rtos-allframes/img_converters.h index 2b83c4d..330f8db 100644 --- a/esp32-cam-rtos-allframes/img_converters.h +++ b/esp32-cam-rtos-allframes/img_converters.h @@ -62,7 +62,8 @@ bool frame2jpg_cb(camera_fb_t * fb, uint8_t quality, jpg_out_cb cb, void * arg); * @param height Height in pixels of the source image * @param format Format of the source image * @param quality JPEG quality of the resulting image - * @param out Pointer to be populated with the address of the resulting buffer + * @param out Pointer to be populated with the address of the resulting buffer. + * You MUST free the pointer once you are done with it. * @param out_len Pointer to be populated with the length of the output buffer * * @return true on success diff --git a/esp32-cam-rtos-allframes/nt99141.c b/esp32-cam-rtos-allframes/nt99141.c new file mode 100644 index 0000000..07a9cc4 --- /dev/null +++ b/esp32-cam-rtos-allframes/nt99141.c @@ -0,0 +1,1032 @@ +/* + * 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. + * + * NT99141 driver. + * + */ +#include +#include +#include +#include "sccb.h" +#include "nt99141.h" +#include "nt99141_regs.h" +#include "nt99141_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 = "NT99141"; +#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_LOGD(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 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) +{ + return -1; +} + +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, 0x01); + + if (ret) { + ESP_LOGE(TAG, "Software Reset FAILED!"); + return ret; + } + + vTaskDelay(100 / portTICK_PERIOD_MS); + ret = write_regs(sensor->slv_addr, sensor_default_regs); //re-initial + + 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; + + // V-Flip + if (sensor->status.vflip) { + reg20 |= 0x01; + reg4514_test |= 1; + } + + // H-Mirror + if (sensor->status.hmirror) { + reg21 |= 0x02; + reg4514_test |= 2; + } + + switch (reg4514_test) { + + } + + if (write_reg(sensor->slv_addr, TIMING_TC_REG20, reg20 | reg21)) { + 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.binning, sensor->status.vflip, sensor->status.hmirror, reg4514); + return ret; +} + +static int set_framesize(sensor_t *sensor, framesize_t framesize) +{ + int ret = 0; + + sensor->status.framesize = framesize; + ret = write_regs(sensor->slv_addr, sensor_default_regs); + + if (framesize == FRAMESIZE_QVGA) { + ESP_LOGD(TAG, "Set FRAMESIZE_QVGA"); + ret = write_regs(sensor->slv_addr, sensor_framesize_QVGA); +#if CONFIG_NT99141_SUPPORT_XSKIP + ESP_LOGD(TAG, "Set FRAMESIZE_QVGA: xskip mode"); + ret = write_regs(sensor->slv_addr, sensor_framesize_QVGA_xskip); +#elif CONFIG_NT99141_SUPPORT_CROP + ESP_LOGD(TAG, "Set FRAMESIZE_QVGA: crop mode"); + ret = write_regs(sensor->slv_addr, sensor_framesize_QVGA_crop); +#endif + } else if (framesize == FRAMESIZE_VGA) { + ESP_LOGD(TAG, "Set FRAMESIZE_VGA"); + // ret = write_regs(sensor->slv_addr, sensor_framesize_VGA); + ret = write_regs(sensor->slv_addr, sensor_framesize_VGA_xyskip);// Resolution:640*360 This configuration is equally-scaled without deforming +#ifdef CONFIG_NT99141_SUPPORT_XSKIP + ESP_LOGD(TAG, "Set FRAMESIZE_QVGA: xskip mode"); + ret = write_regs(sensor->slv_addr, sensor_framesize_VGA_xskip); +#elif CONFIG_NT99141_SUPPORT_CROP + ESP_LOGD(TAG, "Set FRAMESIZE_QVGA: crop mode"); + ret = write_regs(sensor->slv_addr, sensor_framesize_VGA_crop); +#endif + } else if (framesize >= FRAMESIZE_HD) { + ESP_LOGD(TAG, "Set FRAMESIZE_HD"); + ret = write_regs(sensor->slv_addr, sensor_framesize_HD); + } else { + ESP_LOGD(TAG, "Dont suppost this size, Set FRAMESIZE_VGA"); + ret = write_regs(sensor->slv_addr, sensor_framesize_VGA); + } + + return 0; +} + +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, 0x32bb, 0x87, 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; + int data = 0; + // ret = write_reg_bits(sensor->slv_addr, 0x32bb, 0x87, enable); + data = read_reg(sensor->slv_addr, 0x3201); + ESP_LOGD(TAG, "set_exposure_ctrl:enable"); + if (enable) { + ESP_LOGD(TAG, "set_exposure_ctrl:enable"); + ret = write_reg(sensor->slv_addr, 0x3201, (1 << 5) | data); + } else { + ESP_LOGD(TAG, "set_exposure_ctrl:disable"); + ret = write_reg(sensor->slv_addr, 0x3201, (~(1 << 5)) & data); + } + + 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; + + 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; + + 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; + + 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; + + 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; + + 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; + + 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; + + 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) +{ + ESP_LOGD(TAG, "get_agc_gain can not be configured at present"); + return 0; +} + +//real gain +static int set_agc_gain(sensor_t *sensor, int gain) +{ + ESP_LOGD(TAG, "set_agc_gain can not be configured at present"); + // ESP_LOGD(TAG, "GAIN = %d\n", gain); + int cnt = gain / 2; + + switch (cnt) { + case 0: + ESP_LOGD(TAG, "set_agc_gain: 1x"); + write_reg(sensor->slv_addr, 0X301D, 0X00); + break; + + case 1: + ESP_LOGD(TAG,"set_agc_gain: 2x"); + write_reg(sensor->slv_addr, 0X301D, 0X0F); + break; + + case 2: + ESP_LOGD(TAG,"set_agc_gain: 4x"); + write_reg(sensor->slv_addr, 0X301D, 0X2F); + break; + + case 3: + ESP_LOGD(TAG,"set_agc_gain: 6x"); + write_reg(sensor->slv_addr, 0X301D, 0X37); + break; + + case 4: + ESP_LOGD(TAG,"set_agc_gain: 8x"); + write_reg(sensor->slv_addr, 0X301D, 0X3F); + break; + + default: + ESP_LOGD(TAG,"fail set_agc_gain"); + break; + } + + return 0; +} + +static int get_aec_value(sensor_t *sensor) +{ + ESP_LOGD(TAG, "get_aec_value can not be configured at present"); + return 0; +} + +static int set_aec_value(sensor_t *sensor, int value) +{ + ESP_LOGD(TAG, "set_aec_value can not be configured at present"); + int ret = 0; + // ESP_LOGD(TAG, " set_aec_value to: %d", value); + ret = write_reg_bits(sensor->slv_addr, 0x3012, 0x00, (value >> 8) & 0xff); + ret = write_reg_bits(sensor->slv_addr, 0x3013, 0x01, value & 0xff); + + if (ret == 0) { + ESP_LOGD(TAG, " set_aec_value to: %d", value); + // sensor->status.aec = enable; + } + + return ret; +} + +static int set_ae_level(sensor_t *sensor, int level) +{ + ESP_LOGD(TAG, "set_ae_level can not be configured at present"); + int ret = 0; + + if (level < 0) { + level = 0; + } else if (level > 9) { + level = 9; + } + + for (int i = 0; i < 5; i++) { + ret += write_reg(sensor->slv_addr, sensor_ae_level[ 5 * level + i ][0], sensor_ae_level[5 * level + i ][1]); + } + + if (ret) { + ESP_LOGE(TAG, " fail to set ae level: %d", ret); + } + + return 0; +} + +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, 0x3201, (mode != 0)); + + if (ret) { + return ret; + } + + switch (mode) { + case 1://Sunny + ret = write_reg16(sensor->slv_addr, 0x3290, 0x01) + || write_reg16(sensor->slv_addr, 0x3291, 0x38) + || write_reg16(sensor->slv_addr, 0x3296, 0x01) + || write_reg16(sensor->slv_addr, 0x3297, 0x68) + || write_reg16(sensor->slv_addr, 0x3060, 0x01); + + break; + + case 2://Cloudy + + ret = write_reg16(sensor->slv_addr, 0x3290, 0x01) + || write_reg16(sensor->slv_addr, 0x3291, 0x51) + || write_reg16(sensor->slv_addr, 0x3296, 0x01) + || write_reg16(sensor->slv_addr, 0x3297, 0x00) + || write_reg16(sensor->slv_addr, 0x3060, 0x01); + break; + + case 3://INCANDESCENCE] + ret = write_reg16(sensor->slv_addr, 0x3290, 0x01) + || write_reg16(sensor->slv_addr, 0x3291, 0x30) + || write_reg16(sensor->slv_addr, 0x3296, 0x01) + || write_reg16(sensor->slv_addr, 0x3297, 0xCB) + || write_reg16(sensor->slv_addr, 0x3060, 0x01); + break; + + case 4://FLUORESCENT + ret = write_reg16(sensor->slv_addr, 0x3290, 0x01) + || write_reg16(sensor->slv_addr, 0x3291, 0x70) + || write_reg16(sensor->slv_addr, 0x3296, 0x01) + || write_reg16(sensor->slv_addr, 0x3297, 0xFF) + || write_reg16(sensor->slv_addr, 0x3060, 0x01); + 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, 0x32F1, regs[0]) + || write_reg(sensor->slv_addr, 0x32F4, regs[1]) + || write_reg(sensor->slv_addr, 0x32F5, regs[2]) + || write_reg(sensor->slv_addr, 0x3060, 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 = 0xA0; + break; + + case 2: + value = 0x90; + break; + + case 1: + value = 0x88; + break; + + case -1: + value = 0x78; + negative = true; + break; + + case -2: + value = 0x70; + negative = true; + break; + + case -3: + value = 0x60; + negative = true; + break; + + default: // 0 + break; + } + + ret = write_reg(sensor->slv_addr, 0x32F2, value); + + 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; + uint8_t value1 = 0, value2 = 0 ; + bool negative = false; + + switch (level) { + case 3: + value1 = 0xD0; + value2 = 0xB0; + break; + + case 2: + value1 = 0xE0; + value2 = 0xA0; + break; + + case 1: + value1 = 0xF0; + value2 = 0x90; + break; + + case 0: + value1 = 0x00; + value2 = 0x80; + break; + + case -1: + value1 = 0x10; + value2 = 0x70; + break; + + case -2: + value1 = 0x20; + value2 = 0x60; + break; + + case -3: + value1 = 0x30; + value2 = 0x50; + break; + + default: // 0 + break; + } + + ret = write_reg(sensor->slv_addr, 0x32FC, value1); + ret = write_reg(sensor->slv_addr, 0x32F2, value2); + ret = write_reg(sensor->slv_addr, 0x3060, 0x01); + + 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]; + { + ret = write_reg(sensor->slv_addr, 0x32F3, regs[0]); + + if (ret) { + return ret; + } + } + + 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) +{ + ESP_LOGD(TAG, "set_gainceiling can not be configured at present"); + return 0; +} + +static int get_denoise(sensor_t *sensor) +{ + + return (read_reg(sensor->slv_addr, 0x5306) / 4) + 1; +} + +static int set_denoise(sensor_t *sensor, int level) +{ + ESP_LOGD(TAG, "set_denoise can not be configured at present"); + return 0; +} + +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); + + 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; + if (xclk > 10) + { + ESP_LOGE(TAG, "only XCLK under 10MHz is supported, and XCLK is now set to 10M"); + xclk = 10; + } + 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, 0x3301)); + sensor->status.denoise = get_denoise(sensor); + sensor->status.ae_level = 0; + sensor->status.gainceiling = read_reg16(sensor->slv_addr, 0x32F0) & 0xFF; + sensor->status.awb = check_reg_mask(sensor->slv_addr, ISP_CONTROL_01, 0x10); + 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, 0x3000, 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, 0x3000, 0x04); + return 0; +} + +int NT99141_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/esp32-cam-rtos-allframes/nt99141.h b/esp32-cam-rtos-allframes/nt99141.h new file mode 100644 index 0000000..287a742 --- /dev/null +++ b/esp32-cam-rtos-allframes/nt99141.h @@ -0,0 +1,16 @@ +/* + * 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. + * + * NT99141 driver. + * + */ +#ifndef __NT99141_H__ +#define __NT99141_H__ + +#include "sensor.h" + +int NT99141_init(sensor_t *sensor); + +#endif // __NT99141_H__ diff --git a/esp32-cam-rtos-allframes/nt99141_regs.h b/esp32-cam-rtos-allframes/nt99141_regs.h new file mode 100644 index 0000000..8301db9 --- /dev/null +++ b/esp32-cam-rtos-allframes/nt99141_regs.h @@ -0,0 +1,211 @@ +/* + * NT99141 register definitions. + */ +#ifndef __NT99141_REG_REGS_H__ +#define __NT99141_REG_REGS_H__ + +/* system control registers */ +#define SYSTEM_CTROL0 0x3021 // 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 0x3025 // 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 0x3201 // 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 + +/* mirror and flip registers */ +#define TIMING_TC_REG20 0x3022 // Timing Control Register + // Bit[2:1]: Vertical flip enable + // 00: Normal + // 11: Vertical flip + // Bit[0]: Vertical binning enable +#define TIMING_TC_REG21 0x3022 // 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 0x3024// 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 0x306a // 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 0x3021 // 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 0x3401 // 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 0x02 /* 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 0x01 /* Vertical flip enable */ +#define TIMING_TC_REG21_HMIRROR 0x02 /* Horizontal mirror enable */ + +#endif // __NT99141_REG_REGS_H__ diff --git a/esp32-cam-rtos-allframes/nt99141_settings.h b/esp32-cam-rtos-allframes/nt99141_settings.h new file mode 100644 index 0000000..1ffec20 --- /dev/null +++ b/esp32-cam-rtos-allframes/nt99141_settings.h @@ -0,0 +1,825 @@ +#ifndef _NT99141_SETTINGS_H_ +#define _NT99141_SETTINGS_H_ + +#include +#include +#include "esp_attr.h" +#include "nt99141_regs.h" + +static const ratio_settings_t ratio_table[] = { + // mw, mh, sx, sy, ex, ey, ox, oy, tx, ty + { 1280, 720, 0, 4, 1283, 723, 0, 4, 1660, 963 }, + +}; + +#define REG_DLY 0xffff +#define REGLIST_TAIL 0x0000 + +static const DRAM_ATTR uint16_t sensor_default_regs[][2] = { + //initial +{0x3021, 0x00}, +{REG_DLY, 100}, // delay 100ms +{0x3109, 0x04}, +{0x3040, 0x04}, +{0x3041, 0x02}, +{0x3042, 0xFF}, +{0x3043, 0x08}, +{0x3052, 0xE0}, +{0x305F, 0x33}, +{0x3100, 0x07}, +{0x3106, 0x03}, +{0x3105, 0x01}, +{0x3108, 0x05}, +{0x3110, 0x22}, +{0x3111, 0x57}, +{0x3112, 0x22}, +{0x3113, 0x55}, +{0x3114, 0x05}, +{0x3135, 0x00}, +{0x32F0, 0x01}, +{0x3290, 0x01}, +{0x3291, 0x80}, +{0x3296, 0x01}, +{0x3297, 0x73}, +{0x3250, 0x80}, +{0x3251, 0x03}, +{0x3252, 0xFF}, +{0x3253, 0x00}, +{0x3254, 0x03}, +{0x3255, 0xFF}, +{0x3256, 0x00}, +{0x3257, 0x50}, +{0x3270, 0x00}, +{0x3271, 0x0C}, +{0x3272, 0x18}, +{0x3273, 0x32}, +{0x3274, 0x44}, +{0x3275, 0x54}, +{0x3276, 0x70}, +{0x3277, 0x88}, +{0x3278, 0x9D}, +{0x3279, 0xB0}, +{0x327A, 0xCF}, +{0x327B, 0xE2}, +{0x327C, 0xEF}, +{0x327D, 0xF7}, +{0x327E, 0xFF}, +{0x3302, 0x00}, +{0x3303, 0x40}, +{0x3304, 0x00}, +{0x3305, 0x96}, +{0x3306, 0x00}, +{0x3307, 0x29}, +{0x3308, 0x07}, +{0x3309, 0xBA}, +{0x330A, 0x06}, +{0x330B, 0xF5}, +{0x330C, 0x01}, +{0x330D, 0x51}, +{0x330E, 0x01}, +{0x330F, 0x30}, +{0x3310, 0x07}, +{0x3311, 0x16}, +{0x3312, 0x07}, +{0x3313, 0xBA}, +{0x3326, 0x02}, +{0x32F6, 0x0F}, +{0x32F9, 0x42}, +{0x32FA, 0x24}, +{0x3325, 0x4A}, +{0x3330, 0x00}, +{0x3331, 0x0A}, +{0x3332, 0xFF}, +{0x3338, 0x30}, +{0x3339, 0x84}, +{0x333A, 0x48}, +{0x333F, 0x07}, +{0x3360, 0x10}, +{0x3361, 0x18}, +{0x3362, 0x1f}, +{0x3363, 0x37}, +{0x3364, 0x80}, +{0x3365, 0x80}, +{0x3366, 0x68}, +{0x3367, 0x60}, +{0x3368, 0x30}, +{0x3369, 0x28}, +{0x336A, 0x20}, +{0x336B, 0x10}, +{0x336C, 0x00}, +{0x336D, 0x20}, +{0x336E, 0x1C}, +{0x336F, 0x18}, +{0x3370, 0x10}, +{0x3371, 0x38}, +{0x3372, 0x3C}, +{0x3373, 0x3F}, +{0x3374, 0x3F}, +{0x338A, 0x34}, +{0x338B, 0x7F}, +{0x338C, 0x10}, +{0x338D, 0x23}, +{0x338E, 0x7F}, +{0x338F, 0x14}, +{0x3375, 0x08}, +{0x3376, 0x0C}, +{0x3377, 0x18}, +{0x3378, 0x20}, +{0x3012, 0x02}, +{0x3013, 0xD0}, +{0x3025, 0x02}, //colorbar +{REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_fmt_jpeg[][2] = { + {0x32F0, 0x70}, // YUV422 + {REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_fmt_raw[][2] = { + {0x32F0, 0x50}, // RAW + {REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_fmt_grayscale[][2] = { + {0x32F1, 0x01}, + {REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_fmt_yuv422[][2] = { + {0x32F0, 0x00}, // YUV422 + {REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_fmt_rgb565[][2] = { + {0x32F0, 0x01}, // RGB + {REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint8_t sensor_saturation_levels[9][1] = { + {0x60},//-4 + {0x68},//-3 + {0x70},//-2 + {0x78},//-1 + {0x80},//0 + {0x88},//+1 + {0x90},//+2 + {0x98},//+3 + {0xA0},//+4 +}; + +static const DRAM_ATTR uint8_t sensor_special_effects[7][4] = { + {0x00, 0x80, 0x80, 0x01},//Normal + {0x03, 0x80, 0x80, 0x01},//Negative + {0x01, 0x80, 0x80, 0x01},//Grayscale + {0x05, 0x2A, 0xF0, 0x01},//Red Tint + {0x05, 0x60, 0x20, 0x01},//Green Tint + {0x05, 0xF0, 0x80, 0x01},//Blue Tint + {0x02, 0x80, 0x80, 0x01},//Sepia + +}; + +// AE LEVEL +static const DRAM_ATTR uint16_t sensor_ae_level[][2] = { + +// 1. [AE_Target : 0x24] +// Set_Device_Format = FORMAT_16_8 +// SET_Device_Addr = 0x54 + {0x32B8, 0x29 }, + {0x32B9, 0x1F }, + {0x32BC, 0x24 }, + {0x32BD, 0x27 }, + {0x32BE, 0x21 }, +//------------------------------------------------------------------------ +// 2. [AE_Target : 0x28] +// Set_Device_Format = FORMAT_16_8 +// SET_Device_Addr = 0x54 + {0x32B8, 0x2D }, + {0x32B9, 0x23 }, + {0x32BC, 0x28 }, + {0x32BD, 0x2B }, + {0x32BE, 0x25 }, +//------------------------------------------------------------------------ +// 3. [AE_Target : 0x2C] +// Set_Device_Format = FORMAT_16_8 +// SET_Device_Addr = 0x54 + {0x32B8, 0x32 }, + {0x32B9, 0x26 }, + {0x32BC, 0x2C }, + {0x32BD, 0x2F }, + {0x32BE, 0x29 }, +//------------------------------------------------------------------------ +// 4, [AE_Target : 0x30] +// Set_Device_Format = FORMAT_16_8 +// SET_Device_Addr = 0x54 + {0x32B8, 0x36 }, + {0x32B9, 0x2A }, + {0x32BC, 0x30 }, + {0x32BD, 0x33 }, + {0x32BE, 0x2D }, +//------------------------------------------------------------------------ +// 5. [AE_Target : 0x34] +// Set_Device_Format = FORMAT_16_8 +// SET_Device_Addr = 0x54 + {0x32B8, 0x3B }, + {0x32B9, 0x2D }, + {0x32BC, 0x34 }, + {0x32BD, 0x38 }, + {0x32BE, 0x30 }, +//------------------------------------------------------------------------ +// 6. [AE_Target : 0x38] +// Set_Device_Format = FORMAT_16_8 +// SET_Device_Addr = 0x54 + {0x32B8, 0x3F }, + {0x32B9, 0x31 }, + {0x32BC, 0x38 }, + {0x32BD, 0x3C }, + {0x32BE, 0x34 }, +//------------------------------------------------------------------------ +// 7. [AE_Target : 0x3D] +// Set_Device_Format = FORMAT_16_8 +// SET_Device_Addr = 0x54 + {0x32B8, 0x44 }, + {0x32B9, 0x34 }, + {0x32BC, 0x3C }, + {0x32BD, 0x40 }, + {0x32BE, 0x38 }, +//------------------------------------------------------------------------ +// 8. [AE_Target : 0x40] +// Set_Device_Format = FORMAT_16_8 +// SET_Device_Addr = 0x54 + {0x32B8, 0x48 }, + {0x32B9, 0x38 }, + {0x32BC, 0x40 }, + {0x32BD, 0x44 }, + {0x32BE, 0x3C }, +//------------------------------------------------------------------------ +// 9. [AE_Target : 0x44] +// Set_Device_Format = FORMAT_16_8 +// SET_Device_Addr = 0x54 + {0x32B8, 0x4D }, + {0x32B9, 0x3B }, + {0x32BC, 0x44 }, + {0x32BD, 0x49 }, + {0x32BE, 0x3F }, +}; + +static const DRAM_ATTR uint16_t sensor_framesize_HD[][2] = { +//[JPEG_1280x720_8.18_8.18_Fps] +{0x3021, 0x00}, +{REG_DLY, 100}, // delay 100ms +{0x32BF, 0x60}, +{0x32C0, 0x5A}, +{0x32C1, 0x5A}, +{0x32C2, 0x5A}, +{0x32C3, 0x00}, +{0x32C4, 0x20}, +{0x32C5, 0x20}, +{0x32C6, 0x20}, +{0x32C7, 0x00}, +{0x32C8, 0x3C}, +{0x32C9, 0x5A}, +{0x32CA, 0x7A}, +{0x32CB, 0x7A}, +{0x32CC, 0x7A}, +{0x32CD, 0x7A}, +{0x32DB, 0x5E}, +{0x32F0, 0x70}, +{0x3400, 0x08}, +{0x3400, 0x00}, +{0x3401, 0x4E}, +{0x3404, 0x00}, +{0x3405, 0x00}, +{0x3410, 0x00}, +{0x3200, 0x3E}, +{0x3201, 0x0F}, +{0x3028, 0x0F}, +{0x3029, 0x00}, +{0x302A, 0x08}, +{0x3022, 0x24}, +{0x3023, 0x24}, +{0x3002, 0x00}, +{0x3003, 0x04}, +{0x3004, 0x00}, +{0x3005, 0x04}, +{0x3006, 0x05}, +{0x3007, 0x03}, +{0x3008, 0x02}, +{0x3009, 0xD3}, +{0x300A, 0x06}, +{0x300B, 0x7C}, +{0x300C, 0x02}, +{0x300D, 0xE0}, +{0x300E, 0x05}, +{0x300F, 0x00}, +{0x3010, 0x02}, +{0x3011, 0xD0}, +{0x32B8, 0x3F}, +{0x32B9, 0x31}, +{0x32BB, 0x87}, +{0x32BC, 0x38}, +{0x32BD, 0x3C}, +{0x32BE, 0x34}, +{0x3201, 0x3F}, +{0x3021, 0x06}, +{0x3025, 0x00}, //normal +{0x3400, 0x01}, +{0x3060, 0x01}, +{REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_framesize_VGA[][2] = { +//[JPEG_640x480_10.14_10.14_Fps] +{0x3021, 0x00}, +{REG_DLY, 100}, // delay 100ms +{0x32BF, 0x60}, +{0x32C0, 0x5A}, +{0x32C1, 0x5A}, +{0x32C2, 0x5A}, +{0x32C3, 0x00}, +{0x32C4, 0x20}, +{0x32C5, 0x20}, +{0x32C6, 0x20}, +{0x32C7, 0x00}, +{0x32C8, 0x4B}, +{0x32C9, 0x5A}, +{0x32CA, 0x7A}, +{0x32CB, 0x7A}, +{0x32CC, 0x7A}, +{0x32CD, 0x7A}, +{0x32DB, 0x62}, +{0x32F0, 0x70}, +{0x3400, 0x08}, +{0x3400, 0x00}, +{0x3401, 0x4E}, +{0x3404, 0x00}, +{0x3405, 0x00}, +{0x3410, 0x00}, +{0x32E0, 0x02}, +{0x32E1, 0x80}, +{0x32E2, 0x01}, +{0x32E3, 0xE0}, +{0x32E4, 0x00}, +{0x32E5, 0x80}, +{0x32E6, 0x00}, +{0x32E7, 0x80}, +{0x3200, 0x3E}, +{0x3201, 0x0F}, +{0x3028, 0x0F}, +{0x3029, 0x00}, +{0x302A, 0x08}, +{0x3022, 0x24}, +{0x3023, 0x24}, +{0x3002, 0x00}, +{0x3003, 0xA4}, +{0x3004, 0x00}, +{0x3005, 0x04}, +{0x3006, 0x04}, +{0x3007, 0x63}, +{0x3008, 0x02}, +{0x3009, 0xD3}, +{0x300A, 0x05}, +{0x300B, 0x3C}, +{0x300C, 0x02}, +{0x300D, 0xE0}, +{0x300E, 0x03}, +{0x300F, 0xC0}, +{0x3010, 0x02}, +{0x3011, 0xD0}, +{0x32B8, 0x3F}, +{0x32B9, 0x31}, +{0x32BB, 0x87}, +{0x32BC, 0x38}, +{0x32BD, 0x3C}, +{0x32BE, 0x34}, +{0x3201, 0x7F}, +{0x3021, 0x06}, +{0x3025, 0x00}, //normal +{0x3400, 0x01}, +{0x3060, 0x01}, +{REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_framesize_QVGA[][2] = { +//[JPEG_320x240_10.14_10.14_Fps] +{0x3021, 0x00}, +{REG_DLY, 100}, // delay 100ms +{0x32BF, 0x60}, +{0x32C0, 0x5A}, +{0x32C1, 0x5A}, +{0x32C2, 0x5A}, +{0x32C3, 0x00}, +{0x32C4, 0x20}, +{0x32C5, 0x20}, +{0x32C6, 0x20}, +{0x32C7, 0x00}, +{0x32C8, 0x4B}, +{0x32C9, 0x5A}, +{0x32CA, 0x7A}, +{0x32CB, 0x7A}, +{0x32CC, 0x7A}, +{0x32CD, 0x7A}, +{0x32DB, 0x62}, +{0x32F0, 0x70}, +{0x3400, 0x08}, +{0x3400, 0x00}, +{0x3401, 0x4E}, +{0x3404, 0x00}, +{0x3405, 0x00}, +{0x3410, 0x00}, +{0x32E0, 0x01}, +{0x32E1, 0x40}, +{0x32E2, 0x00}, +{0x32E3, 0xF0}, +{0x32E4, 0x02}, +{0x32E5, 0x02}, +{0x32E6, 0x02}, +{0x32E7, 0x03}, +{0x3200, 0x3E}, +{0x3201, 0x0F}, +{0x3028, 0x0F}, +{0x3029, 0x00}, +{0x302A, 0x08}, +{0x3022, 0x24}, +{0x3023, 0x24}, +{0x3002, 0x00}, +{0x3003, 0xA4}, +{0x3004, 0x00}, +{0x3005, 0x04}, +{0x3006, 0x04}, +{0x3007, 0x63}, +{0x3008, 0x02}, +{0x3009, 0xD3}, +{0x300A, 0x05}, +{0x300B, 0x3C}, +{0x300C, 0x02}, +{0x300D, 0xE0}, +{0x300E, 0x03}, +{0x300F, 0xC0}, +{0x3010, 0x02}, +{0x3011, 0xD0}, +{0x32B8, 0x3F}, +{0x32B9, 0x31}, +{0x32BB, 0x87}, +{0x32BC, 0x38}, +{0x32BD, 0x3C}, +{0x32BE, 0x34}, +{0x3201, 0x7F}, +{0x3021, 0x06}, +{0x3025, 0x00}, //normal +{0x3400, 0x01}, +{0x3060, 0x01}, +{REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_framesize_VGA_xyskip[][2] = { +// [JPEG_640x360_20.00_25.01_Fps_XY_Skip] +// Set_Device_Format = FORMAT_16_8 +// SET_Device_Addr = 0x54 +{0x3021, 0x00}, +{REG_DLY, 100}, // delay 100ms +{0x32BF, 0x60 }, +{0x320A, 0xB2 }, +{0x32C0, 0x64 }, +{0x32C1, 0x64 }, +{0x32C2, 0x64 }, +{0x32C3, 0x00 }, +{0x32C4, 0x20 }, +{0x32C5, 0x20 }, +{0x32C6, 0x20 }, +{0x32C7, 0x00 }, +{0x32C8, 0x62 }, +{0x32C9, 0x64 }, +{0x32CA, 0x84 }, +{0x32CB, 0x84 }, +{0x32CC, 0x84 }, +{0x32CD, 0x84 }, +{0x32DB, 0x68 }, +{0x32F0, 0x70 }, +{0x3400, 0x08 }, +{0x3400, 0x00 }, +{0x3401, 0x4E }, +{0x3404, 0x00 }, +{0x3405, 0x00 }, +{0x3410, 0x00 }, +{0x3200, 0x3E }, +{0x3201, 0x0F }, +{0x3028, 0x0F }, +{0x3029, 0x00 }, +{0x302A, 0x08 }, +{0x3022, 0x24 }, +{0x3023, 0x6C }, +{0x3002, 0x00 }, +{0x3003, 0x04 }, +{0x3004, 0x00 }, +{0x3005, 0x04 }, +{0x3006, 0x05 }, +{0x3007, 0x03 }, +{0x3008, 0x02 }, +{0x3009, 0xD3 }, +{0x300A, 0x03 }, +{0x300B, 0xFC }, +{0x300C, 0x01 }, +{0x300D, 0x88 }, +{0x300E, 0x02 }, +{0x300F, 0x80 }, +{0x3010, 0x01 }, +{0x3011, 0x68 }, +{0x32B8, 0x3F }, +{0x32B9, 0x31 }, +{0x32BB, 0x87 }, +{0x32BC, 0x38 }, +{0x32BD, 0x3C }, +{0x32BE, 0x34 }, +{0x3201, 0x3F }, +{0x3025, 0x00 }, //normal +{0x3021, 0x06 }, +{0x3400, 0x01 }, +{0x3060, 0x01 }, +{REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_framesize_VGA_xskip[][2] = { +//[JPEG_640x480_Xskip_13.32_13.32_Fps] +{0x3021, 0x00}, +{REG_DLY, 100}, // delay 100ms +{0x32BF, 0x60}, +{0x32C0, 0x5A}, +{0x32C1, 0x5A}, +{0x32C2, 0x5A}, +{0x32C3, 0x00}, +{0x32C4, 0x20}, +{0x32C5, 0x20}, +{0x32C6, 0x20}, +{0x32C7, 0x00}, +{0x32C8, 0x62}, +{0x32C9, 0x5A}, +{0x32CA, 0x7A}, +{0x32CB, 0x7A}, +{0x32CC, 0x7A}, +{0x32CD, 0x7A}, +{0x32DB, 0x68}, +{0x32F0, 0x70}, +{0x3400, 0x08}, +{0x3400, 0x00}, +{0x3401, 0x4E}, +{0x3404, 0x00}, +{0x3405, 0x00}, +{0x3410, 0x00}, +{0x32E0, 0x02}, +{0x32E1, 0x80}, +{0x32E2, 0x01}, +{0x32E3, 0xE0}, +{0x32E4, 0x00}, +{0x32E5, 0x00}, +{0x32E6, 0x00}, +{0x32E7, 0x80}, +{0x3200, 0x3E}, +{0x3201, 0x0F}, +{0x3028, 0x0F}, +{0x3029, 0x00}, +{0x302A, 0x08}, +{0x3022, 0x24}, +{0x3023, 0x2C}, +{0x3002, 0x00}, +{0x3003, 0x04}, +{0x3004, 0x00}, +{0x3005, 0x04}, +{0x3006, 0x05}, +{0x3007, 0x03}, +{0x3008, 0x02}, +{0x3009, 0xD3}, +{0x300A, 0x03}, +{0x300B, 0xFC}, +{0x300C, 0x02}, +{0x300D, 0xE0}, +{0x300E, 0x02}, +{0x300F, 0x80}, +{0x3010, 0x02}, +{0x3011, 0xD0}, +{0x32B8, 0x3F}, +{0x32B9, 0x31}, +{0x32BB, 0x87}, +{0x32BC, 0x38}, +{0x32BD, 0x3C}, +{0x32BE, 0x34}, +{0x3201, 0x7F}, +{0x3021, 0x06}, +{0x3025, 0x00}, //normal +{0x3400, 0x01}, +{0x3060, 0x01}, +{REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_framesize_QVGA_xskip[][2] = { +{0x3021, 0x00}, +{REG_DLY, 100}, // delay 100ms +//[JPEG_320x240_Xskip_13.32_13.32_Fps] +{0x32BF, 0x60}, +{0x32C0, 0x5A}, +{0x32C1, 0x5A}, +{0x32C2, 0x5A}, +{0x32C3, 0x00}, +{0x32C4, 0x20}, +{0x32C5, 0x20}, +{0x32C6, 0x20}, +{0x32C7, 0x00}, +{0x32C8, 0x62}, +{0x32C9, 0x5A}, +{0x32CA, 0x7A}, +{0x32CB, 0x7A}, +{0x32CC, 0x7A}, +{0x32CD, 0x7A}, +{0x32DB, 0x68}, +{0x32F0, 0x70}, +{0x3400, 0x08}, +{0x3400, 0x00}, +{0x3401, 0x4E}, +{0x3404, 0x00}, +{0x3405, 0x00}, +{0x3410, 0x00}, +{0x32E0, 0x01}, +{0x32E1, 0x40}, +{0x32E2, 0x00}, +{0x32E3, 0xF0}, +{0x32E4, 0x01}, +{0x32E5, 0x01}, +{0x32E6, 0x02}, +{0x32E7, 0x03}, +{0x3200, 0x3E}, +{0x3201, 0x0F}, +{0x3028, 0x0F}, +{0x3029, 0x00}, +{0x302A, 0x08}, +{0x3022, 0x24}, +{0x3023, 0x2C}, +{0x3002, 0x00}, +{0x3003, 0x04}, +{0x3004, 0x00}, +{0x3005, 0x04}, +{0x3006, 0x05}, +{0x3007, 0x03}, +{0x3008, 0x02}, +{0x3009, 0xD3}, +{0x300A, 0x03}, +{0x300B, 0xFC}, +{0x300C, 0x02}, +{0x300D, 0xE0}, +{0x300E, 0x02}, +{0x300F, 0x80}, +{0x3010, 0x02}, +{0x3011, 0xD0}, +{0x32B8, 0x3F}, +{0x32B9, 0x31}, +{0x32BB, 0x87}, +{0x32BC, 0x38}, +{0x32BD, 0x3C}, +{0x32BE, 0x34}, +{0x3201, 0x7F}, +{0x3021, 0x06}, +{0x3025, 0x00}, //normal +{0x3400, 0x01}, +{0x3060, 0x01}, +{REGLIST_TAIL, 0x00}, // tail +}; + + +static const DRAM_ATTR uint16_t sensor_framesize_VGA_crop[][2] = { +//[JPEG_640x480_Crop_19.77_19.77_Fps] +{0x3021, 0x00}, +{REG_DLY, 100}, // delay 100ms +{0x32BF, 0x60}, +{0x32C0, 0x5A}, +{0x32C1, 0x5A}, +{0x32C2, 0x5A}, +{0x32C3, 0x00}, +{0x32C4, 0x20}, +{0x32C5, 0x20}, +{0x32C6, 0x20}, +{0x32C7, 0x00}, +{0x32C8, 0x62}, +{0x32C9, 0x5A}, +{0x32CA, 0x7A}, +{0x32CB, 0x7A}, +{0x32CC, 0x7A}, +{0x32CD, 0x7A}, +{0x32DB, 0x68}, +{0x32F0, 0x70}, +{0x3400, 0x08}, +{0x3400, 0x00}, +{0x3401, 0x4E}, +{0x3404, 0x00}, +{0x3405, 0x00}, +{0x3410, 0x00}, +{0x3200, 0x3E}, +{0x3201, 0x0F}, +{0x3028, 0x0F}, +{0x3029, 0x00}, +{0x302A, 0x08}, +{0x3022, 0x24}, +{0x3023, 0x24}, +{0x3002, 0x01}, +{0x3003, 0x44}, +{0x3004, 0x00}, +{0x3005, 0x7C}, +{0x3006, 0x03}, +{0x3007, 0xC3}, +{0x3008, 0x02}, +{0x3009, 0x5B}, +{0x300A, 0x03}, +{0x300B, 0xFC}, +{0x300C, 0x01}, +{0x300D, 0xF0}, +{0x300E, 0x02}, +{0x300F, 0x80}, +{0x3010, 0x01}, +{0x3011, 0xE0}, +{0x32B8, 0x3F}, +{0x32B9, 0x31}, +{0x32BB, 0x87}, +{0x32BC, 0x38}, +{0x32BD, 0x3C}, +{0x32BE, 0x34}, +{0x3201, 0x3F}, +{0x3021, 0x06}, +{0x3025, 0x00}, //normal +{0x3400, 0x01}, +{0x3060, 0x01}, +{REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_framesize_QVGA_crop[][2] = { +//[JPEG_320x240_Crop_19.77_19.77_Fps] +{0x3021, 0x00}, +{REG_DLY, 100}, // delay 100ms +{0x32BF, 0x60}, +{0x32C0, 0x5A}, +{0x32C1, 0x5A}, +{0x32C2, 0x5A}, +{0x32C3, 0x00}, +{0x32C4, 0x20}, +{0x32C5, 0x20}, +{0x32C6, 0x20}, +{0x32C7, 0x00}, +{0x32C8, 0x62}, +{0x32C9, 0x5A}, +{0x32CA, 0x7A}, +{0x32CB, 0x7A}, +{0x32CC, 0x7A}, +{0x32CD, 0x7A}, +{0x32DB, 0x68}, +{0x32F0, 0x70}, +{0x3400, 0x08}, +{0x3400, 0x00}, +{0x3401, 0x4E}, +{0x3404, 0x00}, +{0x3405, 0x00}, +{0x3410, 0x00}, +{0x32E0, 0x01}, +{0x32E1, 0x40}, +{0x32E2, 0x00}, +{0x32E3, 0xF0}, +{0x32E4, 0x01}, +{0x32E5, 0x01}, +{0x32E6, 0x01}, +{0x32E7, 0x02}, +{0x3200, 0x3E}, +{0x3201, 0x0F}, +{0x3028, 0x0F}, +{0x3029, 0x00}, +{0x302A, 0x08}, +{0x3022, 0x24}, +{0x3023, 0x24}, +{0x3002, 0x01}, +{0x3003, 0x44}, +{0x3004, 0x00}, +{0x3005, 0x7C}, +{0x3006, 0x03}, +{0x3007, 0xC3}, +{0x3008, 0x02}, +{0x3009, 0x5B}, +{0x300A, 0x03}, +{0x300B, 0xFC}, +{0x300C, 0x01}, +{0x300D, 0xF0}, +{0x300E, 0x02}, +{0x300F, 0x80}, +{0x3010, 0x01}, +{0x3011, 0xE0}, +{0x32B8, 0x3F}, +{0x32B9, 0x31}, +{0x32BB, 0x87}, +{0x32BC, 0x38}, +{0x32BD, 0x3C}, +{0x32BE, 0x34}, +{0x3201, 0x7F}, +{0x3021, 0x06}, +{0x3025, 0x00}, //normal +{0x3400, 0x01}, +{0x3060, 0x01}, +{REGLIST_TAIL, 0x00}, // tail +}; + +#endif + + diff --git a/esp32-cam-rtos-allframes/ov7670.c b/esp32-cam-rtos-allframes/ov7670.c new file mode 100644 index 0000000..285fe13 --- /dev/null +++ b/esp32-cam-rtos-allframes/ov7670.c @@ -0,0 +1,439 @@ +/* + * This file is part of the OpenMV project. + * author: Juan Schiavoni + * This work is licensed under the MIT license, see the file LICENSE for details. + * + * OV7725 driver. + * + */ +#include +#include +#include +#include "sccb.h" +#include "ov7670.h" +#include "ov7670_regs.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include + +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) +#include "esp32-hal-log.h" +#else +#include "esp_log.h" +static const char* TAG = "ov7760"; +#endif + +static int ov7670_clkrc = 0x01; + +/* + * The default register settings, as obtained from OmniVision. There + * is really no making sense of most of these - lots of "reserved" values + * and such. + * + * These settings give VGA YUYV. + */ +struct regval_list { + uint8_t reg_num; + uint8_t value; +}; + +static struct regval_list ov7670_default_regs[] = { + /* Sensor automatically sets output window when resolution changes. */ + {TSLB, 0x04}, + + /* Frame rate 30 fps at 12 Mhz clock */ + {CLKRC, 0x00}, + {DBLV, 0x4A}, + + {COM10, COM10_VSYNC_NEG | COM10_PCLK_MASK}, + + /* Improve white balance */ + {COM4, 0x40}, + + /* Improve color */ + {RSVD_B0, 0x84}, + + /* Enable 50/60 Hz auto detection */ + {COM11, COM11_EXP|COM11_HZAUTO}, + + /* Disable some delays */ + {HSYST, 0}, + {HSYEN, 0}, + + {MVFP, MVFP_SUN}, + + /* More reserved magic, some of which tweaks white balance */ + {AWBC1, 0x0a}, + {AWBC2, 0xf0}, + {AWBC3, 0x34}, + {AWBC4, 0x58}, + {AWBC5, 0x28}, + {AWBC6, 0x3a}, + + {AWBCTR3, 0x0a}, + {AWBCTR2, 0x55}, + {AWBCTR1, 0x11}, + {AWBCTR0, 0x9e}, + + {COM8, COM8_FAST_AUTO|COM8_STEP_UNLIMIT|COM8_AGC_EN|COM8_AEC_EN|COM8_AWB_EN}, + + /* End marker is FF because in ov7670 the address of GAIN 0 and default value too. */ + {0xFF, 0xFF}, +}; + +static struct regval_list ov7670_fmt_yuv422[] = { + { COM7, 0x0 }, /* Selects YUV mode */ + { RGB444, 0 }, /* No RGB444 please */ + { COM1, 0 }, /* CCIR601 */ + { COM15, COM15_R00FF }, + { MVFP, MVFP_SUN }, + { COM9, 0x6A }, /* 128x gain ceiling; 0x8 is reserved bit */ + { MTX1, 0x80 }, /* "matrix coefficient 1" */ + { MTX2, 0x80 }, /* "matrix coefficient 2" */ + { MTX3, 0 }, /* vb */ + { MTX4, 0x22 }, /* "matrix coefficient 4" */ + { MTX5, 0x5e }, /* "matrix coefficient 5" */ + { MTX6, 0x80 }, /* "matrix coefficient 6" */ + { COM13, COM13_UVSAT }, + { 0xff, 0xff }, /* END MARKER */ +}; + +static struct regval_list ov7670_fmt_rgb565[] = { + { COM7, COM7_FMT_RGB565 }, /* Selects RGB mode */ + { RGB444, 0 }, /* No RGB444 please */ + { COM1, 0x0 }, /* CCIR601 */ + { COM15, COM15_RGB565 |COM15_R00FF }, + { MVFP, MVFP_SUN }, + { COM9, 0x6A }, /* 128x gain ceiling; 0x8 is reserved bit */ + { MTX1, 0xb3 }, /* "matrix coefficient 1" */ + { MTX2, 0xb3 }, /* "matrix coefficient 2" */ + { MTX3, 0 }, /* vb */ + { MTX4, 0x3d }, /* "matrix coefficient 4" */ + { MTX5, 0xa7 }, /* "matrix coefficient 5" */ + { MTX6, 0xe4 }, /* "matrix coefficient 6" */ + { COM13, COM13_UVSAT }, + { 0xff, 0xff }, /* END MARKER */ +}; + + +static struct regval_list ov7670_vga[] = { + { COM3, 0x00 }, + { COM14, 0x00 }, + { SCALING_XSC, 0x3A }, + { SCALING_YSC, 0x35 }, + { SCALING_DCWCTR, 0x11 }, + { SCALING_PCLK_DIV, 0xF0 }, + { SCALING_PCLK_DELAY, 0x02 }, + { 0xff, 0xff }, +}; + +static struct regval_list ov7670_qvga[] = { + { COM3, 0x04 }, + { COM14, 0x19 }, + { SCALING_XSC, 0x3A }, + { SCALING_YSC, 0x35 }, + { SCALING_DCWCTR, 0x11 }, + { SCALING_PCLK_DIV, 0xF1 }, + { SCALING_PCLK_DELAY, 0x02 }, + { 0xff, 0xff }, +}; + +static struct regval_list ov7670_qqvga[] = { + { COM3, 0x04 }, //DCW enable + { COM14, 0x1a }, //pixel clock divided by 4, manual scaling enable, DCW and PCLK controlled by register + { SCALING_XSC, 0x3a }, + { SCALING_YSC, 0x35 }, + { SCALING_DCWCTR, 0x22 }, //downsample by 4 + { SCALING_PCLK_DIV, 0xf2 }, //pixel clock divided by 4 + { SCALING_PCLK_DELAY, 0x02 }, + { 0xff, 0xff }, +}; + +/* + * Write a list of register settings; ff/ff stops the process. + */ +static int ov7670_write_array(sensor_t *sensor, struct regval_list *vals) +{ +int ret = 0; + + while ( (vals->reg_num != 0xff || vals->value != 0xff) && (ret == 0) ) { + ret = SCCB_Write(sensor->slv_addr, vals->reg_num, vals->value); + + ESP_LOGD(TAG, "reset reg %02X, W(%02X) R(%02X)", vals->reg_num, + vals->value, SCCB_Read(sensor->slv_addr, vals->reg_num) ); + + vals++; + } + + return ret; +} + +/* + * Calculate the frame control registers. + */ +static int ov7670_frame_control(sensor_t *sensor, int hstart, int hstop, int vstart, int vstop) +{ +struct regval_list frame[7]; + + frame[0].reg_num = HSTART; + frame[0].value = (hstart >> 3); + + frame[1].reg_num = HSTOP; + frame[1].value = (hstop >> 3); + + frame[2].reg_num = HREF; + frame[2].value = (((hstop & 0x07) << 3) | (hstart & 0x07)); + + frame[3].reg_num = VSTART; + frame[3].value = (vstart >> 2); + + frame[4].reg_num = VSTOP; + frame[4].value = (vstop >> 2); + + frame[5].reg_num = VREF; + frame[5].value = (((vstop & 0x02) << 2) | (vstart & 0x02)); + + /* End mark */ + frame[5].reg_num = 0xFF; + frame[5].value = 0xFF; + + return ov7670_write_array(sensor, frame); +} + +static int reset(sensor_t *sensor) +{ + int ret; + + // Reset all registers + SCCB_Write(sensor->slv_addr, COM7, COM7_RESET); + + // Delay 10 ms + vTaskDelay(10 / portTICK_PERIOD_MS); + + ret = ov7670_write_array(sensor, ov7670_default_regs); + + // Delay + vTaskDelay(30 / portTICK_PERIOD_MS); + + return ret; +} + +static int set_pixformat(sensor_t *sensor, pixformat_t pixformat) +{ +int ret; + + switch (pixformat) { + case PIXFORMAT_RGB565: + case PIXFORMAT_RGB888: + ret = ov7670_write_array(sensor, ov7670_fmt_rgb565); + break; + + case PIXFORMAT_YUV422: + case PIXFORMAT_GRAYSCALE: + default: + ret = ov7670_write_array(sensor, ov7670_fmt_yuv422); + break; + } + + vTaskDelay(30 / portTICK_PERIOD_MS); + + /* + * If we're running RGB565, we must rewrite clkrc after setting + * the other parameters or the image looks poor. If we're *not* + * doing RGB565, we must not rewrite clkrc or the image looks + * *really* poor. + * + * (Update) Now that we retain clkrc state, we should be able + * to write it unconditionally, and that will make the frame + * rate persistent too. + */ + if (pixformat == PIXFORMAT_RGB565) { + ret = SCCB_Write(sensor->slv_addr, CLKRC, ov7670_clkrc); + } + + return ret; +} + +static int set_framesize(sensor_t *sensor, framesize_t framesize) +{ + int ret; + + // store clkrc before changing window settings... + ov7670_clkrc = SCCB_Read(sensor->slv_addr, CLKRC); + + switch (framesize){ + case FRAMESIZE_VGA: + if( (ret = ov7670_write_array(sensor, ov7670_vga)) == 0 ) { + /* These values from Omnivision */ + ret = ov7670_frame_control(sensor, 158, 14, 10, 490); + } + break; + case FRAMESIZE_QVGA: + if( (ret = ov7670_write_array(sensor, ov7670_qvga)) == 0 ) { + /* These values from Omnivision */ + ret = ov7670_frame_control(sensor, 158, 14, 10, 490); + } + break; + case FRAMESIZE_QQVGA: + if( (ret = ov7670_write_array(sensor, ov7670_qqvga)) == 0 ) { + /* These values from Omnivision */ + ret = ov7670_frame_control(sensor, 158, 14, 10, 490); + } + break; + + default: + ret = -1; + } + + vTaskDelay(30 / portTICK_PERIOD_MS); + + if (ret == 0) { + sensor->status.framesize = framesize; + } + + return ret; +} + +static int set_colorbar(sensor_t *sensor, int enable) +{ + uint8_t ret = 0; + // Read register scaling_xsc + uint8_t reg = SCCB_Read(sensor->slv_addr, SCALING_XSC); + + // Pattern to set color bar bit[0]=0 in every case + reg = SCALING_XSC_CBAR(reg); + + // Write pattern to SCALING_XSC + ret = SCCB_Write(sensor->slv_addr, SCALING_XSC, reg); + + // Read register scaling_ysc + reg = SCCB_Read(sensor->slv_addr, SCALING_YSC); + + // Pattern to set color bar bit[0]=0 in every case + reg = SCALING_YSC_CBAR(reg, enable); + + // Write pattern to SCALING_YSC + ret = ret | SCCB_Write(sensor->slv_addr, SCALING_YSC, reg); + + // return 0 or 0xFF + return ret; +} + +static int set_whitebal(sensor_t *sensor, int enable) +{ + // Read register COM8 + uint8_t reg = SCCB_Read(sensor->slv_addr, COM8); + + // Set white bal on/off + reg = COM8_SET_AWB(reg, enable); + + // Write back register COM8 + return SCCB_Write(sensor->slv_addr, COM8, reg); +} + +static int set_gain_ctrl(sensor_t *sensor, int enable) +{ + // Read register COM8 + uint8_t reg = SCCB_Read(sensor->slv_addr, COM8); + + // Set white bal on/off + reg = COM8_SET_AGC(reg, enable); + + // Write back register COM8 + return SCCB_Write(sensor->slv_addr, COM8, reg); +} + +static int set_exposure_ctrl(sensor_t *sensor, int enable) +{ + // Read register COM8 + uint8_t reg = SCCB_Read(sensor->slv_addr, COM8); + + // Set white bal on/off + reg = COM8_SET_AEC(reg, enable); + + // Write back register COM8 + return SCCB_Write(sensor->slv_addr, COM8, reg); +} + +static int set_hmirror(sensor_t *sensor, int enable) +{ + // Read register MVFP + uint8_t reg = SCCB_Read(sensor->slv_addr, MVFP); + + // Set mirror on/off + reg = MVFP_SET_MIRROR(reg, enable); + + // Write back register MVFP + return SCCB_Write(sensor->slv_addr, MVFP, reg); +} + +static int set_vflip(sensor_t *sensor, int enable) +{ + // Read register MVFP + uint8_t reg = SCCB_Read(sensor->slv_addr, MVFP); + + // Set mirror on/off + reg = MVFP_SET_FLIP(reg, enable); + + // Write back register MVFP + return SCCB_Write(sensor->slv_addr, MVFP, reg); +} + +static int init_status(sensor_t *sensor) +{ + sensor->status.awb = 0; + sensor->status.aec = 0; + sensor->status.agc = 0; + sensor->status.hmirror = 0; + sensor->status.vflip = 0; + sensor->status.colorbar = 0; + return 0; +} + +static int set_dummy(sensor_t *sensor, int val){ return -1; } +static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val){ return -1; } + +int ov7670_init(sensor_t *sensor) +{ + // Set function pointers + sensor->reset = reset; + sensor->init_status = init_status; + sensor->set_pixformat = set_pixformat; + sensor->set_framesize = set_framesize; + sensor->set_colorbar = set_colorbar; + sensor->set_whitebal = set_whitebal; + sensor->set_gain_ctrl = set_gain_ctrl; + sensor->set_exposure_ctrl = set_exposure_ctrl; + sensor->set_hmirror = set_hmirror; + sensor->set_vflip = set_vflip; + + //not supported + sensor->set_brightness= set_dummy; + sensor->set_saturation= set_dummy; + sensor->set_quality = set_dummy; + sensor->set_gainceiling = set_gainceiling_dummy; + sensor->set_aec2 = set_dummy; + sensor->set_aec_value = set_dummy; + sensor->set_special_effect = set_dummy; + sensor->set_wb_mode = set_dummy; + sensor->set_ae_level = set_dummy; + sensor->set_dcw = set_dummy; + sensor->set_bpc = set_dummy; + sensor->set_wpc = set_dummy; + sensor->set_awb_gain = set_dummy; + sensor->set_agc_gain = set_dummy; + sensor->set_raw_gma = set_dummy; + sensor->set_lenc = set_dummy; + sensor->set_sharpness = set_dummy; + sensor->set_denoise = set_dummy; + + // Retrieve sensor's signature + sensor->id.MIDH = SCCB_Read(sensor->slv_addr, REG_MIDH); + sensor->id.MIDL = SCCB_Read(sensor->slv_addr, REG_MIDL); + sensor->id.PID = SCCB_Read(sensor->slv_addr, REG_PID); + sensor->id.VER = SCCB_Read(sensor->slv_addr, REG_VER); + + ESP_LOGD(TAG, "OV7670 Attached"); + + return 0; +} diff --git a/esp32-cam-rtos-allframes/ov7670.h b/esp32-cam-rtos-allframes/ov7670.h new file mode 100644 index 0000000..cdf845c --- /dev/null +++ b/esp32-cam-rtos-allframes/ov7670.h @@ -0,0 +1,14 @@ +/* + * This file is part of the OpenMV project. + * author: Juan Schiavoni + * This work is licensed under the MIT license, see the file LICENSE for details. + * + * OV7670 driver. + * + */ +#ifndef __OV7670_H__ +#define __OV7670_H__ +#include "sensor.h" + +int ov7670_init(sensor_t *sensor); +#endif // __OV7670_H__ diff --git a/esp32-cam-rtos-allframes/ov7670_regs.h b/esp32-cam-rtos-allframes/ov7670_regs.h new file mode 100644 index 0000000..6993548 --- /dev/null +++ b/esp32-cam-rtos-allframes/ov7670_regs.h @@ -0,0 +1,354 @@ +/* + * This file is for the OpenMV project so the OV7670 can be used + * author: Juan Schiavoni + * + * OV7670 register definitions. + */ +#ifndef __OV7670_REG_REGS_H__ +#define __OV7670_REG_REGS_H__ +#define GAIN 0x00 /* AGC – Gain control gain setting */ +#define BLUE 0x01 /* AWB – Blue channel gain setting */ +#define RED 0x02 /* AWB – Red channel gain setting */ +#define VREF 0x03 /* AWB – Green channel gain setting */ +#define COM1 0x04 /* Common Control 1 */ +#define BAVG 0x05 /* U/B Average Level */ +#define GAVG 0x06 /* Y/Gb Average Level */ +#define AECH 0x07 /* Exposure VAlue - AEC MSB 5 bits */ +#define RAVG 0x08 /* V/R Average Level */ + +#define COM2 0x09 /* Common Control 2 */ +#define COM2_SOFT_SLEEP 0x10 /* Soft sleep mode */ +#define COM2_OUT_DRIVE_1x 0x00 /* Output drive capability 1x */ +#define COM2_OUT_DRIVE_2x 0x01 /* Output drive capability 2x */ +#define COM2_OUT_DRIVE_3x 0x02 /* Output drive capability 3x */ +#define COM2_OUT_DRIVE_4x 0x03 /* Output drive capability 4x */ + +#define REG_PID 0x0A /* Product ID Number MSB */ +#define REG_VER 0x0B /* Product ID Number LSB */ + +#define COM3 0x0C /* Common Control 3 */ +#define COM3_SWAP_OUT 0x40 /* Output data MSB/LSB swap */ +#define COM3_TRI_CLK 0x20 /* Tri-state output clock */ +#define COM3_TRI_DATA 0x10 /* Tri-state option output */ +#define COM3_SCALE_EN 0x08 /* Scale enable */ +#define COM3_DCW 0x04 /* DCW enable */ + +#define COM4 0x0D /* Common Control 4 */ +#define COM4_PLL_BYPASS 0x00 /* Bypass PLL */ +#define COM4_PLL_4x 0x40 /* PLL frequency 4x */ +#define COM4_PLL_6x 0x80 /* PLL frequency 6x */ +#define COM4_PLL_8x 0xc0 /* PLL frequency 8x */ +#define COM4_AEC_FULL 0x00 /* AEC evaluate full window */ +#define COM4_AEC_1_2 0x10 /* AEC evaluate 1/2 window */ +#define COM4_AEC_1_4 0x20 /* AEC evaluate 1/4 window */ +#define COM4_AEC_2_3 0x30 /* AEC evaluate 2/3 window */ + +#define COM5 0x0E /* Common Control 5 */ +#define COM5_AFR 0x80 /* Auto frame rate control ON/OFF selection (night mode) */ +#define COM5_AFR_SPEED 0x40 /* Auto frame rate control speed selection */ +#define COM5_AFR_0 0x00 /* No reduction of frame rate */ +#define COM5_AFR_1_2 0x10 /* Max reduction to 1/2 frame rate */ +#define COM5_AFR_1_4 0x20 /* Max reduction to 1/4 frame rate */ +#define COM5_AFR_1_8 0x30 /* Max reduction to 1/8 frame rate */ +#define COM5_AFR_4x 0x04 /* Add frame when AGC reaches 4x gain */ +#define COM5_AFR_8x 0x08 /* Add frame when AGC reaches 8x gain */ +#define COM5_AFR_16x 0x0c /* Add frame when AGC reaches 16x gain */ +#define COM5_AEC_NO_LIMIT 0x01 /* No limit to AEC increase step */ + +#define COM6 0x0F /* Common Control 6 */ +#define COM6_AUTO_WINDOW 0x01 /* Auto window setting ON/OFF selection when format changes */ + +#define AEC 0x10 /* AEC[7:0] (see register AECH for AEC[15:8]) */ +#define CLKRC 0x11 /* Internal Clock */ + +#define COM7 0x12 /* Common Control 7 */ +#define COM7_RESET 0x80 /* SCCB Register Reset */ +#define COM7_RES_VGA 0x00 /* Resolution VGA */ +#define COM7_RES_QVGA 0x40 /* Resolution QVGA */ +#define COM7_BT656 0x20 /* BT.656 protocol ON/OFF */ +#define COM7_SENSOR_RAW 0x10 /* Sensor RAW */ +#define COM7_FMT_GBR422 0x00 /* RGB output format GBR422 */ +#define COM7_FMT_RGB565 0x04 /* RGB output format RGB565 */ +#define COM7_FMT_RGB555 0x08 /* RGB output format RGB555 */ +#define COM7_FMT_RGB444 0x0C /* RGB output format RGB444 */ +#define COM7_FMT_YUV 0x00 /* Output format YUV */ +#define COM7_FMT_P_BAYER 0x01 /* Output format Processed Bayer RAW */ +#define COM7_FMT_RGB 0x04 /* Output format RGB */ +#define COM7_FMT_R_BAYER 0x03 /* Output format Bayer RAW */ +#define COM7_SET_FMT(r, x) ((r&0xFC)|((x&0x5)<<0)) + +#define COM8 0x13 /* Common Control 8 */ +#define COM8_FAST_AUTO 0x80 /* Enable fast AGC/AEC algorithm */ +#define COM8_STEP_VSYNC 0x00 /* AEC - Step size limited to vertical blank */ +#define COM8_STEP_UNLIMIT 0x40 /* AEC - Step size unlimited step size */ +#define COM8_BANDF_EN 0x20 /* Banding filter ON/OFF */ +#define COM8_AEC_BANDF 0x10 /* Enable AEC below banding value */ +#define COM8_AEC_FINE_EN 0x08 /* Fine AEC ON/OFF control */ +#define COM8_AGC_EN 0x04 /* AGC Enable */ +#define COM8_AWB_EN 0x02 /* AWB Enable */ +#define COM8_AEC_EN 0x01 /* AEC Enable */ +#define COM8_SET_AGC(r, x) ((r&0xFB)|((x&0x1)<<2)) +#define COM8_SET_AWB(r, x) ((r&0xFD)|((x&0x1)<<1)) +#define COM8_SET_AEC(r, x) ((r&0xFE)|((x&0x1)<<0)) + +#define COM9 0x14 /* Common Control 9 */ +#define COM9_HISTO_AVG 0x80 /* Histogram or average based AEC/AGC selection */ +#define COM9_AGC_GAIN_2x 0x00 /* Automatic Gain Ceiling 2x */ +#define COM9_AGC_GAIN_4x 0x10 /* Automatic Gain Ceiling 4x */ +#define COM9_AGC_GAIN_8x 0x20 /* Automatic Gain Ceiling 8x */ +#define COM9_AGC_GAIN_16x 0x30 /* Automatic Gain Ceiling 16x */ +#define COM9_AGC_GAIN_32x 0x40 /* Automatic Gain Ceiling 32x */ +#define COM9_DROP_VSYNC 0x04 /* Drop VSYNC output of corrupt frame */ +#define COM9_DROP_HREF 0x02 /* Drop HREF output of corrupt frame */ +#define COM9_SET_AGC(r, x) ((r&0x8F)|((x&0x07)<<4)) + +#define COM10 0x15 /* Common Control 10 */ +#define COM10_NEGATIVE 0x80 /* Output negative data */ +#define COM10_HSYNC_EN 0x40 /* HREF changes to HSYNC */ +#define COM10_PCLK_FREE 0x00 /* PCLK output option: free running PCLK */ +#define COM10_PCLK_MASK 0x20 /* PCLK output option: masked during horizontal blank */ +#define COM10_PCLK_REV 0x10 /* PCLK reverse */ +#define COM10_HREF_REV 0x08 /* HREF reverse */ +#define COM10_VSYNC_FALLING 0x00 /* VSYNC changes on falling edge of PCLK */ +#define COM10_VSYNC_RISING 0x04 /* VSYNC changes on rising edge of PCLK */ +#define COM10_VSYNC_NEG 0x02 /* VSYNC negative */ +#define COM10_OUT_RANGE_8 0x01 /* Output data range: Full range */ +#define COM10_OUT_RANGE_10 0x00 /* Output data range: Data from [10] to [F0] (8 MSBs) */ + +#define RSVD_16 0x16 /* Reserved register */ + +#define HSTART 0x17 /* Horizontal Frame (HREF column) Start high 8-bit(low 3 bits are at HREF[2:0]) */ +#define HSTOP 0x18 /* Horizontal Frame (HREF column) end high 8-bit (low 3 bits are at HREF[5:3]) */ +#define VSTART 0x19 /* Vertical Frame (row) Start high 8-bit (low 2 bits are at VREF[1:0]) */ +#define VSTOP 0x1A /* Vertical Frame (row) End high 8-bit (low 2 bits are at VREF[3:2]) */ +#define PSHFT 0x1B /* Data Format - Pixel Delay Select */ +#define REG_MIDH 0x1C /* Manufacturer ID Byte – High */ +#define REG_MIDL 0x1D /* Manufacturer ID Byte – Low */ + +#define MVFP 0x1E /* Mirror/Vflip Enable */ +#define MVFP_MIRROR 0x20 /* Mirror image */ +#define MVFP_FLIP 0x10 /* Vertical flip */ +#define MVFP_SUN 0x02 /* Black sun enable */ +#define MVFP_SET_MIRROR(r,x) ((r&0xDF)|((x&1)<<5)) /* change only bit5 according to x */ +#define MVFP_SET_FLIP(r,x) ((r&0xEF)|((x&1)<<4)) /* change only bit4 according to x */ + +#define LAEC 0x1F /* Fine AEC Value - defines exposure value less than one row period (Reserved?) */ +#define ADCCTR0 0x20 /* ADC control */ +#define ADCCTR1 0x21 /* reserved */ +#define ADCCTR2 0x22 /* reserved */ +#define ADCCTR3 0x23 /* reserved */ +#define AEW 0x24 /* AGC/AEC - Stable Operating Region (Upper Limit) */ +#define AEB 0x25 /* AGC/AEC - Stable Operating Region (Lower Limit) */ +#define VPT 0x26 /* AGC/AEC Fast Mode Operating Region */ +#define BBIAS 0x27 /* B channel signal output bias (effective only when COM6[3]=1) */ +#define GbBIAS 0x28 /* Gb channel signal output bias (effective only when COM6[3]=1) */ +#define RSVD_29 0x29 /* reserved */ +#define EXHCH 0x2A /* Dummy Pixel Insert MSB */ +#define EXHCL 0x2B /* Dummy Pixel Insert LSB */ +#define RBIAS 0x2C /* R channel signal output bias (effective only when COM6[3]=1) */ +#define ADVFL 0x2D /* LSB of Insert Dummy Rows in Vertical Sync (1 bit equals 1 row) */ +#define ADVFH 0x2E /* MSB of Insert Dummy Rows in Vertical Sync */ +#define YAVE 0x2F /* Y/G Channel Average Value */ +#define HSYST 0x30 /* HSync rising edge delay */ +#define HSYEN 0x31 /* HSync falling edge delay */ +#define HREF 0x32 /* Image Start and Size Control DIFFERENT CONTROL SEQUENCE */ +#define CHLF 0x33 /* Array Current control */ +#define ARBLM 0x34 /* Array reference control */ +#define RSVD_35 0x35 /* Reserved */ +#define RSVD_36 0x36 /* Reserved */ +#define ADC 0x37 /* ADC control */ +#define ACOM 0x38 /* ADC and analog common mode control */ +#define OFON 0x39 /* ADC offset control */ +#define TSLB 0x3A /* Line buffer test option */ + +#define COM11 0x3B /* Common control 11 */ +#define COM11_EXP 0x02 +#define COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */ + +#define COM12 0x3C /* Common control 12 */ + +#define COM13 0x3D /* Common control 13 */ +#define COM13_GAMMA 0x80 /* Gamma enable */ +#define COM13_UVSAT 0x40 /* UV saturation auto adjustment */ + +#define COM14 0x3E /* Common Control 14 */ + +#define EDGE 0x3F /* edge enhancement adjustment */ +#define COM15 0x40 /* Common Control 15 DIFFERENT CONTROLS */ +#define COM15_SET_RGB565(r,x) ((r&0xEF)|((x&1)<<4)) /* set rgb565 mode */ +#define COM15_RGB565 0x10 /* RGB565 output */ +#define COM15_R00FF 0xC0 /* Output range: [00] to [FF] */ + +#define COM16 0x41 /* Common Control 16 DIFFERENT CONTROLS */ +#define COM16_AWBGAIN 0x08 /* AWB gain enable */ +#define COM17 0x42 /* Common Control 17 */ + +#define AWBC1 0x43 /* Reserved */ +#define AWBC2 0x44 /* Reserved */ +#define AWBC3 0x45 /* Reserved */ +#define AWBC4 0x46 /* Reserved */ +#define AWBC5 0x47 /* Reserved */ +#define AWBC6 0x48 /* Reserved */ + +#define RSVD_49 0x49 /* Reserved */ +#define RSVD_4A 0x4A /* Reserved */ + +#define REG4B 0x4B /* Register 4B */ +#define DNSTH 0x4C /* Denoise strength */ + +#define RSVD_4D 0x4D /* Reserved */ +#define RSVD_4E 0x4E /* Reserved */ + +#define MTX1 0x4F /* Matrix coefficient 1 */ +#define MTX2 0x50 /* Matrix coefficient 2 */ +#define MTX3 0x51 /* Matrix coefficient 3 */ +#define MTX4 0x52 /* Matrix coefficient 4 */ +#define MTX5 0x53 /* Matrix coefficient 5 */ +#define MTX6 0x54 /* Matrix coefficient 6 */ +#define BRIGHTNESS 0x55 /* Brightness control */ +#define CONTRAST 0x56 /* Contrast control */ +#define CONTRASCENTER 0x57 /* Contrast center */ +#define MTXS 0x58 /* Matrix coefficient sign for coefficient 5 to 0*/ + +#define RSVD_59 0x59 /* Reserved */ +#define RSVD_5A 0x5A /* Reserved */ +#define RSVD_5B 0x5B /* Reserved */ +#define RSVD_5C 0x5C /* Reserved */ +#define RSVD_5D 0x5D /* Reserved */ +#define RSVD_5E 0x5E /* Reserved */ +#define RSVD_5F 0x5F /* Reserved */ +#define RSVD_60 0x60 /* Reserved */ +#define RSVD_61 0x61 /* Reserved */ + +#define LCC1 0x62 /* Lens correction option 1 */ + +#define LCC2 0x63 /* Lens correction option 2 */ +#define LCC3 0x64 /* Lens correction option 3 */ +#define LCC4 0x65 /* Lens correction option 4 */ +#define LCC5 0x66 /* Lens correction option 5 */ + +#define MANU 0x67 /* Manual U Value */ +#define MANV 0x68 /* Manual V Value */ +#define GFIX 0x69 /* Fix gain control */ +#define GGAIN 0x6A /* G channel AWB gain */ + +#define DBLV 0x6B /* PLL and clock ? */ + +#define AWBCTR3 0x6C /* AWB Control 3 */ +#define AWBCTR2 0x6D /* AWB Control 2 */ +#define AWBCTR1 0x6E /* AWB Control 1 */ +#define AWBCTR0 0x6F /* AWB Control 0 */ +#define SCALING_XSC 0x70 /* test pattern and horizontal scaling factor */ +#define SCALING_XSC_CBAR(r) (r&0x7F) /* make sure bit7 is 0 for color bar */ +#define SCALING_YSC 0x71 /* test pattern and vertical scaling factor */ +#define SCALING_YSC_CBAR(r,x) ((r&0x7F)|((x&1)<<7)) /* change bit7 for color bar on/off */ +#define SCALING_DCWCTR 0x72 /* DCW control */ +#define SCALING_PCLK_DIV 0x73 /* */ +#define REG74 0x74 /* */ +#define REG75 0x75 /* */ +#define REG76 0x76 /* */ +#define REG77 0x77 /* */ + +#define RSVD_78 0x78 /* Reserved */ +#define RSVD_79 0x79 /* Reserved */ + +#define SLOP 0x7A /* Gamma curve highest segment slope */ +#define GAM1 0x7B /* Gamma Curve 1st Segment Input End Point 0x04 Output Value */ +#define GAM2 0x7C /* Gamma Curve 2nd Segment Input End Point 0x08 Output Value */ +#define GAM3 0x7D /* Gamma Curve 3rd Segment Input End Point 0x10 Output Value */ +#define GAM4 0x7E /* Gamma Curve 4th Segment Input End Point 0x20 Output Value */ +#define GAM5 0x7F /* Gamma Curve 5th Segment Input End Point 0x28 Output Value */ +#define GAM6 0x80 /* Gamma Curve 6rd Segment Input End Point 0x30 Output Value */ +#define GAM7 0x81 /* Gamma Curve 7th Segment Input End Point 0x38 Output Value */ +#define GAM8 0x82 /* Gamma Curve 8th Segment Input End Point 0x40 Output Value */ +#define GAM9 0x83 /* Gamma Curve 9th Segment Input End Point 0x48 Output Value */ +#define GAM10 0x84 /* Gamma Curve 10th Segment Input End Point 0x50 Output Value */ +#define GAM11 0x85 /* Gamma Curve 11th Segment Input End Point 0x60 Output Value */ +#define GAM12 0x86 /* Gamma Curve 12th Segment Input End Point 0x70 Output Value */ +#define GAM13 0x87 /* Gamma Curve 13th Segment Input End Point 0x90 Output Value */ +#define GAM14 0x88 /* Gamma Curve 14th Segment Input End Point 0xB0 Output Value */ +#define GAM15 0x89 /* Gamma Curve 15th Segment Input End Point 0xD0 Output Value */ + +#define RSVD_8A 0x8A /* Reserved */ +#define RSVD_8B 0x8B /* Reserved */ + +#define RGB444 0x8C /* */ + +#define RSVD_8D 0x8D /* Reserved */ +#define RSVD_8E 0x8E /* Reserved */ +#define RSVD_8F 0x8F /* Reserved */ +#define RSVD_90 0x90 /* Reserved */ +#define RSVD_91 0x91 /* Reserved */ + +#define DM_LNL 0x92 /* Dummy line low 8 bit */ +#define DM_LNH 0x93 /* Dummy line high 8 bit */ +#define LCC6 0x94 /* Lens correction option 6 */ +#define LCC7 0x95 /* Lens correction option 7 */ + +#define RSVD_96 0x96 /* Reserved */ +#define RSVD_97 0x97 /* Reserved */ +#define RSVD_98 0x98 /* Reserved */ +#define RSVD_99 0x99 /* Reserved */ +#define RSVD_9A 0x9A /* Reserved */ +#define RSVD_9B 0x9B /* Reserved */ +#define RSVD_9C 0x9C /* Reserved */ + +#define BD50ST 0x9D /* 50 Hz banding filter value */ +#define BD60ST 0x9E /* 60 Hz banding filter value */ +#define HAECC1 0x9F /* Histogram-based AEC/AGC control 1 */ +#define HAECC2 0xA0 /* Histogram-based AEC/AGC control 2 */ + +#define RSVD_A1 0xA1 /* Reserved */ + +#define SCALING_PCLK_DELAY 0xA2 /* Pixel clock delay */ + +#define RSVD_A3 0xA3 /* Reserved */ + +#define NT_CNTRL 0xA4 /* */ +#define BD50MAX 0xA5 /* 50 Hz banding step limit */ +#define HAECC3 0xA6 /* Histogram-based AEC/AGC control 3 */ +#define HAECC4 0xA7 /* Histogram-based AEC/AGC control 4 */ +#define HAECC5 0xA8 /* Histogram-based AEC/AGC control 5 */ +#define HAECC6 0xA9 /* Histogram-based AEC/AGC control 6 */ + +#define HAECC7 0xAA /* Histogram-based AEC/AGC control 7 */ +#define HAECC_EN 0x80 /* Histogram-based AEC algorithm enable */ + +#define BD60MAX 0xAB /* 60 Hz banding step limit */ + +#define STR_OPT 0xAC /* Register AC */ +#define STR_R 0xAD /* R gain for led output frame */ +#define STR_G 0xAE /* G gain for led output frame */ +#define STR_B 0xAF /* B gain for led output frame */ +#define RSVD_B0 0xB0 /* Reserved */ +#define ABLC1 0xB1 /* */ +#define RSVD_B2 0xB2 /* Reserved */ +#define THL_ST 0xB3 /* ABLC target */ +#define THL_DLT 0xB5 /* ABLC stable range */ + +#define RSVD_B6 0xB6 /* Reserved */ +#define RSVD_B7 0xB7 /* Reserved */ +#define RSVD_B8 0xB8 /* Reserved */ +#define RSVD_B9 0xB9 /* Reserved */ +#define RSVD_BA 0xBA /* Reserved */ +#define RSVD_BB 0xBB /* Reserved */ +#define RSVD_BC 0xBC /* Reserved */ +#define RSVD_BD 0xBD /* Reserved */ + +#define AD_CHB 0xBE /* blue channel black level compensation */ +#define AD_CHR 0xBF /* Red channel black level compensation */ +#define AD_CHGb 0xC0 /* Gb channel black level compensation */ +#define AD_CHGr 0xC1 /* Gr channel black level compensation */ + +#define RSVD_C2 0xC2 /* Reserved */ +#define RSVD_C3 0xC3 /* Reserved */ +#define RSVD_C4 0xC4 /* Reserved */ +#define RSVD_C5 0xC5 /* Reserved */ +#define RSVD_C6 0xC6 /* Reserved */ +#define RSVD_C7 0xC7 /* Reserved */ +#define RSVD_C8 0xC8 /* Reserved */ + +#define SATCTR 0xC9 /* Saturation control */ +#define SET_REG(reg, x) (##reg_DEFAULT|x) + +#endif //__OV7670_REG_REGS_H__ diff --git a/esp32-cam-rtos-allframes/sccb.c b/esp32-cam-rtos-allframes/sccb.c index d2f5fb9..cb615bb 100644 --- a/esp32-cam-rtos-allframes/sccb.c +++ b/esp32-cam-rtos-allframes/sccb.c @@ -7,6 +7,7 @@ * */ #include +#include #include #include #include "sccb.h" @@ -19,11 +20,8 @@ static const char* TAG = "sccb"; #endif -//#undef CONFIG_SCCB_HARDWARE_I2C - #define LITTLETOBIG(x) ((x<<8)|(x>>8)) -#ifdef CONFIG_SCCB_HARDWARE_I2C #include "driver/i2c.h" #define SCCB_FREQ 100000 /*!< I2C master frequency*/ @@ -39,16 +37,13 @@ const int SCCB_I2C_PORT = 1; const int SCCB_I2C_PORT = 0; #endif static uint8_t ESP_SLAVE_ADDR = 0x3c; -#else -#include "twi.h" -#endif 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; + memset(&conf, 0, sizeof(i2c_config_t)); conf.mode = I2C_MODE_MASTER; conf.sda_io_num = pin_sda; conf.sda_pullup_en = GPIO_PULLUP_ENABLE; @@ -58,15 +53,11 @@ int SCCB_Init(int pin_sda, int pin_scl) 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); -#endif return 0; } 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(); @@ -82,28 +73,10 @@ uint8_t SCCB_Probe() slave_addr++; } return ESP_SLAVE_ADDR; -#else - uint8_t reg = 0x00; - uint8_t slv_addr = 0x00; - - ESP_LOGI(TAG, "SCCB_Probe start"); - for (uint8_t i = 0; i < 127; i++) { - if (twi_writeTo(i, ®, 1, true) == 0) { - slv_addr = i; - break; - } - - if (i!=126) { - vTaskDelay(10 / portTICK_PERIOD_MS); // Necessary for OV7725 camera (not for OV2640). - } - } - return slv_addr; -#endif } 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(); @@ -125,28 +98,10 @@ uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg) ESP_LOGE(TAG, "SCCB_Read Failed addr:0x%02x, reg:0x%02x, data:0x%02x, ret:%d", slv_addr, reg, data, ret); } return data; -#else - uint8_t data=0; - - int rc = twi_writeTo(slv_addr, ®, 1, 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, "SCCB_Read [%02x] failed rc=%d\n", reg, rc); - } - return data; -#endif } 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); @@ -160,23 +115,10 @@ uint8_t SCCB_Write(uint8_t slv_addr, uint8_t reg, uint8_t data) ESP_LOGE(TAG, "SCCB_Write Failed addr:0x%02x, reg:0x%02x, data:0x%02x, ret:%d", slv_addr, reg, data, ret); } return ret == ESP_OK ? 0 : -1; -#else - uint8_t ret=0; - uint8_t buf[] = {reg, data}; - - if(twi_writeTo(slv_addr, buf, 2, true) != 0) { - ret=0xFF; - } - if (ret != 0) { - ESP_LOGE(TAG, "SCCB_Write [%02x]=%02x failed\n", reg, data); - } - 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); @@ -201,32 +143,11 @@ uint8_t SCCB_Read16(uint8_t slv_addr, uint16_t reg) 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 *)®_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 *)®_htons; @@ -243,18 +164,4 @@ uint8_t SCCB_Write16(uint8_t slv_addr, uint16_t reg, uint8_t data) 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 *)®_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 } diff --git a/esp32-cam-rtos-allframes/sensor.h b/esp32-cam-rtos-allframes/sensor.h index 3ea7e2c..ad6cd89 100644 --- a/esp32-cam-rtos-allframes/sensor.h +++ b/esp32-cam-rtos-allframes/sensor.h @@ -11,11 +11,13 @@ #include #include +#define NT99141_PID (0x14) #define OV9650_PID (0x96) #define OV7725_PID (0x77) #define OV2640_PID (0x26) #define OV3660_PID (0x36) #define OV5640_PID (0x56) +#define OV7670_PID (0x76) typedef enum { PIXFORMAT_RGB565, // 2BPP/RGB565 diff --git a/esp32-cam-rtos-allframes/to_bmp.c b/esp32-cam-rtos-allframes/to_bmp.c index 59455de..85f9c88 100644 --- a/esp32-cam-rtos-allframes/to_bmp.c +++ b/esp32-cam-rtos-allframes/to_bmp.c @@ -20,6 +20,17 @@ #include "sdkconfig.h" #include "esp_jpg_decode.h" +#include "esp_system.h" +#if ESP_IDF_VERSION_MAJOR >= 4 // IDF 4+ +#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4 +#include "esp32/spiram.h" +#else +#error Target CONFIG_IDF_TARGET is not supported +#endif +#else // ESP32 Before IDF 4.0 +#include "esp_spiram.h" +#endif + #if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) #include "esp32-hal-log.h" #define TAG "" diff --git a/esp32-cam-rtos-allframes/twi.h b/esp32-cam-rtos-allframes/twi.h index 60624f8..71f9907 100644 --- a/esp32-cam-rtos-allframes/twi.h +++ b/esp32-cam-rtos-allframes/twi.h @@ -35,4 +35,4 @@ uint8_t twi_readFrom(unsigned char address, unsigned char * buf, unsigned int le } #endif -#endif \ No newline at end of file +#endif diff --git a/esp32-cam-rtos/camera.c b/esp32-cam-rtos/camera.c index 0fc85f6..f0f3a2e 100644 --- a/esp32-cam-rtos/camera.c +++ b/esp32-cam-rtos/camera.c @@ -48,6 +48,12 @@ #if CONFIG_OV5640_SUPPORT #include "ov5640.h" #endif +#if CONFIG_NT99141_SUPPORT +#include "nt99141.h" +#endif +#if CONFIG_OV7670_SUPPORT +#include "ov7670.h" +#endif typedef enum { CAMERA_NONE = 0, @@ -56,6 +62,8 @@ typedef enum { CAMERA_OV2640 = 2640, CAMERA_OV3660 = 3660, CAMERA_OV5640 = 5640, + CAMERA_OV7670 = 7670, + CAMERA_NT99141 = 9141, } camera_model_t; #define REG_PID 0x0A @@ -369,12 +377,10 @@ static inline void IRAM_ATTR i2s_conf_reset() } } -static void i2s_init() +static void i2s_gpio_init(const camera_config_t* config) { - camera_config_t* config = &s_state->config; - // Configure input GPIOs - gpio_num_t pins[] = { + const gpio_num_t pins[] = { config->pin_d7, config->pin_d6, config->pin_d5, @@ -391,15 +397,21 @@ static void i2s_init() .mode = GPIO_MODE_INPUT, .pull_up_en = GPIO_PULLUP_ENABLE, .pull_down_en = GPIO_PULLDOWN_DISABLE, - .intr_type = GPIO_INTR_DISABLE + .intr_type = GPIO_INTR_DISABLE, + .pin_bit_mask = 0LL }; for (int i = 0; i < sizeof(pins) / sizeof(gpio_num_t); ++i) { if (rtc_gpio_is_valid_gpio(pins[i])) { rtc_gpio_deinit(pins[i]); } - conf.pin_bit_mask = 1LL << pins[i]; - gpio_config(&conf); + conf.pin_bit_mask |= 1LL << pins[i]; } + gpio_config(&conf); +} + +static void i2s_init() +{ + camera_config_t* config = &s_state->config; // Route input GPIOs to I2S peripheral using GPIO matrix gpio_matrix_in(config->pin_d0, I2S0I_DATA_IN0_IDX, false); @@ -738,7 +750,7 @@ 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("bh 0x%08x\n", sig); + ESP_LOGD(TAG,"unexpected JPEG signature 0x%08x\n", sig); s_state->fb->bad = 1; return; } @@ -955,11 +967,15 @@ esp_err_t camera_probe(const camera_config_t* config, camera_model_t* out_camera return ESP_ERR_NO_MEM; } - ESP_LOGD(TAG, "Enabling XCLK output"); - camera_enable_out_clock(config); + if(config->pin_xclk >= 0) { + ESP_LOGD(TAG, "Enabling XCLK output"); + camera_enable_out_clock(config); + } - ESP_LOGD(TAG, "Initializing SSCB"); - SCCB_Init(config->pin_sscb_sda, config->pin_sscb_scl); + if (config->pin_sscb_sda != -1) { + ESP_LOGD(TAG, "Initializing SSCB"); + SCCB_Init(config->pin_sscb_sda, config->pin_sscb_scl); + } if(config->pin_pwdn >= 0) { ESP_LOGD(TAG, "Resetting camera by power down line"); @@ -1011,16 +1027,33 @@ esp_err_t camera_probe(const camera_config_t* config, camera_model_t* out_camera slv_addr = SCCB_Probe(); } #endif +#if CONFIG_NT99141_SUPPORT + if (slv_addr == 0x2a) + { + ESP_LOGD(TAG, "Resetting NT99141"); + SCCB_Write16(0x2a, 0x3008, 0x01);//bank sensor + } +#endif 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 (CONFIG_OV3660_SUPPORT || CONFIG_OV5640_SUPPORT || CONFIG_NT99141_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); vTaskDelay(10 / portTICK_PERIOD_MS); ESP_LOGD(TAG, "Camera PID=0x%02x VER=0x%02x", id->PID, id->VER); + } else if(s_state->sensor.slv_addr == 0x2a){ + id->PID = SCCB_Read16(s_state->sensor.slv_addr, 0x3000); + id->VER = SCCB_Read16(s_state->sensor.slv_addr, 0x3001); + vTaskDelay(10 / portTICK_PERIOD_MS); + ESP_LOGD(TAG, "Camera PID=0x%02x VER=0x%02x", id->PID, id->VER); + if(config->xclk_freq_hz > 10000000) + { + ESP_LOGE(TAG, "NT99141: only XCLK under 10MHz is supported, and XCLK is now set to 10M"); + s_state->sensor.xclk_freq_hz = 10000000; + } } else { #endif id->PID = SCCB_Read(s_state->sensor.slv_addr, REG_PID); @@ -1031,7 +1064,7 @@ esp_err_t camera_probe(const camera_config_t* config, camera_model_t* out_camera 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 || CONFIG_OV5640_SUPPORT) +#if (CONFIG_OV3660_SUPPORT || CONFIG_OV5640_SUPPORT || CONFIG_NT99141_SUPPORT) } #endif @@ -1060,6 +1093,18 @@ esp_err_t camera_probe(const camera_config_t* config, camera_model_t* out_camera *out_camera_model = CAMERA_OV5640; ov5640_init(&s_state->sensor); break; +#endif +#if CONFIG_OV7670_SUPPORT + case OV7670_PID: + *out_camera_model = CAMERA_OV7670; + ov7670_init(&s_state->sensor); + break; +#endif +#if CONFIG_NT99141_SUPPORT + case NT99141_PID: + *out_camera_model = CAMERA_NT99141; + NT99141_init(&s_state->sensor); + break; #endif default: id->PID = 0; @@ -1116,6 +1161,20 @@ esp_err_t camera_init(const camera_config_t* config) frame_size = FRAMESIZE_QSXGA; } break; +#endif +#if CONFIG_OV7670_SUPPORT + case OV7670_PID: + if (frame_size > FRAMESIZE_VGA) { + frame_size = FRAMESIZE_VGA; + } + break; +#endif +#if CONFIG_NT99141_SUPPORT + case NT99141_PID: + if (frame_size > FRAMESIZE_HD) { + frame_size = FRAMESIZE_HD; + } + break; #endif default: return ESP_ERR_CAMERA_NOT_SUPPORTED; @@ -1126,7 +1185,7 @@ esp_err_t camera_init(const camera_config_t* config) if (pix_format == PIXFORMAT_GRAYSCALE) { s_state->fb_size = s_state->width * s_state->height; - if (s_state->sensor.id.PID == OV3660_PID || s_state->sensor.id.PID == OV5640_PID) { + if (s_state->sensor.id.PID == OV3660_PID || s_state->sensor.id.PID == OV5640_PID || s_state->sensor.id.PID == NT99141_PID) { if (is_hs_mode()) { s_state->sampling_mode = SM_0A00_0B00; s_state->dma_filter = &dma_filter_yuyv_highspeed; @@ -1147,20 +1206,28 @@ esp_err_t camera_init(const camera_config_t* config) } s_state->fb_bytes_per_pixel = 1; // frame buffer stores Y8 } else if (pix_format == PIXFORMAT_YUV422 || pix_format == PIXFORMAT_RGB565) { - s_state->fb_size = s_state->width * s_state->height * 2; - if (is_hs_mode() && s_state->sensor.id.PID != OV7725_PID) { - 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 = 2; // camera sends YU/YV - s_state->fb_bytes_per_pixel = 2; // frame buffer stores YU/YV/RGB565 + s_state->fb_size = s_state->width * s_state->height * 2; + if (is_hs_mode() && s_state->sensor.id.PID != OV7725_PID) { + if(s_state->sensor.id.PID == OV7670_PID) { + s_state->sampling_mode = SM_0A0B_0B0C; + }else{ + 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 = 2; // camera sends YU/YV + 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; + if(s_state->sensor.id.PID == OV7670_PID) { + s_state->sampling_mode = SM_0A0B_0B0C; + }else{ + s_state->sampling_mode = SM_0A00_0B00; + } s_state->dma_filter = &dma_filter_rgb888_highspeed; } else { s_state->sampling_mode = SM_0A0B_0C0D; @@ -1169,7 +1236,7 @@ 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 && s_state->sensor.id.PID != OV5640_PID) { + if (s_state->sensor.id.PID != OV2640_PID && s_state->sensor.id.PID != OV3660_PID && s_state->sensor.id.PID != OV5640_PID && s_state->sensor.id.PID != NT99141_PID) { ESP_LOGE(TAG, "JPEG format is only supported for ov2640, ov3660 and ov5640"); err = ESP_ERR_NOT_SUPPORTED; goto fail; @@ -1306,6 +1373,7 @@ fail: esp_err_t esp_camera_init(const camera_config_t* config) { camera_model_t camera_model = CAMERA_NONE; + i2s_gpio_init(config); esp_err_t err = camera_probe(config, &camera_model); if (err != ESP_OK) { ESP_LOGE(TAG, "Camera probe failed with error 0x%x", err); @@ -1324,6 +1392,10 @@ esp_err_t esp_camera_init(const camera_config_t* config) ESP_LOGI(TAG, "Detected OV3660 camera"); } else if (camera_model == CAMERA_OV5640) { ESP_LOGI(TAG, "Detected OV5640 camera"); + } else if (camera_model == CAMERA_OV7670) { + ESP_LOGI(TAG, "Detected OV7670 camera"); + } else if (camera_model == CAMERA_NT99141) { + ESP_LOGI(TAG, "Detected NT99141 camera"); } else { ESP_LOGI(TAG, "Camera not supported"); err = ESP_ERR_CAMERA_NOT_SUPPORTED; @@ -1370,9 +1442,12 @@ esp_err_t esp_camera_deinit() } dma_desc_deinit(); camera_fb_deinit(); + + if(s_state->config.pin_xclk >= 0) { + camera_disable_out_clock(); + } free(s_state); s_state = NULL; - camera_disable_out_clock(); periph_module_disable(PERIPH_I2S0_MODULE); return ESP_OK; } @@ -1430,11 +1505,11 @@ sensor_t * esp_camera_sensor_get() esp_err_t esp_camera_save_to_nvs(const char *key) { -#if ESP_IDF_VERSION_MAJOR > 3 - nvs_handle_t handle; -#else +//#if ESP_IDF_VERSION_MAJOR > 3 +// nvs_handle_t handle; +//#else nvs_handle handle; -#endif +//#endif esp_err_t ret = nvs_open(key,NVS_READWRITE,&handle); if (ret == ESP_OK) { @@ -1458,11 +1533,11 @@ esp_err_t esp_camera_save_to_nvs(const char *key) esp_err_t esp_camera_load_from_nvs(const char *key) { -#if ESP_IDF_VERSION_MAJOR > 3 - nvs_handle_t handle; -#else +//#if ESP_IDF_VERSION_MAJOR > 3 +// nvs_handle_t handle; +//#else nvs_handle handle; -#endif +//#endif uint8_t pf; esp_err_t ret = nvs_open(key,NVS_READWRITE,&handle); diff --git a/esp32-cam-rtos/esp32-cam-rtos.ino b/esp32-cam-rtos/esp32-cam-rtos.ino index f7c97c6..4479e21 100644 --- a/esp32-cam-rtos/esp32-cam-rtos.ino +++ b/esp32-cam-rtos/esp32-cam-rtos.ino @@ -480,7 +480,9 @@ void setup() ESP.restart(); } - + sensor_t* s = esp_camera_sensor_get(); + s->set_vflip(s, true); + // Configure and connect to WiFi IPAddress ip; diff --git a/esp32-cam-rtos/esp32-camera-master.zip b/esp32-cam-rtos/esp32-camera-master.zip deleted file mode 100644 index e8118ae..0000000 Binary files a/esp32-cam-rtos/esp32-camera-master.zip and /dev/null differ diff --git a/esp32-cam-rtos/img_converters.h b/esp32-cam-rtos/img_converters.h index 2b83c4d..330f8db 100644 --- a/esp32-cam-rtos/img_converters.h +++ b/esp32-cam-rtos/img_converters.h @@ -62,7 +62,8 @@ bool frame2jpg_cb(camera_fb_t * fb, uint8_t quality, jpg_out_cb cb, void * arg); * @param height Height in pixels of the source image * @param format Format of the source image * @param quality JPEG quality of the resulting image - * @param out Pointer to be populated with the address of the resulting buffer + * @param out Pointer to be populated with the address of the resulting buffer. + * You MUST free the pointer once you are done with it. * @param out_len Pointer to be populated with the length of the output buffer * * @return true on success diff --git a/esp32-cam-rtos/nt99141.c b/esp32-cam-rtos/nt99141.c new file mode 100644 index 0000000..07a9cc4 --- /dev/null +++ b/esp32-cam-rtos/nt99141.c @@ -0,0 +1,1032 @@ +/* + * 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. + * + * NT99141 driver. + * + */ +#include +#include +#include +#include "sccb.h" +#include "nt99141.h" +#include "nt99141_regs.h" +#include "nt99141_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 = "NT99141"; +#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_LOGD(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 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) +{ + return -1; +} + +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, 0x01); + + if (ret) { + ESP_LOGE(TAG, "Software Reset FAILED!"); + return ret; + } + + vTaskDelay(100 / portTICK_PERIOD_MS); + ret = write_regs(sensor->slv_addr, sensor_default_regs); //re-initial + + 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; + + // V-Flip + if (sensor->status.vflip) { + reg20 |= 0x01; + reg4514_test |= 1; + } + + // H-Mirror + if (sensor->status.hmirror) { + reg21 |= 0x02; + reg4514_test |= 2; + } + + switch (reg4514_test) { + + } + + if (write_reg(sensor->slv_addr, TIMING_TC_REG20, reg20 | reg21)) { + 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.binning, sensor->status.vflip, sensor->status.hmirror, reg4514); + return ret; +} + +static int set_framesize(sensor_t *sensor, framesize_t framesize) +{ + int ret = 0; + + sensor->status.framesize = framesize; + ret = write_regs(sensor->slv_addr, sensor_default_regs); + + if (framesize == FRAMESIZE_QVGA) { + ESP_LOGD(TAG, "Set FRAMESIZE_QVGA"); + ret = write_regs(sensor->slv_addr, sensor_framesize_QVGA); +#if CONFIG_NT99141_SUPPORT_XSKIP + ESP_LOGD(TAG, "Set FRAMESIZE_QVGA: xskip mode"); + ret = write_regs(sensor->slv_addr, sensor_framesize_QVGA_xskip); +#elif CONFIG_NT99141_SUPPORT_CROP + ESP_LOGD(TAG, "Set FRAMESIZE_QVGA: crop mode"); + ret = write_regs(sensor->slv_addr, sensor_framesize_QVGA_crop); +#endif + } else if (framesize == FRAMESIZE_VGA) { + ESP_LOGD(TAG, "Set FRAMESIZE_VGA"); + // ret = write_regs(sensor->slv_addr, sensor_framesize_VGA); + ret = write_regs(sensor->slv_addr, sensor_framesize_VGA_xyskip);// Resolution:640*360 This configuration is equally-scaled without deforming +#ifdef CONFIG_NT99141_SUPPORT_XSKIP + ESP_LOGD(TAG, "Set FRAMESIZE_QVGA: xskip mode"); + ret = write_regs(sensor->slv_addr, sensor_framesize_VGA_xskip); +#elif CONFIG_NT99141_SUPPORT_CROP + ESP_LOGD(TAG, "Set FRAMESIZE_QVGA: crop mode"); + ret = write_regs(sensor->slv_addr, sensor_framesize_VGA_crop); +#endif + } else if (framesize >= FRAMESIZE_HD) { + ESP_LOGD(TAG, "Set FRAMESIZE_HD"); + ret = write_regs(sensor->slv_addr, sensor_framesize_HD); + } else { + ESP_LOGD(TAG, "Dont suppost this size, Set FRAMESIZE_VGA"); + ret = write_regs(sensor->slv_addr, sensor_framesize_VGA); + } + + return 0; +} + +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, 0x32bb, 0x87, 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; + int data = 0; + // ret = write_reg_bits(sensor->slv_addr, 0x32bb, 0x87, enable); + data = read_reg(sensor->slv_addr, 0x3201); + ESP_LOGD(TAG, "set_exposure_ctrl:enable"); + if (enable) { + ESP_LOGD(TAG, "set_exposure_ctrl:enable"); + ret = write_reg(sensor->slv_addr, 0x3201, (1 << 5) | data); + } else { + ESP_LOGD(TAG, "set_exposure_ctrl:disable"); + ret = write_reg(sensor->slv_addr, 0x3201, (~(1 << 5)) & data); + } + + 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; + + 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; + + 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; + + 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; + + 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; + + 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; + + 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; + + 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) +{ + ESP_LOGD(TAG, "get_agc_gain can not be configured at present"); + return 0; +} + +//real gain +static int set_agc_gain(sensor_t *sensor, int gain) +{ + ESP_LOGD(TAG, "set_agc_gain can not be configured at present"); + // ESP_LOGD(TAG, "GAIN = %d\n", gain); + int cnt = gain / 2; + + switch (cnt) { + case 0: + ESP_LOGD(TAG, "set_agc_gain: 1x"); + write_reg(sensor->slv_addr, 0X301D, 0X00); + break; + + case 1: + ESP_LOGD(TAG,"set_agc_gain: 2x"); + write_reg(sensor->slv_addr, 0X301D, 0X0F); + break; + + case 2: + ESP_LOGD(TAG,"set_agc_gain: 4x"); + write_reg(sensor->slv_addr, 0X301D, 0X2F); + break; + + case 3: + ESP_LOGD(TAG,"set_agc_gain: 6x"); + write_reg(sensor->slv_addr, 0X301D, 0X37); + break; + + case 4: + ESP_LOGD(TAG,"set_agc_gain: 8x"); + write_reg(sensor->slv_addr, 0X301D, 0X3F); + break; + + default: + ESP_LOGD(TAG,"fail set_agc_gain"); + break; + } + + return 0; +} + +static int get_aec_value(sensor_t *sensor) +{ + ESP_LOGD(TAG, "get_aec_value can not be configured at present"); + return 0; +} + +static int set_aec_value(sensor_t *sensor, int value) +{ + ESP_LOGD(TAG, "set_aec_value can not be configured at present"); + int ret = 0; + // ESP_LOGD(TAG, " set_aec_value to: %d", value); + ret = write_reg_bits(sensor->slv_addr, 0x3012, 0x00, (value >> 8) & 0xff); + ret = write_reg_bits(sensor->slv_addr, 0x3013, 0x01, value & 0xff); + + if (ret == 0) { + ESP_LOGD(TAG, " set_aec_value to: %d", value); + // sensor->status.aec = enable; + } + + return ret; +} + +static int set_ae_level(sensor_t *sensor, int level) +{ + ESP_LOGD(TAG, "set_ae_level can not be configured at present"); + int ret = 0; + + if (level < 0) { + level = 0; + } else if (level > 9) { + level = 9; + } + + for (int i = 0; i < 5; i++) { + ret += write_reg(sensor->slv_addr, sensor_ae_level[ 5 * level + i ][0], sensor_ae_level[5 * level + i ][1]); + } + + if (ret) { + ESP_LOGE(TAG, " fail to set ae level: %d", ret); + } + + return 0; +} + +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, 0x3201, (mode != 0)); + + if (ret) { + return ret; + } + + switch (mode) { + case 1://Sunny + ret = write_reg16(sensor->slv_addr, 0x3290, 0x01) + || write_reg16(sensor->slv_addr, 0x3291, 0x38) + || write_reg16(sensor->slv_addr, 0x3296, 0x01) + || write_reg16(sensor->slv_addr, 0x3297, 0x68) + || write_reg16(sensor->slv_addr, 0x3060, 0x01); + + break; + + case 2://Cloudy + + ret = write_reg16(sensor->slv_addr, 0x3290, 0x01) + || write_reg16(sensor->slv_addr, 0x3291, 0x51) + || write_reg16(sensor->slv_addr, 0x3296, 0x01) + || write_reg16(sensor->slv_addr, 0x3297, 0x00) + || write_reg16(sensor->slv_addr, 0x3060, 0x01); + break; + + case 3://INCANDESCENCE] + ret = write_reg16(sensor->slv_addr, 0x3290, 0x01) + || write_reg16(sensor->slv_addr, 0x3291, 0x30) + || write_reg16(sensor->slv_addr, 0x3296, 0x01) + || write_reg16(sensor->slv_addr, 0x3297, 0xCB) + || write_reg16(sensor->slv_addr, 0x3060, 0x01); + break; + + case 4://FLUORESCENT + ret = write_reg16(sensor->slv_addr, 0x3290, 0x01) + || write_reg16(sensor->slv_addr, 0x3291, 0x70) + || write_reg16(sensor->slv_addr, 0x3296, 0x01) + || write_reg16(sensor->slv_addr, 0x3297, 0xFF) + || write_reg16(sensor->slv_addr, 0x3060, 0x01); + 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, 0x32F1, regs[0]) + || write_reg(sensor->slv_addr, 0x32F4, regs[1]) + || write_reg(sensor->slv_addr, 0x32F5, regs[2]) + || write_reg(sensor->slv_addr, 0x3060, 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 = 0xA0; + break; + + case 2: + value = 0x90; + break; + + case 1: + value = 0x88; + break; + + case -1: + value = 0x78; + negative = true; + break; + + case -2: + value = 0x70; + negative = true; + break; + + case -3: + value = 0x60; + negative = true; + break; + + default: // 0 + break; + } + + ret = write_reg(sensor->slv_addr, 0x32F2, value); + + 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; + uint8_t value1 = 0, value2 = 0 ; + bool negative = false; + + switch (level) { + case 3: + value1 = 0xD0; + value2 = 0xB0; + break; + + case 2: + value1 = 0xE0; + value2 = 0xA0; + break; + + case 1: + value1 = 0xF0; + value2 = 0x90; + break; + + case 0: + value1 = 0x00; + value2 = 0x80; + break; + + case -1: + value1 = 0x10; + value2 = 0x70; + break; + + case -2: + value1 = 0x20; + value2 = 0x60; + break; + + case -3: + value1 = 0x30; + value2 = 0x50; + break; + + default: // 0 + break; + } + + ret = write_reg(sensor->slv_addr, 0x32FC, value1); + ret = write_reg(sensor->slv_addr, 0x32F2, value2); + ret = write_reg(sensor->slv_addr, 0x3060, 0x01); + + 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]; + { + ret = write_reg(sensor->slv_addr, 0x32F3, regs[0]); + + if (ret) { + return ret; + } + } + + 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) +{ + ESP_LOGD(TAG, "set_gainceiling can not be configured at present"); + return 0; +} + +static int get_denoise(sensor_t *sensor) +{ + + return (read_reg(sensor->slv_addr, 0x5306) / 4) + 1; +} + +static int set_denoise(sensor_t *sensor, int level) +{ + ESP_LOGD(TAG, "set_denoise can not be configured at present"); + return 0; +} + +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); + + 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; + if (xclk > 10) + { + ESP_LOGE(TAG, "only XCLK under 10MHz is supported, and XCLK is now set to 10M"); + xclk = 10; + } + 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, 0x3301)); + sensor->status.denoise = get_denoise(sensor); + sensor->status.ae_level = 0; + sensor->status.gainceiling = read_reg16(sensor->slv_addr, 0x32F0) & 0xFF; + sensor->status.awb = check_reg_mask(sensor->slv_addr, ISP_CONTROL_01, 0x10); + 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, 0x3000, 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, 0x3000, 0x04); + return 0; +} + +int NT99141_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/esp32-cam-rtos/nt99141.h b/esp32-cam-rtos/nt99141.h new file mode 100644 index 0000000..287a742 --- /dev/null +++ b/esp32-cam-rtos/nt99141.h @@ -0,0 +1,16 @@ +/* + * 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. + * + * NT99141 driver. + * + */ +#ifndef __NT99141_H__ +#define __NT99141_H__ + +#include "sensor.h" + +int NT99141_init(sensor_t *sensor); + +#endif // __NT99141_H__ diff --git a/esp32-cam-rtos/nt99141_regs.h b/esp32-cam-rtos/nt99141_regs.h new file mode 100644 index 0000000..8301db9 --- /dev/null +++ b/esp32-cam-rtos/nt99141_regs.h @@ -0,0 +1,211 @@ +/* + * NT99141 register definitions. + */ +#ifndef __NT99141_REG_REGS_H__ +#define __NT99141_REG_REGS_H__ + +/* system control registers */ +#define SYSTEM_CTROL0 0x3021 // 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 0x3025 // 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 0x3201 // 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 + +/* mirror and flip registers */ +#define TIMING_TC_REG20 0x3022 // Timing Control Register + // Bit[2:1]: Vertical flip enable + // 00: Normal + // 11: Vertical flip + // Bit[0]: Vertical binning enable +#define TIMING_TC_REG21 0x3022 // 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 0x3024// 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 0x306a // 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 0x3021 // 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 0x3401 // 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 0x02 /* 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 0x01 /* Vertical flip enable */ +#define TIMING_TC_REG21_HMIRROR 0x02 /* Horizontal mirror enable */ + +#endif // __NT99141_REG_REGS_H__ diff --git a/esp32-cam-rtos/nt99141_settings.h b/esp32-cam-rtos/nt99141_settings.h new file mode 100644 index 0000000..1ffec20 --- /dev/null +++ b/esp32-cam-rtos/nt99141_settings.h @@ -0,0 +1,825 @@ +#ifndef _NT99141_SETTINGS_H_ +#define _NT99141_SETTINGS_H_ + +#include +#include +#include "esp_attr.h" +#include "nt99141_regs.h" + +static const ratio_settings_t ratio_table[] = { + // mw, mh, sx, sy, ex, ey, ox, oy, tx, ty + { 1280, 720, 0, 4, 1283, 723, 0, 4, 1660, 963 }, + +}; + +#define REG_DLY 0xffff +#define REGLIST_TAIL 0x0000 + +static const DRAM_ATTR uint16_t sensor_default_regs[][2] = { + //initial +{0x3021, 0x00}, +{REG_DLY, 100}, // delay 100ms +{0x3109, 0x04}, +{0x3040, 0x04}, +{0x3041, 0x02}, +{0x3042, 0xFF}, +{0x3043, 0x08}, +{0x3052, 0xE0}, +{0x305F, 0x33}, +{0x3100, 0x07}, +{0x3106, 0x03}, +{0x3105, 0x01}, +{0x3108, 0x05}, +{0x3110, 0x22}, +{0x3111, 0x57}, +{0x3112, 0x22}, +{0x3113, 0x55}, +{0x3114, 0x05}, +{0x3135, 0x00}, +{0x32F0, 0x01}, +{0x3290, 0x01}, +{0x3291, 0x80}, +{0x3296, 0x01}, +{0x3297, 0x73}, +{0x3250, 0x80}, +{0x3251, 0x03}, +{0x3252, 0xFF}, +{0x3253, 0x00}, +{0x3254, 0x03}, +{0x3255, 0xFF}, +{0x3256, 0x00}, +{0x3257, 0x50}, +{0x3270, 0x00}, +{0x3271, 0x0C}, +{0x3272, 0x18}, +{0x3273, 0x32}, +{0x3274, 0x44}, +{0x3275, 0x54}, +{0x3276, 0x70}, +{0x3277, 0x88}, +{0x3278, 0x9D}, +{0x3279, 0xB0}, +{0x327A, 0xCF}, +{0x327B, 0xE2}, +{0x327C, 0xEF}, +{0x327D, 0xF7}, +{0x327E, 0xFF}, +{0x3302, 0x00}, +{0x3303, 0x40}, +{0x3304, 0x00}, +{0x3305, 0x96}, +{0x3306, 0x00}, +{0x3307, 0x29}, +{0x3308, 0x07}, +{0x3309, 0xBA}, +{0x330A, 0x06}, +{0x330B, 0xF5}, +{0x330C, 0x01}, +{0x330D, 0x51}, +{0x330E, 0x01}, +{0x330F, 0x30}, +{0x3310, 0x07}, +{0x3311, 0x16}, +{0x3312, 0x07}, +{0x3313, 0xBA}, +{0x3326, 0x02}, +{0x32F6, 0x0F}, +{0x32F9, 0x42}, +{0x32FA, 0x24}, +{0x3325, 0x4A}, +{0x3330, 0x00}, +{0x3331, 0x0A}, +{0x3332, 0xFF}, +{0x3338, 0x30}, +{0x3339, 0x84}, +{0x333A, 0x48}, +{0x333F, 0x07}, +{0x3360, 0x10}, +{0x3361, 0x18}, +{0x3362, 0x1f}, +{0x3363, 0x37}, +{0x3364, 0x80}, +{0x3365, 0x80}, +{0x3366, 0x68}, +{0x3367, 0x60}, +{0x3368, 0x30}, +{0x3369, 0x28}, +{0x336A, 0x20}, +{0x336B, 0x10}, +{0x336C, 0x00}, +{0x336D, 0x20}, +{0x336E, 0x1C}, +{0x336F, 0x18}, +{0x3370, 0x10}, +{0x3371, 0x38}, +{0x3372, 0x3C}, +{0x3373, 0x3F}, +{0x3374, 0x3F}, +{0x338A, 0x34}, +{0x338B, 0x7F}, +{0x338C, 0x10}, +{0x338D, 0x23}, +{0x338E, 0x7F}, +{0x338F, 0x14}, +{0x3375, 0x08}, +{0x3376, 0x0C}, +{0x3377, 0x18}, +{0x3378, 0x20}, +{0x3012, 0x02}, +{0x3013, 0xD0}, +{0x3025, 0x02}, //colorbar +{REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_fmt_jpeg[][2] = { + {0x32F0, 0x70}, // YUV422 + {REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_fmt_raw[][2] = { + {0x32F0, 0x50}, // RAW + {REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_fmt_grayscale[][2] = { + {0x32F1, 0x01}, + {REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_fmt_yuv422[][2] = { + {0x32F0, 0x00}, // YUV422 + {REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_fmt_rgb565[][2] = { + {0x32F0, 0x01}, // RGB + {REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint8_t sensor_saturation_levels[9][1] = { + {0x60},//-4 + {0x68},//-3 + {0x70},//-2 + {0x78},//-1 + {0x80},//0 + {0x88},//+1 + {0x90},//+2 + {0x98},//+3 + {0xA0},//+4 +}; + +static const DRAM_ATTR uint8_t sensor_special_effects[7][4] = { + {0x00, 0x80, 0x80, 0x01},//Normal + {0x03, 0x80, 0x80, 0x01},//Negative + {0x01, 0x80, 0x80, 0x01},//Grayscale + {0x05, 0x2A, 0xF0, 0x01},//Red Tint + {0x05, 0x60, 0x20, 0x01},//Green Tint + {0x05, 0xF0, 0x80, 0x01},//Blue Tint + {0x02, 0x80, 0x80, 0x01},//Sepia + +}; + +// AE LEVEL +static const DRAM_ATTR uint16_t sensor_ae_level[][2] = { + +// 1. [AE_Target : 0x24] +// Set_Device_Format = FORMAT_16_8 +// SET_Device_Addr = 0x54 + {0x32B8, 0x29 }, + {0x32B9, 0x1F }, + {0x32BC, 0x24 }, + {0x32BD, 0x27 }, + {0x32BE, 0x21 }, +//------------------------------------------------------------------------ +// 2. [AE_Target : 0x28] +// Set_Device_Format = FORMAT_16_8 +// SET_Device_Addr = 0x54 + {0x32B8, 0x2D }, + {0x32B9, 0x23 }, + {0x32BC, 0x28 }, + {0x32BD, 0x2B }, + {0x32BE, 0x25 }, +//------------------------------------------------------------------------ +// 3. [AE_Target : 0x2C] +// Set_Device_Format = FORMAT_16_8 +// SET_Device_Addr = 0x54 + {0x32B8, 0x32 }, + {0x32B9, 0x26 }, + {0x32BC, 0x2C }, + {0x32BD, 0x2F }, + {0x32BE, 0x29 }, +//------------------------------------------------------------------------ +// 4, [AE_Target : 0x30] +// Set_Device_Format = FORMAT_16_8 +// SET_Device_Addr = 0x54 + {0x32B8, 0x36 }, + {0x32B9, 0x2A }, + {0x32BC, 0x30 }, + {0x32BD, 0x33 }, + {0x32BE, 0x2D }, +//------------------------------------------------------------------------ +// 5. [AE_Target : 0x34] +// Set_Device_Format = FORMAT_16_8 +// SET_Device_Addr = 0x54 + {0x32B8, 0x3B }, + {0x32B9, 0x2D }, + {0x32BC, 0x34 }, + {0x32BD, 0x38 }, + {0x32BE, 0x30 }, +//------------------------------------------------------------------------ +// 6. [AE_Target : 0x38] +// Set_Device_Format = FORMAT_16_8 +// SET_Device_Addr = 0x54 + {0x32B8, 0x3F }, + {0x32B9, 0x31 }, + {0x32BC, 0x38 }, + {0x32BD, 0x3C }, + {0x32BE, 0x34 }, +//------------------------------------------------------------------------ +// 7. [AE_Target : 0x3D] +// Set_Device_Format = FORMAT_16_8 +// SET_Device_Addr = 0x54 + {0x32B8, 0x44 }, + {0x32B9, 0x34 }, + {0x32BC, 0x3C }, + {0x32BD, 0x40 }, + {0x32BE, 0x38 }, +//------------------------------------------------------------------------ +// 8. [AE_Target : 0x40] +// Set_Device_Format = FORMAT_16_8 +// SET_Device_Addr = 0x54 + {0x32B8, 0x48 }, + {0x32B9, 0x38 }, + {0x32BC, 0x40 }, + {0x32BD, 0x44 }, + {0x32BE, 0x3C }, +//------------------------------------------------------------------------ +// 9. [AE_Target : 0x44] +// Set_Device_Format = FORMAT_16_8 +// SET_Device_Addr = 0x54 + {0x32B8, 0x4D }, + {0x32B9, 0x3B }, + {0x32BC, 0x44 }, + {0x32BD, 0x49 }, + {0x32BE, 0x3F }, +}; + +static const DRAM_ATTR uint16_t sensor_framesize_HD[][2] = { +//[JPEG_1280x720_8.18_8.18_Fps] +{0x3021, 0x00}, +{REG_DLY, 100}, // delay 100ms +{0x32BF, 0x60}, +{0x32C0, 0x5A}, +{0x32C1, 0x5A}, +{0x32C2, 0x5A}, +{0x32C3, 0x00}, +{0x32C4, 0x20}, +{0x32C5, 0x20}, +{0x32C6, 0x20}, +{0x32C7, 0x00}, +{0x32C8, 0x3C}, +{0x32C9, 0x5A}, +{0x32CA, 0x7A}, +{0x32CB, 0x7A}, +{0x32CC, 0x7A}, +{0x32CD, 0x7A}, +{0x32DB, 0x5E}, +{0x32F0, 0x70}, +{0x3400, 0x08}, +{0x3400, 0x00}, +{0x3401, 0x4E}, +{0x3404, 0x00}, +{0x3405, 0x00}, +{0x3410, 0x00}, +{0x3200, 0x3E}, +{0x3201, 0x0F}, +{0x3028, 0x0F}, +{0x3029, 0x00}, +{0x302A, 0x08}, +{0x3022, 0x24}, +{0x3023, 0x24}, +{0x3002, 0x00}, +{0x3003, 0x04}, +{0x3004, 0x00}, +{0x3005, 0x04}, +{0x3006, 0x05}, +{0x3007, 0x03}, +{0x3008, 0x02}, +{0x3009, 0xD3}, +{0x300A, 0x06}, +{0x300B, 0x7C}, +{0x300C, 0x02}, +{0x300D, 0xE0}, +{0x300E, 0x05}, +{0x300F, 0x00}, +{0x3010, 0x02}, +{0x3011, 0xD0}, +{0x32B8, 0x3F}, +{0x32B9, 0x31}, +{0x32BB, 0x87}, +{0x32BC, 0x38}, +{0x32BD, 0x3C}, +{0x32BE, 0x34}, +{0x3201, 0x3F}, +{0x3021, 0x06}, +{0x3025, 0x00}, //normal +{0x3400, 0x01}, +{0x3060, 0x01}, +{REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_framesize_VGA[][2] = { +//[JPEG_640x480_10.14_10.14_Fps] +{0x3021, 0x00}, +{REG_DLY, 100}, // delay 100ms +{0x32BF, 0x60}, +{0x32C0, 0x5A}, +{0x32C1, 0x5A}, +{0x32C2, 0x5A}, +{0x32C3, 0x00}, +{0x32C4, 0x20}, +{0x32C5, 0x20}, +{0x32C6, 0x20}, +{0x32C7, 0x00}, +{0x32C8, 0x4B}, +{0x32C9, 0x5A}, +{0x32CA, 0x7A}, +{0x32CB, 0x7A}, +{0x32CC, 0x7A}, +{0x32CD, 0x7A}, +{0x32DB, 0x62}, +{0x32F0, 0x70}, +{0x3400, 0x08}, +{0x3400, 0x00}, +{0x3401, 0x4E}, +{0x3404, 0x00}, +{0x3405, 0x00}, +{0x3410, 0x00}, +{0x32E0, 0x02}, +{0x32E1, 0x80}, +{0x32E2, 0x01}, +{0x32E3, 0xE0}, +{0x32E4, 0x00}, +{0x32E5, 0x80}, +{0x32E6, 0x00}, +{0x32E7, 0x80}, +{0x3200, 0x3E}, +{0x3201, 0x0F}, +{0x3028, 0x0F}, +{0x3029, 0x00}, +{0x302A, 0x08}, +{0x3022, 0x24}, +{0x3023, 0x24}, +{0x3002, 0x00}, +{0x3003, 0xA4}, +{0x3004, 0x00}, +{0x3005, 0x04}, +{0x3006, 0x04}, +{0x3007, 0x63}, +{0x3008, 0x02}, +{0x3009, 0xD3}, +{0x300A, 0x05}, +{0x300B, 0x3C}, +{0x300C, 0x02}, +{0x300D, 0xE0}, +{0x300E, 0x03}, +{0x300F, 0xC0}, +{0x3010, 0x02}, +{0x3011, 0xD0}, +{0x32B8, 0x3F}, +{0x32B9, 0x31}, +{0x32BB, 0x87}, +{0x32BC, 0x38}, +{0x32BD, 0x3C}, +{0x32BE, 0x34}, +{0x3201, 0x7F}, +{0x3021, 0x06}, +{0x3025, 0x00}, //normal +{0x3400, 0x01}, +{0x3060, 0x01}, +{REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_framesize_QVGA[][2] = { +//[JPEG_320x240_10.14_10.14_Fps] +{0x3021, 0x00}, +{REG_DLY, 100}, // delay 100ms +{0x32BF, 0x60}, +{0x32C0, 0x5A}, +{0x32C1, 0x5A}, +{0x32C2, 0x5A}, +{0x32C3, 0x00}, +{0x32C4, 0x20}, +{0x32C5, 0x20}, +{0x32C6, 0x20}, +{0x32C7, 0x00}, +{0x32C8, 0x4B}, +{0x32C9, 0x5A}, +{0x32CA, 0x7A}, +{0x32CB, 0x7A}, +{0x32CC, 0x7A}, +{0x32CD, 0x7A}, +{0x32DB, 0x62}, +{0x32F0, 0x70}, +{0x3400, 0x08}, +{0x3400, 0x00}, +{0x3401, 0x4E}, +{0x3404, 0x00}, +{0x3405, 0x00}, +{0x3410, 0x00}, +{0x32E0, 0x01}, +{0x32E1, 0x40}, +{0x32E2, 0x00}, +{0x32E3, 0xF0}, +{0x32E4, 0x02}, +{0x32E5, 0x02}, +{0x32E6, 0x02}, +{0x32E7, 0x03}, +{0x3200, 0x3E}, +{0x3201, 0x0F}, +{0x3028, 0x0F}, +{0x3029, 0x00}, +{0x302A, 0x08}, +{0x3022, 0x24}, +{0x3023, 0x24}, +{0x3002, 0x00}, +{0x3003, 0xA4}, +{0x3004, 0x00}, +{0x3005, 0x04}, +{0x3006, 0x04}, +{0x3007, 0x63}, +{0x3008, 0x02}, +{0x3009, 0xD3}, +{0x300A, 0x05}, +{0x300B, 0x3C}, +{0x300C, 0x02}, +{0x300D, 0xE0}, +{0x300E, 0x03}, +{0x300F, 0xC0}, +{0x3010, 0x02}, +{0x3011, 0xD0}, +{0x32B8, 0x3F}, +{0x32B9, 0x31}, +{0x32BB, 0x87}, +{0x32BC, 0x38}, +{0x32BD, 0x3C}, +{0x32BE, 0x34}, +{0x3201, 0x7F}, +{0x3021, 0x06}, +{0x3025, 0x00}, //normal +{0x3400, 0x01}, +{0x3060, 0x01}, +{REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_framesize_VGA_xyskip[][2] = { +// [JPEG_640x360_20.00_25.01_Fps_XY_Skip] +// Set_Device_Format = FORMAT_16_8 +// SET_Device_Addr = 0x54 +{0x3021, 0x00}, +{REG_DLY, 100}, // delay 100ms +{0x32BF, 0x60 }, +{0x320A, 0xB2 }, +{0x32C0, 0x64 }, +{0x32C1, 0x64 }, +{0x32C2, 0x64 }, +{0x32C3, 0x00 }, +{0x32C4, 0x20 }, +{0x32C5, 0x20 }, +{0x32C6, 0x20 }, +{0x32C7, 0x00 }, +{0x32C8, 0x62 }, +{0x32C9, 0x64 }, +{0x32CA, 0x84 }, +{0x32CB, 0x84 }, +{0x32CC, 0x84 }, +{0x32CD, 0x84 }, +{0x32DB, 0x68 }, +{0x32F0, 0x70 }, +{0x3400, 0x08 }, +{0x3400, 0x00 }, +{0x3401, 0x4E }, +{0x3404, 0x00 }, +{0x3405, 0x00 }, +{0x3410, 0x00 }, +{0x3200, 0x3E }, +{0x3201, 0x0F }, +{0x3028, 0x0F }, +{0x3029, 0x00 }, +{0x302A, 0x08 }, +{0x3022, 0x24 }, +{0x3023, 0x6C }, +{0x3002, 0x00 }, +{0x3003, 0x04 }, +{0x3004, 0x00 }, +{0x3005, 0x04 }, +{0x3006, 0x05 }, +{0x3007, 0x03 }, +{0x3008, 0x02 }, +{0x3009, 0xD3 }, +{0x300A, 0x03 }, +{0x300B, 0xFC }, +{0x300C, 0x01 }, +{0x300D, 0x88 }, +{0x300E, 0x02 }, +{0x300F, 0x80 }, +{0x3010, 0x01 }, +{0x3011, 0x68 }, +{0x32B8, 0x3F }, +{0x32B9, 0x31 }, +{0x32BB, 0x87 }, +{0x32BC, 0x38 }, +{0x32BD, 0x3C }, +{0x32BE, 0x34 }, +{0x3201, 0x3F }, +{0x3025, 0x00 }, //normal +{0x3021, 0x06 }, +{0x3400, 0x01 }, +{0x3060, 0x01 }, +{REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_framesize_VGA_xskip[][2] = { +//[JPEG_640x480_Xskip_13.32_13.32_Fps] +{0x3021, 0x00}, +{REG_DLY, 100}, // delay 100ms +{0x32BF, 0x60}, +{0x32C0, 0x5A}, +{0x32C1, 0x5A}, +{0x32C2, 0x5A}, +{0x32C3, 0x00}, +{0x32C4, 0x20}, +{0x32C5, 0x20}, +{0x32C6, 0x20}, +{0x32C7, 0x00}, +{0x32C8, 0x62}, +{0x32C9, 0x5A}, +{0x32CA, 0x7A}, +{0x32CB, 0x7A}, +{0x32CC, 0x7A}, +{0x32CD, 0x7A}, +{0x32DB, 0x68}, +{0x32F0, 0x70}, +{0x3400, 0x08}, +{0x3400, 0x00}, +{0x3401, 0x4E}, +{0x3404, 0x00}, +{0x3405, 0x00}, +{0x3410, 0x00}, +{0x32E0, 0x02}, +{0x32E1, 0x80}, +{0x32E2, 0x01}, +{0x32E3, 0xE0}, +{0x32E4, 0x00}, +{0x32E5, 0x00}, +{0x32E6, 0x00}, +{0x32E7, 0x80}, +{0x3200, 0x3E}, +{0x3201, 0x0F}, +{0x3028, 0x0F}, +{0x3029, 0x00}, +{0x302A, 0x08}, +{0x3022, 0x24}, +{0x3023, 0x2C}, +{0x3002, 0x00}, +{0x3003, 0x04}, +{0x3004, 0x00}, +{0x3005, 0x04}, +{0x3006, 0x05}, +{0x3007, 0x03}, +{0x3008, 0x02}, +{0x3009, 0xD3}, +{0x300A, 0x03}, +{0x300B, 0xFC}, +{0x300C, 0x02}, +{0x300D, 0xE0}, +{0x300E, 0x02}, +{0x300F, 0x80}, +{0x3010, 0x02}, +{0x3011, 0xD0}, +{0x32B8, 0x3F}, +{0x32B9, 0x31}, +{0x32BB, 0x87}, +{0x32BC, 0x38}, +{0x32BD, 0x3C}, +{0x32BE, 0x34}, +{0x3201, 0x7F}, +{0x3021, 0x06}, +{0x3025, 0x00}, //normal +{0x3400, 0x01}, +{0x3060, 0x01}, +{REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_framesize_QVGA_xskip[][2] = { +{0x3021, 0x00}, +{REG_DLY, 100}, // delay 100ms +//[JPEG_320x240_Xskip_13.32_13.32_Fps] +{0x32BF, 0x60}, +{0x32C0, 0x5A}, +{0x32C1, 0x5A}, +{0x32C2, 0x5A}, +{0x32C3, 0x00}, +{0x32C4, 0x20}, +{0x32C5, 0x20}, +{0x32C6, 0x20}, +{0x32C7, 0x00}, +{0x32C8, 0x62}, +{0x32C9, 0x5A}, +{0x32CA, 0x7A}, +{0x32CB, 0x7A}, +{0x32CC, 0x7A}, +{0x32CD, 0x7A}, +{0x32DB, 0x68}, +{0x32F0, 0x70}, +{0x3400, 0x08}, +{0x3400, 0x00}, +{0x3401, 0x4E}, +{0x3404, 0x00}, +{0x3405, 0x00}, +{0x3410, 0x00}, +{0x32E0, 0x01}, +{0x32E1, 0x40}, +{0x32E2, 0x00}, +{0x32E3, 0xF0}, +{0x32E4, 0x01}, +{0x32E5, 0x01}, +{0x32E6, 0x02}, +{0x32E7, 0x03}, +{0x3200, 0x3E}, +{0x3201, 0x0F}, +{0x3028, 0x0F}, +{0x3029, 0x00}, +{0x302A, 0x08}, +{0x3022, 0x24}, +{0x3023, 0x2C}, +{0x3002, 0x00}, +{0x3003, 0x04}, +{0x3004, 0x00}, +{0x3005, 0x04}, +{0x3006, 0x05}, +{0x3007, 0x03}, +{0x3008, 0x02}, +{0x3009, 0xD3}, +{0x300A, 0x03}, +{0x300B, 0xFC}, +{0x300C, 0x02}, +{0x300D, 0xE0}, +{0x300E, 0x02}, +{0x300F, 0x80}, +{0x3010, 0x02}, +{0x3011, 0xD0}, +{0x32B8, 0x3F}, +{0x32B9, 0x31}, +{0x32BB, 0x87}, +{0x32BC, 0x38}, +{0x32BD, 0x3C}, +{0x32BE, 0x34}, +{0x3201, 0x7F}, +{0x3021, 0x06}, +{0x3025, 0x00}, //normal +{0x3400, 0x01}, +{0x3060, 0x01}, +{REGLIST_TAIL, 0x00}, // tail +}; + + +static const DRAM_ATTR uint16_t sensor_framesize_VGA_crop[][2] = { +//[JPEG_640x480_Crop_19.77_19.77_Fps] +{0x3021, 0x00}, +{REG_DLY, 100}, // delay 100ms +{0x32BF, 0x60}, +{0x32C0, 0x5A}, +{0x32C1, 0x5A}, +{0x32C2, 0x5A}, +{0x32C3, 0x00}, +{0x32C4, 0x20}, +{0x32C5, 0x20}, +{0x32C6, 0x20}, +{0x32C7, 0x00}, +{0x32C8, 0x62}, +{0x32C9, 0x5A}, +{0x32CA, 0x7A}, +{0x32CB, 0x7A}, +{0x32CC, 0x7A}, +{0x32CD, 0x7A}, +{0x32DB, 0x68}, +{0x32F0, 0x70}, +{0x3400, 0x08}, +{0x3400, 0x00}, +{0x3401, 0x4E}, +{0x3404, 0x00}, +{0x3405, 0x00}, +{0x3410, 0x00}, +{0x3200, 0x3E}, +{0x3201, 0x0F}, +{0x3028, 0x0F}, +{0x3029, 0x00}, +{0x302A, 0x08}, +{0x3022, 0x24}, +{0x3023, 0x24}, +{0x3002, 0x01}, +{0x3003, 0x44}, +{0x3004, 0x00}, +{0x3005, 0x7C}, +{0x3006, 0x03}, +{0x3007, 0xC3}, +{0x3008, 0x02}, +{0x3009, 0x5B}, +{0x300A, 0x03}, +{0x300B, 0xFC}, +{0x300C, 0x01}, +{0x300D, 0xF0}, +{0x300E, 0x02}, +{0x300F, 0x80}, +{0x3010, 0x01}, +{0x3011, 0xE0}, +{0x32B8, 0x3F}, +{0x32B9, 0x31}, +{0x32BB, 0x87}, +{0x32BC, 0x38}, +{0x32BD, 0x3C}, +{0x32BE, 0x34}, +{0x3201, 0x3F}, +{0x3021, 0x06}, +{0x3025, 0x00}, //normal +{0x3400, 0x01}, +{0x3060, 0x01}, +{REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_framesize_QVGA_crop[][2] = { +//[JPEG_320x240_Crop_19.77_19.77_Fps] +{0x3021, 0x00}, +{REG_DLY, 100}, // delay 100ms +{0x32BF, 0x60}, +{0x32C0, 0x5A}, +{0x32C1, 0x5A}, +{0x32C2, 0x5A}, +{0x32C3, 0x00}, +{0x32C4, 0x20}, +{0x32C5, 0x20}, +{0x32C6, 0x20}, +{0x32C7, 0x00}, +{0x32C8, 0x62}, +{0x32C9, 0x5A}, +{0x32CA, 0x7A}, +{0x32CB, 0x7A}, +{0x32CC, 0x7A}, +{0x32CD, 0x7A}, +{0x32DB, 0x68}, +{0x32F0, 0x70}, +{0x3400, 0x08}, +{0x3400, 0x00}, +{0x3401, 0x4E}, +{0x3404, 0x00}, +{0x3405, 0x00}, +{0x3410, 0x00}, +{0x32E0, 0x01}, +{0x32E1, 0x40}, +{0x32E2, 0x00}, +{0x32E3, 0xF0}, +{0x32E4, 0x01}, +{0x32E5, 0x01}, +{0x32E6, 0x01}, +{0x32E7, 0x02}, +{0x3200, 0x3E}, +{0x3201, 0x0F}, +{0x3028, 0x0F}, +{0x3029, 0x00}, +{0x302A, 0x08}, +{0x3022, 0x24}, +{0x3023, 0x24}, +{0x3002, 0x01}, +{0x3003, 0x44}, +{0x3004, 0x00}, +{0x3005, 0x7C}, +{0x3006, 0x03}, +{0x3007, 0xC3}, +{0x3008, 0x02}, +{0x3009, 0x5B}, +{0x300A, 0x03}, +{0x300B, 0xFC}, +{0x300C, 0x01}, +{0x300D, 0xF0}, +{0x300E, 0x02}, +{0x300F, 0x80}, +{0x3010, 0x01}, +{0x3011, 0xE0}, +{0x32B8, 0x3F}, +{0x32B9, 0x31}, +{0x32BB, 0x87}, +{0x32BC, 0x38}, +{0x32BD, 0x3C}, +{0x32BE, 0x34}, +{0x3201, 0x7F}, +{0x3021, 0x06}, +{0x3025, 0x00}, //normal +{0x3400, 0x01}, +{0x3060, 0x01}, +{REGLIST_TAIL, 0x00}, // tail +}; + +#endif + + diff --git a/esp32-cam-rtos/ov7670.c b/esp32-cam-rtos/ov7670.c new file mode 100644 index 0000000..285fe13 --- /dev/null +++ b/esp32-cam-rtos/ov7670.c @@ -0,0 +1,439 @@ +/* + * This file is part of the OpenMV project. + * author: Juan Schiavoni + * This work is licensed under the MIT license, see the file LICENSE for details. + * + * OV7725 driver. + * + */ +#include +#include +#include +#include "sccb.h" +#include "ov7670.h" +#include "ov7670_regs.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include + +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) +#include "esp32-hal-log.h" +#else +#include "esp_log.h" +static const char* TAG = "ov7760"; +#endif + +static int ov7670_clkrc = 0x01; + +/* + * The default register settings, as obtained from OmniVision. There + * is really no making sense of most of these - lots of "reserved" values + * and such. + * + * These settings give VGA YUYV. + */ +struct regval_list { + uint8_t reg_num; + uint8_t value; +}; + +static struct regval_list ov7670_default_regs[] = { + /* Sensor automatically sets output window when resolution changes. */ + {TSLB, 0x04}, + + /* Frame rate 30 fps at 12 Mhz clock */ + {CLKRC, 0x00}, + {DBLV, 0x4A}, + + {COM10, COM10_VSYNC_NEG | COM10_PCLK_MASK}, + + /* Improve white balance */ + {COM4, 0x40}, + + /* Improve color */ + {RSVD_B0, 0x84}, + + /* Enable 50/60 Hz auto detection */ + {COM11, COM11_EXP|COM11_HZAUTO}, + + /* Disable some delays */ + {HSYST, 0}, + {HSYEN, 0}, + + {MVFP, MVFP_SUN}, + + /* More reserved magic, some of which tweaks white balance */ + {AWBC1, 0x0a}, + {AWBC2, 0xf0}, + {AWBC3, 0x34}, + {AWBC4, 0x58}, + {AWBC5, 0x28}, + {AWBC6, 0x3a}, + + {AWBCTR3, 0x0a}, + {AWBCTR2, 0x55}, + {AWBCTR1, 0x11}, + {AWBCTR0, 0x9e}, + + {COM8, COM8_FAST_AUTO|COM8_STEP_UNLIMIT|COM8_AGC_EN|COM8_AEC_EN|COM8_AWB_EN}, + + /* End marker is FF because in ov7670 the address of GAIN 0 and default value too. */ + {0xFF, 0xFF}, +}; + +static struct regval_list ov7670_fmt_yuv422[] = { + { COM7, 0x0 }, /* Selects YUV mode */ + { RGB444, 0 }, /* No RGB444 please */ + { COM1, 0 }, /* CCIR601 */ + { COM15, COM15_R00FF }, + { MVFP, MVFP_SUN }, + { COM9, 0x6A }, /* 128x gain ceiling; 0x8 is reserved bit */ + { MTX1, 0x80 }, /* "matrix coefficient 1" */ + { MTX2, 0x80 }, /* "matrix coefficient 2" */ + { MTX3, 0 }, /* vb */ + { MTX4, 0x22 }, /* "matrix coefficient 4" */ + { MTX5, 0x5e }, /* "matrix coefficient 5" */ + { MTX6, 0x80 }, /* "matrix coefficient 6" */ + { COM13, COM13_UVSAT }, + { 0xff, 0xff }, /* END MARKER */ +}; + +static struct regval_list ov7670_fmt_rgb565[] = { + { COM7, COM7_FMT_RGB565 }, /* Selects RGB mode */ + { RGB444, 0 }, /* No RGB444 please */ + { COM1, 0x0 }, /* CCIR601 */ + { COM15, COM15_RGB565 |COM15_R00FF }, + { MVFP, MVFP_SUN }, + { COM9, 0x6A }, /* 128x gain ceiling; 0x8 is reserved bit */ + { MTX1, 0xb3 }, /* "matrix coefficient 1" */ + { MTX2, 0xb3 }, /* "matrix coefficient 2" */ + { MTX3, 0 }, /* vb */ + { MTX4, 0x3d }, /* "matrix coefficient 4" */ + { MTX5, 0xa7 }, /* "matrix coefficient 5" */ + { MTX6, 0xe4 }, /* "matrix coefficient 6" */ + { COM13, COM13_UVSAT }, + { 0xff, 0xff }, /* END MARKER */ +}; + + +static struct regval_list ov7670_vga[] = { + { COM3, 0x00 }, + { COM14, 0x00 }, + { SCALING_XSC, 0x3A }, + { SCALING_YSC, 0x35 }, + { SCALING_DCWCTR, 0x11 }, + { SCALING_PCLK_DIV, 0xF0 }, + { SCALING_PCLK_DELAY, 0x02 }, + { 0xff, 0xff }, +}; + +static struct regval_list ov7670_qvga[] = { + { COM3, 0x04 }, + { COM14, 0x19 }, + { SCALING_XSC, 0x3A }, + { SCALING_YSC, 0x35 }, + { SCALING_DCWCTR, 0x11 }, + { SCALING_PCLK_DIV, 0xF1 }, + { SCALING_PCLK_DELAY, 0x02 }, + { 0xff, 0xff }, +}; + +static struct regval_list ov7670_qqvga[] = { + { COM3, 0x04 }, //DCW enable + { COM14, 0x1a }, //pixel clock divided by 4, manual scaling enable, DCW and PCLK controlled by register + { SCALING_XSC, 0x3a }, + { SCALING_YSC, 0x35 }, + { SCALING_DCWCTR, 0x22 }, //downsample by 4 + { SCALING_PCLK_DIV, 0xf2 }, //pixel clock divided by 4 + { SCALING_PCLK_DELAY, 0x02 }, + { 0xff, 0xff }, +}; + +/* + * Write a list of register settings; ff/ff stops the process. + */ +static int ov7670_write_array(sensor_t *sensor, struct regval_list *vals) +{ +int ret = 0; + + while ( (vals->reg_num != 0xff || vals->value != 0xff) && (ret == 0) ) { + ret = SCCB_Write(sensor->slv_addr, vals->reg_num, vals->value); + + ESP_LOGD(TAG, "reset reg %02X, W(%02X) R(%02X)", vals->reg_num, + vals->value, SCCB_Read(sensor->slv_addr, vals->reg_num) ); + + vals++; + } + + return ret; +} + +/* + * Calculate the frame control registers. + */ +static int ov7670_frame_control(sensor_t *sensor, int hstart, int hstop, int vstart, int vstop) +{ +struct regval_list frame[7]; + + frame[0].reg_num = HSTART; + frame[0].value = (hstart >> 3); + + frame[1].reg_num = HSTOP; + frame[1].value = (hstop >> 3); + + frame[2].reg_num = HREF; + frame[2].value = (((hstop & 0x07) << 3) | (hstart & 0x07)); + + frame[3].reg_num = VSTART; + frame[3].value = (vstart >> 2); + + frame[4].reg_num = VSTOP; + frame[4].value = (vstop >> 2); + + frame[5].reg_num = VREF; + frame[5].value = (((vstop & 0x02) << 2) | (vstart & 0x02)); + + /* End mark */ + frame[5].reg_num = 0xFF; + frame[5].value = 0xFF; + + return ov7670_write_array(sensor, frame); +} + +static int reset(sensor_t *sensor) +{ + int ret; + + // Reset all registers + SCCB_Write(sensor->slv_addr, COM7, COM7_RESET); + + // Delay 10 ms + vTaskDelay(10 / portTICK_PERIOD_MS); + + ret = ov7670_write_array(sensor, ov7670_default_regs); + + // Delay + vTaskDelay(30 / portTICK_PERIOD_MS); + + return ret; +} + +static int set_pixformat(sensor_t *sensor, pixformat_t pixformat) +{ +int ret; + + switch (pixformat) { + case PIXFORMAT_RGB565: + case PIXFORMAT_RGB888: + ret = ov7670_write_array(sensor, ov7670_fmt_rgb565); + break; + + case PIXFORMAT_YUV422: + case PIXFORMAT_GRAYSCALE: + default: + ret = ov7670_write_array(sensor, ov7670_fmt_yuv422); + break; + } + + vTaskDelay(30 / portTICK_PERIOD_MS); + + /* + * If we're running RGB565, we must rewrite clkrc after setting + * the other parameters or the image looks poor. If we're *not* + * doing RGB565, we must not rewrite clkrc or the image looks + * *really* poor. + * + * (Update) Now that we retain clkrc state, we should be able + * to write it unconditionally, and that will make the frame + * rate persistent too. + */ + if (pixformat == PIXFORMAT_RGB565) { + ret = SCCB_Write(sensor->slv_addr, CLKRC, ov7670_clkrc); + } + + return ret; +} + +static int set_framesize(sensor_t *sensor, framesize_t framesize) +{ + int ret; + + // store clkrc before changing window settings... + ov7670_clkrc = SCCB_Read(sensor->slv_addr, CLKRC); + + switch (framesize){ + case FRAMESIZE_VGA: + if( (ret = ov7670_write_array(sensor, ov7670_vga)) == 0 ) { + /* These values from Omnivision */ + ret = ov7670_frame_control(sensor, 158, 14, 10, 490); + } + break; + case FRAMESIZE_QVGA: + if( (ret = ov7670_write_array(sensor, ov7670_qvga)) == 0 ) { + /* These values from Omnivision */ + ret = ov7670_frame_control(sensor, 158, 14, 10, 490); + } + break; + case FRAMESIZE_QQVGA: + if( (ret = ov7670_write_array(sensor, ov7670_qqvga)) == 0 ) { + /* These values from Omnivision */ + ret = ov7670_frame_control(sensor, 158, 14, 10, 490); + } + break; + + default: + ret = -1; + } + + vTaskDelay(30 / portTICK_PERIOD_MS); + + if (ret == 0) { + sensor->status.framesize = framesize; + } + + return ret; +} + +static int set_colorbar(sensor_t *sensor, int enable) +{ + uint8_t ret = 0; + // Read register scaling_xsc + uint8_t reg = SCCB_Read(sensor->slv_addr, SCALING_XSC); + + // Pattern to set color bar bit[0]=0 in every case + reg = SCALING_XSC_CBAR(reg); + + // Write pattern to SCALING_XSC + ret = SCCB_Write(sensor->slv_addr, SCALING_XSC, reg); + + // Read register scaling_ysc + reg = SCCB_Read(sensor->slv_addr, SCALING_YSC); + + // Pattern to set color bar bit[0]=0 in every case + reg = SCALING_YSC_CBAR(reg, enable); + + // Write pattern to SCALING_YSC + ret = ret | SCCB_Write(sensor->slv_addr, SCALING_YSC, reg); + + // return 0 or 0xFF + return ret; +} + +static int set_whitebal(sensor_t *sensor, int enable) +{ + // Read register COM8 + uint8_t reg = SCCB_Read(sensor->slv_addr, COM8); + + // Set white bal on/off + reg = COM8_SET_AWB(reg, enable); + + // Write back register COM8 + return SCCB_Write(sensor->slv_addr, COM8, reg); +} + +static int set_gain_ctrl(sensor_t *sensor, int enable) +{ + // Read register COM8 + uint8_t reg = SCCB_Read(sensor->slv_addr, COM8); + + // Set white bal on/off + reg = COM8_SET_AGC(reg, enable); + + // Write back register COM8 + return SCCB_Write(sensor->slv_addr, COM8, reg); +} + +static int set_exposure_ctrl(sensor_t *sensor, int enable) +{ + // Read register COM8 + uint8_t reg = SCCB_Read(sensor->slv_addr, COM8); + + // Set white bal on/off + reg = COM8_SET_AEC(reg, enable); + + // Write back register COM8 + return SCCB_Write(sensor->slv_addr, COM8, reg); +} + +static int set_hmirror(sensor_t *sensor, int enable) +{ + // Read register MVFP + uint8_t reg = SCCB_Read(sensor->slv_addr, MVFP); + + // Set mirror on/off + reg = MVFP_SET_MIRROR(reg, enable); + + // Write back register MVFP + return SCCB_Write(sensor->slv_addr, MVFP, reg); +} + +static int set_vflip(sensor_t *sensor, int enable) +{ + // Read register MVFP + uint8_t reg = SCCB_Read(sensor->slv_addr, MVFP); + + // Set mirror on/off + reg = MVFP_SET_FLIP(reg, enable); + + // Write back register MVFP + return SCCB_Write(sensor->slv_addr, MVFP, reg); +} + +static int init_status(sensor_t *sensor) +{ + sensor->status.awb = 0; + sensor->status.aec = 0; + sensor->status.agc = 0; + sensor->status.hmirror = 0; + sensor->status.vflip = 0; + sensor->status.colorbar = 0; + return 0; +} + +static int set_dummy(sensor_t *sensor, int val){ return -1; } +static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val){ return -1; } + +int ov7670_init(sensor_t *sensor) +{ + // Set function pointers + sensor->reset = reset; + sensor->init_status = init_status; + sensor->set_pixformat = set_pixformat; + sensor->set_framesize = set_framesize; + sensor->set_colorbar = set_colorbar; + sensor->set_whitebal = set_whitebal; + sensor->set_gain_ctrl = set_gain_ctrl; + sensor->set_exposure_ctrl = set_exposure_ctrl; + sensor->set_hmirror = set_hmirror; + sensor->set_vflip = set_vflip; + + //not supported + sensor->set_brightness= set_dummy; + sensor->set_saturation= set_dummy; + sensor->set_quality = set_dummy; + sensor->set_gainceiling = set_gainceiling_dummy; + sensor->set_aec2 = set_dummy; + sensor->set_aec_value = set_dummy; + sensor->set_special_effect = set_dummy; + sensor->set_wb_mode = set_dummy; + sensor->set_ae_level = set_dummy; + sensor->set_dcw = set_dummy; + sensor->set_bpc = set_dummy; + sensor->set_wpc = set_dummy; + sensor->set_awb_gain = set_dummy; + sensor->set_agc_gain = set_dummy; + sensor->set_raw_gma = set_dummy; + sensor->set_lenc = set_dummy; + sensor->set_sharpness = set_dummy; + sensor->set_denoise = set_dummy; + + // Retrieve sensor's signature + sensor->id.MIDH = SCCB_Read(sensor->slv_addr, REG_MIDH); + sensor->id.MIDL = SCCB_Read(sensor->slv_addr, REG_MIDL); + sensor->id.PID = SCCB_Read(sensor->slv_addr, REG_PID); + sensor->id.VER = SCCB_Read(sensor->slv_addr, REG_VER); + + ESP_LOGD(TAG, "OV7670 Attached"); + + return 0; +} diff --git a/esp32-cam-rtos/ov7670.h b/esp32-cam-rtos/ov7670.h new file mode 100644 index 0000000..cdf845c --- /dev/null +++ b/esp32-cam-rtos/ov7670.h @@ -0,0 +1,14 @@ +/* + * This file is part of the OpenMV project. + * author: Juan Schiavoni + * This work is licensed under the MIT license, see the file LICENSE for details. + * + * OV7670 driver. + * + */ +#ifndef __OV7670_H__ +#define __OV7670_H__ +#include "sensor.h" + +int ov7670_init(sensor_t *sensor); +#endif // __OV7670_H__ diff --git a/esp32-cam-rtos/ov7670_regs.h b/esp32-cam-rtos/ov7670_regs.h new file mode 100644 index 0000000..6993548 --- /dev/null +++ b/esp32-cam-rtos/ov7670_regs.h @@ -0,0 +1,354 @@ +/* + * This file is for the OpenMV project so the OV7670 can be used + * author: Juan Schiavoni + * + * OV7670 register definitions. + */ +#ifndef __OV7670_REG_REGS_H__ +#define __OV7670_REG_REGS_H__ +#define GAIN 0x00 /* AGC – Gain control gain setting */ +#define BLUE 0x01 /* AWB – Blue channel gain setting */ +#define RED 0x02 /* AWB – Red channel gain setting */ +#define VREF 0x03 /* AWB – Green channel gain setting */ +#define COM1 0x04 /* Common Control 1 */ +#define BAVG 0x05 /* U/B Average Level */ +#define GAVG 0x06 /* Y/Gb Average Level */ +#define AECH 0x07 /* Exposure VAlue - AEC MSB 5 bits */ +#define RAVG 0x08 /* V/R Average Level */ + +#define COM2 0x09 /* Common Control 2 */ +#define COM2_SOFT_SLEEP 0x10 /* Soft sleep mode */ +#define COM2_OUT_DRIVE_1x 0x00 /* Output drive capability 1x */ +#define COM2_OUT_DRIVE_2x 0x01 /* Output drive capability 2x */ +#define COM2_OUT_DRIVE_3x 0x02 /* Output drive capability 3x */ +#define COM2_OUT_DRIVE_4x 0x03 /* Output drive capability 4x */ + +#define REG_PID 0x0A /* Product ID Number MSB */ +#define REG_VER 0x0B /* Product ID Number LSB */ + +#define COM3 0x0C /* Common Control 3 */ +#define COM3_SWAP_OUT 0x40 /* Output data MSB/LSB swap */ +#define COM3_TRI_CLK 0x20 /* Tri-state output clock */ +#define COM3_TRI_DATA 0x10 /* Tri-state option output */ +#define COM3_SCALE_EN 0x08 /* Scale enable */ +#define COM3_DCW 0x04 /* DCW enable */ + +#define COM4 0x0D /* Common Control 4 */ +#define COM4_PLL_BYPASS 0x00 /* Bypass PLL */ +#define COM4_PLL_4x 0x40 /* PLL frequency 4x */ +#define COM4_PLL_6x 0x80 /* PLL frequency 6x */ +#define COM4_PLL_8x 0xc0 /* PLL frequency 8x */ +#define COM4_AEC_FULL 0x00 /* AEC evaluate full window */ +#define COM4_AEC_1_2 0x10 /* AEC evaluate 1/2 window */ +#define COM4_AEC_1_4 0x20 /* AEC evaluate 1/4 window */ +#define COM4_AEC_2_3 0x30 /* AEC evaluate 2/3 window */ + +#define COM5 0x0E /* Common Control 5 */ +#define COM5_AFR 0x80 /* Auto frame rate control ON/OFF selection (night mode) */ +#define COM5_AFR_SPEED 0x40 /* Auto frame rate control speed selection */ +#define COM5_AFR_0 0x00 /* No reduction of frame rate */ +#define COM5_AFR_1_2 0x10 /* Max reduction to 1/2 frame rate */ +#define COM5_AFR_1_4 0x20 /* Max reduction to 1/4 frame rate */ +#define COM5_AFR_1_8 0x30 /* Max reduction to 1/8 frame rate */ +#define COM5_AFR_4x 0x04 /* Add frame when AGC reaches 4x gain */ +#define COM5_AFR_8x 0x08 /* Add frame when AGC reaches 8x gain */ +#define COM5_AFR_16x 0x0c /* Add frame when AGC reaches 16x gain */ +#define COM5_AEC_NO_LIMIT 0x01 /* No limit to AEC increase step */ + +#define COM6 0x0F /* Common Control 6 */ +#define COM6_AUTO_WINDOW 0x01 /* Auto window setting ON/OFF selection when format changes */ + +#define AEC 0x10 /* AEC[7:0] (see register AECH for AEC[15:8]) */ +#define CLKRC 0x11 /* Internal Clock */ + +#define COM7 0x12 /* Common Control 7 */ +#define COM7_RESET 0x80 /* SCCB Register Reset */ +#define COM7_RES_VGA 0x00 /* Resolution VGA */ +#define COM7_RES_QVGA 0x40 /* Resolution QVGA */ +#define COM7_BT656 0x20 /* BT.656 protocol ON/OFF */ +#define COM7_SENSOR_RAW 0x10 /* Sensor RAW */ +#define COM7_FMT_GBR422 0x00 /* RGB output format GBR422 */ +#define COM7_FMT_RGB565 0x04 /* RGB output format RGB565 */ +#define COM7_FMT_RGB555 0x08 /* RGB output format RGB555 */ +#define COM7_FMT_RGB444 0x0C /* RGB output format RGB444 */ +#define COM7_FMT_YUV 0x00 /* Output format YUV */ +#define COM7_FMT_P_BAYER 0x01 /* Output format Processed Bayer RAW */ +#define COM7_FMT_RGB 0x04 /* Output format RGB */ +#define COM7_FMT_R_BAYER 0x03 /* Output format Bayer RAW */ +#define COM7_SET_FMT(r, x) ((r&0xFC)|((x&0x5)<<0)) + +#define COM8 0x13 /* Common Control 8 */ +#define COM8_FAST_AUTO 0x80 /* Enable fast AGC/AEC algorithm */ +#define COM8_STEP_VSYNC 0x00 /* AEC - Step size limited to vertical blank */ +#define COM8_STEP_UNLIMIT 0x40 /* AEC - Step size unlimited step size */ +#define COM8_BANDF_EN 0x20 /* Banding filter ON/OFF */ +#define COM8_AEC_BANDF 0x10 /* Enable AEC below banding value */ +#define COM8_AEC_FINE_EN 0x08 /* Fine AEC ON/OFF control */ +#define COM8_AGC_EN 0x04 /* AGC Enable */ +#define COM8_AWB_EN 0x02 /* AWB Enable */ +#define COM8_AEC_EN 0x01 /* AEC Enable */ +#define COM8_SET_AGC(r, x) ((r&0xFB)|((x&0x1)<<2)) +#define COM8_SET_AWB(r, x) ((r&0xFD)|((x&0x1)<<1)) +#define COM8_SET_AEC(r, x) ((r&0xFE)|((x&0x1)<<0)) + +#define COM9 0x14 /* Common Control 9 */ +#define COM9_HISTO_AVG 0x80 /* Histogram or average based AEC/AGC selection */ +#define COM9_AGC_GAIN_2x 0x00 /* Automatic Gain Ceiling 2x */ +#define COM9_AGC_GAIN_4x 0x10 /* Automatic Gain Ceiling 4x */ +#define COM9_AGC_GAIN_8x 0x20 /* Automatic Gain Ceiling 8x */ +#define COM9_AGC_GAIN_16x 0x30 /* Automatic Gain Ceiling 16x */ +#define COM9_AGC_GAIN_32x 0x40 /* Automatic Gain Ceiling 32x */ +#define COM9_DROP_VSYNC 0x04 /* Drop VSYNC output of corrupt frame */ +#define COM9_DROP_HREF 0x02 /* Drop HREF output of corrupt frame */ +#define COM9_SET_AGC(r, x) ((r&0x8F)|((x&0x07)<<4)) + +#define COM10 0x15 /* Common Control 10 */ +#define COM10_NEGATIVE 0x80 /* Output negative data */ +#define COM10_HSYNC_EN 0x40 /* HREF changes to HSYNC */ +#define COM10_PCLK_FREE 0x00 /* PCLK output option: free running PCLK */ +#define COM10_PCLK_MASK 0x20 /* PCLK output option: masked during horizontal blank */ +#define COM10_PCLK_REV 0x10 /* PCLK reverse */ +#define COM10_HREF_REV 0x08 /* HREF reverse */ +#define COM10_VSYNC_FALLING 0x00 /* VSYNC changes on falling edge of PCLK */ +#define COM10_VSYNC_RISING 0x04 /* VSYNC changes on rising edge of PCLK */ +#define COM10_VSYNC_NEG 0x02 /* VSYNC negative */ +#define COM10_OUT_RANGE_8 0x01 /* Output data range: Full range */ +#define COM10_OUT_RANGE_10 0x00 /* Output data range: Data from [10] to [F0] (8 MSBs) */ + +#define RSVD_16 0x16 /* Reserved register */ + +#define HSTART 0x17 /* Horizontal Frame (HREF column) Start high 8-bit(low 3 bits are at HREF[2:0]) */ +#define HSTOP 0x18 /* Horizontal Frame (HREF column) end high 8-bit (low 3 bits are at HREF[5:3]) */ +#define VSTART 0x19 /* Vertical Frame (row) Start high 8-bit (low 2 bits are at VREF[1:0]) */ +#define VSTOP 0x1A /* Vertical Frame (row) End high 8-bit (low 2 bits are at VREF[3:2]) */ +#define PSHFT 0x1B /* Data Format - Pixel Delay Select */ +#define REG_MIDH 0x1C /* Manufacturer ID Byte – High */ +#define REG_MIDL 0x1D /* Manufacturer ID Byte – Low */ + +#define MVFP 0x1E /* Mirror/Vflip Enable */ +#define MVFP_MIRROR 0x20 /* Mirror image */ +#define MVFP_FLIP 0x10 /* Vertical flip */ +#define MVFP_SUN 0x02 /* Black sun enable */ +#define MVFP_SET_MIRROR(r,x) ((r&0xDF)|((x&1)<<5)) /* change only bit5 according to x */ +#define MVFP_SET_FLIP(r,x) ((r&0xEF)|((x&1)<<4)) /* change only bit4 according to x */ + +#define LAEC 0x1F /* Fine AEC Value - defines exposure value less than one row period (Reserved?) */ +#define ADCCTR0 0x20 /* ADC control */ +#define ADCCTR1 0x21 /* reserved */ +#define ADCCTR2 0x22 /* reserved */ +#define ADCCTR3 0x23 /* reserved */ +#define AEW 0x24 /* AGC/AEC - Stable Operating Region (Upper Limit) */ +#define AEB 0x25 /* AGC/AEC - Stable Operating Region (Lower Limit) */ +#define VPT 0x26 /* AGC/AEC Fast Mode Operating Region */ +#define BBIAS 0x27 /* B channel signal output bias (effective only when COM6[3]=1) */ +#define GbBIAS 0x28 /* Gb channel signal output bias (effective only when COM6[3]=1) */ +#define RSVD_29 0x29 /* reserved */ +#define EXHCH 0x2A /* Dummy Pixel Insert MSB */ +#define EXHCL 0x2B /* Dummy Pixel Insert LSB */ +#define RBIAS 0x2C /* R channel signal output bias (effective only when COM6[3]=1) */ +#define ADVFL 0x2D /* LSB of Insert Dummy Rows in Vertical Sync (1 bit equals 1 row) */ +#define ADVFH 0x2E /* MSB of Insert Dummy Rows in Vertical Sync */ +#define YAVE 0x2F /* Y/G Channel Average Value */ +#define HSYST 0x30 /* HSync rising edge delay */ +#define HSYEN 0x31 /* HSync falling edge delay */ +#define HREF 0x32 /* Image Start and Size Control DIFFERENT CONTROL SEQUENCE */ +#define CHLF 0x33 /* Array Current control */ +#define ARBLM 0x34 /* Array reference control */ +#define RSVD_35 0x35 /* Reserved */ +#define RSVD_36 0x36 /* Reserved */ +#define ADC 0x37 /* ADC control */ +#define ACOM 0x38 /* ADC and analog common mode control */ +#define OFON 0x39 /* ADC offset control */ +#define TSLB 0x3A /* Line buffer test option */ + +#define COM11 0x3B /* Common control 11 */ +#define COM11_EXP 0x02 +#define COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */ + +#define COM12 0x3C /* Common control 12 */ + +#define COM13 0x3D /* Common control 13 */ +#define COM13_GAMMA 0x80 /* Gamma enable */ +#define COM13_UVSAT 0x40 /* UV saturation auto adjustment */ + +#define COM14 0x3E /* Common Control 14 */ + +#define EDGE 0x3F /* edge enhancement adjustment */ +#define COM15 0x40 /* Common Control 15 DIFFERENT CONTROLS */ +#define COM15_SET_RGB565(r,x) ((r&0xEF)|((x&1)<<4)) /* set rgb565 mode */ +#define COM15_RGB565 0x10 /* RGB565 output */ +#define COM15_R00FF 0xC0 /* Output range: [00] to [FF] */ + +#define COM16 0x41 /* Common Control 16 DIFFERENT CONTROLS */ +#define COM16_AWBGAIN 0x08 /* AWB gain enable */ +#define COM17 0x42 /* Common Control 17 */ + +#define AWBC1 0x43 /* Reserved */ +#define AWBC2 0x44 /* Reserved */ +#define AWBC3 0x45 /* Reserved */ +#define AWBC4 0x46 /* Reserved */ +#define AWBC5 0x47 /* Reserved */ +#define AWBC6 0x48 /* Reserved */ + +#define RSVD_49 0x49 /* Reserved */ +#define RSVD_4A 0x4A /* Reserved */ + +#define REG4B 0x4B /* Register 4B */ +#define DNSTH 0x4C /* Denoise strength */ + +#define RSVD_4D 0x4D /* Reserved */ +#define RSVD_4E 0x4E /* Reserved */ + +#define MTX1 0x4F /* Matrix coefficient 1 */ +#define MTX2 0x50 /* Matrix coefficient 2 */ +#define MTX3 0x51 /* Matrix coefficient 3 */ +#define MTX4 0x52 /* Matrix coefficient 4 */ +#define MTX5 0x53 /* Matrix coefficient 5 */ +#define MTX6 0x54 /* Matrix coefficient 6 */ +#define BRIGHTNESS 0x55 /* Brightness control */ +#define CONTRAST 0x56 /* Contrast control */ +#define CONTRASCENTER 0x57 /* Contrast center */ +#define MTXS 0x58 /* Matrix coefficient sign for coefficient 5 to 0*/ + +#define RSVD_59 0x59 /* Reserved */ +#define RSVD_5A 0x5A /* Reserved */ +#define RSVD_5B 0x5B /* Reserved */ +#define RSVD_5C 0x5C /* Reserved */ +#define RSVD_5D 0x5D /* Reserved */ +#define RSVD_5E 0x5E /* Reserved */ +#define RSVD_5F 0x5F /* Reserved */ +#define RSVD_60 0x60 /* Reserved */ +#define RSVD_61 0x61 /* Reserved */ + +#define LCC1 0x62 /* Lens correction option 1 */ + +#define LCC2 0x63 /* Lens correction option 2 */ +#define LCC3 0x64 /* Lens correction option 3 */ +#define LCC4 0x65 /* Lens correction option 4 */ +#define LCC5 0x66 /* Lens correction option 5 */ + +#define MANU 0x67 /* Manual U Value */ +#define MANV 0x68 /* Manual V Value */ +#define GFIX 0x69 /* Fix gain control */ +#define GGAIN 0x6A /* G channel AWB gain */ + +#define DBLV 0x6B /* PLL and clock ? */ + +#define AWBCTR3 0x6C /* AWB Control 3 */ +#define AWBCTR2 0x6D /* AWB Control 2 */ +#define AWBCTR1 0x6E /* AWB Control 1 */ +#define AWBCTR0 0x6F /* AWB Control 0 */ +#define SCALING_XSC 0x70 /* test pattern and horizontal scaling factor */ +#define SCALING_XSC_CBAR(r) (r&0x7F) /* make sure bit7 is 0 for color bar */ +#define SCALING_YSC 0x71 /* test pattern and vertical scaling factor */ +#define SCALING_YSC_CBAR(r,x) ((r&0x7F)|((x&1)<<7)) /* change bit7 for color bar on/off */ +#define SCALING_DCWCTR 0x72 /* DCW control */ +#define SCALING_PCLK_DIV 0x73 /* */ +#define REG74 0x74 /* */ +#define REG75 0x75 /* */ +#define REG76 0x76 /* */ +#define REG77 0x77 /* */ + +#define RSVD_78 0x78 /* Reserved */ +#define RSVD_79 0x79 /* Reserved */ + +#define SLOP 0x7A /* Gamma curve highest segment slope */ +#define GAM1 0x7B /* Gamma Curve 1st Segment Input End Point 0x04 Output Value */ +#define GAM2 0x7C /* Gamma Curve 2nd Segment Input End Point 0x08 Output Value */ +#define GAM3 0x7D /* Gamma Curve 3rd Segment Input End Point 0x10 Output Value */ +#define GAM4 0x7E /* Gamma Curve 4th Segment Input End Point 0x20 Output Value */ +#define GAM5 0x7F /* Gamma Curve 5th Segment Input End Point 0x28 Output Value */ +#define GAM6 0x80 /* Gamma Curve 6rd Segment Input End Point 0x30 Output Value */ +#define GAM7 0x81 /* Gamma Curve 7th Segment Input End Point 0x38 Output Value */ +#define GAM8 0x82 /* Gamma Curve 8th Segment Input End Point 0x40 Output Value */ +#define GAM9 0x83 /* Gamma Curve 9th Segment Input End Point 0x48 Output Value */ +#define GAM10 0x84 /* Gamma Curve 10th Segment Input End Point 0x50 Output Value */ +#define GAM11 0x85 /* Gamma Curve 11th Segment Input End Point 0x60 Output Value */ +#define GAM12 0x86 /* Gamma Curve 12th Segment Input End Point 0x70 Output Value */ +#define GAM13 0x87 /* Gamma Curve 13th Segment Input End Point 0x90 Output Value */ +#define GAM14 0x88 /* Gamma Curve 14th Segment Input End Point 0xB0 Output Value */ +#define GAM15 0x89 /* Gamma Curve 15th Segment Input End Point 0xD0 Output Value */ + +#define RSVD_8A 0x8A /* Reserved */ +#define RSVD_8B 0x8B /* Reserved */ + +#define RGB444 0x8C /* */ + +#define RSVD_8D 0x8D /* Reserved */ +#define RSVD_8E 0x8E /* Reserved */ +#define RSVD_8F 0x8F /* Reserved */ +#define RSVD_90 0x90 /* Reserved */ +#define RSVD_91 0x91 /* Reserved */ + +#define DM_LNL 0x92 /* Dummy line low 8 bit */ +#define DM_LNH 0x93 /* Dummy line high 8 bit */ +#define LCC6 0x94 /* Lens correction option 6 */ +#define LCC7 0x95 /* Lens correction option 7 */ + +#define RSVD_96 0x96 /* Reserved */ +#define RSVD_97 0x97 /* Reserved */ +#define RSVD_98 0x98 /* Reserved */ +#define RSVD_99 0x99 /* Reserved */ +#define RSVD_9A 0x9A /* Reserved */ +#define RSVD_9B 0x9B /* Reserved */ +#define RSVD_9C 0x9C /* Reserved */ + +#define BD50ST 0x9D /* 50 Hz banding filter value */ +#define BD60ST 0x9E /* 60 Hz banding filter value */ +#define HAECC1 0x9F /* Histogram-based AEC/AGC control 1 */ +#define HAECC2 0xA0 /* Histogram-based AEC/AGC control 2 */ + +#define RSVD_A1 0xA1 /* Reserved */ + +#define SCALING_PCLK_DELAY 0xA2 /* Pixel clock delay */ + +#define RSVD_A3 0xA3 /* Reserved */ + +#define NT_CNTRL 0xA4 /* */ +#define BD50MAX 0xA5 /* 50 Hz banding step limit */ +#define HAECC3 0xA6 /* Histogram-based AEC/AGC control 3 */ +#define HAECC4 0xA7 /* Histogram-based AEC/AGC control 4 */ +#define HAECC5 0xA8 /* Histogram-based AEC/AGC control 5 */ +#define HAECC6 0xA9 /* Histogram-based AEC/AGC control 6 */ + +#define HAECC7 0xAA /* Histogram-based AEC/AGC control 7 */ +#define HAECC_EN 0x80 /* Histogram-based AEC algorithm enable */ + +#define BD60MAX 0xAB /* 60 Hz banding step limit */ + +#define STR_OPT 0xAC /* Register AC */ +#define STR_R 0xAD /* R gain for led output frame */ +#define STR_G 0xAE /* G gain for led output frame */ +#define STR_B 0xAF /* B gain for led output frame */ +#define RSVD_B0 0xB0 /* Reserved */ +#define ABLC1 0xB1 /* */ +#define RSVD_B2 0xB2 /* Reserved */ +#define THL_ST 0xB3 /* ABLC target */ +#define THL_DLT 0xB5 /* ABLC stable range */ + +#define RSVD_B6 0xB6 /* Reserved */ +#define RSVD_B7 0xB7 /* Reserved */ +#define RSVD_B8 0xB8 /* Reserved */ +#define RSVD_B9 0xB9 /* Reserved */ +#define RSVD_BA 0xBA /* Reserved */ +#define RSVD_BB 0xBB /* Reserved */ +#define RSVD_BC 0xBC /* Reserved */ +#define RSVD_BD 0xBD /* Reserved */ + +#define AD_CHB 0xBE /* blue channel black level compensation */ +#define AD_CHR 0xBF /* Red channel black level compensation */ +#define AD_CHGb 0xC0 /* Gb channel black level compensation */ +#define AD_CHGr 0xC1 /* Gr channel black level compensation */ + +#define RSVD_C2 0xC2 /* Reserved */ +#define RSVD_C3 0xC3 /* Reserved */ +#define RSVD_C4 0xC4 /* Reserved */ +#define RSVD_C5 0xC5 /* Reserved */ +#define RSVD_C6 0xC6 /* Reserved */ +#define RSVD_C7 0xC7 /* Reserved */ +#define RSVD_C8 0xC8 /* Reserved */ + +#define SATCTR 0xC9 /* Saturation control */ +#define SET_REG(reg, x) (##reg_DEFAULT|x) + +#endif //__OV7670_REG_REGS_H__ diff --git a/esp32-cam-rtos/sccb.c b/esp32-cam-rtos/sccb.c index d2f5fb9..cb615bb 100644 --- a/esp32-cam-rtos/sccb.c +++ b/esp32-cam-rtos/sccb.c @@ -7,6 +7,7 @@ * */ #include +#include #include #include #include "sccb.h" @@ -19,11 +20,8 @@ static const char* TAG = "sccb"; #endif -//#undef CONFIG_SCCB_HARDWARE_I2C - #define LITTLETOBIG(x) ((x<<8)|(x>>8)) -#ifdef CONFIG_SCCB_HARDWARE_I2C #include "driver/i2c.h" #define SCCB_FREQ 100000 /*!< I2C master frequency*/ @@ -39,16 +37,13 @@ const int SCCB_I2C_PORT = 1; const int SCCB_I2C_PORT = 0; #endif static uint8_t ESP_SLAVE_ADDR = 0x3c; -#else -#include "twi.h" -#endif 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; + memset(&conf, 0, sizeof(i2c_config_t)); conf.mode = I2C_MODE_MASTER; conf.sda_io_num = pin_sda; conf.sda_pullup_en = GPIO_PULLUP_ENABLE; @@ -58,15 +53,11 @@ int SCCB_Init(int pin_sda, int pin_scl) 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); -#endif return 0; } 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(); @@ -82,28 +73,10 @@ uint8_t SCCB_Probe() slave_addr++; } return ESP_SLAVE_ADDR; -#else - uint8_t reg = 0x00; - uint8_t slv_addr = 0x00; - - ESP_LOGI(TAG, "SCCB_Probe start"); - for (uint8_t i = 0; i < 127; i++) { - if (twi_writeTo(i, ®, 1, true) == 0) { - slv_addr = i; - break; - } - - if (i!=126) { - vTaskDelay(10 / portTICK_PERIOD_MS); // Necessary for OV7725 camera (not for OV2640). - } - } - return slv_addr; -#endif } 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(); @@ -125,28 +98,10 @@ uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg) ESP_LOGE(TAG, "SCCB_Read Failed addr:0x%02x, reg:0x%02x, data:0x%02x, ret:%d", slv_addr, reg, data, ret); } return data; -#else - uint8_t data=0; - - int rc = twi_writeTo(slv_addr, ®, 1, 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, "SCCB_Read [%02x] failed rc=%d\n", reg, rc); - } - return data; -#endif } 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); @@ -160,23 +115,10 @@ uint8_t SCCB_Write(uint8_t slv_addr, uint8_t reg, uint8_t data) ESP_LOGE(TAG, "SCCB_Write Failed addr:0x%02x, reg:0x%02x, data:0x%02x, ret:%d", slv_addr, reg, data, ret); } return ret == ESP_OK ? 0 : -1; -#else - uint8_t ret=0; - uint8_t buf[] = {reg, data}; - - if(twi_writeTo(slv_addr, buf, 2, true) != 0) { - ret=0xFF; - } - if (ret != 0) { - ESP_LOGE(TAG, "SCCB_Write [%02x]=%02x failed\n", reg, data); - } - 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); @@ -201,32 +143,11 @@ uint8_t SCCB_Read16(uint8_t slv_addr, uint16_t reg) 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 *)®_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 *)®_htons; @@ -243,18 +164,4 @@ uint8_t SCCB_Write16(uint8_t slv_addr, uint16_t reg, uint8_t data) 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 *)®_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 } diff --git a/esp32-cam-rtos/sensor.h b/esp32-cam-rtos/sensor.h index 3ea7e2c..ad6cd89 100644 --- a/esp32-cam-rtos/sensor.h +++ b/esp32-cam-rtos/sensor.h @@ -11,11 +11,13 @@ #include #include +#define NT99141_PID (0x14) #define OV9650_PID (0x96) #define OV7725_PID (0x77) #define OV2640_PID (0x26) #define OV3660_PID (0x36) #define OV5640_PID (0x56) +#define OV7670_PID (0x76) typedef enum { PIXFORMAT_RGB565, // 2BPP/RGB565 diff --git a/esp32-cam-rtos/to_bmp.c b/esp32-cam-rtos/to_bmp.c index 59455de..85f9c88 100644 --- a/esp32-cam-rtos/to_bmp.c +++ b/esp32-cam-rtos/to_bmp.c @@ -20,6 +20,17 @@ #include "sdkconfig.h" #include "esp_jpg_decode.h" +#include "esp_system.h" +#if ESP_IDF_VERSION_MAJOR >= 4 // IDF 4+ +#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4 +#include "esp32/spiram.h" +#else +#error Target CONFIG_IDF_TARGET is not supported +#endif +#else // ESP32 Before IDF 4.0 +#include "esp_spiram.h" +#endif + #if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) #include "esp32-hal-log.h" #define TAG "" diff --git a/esp32-cam-rtos/twi.h b/esp32-cam-rtos/twi.h index 60624f8..71f9907 100644 --- a/esp32-cam-rtos/twi.h +++ b/esp32-cam-rtos/twi.h @@ -35,4 +35,4 @@ uint8_t twi_readFrom(unsigned char address, unsigned char * buf, unsigned int le } #endif -#endif \ No newline at end of file +#endif diff --git a/esp32-cam/camera.c b/esp32-cam/camera.c index 1e78c99..f0f3a2e 100644 --- a/esp32-cam/camera.c +++ b/esp32-cam/camera.c @@ -48,6 +48,12 @@ #if CONFIG_OV5640_SUPPORT #include "ov5640.h" #endif +#if CONFIG_NT99141_SUPPORT +#include "nt99141.h" +#endif +#if CONFIG_OV7670_SUPPORT +#include "ov7670.h" +#endif typedef enum { CAMERA_NONE = 0, @@ -56,6 +62,8 @@ typedef enum { CAMERA_OV2640 = 2640, CAMERA_OV3660 = 3660, CAMERA_OV5640 = 5640, + CAMERA_OV7670 = 7670, + CAMERA_NT99141 = 9141, } camera_model_t; #define REG_PID 0x0A @@ -369,12 +377,10 @@ static inline void IRAM_ATTR i2s_conf_reset() } } -static void i2s_init() +static void i2s_gpio_init(const camera_config_t* config) { - camera_config_t* config = &s_state->config; - // Configure input GPIOs - gpio_num_t pins[] = { + const gpio_num_t pins[] = { config->pin_d7, config->pin_d6, config->pin_d5, @@ -391,15 +397,21 @@ static void i2s_init() .mode = GPIO_MODE_INPUT, .pull_up_en = GPIO_PULLUP_ENABLE, .pull_down_en = GPIO_PULLDOWN_DISABLE, - .intr_type = GPIO_INTR_DISABLE + .intr_type = GPIO_INTR_DISABLE, + .pin_bit_mask = 0LL }; for (int i = 0; i < sizeof(pins) / sizeof(gpio_num_t); ++i) { if (rtc_gpio_is_valid_gpio(pins[i])) { rtc_gpio_deinit(pins[i]); } - conf.pin_bit_mask = 1LL << pins[i]; - gpio_config(&conf); + conf.pin_bit_mask |= 1LL << pins[i]; } + gpio_config(&conf); +} + +static void i2s_init() +{ + camera_config_t* config = &s_state->config; // Route input GPIOs to I2S peripheral using GPIO matrix gpio_matrix_in(config->pin_d0, I2S0I_DATA_IN0_IDX, false); @@ -738,7 +750,7 @@ 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("bh 0x%08x\n", sig); + ESP_LOGD(TAG,"unexpected JPEG signature 0x%08x\n", sig); s_state->fb->bad = 1; return; } @@ -955,11 +967,15 @@ esp_err_t camera_probe(const camera_config_t* config, camera_model_t* out_camera return ESP_ERR_NO_MEM; } - ESP_LOGD(TAG, "Enabling XCLK output"); - camera_enable_out_clock(config); + if(config->pin_xclk >= 0) { + ESP_LOGD(TAG, "Enabling XCLK output"); + camera_enable_out_clock(config); + } - ESP_LOGD(TAG, "Initializing SSCB"); - SCCB_Init(config->pin_sscb_sda, config->pin_sscb_scl); + if (config->pin_sscb_sda != -1) { + ESP_LOGD(TAG, "Initializing SSCB"); + SCCB_Init(config->pin_sscb_sda, config->pin_sscb_scl); + } if(config->pin_pwdn >= 0) { ESP_LOGD(TAG, "Resetting camera by power down line"); @@ -1011,16 +1027,33 @@ esp_err_t camera_probe(const camera_config_t* config, camera_model_t* out_camera slv_addr = SCCB_Probe(); } #endif +#if CONFIG_NT99141_SUPPORT + if (slv_addr == 0x2a) + { + ESP_LOGD(TAG, "Resetting NT99141"); + SCCB_Write16(0x2a, 0x3008, 0x01);//bank sensor + } +#endif 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 (CONFIG_OV3660_SUPPORT || CONFIG_OV5640_SUPPORT || CONFIG_NT99141_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); vTaskDelay(10 / portTICK_PERIOD_MS); ESP_LOGD(TAG, "Camera PID=0x%02x VER=0x%02x", id->PID, id->VER); + } else if(s_state->sensor.slv_addr == 0x2a){ + id->PID = SCCB_Read16(s_state->sensor.slv_addr, 0x3000); + id->VER = SCCB_Read16(s_state->sensor.slv_addr, 0x3001); + vTaskDelay(10 / portTICK_PERIOD_MS); + ESP_LOGD(TAG, "Camera PID=0x%02x VER=0x%02x", id->PID, id->VER); + if(config->xclk_freq_hz > 10000000) + { + ESP_LOGE(TAG, "NT99141: only XCLK under 10MHz is supported, and XCLK is now set to 10M"); + s_state->sensor.xclk_freq_hz = 10000000; + } } else { #endif id->PID = SCCB_Read(s_state->sensor.slv_addr, REG_PID); @@ -1031,7 +1064,7 @@ esp_err_t camera_probe(const camera_config_t* config, camera_model_t* out_camera 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 || CONFIG_OV5640_SUPPORT) +#if (CONFIG_OV3660_SUPPORT || CONFIG_OV5640_SUPPORT || CONFIG_NT99141_SUPPORT) } #endif @@ -1060,6 +1093,18 @@ esp_err_t camera_probe(const camera_config_t* config, camera_model_t* out_camera *out_camera_model = CAMERA_OV5640; ov5640_init(&s_state->sensor); break; +#endif +#if CONFIG_OV7670_SUPPORT + case OV7670_PID: + *out_camera_model = CAMERA_OV7670; + ov7670_init(&s_state->sensor); + break; +#endif +#if CONFIG_NT99141_SUPPORT + case NT99141_PID: + *out_camera_model = CAMERA_NT99141; + NT99141_init(&s_state->sensor); + break; #endif default: id->PID = 0; @@ -1116,6 +1161,20 @@ esp_err_t camera_init(const camera_config_t* config) frame_size = FRAMESIZE_QSXGA; } break; +#endif +#if CONFIG_OV7670_SUPPORT + case OV7670_PID: + if (frame_size > FRAMESIZE_VGA) { + frame_size = FRAMESIZE_VGA; + } + break; +#endif +#if CONFIG_NT99141_SUPPORT + case NT99141_PID: + if (frame_size > FRAMESIZE_HD) { + frame_size = FRAMESIZE_HD; + } + break; #endif default: return ESP_ERR_CAMERA_NOT_SUPPORTED; @@ -1126,7 +1185,7 @@ esp_err_t camera_init(const camera_config_t* config) if (pix_format == PIXFORMAT_GRAYSCALE) { s_state->fb_size = s_state->width * s_state->height; - if (s_state->sensor.id.PID == OV3660_PID || s_state->sensor.id.PID == OV5640_PID) { + if (s_state->sensor.id.PID == OV3660_PID || s_state->sensor.id.PID == OV5640_PID || s_state->sensor.id.PID == NT99141_PID) { if (is_hs_mode()) { s_state->sampling_mode = SM_0A00_0B00; s_state->dma_filter = &dma_filter_yuyv_highspeed; @@ -1147,20 +1206,28 @@ esp_err_t camera_init(const camera_config_t* config) } s_state->fb_bytes_per_pixel = 1; // frame buffer stores Y8 } else if (pix_format == PIXFORMAT_YUV422 || pix_format == PIXFORMAT_RGB565) { - s_state->fb_size = s_state->width * s_state->height * 2; - if (is_hs_mode() && s_state->sensor.id.PID != OV7725_PID) { - 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 = 2; // camera sends YU/YV - s_state->fb_bytes_per_pixel = 2; // frame buffer stores YU/YV/RGB565 + s_state->fb_size = s_state->width * s_state->height * 2; + if (is_hs_mode() && s_state->sensor.id.PID != OV7725_PID) { + if(s_state->sensor.id.PID == OV7670_PID) { + s_state->sampling_mode = SM_0A0B_0B0C; + }else{ + 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 = 2; // camera sends YU/YV + 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; + if(s_state->sensor.id.PID == OV7670_PID) { + s_state->sampling_mode = SM_0A0B_0B0C; + }else{ + s_state->sampling_mode = SM_0A00_0B00; + } s_state->dma_filter = &dma_filter_rgb888_highspeed; } else { s_state->sampling_mode = SM_0A0B_0C0D; @@ -1169,7 +1236,7 @@ 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 && s_state->sensor.id.PID != OV5640_PID) { + if (s_state->sensor.id.PID != OV2640_PID && s_state->sensor.id.PID != OV3660_PID && s_state->sensor.id.PID != OV5640_PID && s_state->sensor.id.PID != NT99141_PID) { ESP_LOGE(TAG, "JPEG format is only supported for ov2640, ov3660 and ov5640"); err = ESP_ERR_NOT_SUPPORTED; goto fail; @@ -1256,8 +1323,13 @@ esp_err_t camera_init(const camera_config_t* config) vsync_intr_disable(); err = gpio_install_isr_service(ESP_INTR_FLAG_LEVEL1 | ESP_INTR_FLAG_IRAM); if (err != ESP_OK) { - ESP_LOGE(TAG, "gpio_install_isr_service failed (%x)", err); - goto fail; + if (err != ESP_ERR_INVALID_STATE) { + ESP_LOGE(TAG, "gpio_install_isr_service failed (%x)", err); + goto fail; + } + else { + ESP_LOGW(TAG, "gpio_install_isr_service already installed"); + } } err = gpio_isr_handler_add(s_state->config.pin_vsync, &vsync_isr, NULL); if (err != ESP_OK) { @@ -1301,26 +1373,31 @@ fail: esp_err_t esp_camera_init(const camera_config_t* config) { camera_model_t camera_model = CAMERA_NONE; + i2s_gpio_init(config); esp_err_t err = camera_probe(config, &camera_model); if (err != ESP_OK) { ESP_LOGE(TAG, "Camera probe failed with error 0x%x", err); goto fail; } if (camera_model == CAMERA_OV7725) { - ESP_LOGD(TAG, "Detected OV7725 camera"); + ESP_LOGI(TAG, "Detected OV7725 camera"); if(config->pixel_format == PIXFORMAT_JPEG) { ESP_LOGE(TAG, "Camera does not support JPEG"); err = ESP_ERR_CAMERA_NOT_SUPPORTED; goto fail; } } else if (camera_model == CAMERA_OV2640) { - ESP_LOGD(TAG, "Detected OV2640 camera"); + ESP_LOGI(TAG, "Detected OV2640 camera"); } else if (camera_model == CAMERA_OV3660) { - ESP_LOGD(TAG, "Detected OV3660 camera"); + ESP_LOGI(TAG, "Detected OV3660 camera"); } else if (camera_model == CAMERA_OV5640) { - ESP_LOGD(TAG, "Detected OV5640 camera"); + ESP_LOGI(TAG, "Detected OV5640 camera"); + } else if (camera_model == CAMERA_OV7670) { + ESP_LOGI(TAG, "Detected OV7670 camera"); + } else if (camera_model == CAMERA_NT99141) { + ESP_LOGI(TAG, "Detected NT99141 camera"); } else { - ESP_LOGE(TAG, "Camera not supported"); + ESP_LOGI(TAG, "Camera not supported"); err = ESP_ERR_CAMERA_NOT_SUPPORTED; goto fail; } @@ -1365,9 +1442,12 @@ esp_err_t esp_camera_deinit() } dma_desc_deinit(); camera_fb_deinit(); + + if(s_state->config.pin_xclk >= 0) { + camera_disable_out_clock(); + } free(s_state); s_state = NULL; - camera_disable_out_clock(); periph_module_disable(PERIPH_I2S0_MODULE); return ESP_OK; } @@ -1425,11 +1505,11 @@ sensor_t * esp_camera_sensor_get() esp_err_t esp_camera_save_to_nvs(const char *key) { -#ifdef ESP_IDF_VERSION_MAJOR - nvs_handle_t handle; -#else +//#if ESP_IDF_VERSION_MAJOR > 3 +// nvs_handle_t handle; +//#else nvs_handle handle; -#endif +//#endif esp_err_t ret = nvs_open(key,NVS_READWRITE,&handle); if (ret == ESP_OK) { @@ -1453,11 +1533,11 @@ esp_err_t esp_camera_save_to_nvs(const char *key) esp_err_t esp_camera_load_from_nvs(const char *key) { -#ifdef ESP_IDF_VERSION_MAJOR - nvs_handle_t handle; -#else +//#if ESP_IDF_VERSION_MAJOR > 3 +// nvs_handle_t handle; +//#else nvs_handle handle; -#endif +//#endif uint8_t pf; esp_err_t ret = nvs_open(key,NVS_READWRITE,&handle); diff --git a/esp32-cam/esp32-cam.ino b/esp32-cam/esp32-cam.ino index d8d1c3e..92baed4 100644 --- a/esp32-cam/esp32-cam.ino +++ b/esp32-cam/esp32-cam.ino @@ -37,10 +37,10 @@ // Select camera model //#define CAMERA_MODEL_WROVER_KIT -//#define CAMERA_MODEL_ESP_EYE +#define CAMERA_MODEL_ESP_EYE //#define CAMERA_MODEL_M5STACK_PSRAM //#define CAMERA_MODEL_M5STACK_WIDE -#define CAMERA_MODEL_AI_THINKER +//#define CAMERA_MODEL_AI_THINKER #include "camera_pins.h" @@ -113,7 +113,7 @@ void mjpegCB(void* pvParameters) { NULL, //(void*) handler, 2, &tStream, -// APP_CPU); + // APP_CPU); PRO_CPU); // Registering webserver handling routines @@ -469,6 +469,8 @@ void setup() ESP.restart(); } + sensor_t* s = esp_camera_sensor_get(); + s->set_vflip(s, true); // Configure and connect to WiFi IPAddress ip; diff --git a/esp32-cam/esp32-camera-master.zip b/esp32-cam/esp32-camera-master.zip deleted file mode 100644 index b3dbe9a..0000000 Binary files a/esp32-cam/esp32-camera-master.zip and /dev/null differ diff --git a/esp32-cam/img_converters.h b/esp32-cam/img_converters.h index 2b83c4d..330f8db 100644 --- a/esp32-cam/img_converters.h +++ b/esp32-cam/img_converters.h @@ -62,7 +62,8 @@ bool frame2jpg_cb(camera_fb_t * fb, uint8_t quality, jpg_out_cb cb, void * arg); * @param height Height in pixels of the source image * @param format Format of the source image * @param quality JPEG quality of the resulting image - * @param out Pointer to be populated with the address of the resulting buffer + * @param out Pointer to be populated with the address of the resulting buffer. + * You MUST free the pointer once you are done with it. * @param out_len Pointer to be populated with the length of the output buffer * * @return true on success diff --git a/esp32-cam/nt99141.c b/esp32-cam/nt99141.c new file mode 100644 index 0000000..07a9cc4 --- /dev/null +++ b/esp32-cam/nt99141.c @@ -0,0 +1,1032 @@ +/* + * 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. + * + * NT99141 driver. + * + */ +#include +#include +#include +#include "sccb.h" +#include "nt99141.h" +#include "nt99141_regs.h" +#include "nt99141_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 = "NT99141"; +#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_LOGD(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 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) +{ + return -1; +} + +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, 0x01); + + if (ret) { + ESP_LOGE(TAG, "Software Reset FAILED!"); + return ret; + } + + vTaskDelay(100 / portTICK_PERIOD_MS); + ret = write_regs(sensor->slv_addr, sensor_default_regs); //re-initial + + 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; + + // V-Flip + if (sensor->status.vflip) { + reg20 |= 0x01; + reg4514_test |= 1; + } + + // H-Mirror + if (sensor->status.hmirror) { + reg21 |= 0x02; + reg4514_test |= 2; + } + + switch (reg4514_test) { + + } + + if (write_reg(sensor->slv_addr, TIMING_TC_REG20, reg20 | reg21)) { + 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.binning, sensor->status.vflip, sensor->status.hmirror, reg4514); + return ret; +} + +static int set_framesize(sensor_t *sensor, framesize_t framesize) +{ + int ret = 0; + + sensor->status.framesize = framesize; + ret = write_regs(sensor->slv_addr, sensor_default_regs); + + if (framesize == FRAMESIZE_QVGA) { + ESP_LOGD(TAG, "Set FRAMESIZE_QVGA"); + ret = write_regs(sensor->slv_addr, sensor_framesize_QVGA); +#if CONFIG_NT99141_SUPPORT_XSKIP + ESP_LOGD(TAG, "Set FRAMESIZE_QVGA: xskip mode"); + ret = write_regs(sensor->slv_addr, sensor_framesize_QVGA_xskip); +#elif CONFIG_NT99141_SUPPORT_CROP + ESP_LOGD(TAG, "Set FRAMESIZE_QVGA: crop mode"); + ret = write_regs(sensor->slv_addr, sensor_framesize_QVGA_crop); +#endif + } else if (framesize == FRAMESIZE_VGA) { + ESP_LOGD(TAG, "Set FRAMESIZE_VGA"); + // ret = write_regs(sensor->slv_addr, sensor_framesize_VGA); + ret = write_regs(sensor->slv_addr, sensor_framesize_VGA_xyskip);// Resolution:640*360 This configuration is equally-scaled without deforming +#ifdef CONFIG_NT99141_SUPPORT_XSKIP + ESP_LOGD(TAG, "Set FRAMESIZE_QVGA: xskip mode"); + ret = write_regs(sensor->slv_addr, sensor_framesize_VGA_xskip); +#elif CONFIG_NT99141_SUPPORT_CROP + ESP_LOGD(TAG, "Set FRAMESIZE_QVGA: crop mode"); + ret = write_regs(sensor->slv_addr, sensor_framesize_VGA_crop); +#endif + } else if (framesize >= FRAMESIZE_HD) { + ESP_LOGD(TAG, "Set FRAMESIZE_HD"); + ret = write_regs(sensor->slv_addr, sensor_framesize_HD); + } else { + ESP_LOGD(TAG, "Dont suppost this size, Set FRAMESIZE_VGA"); + ret = write_regs(sensor->slv_addr, sensor_framesize_VGA); + } + + return 0; +} + +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, 0x32bb, 0x87, 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; + int data = 0; + // ret = write_reg_bits(sensor->slv_addr, 0x32bb, 0x87, enable); + data = read_reg(sensor->slv_addr, 0x3201); + ESP_LOGD(TAG, "set_exposure_ctrl:enable"); + if (enable) { + ESP_LOGD(TAG, "set_exposure_ctrl:enable"); + ret = write_reg(sensor->slv_addr, 0x3201, (1 << 5) | data); + } else { + ESP_LOGD(TAG, "set_exposure_ctrl:disable"); + ret = write_reg(sensor->slv_addr, 0x3201, (~(1 << 5)) & data); + } + + 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; + + 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; + + 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; + + 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; + + 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; + + 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; + + 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; + + 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) +{ + ESP_LOGD(TAG, "get_agc_gain can not be configured at present"); + return 0; +} + +//real gain +static int set_agc_gain(sensor_t *sensor, int gain) +{ + ESP_LOGD(TAG, "set_agc_gain can not be configured at present"); + // ESP_LOGD(TAG, "GAIN = %d\n", gain); + int cnt = gain / 2; + + switch (cnt) { + case 0: + ESP_LOGD(TAG, "set_agc_gain: 1x"); + write_reg(sensor->slv_addr, 0X301D, 0X00); + break; + + case 1: + ESP_LOGD(TAG,"set_agc_gain: 2x"); + write_reg(sensor->slv_addr, 0X301D, 0X0F); + break; + + case 2: + ESP_LOGD(TAG,"set_agc_gain: 4x"); + write_reg(sensor->slv_addr, 0X301D, 0X2F); + break; + + case 3: + ESP_LOGD(TAG,"set_agc_gain: 6x"); + write_reg(sensor->slv_addr, 0X301D, 0X37); + break; + + case 4: + ESP_LOGD(TAG,"set_agc_gain: 8x"); + write_reg(sensor->slv_addr, 0X301D, 0X3F); + break; + + default: + ESP_LOGD(TAG,"fail set_agc_gain"); + break; + } + + return 0; +} + +static int get_aec_value(sensor_t *sensor) +{ + ESP_LOGD(TAG, "get_aec_value can not be configured at present"); + return 0; +} + +static int set_aec_value(sensor_t *sensor, int value) +{ + ESP_LOGD(TAG, "set_aec_value can not be configured at present"); + int ret = 0; + // ESP_LOGD(TAG, " set_aec_value to: %d", value); + ret = write_reg_bits(sensor->slv_addr, 0x3012, 0x00, (value >> 8) & 0xff); + ret = write_reg_bits(sensor->slv_addr, 0x3013, 0x01, value & 0xff); + + if (ret == 0) { + ESP_LOGD(TAG, " set_aec_value to: %d", value); + // sensor->status.aec = enable; + } + + return ret; +} + +static int set_ae_level(sensor_t *sensor, int level) +{ + ESP_LOGD(TAG, "set_ae_level can not be configured at present"); + int ret = 0; + + if (level < 0) { + level = 0; + } else if (level > 9) { + level = 9; + } + + for (int i = 0; i < 5; i++) { + ret += write_reg(sensor->slv_addr, sensor_ae_level[ 5 * level + i ][0], sensor_ae_level[5 * level + i ][1]); + } + + if (ret) { + ESP_LOGE(TAG, " fail to set ae level: %d", ret); + } + + return 0; +} + +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, 0x3201, (mode != 0)); + + if (ret) { + return ret; + } + + switch (mode) { + case 1://Sunny + ret = write_reg16(sensor->slv_addr, 0x3290, 0x01) + || write_reg16(sensor->slv_addr, 0x3291, 0x38) + || write_reg16(sensor->slv_addr, 0x3296, 0x01) + || write_reg16(sensor->slv_addr, 0x3297, 0x68) + || write_reg16(sensor->slv_addr, 0x3060, 0x01); + + break; + + case 2://Cloudy + + ret = write_reg16(sensor->slv_addr, 0x3290, 0x01) + || write_reg16(sensor->slv_addr, 0x3291, 0x51) + || write_reg16(sensor->slv_addr, 0x3296, 0x01) + || write_reg16(sensor->slv_addr, 0x3297, 0x00) + || write_reg16(sensor->slv_addr, 0x3060, 0x01); + break; + + case 3://INCANDESCENCE] + ret = write_reg16(sensor->slv_addr, 0x3290, 0x01) + || write_reg16(sensor->slv_addr, 0x3291, 0x30) + || write_reg16(sensor->slv_addr, 0x3296, 0x01) + || write_reg16(sensor->slv_addr, 0x3297, 0xCB) + || write_reg16(sensor->slv_addr, 0x3060, 0x01); + break; + + case 4://FLUORESCENT + ret = write_reg16(sensor->slv_addr, 0x3290, 0x01) + || write_reg16(sensor->slv_addr, 0x3291, 0x70) + || write_reg16(sensor->slv_addr, 0x3296, 0x01) + || write_reg16(sensor->slv_addr, 0x3297, 0xFF) + || write_reg16(sensor->slv_addr, 0x3060, 0x01); + 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, 0x32F1, regs[0]) + || write_reg(sensor->slv_addr, 0x32F4, regs[1]) + || write_reg(sensor->slv_addr, 0x32F5, regs[2]) + || write_reg(sensor->slv_addr, 0x3060, 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 = 0xA0; + break; + + case 2: + value = 0x90; + break; + + case 1: + value = 0x88; + break; + + case -1: + value = 0x78; + negative = true; + break; + + case -2: + value = 0x70; + negative = true; + break; + + case -3: + value = 0x60; + negative = true; + break; + + default: // 0 + break; + } + + ret = write_reg(sensor->slv_addr, 0x32F2, value); + + 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; + uint8_t value1 = 0, value2 = 0 ; + bool negative = false; + + switch (level) { + case 3: + value1 = 0xD0; + value2 = 0xB0; + break; + + case 2: + value1 = 0xE0; + value2 = 0xA0; + break; + + case 1: + value1 = 0xF0; + value2 = 0x90; + break; + + case 0: + value1 = 0x00; + value2 = 0x80; + break; + + case -1: + value1 = 0x10; + value2 = 0x70; + break; + + case -2: + value1 = 0x20; + value2 = 0x60; + break; + + case -3: + value1 = 0x30; + value2 = 0x50; + break; + + default: // 0 + break; + } + + ret = write_reg(sensor->slv_addr, 0x32FC, value1); + ret = write_reg(sensor->slv_addr, 0x32F2, value2); + ret = write_reg(sensor->slv_addr, 0x3060, 0x01); + + 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]; + { + ret = write_reg(sensor->slv_addr, 0x32F3, regs[0]); + + if (ret) { + return ret; + } + } + + 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) +{ + ESP_LOGD(TAG, "set_gainceiling can not be configured at present"); + return 0; +} + +static int get_denoise(sensor_t *sensor) +{ + + return (read_reg(sensor->slv_addr, 0x5306) / 4) + 1; +} + +static int set_denoise(sensor_t *sensor, int level) +{ + ESP_LOGD(TAG, "set_denoise can not be configured at present"); + return 0; +} + +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); + + 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; + if (xclk > 10) + { + ESP_LOGE(TAG, "only XCLK under 10MHz is supported, and XCLK is now set to 10M"); + xclk = 10; + } + 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, 0x3301)); + sensor->status.denoise = get_denoise(sensor); + sensor->status.ae_level = 0; + sensor->status.gainceiling = read_reg16(sensor->slv_addr, 0x32F0) & 0xFF; + sensor->status.awb = check_reg_mask(sensor->slv_addr, ISP_CONTROL_01, 0x10); + 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, 0x3000, 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, 0x3000, 0x04); + return 0; +} + +int NT99141_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/esp32-cam/nt99141.h b/esp32-cam/nt99141.h new file mode 100644 index 0000000..287a742 --- /dev/null +++ b/esp32-cam/nt99141.h @@ -0,0 +1,16 @@ +/* + * 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. + * + * NT99141 driver. + * + */ +#ifndef __NT99141_H__ +#define __NT99141_H__ + +#include "sensor.h" + +int NT99141_init(sensor_t *sensor); + +#endif // __NT99141_H__ diff --git a/esp32-cam/nt99141_regs.h b/esp32-cam/nt99141_regs.h new file mode 100644 index 0000000..8301db9 --- /dev/null +++ b/esp32-cam/nt99141_regs.h @@ -0,0 +1,211 @@ +/* + * NT99141 register definitions. + */ +#ifndef __NT99141_REG_REGS_H__ +#define __NT99141_REG_REGS_H__ + +/* system control registers */ +#define SYSTEM_CTROL0 0x3021 // 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 0x3025 // 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 0x3201 // 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 + +/* mirror and flip registers */ +#define TIMING_TC_REG20 0x3022 // Timing Control Register + // Bit[2:1]: Vertical flip enable + // 00: Normal + // 11: Vertical flip + // Bit[0]: Vertical binning enable +#define TIMING_TC_REG21 0x3022 // 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 0x3024// 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 0x306a // 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 0x3021 // 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 0x3401 // 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 0x02 /* 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 0x01 /* Vertical flip enable */ +#define TIMING_TC_REG21_HMIRROR 0x02 /* Horizontal mirror enable */ + +#endif // __NT99141_REG_REGS_H__ diff --git a/esp32-cam/nt99141_settings.h b/esp32-cam/nt99141_settings.h new file mode 100644 index 0000000..1ffec20 --- /dev/null +++ b/esp32-cam/nt99141_settings.h @@ -0,0 +1,825 @@ +#ifndef _NT99141_SETTINGS_H_ +#define _NT99141_SETTINGS_H_ + +#include +#include +#include "esp_attr.h" +#include "nt99141_regs.h" + +static const ratio_settings_t ratio_table[] = { + // mw, mh, sx, sy, ex, ey, ox, oy, tx, ty + { 1280, 720, 0, 4, 1283, 723, 0, 4, 1660, 963 }, + +}; + +#define REG_DLY 0xffff +#define REGLIST_TAIL 0x0000 + +static const DRAM_ATTR uint16_t sensor_default_regs[][2] = { + //initial +{0x3021, 0x00}, +{REG_DLY, 100}, // delay 100ms +{0x3109, 0x04}, +{0x3040, 0x04}, +{0x3041, 0x02}, +{0x3042, 0xFF}, +{0x3043, 0x08}, +{0x3052, 0xE0}, +{0x305F, 0x33}, +{0x3100, 0x07}, +{0x3106, 0x03}, +{0x3105, 0x01}, +{0x3108, 0x05}, +{0x3110, 0x22}, +{0x3111, 0x57}, +{0x3112, 0x22}, +{0x3113, 0x55}, +{0x3114, 0x05}, +{0x3135, 0x00}, +{0x32F0, 0x01}, +{0x3290, 0x01}, +{0x3291, 0x80}, +{0x3296, 0x01}, +{0x3297, 0x73}, +{0x3250, 0x80}, +{0x3251, 0x03}, +{0x3252, 0xFF}, +{0x3253, 0x00}, +{0x3254, 0x03}, +{0x3255, 0xFF}, +{0x3256, 0x00}, +{0x3257, 0x50}, +{0x3270, 0x00}, +{0x3271, 0x0C}, +{0x3272, 0x18}, +{0x3273, 0x32}, +{0x3274, 0x44}, +{0x3275, 0x54}, +{0x3276, 0x70}, +{0x3277, 0x88}, +{0x3278, 0x9D}, +{0x3279, 0xB0}, +{0x327A, 0xCF}, +{0x327B, 0xE2}, +{0x327C, 0xEF}, +{0x327D, 0xF7}, +{0x327E, 0xFF}, +{0x3302, 0x00}, +{0x3303, 0x40}, +{0x3304, 0x00}, +{0x3305, 0x96}, +{0x3306, 0x00}, +{0x3307, 0x29}, +{0x3308, 0x07}, +{0x3309, 0xBA}, +{0x330A, 0x06}, +{0x330B, 0xF5}, +{0x330C, 0x01}, +{0x330D, 0x51}, +{0x330E, 0x01}, +{0x330F, 0x30}, +{0x3310, 0x07}, +{0x3311, 0x16}, +{0x3312, 0x07}, +{0x3313, 0xBA}, +{0x3326, 0x02}, +{0x32F6, 0x0F}, +{0x32F9, 0x42}, +{0x32FA, 0x24}, +{0x3325, 0x4A}, +{0x3330, 0x00}, +{0x3331, 0x0A}, +{0x3332, 0xFF}, +{0x3338, 0x30}, +{0x3339, 0x84}, +{0x333A, 0x48}, +{0x333F, 0x07}, +{0x3360, 0x10}, +{0x3361, 0x18}, +{0x3362, 0x1f}, +{0x3363, 0x37}, +{0x3364, 0x80}, +{0x3365, 0x80}, +{0x3366, 0x68}, +{0x3367, 0x60}, +{0x3368, 0x30}, +{0x3369, 0x28}, +{0x336A, 0x20}, +{0x336B, 0x10}, +{0x336C, 0x00}, +{0x336D, 0x20}, +{0x336E, 0x1C}, +{0x336F, 0x18}, +{0x3370, 0x10}, +{0x3371, 0x38}, +{0x3372, 0x3C}, +{0x3373, 0x3F}, +{0x3374, 0x3F}, +{0x338A, 0x34}, +{0x338B, 0x7F}, +{0x338C, 0x10}, +{0x338D, 0x23}, +{0x338E, 0x7F}, +{0x338F, 0x14}, +{0x3375, 0x08}, +{0x3376, 0x0C}, +{0x3377, 0x18}, +{0x3378, 0x20}, +{0x3012, 0x02}, +{0x3013, 0xD0}, +{0x3025, 0x02}, //colorbar +{REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_fmt_jpeg[][2] = { + {0x32F0, 0x70}, // YUV422 + {REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_fmt_raw[][2] = { + {0x32F0, 0x50}, // RAW + {REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_fmt_grayscale[][2] = { + {0x32F1, 0x01}, + {REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_fmt_yuv422[][2] = { + {0x32F0, 0x00}, // YUV422 + {REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_fmt_rgb565[][2] = { + {0x32F0, 0x01}, // RGB + {REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint8_t sensor_saturation_levels[9][1] = { + {0x60},//-4 + {0x68},//-3 + {0x70},//-2 + {0x78},//-1 + {0x80},//0 + {0x88},//+1 + {0x90},//+2 + {0x98},//+3 + {0xA0},//+4 +}; + +static const DRAM_ATTR uint8_t sensor_special_effects[7][4] = { + {0x00, 0x80, 0x80, 0x01},//Normal + {0x03, 0x80, 0x80, 0x01},//Negative + {0x01, 0x80, 0x80, 0x01},//Grayscale + {0x05, 0x2A, 0xF0, 0x01},//Red Tint + {0x05, 0x60, 0x20, 0x01},//Green Tint + {0x05, 0xF0, 0x80, 0x01},//Blue Tint + {0x02, 0x80, 0x80, 0x01},//Sepia + +}; + +// AE LEVEL +static const DRAM_ATTR uint16_t sensor_ae_level[][2] = { + +// 1. [AE_Target : 0x24] +// Set_Device_Format = FORMAT_16_8 +// SET_Device_Addr = 0x54 + {0x32B8, 0x29 }, + {0x32B9, 0x1F }, + {0x32BC, 0x24 }, + {0x32BD, 0x27 }, + {0x32BE, 0x21 }, +//------------------------------------------------------------------------ +// 2. [AE_Target : 0x28] +// Set_Device_Format = FORMAT_16_8 +// SET_Device_Addr = 0x54 + {0x32B8, 0x2D }, + {0x32B9, 0x23 }, + {0x32BC, 0x28 }, + {0x32BD, 0x2B }, + {0x32BE, 0x25 }, +//------------------------------------------------------------------------ +// 3. [AE_Target : 0x2C] +// Set_Device_Format = FORMAT_16_8 +// SET_Device_Addr = 0x54 + {0x32B8, 0x32 }, + {0x32B9, 0x26 }, + {0x32BC, 0x2C }, + {0x32BD, 0x2F }, + {0x32BE, 0x29 }, +//------------------------------------------------------------------------ +// 4, [AE_Target : 0x30] +// Set_Device_Format = FORMAT_16_8 +// SET_Device_Addr = 0x54 + {0x32B8, 0x36 }, + {0x32B9, 0x2A }, + {0x32BC, 0x30 }, + {0x32BD, 0x33 }, + {0x32BE, 0x2D }, +//------------------------------------------------------------------------ +// 5. [AE_Target : 0x34] +// Set_Device_Format = FORMAT_16_8 +// SET_Device_Addr = 0x54 + {0x32B8, 0x3B }, + {0x32B9, 0x2D }, + {0x32BC, 0x34 }, + {0x32BD, 0x38 }, + {0x32BE, 0x30 }, +//------------------------------------------------------------------------ +// 6. [AE_Target : 0x38] +// Set_Device_Format = FORMAT_16_8 +// SET_Device_Addr = 0x54 + {0x32B8, 0x3F }, + {0x32B9, 0x31 }, + {0x32BC, 0x38 }, + {0x32BD, 0x3C }, + {0x32BE, 0x34 }, +//------------------------------------------------------------------------ +// 7. [AE_Target : 0x3D] +// Set_Device_Format = FORMAT_16_8 +// SET_Device_Addr = 0x54 + {0x32B8, 0x44 }, + {0x32B9, 0x34 }, + {0x32BC, 0x3C }, + {0x32BD, 0x40 }, + {0x32BE, 0x38 }, +//------------------------------------------------------------------------ +// 8. [AE_Target : 0x40] +// Set_Device_Format = FORMAT_16_8 +// SET_Device_Addr = 0x54 + {0x32B8, 0x48 }, + {0x32B9, 0x38 }, + {0x32BC, 0x40 }, + {0x32BD, 0x44 }, + {0x32BE, 0x3C }, +//------------------------------------------------------------------------ +// 9. [AE_Target : 0x44] +// Set_Device_Format = FORMAT_16_8 +// SET_Device_Addr = 0x54 + {0x32B8, 0x4D }, + {0x32B9, 0x3B }, + {0x32BC, 0x44 }, + {0x32BD, 0x49 }, + {0x32BE, 0x3F }, +}; + +static const DRAM_ATTR uint16_t sensor_framesize_HD[][2] = { +//[JPEG_1280x720_8.18_8.18_Fps] +{0x3021, 0x00}, +{REG_DLY, 100}, // delay 100ms +{0x32BF, 0x60}, +{0x32C0, 0x5A}, +{0x32C1, 0x5A}, +{0x32C2, 0x5A}, +{0x32C3, 0x00}, +{0x32C4, 0x20}, +{0x32C5, 0x20}, +{0x32C6, 0x20}, +{0x32C7, 0x00}, +{0x32C8, 0x3C}, +{0x32C9, 0x5A}, +{0x32CA, 0x7A}, +{0x32CB, 0x7A}, +{0x32CC, 0x7A}, +{0x32CD, 0x7A}, +{0x32DB, 0x5E}, +{0x32F0, 0x70}, +{0x3400, 0x08}, +{0x3400, 0x00}, +{0x3401, 0x4E}, +{0x3404, 0x00}, +{0x3405, 0x00}, +{0x3410, 0x00}, +{0x3200, 0x3E}, +{0x3201, 0x0F}, +{0x3028, 0x0F}, +{0x3029, 0x00}, +{0x302A, 0x08}, +{0x3022, 0x24}, +{0x3023, 0x24}, +{0x3002, 0x00}, +{0x3003, 0x04}, +{0x3004, 0x00}, +{0x3005, 0x04}, +{0x3006, 0x05}, +{0x3007, 0x03}, +{0x3008, 0x02}, +{0x3009, 0xD3}, +{0x300A, 0x06}, +{0x300B, 0x7C}, +{0x300C, 0x02}, +{0x300D, 0xE0}, +{0x300E, 0x05}, +{0x300F, 0x00}, +{0x3010, 0x02}, +{0x3011, 0xD0}, +{0x32B8, 0x3F}, +{0x32B9, 0x31}, +{0x32BB, 0x87}, +{0x32BC, 0x38}, +{0x32BD, 0x3C}, +{0x32BE, 0x34}, +{0x3201, 0x3F}, +{0x3021, 0x06}, +{0x3025, 0x00}, //normal +{0x3400, 0x01}, +{0x3060, 0x01}, +{REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_framesize_VGA[][2] = { +//[JPEG_640x480_10.14_10.14_Fps] +{0x3021, 0x00}, +{REG_DLY, 100}, // delay 100ms +{0x32BF, 0x60}, +{0x32C0, 0x5A}, +{0x32C1, 0x5A}, +{0x32C2, 0x5A}, +{0x32C3, 0x00}, +{0x32C4, 0x20}, +{0x32C5, 0x20}, +{0x32C6, 0x20}, +{0x32C7, 0x00}, +{0x32C8, 0x4B}, +{0x32C9, 0x5A}, +{0x32CA, 0x7A}, +{0x32CB, 0x7A}, +{0x32CC, 0x7A}, +{0x32CD, 0x7A}, +{0x32DB, 0x62}, +{0x32F0, 0x70}, +{0x3400, 0x08}, +{0x3400, 0x00}, +{0x3401, 0x4E}, +{0x3404, 0x00}, +{0x3405, 0x00}, +{0x3410, 0x00}, +{0x32E0, 0x02}, +{0x32E1, 0x80}, +{0x32E2, 0x01}, +{0x32E3, 0xE0}, +{0x32E4, 0x00}, +{0x32E5, 0x80}, +{0x32E6, 0x00}, +{0x32E7, 0x80}, +{0x3200, 0x3E}, +{0x3201, 0x0F}, +{0x3028, 0x0F}, +{0x3029, 0x00}, +{0x302A, 0x08}, +{0x3022, 0x24}, +{0x3023, 0x24}, +{0x3002, 0x00}, +{0x3003, 0xA4}, +{0x3004, 0x00}, +{0x3005, 0x04}, +{0x3006, 0x04}, +{0x3007, 0x63}, +{0x3008, 0x02}, +{0x3009, 0xD3}, +{0x300A, 0x05}, +{0x300B, 0x3C}, +{0x300C, 0x02}, +{0x300D, 0xE0}, +{0x300E, 0x03}, +{0x300F, 0xC0}, +{0x3010, 0x02}, +{0x3011, 0xD0}, +{0x32B8, 0x3F}, +{0x32B9, 0x31}, +{0x32BB, 0x87}, +{0x32BC, 0x38}, +{0x32BD, 0x3C}, +{0x32BE, 0x34}, +{0x3201, 0x7F}, +{0x3021, 0x06}, +{0x3025, 0x00}, //normal +{0x3400, 0x01}, +{0x3060, 0x01}, +{REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_framesize_QVGA[][2] = { +//[JPEG_320x240_10.14_10.14_Fps] +{0x3021, 0x00}, +{REG_DLY, 100}, // delay 100ms +{0x32BF, 0x60}, +{0x32C0, 0x5A}, +{0x32C1, 0x5A}, +{0x32C2, 0x5A}, +{0x32C3, 0x00}, +{0x32C4, 0x20}, +{0x32C5, 0x20}, +{0x32C6, 0x20}, +{0x32C7, 0x00}, +{0x32C8, 0x4B}, +{0x32C9, 0x5A}, +{0x32CA, 0x7A}, +{0x32CB, 0x7A}, +{0x32CC, 0x7A}, +{0x32CD, 0x7A}, +{0x32DB, 0x62}, +{0x32F0, 0x70}, +{0x3400, 0x08}, +{0x3400, 0x00}, +{0x3401, 0x4E}, +{0x3404, 0x00}, +{0x3405, 0x00}, +{0x3410, 0x00}, +{0x32E0, 0x01}, +{0x32E1, 0x40}, +{0x32E2, 0x00}, +{0x32E3, 0xF0}, +{0x32E4, 0x02}, +{0x32E5, 0x02}, +{0x32E6, 0x02}, +{0x32E7, 0x03}, +{0x3200, 0x3E}, +{0x3201, 0x0F}, +{0x3028, 0x0F}, +{0x3029, 0x00}, +{0x302A, 0x08}, +{0x3022, 0x24}, +{0x3023, 0x24}, +{0x3002, 0x00}, +{0x3003, 0xA4}, +{0x3004, 0x00}, +{0x3005, 0x04}, +{0x3006, 0x04}, +{0x3007, 0x63}, +{0x3008, 0x02}, +{0x3009, 0xD3}, +{0x300A, 0x05}, +{0x300B, 0x3C}, +{0x300C, 0x02}, +{0x300D, 0xE0}, +{0x300E, 0x03}, +{0x300F, 0xC0}, +{0x3010, 0x02}, +{0x3011, 0xD0}, +{0x32B8, 0x3F}, +{0x32B9, 0x31}, +{0x32BB, 0x87}, +{0x32BC, 0x38}, +{0x32BD, 0x3C}, +{0x32BE, 0x34}, +{0x3201, 0x7F}, +{0x3021, 0x06}, +{0x3025, 0x00}, //normal +{0x3400, 0x01}, +{0x3060, 0x01}, +{REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_framesize_VGA_xyskip[][2] = { +// [JPEG_640x360_20.00_25.01_Fps_XY_Skip] +// Set_Device_Format = FORMAT_16_8 +// SET_Device_Addr = 0x54 +{0x3021, 0x00}, +{REG_DLY, 100}, // delay 100ms +{0x32BF, 0x60 }, +{0x320A, 0xB2 }, +{0x32C0, 0x64 }, +{0x32C1, 0x64 }, +{0x32C2, 0x64 }, +{0x32C3, 0x00 }, +{0x32C4, 0x20 }, +{0x32C5, 0x20 }, +{0x32C6, 0x20 }, +{0x32C7, 0x00 }, +{0x32C8, 0x62 }, +{0x32C9, 0x64 }, +{0x32CA, 0x84 }, +{0x32CB, 0x84 }, +{0x32CC, 0x84 }, +{0x32CD, 0x84 }, +{0x32DB, 0x68 }, +{0x32F0, 0x70 }, +{0x3400, 0x08 }, +{0x3400, 0x00 }, +{0x3401, 0x4E }, +{0x3404, 0x00 }, +{0x3405, 0x00 }, +{0x3410, 0x00 }, +{0x3200, 0x3E }, +{0x3201, 0x0F }, +{0x3028, 0x0F }, +{0x3029, 0x00 }, +{0x302A, 0x08 }, +{0x3022, 0x24 }, +{0x3023, 0x6C }, +{0x3002, 0x00 }, +{0x3003, 0x04 }, +{0x3004, 0x00 }, +{0x3005, 0x04 }, +{0x3006, 0x05 }, +{0x3007, 0x03 }, +{0x3008, 0x02 }, +{0x3009, 0xD3 }, +{0x300A, 0x03 }, +{0x300B, 0xFC }, +{0x300C, 0x01 }, +{0x300D, 0x88 }, +{0x300E, 0x02 }, +{0x300F, 0x80 }, +{0x3010, 0x01 }, +{0x3011, 0x68 }, +{0x32B8, 0x3F }, +{0x32B9, 0x31 }, +{0x32BB, 0x87 }, +{0x32BC, 0x38 }, +{0x32BD, 0x3C }, +{0x32BE, 0x34 }, +{0x3201, 0x3F }, +{0x3025, 0x00 }, //normal +{0x3021, 0x06 }, +{0x3400, 0x01 }, +{0x3060, 0x01 }, +{REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_framesize_VGA_xskip[][2] = { +//[JPEG_640x480_Xskip_13.32_13.32_Fps] +{0x3021, 0x00}, +{REG_DLY, 100}, // delay 100ms +{0x32BF, 0x60}, +{0x32C0, 0x5A}, +{0x32C1, 0x5A}, +{0x32C2, 0x5A}, +{0x32C3, 0x00}, +{0x32C4, 0x20}, +{0x32C5, 0x20}, +{0x32C6, 0x20}, +{0x32C7, 0x00}, +{0x32C8, 0x62}, +{0x32C9, 0x5A}, +{0x32CA, 0x7A}, +{0x32CB, 0x7A}, +{0x32CC, 0x7A}, +{0x32CD, 0x7A}, +{0x32DB, 0x68}, +{0x32F0, 0x70}, +{0x3400, 0x08}, +{0x3400, 0x00}, +{0x3401, 0x4E}, +{0x3404, 0x00}, +{0x3405, 0x00}, +{0x3410, 0x00}, +{0x32E0, 0x02}, +{0x32E1, 0x80}, +{0x32E2, 0x01}, +{0x32E3, 0xE0}, +{0x32E4, 0x00}, +{0x32E5, 0x00}, +{0x32E6, 0x00}, +{0x32E7, 0x80}, +{0x3200, 0x3E}, +{0x3201, 0x0F}, +{0x3028, 0x0F}, +{0x3029, 0x00}, +{0x302A, 0x08}, +{0x3022, 0x24}, +{0x3023, 0x2C}, +{0x3002, 0x00}, +{0x3003, 0x04}, +{0x3004, 0x00}, +{0x3005, 0x04}, +{0x3006, 0x05}, +{0x3007, 0x03}, +{0x3008, 0x02}, +{0x3009, 0xD3}, +{0x300A, 0x03}, +{0x300B, 0xFC}, +{0x300C, 0x02}, +{0x300D, 0xE0}, +{0x300E, 0x02}, +{0x300F, 0x80}, +{0x3010, 0x02}, +{0x3011, 0xD0}, +{0x32B8, 0x3F}, +{0x32B9, 0x31}, +{0x32BB, 0x87}, +{0x32BC, 0x38}, +{0x32BD, 0x3C}, +{0x32BE, 0x34}, +{0x3201, 0x7F}, +{0x3021, 0x06}, +{0x3025, 0x00}, //normal +{0x3400, 0x01}, +{0x3060, 0x01}, +{REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_framesize_QVGA_xskip[][2] = { +{0x3021, 0x00}, +{REG_DLY, 100}, // delay 100ms +//[JPEG_320x240_Xskip_13.32_13.32_Fps] +{0x32BF, 0x60}, +{0x32C0, 0x5A}, +{0x32C1, 0x5A}, +{0x32C2, 0x5A}, +{0x32C3, 0x00}, +{0x32C4, 0x20}, +{0x32C5, 0x20}, +{0x32C6, 0x20}, +{0x32C7, 0x00}, +{0x32C8, 0x62}, +{0x32C9, 0x5A}, +{0x32CA, 0x7A}, +{0x32CB, 0x7A}, +{0x32CC, 0x7A}, +{0x32CD, 0x7A}, +{0x32DB, 0x68}, +{0x32F0, 0x70}, +{0x3400, 0x08}, +{0x3400, 0x00}, +{0x3401, 0x4E}, +{0x3404, 0x00}, +{0x3405, 0x00}, +{0x3410, 0x00}, +{0x32E0, 0x01}, +{0x32E1, 0x40}, +{0x32E2, 0x00}, +{0x32E3, 0xF0}, +{0x32E4, 0x01}, +{0x32E5, 0x01}, +{0x32E6, 0x02}, +{0x32E7, 0x03}, +{0x3200, 0x3E}, +{0x3201, 0x0F}, +{0x3028, 0x0F}, +{0x3029, 0x00}, +{0x302A, 0x08}, +{0x3022, 0x24}, +{0x3023, 0x2C}, +{0x3002, 0x00}, +{0x3003, 0x04}, +{0x3004, 0x00}, +{0x3005, 0x04}, +{0x3006, 0x05}, +{0x3007, 0x03}, +{0x3008, 0x02}, +{0x3009, 0xD3}, +{0x300A, 0x03}, +{0x300B, 0xFC}, +{0x300C, 0x02}, +{0x300D, 0xE0}, +{0x300E, 0x02}, +{0x300F, 0x80}, +{0x3010, 0x02}, +{0x3011, 0xD0}, +{0x32B8, 0x3F}, +{0x32B9, 0x31}, +{0x32BB, 0x87}, +{0x32BC, 0x38}, +{0x32BD, 0x3C}, +{0x32BE, 0x34}, +{0x3201, 0x7F}, +{0x3021, 0x06}, +{0x3025, 0x00}, //normal +{0x3400, 0x01}, +{0x3060, 0x01}, +{REGLIST_TAIL, 0x00}, // tail +}; + + +static const DRAM_ATTR uint16_t sensor_framesize_VGA_crop[][2] = { +//[JPEG_640x480_Crop_19.77_19.77_Fps] +{0x3021, 0x00}, +{REG_DLY, 100}, // delay 100ms +{0x32BF, 0x60}, +{0x32C0, 0x5A}, +{0x32C1, 0x5A}, +{0x32C2, 0x5A}, +{0x32C3, 0x00}, +{0x32C4, 0x20}, +{0x32C5, 0x20}, +{0x32C6, 0x20}, +{0x32C7, 0x00}, +{0x32C8, 0x62}, +{0x32C9, 0x5A}, +{0x32CA, 0x7A}, +{0x32CB, 0x7A}, +{0x32CC, 0x7A}, +{0x32CD, 0x7A}, +{0x32DB, 0x68}, +{0x32F0, 0x70}, +{0x3400, 0x08}, +{0x3400, 0x00}, +{0x3401, 0x4E}, +{0x3404, 0x00}, +{0x3405, 0x00}, +{0x3410, 0x00}, +{0x3200, 0x3E}, +{0x3201, 0x0F}, +{0x3028, 0x0F}, +{0x3029, 0x00}, +{0x302A, 0x08}, +{0x3022, 0x24}, +{0x3023, 0x24}, +{0x3002, 0x01}, +{0x3003, 0x44}, +{0x3004, 0x00}, +{0x3005, 0x7C}, +{0x3006, 0x03}, +{0x3007, 0xC3}, +{0x3008, 0x02}, +{0x3009, 0x5B}, +{0x300A, 0x03}, +{0x300B, 0xFC}, +{0x300C, 0x01}, +{0x300D, 0xF0}, +{0x300E, 0x02}, +{0x300F, 0x80}, +{0x3010, 0x01}, +{0x3011, 0xE0}, +{0x32B8, 0x3F}, +{0x32B9, 0x31}, +{0x32BB, 0x87}, +{0x32BC, 0x38}, +{0x32BD, 0x3C}, +{0x32BE, 0x34}, +{0x3201, 0x3F}, +{0x3021, 0x06}, +{0x3025, 0x00}, //normal +{0x3400, 0x01}, +{0x3060, 0x01}, +{REGLIST_TAIL, 0x00}, // tail +}; + +static const DRAM_ATTR uint16_t sensor_framesize_QVGA_crop[][2] = { +//[JPEG_320x240_Crop_19.77_19.77_Fps] +{0x3021, 0x00}, +{REG_DLY, 100}, // delay 100ms +{0x32BF, 0x60}, +{0x32C0, 0x5A}, +{0x32C1, 0x5A}, +{0x32C2, 0x5A}, +{0x32C3, 0x00}, +{0x32C4, 0x20}, +{0x32C5, 0x20}, +{0x32C6, 0x20}, +{0x32C7, 0x00}, +{0x32C8, 0x62}, +{0x32C9, 0x5A}, +{0x32CA, 0x7A}, +{0x32CB, 0x7A}, +{0x32CC, 0x7A}, +{0x32CD, 0x7A}, +{0x32DB, 0x68}, +{0x32F0, 0x70}, +{0x3400, 0x08}, +{0x3400, 0x00}, +{0x3401, 0x4E}, +{0x3404, 0x00}, +{0x3405, 0x00}, +{0x3410, 0x00}, +{0x32E0, 0x01}, +{0x32E1, 0x40}, +{0x32E2, 0x00}, +{0x32E3, 0xF0}, +{0x32E4, 0x01}, +{0x32E5, 0x01}, +{0x32E6, 0x01}, +{0x32E7, 0x02}, +{0x3200, 0x3E}, +{0x3201, 0x0F}, +{0x3028, 0x0F}, +{0x3029, 0x00}, +{0x302A, 0x08}, +{0x3022, 0x24}, +{0x3023, 0x24}, +{0x3002, 0x01}, +{0x3003, 0x44}, +{0x3004, 0x00}, +{0x3005, 0x7C}, +{0x3006, 0x03}, +{0x3007, 0xC3}, +{0x3008, 0x02}, +{0x3009, 0x5B}, +{0x300A, 0x03}, +{0x300B, 0xFC}, +{0x300C, 0x01}, +{0x300D, 0xF0}, +{0x300E, 0x02}, +{0x300F, 0x80}, +{0x3010, 0x01}, +{0x3011, 0xE0}, +{0x32B8, 0x3F}, +{0x32B9, 0x31}, +{0x32BB, 0x87}, +{0x32BC, 0x38}, +{0x32BD, 0x3C}, +{0x32BE, 0x34}, +{0x3201, 0x7F}, +{0x3021, 0x06}, +{0x3025, 0x00}, //normal +{0x3400, 0x01}, +{0x3060, 0x01}, +{REGLIST_TAIL, 0x00}, // tail +}; + +#endif + + diff --git a/esp32-cam/ov7670.c b/esp32-cam/ov7670.c new file mode 100644 index 0000000..285fe13 --- /dev/null +++ b/esp32-cam/ov7670.c @@ -0,0 +1,439 @@ +/* + * This file is part of the OpenMV project. + * author: Juan Schiavoni + * This work is licensed under the MIT license, see the file LICENSE for details. + * + * OV7725 driver. + * + */ +#include +#include +#include +#include "sccb.h" +#include "ov7670.h" +#include "ov7670_regs.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include + +#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) +#include "esp32-hal-log.h" +#else +#include "esp_log.h" +static const char* TAG = "ov7760"; +#endif + +static int ov7670_clkrc = 0x01; + +/* + * The default register settings, as obtained from OmniVision. There + * is really no making sense of most of these - lots of "reserved" values + * and such. + * + * These settings give VGA YUYV. + */ +struct regval_list { + uint8_t reg_num; + uint8_t value; +}; + +static struct regval_list ov7670_default_regs[] = { + /* Sensor automatically sets output window when resolution changes. */ + {TSLB, 0x04}, + + /* Frame rate 30 fps at 12 Mhz clock */ + {CLKRC, 0x00}, + {DBLV, 0x4A}, + + {COM10, COM10_VSYNC_NEG | COM10_PCLK_MASK}, + + /* Improve white balance */ + {COM4, 0x40}, + + /* Improve color */ + {RSVD_B0, 0x84}, + + /* Enable 50/60 Hz auto detection */ + {COM11, COM11_EXP|COM11_HZAUTO}, + + /* Disable some delays */ + {HSYST, 0}, + {HSYEN, 0}, + + {MVFP, MVFP_SUN}, + + /* More reserved magic, some of which tweaks white balance */ + {AWBC1, 0x0a}, + {AWBC2, 0xf0}, + {AWBC3, 0x34}, + {AWBC4, 0x58}, + {AWBC5, 0x28}, + {AWBC6, 0x3a}, + + {AWBCTR3, 0x0a}, + {AWBCTR2, 0x55}, + {AWBCTR1, 0x11}, + {AWBCTR0, 0x9e}, + + {COM8, COM8_FAST_AUTO|COM8_STEP_UNLIMIT|COM8_AGC_EN|COM8_AEC_EN|COM8_AWB_EN}, + + /* End marker is FF because in ov7670 the address of GAIN 0 and default value too. */ + {0xFF, 0xFF}, +}; + +static struct regval_list ov7670_fmt_yuv422[] = { + { COM7, 0x0 }, /* Selects YUV mode */ + { RGB444, 0 }, /* No RGB444 please */ + { COM1, 0 }, /* CCIR601 */ + { COM15, COM15_R00FF }, + { MVFP, MVFP_SUN }, + { COM9, 0x6A }, /* 128x gain ceiling; 0x8 is reserved bit */ + { MTX1, 0x80 }, /* "matrix coefficient 1" */ + { MTX2, 0x80 }, /* "matrix coefficient 2" */ + { MTX3, 0 }, /* vb */ + { MTX4, 0x22 }, /* "matrix coefficient 4" */ + { MTX5, 0x5e }, /* "matrix coefficient 5" */ + { MTX6, 0x80 }, /* "matrix coefficient 6" */ + { COM13, COM13_UVSAT }, + { 0xff, 0xff }, /* END MARKER */ +}; + +static struct regval_list ov7670_fmt_rgb565[] = { + { COM7, COM7_FMT_RGB565 }, /* Selects RGB mode */ + { RGB444, 0 }, /* No RGB444 please */ + { COM1, 0x0 }, /* CCIR601 */ + { COM15, COM15_RGB565 |COM15_R00FF }, + { MVFP, MVFP_SUN }, + { COM9, 0x6A }, /* 128x gain ceiling; 0x8 is reserved bit */ + { MTX1, 0xb3 }, /* "matrix coefficient 1" */ + { MTX2, 0xb3 }, /* "matrix coefficient 2" */ + { MTX3, 0 }, /* vb */ + { MTX4, 0x3d }, /* "matrix coefficient 4" */ + { MTX5, 0xa7 }, /* "matrix coefficient 5" */ + { MTX6, 0xe4 }, /* "matrix coefficient 6" */ + { COM13, COM13_UVSAT }, + { 0xff, 0xff }, /* END MARKER */ +}; + + +static struct regval_list ov7670_vga[] = { + { COM3, 0x00 }, + { COM14, 0x00 }, + { SCALING_XSC, 0x3A }, + { SCALING_YSC, 0x35 }, + { SCALING_DCWCTR, 0x11 }, + { SCALING_PCLK_DIV, 0xF0 }, + { SCALING_PCLK_DELAY, 0x02 }, + { 0xff, 0xff }, +}; + +static struct regval_list ov7670_qvga[] = { + { COM3, 0x04 }, + { COM14, 0x19 }, + { SCALING_XSC, 0x3A }, + { SCALING_YSC, 0x35 }, + { SCALING_DCWCTR, 0x11 }, + { SCALING_PCLK_DIV, 0xF1 }, + { SCALING_PCLK_DELAY, 0x02 }, + { 0xff, 0xff }, +}; + +static struct regval_list ov7670_qqvga[] = { + { COM3, 0x04 }, //DCW enable + { COM14, 0x1a }, //pixel clock divided by 4, manual scaling enable, DCW and PCLK controlled by register + { SCALING_XSC, 0x3a }, + { SCALING_YSC, 0x35 }, + { SCALING_DCWCTR, 0x22 }, //downsample by 4 + { SCALING_PCLK_DIV, 0xf2 }, //pixel clock divided by 4 + { SCALING_PCLK_DELAY, 0x02 }, + { 0xff, 0xff }, +}; + +/* + * Write a list of register settings; ff/ff stops the process. + */ +static int ov7670_write_array(sensor_t *sensor, struct regval_list *vals) +{ +int ret = 0; + + while ( (vals->reg_num != 0xff || vals->value != 0xff) && (ret == 0) ) { + ret = SCCB_Write(sensor->slv_addr, vals->reg_num, vals->value); + + ESP_LOGD(TAG, "reset reg %02X, W(%02X) R(%02X)", vals->reg_num, + vals->value, SCCB_Read(sensor->slv_addr, vals->reg_num) ); + + vals++; + } + + return ret; +} + +/* + * Calculate the frame control registers. + */ +static int ov7670_frame_control(sensor_t *sensor, int hstart, int hstop, int vstart, int vstop) +{ +struct regval_list frame[7]; + + frame[0].reg_num = HSTART; + frame[0].value = (hstart >> 3); + + frame[1].reg_num = HSTOP; + frame[1].value = (hstop >> 3); + + frame[2].reg_num = HREF; + frame[2].value = (((hstop & 0x07) << 3) | (hstart & 0x07)); + + frame[3].reg_num = VSTART; + frame[3].value = (vstart >> 2); + + frame[4].reg_num = VSTOP; + frame[4].value = (vstop >> 2); + + frame[5].reg_num = VREF; + frame[5].value = (((vstop & 0x02) << 2) | (vstart & 0x02)); + + /* End mark */ + frame[5].reg_num = 0xFF; + frame[5].value = 0xFF; + + return ov7670_write_array(sensor, frame); +} + +static int reset(sensor_t *sensor) +{ + int ret; + + // Reset all registers + SCCB_Write(sensor->slv_addr, COM7, COM7_RESET); + + // Delay 10 ms + vTaskDelay(10 / portTICK_PERIOD_MS); + + ret = ov7670_write_array(sensor, ov7670_default_regs); + + // Delay + vTaskDelay(30 / portTICK_PERIOD_MS); + + return ret; +} + +static int set_pixformat(sensor_t *sensor, pixformat_t pixformat) +{ +int ret; + + switch (pixformat) { + case PIXFORMAT_RGB565: + case PIXFORMAT_RGB888: + ret = ov7670_write_array(sensor, ov7670_fmt_rgb565); + break; + + case PIXFORMAT_YUV422: + case PIXFORMAT_GRAYSCALE: + default: + ret = ov7670_write_array(sensor, ov7670_fmt_yuv422); + break; + } + + vTaskDelay(30 / portTICK_PERIOD_MS); + + /* + * If we're running RGB565, we must rewrite clkrc after setting + * the other parameters or the image looks poor. If we're *not* + * doing RGB565, we must not rewrite clkrc or the image looks + * *really* poor. + * + * (Update) Now that we retain clkrc state, we should be able + * to write it unconditionally, and that will make the frame + * rate persistent too. + */ + if (pixformat == PIXFORMAT_RGB565) { + ret = SCCB_Write(sensor->slv_addr, CLKRC, ov7670_clkrc); + } + + return ret; +} + +static int set_framesize(sensor_t *sensor, framesize_t framesize) +{ + int ret; + + // store clkrc before changing window settings... + ov7670_clkrc = SCCB_Read(sensor->slv_addr, CLKRC); + + switch (framesize){ + case FRAMESIZE_VGA: + if( (ret = ov7670_write_array(sensor, ov7670_vga)) == 0 ) { + /* These values from Omnivision */ + ret = ov7670_frame_control(sensor, 158, 14, 10, 490); + } + break; + case FRAMESIZE_QVGA: + if( (ret = ov7670_write_array(sensor, ov7670_qvga)) == 0 ) { + /* These values from Omnivision */ + ret = ov7670_frame_control(sensor, 158, 14, 10, 490); + } + break; + case FRAMESIZE_QQVGA: + if( (ret = ov7670_write_array(sensor, ov7670_qqvga)) == 0 ) { + /* These values from Omnivision */ + ret = ov7670_frame_control(sensor, 158, 14, 10, 490); + } + break; + + default: + ret = -1; + } + + vTaskDelay(30 / portTICK_PERIOD_MS); + + if (ret == 0) { + sensor->status.framesize = framesize; + } + + return ret; +} + +static int set_colorbar(sensor_t *sensor, int enable) +{ + uint8_t ret = 0; + // Read register scaling_xsc + uint8_t reg = SCCB_Read(sensor->slv_addr, SCALING_XSC); + + // Pattern to set color bar bit[0]=0 in every case + reg = SCALING_XSC_CBAR(reg); + + // Write pattern to SCALING_XSC + ret = SCCB_Write(sensor->slv_addr, SCALING_XSC, reg); + + // Read register scaling_ysc + reg = SCCB_Read(sensor->slv_addr, SCALING_YSC); + + // Pattern to set color bar bit[0]=0 in every case + reg = SCALING_YSC_CBAR(reg, enable); + + // Write pattern to SCALING_YSC + ret = ret | SCCB_Write(sensor->slv_addr, SCALING_YSC, reg); + + // return 0 or 0xFF + return ret; +} + +static int set_whitebal(sensor_t *sensor, int enable) +{ + // Read register COM8 + uint8_t reg = SCCB_Read(sensor->slv_addr, COM8); + + // Set white bal on/off + reg = COM8_SET_AWB(reg, enable); + + // Write back register COM8 + return SCCB_Write(sensor->slv_addr, COM8, reg); +} + +static int set_gain_ctrl(sensor_t *sensor, int enable) +{ + // Read register COM8 + uint8_t reg = SCCB_Read(sensor->slv_addr, COM8); + + // Set white bal on/off + reg = COM8_SET_AGC(reg, enable); + + // Write back register COM8 + return SCCB_Write(sensor->slv_addr, COM8, reg); +} + +static int set_exposure_ctrl(sensor_t *sensor, int enable) +{ + // Read register COM8 + uint8_t reg = SCCB_Read(sensor->slv_addr, COM8); + + // Set white bal on/off + reg = COM8_SET_AEC(reg, enable); + + // Write back register COM8 + return SCCB_Write(sensor->slv_addr, COM8, reg); +} + +static int set_hmirror(sensor_t *sensor, int enable) +{ + // Read register MVFP + uint8_t reg = SCCB_Read(sensor->slv_addr, MVFP); + + // Set mirror on/off + reg = MVFP_SET_MIRROR(reg, enable); + + // Write back register MVFP + return SCCB_Write(sensor->slv_addr, MVFP, reg); +} + +static int set_vflip(sensor_t *sensor, int enable) +{ + // Read register MVFP + uint8_t reg = SCCB_Read(sensor->slv_addr, MVFP); + + // Set mirror on/off + reg = MVFP_SET_FLIP(reg, enable); + + // Write back register MVFP + return SCCB_Write(sensor->slv_addr, MVFP, reg); +} + +static int init_status(sensor_t *sensor) +{ + sensor->status.awb = 0; + sensor->status.aec = 0; + sensor->status.agc = 0; + sensor->status.hmirror = 0; + sensor->status.vflip = 0; + sensor->status.colorbar = 0; + return 0; +} + +static int set_dummy(sensor_t *sensor, int val){ return -1; } +static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val){ return -1; } + +int ov7670_init(sensor_t *sensor) +{ + // Set function pointers + sensor->reset = reset; + sensor->init_status = init_status; + sensor->set_pixformat = set_pixformat; + sensor->set_framesize = set_framesize; + sensor->set_colorbar = set_colorbar; + sensor->set_whitebal = set_whitebal; + sensor->set_gain_ctrl = set_gain_ctrl; + sensor->set_exposure_ctrl = set_exposure_ctrl; + sensor->set_hmirror = set_hmirror; + sensor->set_vflip = set_vflip; + + //not supported + sensor->set_brightness= set_dummy; + sensor->set_saturation= set_dummy; + sensor->set_quality = set_dummy; + sensor->set_gainceiling = set_gainceiling_dummy; + sensor->set_aec2 = set_dummy; + sensor->set_aec_value = set_dummy; + sensor->set_special_effect = set_dummy; + sensor->set_wb_mode = set_dummy; + sensor->set_ae_level = set_dummy; + sensor->set_dcw = set_dummy; + sensor->set_bpc = set_dummy; + sensor->set_wpc = set_dummy; + sensor->set_awb_gain = set_dummy; + sensor->set_agc_gain = set_dummy; + sensor->set_raw_gma = set_dummy; + sensor->set_lenc = set_dummy; + sensor->set_sharpness = set_dummy; + sensor->set_denoise = set_dummy; + + // Retrieve sensor's signature + sensor->id.MIDH = SCCB_Read(sensor->slv_addr, REG_MIDH); + sensor->id.MIDL = SCCB_Read(sensor->slv_addr, REG_MIDL); + sensor->id.PID = SCCB_Read(sensor->slv_addr, REG_PID); + sensor->id.VER = SCCB_Read(sensor->slv_addr, REG_VER); + + ESP_LOGD(TAG, "OV7670 Attached"); + + return 0; +} diff --git a/esp32-cam/ov7670.h b/esp32-cam/ov7670.h new file mode 100644 index 0000000..cdf845c --- /dev/null +++ b/esp32-cam/ov7670.h @@ -0,0 +1,14 @@ +/* + * This file is part of the OpenMV project. + * author: Juan Schiavoni + * This work is licensed under the MIT license, see the file LICENSE for details. + * + * OV7670 driver. + * + */ +#ifndef __OV7670_H__ +#define __OV7670_H__ +#include "sensor.h" + +int ov7670_init(sensor_t *sensor); +#endif // __OV7670_H__ diff --git a/esp32-cam/ov7670_regs.h b/esp32-cam/ov7670_regs.h new file mode 100644 index 0000000..6993548 --- /dev/null +++ b/esp32-cam/ov7670_regs.h @@ -0,0 +1,354 @@ +/* + * This file is for the OpenMV project so the OV7670 can be used + * author: Juan Schiavoni + * + * OV7670 register definitions. + */ +#ifndef __OV7670_REG_REGS_H__ +#define __OV7670_REG_REGS_H__ +#define GAIN 0x00 /* AGC – Gain control gain setting */ +#define BLUE 0x01 /* AWB – Blue channel gain setting */ +#define RED 0x02 /* AWB – Red channel gain setting */ +#define VREF 0x03 /* AWB – Green channel gain setting */ +#define COM1 0x04 /* Common Control 1 */ +#define BAVG 0x05 /* U/B Average Level */ +#define GAVG 0x06 /* Y/Gb Average Level */ +#define AECH 0x07 /* Exposure VAlue - AEC MSB 5 bits */ +#define RAVG 0x08 /* V/R Average Level */ + +#define COM2 0x09 /* Common Control 2 */ +#define COM2_SOFT_SLEEP 0x10 /* Soft sleep mode */ +#define COM2_OUT_DRIVE_1x 0x00 /* Output drive capability 1x */ +#define COM2_OUT_DRIVE_2x 0x01 /* Output drive capability 2x */ +#define COM2_OUT_DRIVE_3x 0x02 /* Output drive capability 3x */ +#define COM2_OUT_DRIVE_4x 0x03 /* Output drive capability 4x */ + +#define REG_PID 0x0A /* Product ID Number MSB */ +#define REG_VER 0x0B /* Product ID Number LSB */ + +#define COM3 0x0C /* Common Control 3 */ +#define COM3_SWAP_OUT 0x40 /* Output data MSB/LSB swap */ +#define COM3_TRI_CLK 0x20 /* Tri-state output clock */ +#define COM3_TRI_DATA 0x10 /* Tri-state option output */ +#define COM3_SCALE_EN 0x08 /* Scale enable */ +#define COM3_DCW 0x04 /* DCW enable */ + +#define COM4 0x0D /* Common Control 4 */ +#define COM4_PLL_BYPASS 0x00 /* Bypass PLL */ +#define COM4_PLL_4x 0x40 /* PLL frequency 4x */ +#define COM4_PLL_6x 0x80 /* PLL frequency 6x */ +#define COM4_PLL_8x 0xc0 /* PLL frequency 8x */ +#define COM4_AEC_FULL 0x00 /* AEC evaluate full window */ +#define COM4_AEC_1_2 0x10 /* AEC evaluate 1/2 window */ +#define COM4_AEC_1_4 0x20 /* AEC evaluate 1/4 window */ +#define COM4_AEC_2_3 0x30 /* AEC evaluate 2/3 window */ + +#define COM5 0x0E /* Common Control 5 */ +#define COM5_AFR 0x80 /* Auto frame rate control ON/OFF selection (night mode) */ +#define COM5_AFR_SPEED 0x40 /* Auto frame rate control speed selection */ +#define COM5_AFR_0 0x00 /* No reduction of frame rate */ +#define COM5_AFR_1_2 0x10 /* Max reduction to 1/2 frame rate */ +#define COM5_AFR_1_4 0x20 /* Max reduction to 1/4 frame rate */ +#define COM5_AFR_1_8 0x30 /* Max reduction to 1/8 frame rate */ +#define COM5_AFR_4x 0x04 /* Add frame when AGC reaches 4x gain */ +#define COM5_AFR_8x 0x08 /* Add frame when AGC reaches 8x gain */ +#define COM5_AFR_16x 0x0c /* Add frame when AGC reaches 16x gain */ +#define COM5_AEC_NO_LIMIT 0x01 /* No limit to AEC increase step */ + +#define COM6 0x0F /* Common Control 6 */ +#define COM6_AUTO_WINDOW 0x01 /* Auto window setting ON/OFF selection when format changes */ + +#define AEC 0x10 /* AEC[7:0] (see register AECH for AEC[15:8]) */ +#define CLKRC 0x11 /* Internal Clock */ + +#define COM7 0x12 /* Common Control 7 */ +#define COM7_RESET 0x80 /* SCCB Register Reset */ +#define COM7_RES_VGA 0x00 /* Resolution VGA */ +#define COM7_RES_QVGA 0x40 /* Resolution QVGA */ +#define COM7_BT656 0x20 /* BT.656 protocol ON/OFF */ +#define COM7_SENSOR_RAW 0x10 /* Sensor RAW */ +#define COM7_FMT_GBR422 0x00 /* RGB output format GBR422 */ +#define COM7_FMT_RGB565 0x04 /* RGB output format RGB565 */ +#define COM7_FMT_RGB555 0x08 /* RGB output format RGB555 */ +#define COM7_FMT_RGB444 0x0C /* RGB output format RGB444 */ +#define COM7_FMT_YUV 0x00 /* Output format YUV */ +#define COM7_FMT_P_BAYER 0x01 /* Output format Processed Bayer RAW */ +#define COM7_FMT_RGB 0x04 /* Output format RGB */ +#define COM7_FMT_R_BAYER 0x03 /* Output format Bayer RAW */ +#define COM7_SET_FMT(r, x) ((r&0xFC)|((x&0x5)<<0)) + +#define COM8 0x13 /* Common Control 8 */ +#define COM8_FAST_AUTO 0x80 /* Enable fast AGC/AEC algorithm */ +#define COM8_STEP_VSYNC 0x00 /* AEC - Step size limited to vertical blank */ +#define COM8_STEP_UNLIMIT 0x40 /* AEC - Step size unlimited step size */ +#define COM8_BANDF_EN 0x20 /* Banding filter ON/OFF */ +#define COM8_AEC_BANDF 0x10 /* Enable AEC below banding value */ +#define COM8_AEC_FINE_EN 0x08 /* Fine AEC ON/OFF control */ +#define COM8_AGC_EN 0x04 /* AGC Enable */ +#define COM8_AWB_EN 0x02 /* AWB Enable */ +#define COM8_AEC_EN 0x01 /* AEC Enable */ +#define COM8_SET_AGC(r, x) ((r&0xFB)|((x&0x1)<<2)) +#define COM8_SET_AWB(r, x) ((r&0xFD)|((x&0x1)<<1)) +#define COM8_SET_AEC(r, x) ((r&0xFE)|((x&0x1)<<0)) + +#define COM9 0x14 /* Common Control 9 */ +#define COM9_HISTO_AVG 0x80 /* Histogram or average based AEC/AGC selection */ +#define COM9_AGC_GAIN_2x 0x00 /* Automatic Gain Ceiling 2x */ +#define COM9_AGC_GAIN_4x 0x10 /* Automatic Gain Ceiling 4x */ +#define COM9_AGC_GAIN_8x 0x20 /* Automatic Gain Ceiling 8x */ +#define COM9_AGC_GAIN_16x 0x30 /* Automatic Gain Ceiling 16x */ +#define COM9_AGC_GAIN_32x 0x40 /* Automatic Gain Ceiling 32x */ +#define COM9_DROP_VSYNC 0x04 /* Drop VSYNC output of corrupt frame */ +#define COM9_DROP_HREF 0x02 /* Drop HREF output of corrupt frame */ +#define COM9_SET_AGC(r, x) ((r&0x8F)|((x&0x07)<<4)) + +#define COM10 0x15 /* Common Control 10 */ +#define COM10_NEGATIVE 0x80 /* Output negative data */ +#define COM10_HSYNC_EN 0x40 /* HREF changes to HSYNC */ +#define COM10_PCLK_FREE 0x00 /* PCLK output option: free running PCLK */ +#define COM10_PCLK_MASK 0x20 /* PCLK output option: masked during horizontal blank */ +#define COM10_PCLK_REV 0x10 /* PCLK reverse */ +#define COM10_HREF_REV 0x08 /* HREF reverse */ +#define COM10_VSYNC_FALLING 0x00 /* VSYNC changes on falling edge of PCLK */ +#define COM10_VSYNC_RISING 0x04 /* VSYNC changes on rising edge of PCLK */ +#define COM10_VSYNC_NEG 0x02 /* VSYNC negative */ +#define COM10_OUT_RANGE_8 0x01 /* Output data range: Full range */ +#define COM10_OUT_RANGE_10 0x00 /* Output data range: Data from [10] to [F0] (8 MSBs) */ + +#define RSVD_16 0x16 /* Reserved register */ + +#define HSTART 0x17 /* Horizontal Frame (HREF column) Start high 8-bit(low 3 bits are at HREF[2:0]) */ +#define HSTOP 0x18 /* Horizontal Frame (HREF column) end high 8-bit (low 3 bits are at HREF[5:3]) */ +#define VSTART 0x19 /* Vertical Frame (row) Start high 8-bit (low 2 bits are at VREF[1:0]) */ +#define VSTOP 0x1A /* Vertical Frame (row) End high 8-bit (low 2 bits are at VREF[3:2]) */ +#define PSHFT 0x1B /* Data Format - Pixel Delay Select */ +#define REG_MIDH 0x1C /* Manufacturer ID Byte – High */ +#define REG_MIDL 0x1D /* Manufacturer ID Byte – Low */ + +#define MVFP 0x1E /* Mirror/Vflip Enable */ +#define MVFP_MIRROR 0x20 /* Mirror image */ +#define MVFP_FLIP 0x10 /* Vertical flip */ +#define MVFP_SUN 0x02 /* Black sun enable */ +#define MVFP_SET_MIRROR(r,x) ((r&0xDF)|((x&1)<<5)) /* change only bit5 according to x */ +#define MVFP_SET_FLIP(r,x) ((r&0xEF)|((x&1)<<4)) /* change only bit4 according to x */ + +#define LAEC 0x1F /* Fine AEC Value - defines exposure value less than one row period (Reserved?) */ +#define ADCCTR0 0x20 /* ADC control */ +#define ADCCTR1 0x21 /* reserved */ +#define ADCCTR2 0x22 /* reserved */ +#define ADCCTR3 0x23 /* reserved */ +#define AEW 0x24 /* AGC/AEC - Stable Operating Region (Upper Limit) */ +#define AEB 0x25 /* AGC/AEC - Stable Operating Region (Lower Limit) */ +#define VPT 0x26 /* AGC/AEC Fast Mode Operating Region */ +#define BBIAS 0x27 /* B channel signal output bias (effective only when COM6[3]=1) */ +#define GbBIAS 0x28 /* Gb channel signal output bias (effective only when COM6[3]=1) */ +#define RSVD_29 0x29 /* reserved */ +#define EXHCH 0x2A /* Dummy Pixel Insert MSB */ +#define EXHCL 0x2B /* Dummy Pixel Insert LSB */ +#define RBIAS 0x2C /* R channel signal output bias (effective only when COM6[3]=1) */ +#define ADVFL 0x2D /* LSB of Insert Dummy Rows in Vertical Sync (1 bit equals 1 row) */ +#define ADVFH 0x2E /* MSB of Insert Dummy Rows in Vertical Sync */ +#define YAVE 0x2F /* Y/G Channel Average Value */ +#define HSYST 0x30 /* HSync rising edge delay */ +#define HSYEN 0x31 /* HSync falling edge delay */ +#define HREF 0x32 /* Image Start and Size Control DIFFERENT CONTROL SEQUENCE */ +#define CHLF 0x33 /* Array Current control */ +#define ARBLM 0x34 /* Array reference control */ +#define RSVD_35 0x35 /* Reserved */ +#define RSVD_36 0x36 /* Reserved */ +#define ADC 0x37 /* ADC control */ +#define ACOM 0x38 /* ADC and analog common mode control */ +#define OFON 0x39 /* ADC offset control */ +#define TSLB 0x3A /* Line buffer test option */ + +#define COM11 0x3B /* Common control 11 */ +#define COM11_EXP 0x02 +#define COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */ + +#define COM12 0x3C /* Common control 12 */ + +#define COM13 0x3D /* Common control 13 */ +#define COM13_GAMMA 0x80 /* Gamma enable */ +#define COM13_UVSAT 0x40 /* UV saturation auto adjustment */ + +#define COM14 0x3E /* Common Control 14 */ + +#define EDGE 0x3F /* edge enhancement adjustment */ +#define COM15 0x40 /* Common Control 15 DIFFERENT CONTROLS */ +#define COM15_SET_RGB565(r,x) ((r&0xEF)|((x&1)<<4)) /* set rgb565 mode */ +#define COM15_RGB565 0x10 /* RGB565 output */ +#define COM15_R00FF 0xC0 /* Output range: [00] to [FF] */ + +#define COM16 0x41 /* Common Control 16 DIFFERENT CONTROLS */ +#define COM16_AWBGAIN 0x08 /* AWB gain enable */ +#define COM17 0x42 /* Common Control 17 */ + +#define AWBC1 0x43 /* Reserved */ +#define AWBC2 0x44 /* Reserved */ +#define AWBC3 0x45 /* Reserved */ +#define AWBC4 0x46 /* Reserved */ +#define AWBC5 0x47 /* Reserved */ +#define AWBC6 0x48 /* Reserved */ + +#define RSVD_49 0x49 /* Reserved */ +#define RSVD_4A 0x4A /* Reserved */ + +#define REG4B 0x4B /* Register 4B */ +#define DNSTH 0x4C /* Denoise strength */ + +#define RSVD_4D 0x4D /* Reserved */ +#define RSVD_4E 0x4E /* Reserved */ + +#define MTX1 0x4F /* Matrix coefficient 1 */ +#define MTX2 0x50 /* Matrix coefficient 2 */ +#define MTX3 0x51 /* Matrix coefficient 3 */ +#define MTX4 0x52 /* Matrix coefficient 4 */ +#define MTX5 0x53 /* Matrix coefficient 5 */ +#define MTX6 0x54 /* Matrix coefficient 6 */ +#define BRIGHTNESS 0x55 /* Brightness control */ +#define CONTRAST 0x56 /* Contrast control */ +#define CONTRASCENTER 0x57 /* Contrast center */ +#define MTXS 0x58 /* Matrix coefficient sign for coefficient 5 to 0*/ + +#define RSVD_59 0x59 /* Reserved */ +#define RSVD_5A 0x5A /* Reserved */ +#define RSVD_5B 0x5B /* Reserved */ +#define RSVD_5C 0x5C /* Reserved */ +#define RSVD_5D 0x5D /* Reserved */ +#define RSVD_5E 0x5E /* Reserved */ +#define RSVD_5F 0x5F /* Reserved */ +#define RSVD_60 0x60 /* Reserved */ +#define RSVD_61 0x61 /* Reserved */ + +#define LCC1 0x62 /* Lens correction option 1 */ + +#define LCC2 0x63 /* Lens correction option 2 */ +#define LCC3 0x64 /* Lens correction option 3 */ +#define LCC4 0x65 /* Lens correction option 4 */ +#define LCC5 0x66 /* Lens correction option 5 */ + +#define MANU 0x67 /* Manual U Value */ +#define MANV 0x68 /* Manual V Value */ +#define GFIX 0x69 /* Fix gain control */ +#define GGAIN 0x6A /* G channel AWB gain */ + +#define DBLV 0x6B /* PLL and clock ? */ + +#define AWBCTR3 0x6C /* AWB Control 3 */ +#define AWBCTR2 0x6D /* AWB Control 2 */ +#define AWBCTR1 0x6E /* AWB Control 1 */ +#define AWBCTR0 0x6F /* AWB Control 0 */ +#define SCALING_XSC 0x70 /* test pattern and horizontal scaling factor */ +#define SCALING_XSC_CBAR(r) (r&0x7F) /* make sure bit7 is 0 for color bar */ +#define SCALING_YSC 0x71 /* test pattern and vertical scaling factor */ +#define SCALING_YSC_CBAR(r,x) ((r&0x7F)|((x&1)<<7)) /* change bit7 for color bar on/off */ +#define SCALING_DCWCTR 0x72 /* DCW control */ +#define SCALING_PCLK_DIV 0x73 /* */ +#define REG74 0x74 /* */ +#define REG75 0x75 /* */ +#define REG76 0x76 /* */ +#define REG77 0x77 /* */ + +#define RSVD_78 0x78 /* Reserved */ +#define RSVD_79 0x79 /* Reserved */ + +#define SLOP 0x7A /* Gamma curve highest segment slope */ +#define GAM1 0x7B /* Gamma Curve 1st Segment Input End Point 0x04 Output Value */ +#define GAM2 0x7C /* Gamma Curve 2nd Segment Input End Point 0x08 Output Value */ +#define GAM3 0x7D /* Gamma Curve 3rd Segment Input End Point 0x10 Output Value */ +#define GAM4 0x7E /* Gamma Curve 4th Segment Input End Point 0x20 Output Value */ +#define GAM5 0x7F /* Gamma Curve 5th Segment Input End Point 0x28 Output Value */ +#define GAM6 0x80 /* Gamma Curve 6rd Segment Input End Point 0x30 Output Value */ +#define GAM7 0x81 /* Gamma Curve 7th Segment Input End Point 0x38 Output Value */ +#define GAM8 0x82 /* Gamma Curve 8th Segment Input End Point 0x40 Output Value */ +#define GAM9 0x83 /* Gamma Curve 9th Segment Input End Point 0x48 Output Value */ +#define GAM10 0x84 /* Gamma Curve 10th Segment Input End Point 0x50 Output Value */ +#define GAM11 0x85 /* Gamma Curve 11th Segment Input End Point 0x60 Output Value */ +#define GAM12 0x86 /* Gamma Curve 12th Segment Input End Point 0x70 Output Value */ +#define GAM13 0x87 /* Gamma Curve 13th Segment Input End Point 0x90 Output Value */ +#define GAM14 0x88 /* Gamma Curve 14th Segment Input End Point 0xB0 Output Value */ +#define GAM15 0x89 /* Gamma Curve 15th Segment Input End Point 0xD0 Output Value */ + +#define RSVD_8A 0x8A /* Reserved */ +#define RSVD_8B 0x8B /* Reserved */ + +#define RGB444 0x8C /* */ + +#define RSVD_8D 0x8D /* Reserved */ +#define RSVD_8E 0x8E /* Reserved */ +#define RSVD_8F 0x8F /* Reserved */ +#define RSVD_90 0x90 /* Reserved */ +#define RSVD_91 0x91 /* Reserved */ + +#define DM_LNL 0x92 /* Dummy line low 8 bit */ +#define DM_LNH 0x93 /* Dummy line high 8 bit */ +#define LCC6 0x94 /* Lens correction option 6 */ +#define LCC7 0x95 /* Lens correction option 7 */ + +#define RSVD_96 0x96 /* Reserved */ +#define RSVD_97 0x97 /* Reserved */ +#define RSVD_98 0x98 /* Reserved */ +#define RSVD_99 0x99 /* Reserved */ +#define RSVD_9A 0x9A /* Reserved */ +#define RSVD_9B 0x9B /* Reserved */ +#define RSVD_9C 0x9C /* Reserved */ + +#define BD50ST 0x9D /* 50 Hz banding filter value */ +#define BD60ST 0x9E /* 60 Hz banding filter value */ +#define HAECC1 0x9F /* Histogram-based AEC/AGC control 1 */ +#define HAECC2 0xA0 /* Histogram-based AEC/AGC control 2 */ + +#define RSVD_A1 0xA1 /* Reserved */ + +#define SCALING_PCLK_DELAY 0xA2 /* Pixel clock delay */ + +#define RSVD_A3 0xA3 /* Reserved */ + +#define NT_CNTRL 0xA4 /* */ +#define BD50MAX 0xA5 /* 50 Hz banding step limit */ +#define HAECC3 0xA6 /* Histogram-based AEC/AGC control 3 */ +#define HAECC4 0xA7 /* Histogram-based AEC/AGC control 4 */ +#define HAECC5 0xA8 /* Histogram-based AEC/AGC control 5 */ +#define HAECC6 0xA9 /* Histogram-based AEC/AGC control 6 */ + +#define HAECC7 0xAA /* Histogram-based AEC/AGC control 7 */ +#define HAECC_EN 0x80 /* Histogram-based AEC algorithm enable */ + +#define BD60MAX 0xAB /* 60 Hz banding step limit */ + +#define STR_OPT 0xAC /* Register AC */ +#define STR_R 0xAD /* R gain for led output frame */ +#define STR_G 0xAE /* G gain for led output frame */ +#define STR_B 0xAF /* B gain for led output frame */ +#define RSVD_B0 0xB0 /* Reserved */ +#define ABLC1 0xB1 /* */ +#define RSVD_B2 0xB2 /* Reserved */ +#define THL_ST 0xB3 /* ABLC target */ +#define THL_DLT 0xB5 /* ABLC stable range */ + +#define RSVD_B6 0xB6 /* Reserved */ +#define RSVD_B7 0xB7 /* Reserved */ +#define RSVD_B8 0xB8 /* Reserved */ +#define RSVD_B9 0xB9 /* Reserved */ +#define RSVD_BA 0xBA /* Reserved */ +#define RSVD_BB 0xBB /* Reserved */ +#define RSVD_BC 0xBC /* Reserved */ +#define RSVD_BD 0xBD /* Reserved */ + +#define AD_CHB 0xBE /* blue channel black level compensation */ +#define AD_CHR 0xBF /* Red channel black level compensation */ +#define AD_CHGb 0xC0 /* Gb channel black level compensation */ +#define AD_CHGr 0xC1 /* Gr channel black level compensation */ + +#define RSVD_C2 0xC2 /* Reserved */ +#define RSVD_C3 0xC3 /* Reserved */ +#define RSVD_C4 0xC4 /* Reserved */ +#define RSVD_C5 0xC5 /* Reserved */ +#define RSVD_C6 0xC6 /* Reserved */ +#define RSVD_C7 0xC7 /* Reserved */ +#define RSVD_C8 0xC8 /* Reserved */ + +#define SATCTR 0xC9 /* Saturation control */ +#define SET_REG(reg, x) (##reg_DEFAULT|x) + +#endif //__OV7670_REG_REGS_H__ diff --git a/esp32-cam/ov7725.c b/esp32-cam/ov7725.c index b4e6710..bb31573 100644 --- a/esp32-cam/ov7725.c +++ b/esp32-cam/ov7725.c @@ -121,6 +121,51 @@ static const uint8_t default_regs[][2] = { {0x00, 0x00}, }; +static int get_reg(sensor_t *sensor, int reg, int mask) +{ + int ret = SCCB_Read(sensor->slv_addr, 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 = SCCB_Read(sensor->slv_addr, reg & 0xFF); + if(ret < 0){ + return ret; + } + value = (ret & ~mask) | (value & mask); + ret = SCCB_Write(sensor->slv_addr, reg & 0xFF, value); + return ret; +} + +static int set_reg_bits(sensor_t *sensor, uint8_t reg, uint8_t offset, uint8_t length, uint8_t value) +{ + int ret = 0; + ret = SCCB_Read(sensor->slv_addr, reg); + if(ret < 0){ + return ret; + } + uint8_t mask = ((1 << length) - 1) << offset; + value = (ret & ~mask) | ((value << offset) & mask); + ret = SCCB_Write(sensor->slv_addr, reg & 0xFF, value); + return ret; +} + +static int get_reg_bits(sensor_t *sensor, uint8_t reg, uint8_t offset, uint8_t length) +{ + int ret = 0; + ret = SCCB_Read(sensor->slv_addr, reg); + if(ret < 0){ + return ret; + } + uint8_t mask = ((1 << length) - 1) << offset; + return (ret & mask) >> offset; +} + static int reset(sensor_t *sensor) { @@ -176,6 +221,9 @@ static int set_pixformat(sensor_t *sensor, pixformat_t pixformat) static int set_framesize(sensor_t *sensor, framesize_t framesize) { int ret=0; + if (framesize > FRAMESIZE_VGA) { + return -1; + } uint16_t w = resolution[framesize].width; uint16_t h = resolution[framesize].height; uint8_t reg = SCCB_Read(sensor->slv_addr, COM7); @@ -251,82 +299,208 @@ static int set_colorbar(sensor_t *sensor, int enable) static int set_whitebal(sensor_t *sensor, int enable) { - // Read register COM8 - uint8_t reg = SCCB_Read(sensor->slv_addr, COM8); + if(set_reg_bits(sensor, COM8, 1, 1, enable) >= 0){ + sensor->status.awb = !!enable; + } + return sensor->status.awb; +} - sensor->status.awb = enable; - // Set white bal on/off - reg = COM8_SET_AWB(reg, enable); +static int set_gain_ctrl(sensor_t *sensor, int enable) +{ + if(set_reg_bits(sensor, COM8, 2, 1, enable) >= 0){ + sensor->status.agc = !!enable; + } + return sensor->status.agc; +} - // Write back register COM8 - return SCCB_Write(sensor->slv_addr, COM8, reg); +static int set_exposure_ctrl(sensor_t *sensor, int enable) +{ + if(set_reg_bits(sensor, COM8, 0, 1, enable) >= 0){ + sensor->status.aec = !!enable; + } + return sensor->status.aec; } -static int set_gain_ctrl(sensor_t *sensor, int enable) +static int set_hmirror(sensor_t *sensor, int enable) { - sensor->status.agc = enable; - // Read register COM8 - uint8_t reg = SCCB_Read(sensor->slv_addr, COM8); + if(set_reg_bits(sensor, COM3, 6, 1, enable) >= 0){ + sensor->status.hmirror = !!enable; + } + return sensor->status.hmirror; +} - // Set white bal on/off - reg = COM8_SET_AGC(reg, enable); +static int set_vflip(sensor_t *sensor, int enable) +{ + if(set_reg_bits(sensor, COM3, 7, 1, enable) >= 0){ + sensor->status.vflip = !!enable; + } + return sensor->status.vflip; +} - // Write back register COM8 - return SCCB_Write(sensor->slv_addr, COM8, reg); +static int set_dcw_dsp(sensor_t *sensor, int enable) +{ + int ret = 0; + ret = set_reg_bits(sensor, 0x65, 2, 1, !enable); + if (ret == 0) { + ESP_LOGD(TAG, "Set dcw to: %d", enable); + sensor->status.dcw = enable; + } + return ret; } -static int set_exposure_ctrl(sensor_t *sensor, int enable) +static int set_aec2(sensor_t *sensor, int enable) { - sensor->status.aec = enable; - // Read register COM8 - uint8_t reg = SCCB_Read(sensor->slv_addr, COM8); + int ret = 0; + ret = set_reg_bits(sensor, COM8, 7, 1, enable); + if (ret == 0) { + ESP_LOGD(TAG, "Set aec2 to: %d", enable); + sensor->status.aec2 = enable; + } + return ret; +} - // Set white bal on/off - reg = COM8_SET_AEC(reg, enable); +static int set_bpc_dsp(sensor_t *sensor, int enable) +{ + int ret = 0; + ret = set_reg_bits(sensor, 0x64, 1, 1, enable); + if (ret == 0) { + ESP_LOGD(TAG, "Set bpc to: %d", enable); + sensor->status.bpc = enable; + } + return ret; +} - // Write back register COM8 - return SCCB_Write(sensor->slv_addr, COM8, reg); +static int set_wpc_dsp(sensor_t *sensor, int enable) +{ + int ret = 0; + ret = set_reg_bits(sensor, 0x64, 0, 1, enable); + if (ret == 0) { + ESP_LOGD(TAG, "Set wpc to: %d", enable); + sensor->status.wpc = enable; + } + return ret; } -static int set_hmirror(sensor_t *sensor, int enable) +static int set_raw_gma_dsp(sensor_t *sensor, int enable) { - sensor->status.hmirror = enable; - // Read register COM3 - uint8_t reg = SCCB_Read(sensor->slv_addr, COM3); + int ret = 0; + ret = set_reg_bits(sensor, 0x64, 2, 1, enable); + if (ret == 0) { + ESP_LOGD(TAG, "Set raw_gma to: %d", enable); + sensor->status.raw_gma = enable; + } + return ret; +} - // Set mirror on/off - reg = COM3_SET_MIRROR(reg, enable); +static int set_lenc_dsp(sensor_t *sensor, int enable) +{ + int ret = 0; + ret = set_reg_bits(sensor, LC_CTR, 0, 1, enable); + if (ret == 0) { + ESP_LOGD(TAG, "Set lenc to: %d", enable); + sensor->status.lenc = enable; + } + return ret; +} - // Write back register COM3 - return SCCB_Write(sensor->slv_addr, COM3, reg); +//real gain +static int set_agc_gain(sensor_t *sensor, int gain) +{ + int ret = 0; + ret = set_reg_bits(sensor, COM9, 4, 3, gain % 5); + if (ret == 0) { + ESP_LOGD(TAG, "Set gain to: %d", gain); + sensor->status.agc_gain = gain; + } + return ret; } -static int set_vflip(sensor_t *sensor, int enable) +static int set_aec_value(sensor_t *sensor, int value) { - sensor->status.vflip = enable; - // Read register COM3 - uint8_t reg = SCCB_Read(sensor->slv_addr, COM3); + int ret = 0; + ret = SCCB_Write(sensor->slv_addr, AEC, value & 0xff) | SCCB_Write(sensor->slv_addr, AECH, value >> 8); + if (ret == 0) { + ESP_LOGD(TAG, "Set aec_value to: %d", value); + sensor->status.aec_value = value; + } + return ret; +} + +static int set_awb_gain_dsp(sensor_t *sensor, int enable) +{ + int ret = 0; + ret = set_reg_bits(sensor, 0x63, 7, 1, enable); + if (ret == 0) { + ESP_LOGD(TAG, "Set awb_gain to: %d", enable); + sensor->status.awb_gain = enable; + } + return ret; +} - // Set mirror on/off - reg = COM3_SET_FLIP(reg, enable); +static int set_brightness(sensor_t *sensor, int level) +{ + int ret = 0; + ret = SCCB_Write(sensor->slv_addr, 0x9B, level); + if (ret == 0) { + ESP_LOGD(TAG, "Set brightness to: %d", level); + sensor->status.brightness = level; + } + return ret; +} - // Write back register COM3 - return SCCB_Write(sensor->slv_addr, COM3, reg); +static int set_contrast(sensor_t *sensor, int level) +{ + int ret = 0; + ret = SCCB_Write(sensor->slv_addr, 0x9C, level); + if (ret == 0) { + ESP_LOGD(TAG, "Set contrast to: %d", level); + sensor->status.contrast = level; + } + return ret; } static int init_status(sensor_t *sensor) { - sensor->status.awb = 0;//get_reg_bits(sensor, BANK_DSP, CTRL1, 3, 1); - sensor->status.aec = 0; - sensor->status.agc = 0; - sensor->status.hmirror = 0; - sensor->status.vflip = 0; - sensor->status.colorbar = 0; + sensor->status.brightness = SCCB_Read(sensor->slv_addr, 0x9B); + sensor->status.contrast = SCCB_Read(sensor->slv_addr, 0x9C); + sensor->status.saturation = 0; + sensor->status.ae_level = 0; + sensor->status.special_effect = get_reg_bits(sensor, 0x64, 5, 1); + sensor->status.wb_mode = get_reg_bits(sensor, 0x6B, 7, 1); + sensor->status.agc_gain = get_reg_bits(sensor, COM9, 4, 3); + sensor->status.aec_value = SCCB_Read(sensor->slv_addr, AEC) | (SCCB_Read(sensor->slv_addr, AECH) << 8); + sensor->status.gainceiling = SCCB_Read(sensor->slv_addr, 0x00); + sensor->status.awb = get_reg_bits(sensor, COM8, 1, 1); + sensor->status.awb_gain = get_reg_bits(sensor, 0x63, 7, 1); + sensor->status.aec = get_reg_bits(sensor, COM8, 0, 1); + sensor->status.aec2 = get_reg_bits(sensor, COM8, 7, 1); + sensor->status.agc = get_reg_bits(sensor, COM8, 2, 1); + sensor->status.bpc = get_reg_bits(sensor, 0x64, 1, 1); + sensor->status.wpc = get_reg_bits(sensor, 0x64, 0, 1); + sensor->status.raw_gma = get_reg_bits(sensor, 0x64, 2, 1); + sensor->status.lenc = get_reg_bits(sensor, LC_CTR, 0, 1); + sensor->status.hmirror = get_reg_bits(sensor, COM3, 6, 1); + sensor->status.vflip = get_reg_bits(sensor, COM3, 7, 1); + sensor->status.dcw = get_reg_bits(sensor, 0x65, 2, 1); + sensor->status.colorbar = get_reg_bits(sensor, COM3, 0, 1); + sensor->status.sharpness = get_reg_bits(sensor, EDGE0, 0, 5); + sensor->status.denoise = SCCB_Read(sensor->slv_addr, 0x8E); return 0; } static int set_dummy(sensor_t *sensor, int val){ return -1; } static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val){ return -1; } +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 -1;} +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; +} int ov7725_init(sensor_t *sensor) { @@ -342,34 +516,35 @@ int ov7725_init(sensor_t *sensor) sensor->set_hmirror = set_hmirror; sensor->set_vflip = set_vflip; + sensor->set_brightness = set_brightness; + sensor->set_contrast = set_contrast; + sensor->set_aec2 = set_aec2; + sensor->set_aec_value = set_aec_value; + sensor->set_awb_gain = set_awb_gain_dsp; + sensor->set_agc_gain = set_agc_gain; + sensor->set_dcw = set_dcw_dsp; + sensor->set_bpc = set_bpc_dsp; + sensor->set_wpc = set_wpc_dsp; + sensor->set_raw_gma = set_raw_gma_dsp; + sensor->set_lenc = set_lenc_dsp; + //not supported - sensor->set_brightness= set_dummy; sensor->set_saturation= set_dummy; + sensor->set_sharpness = set_dummy; + sensor->set_denoise = set_dummy; sensor->set_quality = set_dummy; - sensor->set_gainceiling = set_gainceiling_dummy; - sensor->set_gain_ctrl = set_dummy; - sensor->set_exposure_ctrl = set_dummy; - sensor->set_hmirror = set_dummy; - sensor->set_vflip = set_dummy; - sensor->set_whitebal = set_dummy; - sensor->set_aec2 = set_dummy; - sensor->set_aec_value = set_dummy; sensor->set_special_effect = set_dummy; sensor->set_wb_mode = set_dummy; sensor->set_ae_level = set_dummy; - sensor->set_dcw = set_dummy; - sensor->set_bpc = set_dummy; - sensor->set_wpc = set_dummy; - sensor->set_awb_gain = set_dummy; - sensor->set_agc_gain = set_dummy; - sensor->set_raw_gma = set_dummy; - sensor->set_lenc = set_dummy; - sensor->set_sharpness = set_dummy; - sensor->set_denoise = set_dummy; - - + sensor->set_gainceiling = set_gainceiling_dummy; + 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; + // Retrieve sensor's signature sensor->id.MIDH = SCCB_Read(sensor->slv_addr, REG_MIDH); sensor->id.MIDL = SCCB_Read(sensor->slv_addr, REG_MIDL); diff --git a/esp32-cam/sccb.c b/esp32-cam/sccb.c index 30e725c..cb615bb 100644 --- a/esp32-cam/sccb.c +++ b/esp32-cam/sccb.c @@ -7,6 +7,7 @@ * */ #include +#include #include #include #include "sccb.h" @@ -19,14 +20,11 @@ static const char* TAG = "sccb"; #endif -//#undef CONFIG_SCCB_HARDWARE_I2C - #define LITTLETOBIG(x) ((x<<8)|(x>>8)) -#ifdef CONFIG_SCCB_HARDWARE_I2C #include "driver/i2c.h" -#define SCCB_FREQ 200000 /*!< I2C master frequency*/ +#define SCCB_FREQ 100000 /*!< 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*/ @@ -39,16 +37,13 @@ const int SCCB_I2C_PORT = 1; const int SCCB_I2C_PORT = 0; #endif static uint8_t ESP_SLAVE_ADDR = 0x3c; -#else -#include "twi.h" -#endif 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; + memset(&conf, 0, sizeof(i2c_config_t)); conf.mode = I2C_MODE_MASTER; conf.sda_io_num = pin_sda; conf.sda_pullup_en = GPIO_PULLUP_ENABLE; @@ -58,15 +53,11 @@ int SCCB_Init(int pin_sda, int pin_scl) 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); -#endif return 0; } 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(); @@ -82,28 +73,10 @@ uint8_t SCCB_Probe() slave_addr++; } return ESP_SLAVE_ADDR; -#else - uint8_t reg = 0x00; - uint8_t slv_addr = 0x00; - - ESP_LOGI(TAG, "SCCB_Probe start"); - for (uint8_t i = 0; i < 127; i++) { - if (twi_writeTo(i, ®, 1, true) == 0) { - slv_addr = i; - break; - } - - if (i!=126) { - vTaskDelay(10 / portTICK_PERIOD_MS); // Necessary for OV7725 camera (not for OV2640). - } - } - return slv_addr; -#endif } 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(); @@ -125,28 +98,10 @@ uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg) ESP_LOGE(TAG, "SCCB_Read Failed addr:0x%02x, reg:0x%02x, data:0x%02x, ret:%d", slv_addr, reg, data, ret); } return data; -#else - uint8_t data=0; - - int rc = twi_writeTo(slv_addr, ®, 1, 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, "SCCB_Read [%02x] failed rc=%d\n", reg, rc); - } - return data; -#endif } 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); @@ -160,23 +115,10 @@ uint8_t SCCB_Write(uint8_t slv_addr, uint8_t reg, uint8_t data) ESP_LOGE(TAG, "SCCB_Write Failed addr:0x%02x, reg:0x%02x, data:0x%02x, ret:%d", slv_addr, reg, data, ret); } return ret == ESP_OK ? 0 : -1; -#else - uint8_t ret=0; - uint8_t buf[] = {reg, data}; - - if(twi_writeTo(slv_addr, buf, 2, true) != 0) { - ret=0xFF; - } - if (ret != 0) { - ESP_LOGE(TAG, "SCCB_Write [%02x]=%02x failed\n", reg, data); - } - 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); @@ -201,32 +143,11 @@ uint8_t SCCB_Read16(uint8_t slv_addr, uint16_t reg) 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 *)®_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 *)®_htons; @@ -243,18 +164,4 @@ uint8_t SCCB_Write16(uint8_t slv_addr, uint16_t reg, uint8_t data) 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 *)®_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 } diff --git a/esp32-cam/sensor.h b/esp32-cam/sensor.h index 3ea7e2c..ad6cd89 100644 --- a/esp32-cam/sensor.h +++ b/esp32-cam/sensor.h @@ -11,11 +11,13 @@ #include #include +#define NT99141_PID (0x14) #define OV9650_PID (0x96) #define OV7725_PID (0x77) #define OV2640_PID (0x26) #define OV3660_PID (0x36) #define OV5640_PID (0x56) +#define OV7670_PID (0x76) typedef enum { PIXFORMAT_RGB565, // 2BPP/RGB565 diff --git a/esp32-cam/to_bmp.c b/esp32-cam/to_bmp.c index 59455de..85f9c88 100644 --- a/esp32-cam/to_bmp.c +++ b/esp32-cam/to_bmp.c @@ -20,6 +20,17 @@ #include "sdkconfig.h" #include "esp_jpg_decode.h" +#include "esp_system.h" +#if ESP_IDF_VERSION_MAJOR >= 4 // IDF 4+ +#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4 +#include "esp32/spiram.h" +#else +#error Target CONFIG_IDF_TARGET is not supported +#endif +#else // ESP32 Before IDF 4.0 +#include "esp_spiram.h" +#endif + #if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) #include "esp32-hal-log.h" #define TAG "" diff --git a/esp32-cam/twi.h b/esp32-cam/twi.h index 60624f8..71f9907 100644 --- a/esp32-cam/twi.h +++ b/esp32-cam/twi.h @@ -35,4 +35,4 @@ uint8_t twi_readFrom(unsigned char address, unsigned char * buf, unsigned int le } #endif -#endif \ No newline at end of file +#endif