由于这部分内容过多,分开介绍,本文章主要是介绍ov7670模块。

一、简单认识

先来看一下外观:

OV7670,图像传感器,体积小,工作电压低,提供单片VGA摄像头影像处理器的所有功能。通过SCCB总线控制,可以输入整帧、子采样、取窗口等方式的各种分辨率8位影像数据。

其他介绍比较繁琐,这里就不一一赘述了,我们只需要知道它是通过SCCB总线控制的就行了。

带FIFO与不带FIFO

目前市场上购买的ov7670模块分为两种,一种是不带FIFO芯片、一种是带FIFO芯片的。

FIFO是什么:(First Input First Output)先入先出的意思,它的本质是一种数据缓存器,只能顺序进行数据读和写。

FIFO的作用:就是OV7670输出数据过快,你所用的单片机跟不上,就需要这么一个数据缓存,让ov7670先把数据传入到FIFO,再让单片机去读写即可。

(有点枯燥,举个例子就很好理解了,想必大家都参加过英语四级考试,四级听力都经受过,是不是有种听天书的感觉,叭叭的讲的什么东西,上一句还没听懂呢,下一句已经开始。那个播音机 可以理解为这里的 ov7670作为数据的输出,坐在下面听的“你” 理解为 接受数据的单片机 作为数据输入端,   由于语速太快听不懂怎么办呢,,给你一个录音器,让你先录下来再去听,是不是有种开外挂的感觉,,,而FIFI就相当于录音器的作用将数据缓冲)

不带FIFO:就是单片机直接去接受ov7670传来的数据,但是这种方式对单片机的接收数据速率有很大的要求,需要使用性能好的单片机,价格嘛   自然会高不少。

带FIFO:相当于“带一个物理外挂”,这样处理起来就比较简单,ov7670采集到的数据自动传到FIFO中,我们只需读取FIFO中存储的数据即可。(对小白很相友善)

因此呢  博主强烈建议小白入手带FIFO的ov7670

二、ov7670是如何工作的

开篇我们也说了,ov7670是基于SCCB总线控制,可能不少人对该总线比较陌生,但是大家肯定都很熟悉IIC总线,而SCCB总线的控制方法基本上和IIC总线差不多,所以控制起来也比较简单。

先来看一下时序图:

字节格式:在SIO_C高电平期间数据保持,在SIO_C低电平时数据更新。和IIC一样

写一个字节:在SIO_C低电平区间改变SIO_D数据,在SIO_C高电平期间SIO_D保持

static u8 Sccb_Write_Byte(u8 dat)//写入一个字节的数据到SCCB
{
	u8 i;
	for(i=0;i<8;i++)
	{
		GPIO_WriteBit(SCCB_SID_PORT, SCCB_SID_PIN, (((dat<<i)&0x80))>>7);   //数据线拉高或拉低
		delay(DELAYTIME);

		GPIO_WriteBit(SCCB_SIC_PORT, SCCB_SIC_PIN, Bit_SET);     //时钟线拉高
		delay(DELAYTIME);

		GPIO_WriteBit(SCCB_SIC_PORT, SCCB_SIC_PIN, Bit_RESET);	 //时钟线拉低
		delay(DELAYTIME);
	}
	GPIO_WriteBit(SCCB_SID_PORT, SCCB_SID_PIN, Bit_RESET);       //复位数据线
	return Get_Ack();
}

读一个字节:拉高SIO_C读SIO_D的状态。

static u8 Sccb_Read_Byte(void)//一个字节数据读取并且返回
{
	u8 i,rbyte=0;
	Sccb_Sid_Change_In();
	for(i=0;i<8;i++)
	{
		GPIO_WriteBit(SCCB_SIC_PORT, SCCB_SIC_PIN, Bit_SET);
		delay(DELAYTIME);

		if(GPIO_ReadInputDataBit(SCCB_SID_PORT, SCCB_SID_PIN)) rbyte|=(0x80>>i);
		delay(DELAYTIME);

		GPIO_WriteBit(SCCB_SIC_PORT, SCCB_SIC_PIN, Bit_RESET);	 
		delay(DELAYTIME);
	} 
	Sccb_Sid_Change_Out();//输出
	GPIO_WriteBit(SCCB_SID_PORT, SCCB_SID_PIN, Bit_RESET);
	return rbyte;
}

​

连续写:

static u8 Write_Sensor_Reg(u8 regID, u8 regDat)//写OV7670寄存器
{
		Start_Sccb(); //发送SCCB 总线开始传输命令
		if(0 == Sccb_Write_Byte(0x42))//写地址
		{	
			Stop_Sccb();//发送SCCB 总线停止传输命令
			return(0);//错误返回
		}
	
		if(0 == Sccb_Write_Byte(regID))/寄存器ID
		{
			Stop_Sccb();//发送SCCB 总线停止传输命令
			return(0); //错误返回
		}
		
		if(0 == Sccb_Write_Byte(regDat))//写数据到积存器
		{
			Stop_Sccb();//发送SCCB 总线停止传输命令
			return(0);//错误返回
		}
		Stop_Sccb();//发送SCCB 总线停止传输命令
		return(1);//成功返回
}

连续读:

static u8 Readd_Sensor_Reg(u8 regID, u8 *regDat)//读OV7670寄存器
{
		//通过写操作设置寄存器地址
		Start_Sccb();
		if(0 == Sccb_Write_Byte(0x42))//写地址
		{
			Stop_Sccb();//发送SCCB 总线停止传输命令
			return(0);//错误返回
		}
		if(0 == Sccb_Write_Byte(regID))//寄存器ID
		{
			Stop_Sccb();//发送SCCB 总线停止传输命令
			return(0);//错误返回
		}
		Stop_Sccb();//发送SCCB 总线停止传输命令

		//设置寄存器地址后,才是读
		Start_Sccb();
		if(0 == Sccb_Write_Byte(0x43))//读地址
		{
			Stop_Sccb();//发送SCCB 总线停止传输命令
			return(0);//错误返回
		}
		*regDat = Sccb_Read_Byte();//返回读到的值
		No_Ack();//发送NACK命令
		Stop_Sccb();//发送SCCB 总线停止传输命令
		return(1);//成功返回
}

摄像头初始化:

u8 Sensor_Init(void)//摄像头芯片初始化
{
		u8 temp;	
		u8 i = 0;
		temp = 0x80;
		if(0 == Write_Sensor_Reg(0x12,temp)) //Reset SCCB总线
		{
			return 0 ;//错误返回
		}							
		if(0 == Readd_Sensor_Reg(0x0b, &temp))//读ID
		{
			return 0 ;//错误返回
		}									
		//正常的话  temp==0x73
		if(temp == 0x73)//OV7670
		{																	
			for(i=0;i<OV7670_REG_NUM;i++)
			{
				if(0 == Write_Sensor_Reg(OV7670_reg[i][0],OV7670_reg[i][1]))
				{																															
					return 0;//错误返回
				}
			}
		}
		return 1; //ok
} 

到此前期初始化工作已做完。转战到下一篇文章,讲解如何用ov7670识别车牌。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐