diff --git a/src/omv/mt9v034.c b/src/omv/mt9v034.c index 65688e777..01e2283ff 100644 --- a/src/omv/mt9v034.c +++ b/src/omv/mt9v034.c @@ -95,6 +95,13 @@ #define MT9V034_AGC_ENABLE (1 << 1) #define MT9V034_THERMAL_INFO (0xC1) +#define MT9V034_WINDOW_HEIGHT_A (0x03) +#define MT9V034_WINDOW_WIDTH_A (0x04) +#define MT9V034_HORIZONTAL_BLANKING_A (0x05) +#define MT9V034_VERTICAL_BLANKING_A (0x06) +#define MT9V034_COARSE_SHUTTER_WIDTH_TOTAL_A (0x0B) +#define MT9V034_FINE_SHUTTER_WIDTH_TOTAL_A (0xD5) + static int reset(sensor_t *sensor) { // NOTE: TODO This doesn't reset register configuration. @@ -199,9 +206,41 @@ static int set_auto_gain(sensor_t *sensor, int enable, int gain) return 0; } -static int set_auto_exposure(sensor_t *sensor, int enable, int exposure) +static int set_auto_exposure(sensor_t *sensor, int enable, int exposure_us) { - return 0; + uint16_t reg, row_limit_0, row_limit_1, row_time_0, row_time_1; + int ret = cambus_readw(sensor->slv_addr, MT9V034_AEC_AGC_ENABLE, ®); + ret |= cambus_writew(sensor->slv_addr, MT9V034_AEC_AGC_ENABLE, (reg & (~MT9V034_AEC_ENABLE)) | ((enable != 0) & MT9V034_AEC_ENABLE)); + + if ((enable == 0) && (exposure_us >= 0)) { + ret |= cambus_readw(sensor->slv_addr, MT9V034_WINDOW_HEIGHT_A, &row_limit_0); + ret |= cambus_readw(sensor->slv_addr, MT9V034_VERTICAL_BLANKING_A, &row_limit_1); + ret |= cambus_readw(sensor->slv_addr, MT9V034_WINDOW_WIDTH_A, &row_time_0); + ret |= cambus_readw(sensor->slv_addr, MT9V034_HORIZONTAL_BLANKING_A, &row_time_1); + + int exposure = exposure_us * (27000000 / 1000000); + int row_time = row_time_0 + row_time_1; + int coarse_time = IM_MIN((exposure / row_time), (row_limit_0 + row_limit_1)); + int fine_time = exposure % row_time; + + ret |= cambus_writew(sensor->slv_addr, MT9V034_COARSE_SHUTTER_WIDTH_TOTAL_A, coarse_time); + ret |= cambus_writew(sensor->slv_addr, MT9V034_FINE_SHUTTER_WIDTH_TOTAL_A, fine_time); + } + + return ret; +} + +static int get_exposure_us(sensor_t *sensor, int *exposure_us) +{ + uint16_t int_rows, int_pixels, row_time_0, row_time_1; + int ret = cambus_readw(sensor->slv_addr, MT9V034_COARSE_SHUTTER_WIDTH_TOTAL_A, &int_rows); + ret |= cambus_readw(sensor->slv_addr, MT9V034_FINE_SHUTTER_WIDTH_TOTAL_A, &int_pixels); + ret |= cambus_readw(sensor->slv_addr, MT9V034_WINDOW_WIDTH_A, &row_time_0); + ret |= cambus_readw(sensor->slv_addr, MT9V034_HORIZONTAL_BLANKING_A, &row_time_1); + + *exposure_us = ((int_rows * (row_time_0 + row_time_1)) + int_pixels) / (27000000 / 1000000); + + return ret; } static int set_auto_whitebal(sensor_t *sensor, int enable, int r_gain, int g_gain, int b_gain) @@ -229,7 +268,6 @@ static int set_lens_correction(sensor_t *sensor, int enable, int radi, int coef) return 0; } - int mt9v034_init(sensor_t *sensor) { // Initialize sensor structure. @@ -248,6 +286,7 @@ int mt9v034_init(sensor_t *sensor) sensor->set_colorbar = set_colorbar; sensor->set_auto_gain = set_auto_gain; sensor->set_auto_exposure = set_auto_exposure; + sensor->get_exposure_us = get_exposure_us; sensor->set_auto_whitebal = set_auto_whitebal; sensor->set_hmirror = set_hmirror; sensor->set_vflip = set_vflip; diff --git a/src/omv/ov2640.c b/src/omv/ov2640.c index 691242a79..ee86f6849 100644 --- a/src/omv/ov2640.c +++ b/src/omv/ov2640.c @@ -14,6 +14,7 @@ #include "ov2640.h" #include "systick.h" #include "ov2640_regs.h" +#include "omv_boardconfig.h" #define SVGA_HSIZE (800) #define SVGA_VSIZE (600) @@ -619,22 +620,96 @@ static int set_auto_gain(sensor_t *sensor, int enable, int gain) return cambus_writeb(sensor->slv_addr, COM8, reg) | ret; } -static int set_auto_exposure(sensor_t *sensor, int enable, int exposure) +static int set_auto_exposure(sensor_t *sensor, int enable, int exposure_us) { uint8_t reg; - /* Switch to SENSOR register bank */ - int ret = cambus_writeb(sensor->slv_addr, BANK_SEL, BANK_SEL_SENSOR); - - /* Update COM8 */ + int ret = cambus_readb(sensor->slv_addr, BANK_SEL, ®); + ret |= cambus_writeb(sensor->slv_addr, BANK_SEL, reg | BANK_SEL_SENSOR); ret |= cambus_readb(sensor->slv_addr, COM8, ®); + ret |= cambus_writeb(sensor->slv_addr, COM8, COM8_SET_AEC(reg, (enable != 0))); - if (enable) { - reg |= COM8_AEC_EN; - } else { - reg &= ~COM8_AEC_EN; + if ((enable == 0) && (exposure_us >= 0)) { + ret |= cambus_readb(sensor->slv_addr, COM7, ®); + int t_line = 0; + + if (COM7_GET_RES(reg) == COM7_RES_UXGA) t_line = 1600 + 322; + if (COM7_GET_RES(reg) == COM7_RES_SVGA) t_line = 800 + 390; + if (COM7_GET_RES(reg) == COM7_RES_CIF) t_line = 400 + 195; + + ret |= cambus_readb(sensor->slv_addr, CLKRC, ®); + int pll_mult = (reg & CLKRC_DOUBLE) ? 2 : 1; + int clk_rc = ((reg & CLKRC_DIVIDER_MASK) + 1) * 2; + + ret |= cambus_readb(sensor->slv_addr, BANK_SEL, ®); + ret |= cambus_writeb(sensor->slv_addr, BANK_SEL, reg & (~BANK_SEL_SENSOR)); + ret |= cambus_readb(sensor->slv_addr, IMAGE_MODE, ®); + int t_pclk = 0; + + if (IMAGE_MODE_GET_FMT(reg) == IMAGE_MODE_YUV422) t_pclk = 2; + if (IMAGE_MODE_GET_FMT(reg) == IMAGE_MODE_RAW10) t_pclk = 1; + if (IMAGE_MODE_GET_FMT(reg) == IMAGE_MODE_RGB565) t_pclk = 2; + + int exposure = IM_MAX(IM_MIN(((exposure_us*(((OMV_XCLK_FREQUENCY/clk_rc)*pll_mult)/1000000))/t_pclk)/t_line,0xFFFF),0x0000); + + ret |= cambus_readb(sensor->slv_addr, BANK_SEL, ®); + ret |= cambus_writeb(sensor->slv_addr, BANK_SEL, reg | BANK_SEL_SENSOR); + + ret |= cambus_readb(sensor->slv_addr, REG04, ®); + ret |= cambus_writeb(sensor->slv_addr, REG04, (reg & 0xFC) | ((exposure >> 0) & 0x3)); + + ret |= cambus_readb(sensor->slv_addr, AEC, ®); + ret |= cambus_writeb(sensor->slv_addr, AEC, (reg & 0x00) | ((exposure >> 2) & 0xFF)); + + ret |= cambus_readb(sensor->slv_addr, REG04, ®); + ret |= cambus_writeb(sensor->slv_addr, REG04, (reg & 0xC0) | ((exposure >> 10) & 0x3F)); } - return cambus_writeb(sensor->slv_addr, COM8, reg) | ret; + return ret; +} + +static int get_exposure_us(sensor_t *sensor, int *exposure_us) +{ + uint8_t reg, aec_10, aec_92, aec_1510; + int ret = cambus_readb(sensor->slv_addr, BANK_SEL, ®); + ret |= cambus_writeb(sensor->slv_addr, BANK_SEL, reg | BANK_SEL_SENSOR); + ret |= cambus_readb(sensor->slv_addr, COM8, ®); + + if (reg & COM8_AEC_EN) { + ret |= cambus_writeb(sensor->slv_addr, COM8, reg & (~COM8_AEC_EN)); + } + + ret |= cambus_readb(sensor->slv_addr, REG04, &aec_10); + ret |= cambus_readb(sensor->slv_addr, AEC, &aec_92); + ret |= cambus_readb(sensor->slv_addr, REG45, &aec_1510); + + if (reg & COM8_AEC_EN) { + ret |= cambus_writeb(sensor->slv_addr, COM8, reg | COM8_AEC_EN); + } + + ret |= cambus_readb(sensor->slv_addr, COM7, ®); + int t_line = 0; + + if (COM7_GET_RES(reg) == COM7_RES_UXGA) t_line = 1600 + 322; + if (COM7_GET_RES(reg) == COM7_RES_SVGA) t_line = 800 + 390; + if (COM7_GET_RES(reg) == COM7_RES_CIF) t_line = 400 + 195; + + ret |= cambus_readb(sensor->slv_addr, CLKRC, ®); + int pll_mult = (reg & CLKRC_DOUBLE) ? 2 : 1; + int clk_rc = ((reg & CLKRC_DIVIDER_MASK) + 1) * 2; + + ret |= cambus_readb(sensor->slv_addr, BANK_SEL, ®); + ret |= cambus_writeb(sensor->slv_addr, BANK_SEL, reg & (~BANK_SEL_SENSOR)); + ret |= cambus_readb(sensor->slv_addr, IMAGE_MODE, ®); + int t_pclk = 0; + + if (IMAGE_MODE_GET_FMT(reg) == IMAGE_MODE_YUV422) t_pclk = 2; + if (IMAGE_MODE_GET_FMT(reg) == IMAGE_MODE_RAW10) t_pclk = 1; + if (IMAGE_MODE_GET_FMT(reg) == IMAGE_MODE_RGB565) t_pclk = 2; + + uint16_t exposure = ((aec_1510 & 0x3F) << 10) + ((aec_92 & 0xFF) << 2) + ((aec_10 & 0x3) << 0); + *exposure_us = (exposure*t_line*t_pclk)/(((OMV_XCLK_FREQUENCY/clk_rc)*pll_mult)/1000000); + + return ret; } static int set_auto_whitebal(sensor_t *sensor, int enable, int r_gain, int g_gain, int b_gain) @@ -708,6 +783,7 @@ int ov2640_init(sensor_t *sensor) sensor->set_colorbar = set_colorbar; sensor->set_auto_gain = set_auto_gain; sensor->set_auto_exposure = set_auto_exposure; + sensor->get_exposure_us = get_exposure_us; sensor->set_auto_whitebal = set_auto_whitebal; sensor->set_hmirror = set_hmirror; sensor->set_vflip = set_vflip; diff --git a/src/omv/ov2640_regs.h b/src/omv/ov2640_regs.h index 88730751b..7112e59db 100644 --- a/src/omv/ov2640_regs.h +++ b/src/omv/ov2640_regs.h @@ -5,61 +5,64 @@ * * OV2640 register definitions. */ + #ifndef __REG_REGS_H__ #define __REG_REGS_H__ + /* DSP register bank FF=0x00*/ -#define QS 0x44 -#define HSIZE 0x51 -#define VSIZE 0x52 -#define XOFFL 0x53 -#define YOFFL 0x54 -#define VHYX 0x55 -#define DPRP 0x56 -#define TEST 0x57 -#define ZMOW 0x5A -#define ZMOH 0x5B -#define ZMHH 0x5C -#define BPADDR 0x7C -#define BPDATA 0x7D -#define SIZEL 0x8C -#define HSIZE8 0xC0 -#define VSIZE8 0xC1 -#define CTRL1 0xC3 -#define MS_SP 0xF0 -#define SS_ID 0xF7 -#define SS_CTRL 0xF7 -#define MC_AL 0xFA -#define MC_AH 0xFB -#define MC_D 0xFC -#define P_CMD 0xFD -#define P_STATUS 0xFE -#define CTRLI 0x50 -#define CTRLI_LP_DP 0x80 -#define CTRLI_ROUND 0x40 +#define QS 0x44 +#define HSIZE 0x51 +#define VSIZE 0x52 +#define XOFFL 0x53 +#define YOFFL 0x54 +#define VHYX 0x55 +#define DPRP 0x56 +#define TEST 0x57 +#define ZMOW 0x5A +#define ZMOH 0x5B +#define ZMHH 0x5C +#define BPADDR 0x7C +#define BPDATA 0x7D +#define SIZEL 0x8C +#define HSIZE8 0xC0 +#define VSIZE8 0xC1 +#define CTRL1 0xC3 +#define MS_SP 0xF0 +#define SS_ID 0xF7 +#define SS_CTRL 0xF7 +#define MC_AL 0xFA +#define MC_AH 0xFB +#define MC_D 0xFC +#define P_CMD 0xFD +#define P_STATUS 0xFE -#define CTRL0 0xC2 -#define CTRL0_AEC_EN 0x80 -#define CTRL0_AEC_SEL 0x40 -#define CTRL0_STAT_SEL 0x20 -#define CTRL0_VFIRST 0x10 -#define CTRL0_YUV422 0x08 -#define CTRL0_YUV_EN 0x04 -#define CTRL0_RGB_EN 0x02 -#define CTRL0_RAW_EN 0x01 +#define CTRLI 0x50 +#define CTRLI_LP_DP 0x80 +#define CTRLI_ROUND 0x40 -#define CTRL2 0x86 -#define CTRL2_DCW_EN 0x20 -#define CTRL2_SDE_EN 0x10 -#define CTRL2_UV_ADJ_EN 0x08 -#define CTRL2_UV_AVG_EN 0x04 -#define CTRL2_CMX_EN 0x01 +#define CTRL0 0xC2 +#define CTRL0_AEC_EN 0x80 +#define CTRL0_AEC_SEL 0x40 +#define CTRL0_STAT_SEL 0x20 +#define CTRL0_VFIRST 0x10 +#define CTRL0_YUV422 0x08 +#define CTRL0_YUV_EN 0x04 +#define CTRL0_RGB_EN 0x02 +#define CTRL0_RAW_EN 0x01 -#define CTRL3 0x87 -#define CTRL3_BPC_EN 0x80 -#define CTRL3_WPC_EN 0x40 -#define R_DVP_SP 0xD3 -#define R_DVP_SP_AUTO_MODE 0x80 +#define CTRL2 0x86 +#define CTRL2_DCW_EN 0x20 +#define CTRL2_SDE_EN 0x10 +#define CTRL2_UV_ADJ_EN 0x08 +#define CTRL2_UV_AVG_EN 0x04 +#define CTRL2_CMX_EN 0x01 + +#define CTRL3 0x87 +#define CTRL3_BPC_EN 0x80 +#define CTRL3_WPC_EN 0x40 +#define R_DVP_SP 0xD3 +#define R_DVP_SP_AUTO_MODE 0x80 #define R_BYPASS 0x05 #define R_BYPASS_DSP_EN 0x00 @@ -73,6 +76,7 @@ #define IMAGE_MODE_RGB565 0x08 #define IMAGE_MODE_HREF_VSYNC 0x02 #define IMAGE_MODE_LBYTE_FIRST 0x01 +#define IMAGE_MODE_GET_FMT(x) ((x)&0xC) #define RESET 0xE0 #define RESET_MICROC 0x40 @@ -98,13 +102,18 @@ #define BANK_SEL_SENSOR 0x01 /* Sensor register bank FF=0x01*/ + #define GAIN 0x00 #define COM1 0x03 #define REG_PID 0x0A #define REG_VER 0x0B #define COM4 0x0D #define AEC 0x10 + #define CLKRC 0x11 +#define CLKRC_DOUBLE 0x80 +#define CLKRC_DIVIDER_MASK 0x3F + #define COM10 0x15 #define HSTART 0x17 #define HSTOP 0x18 @@ -168,6 +177,7 @@ #define COM7_RES_CIF 0x20 /* CIF */ #define COM7_ZOOM_EN 0x04 /* Enable Zoom */ #define COM7_COLOR_BAR 0x02 /* Enable Color Bar Test */ +#define COM7_GET_RES(x) ((x)&0x70) #define COM8 0x13 #define COM8_DEFAULT 0xC0 @@ -175,6 +185,7 @@ #define COM8_AGC_EN 0x04 /* AGC Auto/Manual control selection */ #define COM8_AEC_EN 0x01 /* Auto/Manual Exposure control */ #define COM8_SET(x) (COM8_DEFAULT|x) +#define COM8_SET_AEC(r,x) (((r)&0xFE)|((x)&1)) #define COM9 0x14 /* AGC gain ceiling */ #define COM9_DEFAULT 0x08 @@ -196,4 +207,5 @@ #define REG32_UXGA 0x36 #define REG32_SVGA 0x09 #define REG32_CIF 0x00 + #endif //__REG_REGS_H__ diff --git a/src/omv/ov7725.c b/src/omv/ov7725.c index f4c99436b..ca3df32a2 100644 --- a/src/omv/ov7725.c +++ b/src/omv/ov7725.c @@ -369,24 +369,87 @@ static int set_auto_gain(sensor_t *sensor, int enable, int gain) return ret; } -static int set_auto_exposure(sensor_t *sensor, int enable, int exposure) +static int set_auto_exposure(sensor_t *sensor, int enable, int exposure_us) { uint8_t reg; int ret = cambus_readb(sensor->slv_addr, COM8, ®); + ret |= cambus_writeb(sensor->slv_addr, COM8, COM8_SET_AEC(reg, (enable != 0))); - // Set AEC on/off - reg = COM8_SET_AEC(reg, enable); - ret |= cambus_writeb(sensor->slv_addr, COM8, reg); + if ((enable == 0) && (exposure_us >= 0)) { + ret |= cambus_readb(sensor->slv_addr, COM7, ®); - if (enable == 0 && exposure >= 0) { - // Set value manually. - ret |= cambus_writeb(sensor->slv_addr, AEC, (exposure&0xFF)); - ret |= cambus_writeb(sensor->slv_addr, AECH, ((exposure>>8)&0xFF)); + int t_line = (reg & COM7_RES_QVGA) ? (320 + 256) : (640 + 144); + int t_pclk = (COM7_GET_FMT(reg) == COM7_FMT_P_BAYER) ? 1 : 2; + + ret |= cambus_readb(sensor->slv_addr, COM4, ®); + int pll_mult = 0; + + if (COM4_GET_PLL(reg) == COM4_PLL_BYPASS) pll_mult = 1; + if (COM4_GET_PLL(reg) == COM4_PLL_4x) pll_mult = 4; + if (COM4_GET_PLL(reg) == COM4_PLL_6x) pll_mult = 6; + if (COM4_GET_PLL(reg) == COM4_PLL_8x) pll_mult = 8; + + ret |= cambus_readb(sensor->slv_addr, CLKRC, ®); + int clk_rc = 0; + + if (reg & CLKRC_NO_PRESCALE) { + clk_rc = 1; + } else { + clk_rc = ((reg & CLKRC_PRESCALER) + 1) * 2; + } + + int exposure = IM_MAX(IM_MIN(((exposure_us*(((OMV_XCLK_FREQUENCY/clk_rc)*pll_mult)/1000000))/t_pclk)/t_line,0xFFFF),0x0000); + + ret |= cambus_writeb(sensor->slv_addr, AEC, ((exposure >> 0) & 0xFF)); + ret |= cambus_writeb(sensor->slv_addr, AECH, ((exposure >> 8) & 0xFF)); } return ret; } +static int get_exposure_us(sensor_t *sensor, int *exposure_us) +{ + uint8_t reg, aec_l, aec_h; + int ret = cambus_readb(sensor->slv_addr, COM8, ®); + + if (reg & COM8_AEC_EN) { + ret |= cambus_writeb(sensor->slv_addr, COM8, COM8_SET_AEC(reg, 0)); + } + + ret |= cambus_readb(sensor->slv_addr, AEC, &aec_l); + ret |= cambus_readb(sensor->slv_addr, AECH, &aec_h); + + if (reg & COM8_AEC_EN) { + ret |= cambus_writeb(sensor->slv_addr, COM8, COM8_SET_AEC(reg, 1)); + } + + ret |= cambus_readb(sensor->slv_addr, COM7, ®); + + int t_line = (reg & COM7_RES_QVGA) ? (320 + 256) : (640 + 144); + int t_pclk = (COM7_GET_FMT(reg) == COM7_FMT_P_BAYER) ? 1 : 2; + + ret |= cambus_readb(sensor->slv_addr, COM4, ®); + int pll_mult = 0; + + if (COM4_GET_PLL(reg) == COM4_PLL_BYPASS) pll_mult = 1; + if (COM4_GET_PLL(reg) == COM4_PLL_4x) pll_mult = 4; + if (COM4_GET_PLL(reg) == COM4_PLL_6x) pll_mult = 6; + if (COM4_GET_PLL(reg) == COM4_PLL_8x) pll_mult = 8; + + ret |= cambus_readb(sensor->slv_addr, CLKRC, ®); + int clk_rc = 0; + + if (reg & CLKRC_NO_PRESCALE) { + clk_rc = 1; + } else { + clk_rc = ((reg & CLKRC_PRESCALER) + 1) * 2; + } + + *exposure_us = (((aec_h<<8)+(aec_l<<0))*t_line*t_pclk)/(((OMV_XCLK_FREQUENCY/clk_rc)*pll_mult)/1000000); + + return ret; +} + static int set_auto_whitebal(sensor_t *sensor, int enable, int r_gain, int g_gain, int b_gain) { uint8_t reg; @@ -457,7 +520,6 @@ static int set_lens_correction(sensor_t *sensor, int enable, int radi, int coef) return ret; } - int ov7725_init(sensor_t *sensor) { // Initialize sensor structure. @@ -476,6 +538,7 @@ int ov7725_init(sensor_t *sensor) sensor->set_colorbar = set_colorbar; sensor->set_auto_gain = set_auto_gain; sensor->set_auto_exposure = set_auto_exposure; + sensor->get_exposure_us = get_exposure_us; sensor->set_auto_whitebal = set_auto_whitebal; sensor->set_hmirror = set_hmirror; sensor->set_vflip = set_vflip; diff --git a/src/omv/ov7725_regs.h b/src/omv/ov7725_regs.h index 265770c58..c85e472b9 100644 --- a/src/omv/ov7725_regs.h +++ b/src/omv/ov7725_regs.h @@ -3,18 +3,20 @@ * Copyright (c) 2013/2014 Ibrahim Abdelkader * This work is licensed under the MIT license, see the file LICENSE for details. * - * OV2640 register definitions. + * OV7725 register definitions. */ + #ifndef __REG_REGS_H__ #define __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 GREEN 0x03 /* AWB – Green channel gain setting */ -#define BAVG 0x05 /* U/B Average Level */ -#define GAVG 0x06 /* Y/Gb Average Level */ -#define RAVG 0x07 /* V/R Average Level */ -#define AECH 0x08 /* Exposure Value – AEC MSBs */ + +#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 GREEN 0x03 /* AWB - Green channel gain setting */ +#define BAVG 0x05 /* U/B Average Level */ +#define GAVG 0x06 /* Y/Gb Average Level */ +#define RAVG 0x07 /* V/R Average Level */ +#define AECH 0x08 /* Exposure Value - AEC MSBs */ #define COM2 0x09 /* Common Control 2 */ #define COM2_SOFT_SLEEP 0x10 /* Soft sleep mode */ @@ -26,28 +28,32 @@ #define PID 0x0A /* Product ID Number MSB */ #define VER 0x0B /* Product ID Number LSB */ -#define COM3 0x0C /* Common Control 3 */ -#define COM3_VFLIP 0x80 /* Vertical flip image ON/OFF selection */ -#define COM3_MIRROR 0x40 /* Horizontal mirror image ON/OFF selection */ -#define COM3_SWAP_BR 0x20 /* Swap B/R output sequence in RGB output mode */ -#define COM3_SWAP_YUV 0x10 /* Swap Y/UV output sequence in YUV output mode */ -#define COM3_SWAP_MSB 0x08 /* Swap output MSB/LSB */ -#define COM3_TRI_CLOCK 0x04 /* Tri-state option for output clock at power-down period */ -#define COM3_TRI_DATA 0x02 /* Tri-state option for output data at power-down period */ -#define COM3_COLOR_BAR 0x01 /* Sensor color bar test pattern output enable */ +#define COM3 0x0C /* Common Control 3 */ +#define COM3_VFLIP 0x80 /* Vertical flip image ON/OFF selection */ +#define COM3_MIRROR 0x40 /* Horizontal mirror image ON/OFF selection */ +#define COM3_SWAP_BR 0x20 /* Swap B/R output sequence in RGB output mode */ +#define COM3_SWAP_YUV 0x10 /* Swap Y/UV output sequence in YUV output mode */ +#define COM3_SWAP_MSB 0x08 /* Swap output MSB/LSB */ +#define COM3_TRI_CLOCK 0x04 /* Tri-state option for output clock at power-down period */ +#define COM3_TRI_DATA 0x02 /* Tri-state option for output data at power-down period */ +#define COM3_COLOR_BAR 0x01 /* Sensor color bar test pattern output enable */ #define COM3_SET_CBAR(r, x) ((r&0xFE)|((x&1)<<0)) #define COM3_SET_MIRROR(r, x) ((r&0xBF)|((x&1)<<6)) #define COM3_SET_FLIP(r, x) ((r&0x7F)|((x&1)<<7)) +#define COM3_GET_CBAR(r) ((r>>0)&1) +#define COM3_GET_MIRROR(r) ((r>>6)&1) +#define COM3_GET_FLIP(r) ((r>>7)&1) #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_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 COM4_GET_PLL(r) (r&0xC0) #define COM5 0x0E /* Common Control 5 */ #define COM5_AFR 0x80 /* Auto frame rate control ON/OFF selection (night mode) */ @@ -65,7 +71,10 @@ #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 CLKRC_NO_PRESCALE 0x40 /* Use external clock directly */ +#define CLKRC_PRESCALER 0x3F /* Internal clock pre-scaler */ #define COM7 0x12 /* Common Control 7 */ #define COM7_RESET 0x80 /* SCCB Register Reset */ @@ -83,9 +92,10 @@ #define COM7_FMT_R_BAYER 0x03 /* Output format Bayer RAW */ #define COM7_SET_FMT(r, x) ((r&0xFC)|((x&0x3)<<0)) #define COM7_SET_RES(r, x) ((r&0xBF)|(x)) +#define COM7_GET_FMT(r) (r&0x03) -#define COM8 0x13 /* Common Control 8 */ -#define COM8_FAST_AUTO 0x80 /* Enable fast AGC/AEC algorithm */ +#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 */ @@ -124,13 +134,14 @@ #define REG16 0x16 /* Register 16 */ #define REG16_BIT_SHIFT 0x80 /* Bit shift test pattern options */ + #define HSTART 0x17 /* Horizontal Frame (HREF column) Start 8 MSBs (2 LSBs are at HREF[5:4]) */ #define HSIZE 0x18 /* Horizontal Sensor Size (2 LSBs are at HREF[1:0]) */ #define VSTART 0x19 /* Vertical Frame (row) Start 8 MSBs (1 LSB is at HREF[6]) */ #define VSIZE 0x1A /* Vertical Sensor Size (1 LSB is at HREF[2]) */ #define PSHFT 0x1B /* Data Format - Pixel Delay Select */ -#define MIDH 0x1C /* Manufacturer ID Byte – High */ -#define MIDL 0x1D /* Manufacturer ID Byte – Low */ +#define MIDH 0x1C /* Manufacturer ID Byte - High */ +#define MIDL 0x1D /* Manufacturer ID Byte - Low */ #define LAEC 0x1F /* Fine AEC Value - defines exposure value less than one row period */ #define COM11 0x20 /* Common Control 11 */ @@ -146,8 +157,8 @@ #define HOUTSIZE 0x29 /* Horizontal Data Output Size MSBs (2 LSBs at register EXHCH[1:0]) */ #define EXHCH 0x2A /* Dummy Pixel Insert MSB */ #define EXHCL 0x2B /* Dummy Pixel Insert LSB */ -#define VOUTSIZE 0x2C /* Vertical Data Output Size MSBs (LSB at register EXHCH[2]) */ -#define ADVFL 0x2D /* LSB of Insert Dummy Rows in Vertical Sync (1 bit equals 1 row) */ +#define VOUTSIZE 0x2C /* Vertical Data Output Size MSBs (LSB at register EXHCH[2]) */ +#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 LUMHTH 0x30 /* Histogram AEC/AGC Luminance High Level Threshold */ @@ -163,6 +174,7 @@ #define OFF_R 0x3A /* AD Offset Compensation Value for R Channel */ #define OFF_GB 0x3B /* AD Offset Compensation Value for GB Channel */ #define OFF_GR 0x3C /* AD Offset Compensation Value for GR Channel */ + #define COM12 0x3D /* DC offset compensation for analog process */ #define COM13 0x3E /* Common Control 13 */ @@ -174,10 +186,10 @@ #define COM14 0x3F /* Common Control 14 */ #define COM15 0x40 /* Common Control 15 */ #define COM16 0x41 /* Common Control 16 */ -#define TGT_B 0x42 /* BLC Blue Channel Target Value */ -#define TGT_R 0x43 /* BLC Red Channel Target Value */ -#define TGT_GB 0x44 /* BLC Gb Channel Target Value */ -#define TGT_GR 0x45 /* BLC Gr Channel Target Value */ +#define TGT_B 0x42 /* BLC Blue Channel Target Value */ +#define TGT_R 0x43 /* BLC Red Channel Target Value */ +#define TGT_GB 0x44 /* BLC Gb Channel Target Value */ +#define TGT_GR 0x45 /* BLC Gr Channel Target Value */ #define LC_CTR 0x46 /* Lens Correction Control */ #define LC_CTR_RGB_COMP_1 0x00 /* R, G, and B channel compensation coefficient is set by LC_COEF (0x49) */ @@ -219,11 +231,11 @@ #define DSP_CTRL1_BLACK_EN 0x02 /* Black defect auto correction ON/OFF */ #define DSP_CTRL1_WHITE_EN 0x01 /* White defect auto correction ON/OFF */ -#define DSP_CTRL2 0x65 /* DSP Control Byte 2 */ -#define DSP_CTRL2_VDCW_EN 0x08 /* Vertical DCW enable */ -#define DSP_CTRL2_HDCW_EN 0x04 /* Horizontal DCW enable */ -#define DSP_CTRL2_VZOOM_EN 0x02 /* Vertical zoom out enable */ -#define DSP_CTRL2_HZOOM_EN 0x01 /* Horizontal zoom out enable */ +#define DSP_CTRL2 0x65 /* DSP Control Byte 2 */ +#define DSP_CTRL2_VDCW_EN 0x08 /* Vertical DCW enable */ +#define DSP_CTRL2_HDCW_EN 0x04 /* Horizontal DCW enable */ +#define DSP_CTRL2_VZOOM_EN 0x02 /* Vertical zoom out enable */ +#define DSP_CTRL2_HZOOM_EN 0x01 /* Horizontal zoom out enable */ #define DSP_CTRL3 0x66 /* DSP Control Byte 3 */ #define DSP_CTRL3_UV_EN 0x80 /* UV output sequence option */ @@ -234,13 +246,11 @@ #define DSP_CTRL3_INTRP_PWDN 0x01 /* Interpolation module power down control */ #define DSP_CTRL3_SET_CBAR(r, x) ((r&0xDF)|((x&1)<<5)) - #define DSP_CTRL4 0x67 /* DSP Control Byte 4 */ #define DSP_CTRL4_YUV_RGB 0x00 /* Output selection YUV or RGB */ #define DSP_CTRL4_RAW8 0x02 /* Output selection RAW8 */ #define DSP_CTRL4_RAW10 0x03 /* Output selection RAW10 */ - #define AWB_BIAS 0x68 /* AWB BLC Level Clip */ #define AWB_CTRL1 0x69 /* AWB Control 1 */ #define AWB_CTRL2 0x6A /* AWB Control 2 */ @@ -309,14 +319,14 @@ #define FIFODLYM 0xA3 /* FIFO Manual Mode Delay Control */ #define FIFODLYA 0xA4 /* FIFO Auto Mode Delay Control */ -#define SDE 0xA6 /* Special Digital Effect Control */ -#define SDE_NEGATIVE_EN 0x40 /* Negative image enable */ -#define SDE_GRAYSCALE_EN 0x20 /* Gray scale image enable */ -#define SDE_V_FIXED_EN 0x10 /* V fixed value enable */ -#define SDE_U_FIXED_EN 0x08 /* U fixed value enable */ -#define SDE_CONT_BRIGHT_EN 0x04 /* Contrast/Brightness enable */ -#define SDE_SATURATION_EN 0x02 /* Saturation enable */ -#define SDE_HUE_EN 0x01 /* Hue enable */ +#define SDE 0xA6 /* Special Digital Effect Control */ +#define SDE_NEGATIVE_EN 0x40 /* Negative image enable */ +#define SDE_GRAYSCALE_EN 0x20 /* Gray scale image enable */ +#define SDE_V_FIXED_EN 0x10 /* V fixed value enable */ +#define SDE_U_FIXED_EN 0x08 /* U fixed value enable */ +#define SDE_CONT_BRIGHT_EN 0x04 /* Contrast/Brightness enable */ +#define SDE_SATURATION_EN 0x02 /* Saturation enable */ +#define SDE_HUE_EN 0x01 /* Hue enable */ #define USAT 0xA7 /* U Component Saturation Gain */ #define VSAT 0xA8 /* V Component Saturation Gain */ @@ -330,6 +340,6 @@ #define DSPAUTO_EDGE_EN 0x20 /* Sharpness (edge enhancement) auto strength control */ #define DSPAUTO_UV_EN 0x10 /* UV adjust auto slope control */ #define DSPAUTO_SCAL0_EN 0x08 /* Auto scaling factor control (register SCAL0 (0xA0)) */ -#define DSPAUTO_SCAL1_EN 0x04 /* Auto scaling factor control (registers SCAL1 (0xA1 and SCAL2 (0xA2))*/ -#define SET_REG(reg, x) (##reg_DEFAULT|x) +#define DSPAUTO_SCAL1_EN 0x04 /* Auto scaling factor control (registers SCAL1 (0xA1 and SCAL2 (0xA2)) */ + #endif //__REG_REGS_H__ diff --git a/src/omv/ov9650.c b/src/omv/ov9650.c index 666eb88d1..fa535be3b 100644 --- a/src/omv/ov9650.c +++ b/src/omv/ov9650.c @@ -14,6 +14,7 @@ #include "ov9650.h" #include "systick.h" #include "ov9650_regs.h" +#include "omv_boardconfig.h" #define NUM_BR_LEVELS 7 @@ -320,23 +321,6 @@ static int set_brightness(sensor_t *sensor, int level) return 0; } -//static int set_exposure(sensor_t *sensor, int exposure) -//{ -// uint8_t val; -// val = cambus_readb(sensor->slv_addr, REG_COM1); - -// /* exposure [1:0] */ -// cambus_writeb(sensor->slv_addr, REG_COM1, val | (exposure&0x03)); - -// /* exposure [9:2] */ -// cambus_writeb(sensor->slv_addr, REG_AECH, ((exposure>>2)&0xFF)); - -// /* exposure [15:10] */ -// cambus_writeb(sensor->slv_addr, REG_AECHM, ((exposure>>10)&0x3F)); - -// return 0; -//} - static int set_gainceiling(sensor_t *sensor, gainceiling_t gainceiling) { /* Write gain ceiling register */ @@ -355,15 +339,73 @@ static int set_auto_gain(sensor_t *sensor, int enable, int gain) return 0; } -static int set_auto_exposure(sensor_t *sensor, int enable, int exposure) +static int set_auto_exposure(sensor_t *sensor, int enable, int exposure_us) { - uint8_t val; - cambus_readb(sensor->slv_addr, REG_COM8, &val); + uint8_t reg; + int ret = cambus_readb(sensor->slv_addr, REG_COM8, ®); + ret |= cambus_writeb(sensor->slv_addr, REG_COM8, (reg & (~REG_COM8_AEC)) | ((enable != 0) & REG_COM8_AEC)); - cambus_writeb(sensor->slv_addr, REG_COM8, - enable ? (val | REG_COM8_AEC) : (val & ~REG_COM8_AEC)); + if ((enable == 0) && (exposure_us >= 0)) { + ret |= cambus_readb(sensor->slv_addr, REG_COM7, ®); + int t_line = 0, t_pclk = (reg & REG_COM7_RGB) ? 2 : 1; + + if (reg & REG_COM7_VGA) t_line = 640 + 160; + if (reg & REG_COM7_CIF) t_line = 352 + 168; + if (reg & REG_COM7_QVGA) t_line = 320 + 80; + if (reg & REG_COM7_QCIF) t_line = 176 + 84; + + ret |= cambus_readb(sensor->slv_addr, REG_CLKRC, ®); + int pll_mult = (reg & REG_CLKRC_DOUBLE) ? 2 : 1; + int clk_rc = ((reg & REG_CLKRC_DIVIDER_MASK) + 1) * 2; - return 0; + int exposure = IM_MAX(IM_MIN(((exposure_us*(((OMV_XCLK_FREQUENCY/clk_rc)*pll_mult)/1000000))/t_pclk)/t_line,0xFFFF),0x0000); + + ret |= cambus_readb(sensor->slv_addr, REG_COM1, ®); + ret |= cambus_writeb(sensor->slv_addr, REG_COM1, (reg & 0xFC) | ((exposure >> 0) & 0x3)); + + ret |= cambus_readb(sensor->slv_addr, REG_AECH, ®); + ret |= cambus_writeb(sensor->slv_addr, REG_AECH, (reg & 0x00) | ((exposure >> 2) & 0xFF)); + + ret |= cambus_readb(sensor->slv_addr, REG_AECHM, ®); + ret |= cambus_writeb(sensor->slv_addr, REG_AECHM, (reg & 0xC0) | ((exposure >> 10) & 0x3F)); + } + + return ret; +} + +static int get_exposure_us(sensor_t *sensor, int *exposure_us) +{ + uint8_t reg, aec_10, aec_92, aec_1510; + int ret = cambus_readb(sensor->slv_addr, REG_COM8, ®); + + if (reg & REG_COM8_AEC) { + ret |= cambus_writeb(sensor->slv_addr, REG_COM8, reg & (~REG_COM8_AEC)); + } + + ret |= cambus_readb(sensor->slv_addr, REG_COM1, &aec_10); + ret |= cambus_readb(sensor->slv_addr, REG_AECH, &aec_92); + ret |= cambus_readb(sensor->slv_addr, REG_AECHM, &aec_1510); + + if (reg & REG_COM8_AEC) { + ret |= cambus_writeb(sensor->slv_addr, REG_COM8, reg | REG_COM8_AEC); + } + + ret |= cambus_readb(sensor->slv_addr, REG_COM7, ®); + int t_line = 0, t_pclk = (reg & REG_COM7_RGB) ? 2 : 1; + + if (reg & REG_COM7_VGA) t_line = 640 + 160; + if (reg & REG_COM7_CIF) t_line = 352 + 168; + if (reg & REG_COM7_QVGA) t_line = 320 + 80; + if (reg & REG_COM7_QCIF) t_line = 176 + 84; + + ret |= cambus_readb(sensor->slv_addr, REG_CLKRC, ®); + int pll_mult = (reg & REG_CLKRC_DOUBLE) ? 2 : 1; + int clk_rc = ((reg & REG_CLKRC_DIVIDER_MASK) + 1) * 2; + + uint16_t exposure = ((aec_1510 & 0x3F) << 10) + ((aec_92 & 0xFF) << 2) + ((aec_10 & 0x3) << 0); + *exposure_us = (exposure*t_line*t_pclk)/(((OMV_XCLK_FREQUENCY/clk_rc)*pll_mult)/1000000); + + return ret; } static int set_auto_whitebal(sensor_t *sensor, int enable, int r_gain, int g_gain, int b_gain) @@ -411,6 +453,7 @@ int ov9650_init(sensor_t *sensor) sensor->set_gainceiling = set_gainceiling; sensor->set_auto_gain = set_auto_gain; sensor->set_auto_exposure = set_auto_exposure; + sensor->get_exposure_us = get_exposure_us; sensor->set_auto_whitebal = set_auto_whitebal; sensor->set_hmirror = set_hmirror; sensor->set_vflip = set_vflip; diff --git a/src/omv/ov9650_regs.h b/src/omv/ov9650_regs.h index 4609dab07..38eae7471 100644 --- a/src/omv/ov9650_regs.h +++ b/src/omv/ov9650_regs.h @@ -5,8 +5,10 @@ * * OV9650 register definitions. */ + #ifndef __REG_REGS_H__ #define __REG_REGS_H__ + #define REG_GAIN 0x00 #define REG_BLUE 0x01 #define REG_RED 0x02 @@ -110,7 +112,9 @@ #define REG_COM26 0xA5 #define REG_GGAIN 0xA6 #define REG_VGAST 0xA7 + /* register bits */ + #define REG_COM1_QQCIF (1<<5) #define REG_COM1_QQVGA (1<<5) #define REG_COM1_SKIP2 (1<<2) @@ -125,4 +129,7 @@ #define REG_COM8_AEC (1<<0) #define REG_MVFP_HMIRROR (1<<5) #define REG_MVFP_VFLIP (1<<4) +#define REG_CLKRC_DOUBLE (1<<8) +#define REG_CLKRC_DIVIDER_MASK (0x3F) + #endif //__REG_REGS_H__ diff --git a/src/omv/py/py_sensor.c b/src/omv/py/py_sensor.c index d9bec0b29..b4be4d38c 100644 --- a/src/omv/py/py_sensor.c +++ b/src/omv/py/py_sensor.c @@ -354,12 +354,19 @@ static mp_obj_t py_sensor_set_auto_gain(uint n_args, const mp_obj_t *args, mp_ma } static mp_obj_t py_sensor_set_auto_exposure(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { - int enable = mp_obj_get_int(args[0]); - int value = py_helper_lookup_int(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_value), -1); - if (sensor_set_auto_exposure(enable, value) != 0) { - return mp_const_false; + int exposure_us = py_helper_lookup_int(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_exposure_us), -1); + if (sensor_set_auto_exposure(mp_obj_get_int(args[0]), exposure_us) != 0) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "Sensor control failed!")); } - return mp_const_true; + return mp_const_none; +} + +static mp_obj_t py_sensor_get_exposure_us() { + int exposure_us; + if (sensor_get_exposure_us(&exposure_us) != 0) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "Sensor control failed!")); + } + return mp_obj_new_int(exposure_us); } static mp_obj_t py_sensor_set_auto_whitebal(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { @@ -444,6 +451,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_sensor_set_quality_obj, py_sensor_se STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_sensor_set_colorbar_obj, py_sensor_set_colorbar); STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_sensor_set_auto_gain_obj, 1,py_sensor_set_auto_gain); STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_sensor_set_auto_exposure_obj,1,py_sensor_set_auto_exposure); +STATIC MP_DEFINE_CONST_FUN_OBJ_0(py_sensor_get_exposure_us_obj, py_sensor_get_exposure_us); STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_sensor_set_auto_whitebal_obj,1,py_sensor_set_auto_whitebal); STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_sensor_set_hmirror_obj, py_sensor_set_hmirror); STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_sensor_set_vflip_obj, py_sensor_set_vflip); @@ -520,6 +528,7 @@ STATIC const mp_map_elem_t globals_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_set_colorbar), (mp_obj_t)&py_sensor_set_colorbar_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_set_auto_gain), (mp_obj_t)&py_sensor_set_auto_gain_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_set_auto_exposure), (mp_obj_t)&py_sensor_set_auto_exposure_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_get_exposure_us), (mp_obj_t)&py_sensor_get_exposure_us_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_set_auto_whitebal), (mp_obj_t)&py_sensor_set_auto_whitebal_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_set_hmirror), (mp_obj_t)&py_sensor_set_hmirror_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_set_vflip), (mp_obj_t)&py_sensor_set_vflip_obj }, diff --git a/src/omv/py/qstrdefsomv.h b/src/omv/py/qstrdefsomv.h index 892a1dbc3..3bad82aca 100644 --- a/src/omv/py/qstrdefsomv.h +++ b/src/omv/py/qstrdefsomv.h @@ -223,6 +223,8 @@ Q(set_quality) Q(set_colorbar) Q(set_auto_gain) Q(set_auto_exposure) +Q(exposure_us) +Q(get_exposure_us) Q(set_auto_whitebal) Q(set_hmirror) Q(set_vflip) diff --git a/src/omv/sensor.c b/src/omv/sensor.c index 56ddb22d2..d9a440f80 100644 --- a/src/omv/sensor.c +++ b/src/omv/sensor.c @@ -536,11 +536,22 @@ int sensor_set_auto_gain(int enable, int gain) return 0; } -int sensor_set_auto_exposure(int enable, int exposure) +int sensor_set_auto_exposure(int enable, int exposure_us) { /* call the sensor specific function */ if (sensor.set_auto_exposure == NULL - || sensor.set_auto_exposure(&sensor, enable, exposure) != 0) { + || sensor.set_auto_exposure(&sensor, enable, exposure_us) != 0) { + /* operation not supported */ + return -1; + } + return 0; +} + +int sensor_get_exposure_us(int *exposure_us) +{ + /* call the sensor specific function */ + if (sensor.get_exposure_us == NULL + || sensor.get_exposure_us(&sensor, exposure_us) != 0) { /* operation not supported */ return -1; } diff --git a/src/omv/sensor.h b/src/omv/sensor.h index af67d0d7f..acc5f919b 100644 --- a/src/omv/sensor.h +++ b/src/omv/sensor.h @@ -136,7 +136,8 @@ typedef struct _sensor { int (*set_quality) (sensor_t *sensor, int quality); int (*set_colorbar) (sensor_t *sensor, int enable); int (*set_auto_gain) (sensor_t *sensor, int enable, int gain); - int (*set_auto_exposure) (sensor_t *sensor, int enable, int exposure); + int (*set_auto_exposure) (sensor_t *sensor, int enable, int exposure_us); + int (*get_exposure_us) (sensor_t *sensor, int *exposure_us); int (*set_auto_whitebal) (sensor_t *sensor, int enable, int r_gain, int g_gain, int b_gain); int (*set_hmirror) (sensor_t *sensor, int enable); int (*set_vflip) (sensor_t *sensor, int enable); @@ -203,7 +204,10 @@ int sensor_set_colorbar(int enable); int sensor_set_auto_gain(int enable, int gain); // Enable auto exposure or set value manually. -int sensor_set_auto_exposure(int enable, int exposure); +int sensor_set_auto_exposure(int enable, int exposure_us); + +// Get the exposure value. +int sensor_get_exposure_us(int *get_exposure_us); // Enable auto white balance or set value manually. int sensor_set_auto_whitebal(int enable, int r_gain, int g_gain, int b_gain); diff --git a/usr/examples/02-Board-Control/sensor_exposure_control.py b/usr/examples/02-Board-Control/sensor_exposure_control.py new file mode 100644 index 000000000..5bf609921 --- /dev/null +++ b/usr/examples/02-Board-Control/sensor_exposure_control.py @@ -0,0 +1,53 @@ +# Sensor Exposure Control +# +# This example shows off how to cotnrol the camera sensor's +# exposure manually versus letting auto exposure control run. + +import sensor, image, time + +# Change this value to adjust the exposure. Try 10.0/0.1/etc. +EXPOSURE_TIME_SCALE = 1.0 + +sensor.reset() # Reset and initialize the sensor. +sensor.set_pixformat(sensor.RGB565) # Set pixel format to RGB565 (or GRAYSCALE) +sensor.set_framesize(sensor.QVGA) # Set frame size to QVGA (320x240) + +# Print out the initial exposure time for comparison. +print("Initial exposure == %d" % sensor.get_exposure_us()) + +sensor.skip_frames(time = 2000) # Wait for settings take effect. +clock = time.clock() # Create a clock object to track the FPS. + +# You have to turn automatic gain control and automatic white blance off +# otherwise they will change the image gains to undo any exposure settings +# that you put in place... +sensor.set_auto_gain(False) +sensor.set_auto_whitebal(False) + +current_exposure_time_in_microseconds = sensor.get_exposure_us() +print("Current Exposure == %d" % current_exposure_time_in_microseconds) + +# Auto exposure control (AEC) is enabled by default. Calling the below function +# disables sensor auto exposure control. The additionally "exposure_us" +# argument then overrides the auto exposure value after AEC is disabled. +sensor.set_auto_exposure(False, \ + exposure_us = int(current_exposure_time_in_microseconds * EXPOSURE_TIME_SCALE)) + +print("New exposure == %d" % sensor.get_exposure_us()) +# sensor.get_exposure_us() returns the exact camera sensor exposure time +# in microseconds. However, this may be a different number than what was +# commanded because the sensor code converts the exposure time in microseconds +# to a row/pixel/clock time which doesn't perfectly match with microseconds... + +# If you want to turn auto exposure back on do: sensor.set_auto_exposure(True) +# Note that the camera sensor will then change the exposure time as it likes. + +# Doing: sensor.set_auto_exposure(False) +# Just disables the exposure value update but does not change the exposure +# value the camera sensor determined was good. + +while(True): + clock.tick() # Update the FPS clock. + img = sensor.snapshot() # Take a picture and return the image. + print(clock.fps()) # Note: OpenMV Cam runs about half as fast when connected + # to the IDE. The FPS should increase once disconnected.