diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d81ab5..162e798 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,32 +1,35 @@ -set(COMPONENT_SRCS - driver/camera.c - driver/sccb.c - driver/sensor.c - driver/twi.c - driver/xclk.c - sensors/ov2640.c - sensors/ov3660.c - sensors/ov5640.c - sensors/ov7725.c - conversions/yuv.c - conversions/to_jpg.cpp - conversions/to_bmp.c - conversions/jpge.cpp - conversions/esp_jpg_decode.c - ) -set(COMPONENT_ADD_INCLUDEDIRS - driver/include - conversions/include - ) +if(IDF_TARGET STREQUAL "esp32") + set(COMPONENT_SRCS + driver/camera.c + driver/sccb.c + driver/sensor.c + driver/xclk.c + sensors/ov2640.c + sensors/ov3660.c + sensors/ov5640.c + sensors/ov7725.c + sensors/ov7670.c + conversions/yuv.c + conversions/to_jpg.cpp + conversions/to_bmp.c + conversions/jpge.cpp + conversions/esp_jpg_decode.c + ) -set(COMPONENT_PRIV_INCLUDEDIRS - driver/private_include - sensors/private_include - conversions/private_include - ) + set(COMPONENT_ADD_INCLUDEDIRS + driver/include + conversions/include + ) -set(COMPONENT_REQUIRES driver) -set(COMPONENT_PRIV_REQUIRES freertos nvs_flash) + set(COMPONENT_PRIV_INCLUDEDIRS + driver/private_include + sensors/private_include + conversions/private_include + ) -register_component() + set(COMPONENT_REQUIRES driver) + set(COMPONENT_PRIV_REQUIRES freertos nvs_flash) + + register_component() +endif() diff --git a/Kconfig b/Kconfig index 5281eae..8aaab78 100755 --- a/Kconfig +++ b/Kconfig @@ -1,43 +1,42 @@ menu "Camera configuration" - config OV2640_SUPPORT - bool "OV2640 Support" - default y - help - Enable this option if you want to use the OV2640. - Disable this option to save memory. + config OV7670_SUPPORT + bool "Support OV7670 VGA" + default y + help + Enable this option if you want to use the OV7670. + Disable this option to safe memory. config OV7725_SUPPORT - bool "OV7725 Support" - default n - help - Enable this option if you want to use the OV7725. - Disable this option to save memory. + bool "Support OV7725 SVGA" + default n + help + Enable this option if you want to use the OV7725. + Disable this option to save memory. + + config OV2640_SUPPORT + bool "Support OV2640 2MP" + default y + help + Enable this option if you want to use the OV2640. + Disable this option to save memory. config OV3660_SUPPORT - bool "OV3660 Support" + bool "Support OV3660 3MP" default y help Enable this option if you want to use the OV3360. Disable this option to save memory. config OV5640_SUPPORT - bool "OV5640 Support" + bool "Support OV5640 5MP" default y help Enable this option if you want to use the OV5640. Disable this option to save memory. - config SCCB_HARDWARE_I2C - bool "Use hardware I2C for SCCB" - default y - help - Enable this option if you want to use hardware I2C to control the camera. - Disable this option to use software I2C. - choice SCCB_HARDWARE_I2C_PORT bool "I2C peripheral to use for SCCB" - depends on SCCB_HARDWARE_I2C default SCCB_HARDWARE_I2C_PORT1 config SCCB_HARDWARE_I2C_PORT0 diff --git a/README.md b/README.md index 81d7f33..96616bc 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ## General Information -This repository hosts ESP32 compatible driver for OV2640, OV3660, OV5640 and OV7725 image sensors. Additionally it provides a few tools, which allow converting the captured frame data to the more common BMP and JPEG formats. +This repository hosts ESP32 compatible driver for OV2640, OV3660, OV5640, OV7670 and OV7725 image sensors. Additionally it provides a few tools, which allow converting the captured frame data to the more common BMP and JPEG formats. ## Important to Remember diff --git a/conversions/to_bmp.c b/conversions/to_bmp.c index 59455de..85f9c88 100644 --- a/conversions/to_bmp.c +++ b/conversions/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/driver/camera.c b/driver/camera.c index f56b5ce..08b1fb1 100755 --- a/driver/camera.c +++ b/driver/camera.c @@ -48,6 +48,9 @@ #if CONFIG_OV5640_SUPPORT #include "ov5640.h" #endif +#if CONFIG_OV7670_SUPPORT +#include "ov7670.h" +#endif typedef enum { CAMERA_NONE = 0, @@ -56,6 +59,7 @@ typedef enum { CAMERA_OV2640 = 2640, CAMERA_OV3660 = 3660, CAMERA_OV5640 = 5640, + CAMERA_OV7670 = 7670, } camera_model_t; #define REG_PID 0x0A @@ -369,12 +373,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 +393,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); @@ -955,11 +963,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"); @@ -1060,6 +1072,12 @@ 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 default: id->PID = 0; @@ -1116,6 +1134,13 @@ 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 default: return ESP_ERR_CAMERA_NOT_SUPPORTED; @@ -1147,20 +1172,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; @@ -1306,6 +1339,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 +1358,8 @@ 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 { ESP_LOGI(TAG, "Camera not supported"); err = ESP_ERR_CAMERA_NOT_SUPPORTED; @@ -1370,9 +1406,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; } diff --git a/driver/include/sensor.h b/driver/include/sensor.h index 3ea7e2c..b899118 100755 --- a/driver/include/sensor.h +++ b/driver/include/sensor.h @@ -16,6 +16,7 @@ #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/driver/private_include/twi.h b/driver/private_include/twi.h deleted file mode 100755 index 60624f8..0000000 --- a/driver/private_include/twi.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - twi.h - Software I2C library for ESP31B - - Copyright (c) 2015 Hristo Gochkov. All rights reserved. - This file is part of the ESP31B core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ -#ifndef SI2C_h -#define SI2C_h - -#ifdef __cplusplus -extern "C" { -#endif - -void twi_init(unsigned char sda, unsigned char scl); -void twi_stop(void); -void twi_setClock(unsigned int freq); -uint8_t twi_writeTo(unsigned char address, unsigned char * buf, unsigned int len, unsigned char sendStop); -uint8_t twi_readFrom(unsigned char address, unsigned char * buf, unsigned int len, unsigned char sendStop); - -#ifdef __cplusplus -} -#endif - -#endif \ No newline at end of file diff --git a/driver/sccb.c b/driver/sccb.c index d2f5fb9..8befb7c 100755 --- a/driver/sccb.c +++ b/driver/sccb.c @@ -19,11 +19,8 @@ static const char* TAG = "sccb"; #endif -//#undef CONFIG_SCCB_HARDWARE_I2C - #define LITTLETOBIG(x) ((x<<8)|(x>>8)) -#ifdef CONFIG_SCCB_HARDWARE_I2C #include "driver/i2c.h" #define SCCB_FREQ 100000 /*!< I2C master frequency*/ @@ -39,14 +36,10 @@ 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; conf.mode = I2C_MODE_MASTER; @@ -58,15 +51,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 +71,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 +96,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 +113,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 +141,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 +162,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/driver/twi.c b/driver/twi.c deleted file mode 100755 index 25d71fc..0000000 --- a/driver/twi.c +++ /dev/null @@ -1,432 +0,0 @@ -/* - si2c.c - Software I2C library for ESP31B - - Copyright (c) 2015 Hristo Gochkov. All rights reserved. - This file is part of the ESP31B core for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -#include -#include -#include "twi.h" -#include "soc/gpio_reg.h" -#include "soc/gpio_struct.h" -#include "soc/io_mux_reg.h" -#include "driver/rtc_io.h" -#include - - -#define LOW 0x0 -#define HIGH 0x1 - -//GPIO FUNCTIONS -#define INPUT 0x01 -#define OUTPUT 0x02 -#define PULLUP 0x04 -#define INPUT_PULLUP 0x05 -#define PULLDOWN 0x08 -#define INPUT_PULLDOWN 0x09 -#define OPEN_DRAIN 0x10 -#define OUTPUT_OPEN_DRAIN 0x12 -#define SPECIAL 0xF0 -#define FUNCTION_1 0x00 -#define FUNCTION_2 0x20 -#define FUNCTION_3 0x40 -#define FUNCTION_4 0x60 -#define FUNCTION_5 0x80 -#define FUNCTION_6 0xA0 - -#define ESP_REG(addr) *((volatile uint32_t *)(addr)) - -const uint8_t pin_to_mux[40] = { 0x44, 0x88, 0x40, 0x84, 0x48, 0x6c, 0x60, 0x64, 0x68, 0x54, 0x58, 0x5c, 0x34, 0x38, 0x30, 0x3c, 0x4c, 0x50, 0x70, 0x74, 0x78, 0x7c, 0x80, 0x8c, 0, 0x24, 0x28, 0x2c, 0, 0, 0, 0, 0x1c, 0x20, 0x14, 0x18, 0x04, 0x08, 0x0c, 0x10}; - -static void pinMode(uint8_t pin, uint8_t mode) -{ - if(pin >= 40) { - return; - } - - uint32_t rtc_reg = rtc_gpio_desc[pin].reg; - - //RTC pins PULL settings - if(rtc_reg) { - //lock rtc - ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_gpio_desc[pin].mux); - if(mode & PULLUP) { - ESP_REG(rtc_reg) = (ESP_REG(rtc_reg) | rtc_gpio_desc[pin].pullup) & ~(rtc_gpio_desc[pin].pulldown); - } else if(mode & PULLDOWN) { - ESP_REG(rtc_reg) = (ESP_REG(rtc_reg) | rtc_gpio_desc[pin].pulldown) & ~(rtc_gpio_desc[pin].pullup); - } else { - ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_gpio_desc[pin].pullup | rtc_gpio_desc[pin].pulldown); - } - //unlock rtc - } - - uint32_t pinFunction = 0, pinControl = 0; - - //lock gpio - if(mode & INPUT) { - if(pin < 32) { - GPIO.enable_w1tc = BIT(pin); - } else { - GPIO.enable1_w1tc.val = BIT(pin - 32); - } - } else if(mode & OUTPUT) { - if(pin > 33) { - //unlock gpio - return;//pins above 33 can be only inputs - } else if(pin < 32) { - GPIO.enable_w1ts = BIT(pin); - } else { - GPIO.enable1_w1ts.val = BIT(pin - 32); - } - } - - if(mode & PULLUP) { - pinFunction |= FUN_PU; - } else if(mode & PULLDOWN) { - pinFunction |= FUN_PD; - } - - pinFunction |= ((uint32_t)2 << FUN_DRV_S);//what are the drivers? - pinFunction |= FUN_IE;//input enable but required for output as well? - - if(mode & (INPUT | OUTPUT)) { - pinFunction |= ((uint32_t)2 << MCU_SEL_S); - } else if(mode == SPECIAL) { - pinFunction |= ((uint32_t)(((pin)==1||(pin)==3)?0:1) << MCU_SEL_S); - } else { - pinFunction |= ((uint32_t)(mode >> 5) << MCU_SEL_S); - } - - ESP_REG(DR_REG_IO_MUX_BASE + pin_to_mux[pin]) = pinFunction; - - if(mode & OPEN_DRAIN) { - pinControl = (1 << GPIO_PIN0_PAD_DRIVER_S); - } - - GPIO.pin[pin].val = pinControl; - //unlock gpio -} - -static void digitalWrite(uint8_t pin, uint8_t val) -{ - if(val) { - if(pin < 32) { - GPIO.out_w1ts = BIT(pin); - } else if(pin < 34) { - GPIO.out1_w1ts.val = BIT(pin - 32); - } - } else { - if(pin < 32) { - GPIO.out_w1tc = BIT(pin); - } else if(pin < 34) { - GPIO.out1_w1tc.val = BIT(pin - 32); - } - } -} - - -unsigned char twi_dcount = 18; -static unsigned char twi_sda, twi_scl; - - -static inline void SDA_LOW() -{ - //Enable SDA (becomes output and since GPO is 0 for the pin, - // it will pull the line low) - if (twi_sda < 32) { - GPIO.enable_w1ts = BIT(twi_sda); - } else { - GPIO.enable1_w1ts.val = BIT(twi_sda - 32); - } -} - -static inline void SDA_HIGH() -{ - //Disable SDA (becomes input and since it has pullup it will go high) - if (twi_sda < 32) { - GPIO.enable_w1tc = BIT(twi_sda); - } else { - GPIO.enable1_w1tc.val = BIT(twi_sda - 32); - } -} - -static inline uint32_t SDA_READ() -{ - if (twi_sda < 32) { - return (GPIO.in & BIT(twi_sda)) != 0; - } else { - return (GPIO.in1.val & BIT(twi_sda - 32)) != 0; - } -} - -static void SCL_LOW() -{ - if (twi_scl < 32) { - GPIO.enable_w1ts = BIT(twi_scl); - } else { - GPIO.enable1_w1ts.val = BIT(twi_scl - 32); - } -} - -static void SCL_HIGH() -{ - if (twi_scl < 32) { - GPIO.enable_w1tc = BIT(twi_scl); - } else { - GPIO.enable1_w1tc.val = BIT(twi_scl - 32); - } -} - -static uint32_t SCL_READ() -{ - if (twi_scl < 32) { - return (GPIO.in & BIT(twi_scl)) != 0; - } else { - return (GPIO.in1.val & BIT(twi_scl - 32)) != 0; - } -} - - -#ifndef FCPU80 -#define FCPU80 80000000L -#endif - -#if F_CPU == FCPU80 -#define TWI_CLOCK_STRETCH 800 -#else -#define TWI_CLOCK_STRETCH 1600 -#endif - -void twi_setClock(unsigned int freq) -{ -#if F_CPU == FCPU80 - if(freq <= 100000) { - twi_dcount = 19; //about 100KHz - } else if(freq <= 200000) { - twi_dcount = 8; //about 200KHz - } else if(freq <= 300000) { - twi_dcount = 3; //about 300KHz - } else if(freq <= 400000) { - twi_dcount = 1; //about 400KHz - } else { - twi_dcount = 1; //about 400KHz - } -#else - if(freq <= 100000) { - twi_dcount = 32; //about 100KHz - } else if(freq <= 200000) { - twi_dcount = 14; //about 200KHz - } else if(freq <= 300000) { - twi_dcount = 8; //about 300KHz - } else if(freq <= 400000) { - twi_dcount = 5; //about 400KHz - } else if(freq <= 500000) { - twi_dcount = 3; //about 500KHz - } else if(freq <= 600000) { - twi_dcount = 2; //about 600KHz - } else { - twi_dcount = 1; //about 700KHz - } -#endif -} - -void twi_init(unsigned char sda, unsigned char scl) -{ - twi_sda = sda; - twi_scl = scl; - pinMode(twi_sda, OUTPUT); - pinMode(twi_scl, OUTPUT); - - digitalWrite(twi_sda, 0); - digitalWrite(twi_scl, 0); - - pinMode(twi_sda, INPUT_PULLUP); - pinMode(twi_scl, INPUT_PULLUP); - twi_setClock(100000); -} - -void twi_stop(void) -{ - pinMode(twi_sda, INPUT); - pinMode(twi_scl, INPUT); -} - -static void twi_delay(unsigned char v) -{ - unsigned int i; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-but-set-variable" - unsigned int reg; - for(i=0; i + * 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/sensors/private_include/ov7670.h b/sensors/private_include/ov7670.h new file mode 100644 index 0000000..cdf845c --- /dev/null +++ b/sensors/private_include/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/sensors/private_include/ov7670_regs.h b/sensors/private_include/ov7670_regs.h new file mode 100644 index 0000000..6993548 --- /dev/null +++ b/sensors/private_include/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__