OV7670 Camera Module
The OV7670/OV7171 CAMERACHIPTM is a low voltage CMOS image sensor that provides the full functionality of a single-chip VGA camera and image processor in a small footprint package. The OV7670/OV7171 provides full-frame, sub-sampled or windowed 8-bit images in a wide range of formats, controlled through the Serial Camera Control Bus (SCCB) interface.


This product has an image array capable of operating at up to 30 frames per second (fps) in VGA with complete user control over image quality, formatting and output data transfer. All required image processing functions, including exposure control, gamma, white balance, color saturation, hue control and more, are also programmable through the SCCB interface. In addition, Omni Vision CAMERACHIPs use proprietary sensor technology to improve image quality by reducing or eliminating common lighting/electrical sources of image contamination, such as fixed pattern noise (FPN), smearing, blooming, etc., to produce a clean, fully stable color image.
Features:
- Optical size 1/6 inch
- High sensitivity for low-light operation
- Low operating voltage for embedded portable apps
- Standard SCCB interface compatible with I2C interface
- VarioPixel® method for sub-sampling Ø Automatic image control functions including: Automatic
- Image quality controls including color saturation, hue, gamma, sharpness (edge enhancement), and anti-blooming
- ISP includes noise reduction and defect correction
- Supports LED and flash strobe mode
- Supports scaling
- Lens shading correction
- Flicker (50/60 Hz) auto detection
- Saturation level auto adjust (UV adjust)
- Edge enhancement level auto adjust
- De-noise level auto adjust 0.3M Pixels CMOS
Specification:
- Resolution 640×480 VGA
- Onboard regulator, only single 3.3V supply needed
- Standard 0.1inch (2.54mm) pin pitch header connector
- Mounted with high quality F1.8 / 6mm lens
- Output support for Raw RGB, RGB (GRB 4:2:2, RGB565/555/444), YUV (4:2:2) and YCbCr (4:2:2) formats
- Supports image sizes: VGA, CIF, and any size scaling down from CIF to 40×30
- Exposure Control (AEC), Automatic Gain Control (AGC), Automatic White Balance (AWB), Automatic
- Band Filter (ABF), and Automatic Black-Level Calibration (ABLC)
Interfacing with Arduino:

Code:
#define F_CPU 16000000UL
#include <stdint.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/twi.h>
#include <util/delay.h>
#include <avr/pgmspace.h>
#include “ov7670.h”
/* Configuration: this lets you easily change between different resolutions
* You must only uncomment one
* no more no less*/
#define useVga
//#define useQvga
//#define useQqvga
static inline void serialWrB(uint8_t dat){
while(!( UCSR0A & (1<<UDRE0)));//wait for byte to transmit
UDR0=dat;
while(!( UCSR0A & (1<<UDRE0)));//wait for byte to transmit
}
static void StringPgm(const char * str){
do{
serialWrB(pgm_read_byte_near(str));
}while(pgm_read_byte_near(++str));
}
static void captureImg(uint16_t wg,uint16_t hg){
uint16_t lg2;
#ifdef useQvga
uint8_t buf[640];
#elif defined(useQqvga)
uint8_t buf[320];
#endif
StringPgm(PSTR(“RDY”));
//Wait for vsync it is on pin 3 (counting from 0) portD
while(!(PIND&8));//wait for high
while((PIND&8));//wait for low
#ifdef useVga
while(hg–){
lg2=wg;
while(lg2–){
while((PIND&4));//wait for low
UDR0=(PINC&15)|(PIND&240);
while(!(PIND&4));//wait for high
}
}
#elif defined(useQvga)
/*We send half of the line while reading then half later */
while(hg–){
uint8_t*b=buf,*b2=buf;
lg2=wg/2;
while(lg2–){
while((PIND&4));//wait for low
*b++=(PINC&15)|(PIND&240);
while(!(PIND&4));//wait for high
while((PIND&4));//wait for low
*b++=(PINC&15)|(PIND&240);
UDR0=*b2++;
while(!(PIND&4));//wait for high
}
/* Finish sending the remainder during blanking */
lg2=wg/2;
while(!( UCSR0A & (1<<UDRE0)));//wait for byte to transmit
while(lg2–){
UDR0=*b2++;
while(!( UCSR0A & (1<<UDRE0)));//wait for byte to transmit
}
}
#else
/* This code is very similar to qvga sending code except we have even more blanking time to take advantage of */
while(hg–){
uint8_t*b=buf,*b2=buf;
lg2=wg/5;
while(lg2–){
while((PIND&4));//wait for low
*b++=(PINC&15)|(PIND&240);
while(!(PIND&4));//wait for high
while((PIND&4));//wait for low
*b++=(PINC&15)|(PIND&240);
while(!(PIND&4));//wait for high
while((PIND&4));//wait for low
*b++=(PINC&15)|(PIND&240);
while(!(PIND&4));//wait for high
while((PIND&4));//wait for low
*b++=(PINC&15)|(PIND&240);
while(!(PIND&4));//wait for high
while((PIND&4));//wait for low
*b++=(PINC&15)|(PIND&240);
UDR0=*b2++;
while(!(PIND&4));//wait for high
}
/* Finish sending the remainder during blanking */
lg2=320-(wg/5);
while(!( UCSR0A & (1<<UDRE0)));//wait for byte to transmit
while(lg2–){
UDR0=*b2++;
while(!( UCSR0A & (1<<UDRE0)));//wait for byte to transmit
}
}
#endif
}
int main(void){
cli();//disable interrupts
/* Setup the 8mhz PWM clock
* This will be on pin 11*/
DDRB|=(1<<3);//pin 11
ASSR &= ~(_BV(EXCLK) | _BV(AS2));
TCCR2A=(1<<COM2A0)|(1<<WGM21)|(1<<WGM20);
TCCR2B=(1<<WGM22)|(1<<CS20);
OCR2A=0;//(F_CPU)/(2*(X+1))
DDRC&=~15;//low d0-d3 camera
DDRD&=~252;//d7-d4 and interrupt pins
_delay_ms(3000);
//set up twi for 100khz
TWSR&=~3;//disable prescaler for TWI
TWBR=72;//set to 100khz
//enable serial
UBRR0H=0;
UBRR0L=1;//0 = 2M baud rate. 1 = 1M baud. 3 = 0.5M. 7 = 250k 207 is 9600 baud rate.
UCSR0A|=2;//double speed aysnc
UCSR0B = (1<<RXEN0)|(1<<TXEN0);//Enable receiver and transmitter
UCSR0C=6;//async 1 stop bit 8bit char no parity bits
camInit();
#ifdef useVga
setRes(VGA);
setColorSpace(BAYER_RGB);
wrReg(0x11,25);
#elif defined(useQvga)
setRes(QVGA);
setColorSpace(YUV422);
wrReg(0x11,12);
#else
setRes(QQVGA);
setColorSpace(YUV422);
wrReg(0x11,3);
#endif
/* If you are not sure what value to use here for the divider (register 0x11)
* Values I have found to work raw vga 25 qqvga yuv422 12 qvga yuv422 21
* run the commented out test below and pick the smallest value that gets a correct image */
while (1){
/* captureImg operates in bytes not pixels in some cases pixels are two bytes per pixel
* So for the width (if you were reading 640×480) you would put 1280 if you are reading yuv422 or rgb565 */
/*uint8_t x=63;//Uncomment this block to test divider settings note the other line you need to uncomment
do{
wrReg(0x11,x);
_delay_ms(1000);*/
#ifdef useVga
captureImg(640,480);
#elif defined(useQvga)
captureImg(320*2,240);
#else
captureImg(160*2,120);
#endif
//}while(–x);//Uncomment this line to test divider settings
}
}
Application:
- Cellular phones
- PDAs
- Toys
- Other battery-powered products
- Can be used in Arduino, Maple, ChipKit, STM32, ARM, DSP, FPGA
Reference:
https://www.openhacks.com/uploadsproductos/ov7670_cmos_camera_module_revc_ds.pdf
https://forum.arduino.cc/index.php?topic=20828.0
https://circuitdigest.com/microcontroller-projects/how-to-use-ov7670-camera-module-with-arduino
https://www.instructables.com/id/OV7670-Arduino-Camera-Sensor-Module-Framecapture-T/