基于STM32F103的二维码识别项目——OV7670篇

无线手持二维码识别项目是中山大学电子与信息工程学院(微电子学院)的工程应用训练课程的设计要求。项目是基于STM32F103开发的,开源代码:stm32f103zet6-qrcode-detect,bilibili:基于STM32F1的无线手持二维码识别项目。CSDN总目录:基于STM32F103的二维码识别项目。项目移植了Zbar库进行二维码识别,而非使用二维码识别模块。

此篇文章讲述OV7670FIFO模块相关的设计。

文章目录

一、 OV7670简介

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

OV7670 的特点有:
● 高灵敏度、低电压适合嵌入式应用
● 标准的SCCB接口,兼容IC 接口
● 支持 RawRGB、RGB(GBR4:2:2,RGB565/RGB555/RGB444),YUV(4:2:2)和 YCbCr(4:2:2)输出格式
●支持VGA、CIF,和从CIF到40*30的各种尺寸输出
● 支持自动曝光控制、自动增益控制、自动白平衡、自动消除灯光条纹、自动黑电平校准等自动控制功能。同时支持色饱和度、色相、伽马、锐度等设置。
● 支持闪光灯
● 支持图像缩放

OV7670的功能框图如下图所示:

在这里插入图片描述

二、 接口定义

下面先简单介绍一下各个接口的意思。

在这里插入图片描述

● VSYNC:每次在摄像头写出一帧图之前拉高,表示开始输出。

● RCK:每读取一个像素数据给一个上升沿。

● WR:拉高表示允许图像写入FIFO。

● OE:电源使能

● WRST:写复位,回到最开始的位置准备写入FIFO。

● RRST:读复位,从最开始的地方开始准备读取FIFO。

● SIOC与SIOD:I2C通信。

二、 寄存器的读写

对于OV7670的寄存器的读写极其重要,摄像头必须初始化寄存器才能正常使用。

//初始化寄存器序列及其对应的值
const u8 ov7670_init_reg_tbl[][2]= 
{   
	/*以下为OV7670 QVGA RGB565参数  */
	{0x3a, 0x04},//dummy
	{0x40, 0xd0},//565   
	{0x12, 0x14},//QVGA,RGB输出

	//输出窗口设置
	{0x32, 0x80},//HREF control	bit[2:0] HREF start 3 LSB	 bit[5:3] HSTOP HREF end 3LSB
	{0x17, 0x16},//HSTART start high 8-bit MSB         
	{0x18, 0x04},//5 HSTOP end high 8-bit
	{0x19, 0x02},
	{0x1a, 0x7b},//0x7a,
	{0x03, 0x06},//0x0a,帧竖直方向控制

	{0x0c, 0x00},
	{0x15, 0x00},//0x00
	{0x3e, 0x00},//10
	{0x70, 0x3a},
	{0x71, 0x35},
	{0x72, 0x11},
	{0x73, 0x00},//

	{0xa2, 0x02},//15
	{0x11, 0x81},//时钟分频设置,0,不分频.
	{0x7a, 0x20},
	{0x7b, 0x1c},
	{0x7c, 0x28},

	{0x7d, 0x3c},//20
	{0x7e, 0x55},
	{0x7f, 0x68},
	{0x80, 0x76},
	{0x81, 0x80},

	{0x82, 0x88},
	{0x83, 0x8f},
	{0x84, 0x96},
	{0x85, 0xa3},
	{0x86, 0xaf},

	{0x87, 0xc4},//30
	{0x88, 0xd7},
	{0x89, 0xe8},
	{0x13, 0xe0},
	{0x00, 0x00},//AGC

	{0x10, 0x00},
	{0x0d, 0x00},//全窗口, 位[5:4]: 01 半窗口,10 1/4窗口,11 1/4窗口 
	{0x14, 0x28},//0x38, limit the max gain
	{0xa5, 0x05},
	{0xab, 0x07},

	{0x24, 0x75},//40
	{0x25, 0x63},
	{0x26, 0xA5},
	{0x9f, 0x78},
	{0xa0, 0x68},

	{0xa1, 0x03},//0x0b,
	{0xa6, 0xdf},//0xd8,
	{0xa7, 0xdf},//0xd8,
	{0xa8, 0xf0},
	{0xa9, 0x90},

	{0xaa, 0x94},//50
	{0x13, 0xe5},
	{0x0e, 0x61},
	{0x0f, 0x4b},
	{0x16, 0x02},

	{0x1e, 0x27},//图像输出镜像控制.0x07
	{0x21, 0x02},
	{0x22, 0x91},
	{0x29, 0x07},
	{0x33, 0x0b},

	{0x35, 0x0b},//60
	{0x37, 0x1d},
	{0x38, 0x71},
	{0x39, 0x2a},
	{0x3c, 0x78},

	{0x4d, 0x40},
	{0x4e, 0x20},
	{0x69, 0x00},
	{0x6b, 0x40},//PLL*4=48Mhz
	{0x74, 0x19},
	{0x8d, 0x4f},

	{0x8e, 0x00},//70
	{0x8f, 0x00},
	{0x90, 0x00},
	{0x91, 0x00},
	{0x92, 0x00},//0x19,//0x66

	{0x96, 0x00},
	{0x9a, 0x80},
	{0xb0, 0x84},
	{0xb1, 0x0c},
	{0xb2, 0x0e},

	{0xb3, 0x82},//80
	{0xb8, 0x0a},
	{0x43, 0x14},
	{0x44, 0xf0},
	{0x45, 0x34},

	{0x46, 0x58},
	{0x47, 0x28},
	{0x48, 0x3a},
	{0x59, 0x88},
	{0x5a, 0x88},

	{0x5b, 0x44},//90
	{0x5c, 0x67},
	{0x5d, 0x49},
	{0x5e, 0x0e},
	{0x64, 0x04},
	{0x65, 0x20},

	{0x66, 0x05},
	{0x94, 0x04},
	{0x95, 0x08},
	{0x6c, 0x0a},
	{0x6d, 0x55},


	{0x4f, 0x80},
	{0x50, 0x80},
	{0x51, 0x00},
	{0x52, 0x22},
	{0x53, 0x5e},
	{0x54, 0x80},

	//{0x54, 0x40},//110


	{0x09, 0x03},//驱动能力最大

	{0x6e, 0x11},//100
	{0x6f, 0x9f},//0x9e for advance AWB
	{0x55, 0x00},//亮度
	{0x56, 0x40},//对比度 0x40
	{0x57, 0x40},//0x40,  change according to Jim's request
//写寄存器
//返回值:0,成功;1,失败.
u8 SCCB_WR_Reg(u8 reg,u8 data)
{
	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;
}

三、 SCCB时序

寄存器的读写以及对像素数据的读取都是通过SCCB实现的。SCCB(Serial Camera Control Bus)是OmniVision公司公布的串行相机总线协议。OV开头的相机模块例如OV7670都使用SCCB协议。而事实上,SCCB就是改编版的IIC,完全可以按照IIC来理解。

在这里插入图片描述

总线空闲状态:SIO_D为高电平,SIO_C为高电平;
起始位:当SIO_C为高电平时,SIO_D出现下降沿,产生一个起始位;
结束位:当SIO_C为高电平时,SIO_D出现上升沿,产生一个结束位;
数据有效性:SIO_D线上的数据必须在时钟的高电平时期保持稳定,这时候数据接收方才可以接收数据;数据线的高或低电平状态只有在SIO_C线的时钟信号是低电平时才能改变,这个时候数据发送方才向SIO_D线上发送数据。
总结:总线空闲状态、起始位、结束位、数据有效性 与I2C一样。


写时序
SCCB的写时序和I2C的写时序一样,只不过响应位ACK变成了不关心位X:

ACK = 0
NO ACK = 1
X:Don’t care

I2C:Start + 器件地址(ACK) + 寄存器地址(ACK) + 写数据(ACK)+ 停止位
SCCB:Start + 器件地址(X) + 寄存器地址(X) + 写数据(X)+ 停止位

在这里插入图片描述

//SCCB,写入一个字节
//返回值:0,成功;1,失败. 
u8 SCCB_WR_Byte(u8 dat)
{
	u8 j,res;	 
	for(j=0;j<8;j++) //循环8次发送数据
	{
		if(dat&0x80)SCCB_SDA=1;	
		else SCCB_SDA=0;
		dat<<=1;
		delay_us(50);
		SCCB_SCL=1;	
		delay_us(50);
		SCCB_SCL=0;		   
	}			 
	SCCB_SDA_IN();		//设置SDA为输入 
	delay_us(50);
	SCCB_SCL=1;			//接收第九位,以判断是否发送成功
	delay_us(50);
	if(SCCB_READ_SDA)res=1;  //SDA=1发送失败,返回1
	else res=0;         //SDA=0发送成功,返回0
	SCCB_SCL=0;		 
	SCCB_SDA_OUT();		//设置SDA为输出    
	return res;  
}	 

读时序
SCCB的读时序相当于在I2C读时序的寄存器地址和第二个器件地址中加了一个停止位和起始位。

I2C:Start + 器件地址(ACK) + 寄存器地址(ACK) + 器件地址(ACK) + 读数据(NO ACK)+ Stop
SCCB:Start2 + 器件地址(X) + 寄存器地址(X) + Stop1 + Start1 + 器件地址(X) + 读数据(NO ACK)+ Stop2
在这里插入图片描述

//SCCB 读取一个字节
//在SCL的上升沿,数据锁存
//返回值:读到的数据
u8 SCCB_RD_Byte(void)
{
	u8 temp=0,j;    
	SCCB_SDA_IN();		//设置SDA为输入  
	for(j=8;j>0;j--) 	//循环8次接收数据
	{		     	  
		delay_us(50);
		SCCB_SCL=1;
		temp=temp<<1;
		if(SCCB_READ_SDA)temp++;   
		delay_us(50);
		SCCB_SCL=0;
	}	
	SCCB_SDA_OUT();		//设置SDA为输出    
	return temp;
} 		

四、 OV7670读写原理

OV7670的图像数据输出(通过D[7:0])就是在PCLK,VSYNC和HREF/HSYNC的控制下进行的。我们看帧输出时序,如下图所示:

在这里插入图片描述

每一帧数据前会有一个VSYNC的脉冲信号,代表这帧数据开始传输,每一行数据都会有一HREF脉冲,不过我们不需要这个。

下面简单概括 (用人话说) 一下图像输入和输出的过程:

图像写入过程

  1. 等待帧同步VSYNC信号拉高:两个帧同步信号产生(拉高)说明一帧图片输出完成,中间拉低的时候在传数据。

  2. FIFO复位写指针:WRST先置0再置1。

  3. FIFO写允许:WR置1。

  4. 等待帧同步VSYNC信号再拉高。

  5. FIFO写禁止:WR置0。

图像读出过程

  1. FIFO读指针复位:RRST先置0再置1。

  2. 输出使能:OE置0。

  3. RCK。

  4. 读数据(RGB565有16位,一次只能读8位,先读高字节再读低字节)。

  5. RCK。

  6. 读数据(两次读完才表示一个像素)。

  7. 重复RCK和读数据的过程直到全部读完。

  8. 读指针复位,输出失能(OE置1)。

	//核心代码
	if(ov_sta)//有帧中断更新?
	{
		OV7670_RRST=0;				//开始复位读指针 
		OV7670_RCK_L;
		OV7670_RCK_H;
		OV7670_RCK_L;
		OV7670_RRST=1;				//复位读指针结束 
		OV7670_RCK_H;
		for(i=0;i<240;i++)   //此种方式可以兼容任何彩屏,但是速度很慢
		{
			for(j=0;j<320;j++)
			{
				OV7670_RCK_L;
				color=GPIOF->IDR&0XFF;	//读数据
				OV7670_RCK_H; 
				color<<=8;  
				OV7670_RCK_L;
				color|=GPIOF->IDR&0XFF;	//读数据
				OV7670_RCK_H; 
				LCD_DrawFRONT_COLOR(i,j,color);
			}
		}
	}
  • 15
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值