diff --git a/src/omv/ov5640.c b/src/omv/ov5640.c index be9c6ee62..62fb70f3b 100644 --- a/src/omv/ov5640.c +++ b/src/omv/ov5640.c @@ -41,10 +41,8 @@ static const uint8_t default_regs[][3] = { // https://github.com/ArduCAM/Arduino/blob/master/ArduCAM/ov5640_regs.h { 0x47, 0x40, 0x20 }, - { 0x40, 0x50, 0x6e }, { 0x40, 0x51, 0x8f }, - { 0x30, 0x08, 0x42 }, { 0x31, 0x03, 0x03 }, { 0x30, 0x17, 0xff }, // { 0x30, 0x17, 0x7f }, @@ -71,14 +69,11 @@ static const uint8_t default_regs[][3] = { { 0x36, 0x20, 0x52 }, { 0x37, 0x1b, 0x20 }, { 0x47, 0x1c, 0x50 }, - { 0x3a, 0x18, 0x00 }, { 0x3a, 0x19, 0xf8 }, - { 0x36, 0x35, 0x1c }, { 0x36, 0x34, 0x40 }, { 0x36, 0x22, 0x01 }, - { 0x3c, 0x04, 0x28 }, { 0x3c, 0x05, 0x98 }, { 0x3c, 0x06, 0x00 }, @@ -87,10 +82,8 @@ static const uint8_t default_regs[][3] = { { 0x3c, 0x09, 0x1c }, { 0x3c, 0x0a, 0x9c }, { 0x3c, 0x0b, 0x40 }, - { 0x38, 0x20, 0x47 }, // { 0x38, 0x20, 0x41 }, { 0x38, 0x21, 0x01 }, - { 0x38, 0x00, 0x00 }, { 0x38, 0x01, 0x00 }, { 0x38, 0x02, 0x00 }, @@ -109,19 +102,16 @@ static const uint8_t default_regs[][3] = { { 0x38, 0x13, 0x06 }, { 0x38, 0x14, 0x31 }, { 0x38, 0x15, 0x31 }, - - { 0x30, 0x34, 0x18 }, // { 0x30, 0x34, 0x1a }, + { 0x30, 0x34, 0x1a }, { 0x30, 0x35, 0x11 }, // { 0x30, 0x35, 0x21 }, - { 0x30, 0x36, 0x69 }, // { 0x30, 0x36, 0x46 }, + { 0x30, 0x36, 0x50 }, // { 0x30, 0x36, 0x46 }, { 0x30, 0x37, 0x13 }, { 0x30, 0x38, 0x00 }, { 0x30, 0x39, 0x00 }, - { 0x38, 0x0c, 0x07 }, { 0x38, 0x0d, 0x68 }, { 0x38, 0x0e, 0x03 }, { 0x38, 0x0f, 0xd8 }, - { 0x3c, 0x01, 0xb4 }, { 0x3c, 0x00, 0x04 }, { 0x3a, 0x08, 0x00 }, @@ -130,19 +120,16 @@ static const uint8_t default_regs[][3] = { { 0x3a, 0x0a, 0x00 }, { 0x3a, 0x0b, 0x7b }, { 0x3a, 0x0d, 0x08 }, - { 0x3a, 0x00, 0x38 }, // { 0x3a, 0x00, 0x3c }, { 0x3a, 0x02, 0x05 }, { 0x3a, 0x03, 0xc4 }, { 0x3a, 0x14, 0x05 }, { 0x3a, 0x15, 0xc4 }, - { 0x36, 0x18, 0x00 }, { 0x36, 0x12, 0x29 }, { 0x37, 0x08, 0x64 }, { 0x37, 0x09, 0x52 }, { 0x37, 0x0c, 0x03 }, - { 0x40, 0x01, 0x02 }, { 0x40, 0x04, 0x02 }, { 0x30, 0x00, 0x00 }, @@ -159,7 +146,6 @@ static const uint8_t default_regs[][3] = { { 0x46, 0x0c, 0x22 }, { 0x38, 0x24, 0x02 }, // { 0x38, 0x24, 0x01 }, { 0x50, 0x01, 0xa3 }, - { 0x34, 0x06, 0x01 }, { 0x34, 0x00, 0x06 }, { 0x34, 0x01, 0x80 }, @@ -167,7 +153,6 @@ static const uint8_t default_regs[][3] = { { 0x34, 0x03, 0x00 }, { 0x34, 0x04, 0x06 }, { 0x34, 0x05, 0x00 }, - { 0x51, 0x80, 0xff }, { 0x51, 0x81, 0xf2 }, { 0x51, 0x82, 0x00 }, @@ -199,7 +184,6 @@ static const uint8_t default_regs[][3] = { { 0x51, 0x9c, 0x06 }, { 0x51, 0x9d, 0x82 }, { 0x51, 0x9e, 0x38 }, - { 0x53, 0x81, 0x1e }, { 0x53, 0x82, 0x5b }, { 0x53, 0x83, 0x14 }, @@ -211,7 +195,6 @@ static const uint8_t default_regs[][3] = { { 0x53, 0x89, 0x1c }, { 0x53, 0x8a, 0x01 }, { 0x53, 0x8b, 0x98 }, - { 0x53, 0x00, 0x08 }, { 0x53, 0x01, 0x30 }, { 0x53, 0x02, 0x3f }, @@ -224,7 +207,6 @@ static const uint8_t default_regs[][3] = { { 0x53, 0x0a, 0x30 }, { 0x53, 0x0b, 0x04 }, { 0x53, 0x0c, 0x06 }, - { 0x54, 0x80, 0x01 }, { 0x54, 0x81, 0x06 }, { 0x54, 0x82, 0x12 }, @@ -242,14 +224,12 @@ static const uint8_t default_regs[][3] = { { 0x54, 0x8e, 0xdd }, { 0x54, 0x8f, 0xf0 }, { 0x54, 0x90, 0x15 }, - { 0x55, 0x80, 0x06 }, { 0x55, 0x83, 0x40 }, { 0x55, 0x84, 0x20 }, { 0x55, 0x89, 0x10 }, { 0x55, 0x8a, 0x00 }, { 0x55, 0x8b, 0xf8 }, - { 0x50, 0x00, 0xa7 }, { 0x58, 0x00, 0x20 }, { 0x58, 0x01, 0x19 }, @@ -313,7 +293,6 @@ static const uint8_t default_regs[][3] = { { 0x58, 0x3b, 0x2a }, { 0x58, 0x3c, 0x46 }, { 0x58, 0x3d, 0xCE }, - { 0x56, 0x88, 0x22 }, { 0x56, 0x89, 0x22 }, { 0x56, 0x8a, 0x42 }, @@ -322,16 +301,13 @@ static const uint8_t default_regs[][3] = { { 0x56, 0x8d, 0x24 }, { 0x56, 0x8e, 0x22 }, { 0x56, 0x8f, 0x22 }, - { 0x50, 0x25, 0x00 }, - { 0x3a, 0x0f, 0x30 }, { 0x3a, 0x10, 0x28 }, { 0x3a, 0x1b, 0x30 }, { 0x3a, 0x1e, 0x28 }, { 0x3a, 0x11, 0x61 }, { 0x3a, 0x1f, 0x10 }, - { 0x40, 0x05, 0x1a }, { 0x34, 0x06, 0x00 }, { 0x35, 0x03, 0x00 }, @@ -339,6 +315,17 @@ static const uint8_t default_regs[][3] = { // OpenMV Custom. + { 0x3a, 0x02, 0x07 }, + { 0x3a, 0x03, 0xae }, + { 0x3a, 0x08, 0x01 }, + { 0x3a, 0x09, 0x27 }, + { 0x3a, 0x0a, 0x00 }, + { 0x3a, 0x0b, 0xf6 }, + { 0x3a, 0x0e, 0x06 }, + { 0x3a, 0x0d, 0x08 }, + { 0x3a, 0x14, 0x07 }, + { 0x3a, 0x15, 0xae }, + // End. { 0x00, 0x00, 0x00 } @@ -418,14 +405,14 @@ static int write_reg(sensor_t *sensor, uint16_t reg_addr, uint16_t reg_data) static int set_pixformat(sensor_t *sensor, pixformat_t pixformat) { - uint8_t reg, reg2; + uint8_t pll, reg, reg2; int ret = 0; - if ((pixformat == PIXFORMAT_JPEG) - && ((sensor->framesize == FRAMESIZE_QQCIF) // doesn't work with jpeg, unknown why - || (sensor->framesize == FRAMESIZE_QQSIF) // doesn't work with jpeg, not a multiple of 8 - || (sensor->framesize == FRAMESIZE_HQQQVGA) // doesn't work with jpeg, unknown why - || (sensor->framesize == FRAMESIZE_HQQVGA) // doesn't work with jpeg, unknown why + if (((pixformat == PIXFORMAT_BAYER) || (pixformat == PIXFORMAT_JPEG)) + && ((sensor->framesize == FRAMESIZE_QQCIF) // doesn't work with bayer/jpeg, unknown why + || (sensor->framesize == FRAMESIZE_QQSIF) // doesn't work with bayer/jpeg, not a multiple of 8 + || (sensor->framesize == FRAMESIZE_HQQQVGA) // doesn't work with bayer/jpeg, unknown why + || (sensor->framesize == FRAMESIZE_HQQVGA) // doesn't work with bayer/jpeg, unknown why || (resolution[sensor->framesize][0] % 8) // w/h must be divisble by 8 || (resolution[sensor->framesize][1] % 8))) { return -1; @@ -435,19 +422,23 @@ static int set_pixformat(sensor_t *sensor, pixformat_t pixformat) case PIXFORMAT_RGB565: ret |= cambus_writeb2(sensor->slv_addr, FORMAT_CONTROL, 0x61); ret |= cambus_writeb2(sensor->slv_addr, FORMAT_CONTROL_MUX, 0x01); + pll = (resolution[sensor->framesize][0] > 2048) ? 0x50 : 0x64; // 32 MHz vs 40 MHz break; - case PIXFORMAT_YUV422: case PIXFORMAT_GRAYSCALE: - ret |= cambus_writeb2(sensor->slv_addr, FORMAT_CONTROL, 0x10); + case PIXFORMAT_YUV422: + ret |= cambus_writeb2(sensor->slv_addr, FORMAT_CONTROL, 0x30); ret |= cambus_writeb2(sensor->slv_addr, FORMAT_CONTROL_MUX, 0x00); + pll = (resolution[sensor->framesize][0] > 2048) ? 0x50 : 0x64; // 32 MHz vs 40 MHz break; case PIXFORMAT_BAYER: ret |= cambus_writeb2(sensor->slv_addr, FORMAT_CONTROL, 0x00); ret |= cambus_writeb2(sensor->slv_addr, FORMAT_CONTROL_MUX, 0x01); + pll = (resolution[sensor->framesize][0] > 2048) ? 0x64 : 0x50; // 40 MHz vs 32 MHz (jpeg can go faster at higher reses) break; case PIXFORMAT_JPEG: ret |= cambus_writeb2(sensor->slv_addr, FORMAT_CONTROL, 0x30); ret |= cambus_writeb2(sensor->slv_addr, FORMAT_CONTROL_MUX, 0x00); + pll = (resolution[sensor->framesize][0] > 2048) ? 0x50 : 0x64; // 32 MHz vs 40 MHz break; default: return -1; @@ -477,12 +468,12 @@ static int set_pixformat(sensor_t *sensor, pixformat_t pixformat) } } - return ret; + return cambus_writeb2(sensor->slv_addr, SC_PLL_CONTRL2, pll) | ret; } static int set_framesize(sensor_t *sensor, framesize_t framesize) { - uint8_t reg; + uint8_t pll, reg; uint16_t sensor_w = 0; uint16_t sensor_h = 0; uint16_t sensor_div = 0; @@ -490,11 +481,11 @@ static int set_framesize(sensor_t *sensor, framesize_t framesize) uint16_t w = resolution[framesize][0]; uint16_t h = resolution[framesize][1]; - if ((sensor->pixformat == PIXFORMAT_JPEG) - && ((framesize == FRAMESIZE_QQCIF) // doesn't work with jpeg, unknown why - || (framesize == FRAMESIZE_QQSIF) // doesn't work with jpeg, not a multiple of 8 - || (framesize == FRAMESIZE_HQQQVGA) // doesn't work with jpeg, unknown why - || (framesize == FRAMESIZE_HQQVGA) // doesn't work with jpeg, unknown why + if (((sensor->pixformat == PIXFORMAT_BAYER) || (sensor->pixformat == PIXFORMAT_JPEG)) + && ((framesize == FRAMESIZE_QQCIF) // doesn't work with bayer/jpeg, unknown why + || (framesize == FRAMESIZE_QQSIF) // doesn't work with bayer/jpeg, not a multiple of 8 + || (framesize == FRAMESIZE_HQQQVGA) // doesn't work with bayer/jpeg, unknown why + || (framesize == FRAMESIZE_HQQVGA) // doesn't work with bayer/jpeg, unknown why || (w % 8) // w/h must be divisble by 8 || (h % 8))) { return -1; @@ -510,7 +501,7 @@ static int set_framesize(sensor_t *sensor, framesize_t framesize) sensor_w = 2560; sensor_h = 1440; sensor_div = 2; - } else if ((w <= 1280) && (h <= 960)) { + } else if ((w < 1280) && (h < 960)) { // < and not <= sensor_w = 2560; sensor_h = 1600; sensor_div = 2; @@ -613,6 +604,28 @@ static int set_framesize(sensor_t *sensor, framesize_t framesize) ret |= cambus_writeb2(sensor->slv_addr, JPEG_CTRL07, (sensor_div > 1) ? 0x4 : 0x10); + // Step 8: Adjust PLL freq. + + switch (sensor->pixformat) { + case PIXFORMAT_RGB565: + pll = (w > 2048) ? 0x50 : 0x64; // 32 MHz vs 40 MHz + break; + case PIXFORMAT_GRAYSCALE: + case PIXFORMAT_YUV422: + pll = (w > 2048) ? 0x50 : 0x64; // 32 MHz vs 40 MHz + break; + case PIXFORMAT_BAYER: + pll = (w > 2048) ? 0x64 : 0x50; // 40 MHz vs 32 MHz (jpeg can go faster at higher reses) + break; + case PIXFORMAT_JPEG: + pll = (w > 2048) ? 0x50 : 0x64; // 32 MHz vs 40 MHz + break; + default: + return -1; + } + + ret |= cambus_writeb2(sensor->slv_addr, SC_PLL_CONTRL2, pll); + // Delay 300 ms systick_sleep(300); @@ -757,12 +770,69 @@ static int get_gain_db(sensor_t *sensor, float *gain_db) static int set_auto_exposure(sensor_t *sensor, int enable, int exposure_us) { - return 0; + uint8_t reg, pll, hts_h, hts_l, vts_h, vts_l; + int ret = cambus_readb2(sensor->slv_addr, AEC_PK_MANUAL, ®); + ret |= cambus_writeb2(sensor->slv_addr, AEC_PK_MANUAL, (reg & 0xFE) | ((enable == 0) << 0)); + + if ((enable == 0) && (exposure_us >= 0)) { + ret |= cambus_readb2(sensor->slv_addr, SC_PLL_CONTRL2, &pll); + + ret |= cambus_readb2(sensor->slv_addr, TIMING_HTS_H, &hts_h); + ret |= cambus_readb2(sensor->slv_addr, TIMING_HTS_L, &hts_l); + + ret |= cambus_readb2(sensor->slv_addr, TIMING_VTS_H, &vts_h); + ret |= cambus_readb2(sensor->slv_addr, TIMING_VTS_L, &vts_l); + + uint16_t hts = (hts_h << 8) | hts_l; + uint16_t vts = (vts_h << 8) | vts_l; + + // "/ 3" -> SC_PLL_CONTRL3[3:0] (pll pre-divider) + // "/ 1" -> SC_PLL_CONTRL1[7:4] (system clock divider) + // "/ 2" -> SC_PLL_CONTRL3[4] (pll root divider) + // "/ 10" -> SYSTEM_CTROL0[3:0] (bit mode) + // "/ 1" -> SYSTEM_ROOT_DIVIDER[5:4] (pclk root divider) + int pclk_freq = (((((OV5640_XCLK_FREQ / 3) * pll) / 1) / 2) / 10) / 1; + int clocks_per_us = pclk_freq / 1000000; + int exposure = IM_MAX(IM_MIN((exposure_us * clocks_per_us) / hts, 0xFFFF), 0x0000); + + int new_vts = IM_MAX(exposure, vts); + + ret |= cambus_writeb2(sensor->slv_addr, AEC_PK_EXPOSURE_0, exposure >> 12); + ret |= cambus_writeb2(sensor->slv_addr, AEC_PK_EXPOSURE_1, exposure >> 4); + ret |= cambus_writeb2(sensor->slv_addr, AEC_PK_EXPOSURE_2, exposure << 4); + + ret |= cambus_writeb2(sensor->slv_addr, TIMING_VTS_H, new_vts >> 8); + ret |= cambus_writeb2(sensor->slv_addr, TIMING_VTS_L, new_vts); + } + + return ret; } static int get_exposure_us(sensor_t *sensor, int *exposure_us) { - return 0; + uint8_t pll, aec_0, aec_1, aec_2, hts_h, hts_l; + int ret = cambus_readb2(sensor->slv_addr, SC_PLL_CONTRL2, &pll); + + ret |= cambus_readb2(sensor->slv_addr, AEC_PK_EXPOSURE_0, &aec_0); + ret |= cambus_readb2(sensor->slv_addr, AEC_PK_EXPOSURE_1, &aec_1); + ret |= cambus_readb2(sensor->slv_addr, AEC_PK_EXPOSURE_2, &aec_2); + + ret |= cambus_readb2(sensor->slv_addr, TIMING_HTS_H, &hts_h); + ret |= cambus_readb2(sensor->slv_addr, TIMING_HTS_L, &hts_l); + + uint32_t aec = ((aec_0 << 16) | (aec_1 << 8) | aec_2) >> 4; + uint16_t hts = (hts_h << 8) | hts_l; + + // "/ 3" -> SC_PLL_CONTRL3[3:0] (pll pre-divider) + // "/ 1" -> SC_PLL_CONTRL1[7:4] (system clock divider) + // "/ 2" -> SC_PLL_CONTRL3[4] (pll root divider) + // "/ 10" -> SYSTEM_CTROL0[3:0] (bit mode) + // "/ 1" -> SYSTEM_ROOT_DIVIDER[5:4] (pclk root divider) + int pclk_freq = (((((OV5640_XCLK_FREQ / 3) * pll) / 1) / 2) / 10) / 1; + int clocks_per_us = pclk_freq / 1000000; + *exposure_us = (aec * hts) / clocks_per_us; + + return ret; } static int set_auto_whitebal(sensor_t *sensor, int enable, float r_gain_db, float g_gain_db, float b_gain_db) @@ -823,7 +893,7 @@ static int set_vflip(sensor_t *sensor, int enable) { uint8_t reg; int ret = cambus_readb2(sensor->slv_addr, TIMING_TC_REG_20, ®); - if (enable){ + if (!enable){ ret |= cambus_writeb2(sensor->slv_addr, TIMING_TC_REG_20, reg|0x06); } else { ret |= cambus_writeb2(sensor->slv_addr, TIMING_TC_REG_20, reg&0xF9); @@ -871,7 +941,7 @@ static int set_lens_correction(sensor_t *sensor, int enable, int radi, int coef) int ov5640_init(sensor_t *sensor) { // Initialize sensor structure. - sensor->gs_bpp = 1; + sensor->gs_bpp = 2; sensor->reset = reset; sensor->sleep = sleep; sensor->read_reg = read_reg; diff --git a/src/omv/ov5640.h b/src/omv/ov5640.h index 3ea9a8bd4..756582301 100644 --- a/src/omv/ov5640.h +++ b/src/omv/ov5640.h @@ -11,5 +11,6 @@ #ifndef __OV5640_H__ #define __OV5640_H__ #include "sensor.h" +#define OV5640_XCLK_FREQ 24000000 int ov5640_init(sensor_t *sensor); #endif // __OV5640_H__ diff --git a/src/omv/ov5640_regs.h b/src/omv/ov5640_regs.h index 530f591cb..b4d8885b0 100644 --- a/src/omv/ov5640_regs.h +++ b/src/omv/ov5640_regs.h @@ -17,6 +17,8 @@ #define SYSTEM_CTROL0 0x3008 +#define SC_PLL_CONTRL2 0x3036 + #define SCCB_SYSTEM_CTRL_1 0x3103 #define AWB_R_GAIN_H 0x3400 @@ -30,6 +32,10 @@ #define AWB_MANUAL_CONTROL 0x3406 +#define AEC_PK_EXPOSURE_0 0x3500 +#define AEC_PK_EXPOSURE_1 0x3501 +#define AEC_PK_EXPOSURE_2 0x3502 + #define AEC_PK_MANUAL 0x3503 #define AEC_PK_REAL_GAIN_H 0x350A diff --git a/src/omv/sensor.c b/src/omv/sensor.c index 23d551884..5619cc38c 100644 --- a/src/omv/sensor.c +++ b/src/omv/sensor.c @@ -352,6 +352,9 @@ int sensor_init() init_ret = lepton_init(&sensor); break; case OV5640_ID: + if (extclk_config(OV5640_XCLK_FREQ) != 0) { + return -3; + } init_ret = ov5640_init(&sensor); break; case OV2640_ID: @@ -832,29 +835,33 @@ void DCMI_DMAConvCpltUser(uint32_t addr) switch (sensor.pixformat) { case PIXFORMAT_BAYER: dst += (line - MAIN_FB()->y) * MAIN_FB()->w; - for (int i=0; iw; i++) { - dst[i] = src[MAIN_FB()->x + i]; + src += MAIN_FB()->x; + for (int i = MAIN_FB()->w; i; i--) { + *dst++ = *src++; } break; case PIXFORMAT_GRAYSCALE: dst += (line - MAIN_FB()->y) * MAIN_FB()->w; if (sensor.gs_bpp == 1) { + src += MAIN_FB()->x; // 1BPP GRAYSCALE. - for (int i=0; iw; i++) { - dst[i] = src[MAIN_FB()->x + i]; + for (int i = MAIN_FB()->w; i; i--) { + *dst++ = *src++; } } else { + src16 += MAIN_FB()->x; // Extract Y channel from YUV. - for (int i=0; iw; i++) { - dst[i] = src[MAIN_FB()->x * 2 + i * 2]; + for (int i = MAIN_FB()->w; i; i--) { + *dst++ = *src16++; } } break; case PIXFORMAT_YUV422: case PIXFORMAT_RGB565: dst16 += (line - MAIN_FB()->y) * MAIN_FB()->w; - for (int i=0; iw; i++) { - dst16[i] = src16[MAIN_FB()->x + i]; + src16 += MAIN_FB()->x; + for (int i = MAIN_FB()->w; i; i--) { + *dst16++ = *src16++; } break; case PIXFORMAT_JPEG: