OV5640 Driver Finished

It basically works. I might be able to improve it with the final
hardware.
This commit is contained in:
Kwabena W. Agyeman 2020-01-13 00:15:35 -08:00
parent ff571a7979
commit e249770fe1
4 changed files with 138 additions and 54 deletions

View File

@ -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, &reg);
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, &reg);
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;

View File

@ -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__

View File

@ -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

View File

@ -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; i<MAIN_FB()->w; 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; i<MAIN_FB()->w; 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; i<MAIN_FB()->w; 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; i<MAIN_FB()->w; 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: