赞
踩
目录
摄像头与 STM32 连接关系中主要分为 SCCB 控制、VGA 时序控制、FIFO 数据读取部分,介绍如下:
摄像头中的 SIO_C 和 SIO_D 引脚直接连接到 STM32 普通的 GPIO,它们不具有硬件I2C 的功能,所以在后面的代码中采用模拟 I2C 时序,实际上直接使用硬件 I2C 是完全可以实现 SCCB 协议的,本设计采用模拟 I2C 是芯片资源分配妥协的结果。
检测 VGA 时序的 HREF、VSYNC 引脚,它们与 STM32 连接的 GPIO 均设置为输入模式,其中 HREF 在本实验中并没有使用,它已经通过摄像头内部的与非门控制了 FIFO 的写使能;VSYNC 与 STM32 连接的 GPIO 引脚会在程序中配置成中断模式,STM32 利用该中断信号获知新的图像是否采集完成,从而控制 FIFO 是否写使能。
与 FIFO 控制相关的 RCLK、RRST、WRST、WEN 及 OE 与 STM32 连接的引脚均直接配置成推挽输出,STM32 根据图像的采集情况利用这些引脚控制 FIFO;读取 FIFO 数据内容使用的数据引脚 DO[0:7]均连接到 STM32 同一个 GPIO 端口连续的高 8 位引脚 PB[8:15],这些引脚使用时均配置成输入,程序设计中直接读取 GPIO 端口的高 8 位状态直接获取一个字节的 FIFO 内容,建议在连接这部分数据信号时,参考本设计采用同一个 GPIO 端口连续的 8 位(高 8 位或低 8 位均可),否则会导致读取数据的程序非常复杂。
本设计中 STM32 的摄像头接口还预留了 PA8 引脚用于与摄像头的 XCLK 连接,STM32的 PA8 可以对外输出时钟信号,所以在使用不带晶振的摄像头时,可以通过该引脚给摄像头提供时钟,秉火摄像头内部已自带晶振,在程序中没有使用 PA8 引脚。
- #ifndef __SCCB_H
- #define __SCCB_H
-
-
-
- #include "stm32f10x.h"
-
-
-
- /************************** OV7725 连接引脚定义********************************/
- #define OV7725_SIO_C_SCK_APBxClock_FUN RCC_APB2PeriphClockCmd
- #define OV7725_SIO_C_GPIO_CLK RCC_APB2Periph_GPIOC
- #define OV7725_SIO_C_GPIO_PORT GPIOC
- #define OV7725_SIO_C_GPIO_PIN GPIO_Pin_6
-
- #define OV7725_SIO_D_SCK_APBxClock_FUN RCC_APB2PeriphClockCmd
- #define OV7725_SIO_D_GPIO_CLK RCC_APB2Periph_GPIOC
- #define OV7725_SIO_D_GPIO_PORT GPIOC
- #define OV7725_SIO_D_GPIO_PIN GPIO_Pin_7
-
-
-
- #define SCL_H GPIO_SetBits(OV7725_SIO_C_GPIO_PORT , OV7725_SIO_C_GPIO_PIN)
- #define SCL_L GPIO_ResetBits(OV7725_SIO_C_GPIO_PORT , OV7725_SIO_C_GPIO_PIN)
-
- #define SDA_H GPIO_SetBits(OV7725_SIO_D_GPIO_PORT , OV7725_SIO_D_GPIO_PIN)
- #define SDA_L GPIO_ResetBits(OV7725_SIO_D_GPIO_PORT , OV7725_SIO_D_GPIO_PIN)
-
- #define SCL_read GPIO_ReadInputDataBit(OV7725_SIO_C_GPIO_PORT , OV7725_SIO_C_GPIO_PIN)
- #define SDA_read GPIO_ReadInputDataBit(OV7725_SIO_D_GPIO_PORT , OV7725_SIO_D_GPIO_PIN)
-
- #define ADDR_OV7725 0x42
-
-
-
- void SCCB_GPIO_Config(void);
- int SCCB_WriteByte( u16 WriteAddress , u8 SendByte);
- int SCCB_ReadByte(u8* pBuffer, u16 length, u8 ReadAddress);
-
-
-
- #endif
前面我们说过,SCCB的引脚配置类似于IIC的引脚配置,这里我们对SCCB的引脚进行初始化,使用的类似软件IIC的协议,可以不用规定必须是IIC的引脚,进行配置为开漏输出模式:
- void SCCB_GPIO_Config(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure;
-
- /* SCL(PC6)、SDA(PC7)管脚配置 */
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE );
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
- GPIO_Init(GPIOC, &GPIO_InitStructure);
-
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE );
- GPIO_InitStructure.GPIO_Pin =GPIO_Pin_7;
- GPIO_Init(GPIOC, &GPIO_InitStructure);
- }
按照宏定义进行转换:
- void SCCB_GPIO_Config(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure;
-
-
- /* SCL(PC6)、SDA(PC7)管脚配置 */
- OV7725_SIO_C_SCK_APBxClock_FUN ( OV7725_SIO_C_GPIO_CLK, ENABLE );
- GPIO_InitStructure.GPIO_Pin = OV7725_SIO_C_GPIO_PIN ;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
- GPIO_Init(OV7725_SIO_C_GPIO_PORT, &GPIO_InitStructure);
-
- OV7725_SIO_D_SCK_APBxClock_FUN ( OV7725_SIO_D_GPIO_CLK, ENABLE );
- GPIO_InitStructure.GPIO_Pin = OV7725_SIO_D_GPIO_PIN ;
- GPIO_Init(OV7725_SIO_D_GPIO_PORT, &GPIO_InitStructure);
-
- }
一个简单的循环等于0时跳出循环,起到延时的作用:
- static void SCCB_delay(void)
- {
- uint16_t i = 400;
- while(i)
- {
- i--;
- }
- }
类比IIC协议,起始条件下,SCL高电平期间,SDA从高电平切换到低电平。
- static int SCCB_Start(void)
- {
- GPIO_SetBits(GPIOC, GPIO_Pin_7);
- SCCB_delay();
- GPIO_SetBits(GPIOC, GPIO_Pin_6);
- SCCB_delay();
- GPIO_ResetBits(GPIOC, GPIO_Pin_7);
- SCCB_delay();
- GPIO_ResetBits(GPIOC, GPIO_Pin_6);
- SCCB_delay();
-
- }
为了提高程序的健壮性,可以加入:GPIO_ReadInputDataBit 函数,其作用是读取指定GPIO端口的指定引脚的输入状态,并返回该引脚的输入值(0 或 1) ,进行检测SDA线是否忙碌是否正常。
- static int SCCB_Start(void)
- {
- GPIO_SetBits(GPIOC, GPIO_Pin_7);
- GPIO_SetBits(GPIOC, GPIO_Pin_6);
- SCCB_delay();
- if(!GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_7))
- return DISABLE; /* SDA线为低电平则总线忙,退出 */
- GPIO_ResetBits(GPIOC, GPIO_Pin_7);
- SCCB_delay();
- if(GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_7))
- return DISABLE; /* SDA线为高电平则总线出错,退出 */
- GPIO_ResetBits(GPIOC, GPIO_Pin_7);
- SCCB_delay();
- return ENABLE;
- }
按照宏定义进行转换:
- static int SCCB_Start(void)
- {
- SDA_H;
- SCL_H;
- SCCB_delay();
- if(!SDA_read)
- return DISABLE; /* SDA线为低电平则总线忙,退出 */
- SDA_L;
- SCCB_delay();
- if(SDA_read)
- return DISABLE; /* SDA线为高电平则总线出错,退出 */
- SDA_L;
- SCCB_delay();
- return ENABLE;
- }
终止条件下,SCL高电平期间,SDA从低电平切换到高电平。
- static void SCCB_Stop(void)
- {
- GPIO_ResetBits(GPIOC, GPIO_Pin_6);
- SCCB_delay();
- GPIO_ResetBits(GPIOC, GPIO_Pin_7);
- SCCB_delay();
- GPIO_SetBits(GPIOC, GPIO_Pin_6);
- SCCB_delay();
- GPIO_SetBits(GPIOC, GPIO_Pin_7);
- SCCB_delay();
- }
按照宏定义进行转换:
- static void SCCB_Stop(void)
- {
- SCL_L;
- SCCB_delay();
- SDA_L;
- SCCB_delay();
- SCL_H;
- SCCB_delay();
- SDA_H;
- SCCB_delay();
- }
与 I2C 时序类似,在 SCCB 时序也使用自由位(Don’t care bit )和非应答(NA)信号来保证正常通讯。自由位和非应答信号位于 SCCB 每个传输阶段中的第九位。
在写数据的第一个传输阶段中,第 9 位为自由位,在一般的正常通讯中,第 9 位时,主机的 SDA 线输出高电平,而从机把 SDA 线拉低作为响应,只是传输的内容分别为目的寄存器地址和要写入的数据。
应答信号的发送是 SCCB 协议中的一个重要步骤,用于确认数据传输的成功。
- static void SCCB_Ack(void)
- {
- GPIO_ResetBits(GPIOC, GPIO_Pin_6);
- SCCB_delay();
- GPIO_ResetBits(GPIOC, GPIO_Pin_7);
- SCCB_delay();
- GPIO_SetBits(GPIOC, GPIO_Pin_6);
- SCCB_delay();
- GPIO_ResetBits(GPIOC, GPIO_Pin_6);
- SCCB_delay();
- }
按照宏定义进行转换:
- static void SCCB_Ack(void)
- {
- SCL_L;
- SCCB_delay();
- SDA_L;
- SCCB_delay();
- SCL_H;
- SCCB_delay();
- SCL_L;
- SCCB_delay();
- }
非应答信号的发送也是 SCCB 协议中的一部分,用于处理数据传输失败或其他错误情况。在某些情况下,如果从设备无法正确接收数据,主设备可能会发送非应答信号并采取相应的错误处理措施。
- static void SCCB_NoAck(void)
- {
- GPIO_ResetBits(GPIOC, GPIO_Pin_6);
- SCCB_delay();
- GPIO_SetBits(GPIOC, GPIO_Pin_7);
- SCCB_delay();
- GPIO_SetBits(GPIOC, GPIO_Pin_6);
- SCCB_delay();
- GPIO_ResetBits(GPIOC, GPIO_Pin_6);
- SCCB_delay();
- }
按照宏定义进行转换:
- static void SCCB_NoAck(void)
- {
- SCL_L;
- SCCB_delay();
- SDA_H;
- SCCB_delay();
- SCL_H;
- SCCB_delay();
- SCL_L;
- SCCB_delay();
- }
让主机把 SDA 线设为高电平,延时一段时间后再检测 SDA 线的电平,若为低则返回 ENABLE 表示接收到从机的应答,反之返回 DISABLE。(L代表低,H代表高,以下直接使用SCL和SDA的宏定义表示)
- static int SCCB_WaitAck(void)
- {
- SCL_L;
- SCCB_delay();
- SDA_H;
- SCCB_delay();
- SCL_H;
- SCCB_delay();
- if(SDA_read)
- {
- SCL_L;
- return DISABLE;
- }
- SCL_L;
- return ENABLE;
- }
首先,我们先创建一个用于接收数据的参数SendByt,它接受一个 8 位的字节数据作为输入参数,使用一个循环逐位发送这个字节的数据。循环从最高位开始,依次发送每一位,直到最低位。在每一次循环中,首先将 SCL(时钟线)拉低,然后通过 SCCB_delay 函数产生一定延迟,接着,根据 SendByte 的当前最高位,控制 SDA(数据线)为高电平或低电平,以此来发送数据的当前位,然后将 SendByte 左移一位,准备发送下一位数据。通过循环8次来完成一位数据的发送。
- static void SCCB_SendByte(uint8_t SendByte)
- {
- uint8_t i=8;
- while(i--)
- {
- SCL_L;
- SCCB_delay();
- if(SendByte&0x80)
- SDA_H;
- else
- SDA_L;
- SendByte<<=1;
- SCCB_delay();
- SCL_H;
- SCCB_delay();
- }
- SCL_L;
- }
首先声明了一个变量 i 并初始化为 8,以及再声明了一个变量 ReceiveByte 并初始化为 0,用于存储接收到的字节数据,然后,将 SDA(数据线)拉高,准备接收数据,通过一个循环逐位接收一个字节的数据。循环从最高位开始,依次接收每一位,直到最低位,读取 SDA 线上的数据,并根据其值决定是否将 ReceiveByte 的当前最低位设置为 1,将 ReceiveByte 左移一位,准备接收下一位数据。
- static int SCCB_ReceiveByte(void)
- {
- uint8_t i=8;
- uint8_t ReceiveByte=0;
-
- SDA_H;
- while(i--)
- {
- ReceiveByte<<=1;
- SCL_L;
- SCCB_delay();
- SCL_H;
- SCCB_delay();
- if(SDA_read)
- {
- ReceiveByte|=0x01;
- }
- }
- SCL_L;
- return ReceiveByte;
- }
首先,明确两个宏,其实OV7725的设备地址:
- #define ADDR_OV7725 0x42
- #define DEV_ADR ADDR_OV7725
-
为了确保SCCB总线通信正常启动,我们可以先进行判断SCCB是否发送起始信号,若是发送继续,若是失败则返回DISABLE。
- if(!SCCB_Start())
- {
- return DISABLE;
- }
然后,通过 SCCB_SendByte 函数发送设备地址 DEV_ADR,用于指定要通信的设备,此时指定要通信的设备会发送应答,通过判断本机时候接收到应答,来确定两个设备间是否能进行正常通信,若是不能则终止信号,并返回DISABLE。
- SCCB_SendByte( DEV_ADR );
- if( !SCCB_WaitAck() )
- {
- SCCB_Stop();
- return DISABLE;
- }
若是能则将要写入的寄存器地址的低八位(WriteAddress 的低八位)发送给设备,以确定写入数据的目标寄存器,再次等待指定要通信的设备的应答,然后发送要写入的数据 SendByte 给设备,再次等待设备的应答,,停止 SCCB 通信,并返回 ENABLE 表示写入操作成功。
- SCCB_SendByte((uint8_t)(WriteAddress & 0x00FF));
- SCCB_SendByte(SendByte);
- SCCB_WaitAck();
- SCCB_Stop();
- return ENABLE;
完整代码:
- #define ADDR_OV7725 0x42
- #define DEV_ADR ADDR_OV7725
-
- int SCCB_WriteByte( uint16_t WriteAddress , uint8_t SendByte )
- {
- if(!SCCB_Start())
- {
- return DISABLE;
- }
- SCCB_SendByte( DEV_ADR ); /* Æ÷¼þµØÖ· */
- if( !SCCB_WaitAck() )
- {
- SCCB_Stop();
- return DISABLE;
- }
- SCCB_SendByte((uint8_t)(WriteAddress & 0x00FF)); /* ÉèÖõÍÆðʼµØÖ· */
- SCCB_WaitAck();
- SCCB_SendByte(SendByte);
- SCCB_WaitAck();
- SCCB_Stop();
- return ENABLE;
- }
- int SCCB_ReadByte(uint8_t* pBuffer, uint16_t length, uint8_t ReadAddress)
- {
- if(!SCCB_Start())
- {
- return DISABLE;
- }
- SCCB_SendByte( DEV_ADR ); /* Æ÷¼þµØÖ· */
- if( !SCCB_WaitAck() )
- {
- SCCB_Stop();
- return DISABLE;
- }
- SCCB_SendByte( ReadAddress ); /* ÉèÖõÍÆðʼµØÖ· */
- SCCB_WaitAck();
- SCCB_Stop();
-
- if(!SCCB_Start())
- {
- return DISABLE;
- }
- SCCB_SendByte( DEV_ADR + 1 ); /* Æ÷¼þµØÖ· */
- if(!SCCB_WaitAck())
- {
- SCCB_Stop();
- return DISABLE;
- }
- while(length)
- {
- *pBuffer = SCCB_ReceiveByte();
- if(length == 1)
- {
- SCCB_NoAck();
- }
- else
- {
- SCCB_Ack();
- }
- pBuffer++;
- length--;
- }
- SCCB_Stop();
- return ENABLE;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。