First, let's briefly introduce the camera OV7670 used. OV7670 is a 1/6-inch CMOS VGA image sensor produced by OV (OmniVision). The sensor is small in size and low in operating voltage, and provides all the functions of a single-chip VGA camera and image processor. Through SCCB bus control, it can output 8-bit image data of various resolutions in the form of full frame, sub-sampling, windowing, etc. The VGA image of this product reaches up to 30 frames per second. Users can fully control the image quality, data format and transmission method. All image processing functions including gamma curve, white balance, brightness, chromaticity, etc. can be programmed through the SCCB interface. OmmiVision image sensors use unique sensor technology to improve image quality by reducing or eliminating optical or electronic defects such as fixed pattern noise, tailing, and floating, and obtain clear and stable color images. It has high sensitivity, low voltage, suitable for embedded applications, standard SCCB interface, and supports RGB565 format output. Because the pixel clock of the camera is very high, it is very difficult to read data directly through the IO port of MSP430, and it also consumes a lot of CPU. By using OV7670 with FIFO module, the image data collected by the camera can be easily read by reading FIFO. This can meet the speed requirements and save CPU. The OV7670 used this time has its own active crystal oscillator and does not need an external clock. The capacity of a FIFO chip is 384K bytes, which can store two frames of QVGA image data, so this design uses QVGA mode RGB565 format to transmit image data. Next, the specific driver is introduced.
Macro definition:
//SCL-P1.3,SDA-P1.6
#define SCCB_SIC_H() P1OUT|=BIT3
#define SCCB_SIC_L() P1OUT&=~BIT3
#define SCCB_SID_H() P1OUT|=BIT6
#define SCCB_SID_L() P1OUT&=~BIT6
# SCCB_SID_IN P1DIR &= ~BIT6
#define SCCB_SID_OUT P1DIR |= BIT6
#define SCCB_SID_STATE P1IN&BIT6
#define OE_L P4OUT &= ~BIT3
#define OE_H P4OUT |= BIT3
#define RCLK_L P4OUT &= ~BIT4
#define RCLK_H P4OUT |= BIT4
#define WEN_L P4OUT &= ~BIT5
#define WEN_H P4OUT |= BIT5
#define WRST_L P4OUT &= ~BIT6
#define WRST_H P4OUT |= BIT6
#define RRST_L P4OUT &= ~BIT7
#define RRST_H P4OUT |= BIT7
//Pixel storage
#define piexl_w 320
#define piexl_h 240
OV7670 initialization procedure:
unsigned char ov7670_init(void)
{
unsigned int i=0;
unsigned char temp;
//VSYNC-P1.0
//Pull-up input, external interrupt
P1DIR &= ~BIT0;
P1REN |= BIT0;
P1OUT |= BIT0; //Pull-up input
//FIFO data input pins
//D0-D3--P6.4-P6.7,D4-D7--P7.4--P7.7
//Pull-up input
P6DIR &= 0x0f;
P6REN |= 0xf0;
P6OUT |= 0xf0;
P7DIR &= 0x0f;
P7REN |= 0xf0;
P7OUT |= 0xf0;
//OE-P4.3,RCLK-P4.4,WEN-P4.5,WRST-P4.6,RRST-P4.7
//Output
P4DIR |= 0xf8;
P4OUT |= 0xf8;
SCCB_init();
//Error in read and write register function
if(wr_Sensor_Reg(0x12,0x80)!= 0 ) //Reset SCCB
{
return 1;//Error return
}
delay_ms(50);
if(rd_Sensor_Reg(0x0b, &temp) != 0)//Read ID
{
return 2 ;//Error return
}
if(temp==0x73)//OV7670
{
for(i=0;i<OV7670_REG_NUM;i++)
{
if(wr_Sensor_Reg(OV7670_reg[i][0],OV7670_reg[i][1]) != 0)
{
return 3;//Error return
}
}
}
return 0; //ok
}
The write register operation function is as follows:
//Function: Read OV7660 register
//Return: 0-successful, others failed
unsigned char rd_Sensor_Reg(unsigned char regID,unsigned char *regDat)
{
//Set register address by write operation
startSCCB();
if(SCCBwriteByte(0x42)==0)//Write address
{
return 1;//Error return
}
delay_us(100);
if(SCCBwriteByte(regID)==0)//Accumulator ID
{
return 2;//Error return
}
delay_us(100);
stopSCCB();//Send SCCB bus stop transmission command
delay_us(100);
//Read after setting register address
startSCCB();
if(SCCBwriteByte(0x43)==0)//Read address
{
return 3;//Error return
}
delay_us(100);
*regDat=SCCBreadByte();//Return the read value
noAck();//Send NACK command
stopSCCB();//Send SCCB bus stop transmission command
return 0;//Successful return
}
The operation of reading register is as follows:
//Function: Read OV7660 register
//Return: 0-successful, others failed
unsigned char rd_Sensor_Reg(unsigned char regID,unsigned char *regDat)
{
//Set register address by write operation
startSCCB();
if(SCCBwriteByte(0x42)==0)//Write address
{
return 1;//Error return
}
delay_us(100);
if(SCCBwriteByte(regID)==0)//Accumulator ID
{
return 2;//Error return
}
delay_us(100);
stopSCCB();//Send SCCB bus stop transmission command
delay_us(100);
//Read after setting register address
startSCCB();
if(SCCBwriteByte(0x43)==0)//Read address
{
return 3;//Error return
}
delay_us(100);
*regDat=SCCBreadByte();//Return the read value
noAck();//Send NACK command
stopSCCB();//Send SCCB bus stop transmission command
return 0;//Successful return
}
The simple SCCB bus control protocol is as follows:
/*
---------------------------------------------------------------
Function: Initialize SCCB port, SCL-P1.3, output, SCL-P1.3, output
Parameter: None
Return value: None
-----------------------------------------------
*/
void SCCB_init(void)
{
//SDA-P1.6, pull-up input
P1DIR &= ~BIT6;
P1REN |= BIT6;
P1OUT |= BIT6;
//SCL-P1.3, output
P1DIR |= BIT3;
P1OUT |= BIT3;
SCCB_SID_OUT;
}
/*
-----------------------------------------------
Function: start command, SCCB start signal
Parameter: None
Return value: None
-----------------------------------------------
*/
void startSCCB(void)
{
SCCB_SID_H(); //Data line high level
SCCB_SIC_H(); //When the clock line is high, the data line goes from high to low
delay_us(50);
SCCB_SID_L();
delay_us(50);
SCCB_SIC_L(); //Clock recovery low level, single operation function necessary
}
/*
-----------------------------------------------
Function: stop command, SCCB stop signal
Parameter: None
Return value: None
-----------------------------------------------
*/
void stopSCCB(void)
{
SCCB_SID_L();
delay_us(50);
SCCB_SIC_H();
delay_us(50);
SCCB_SID_H();
delay_us(50);
}
/*
-----------------------------------------------
Function: noAck, used for the last end cycle in continuous reading
Parameter: None
Return value: None
-----------------------------------------------
*/
void noAck(void)
{
delay_us(50);
SCCB_SID_H();
SCCB_SIC_H();
delay_us(50);
SCCB_SIC_L();
delay_us(50);
SCCB_SID_L();
delay_us(50);
}
/*
-----------------------------------------------
Function: Write one byte of data to SCCB
Parameter: Write data
Return value: 1 if sending successfully, 0 if sending failed
-----------------------------------------------
*/
unsigned int SCCBwriteByte(unsigned int m_data)
{
unsigned char j,tem;
for(j=0;j<8;j++) //Loop 8 times to send data
{
if(m_data&0x80)
{
SCCB_SID_H();
}
else
{
SCCB_SID_L();
}
m_data<<=1;
delay_us(50);
SCCB_SIC_H();
delay_us(50);
SCCB_SIC_L();
}
SCCB_SID_IN;/*Set SDA as input*/
delay_us(50);
SCCB_SIC_H();
delay_us(50);
if(SCCB_SID_STATE){tem=0;} //SDA=1 send failed, return 0}
else {tem=1;} //SDA=0 send successful, return 1
SCCB_SIC_L();
SCCB_SID_OUT;/*Set SDA as output*/
return tem;
}
/*
-----------------------------------------------
Function: One byte of data read and return
Parameter: No
return value: The data read
-----------------------------------------------
*/
unsigned char SCCBreadByte(void)
{
unsigned char read,j;
read = 0x00;
SCCB_SID_IN;/*Set SDA as input*/
for(j=8;j>0;j--) //Loop 8 times to receive data
{
delay_us(50);
SCCB_SIC_H();
read=read<<1;
if(SCCB_SID_STATE)
{
read++;
}
delay_us(50);
SCCB_SIC_L();
}
SCCB_SID_OUT;/*Set SDA to output*/
return read;
}
The most important thing about image acquisition is how the FIFO module stores image data and how the microcontroller reads the image data in the FIFO module. The specific implementation steps are as follows:
The process of storing image data in the camera module is: waiting for the OV7670 synchronization signal, resetting the FIFO write pointer, enabling FIFO write, waiting for the second OV7670 synchronization signal, and disabling FIFO write.
After storing a frame of image, you can start reading image data. The reading process is: reset FIFO read pointer, give FIFO read clock (FIFO_RCLK), read the first pixel high byte, give FIFO read clock, read the first pixel low byte, give FIFO read clock, read the second pixel high byte, read the remaining pixels in a loop, and end.
Therefore, an external interrupt (P1.0 in the design) is used to capture the frame synchronization signal, and the image data of OV7670 is stored in the FIFO chip in the interrupt service function. Then, after a frame synchronization signal arrives, the data storage is closed. In this way, a frame of data is stored. The interrupt service function source code is as follows:
//FIFO module stores camera data
#pragma vector = PORT1_VECTOR
__interrupt void PORT1_B0_ISR(void)
{
if(P1IV == 2)
{
WRST_L;//Start resetting the write pointer
WRST_H;//End of resetting the write pointerif
(ov_sta == 0)
{
WEN_H;
ov_sta = 1;
}
else if(ov_sta == 1)
{
WEN_L;
ov_sta = 2;
}
}
P1IFG = 0; //Clear flag
}
When a frame of image data is stored in the FIFO chip, the data in the FIFO chip can be read in the main function. Pay special attention to the fact that when reading the image data in the FIFO chip, the CS bit must be set low, otherwise the input pin is in high impedance. Write a program according to the timing requirements. The source code is as follows:
/* OE AL422 FIFO output enable pin, when OE is low, data output is allowed,
when high, data output is in high impedance state*/
OE_L;
if(ov_sta == 2)//read data
{
P1IE &= ~BIT0;//turn off external interrupt
//set image resolution
OV7670_Window_Set(180,10,piexl_w,piexl_h);
RRST_L;//start resetting read pointer
RCLK_L;
RCLK_H;
RCLK_L;
RRST_H;//read pointer reset end
RCLK_H;
for(unsigned int p=0;p < piexl_h;p++)//transmit image piexl_w*piexl_h
{
for(unsigned int j=0;j < piexl_w;j++)
{
RCLK_L;
FIFO_1 = P6IN&0xf0;
FIFO_2 = P7IN&0xf0;
FIFO_data = (FIFO_1>>4)|FIFO_2;
data_fifo[0] = FIFO_data; //Read high byte
RCLK_H;
RCLK_L;
FIFO_1 = P6IN&0xf0; //Read low byte
FIFO_2 = P7IN&0xf0;
FIFO_data = (FIFO_1>>4)|FIFO_2;
data_fifo[1] = FIFO_data;
RCLK_H;
}
}
ov_sta = 0;
P1IE |= BIT0;
}
Because the MSP430F5438a microcontroller is used, the image data transmitted in QVGA mode is still a lot and the speed is very slow. In order to reduce the amount of image data transmitted and increase the data, a function for setting image resolution on the Internet is used for reference. The source code is as follows:
//QVGA resolution setting
void OV7670_Window_Set(unsigned int sx, unsigned int sy, unsigned int width, unsigned int height)
{
unsigned int endx;
unsigned int endy;
unsigned char temp;
endx=(sx+width*2)%784; // sx:HSTART endx:HSTOP
endy=sy+height*2; // sy:VSTRT endy:VSTOP
rd_Sensor_Reg(0x32,&temp);
temp&=0Xc0;
temp|=((endx&0X07)<<3)|(sx&0X07) ;
wr_Sensor_Reg(0X032,temp);
wr_Sensor_Reg(0X17,sx>>3);
wr_Sensor_Reg(0X18,endx>>3);
rd_Sensor_Reg(0x03,&temp);
temp&=0Xf0;
temp|=((endy&0X03)<<2)|(sy&0X03);
wr_Sensor_Reg(0X03,temp);
wr_Sensor_Reg(0X19,sy>>2);
wr_Sensor_Reg(0X1A,endy> >2);
}
|