最近做项目需要用到OV7725,于是花了些时间研究。由于OV7725对于工作频率的要求较高,因此使用带FIFO的摄像头模块。代码参考自正点原子官方。
OV7725资源
引脚说明
以下时关于十八个引脚的说明(来自正点原子的用户手册)
其中,
OV_SCL和OV_SDA
组成SCCB,即串行摄像头控制总线,它的通信机制类似于IIC,有使用过IIC总线的较容易理解。OV_VSYNC为帧同步信号,该引脚产生信号时,意味着一帧数据传输完成后,下一帧数据传输即将开始(这里,从OV7725传输至FIFO)。
FIFO_D[7:0]
是FIFO的数据引脚,用于数据传输。当发生帧同步信号时,复位
FIFO_WRST
写指针信号,使能
FIFO_WEN
写使能信号,则OV7725开始往FIFO写数据,并置位帧中断标志位(该标志位在读数据完成后被清零),保证FIFO的数据被读取完成之前不会被覆盖,具体见下面的中断服务程序。帧中断标志位有效时,就复位
RIFO_RRST
读指针信号,然后在每个
FIFO_RCLK
读时钟信号,使用STM32去读取
FIFO_D[7:0]
的数据。FIFO_OE片选信号在初始化之后就拉低,使之有效。
寄存器
这是来自datasheet的寄存器描述,看手册,英文很重要:
这些寄存器在代码里表现为一堆宏定义
/*OV7725寄存器宏定义*/
#define GAIN 0x00
#define BLUE 0x01
#define RED 0x02
#define GREEN 0x03
#define BAVG 0x05
#define GAVG 0x06
…………
模块初始化
引脚定义
//D0~D7:PA0~PA7 VSYNC:PA8
//WRST:PB13 RRST:PB14 OE:PB5 RCLK:PB6 WEN:PB7 SDA:PB8 SCL:PB9
#define OV7725_VSYNC PAin(8)
#define OV7725_WRST PBout(13)
#define OV7725_WREN PBout(7)
#define OV7725_RCK_H GPIOB->BSRR=1<<6
#define OV7725_RCK_L GPIOB->BRR=1<<6
#define OV7725_RRST PBout(14)
#define OV7725_CS PBout(5)
#define SCCB_SCL PBout(9) //SCL
#define SCCB_SDA PBout(8) //SDA
#define SCCB_READ_SDA PBin(8) //ÊäÈëSDA
我使用的是杜邦线连接如,IO口是自己重新定义的(全使用普通IO即可),STM32F1系列的PB3,PB4,PA13,PA14,PA15用于JTAG,如果有使用到其中的引脚,需要进行特殊配置,具体可参考网上更多教程。当时使用PB3,PB4作为SCCB的引脚,出现问题才发现这个原因。
初始化
写寄存器的函数,寄存器的配置都是通过这个函数
u8 SCCB_WR_Reg(u8 reg,u8 data)
{
u8 res=0;
SCCB_Start(); //启动SCCB传输
if(SCCB_WR_Byte(SCCB_ID))res=1; //写器件ID,为0x42
delay_us(100);
if(SCCB_WR_Byte(reg))res=1; //写寄存器地址,直接写对应的寄存器地址
delay_us(100);
if(SCCB_WR_Byte(data))res=1; //写数据,根据寄存器功能写对应数据
SCCB_Stop();
return res;
}
将要配置的寄存器地址和对应的值,存在一个二维数组里边
//初始化寄存器系列及其对应的值
const u8 ov7725_init_reg_tb1[][2]=
{
/*输出窗口设置*/
{CLKRC, 0x00}, //clock config
{COM7, 0x06}, //VGA RGB565
{HSTART, 0x3f}, //水平起始位置
{HSIZE, 0x50}, //水平尺寸
{VSTRT, 0x03}, //垂直其实位置
......
初始化时,在循环语句调用写寄存器的函数,依次写入寄存器的地址和对应的值,即可完成寄存器的配置
//初始化OV7725,采用QVGA分辨率(320*240)
for(i=0;i<sizeof(ov7725_init_reg_tb1)/sizeof(ov7725_init_reg_tb1[0]);i++)
{
SCCB_WR_Reg(ov7725_init_reg_tb1[i][0],ov7725_init_reg_tb1[i][1]);
}
帧中断和数据处理
发生帧中断时
u8 ov_sta; //帧中断标记
//中断处理函数
void EXTI9_5_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line8)==SET)//是8线的中断
{
if(ov_sta==0)
{
OV7725_WRST=0; //复位写指针
OV7725_WRST=1;
OV7725_WREN=1; //允许写入FIFO
ov_sta++; //帧中断加1
}else OV7725_WREN=0;//禁止写入FIFO
}
EXTI_ClearITPendingBit(EXTI_Line8); //清除中断标志位
}
数据处理
if(ov_sta)//判断是否有帧中断发生
{
OV7725_RRST=0; //复
OV7725_RCK_L; //位
OV7725_RCK_H; //读
OV7725_RCK_L; //指
OV7725_RRST=1; //针
OV7725_RCK_H;
for(i=0;i<OV7725_WINDOW_HEIGHT;i++)//循环语句读取FIFO的数据
{
for(j=0;j<OV7725_WINDOW_WIDTH;j++)
{
//将数据填入相应缓冲区
......
}
}
ov_sta=0; //清零帧中断标志
}
以上是对于OV7725的初步理解,还有很多不懂的地方,后续有进一步研究再补充