stm32 OV7670摄像头模块的介绍以及应用(SCCB的使用)

02-27 1229阅读

今天学习摄像头模块,使用该模块进行拍摄,照相等功能进行对stm32 控制板的结合,了解他的原理以及应用。

主角: ov7670摄像头模块。(大概了解一下产品背景)

 OV7670 是 OV( OmniVision)公司生产的一颗 1/6 寸的 CMOS VGA 图像传 感器。该传感器体积小、工作电压低,提供单片 VGA 摄像头和影像处理器的所 有功能。通过 SCCB 总线控制,可以输出整帧、子采样、取窗口等方式的各种分 辨率 8 位影像数据。该产品 VGA 图像最高达到 30 帧/秒。用户可以完全控制 图像质量、数据格式和传输方式。所有图像处理功能过程包括伽玛曲线、白平衡、 度、色度等都可以通过 SCCB 接口编程。 OmmiVision 图像传感器应用独有的传 感器技术,通过减少或消除光学或电子缺陷如固定图案噪声、托尾、浮散等,提 高图像质量,得到清晰的稳定的彩色图像。(官方说明请进一步查看说明文档,部分 中文版翻译有出入,有能力的可以看英文版的)

SCCB?(后面会用到这里简单讲解一下)

 SCCB是欧姆尼图像技术公司(OmniVision)开发的一种总线,并广泛的应用于OV系列图像传感器上,所以一般使用OV的图像传感器都离不开SCCB总线协议。可以通俗地讲SCCB有两种工作模式,一主多从,一主一从模式。

参考正反面接线柱以及元器件(如下图)

stm32 OV7670摄像头模块的介绍以及应用(SCCB的使用)

实物图如下

stm32 OV7670摄像头模块的介绍以及应用(SCCB的使用)

OV7670 的特点有:

(1)高灵敏度、低电压适合嵌入式应用

(2)标准的 SCCB 接口,兼容 IIC 接口

(3)支持 RawRGB、RGB(GBR4:2:2,RGB565/RGB555/RGB444),YUV(4:2:2) 和 YCbCr( 4:2:2)输出格式

(4)支持 VGA、 CIF,和从 CIF 到 40*30 的各种尺寸输出

(5)支持自动曝光控制、自动增益控制、自动白平衡、自动消除灯光条纹、 自动黑电平校准等自动控制功能。同时支持色饱和度、色相、伽马、锐度等设置。

(6)支持闪光灯

(7)支持图像缩放

应用,进行拍摄,画面传输在控制板的TFT 屏幕上,下期更新拍照。

OV7670 传感器 功能模块:

1.感光整列( Image Array) OV7670 总共有 656488 个像素,其中 640480 个有效(即有效像素为 30W)。

2.时序发生器( Video Timing Generator) 时序发生器具有的功能包括:整列控制和帧率发生( 7 种不同格式输出)、 内部信号发生器和分布、帧率时序、自动曝光控制、输出外部时序( VSYNC帧同步信号、 HREF/HSYNC同步信号 和 PCLK像素时钟)。

3.模拟信号处理( Analog Processing) 模拟信号处理所有模拟功能,并包括:自动增益( AGC)和自动白平衡( AWB)。

4.A/D 转换( A/D) 原始的信号经过模拟处理器模块之后 ,分 G 和 BR 两路进入一个 10 位的 A/D 转换器,A/D 转换器工作在 12M 频率,与像素频率完全同步(转换的频率 和帧率有关)。 除 A/D 转换器外,该模块还有以下三个功能: ①黑电平校正( BLC) ②U/V 通道延迟 ③A/D 范围控制 A/D 范围乘积和 A/D 的范围控制共同设置 A/D 的范围和最大值,允许用户 2根据应用调整图片的亮度。

5.测试图案发生器( Test Pattern Generator) 测试图案发生器功能包括:八色彩色条图案、渐变至黑白彩色条图案和输出 脚移位“ 1”。

6.数字处理器( DSP) 这个部分控制由原始信号插值到 RGB 信号的过程,并控制一些图像质量: ①边缘锐化(二维高通滤波器) ②颜色空间转换(原始信号到 RGB 或者 YUV/YCbYCr) ③RGB 色彩矩阵以消除串扰 ④色相和饱和度的控制 ⑤黑/白点补偿 ⑥降噪 ⑦镜头补偿 ⑧可编程的伽玛 ⑨十位到八位数据转换

7.缩放功能( Image Scaler) 这个模块按照预先设置的要求输出数据格式,能将 YUV/RGB 信号从 VGA 缩 小到 CIF 以下的任何尺寸。

8.数字视频接口( Digital Video Port) 通过寄存器 COM2[1:0],调节 IOL/IOH 的驱动电流,以适应用户的负载。

9.SCCB 接口( SCCB Interface) SCCB 接 口 控 制 图 像 传 感 器 芯 片 的 运 行 , 详 细 使 用 方 法 参 照 《 OmniVisionTechnologies Seril Camera Control Bus(SCCB) Specification》 这个文档。

10.LED 和闪光灯的输出控制( LED and Storbe Flash Control Output) OV7670 有闪光灯模式,可以控制外接闪光灯或闪光 LED 的工作。 OV7670 的寄存器通过 SCCB 时序访问并设置,SCCB 时序和 IIC 时序十分 类似,在这里我们不做介绍,请大家参考模块的相关文档。

重点定义:

VGA,即分辨率为 640480 的输出模式;

QVGA,即分辨率为 320240 的输出格式,也就是本手册我们需要用到的格 式;

QQVGA,即分辨率为 160*120 的输出格式;

PCLK,即像素时钟,一个 PCLK 时钟,输出一个像素(或半个像素)。

VSYNC,即帧同步信号。

HREF /HSYNC,即行同步信号。

图像输出的控制条件

OV7670 的图像数据输出(通过 D[7:0])就是在 PCLK, VSYNC 和 HREF/ HSYNC 的控制下进行的。

首先看看行输出时序,如图所示:

stm32 OV7670摄像头模块的介绍以及应用(SCCB的使用)

stm32 OV7670摄像头模块的介绍以及应用(SCCB的使用)

-----上属已经初步了解了0V7670的原理和背景(下面市重点),下面学习OV7670 模块使用方法-------------------------

下面图像数据的存储于读取的过程分析

如何存储图像数据?

-OV7670 摄像头模块存储图像数据的过程为:等待 OV7670 同步信号→ FIFO 写指针复位→FIFO 写使能→等待第二个 OV7670 同步信号→FIFO 写禁 止。通过以上 5 个步骤,我们就完成了 1 帧图像数据的存储

如何读取图像数据?

在存储完一帧图像以后,我们就可以开始读取图像数据了。读取过程为: FIFO 读指针复位→给 FIFO 读时钟( FIFO_RCLK)→读取第一个像素高字节→ 给 FIFO 读时钟→读取第一个像素低字节→给 FIFO 读时钟→读取第二个像素 高字节→循环读取剩余像素→结束。

原理图如下所示

stm32 OV7670摄像头模块的介绍以及应用(SCCB的使用)

由上图可以看出0v7670(图片信号) ------传输数据到--------->FIFO------------------------>经过输出模块------------------->mcu (我们拿到的图片信息)

stm32 OV7670摄像头模块的介绍以及应用(SCCB的使用)

下面直接开始分析代码函数的使用

初始化SCCB接口(前面讲到使用sccb 进行控制图像)/

初始化OV7670

设置图像输出窗口

帧中断标记

更新LCD显示

//初始化SCCB接口
//CHECK OK
void SCCB_Init(void)
{			
 	GPIO_InitTypeDef  GPIO_InitStructure;
 	
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOG, ENABLE);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;				 // 端口配置
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; 		 //输入
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOG, &GPIO_InitStructure);
 	GPIO_SetBits(GPIOG,GPIO_Pin_13);						 // 输出高
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;				 // 端口配置
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //输输出
 	GPIO_Init(GPIOD, &GPIO_InitStructure);
 	GPIO_SetBits(GPIOD,GPIO_Pin_3);						 // 输出高
 
	SCCB_SDA_OUT();   
}			 
	    
//初始化OV7670
//返回0:成功
//返回其他值:错误代码
u8 OV7670_Init(void)
{
	u8 temp;
	u16 i=0;	  
	//设置IO
 	GPIO_InitTypeDef  GPIO_InitStructure;
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOG|RCC_APB2Periph_AFIO, ENABLE);	 //使能相关端口时钟
 
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_8; 	//PA8 输入 上拉 **VSYNC**
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOA, &GPIO_InitStructure);
	GPIO_SetBits(GPIOA,GPIO_Pin_8);
		
 	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4;// 端口配置SWD 禁止swj 因为默认的PB3和pb4 是Jtag下载占用
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
 	GPIO_Init(GPIOB, &GPIO_InitStructure);
 	GPIO_SetBits(GPIOB,GPIO_Pin_3|GPIO_Pin_4);	
	GPIO_InitStructure.GPIO_Pin  = 0xff; //PC0~7 输入 上拉
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
 	GPIO_Init(GPIOC, &GPIO_InitStructure);
	 
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_6;  
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
 	GPIO_Init(GPIOD, &GPIO_InitStructure);
	GPIO_SetBits(GPIOD,GPIO_Pin_6);
	
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_14|GPIO_Pin_15;  
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
 	GPIO_Init(GPIOG, &GPIO_InitStructure);
	GPIO_SetBits(GPIOG,GPIO_Pin_14|GPIO_Pin_15);
	
	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);	//SWD 禁止swj 因为默认的PB3和pb4 是Jtag下载占用的
	
 	SCCB_Init();        		//初始化SCCB 的IO口	   	  
 	if(SCCB_WR_Reg(0x12,0x80))return 1;	//复位SCCB	  
	delay_ms(50);  
	//读取产品型号
 	temp=SCCB_RD_Reg(0x0b);   
	if(temp!=0x73)return 2;  
 	temp=SCCB_RD_Reg(0x0a);   
	if(temp!=0x76)return 2;
	//初始化序列	  
	for(i=0;i
		//初始化寄存器序列及其对应的值
	   	SCCB_WR_Reg(ov7670_init_reg_tbl[i][0],ov7670_init_reg_tbl[i][1]); //SCCB_WR_Reg对ov7670_init_reg_tbl的写入完成初始化
  	}
   	return 0x00; 	//ok
} 

	u16 endx;
	u16 endy;
	u8 temp; 
	endx=sx+width*2;	//V*2
 	endy=sy+height*2;
	if(endy784)endy-=784;
	temp=SCCB_RD_Reg(0X03);				//读取Vref之前的值
	temp&=0XF0;
	temp|=((endx&0X03)2);			//设置Vref的start高8位
	SCCB_WR_Reg(0X1A,endx>>2);			//设置Vref的end的高8位
	temp=SCCB_RD_Reg(0X32);				//读取Href之前的值
	temp&=0XC0;
	temp|=((endy&0X07)3);			//设置Href的start高8位
	SCCB_WR_Reg(0X18,endy>>3);			//设置Href的end的高8位
}
//等待ov7670 帧同步信号(帧中断标记是否开始)
u8 ov_sta;	//帧中断标记
void EXTI9_5_IRQHandler(void)	  //红外遥控外部中断
{
	if(EXTI_GetITStatus(EXTI_Line8)==SET)	//是8线的中断
	{      
		if(ov_sta
			if(ov_sta==0)
			{	 
				OV7670_WRST=0;	//复位写指针		  		 
				OV7670_WRST=1;	
				OV7670_WREN=1;	//允许写入FIFO
			}
			else
			{
				OV7670_WREN=0;//禁止写入FIFO
				OV7670_WRST=0;	//复位写指针		  		 
				OV7670_WRST=1;
			}
			ov_sta++;		//帧中断加1
		}
	}
	EXTI_ClearITPendingBit(EXTI_Line8);  //清除EXTI8线路挂起位
}

	u32 j;
 	u16 color;
	if(ov_sta)//有帧中断更新?
	{
		LCD_Display_Dir(1);
		LCD_Set_Window(0,0,320-1,240-1);//将显示区域设置到屏幕中央
		
		OV7670_RRST=0;				//开始复位读指针 
		OV7670_RCK_L;
		OV7670_RCK_H;
		OV7670_RCK_L;
		OV7670_RRST=1;				//复位读指针结束 
		OV7670_RCK_H;
		for(j=0;j
			OV7670_RCK_L;
			color=GPIOC-IDR&0XFF;	//读数据
			OV7670_RCK_H; 
			colorCRH|=0X00800000;}
//#define SCCB_SDA_OUT() {GPIOG->CRH&=0XFF0FFFFF;GPIOG->CRH|=0X00300000;}
//IO操作函数	 	 
#define SCCB_SCL    		PDout(3)	 	//SCL
#define SCCB_SDA    		PGout(13) 		//SDA	 
#define SCCB_READ_SDA    	PGin(13)  		//输入SDA    
#define SCCB_ID   			0X42  			//OV7670的ID
///
void SCCB_Init(void);
void SCCB_Start(void);
void SCCB_Stop(void);
void SCCB_No_Ack(void);
u8 SCCB_WR_Byte(u8 dat);
u8 SCCB_RD_Byte(void);
u8 SCCB_WR_Reg(u8 reg,u8 data);
u8 SCCB_RD_Reg(u8 reg);
#endif
sccb。c
```c
#include "system.h"
#include "sccb.h"
#include "SysTick.h"
void SCCB_SDA_OUT(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	
 	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;				 // 端口配置
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOG, &GPIO_InitStructure);
}
void SCCB_SDA_IN(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	
 	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;				 // 端口配置
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; 		 //输入
 	GPIO_Init(GPIOG, &GPIO_InitStructure);
}
 
//初始化SCCB接口
//CHECK OK
void SCCB_Init(void)
{			
 	GPIO_InitTypeDef  GPIO_InitStructure;
 	
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOG, ENABLE);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;				 // 端口配置
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; 		 //输入
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOG, &GPIO_InitStructure);
 	GPIO_SetBits(GPIOG,GPIO_Pin_13);						 // 输出高
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;				 // 端口配置
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //输输出
 	GPIO_Init(GPIOD, &GPIO_InitStructure);
 	GPIO_SetBits(GPIOD,GPIO_Pin_3);						 // 输出高
 
	SCCB_SDA_OUT();   
}			 
//SCCB起始信号
//当时钟为高的时候,数据线的高到低,为SCCB起始信号
//在激活状态下,SDA和SCL均为低电平
void SCCB_Start(void)
{
    SCCB_SDA=1;     //数据线高电平	   
    SCCB_SCL=1;	    //在时钟线高的时候数据线由高至低
    delay_us(50);  
    SCCB_SDA=0;
    delay_us(50);	 
    SCCB_SCL=0;	    //数据线恢复低电平,单操作函数必要	  
}
//SCCB停止信号
//当时钟为高的时候,数据线的低到高,为SCCB停止信号
//空闲状况下,SDA,SCL均为高电平
void SCCB_Stop(void)
{
    SCCB_SDA=0;
    delay_us(50);	 
    SCCB_SCL=1;	
    delay_us(50); 
    SCCB_SDA=1;	
    delay_us(50);
}  
//产生NA信号
void SCCB_No_Ack(void)
{
	delay_us(50);
	SCCB_SDA=1;	
	SCCB_SCL=1;	
	delay_us(50);
	SCCB_SCL=0;	
	delay_us(50);
	SCCB_SDA=0;	
	delay_us(50);
}
//SCCB,写入一个字节
//返回值:0,成功;1,失败. 
u8 SCCB_WR_Byte(u8 dat)
{
	u8 j,res;	 
	for(j=0;j
		if(dat&0x80)SCCB_SDA=1;	
		else SCCB_SDA=0;
		dat
	u8 temp=0,j;    
	SCCB_SDA_IN();		//设置SDA为输入  
	for(j=8;j0;j--) 	//循环8次接收数据
	{		     	  
		delay_us(50);
		SCCB_SCL=1;
		temp=temp
	u8 res=0;
	SCCB_Start(); 					//启动SCCB传输
	if(SCCB_WR_Byte(SCCB_ID))res=1;	//写器件ID	  
	delay_us(100);
  	if(SCCB_WR_Byte(reg))res=1;		//写寄存器地址	  
	delay_us(100);
  	if(SCCB_WR_Byte(data))res=1; 	//写数据	 
  	SCCB_Stop();	  
  	return	res;
}		  					    
//读寄存器
//返回值:读到的寄存器值
u8 SCCB_RD_Reg(u8 reg)
{
	u8 val=0;
	SCCB_Start(); 				//启动SCCB传输
	SCCB_WR_Byte(SCCB_ID);		//写器件ID	  
	delay_us(100);	 
  	SCCB_WR_Byte(reg);			//写寄存器地址	  
	delay_us(100);	  
	SCCB_Stop();   
	delay_us(100);	   
	//设置寄存器地址后,才是读
	SCCB_Start();
	SCCB_WR_Byte(SCCB_ID|0X01);	//发送读命令	  
	delay_us(100);
  	val=SCCB_RD_Byte();		 	//读取数据
  	SCCB_No_Ack();
  	SCCB_Stop();
  	return val;
}

	u32 j;
 	u16 color;
	if(ov_sta)//有帧中断更新?
	{
		LCD_Display_Dir(1);
		LCD_Set_Window(0,0,320-1,240-1);//将显示区域设置到屏幕中央
		
		OV7670_RRST=0;				//开始复位读指针 
		OV7670_RCK_L;
		OV7670_RCK_H;
		OV7670_RCK_L;
		OV7670_RRST=1;				//复位读指针结束 
		OV7670_RCK_H;
		for(j=0;j
			OV7670_RCK_L;
			color=GPIOC-IDR&0XFF;	//读数据
			OV7670_RCK_H; 
			color
				LCD_DrawRectangle( result.x-result.w/2, result.y-result.h/2, result.x-result.w/2+result.w,  result.y-result.h/2+result.h);
				LCD_DrawRectangle( result.x-2, result.y-2,result.x+2, result.y+2);
		}
	} 
}
const u8*LMODE_TBL[5]={"Auto","Sunny","Cloudy","Office","Home"};
const u8*EFFECTS_TBL[7]={"Normal","Negative","B&W","Redish","Greenish","Bluish","Antique"};	//7种特效 
int main()
{
	u8 i=0;
	u8 key;
	u8 lightmode=0,saturation=2,brightness=2,contrast=2;
	u8 effect=0;
	u8 sbuf[15];
	u8 count;
	
	SysTick_Init(72);
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  //中断优先级分组 分2组
	LED_Init();
	USART1_Init(115200);
	TFTLCD_Init();			//LCD初始化
	KEY_Init();
	EN25QXX_Init();				//初始化EN25Q128	 
	my_mem_init(SRAMIN);		//初始化内部内存池
	
	FRONT_COLOR=RED;//设置字体为红色 
	LCD_ShowString(10,10,tftlcd_data.width,tftlcd_data.height,16,"PRECHIN STM32F1");	
	LCD_ShowString(10,30,tftlcd_data.width,tftlcd_data.height,16,"www.prechin.net");
	LCD_ShowString(10,50,tftlcd_data.width,tftlcd_data.height,16,"OV7670 Test");
	
	while(OV7670_Init())//初始化OV7670
	{
		LCD_ShowString(10,80,tftlcd_data.width,tftlcd_data.height,16,"OV7670 Error!");
		delay_ms(200);
		LCD_Fill(10,80,239,206,WHITE);
		delay_ms(200);
	}
 	LCD_ShowString(10,80,tftlcd_data.width,tftlcd_data.height,16,"OV7670 OK!     ");
	delay_ms(1500);	 
	OV7670_Light_Mode(0);
	OV7670_Color_Saturation(2);
	OV7670_Brightness(2);
	OV7670_Contrast(2);
 	OV7670_Special_Effects(0);
		
	TIM4_Init(10000,7199);			//10Khz计数频率,1秒钟中断									  
	EXTI8_Init();			
	OV7670_Window_Set(12,176,240,320);	//设置窗口	
  	OV7670_CS=0;	
	LCD_Clear(BLACK);
	while(1)
	{
		key=KEY_Scan(0);
		if(key)count=20;
		switch(key)
		{
			case KEY_UP_PRESS:           //灯光模式设置
				lightmode++;
				if(lightmode4)lightmode=0;
				OV7670_Light_Mode(lightmode);
				sprintf((char*)sbuf,"%s",LMODE_TBL[lightmode]);
				break;
			case KEY1_PRESS:         //饱和度
				saturation++;
				if(saturation4)saturation=0;
				OV7670_Color_Saturation(saturation);
				sprintf((char*)sbuf,"Saturation:%d",(char)saturation-2);
				break;
			case KEY2_PRESS:        //亮度
				brightness++;
				if(brightness4)brightness=0;
				OV7670_Brightness(brightness);
				sprintf((char*)sbuf,"Brightness:%d",(char)brightness-2);
				break;
			case KEY0_PRESS:     //对比度
				contrast++;
				if(contrast4)contrast=0;
				OV7670_Contrast(contrast);
				sprintf((char*)sbuf,"Contrast:%d",(char)contrast-2);
				break;
		}
		if(count)
		{
			count--;
			LCD_ShowString((tftlcd_data.width-240)/2+30,(tftlcd_data.height-320)/2+60,200,16,16,sbuf);
		}
		camera_refresh();//更新显示
		i++;
		if(i%20==0)
		{
			LED1=!LED1;
		}
//		delay_ms(5);	
	}
}
VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]