SparkFun Forums 

Where electronics enthusiasts find answers.

Have questions about a SparkFun product or board? This is the place to be.
By octal
#154865
Hi brijesh,
could you please share your code with me?
I'm using the STM32F4Discovery board with same camera as you. I'm getting the bars in QVGA mode but I have same behaviour as on your first post (some strange odd colors between bars).
I'm using DCMI and do a capture of (half) a single QVGA shot (115KB) in memory and then send this buffer via uart to draw it on PC.
Can you send me your code ( octal [at) pocketmt (dot)com )
PS. And yes I'm interrested in the project, if you send me one of your boards, I'll be happy to contribute (I'll pay you for the board).
thank you for your help
By robodve
#155564
Hello all,

I too am struggling with the OV7670 specially with the lack of good info. I have the datasheet and an implementation guide. I use an STM32F103 based devboard with 2.8" LCD 320x240 pixels with ILI9325 driver chip. My OV7670 board is from dealextreme, has the AL422 fifo on-board.
I use Keil uVision and the ST firmware lib for stm32F10x

I have working code! I can display QVGA images on my LCD. So I have:
-bitbanged version of SCCB
-OV7670 register settings that give me QVGA with RGB565 as mode.

I keep it simple for you, I just paste the OV7670 settings under this post. Never mind the comments in Dutch (sorry).
I got this from some arduino forum and tweaked it until I had my QVGA with RGB565 at the maximum possible framerate.
registers 0x12, 0x40, 0x11 and 0x14 are of your interest to choose your own resolution/dataformat and framerates/pixelclocks. The rest I recommand not to modify

I can share the rest als .c and .h files Reply to this post and mention

My problem is that with all the bitbanging and QVGA I can only get about 1 frame per second to process.
I like to go to QCIF but can not because this is in YuV format which my LCD dont like........

So now I try to figure out how to get to qqvga or how to scale / resize the image.
Maybe somebody can help me with that??

#define CHANGE_REG_NUM 173

//DVE: Deze settings werken icm mijn 2.8" 320x240 TFT met ILI9325 en MINISTM32!
//linker kolom is het registernummer (regID) en rechter is de data oftewel de waarde waarop het register staat.
const char change_reg[CHANGE_REG_NUM][2]=
{
//
// The really important stuff.
{0x12, 0x14}, //COM7 DVE: RGB mode, color bar disabled, QVGA (= 320x240 pixels) DVE: was 0x14
//{0x12, 0x0c}, //COM7 DVE: RGB mode, color bar disabled, QCIF (= 176x144 pixels)
{0x8c, 0x00},//2 / RGB444: 0b [7-2] Reserved, [1] RGB444 [0] XR GB(0) , RG BX(1)
{0x04, 0x00},//3 / COM1 : CCIR656 Format Disable
{0x40, 0x10}, //COM15 DVE: RGB565 mode
{0x14, 0x18},//5 / COM9 : 4x gain ceiling
{0x11, 0x01},//6 / CLKRC : (was 3f) Internal Clock, [000000] to [111111] , 16000000 / ( [111111]+ 1 ) = 250000 DVE: deze deelt mijn MCO out van 36MHz [b0..5 + 1]
{0x6b, 0x4a},//72/ DBLV : PPL Control, PLL clk x 4, together with CLKRC it defines the frame rate
//
// Hardware Window Settings
//
{0x0c, 0x00},//13/ COM3 : DVE: this one can enable windowing, scaling, digital cropping and since it was set to 0x00 the settings after this line were NOT effective! (
{0x32, 0xb6},//7 / HREF : DVE; this one MUST be 0xb6 or otherwise my images get all scrambled
{0x17, 0x13},//8 / HSTART: HREF Column start high 8-bit DVE: was 0x13
{0x18, 0x01},//9 / HSTOP : HREF Column end high 8-bit DVE: was 0x01
{0x19, 0x02},//10/ VSTRT : VSYNC Start high 8-bit DVE: was 0x02
{0x1a, 0x7a},//11/ VSTOP : VSYNC End high 8-bit, DVE: was 0x7a
{0x03, 0x0a},//12/ VREF : Vertical Frame Control, DVE: was 0x0a
{0x3e, 0x00},//14/ COM14 : Pixel Clock Devider DVE: was 0x00
//
// Mystery Scaling Numbers
//
{0x70, 0x3a},//15/ SCALING XSC : Horizontal Scale Factor DVE: was 0x3a
{0x71, 0x35},//16/ SCALING YSC : Vertical Scale Factor DVE: was 0x35
{0x72, 0x11},//17/ SCALING DCW : DCW Control DVE: was 0x11
{0x73, 0xf0},//18/ SCALING PCLK: Scaling Pixel Clock Devider DVE: was 0xf0
{0xa2, 0x02},//19/ SCALING PCLK: Scaling Pixel Delay
{0x15, 0x00},//20/ COM10 : VSYNC , HREF , PCLK Settings
//
// Gamma Curve Values
//
{0x7a, 0x20},//21/ SLOP : Gamma Curve Highest Segment Slope
{0x7b, 0x10},//22/ GAM1 : Gamme Curve 1st Segment
{0x7c, 0x1e},//23/ GAM2 : Gamme Curve 2st Segment
{0x7d, 0x35},//24/ GAM3 : Gamme Curve 3st Segment
{0x7e, 0x5a},//25/ GAM4 : Gamme Curve 4st Segment
{0x7f, 0x69},//26/ GAM5 : Gamme Curve 5st Segment
{0x80, 0x76},//27/ GAM6 : Gamme Curve 6st Segment
{0x81, 0x80},//28/ GAM7 : Gamme Curve 7st Segment
{0x82, 0x88},//29/ GAM8 : Gamme Curve 8st Segment
{0x83, 0x8f},//30/ GAM9 : Gamme Curve 9st Segment
{0x84, 0x96},//31/ GAM10: Gamme Curve 10st Segment
{0x85, 0xa3},//32/ GAM11: Gamme Curve 11st Segment
{0x86, 0xaf},//33/ GAM12: Gamme Curve 12st Segment
{0x87, 0xc4},//34/ GAM13: Gamme Curve 13st Segment
{0x88, 0xd7},//35/ GAM14: Gamme Curve 14st Segment
{0x89, 0xe8},//36/ GAM15: Gamme Curve 15st Segment
//
// Automatic Gain Control and AEC Parameters
//
{0x13, 0x00},//37/ COM8 : Fast AGC/AEC Algorithm
{0x00, 0x00},//38/ GAIN
{0x10, 0x00},//39/ AECH
{0x0d, 0x00},//40/ COM4 :
{0x14, 0x18},//41/ COM9 : Automatic Gain Ceiling : 8x
{0xa5, 0x05},//42/ BD50MAX: 50 Hz Banding Step Limit
{0xab, 0x07},//43/ BD60MAX: 60 Hz Banding Step Limit
{0x24, 0x95},//44/ AGC - Stable Operating Region Upper Limit
{0x25, 0x33},//45/ AGC - Stable Operating Region Lower Limit
{0x26, 0xe3},//46/ AGC - Fast Mode Operating Region
{0x9f, 0x78},//47/ HAECC1 : Histogram based AEC Control 1
{0xa0, 0x68},//48/ HAECC2 : Histogram based AEC Control 2
{0xa1, 0x03},//49/ Reserved
{0xa6, 0xd8},//50/ HAECC3 : Histogram based AEC Control 3
{0xa7, 0xd8},//51/ HAECC4 : Histogram based AEC Control 4
{0xa8, 0xf0},//52/ HAECC5 : Histogram based AEC Control 5
{0xa9, 0x90},//53/ HAECC6 : Histogram based AEC Control 6
{0xaa, 0x94},//54/ HAECC7 : AEC Algorithm Selection
{0x13, 0xe5},//55/ COM8 : Fast AGC Algorithm, Unlimited Step Size , Banding Filter ON, AGC and AEC enable.
//
// Reserved Values without function specification
//
{0x0e, 0x61},//56/ COM5 : Reserved
{0x0f, 0x4b},//57/ COM6 : Reserved
{0x16, 0x02},//58/ Reserved
{0x1e, 0x2f},//59/ MVFP : Mirror/Vflip disabled ( 0x37 enabled ) //DVE: was 0x07 is now 0x2f so mirror ENABLED. Was required for my TFT to show pictures without mirroring (probably my TFT setting is also mirrored)
{0x21, 0x02},//60/ Reserved
{0x22, 0x91},//61/ Reserved
{0x29, 0x07},//62/ Reserved
{0x33, 0x0b},//63/ Reserved
{0x35, 0x0b},//64/ Reserved
{0x37, 0x1d},//65/ Reserved
{0x38, 0x71},//66/ Reserved
{0x39, 0x2a},//67/ Reserved
{0x3c, 0x78},//68/ COM12 : Reserved
{0x4d, 0x40},//69/ Reserved
{0x4e, 0x20},//70/ Reserved
{0x69, 0x00},//71/ GFIX : Fix Gain for RGB Values
//{0x6b, 0x80},//DEBUG DBLV: aantal frames per seconde dmv PCLK hoger of lager, staat nu op INPUT CLK x6 en INT regulator enabled
{0x74, 0x10},//73/ Reserved
{0x8d, 0x4f},//74/ Reserved
{0x8e, 0x00},//75/ Reserved
{0x8f, 0x00},//76/ Reserved
{0x90, 0x00},//77/ Reserved
{0x91, 0x00},//78/ Reserved
{0x92, 0x00},//79/ Reserved
{0x96, 0x00},//80/ Reserved
{0x9a, 0x00},//81/ Reserved
{0xb0, 0x84},//82/ Reserved
{0xb1, 0x0c},//83/ Reserved
{0xb2, 0x0e},//84/ Reserved
{0xb3, 0x82},//85/ Reserved
{0xb8, 0x0a},//86/ Reserved
//
// Reserved Values without function specification
//
{0x43, 0x0a},//87/ Reserved
{0x44, 0xf0},//88/ Reserved
{0x45, 0x34},//89/ Reserved
{0x46, 0x58},//90/ Reserved
{0x47, 0x28},//91/ Reserved
{0x48, 0x3a},//92/ Reserved
{0x59, 0x88},//93/ Reserved
{0x5a, 0x88},//94/ Reserved
{0x5b, 0x44},//95/ Reserved
{0x5c, 0x67},//96/ Reserved
{0x5d, 0x49},//97/ Reserved
{0x5e, 0x0e},//98/ Reserved
{0x64, 0x04},//99/ Reserved
{0x65, 0x20},//100/ Reserved
{0x66, 0x05},//101/ Reserved
{0x94, 0x04},//102/ Reserved
{0x95, 0x08},//103/ Reserved
{0x6c, 0x0a},//104/ Reserved
{0x6d, 0x55},//105/ Reserved
{0x6e, 0x11},//106/ Reserved
{0x6f, 0x9f},//107/ Reserved
{0x6a, 0x40},//108/ Reserved
{0x01, 0x40},//109/ REG BLUE : Reserved
{0x02, 0x40},//110/ REG RED : Reserved
{0x13, 0xe7},//111/ COM8 : FAST AEC, AEC unlimited STEP, Band Filter, AGC , ARC , AWB enable.
//
// Matrix Coefficients
//
{0x4f, 0x80},//112/ MTX 1 : Matrix Coefficient 1
{0x50, 0x80},//113/ MTX 2 : Matrix Coefficient 2
{0x51, 0x00},//114/ MTX 3 : Matrix Coefficient 3
{0x52, 0x22},//115/ MTX 4 : Matrix Coefficient 4
{0x53, 0x5e},//116/ MTX 5 : Matrix Coefficient 5
{0x54, 0x80},//117/ MTX 6 : Matrix Coefficient 6
{0x58, 0x9e},//118/ MTXS : Matrix Coefficient Sign for Coefficient 5 to 0
{0x41, 0x08},//119/ COM16 : AWB Gain enable
{0x3f, 0x00},//120/ EDGE : Edge Enhancement Adjustment
{0x75, 0x05},//121/ Reserved
{0x76, 0xe1},//122/ Reserved
{0x4c, 0x00},//123/ Reserved
{0x77, 0x01},//124/ Reserved
{0x3d, 0xc0},//125/ COM13
{0x4b, 0x09},//126/ Reserved
{0xc9, 0x60},//127/ Reserved
{0x41, 0x38},//128/ COM16
{0x56, 0x40},//129/ Reserved
{0x34, 0x11},//130/ Reserved
{0x3b, 0x12},//131/ COM11 : Exposure and Hz Auto detect enabled.
{0xa4, 0x88},//132/ Reserved
{0x96, 0x00},//133/ Reserved
{0x97, 0x30},//134/ Reserved
{0x98, 0x20},//135/ Reserved
{0x99, 0x30},//136/ Reserved
{0x9a, 0x84},//137/ Reserved
{0x9b, 0x29},//138/ Reserved
{0x9c, 0x03},//139/ Reserved
{0x9d, 0x4c},//140/ Reserved
{0x9e, 0x3f},//141/ Reserved
{0x78, 0x04},//142/ Reserved
//
// Mutliplexing Registers
//
{0x79, 0x01},//143/ Reserved
{0xc8, 0xf0},//144/ Reserved
{0x79, 0x0f},//145/ Reserved
{0xc8, 0x00},//146/ Reserved
{0x79, 0x10},//147/ Reserved
{0xc8, 0x7e},//148/ Reserved
{0x79, 0x0a},//149/ Reserved
{0xc8, 0x80},//150/ Reserved
{0x79, 0x0b},//151/ Reserved
{0xc8, 0x01},//152/ Reserved
{0x79, 0x0c},//153/ Reserved
{0xc8, 0x0f},//154/ Reserved
{0x79, 0x0d},//155/ Reserved
{0xc8, 0x20},//156/ Reserved
{0x79, 0x09},//157/ Reserved
{0xc8, 0x80},//158/ Reserved
{0x79, 0x02},//159/ Reserved
{0xc8, 0xc0},//160/ Reserved
{0x79, 0x03},//161/ Reserved
{0xc8, 0x40},//162/ Reserved
{0x79, 0x05},//163/ Reserved
{0xc8, 0x30},//164/ Reserved
{0x79, 0x26},//165/ Reserved
//
// Additional Settings
//
{0x09, 0x00},//166/ COM2 : Output Drive Capability
{0x55, 0x00},//167/ Brightness Control
//
// End Of Constant
//
{0xff, 0xff},//174/ End Marker
};
By brijesh
#156148
For QQVGA the following register values worked for me

- OV7670_WriteReg(0x0C,0x04); //enable down sampling
- OV7670_WriteReg(0x72,0x22); //Down sample by 4
- OV7670_WriteReg(0x73,0x02); //Clock div4
- OV7670_WriteReg(0x3E,0x1A); //Clock div4

Note the clock div4 setup depends on what is your clock input to camera module. In my case the clock is 24MHz.
By kchuva
#159743
brijesh wrote:Haris, Rujatt,

Sorry about not responding to your posts earlier. For some reason I did not get email alerts of new posts on this thread. If you guys still need register settings send me a PM I will be glad to share.

Cheers
Hi, Brijesh, Haris,
I develop board with OVM7690 Camera module. I got output picture with wrong colors(more pink and grey) and I don't know what registers settings I should set to get normal picture. I need your settings, guys. Please, answer me here or to email: kchuva @ gmail . com
By gammafun
#163420
Hi,

I'm trying to interface the OV7670 with STM32F4Discovery too and have been experiencing trouble.
I implemented a bit banging version of i2c to communicate with the camera, but when I tried to read from any registers, I always get 255.

It'd be great if you can share the code with me.
By brijesh
#163467
here you go, I actually found this code from somewhere, don't remember now. I think it is from Olimex reference board design.
Code: Select all
////////////////////////////////////////////////////////////////////////
/// Bit Bang I2C implementation 
/////////////////////////////////////////////////////////////////////
/* DEFINE LOCAL CONSTANTS HERE */
#define SOFT_I2C_PORT_CLOCK RCC_AHB1Periph_GPIOB
#define SOFT_I2C_PORT GPIOB
#define SOFT_I2C_SCL_PIN GPIO_Pin_8
#define SOFT_I2C_SDA_PIN GPIO_Pin_9

#define ACK 0
#define NACK 1

#define I2C_SLAVE_ADDRESS_WRITE 0x42
#define I2C_SLAVE_ADDRESS_READ 0x43


static void local_I2C_Delay(void)
{
	uint32_t d = 100;
	while(d--) {
	}
}

static void local_I2C_Initialize(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	/* Enable the software I2C Clock */
	RCC_AHB1PeriphClockCmd(SOFT_I2C_PORT_CLOCK, ENABLE);

	/* Configure the SDA and SCL pins */
	GPIO_InitStructure.GPIO_Pin = SOFT_I2C_SCL_PIN | SOFT_I2C_SDA_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(SOFT_I2C_PORT, &GPIO_InitStructure);
	GPIO_WriteBit(SOFT_I2C_PORT, SOFT_I2C_SDA_PIN, Bit_SET);
	GPIO_WriteBit(SOFT_I2C_PORT, SOFT_I2C_SCL_PIN, Bit_SET);

	local_I2C_Delay();
}

static void local_I2C_Start(void)
{
	GPIO_WriteBit(SOFT_I2C_PORT, SOFT_I2C_SDA_PIN, Bit_RESET);
	local_I2C_Delay();
	GPIO_WriteBit(SOFT_I2C_PORT, SOFT_I2C_SCL_PIN, Bit_RESET);
	local_I2C_Delay();

}

static void local_I2C_Stop(void)
{
	GPIO_WriteBit(SOFT_I2C_PORT, SOFT_I2C_SDA_PIN, Bit_RESET);
	local_I2C_Delay();
	GPIO_WriteBit(SOFT_I2C_PORT, SOFT_I2C_SCL_PIN, Bit_SET);
	local_I2C_Delay();
	GPIO_WriteBit(SOFT_I2C_PORT, SOFT_I2C_SDA_PIN, Bit_SET);
	local_I2C_Delay();
}

static uint8_t local_I2C_ReadByte(char ack)
{
	uint8_t data = 0;
	char i;

	GPIO_WriteBit(SOFT_I2C_PORT, SOFT_I2C_SDA_PIN, Bit_SET); // make input
	for(i = 0; i < 8; i++) {
		local_I2C_Delay();
		GPIO_WriteBit(SOFT_I2C_PORT, SOFT_I2C_SCL_PIN, Bit_SET);
		local_I2C_Delay();
		data |= GPIO_ReadInputDataBit(SOFT_I2C_PORT, SOFT_I2C_SDA_PIN) & 0x01;
		if(i != 7)
			data <<= 1;
		GPIO_WriteBit(SOFT_I2C_PORT, SOFT_I2C_SCL_PIN, Bit_RESET);
	}

	// issue the ack
	local_I2C_Delay();
	GPIO_WriteBit(SOFT_I2C_PORT, SOFT_I2C_SDA_PIN, ack ? Bit_SET : Bit_RESET);
	local_I2C_Delay();
	GPIO_WriteBit(SOFT_I2C_PORT, SOFT_I2C_SCL_PIN, Bit_SET);
	local_I2C_Delay();
	GPIO_WriteBit(SOFT_I2C_PORT, SOFT_I2C_SCL_PIN, Bit_RESET);
	local_I2C_Delay();
	GPIO_WriteBit(SOFT_I2C_PORT, SOFT_I2C_SDA_PIN, Bit_SET);
	local_I2C_Delay();

	return data;
}

// returns ack state, 0 means acknowledged
static char local_I2C_WriteByte(uint8_t data)
{
	char i;

	// send the 8 bits
	for(i = 0; i < 8; i++) {
		GPIO_WriteBit(SOFT_I2C_PORT, SOFT_I2C_SDA_PIN, (data & 0x80) ? Bit_SET : Bit_RESET);
		data <<= 1;
		local_I2C_Delay();
		GPIO_WriteBit(SOFT_I2C_PORT, SOFT_I2C_SCL_PIN, Bit_SET);
		local_I2C_Delay();
		GPIO_WriteBit(SOFT_I2C_PORT, SOFT_I2C_SCL_PIN, Bit_RESET);
	}

	// read the ack
	GPIO_WriteBit(SOFT_I2C_PORT, SOFT_I2C_SDA_PIN, Bit_SET);
	local_I2C_Delay();
	GPIO_WriteBit(SOFT_I2C_PORT, SOFT_I2C_SCL_PIN, Bit_SET);
	local_I2C_Delay();
	i = GPIO_ReadInputDataBit(SOFT_I2C_PORT, SOFT_I2C_SDA_PIN);
	GPIO_WriteBit(SOFT_I2C_PORT, SOFT_I2C_SCL_PIN, Bit_RESET);
	local_I2C_Delay();

	return i;
}

// returns ack state, 0 means acknowledged
char local_I2C_WriteReg(uint8_t dev_addr, uint8_t reg_addr, uint8_t data)
{
	char result = 0;

	local_I2C_Start();
	result |= local_I2C_WriteByte(dev_addr); //reset the last bit
	result |= local_I2C_WriteByte(reg_addr); // id address
	result |= local_I2C_WriteByte(data); // id address
	local_I2C_Stop();

	return result;
}

uint8_t local_I2C_ReadReg(uint8_t dev_addr, uint8_t reg_addr)
{
	uint8_t data;

	local_I2C_Start();
	local_I2C_WriteByte(dev_addr & 0xFE);
	local_I2C_WriteByte(reg_addr); // id address
	local_I2C_Stop();
	//if(result) break;

	local_I2C_Start();
	local_I2C_WriteByte(dev_addr);
	data = local_I2C_ReadByte(NACK);
	local_I2C_Stop();

	return data;
}
By gammafun
#163597
Thanks brijesh! sparkfun doesn't tell me there's a response to the post so I didn't know.

My code is actually very similar to what you just showed me and it still doesn't work. I'm guessing the only difference is timing. Do you mind telling me what's the sys clock frequency that you used and if you used a pull-up resistor? I'm running at 168MHz and put about 500us delay between each output/input.
By brijesh
#163602
The CPU clock is not relevant here, as the delays are hard coded. The Camera module needs a clock for I2C to work, are you sending clock to camera?
By gammafun
#163754
Yea, I'm using a clk generated from the HSI to run XCLK. After some more fiddling I finally got the camera to ack. And I have successfully write and read from the registers.

The problem I'm having right now is the image I grabbed from the camera is all gibberish. Eg, if I cover the lens, only some portion of the image will be black and there will be random white spots. I pretty much leave all the register settings to default and only read the Y components to get a gray-scale image.

I don't know what's causing the issue... I've done some research and it seems that we should be able to get a decent gray-scale image by using the default settings. It'd be great if you can provide any info/advice/suggestion