s3c2440 提供了一个摄像接口,使开发人员很容易地实现摄像、照相等功能。摄像接口包括 8 位来自摄像头的输入数据信号,一个输出主时钟信号,三个来自摄像头的输入同步时钟信号和一个输出复位信号。摄像接口的主时钟信号由 USB PLL 产生,它的频率为 96MHz ,再经过分频处理后输出给摄像头,摄像头再根据该时钟信号产生三个同步时钟信号(像素时钟、帧同步时钟和行同步时钟),反过来再输入回 s3c2440 。s3c2440 仅仅提供了一个摄像接口,因此要实现其功能,还需要摄像头。在这里,我们使用 OV9650 。 OV9650 内部有大量的寄存器需要配置,这就需要另外的数据接口。 OV9650 的数据接口称为 SCCB (串行摄像控制总线),它由两条数据线组成:一个是用于传输时钟信号的 SIO_C ,另一个是用于传输数据信号的 SIO_D 。 SCCB 的传输协议与 IIC 的极其相似,只不过 IIC 在每传输完一个字节后,接收数据的一方要发送一位的确认数据,而 SCCB 一次要传输 9 位数据,前 8 位为有用数据,而第 9 位数据在写周期中是 Don’t-Care 位(即不必关心位),在读周期中是 NA 位。 SCCB 定义数据传输的基本单元为相( phase ),即一个相传输一个字节数据。 SCCB 只包括三种传输周期,即 3 相写传输周期(三个相依次为设备从地址,内存地址,所写数据), 2 相写传输周期(两个相依次为设备从地址,内存地址)和 2 相读传输周期(两个相依次为设备从地址,所读数据)。当需要写操作时,应用 3 相写传输周期,当需要读操作时,依次应用 2 相写传输周期和 2 相读传输周期。因此 SCCB 一次只能读或写一个字节。下面我们就用 s3c2440 的 IIC 总线接口分别与 OV9650 的 SIO_C 和 SIO_D 相连接来实现 SCCB 的功能。具体的读、写函数为:
本文引用地址:https://www.eepw.com.cn/article/201611/321674.htm
// 配置 IIC 接口
rGPEUP = 0xc000;// 上拉无效
rGPECON = 0xa0000000;//GPE15 : IICSDA , GPE14 : IICSCL
//IIC 中断
void __irq IicISR(void)
{
rSRCPND |= 0x1<<27;
rINTPND |= 0x1<<27;
flag = 0;
}
// 写操作
// 输入参数分别为要写入的内存地址和数据
void Wr_SCCB(unsigned char wordAddr, unsigned char data)
{
//3 相写传输周期
// 写 OV9650 设备从地址字节
flag =1;
rIICDS =0x60;//OV9650 设备从地址为 0x60
rIICSTAT = 0xf0;
rIICCON &= ~0x10;
while(flag == 1)
delay(100);
// 写 OV9650 内存地址字节
flag = 1;
rIICDS = wordAddr;
rIICCON &= ~0x10;
while(flag)
delay(100);
// 写具体的数据字节
flag = 1;
rIICDS = data;
rIICCON &= ~0x10;
while(flag)
delay(100);
rIICSTAT = 0xd0;// 停止位
rIICCON = 0xe3;// 为下一次数据传输做准备
delay(100);
}
// 读操作
// 参数分别为要读取的内存地址和数据
void Rd_SCCB (unsigned char wordAddr,unsigned char *data)
{
unsigned char temp;
//2 相写传输周期
// 写入 OV9650 设备从地址字节
flag =1;
rIICDS = 0x60;
rIICSTAT = 0xf0;
rIICCON &= ~0x10;
while(flag)
delay(100);
// 写入内存地址字节
flag = 1;
rIICDS = wordAddr;
rIICCON &= ~0x10;
while(flag)
delay(100);
rIICSTAT = 0xd0;// 停止位
rIICCON = 0xe3;// 为下一次数据传输做准备
delay(100);
//2 相读传输周期
// 写入 OV9650 设备从地址字节
flag = 1;
rIICDS = 0x60;
rIICSTAT = 0xb0;
rIICCON &= ~0x10;
while (flag)
delay(100);
// 读取一个无用字节
flag = 1;
temp = rIICDS;
rIICCON &= ~((1<<7)|(1<<4));
while(flag)
delay(100);
// 读取数据
flag = 1;
*data= rIICDS;
rIICCON &= ~((1<<7)|(1<<4));
while(flag)
delay(100);
rIICSTAT = 0x90;// 停止位
rIICCON = 0xe3;// 为下一次传输做准备
delay(100);
}
当然我们也可以用两个通用 IO 口来模拟 SCCB 总线,下面我们给出具体的程序,其中 GPE15 为 SIO_D , GPE14 为 SIO_C 。
#define CLOCK_LOW()(rGPEDAT&=(~(1<<14)))// 时钟信号低
#define CLOCK_HIGH()(rGPEDAT|=(1<<14))// 时钟信号高
#define DATA_LOW()(rGPEDAT&=(~(1<<15)))// 数据信号低
#define DATA_HIGH()(rGPEDAT|=(1<<15))// 数据信号高
// 配置 IO
rGPEUP = 0xc000;// 上拉无效
rGPECON = 5<<28;//GPE15 为 SIO_D , GPE14 为 SIO_C ,都为输出
void delay(int a)
{
int k;
for(k=0;k
;
}
// 启动 SCCB
void __inline SCCB_start(void)
{
CLOCK_HIGH();
DATA_HIGH();
delay(10);
DATA_LOW();
delay(10);
CLOCK_LOW();
delay(10);
}
// 结束 SCCB
void __inline SCCB_end(void)
{
DATA_LOW();
delay(10);
CLOCK_HIGH();
delay(10);
DATA_HIGH();
delay(10);
}
//SCCB 发送一个字节
void __inline SCCB_sendbyte(unsigned char data)
{
int i=0;
// 并行数据转串行输出,串行数据输出的顺序为先高位再低位
for(i=0;i<8;i++)
{
if(data & 0x80)
DATA_HIGH();
else
DATA_LOW();
delay(10);
CLOCK_HIGH();
delay(10);
CLOCK_LOW();
delay(10);
DATA_LOW();
delay(10);
data <<= 1;
}
// 第 9 位, Don’t Care
DATA_HIGH();
delay(10);
CLOCK_HIGH();
delay(10);
CLOCK_LOW();
delay(10);
}
评论