123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457 |
- /*
- * This file is part of the OpenMV project.
- * author: Juan Schiavoni <juanjoseschiavoni@hotmail.com>
- * This work is licensed under the MIT license, see the file LICENSE for details.
- *
- * OV7725 driver.
- *
- */
- #include <stdint.h>
- #include <stdlib.h>
- #include <string.h>
- #include "sccb.h"
- #include "ov7670.h"
- #include "ov7670_regs.h"
- #include "freertos/FreeRTOS.h"
- #include "freertos/task.h"
- #include <stdio.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 = "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_FREE},
- /* 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_detect(int slv_addr, sensor_id_t *id)
- {
- if (OV7670_SCCB_ADDR == slv_addr) {
- SCCB_Write(slv_addr, 0xFF, 0x01);//bank sensor
- uint16_t PID = SCCB_Read(slv_addr, 0x0A);
- if (OV7670_PID == PID) {
- id->PID = PID;
- id->VER = SCCB_Read(slv_addr, REG_VER);
- id->MIDL = SCCB_Read(slv_addr, REG_MIDL);
- id->MIDH = SCCB_Read(slv_addr, REG_MIDH);
- return PID;
- } else {
- ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
- }
- }
- return 0;
- }
- 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;
- }
|