一、概述
SCCB(串行摄像头控制总线)是由欧姆尼图像技术公司(OmniVision)开发的一种类IIC的总线,主要用于其OV系列的图像传感器上(但目前有很多家的图像传感器都有采用该控制总线)。相对于IIC总线来说SCCB与之最主要的差异在于连续读写模式;SCCB不支持该模式,即每次读写完一个字节,主机必须发送一个NA信号。
采用了SCCB总线的图像传感器都工作在Slave模式,对应的主控端为Master模式,也就是说和IIC一样的为主从模式的总线,同样的支持一主多从和单主单从(通过SCCB_E控制从机使能,低电平使能)。
二、信号线定义
完整的SCCB总线包含:SCCB_E、SIO_C、SIO_D、PWDN四根信号线,其具体的作用分别为:
SCCB_E:传输使能,主端输出,从端输入,默认空闲状态为高电平。低电平时传输有效,电平高到低表示 总线通信开始,电平低到高表示 总线通信结束。
SIO_C:数据传输时钟,主端输出,从端输入,默认空闲状态为高电平。在SCCB_E使能(拉低)传输开始后电平由高到低表示数据传输开始,数据传输过程中高电平期间SIO_D数据采样有效,低电平期间SIO_D状态切换。
SIO_D:数据传输信号,双向输入、输出,默认总线空闲为浮空电平(通常主端在空闲状态会选择将其拉高),高电平表示逻辑1(bit 1),低电平表示逻辑0(bit 0)。在总线通信开始,SCCB_E产生下降沿前,主机需要将SIO_D拉高,可以有效的避免总线出现未知错误。
PWDN:输出、输入关闭。
三、通信过程
SCCB的数据传输发生在通信开始信号(起始信号)和通信结束信号(结束信号)之间,由称之为相(phase)的基础传输单元组成。
通信开始(起始信号):通信开始时序由SCCB_E下降沿前后的各信号线的序列时序状态组成;在SCCB_E下降沿前主端将SIO_D置1,并且SIO_D必须保持间隔一个不低于15ns的tPRC的高电平时间;在SCCB_E下降沿后SIO_D必须要保持间隔一个不低于1.25us的tPRA的高电平时间;在此期间,SIO_C必须始终保持在高电平状态。
通信结束(结束信号):通信开始时序由SCCB_E上升沿前后的各信号线的序列时序状态组成;在SCCB_E上升沿前SIO_C拉高,并且要保持间隔一个不低于0ns的tPSA时间;在SCCB_E上升沿后SIO_D拉低,并且期间要保持间隔一个不低于15ns的tPSC时间。
SCCB的数据传输主要分为:3相写、2相写、2相读,三种传输时序类型。这里的3相、2相的相指的是基础传输单元。
每一个相元(phase)由8位数据位 + 1位 don’t care/NA位组成。如果是主端发数据(写操作),第9位就是don’t care(不关心)位;如果是从端发数据(读操作),第9位就是NA位。数据比特流都是MSB高比特在前的方式传输的。
相元1传输的主要是从机的ID信息,SCCB支持单主多从,所以主机需要在相元1阶段发送从机ID信息,以便总线上的从机识别当前主机要与谁通信(单主单从时也不可省略)。从机ID为7bit,表示范围为0~127,bit 0 用于表示读/写操作,0为读取数据,1为写入。在相元1的8位数据之后的x位为don’t care位(但有的sensor会在该位通过SIO_D向主端发送一个逻辑0的NA数据,主机端可以通过该位判断对应ID的从机是否在线)。
相元2传输的主要是从机寄存地址信息或者读取的数据信息;8bit后的1bit位也同样的为don’t care/NA位。
相元3传输的主要是主机向从机寄存要写入的数据信息;8bit后的1bit位也同样的为don’t care/NA位。
3相写时序:3相写时序是一个完整的主机向指定从机的指定寄存地址写入指定8bit数据的一个完整数据传输周期(相元1的bit0为1),每一个相元的第9bit都是don’t care位。
2相写时序:2相写时序实际上是前半个主机从指定从机的指定寄存地址读取8bit数据的完整数据传输周期(和2相读时序共同组成一个完整的读时序),每一个相元的第9bit都是don’t care位。先向目标从机传输读标志(相元1的bit0和相元2中的8bit寄存地址)。
2相读时序:2相读序实际上是后半个主机从指定从机的指定寄存地址读取8bit数据的完整数据传输周期(和2相写时序共同组成一个完整的读时序))。第一个相元的第9bit都是don’t care位,第二个相元的第9bit为NA位(主端向从端发送的确认信号)。
四、伪代码实现
//通信开始
void sccb_start(void)
{
sccb_sda_out();
sccb_sda_set(1);
sccb_scl_set(1);
delay_us(25);
sccb_sda_set(0);
delay_us(25);
sccb_scl_set(0);
delay_us(25);
return;
}
//通信结束
void sccb_stop(void)
{
sccb_sda_out();
sccb_sda_set(0);
delay_us(25);
sccb_scl_set(1);
delay_us(25);
sccb_sda_set(1);
delay_us(25);
return;
}
//NA信号
void sccb_na(void)
{
sccb_sda_out();
delay_us(25);
sccb_sda_set(1);
sccb_scl_set(1);
delay_us(25);
sccb_scl_set(0);
delay_us(25);
sccb_sda_set(0);
delay_us(25);
return;
}
//读取1字节,返回读取的数据
unsigned char sccb_read_byte(void)
{
unsigned char byte = 0, index = 0;
sccb_sda_in();
for(index = 0; index < 8; index++) {
delay_us(25);
sccb_scl_set(1);
byte = byte << 1;
if(1 == sccb_read_sda()) {
byte++;
}
delay_us(25);
sccb_scl_set(0);
}
sccb_sda_out();
return byte;
}
//写入1字节,写入成功返回1,失败返回0
unsigned char sccb_write_byte(unsigned char data)
{
unsigned char res = 0, index = 0;
for(index = 0; index < 8; inde++) {
if(1 == (data & 0x80)) {
sccb_sda_set(1);
} else {
sccb_sda_set(0);
}
data <<= 1;
delay_us(25);
sccb_scl_set(1);
delay_us(25);
sccb_scl_set(0);
}
sccb_sda_in();
delay_us(25);
sccb_scl_set(1);
delay_us(25);
if(1 == sccb_read_sda()) {
res = 1;
} else {
res = 0;
}
sccb_scl_set(0);
sccb_sda_out();
return res;
}
//向寄存器写入1字节,写入成功返回1,失败返回0
unsigned char sccb_write_reg(unsigned char reg, unsigned char data)
{
unsigned char res = 0;
sccb_start();
if(1 == sccb_write_byte(sccb_dev_id)) {
res = 1;
}
delay_us(50);
if(1 == sccb_write_byte(reg)) {
res = 1;
}
delay_us(50);
if(1 == sccb_write_byte(data)) {
res = 1;
}
delay_us(50);
sccb_stop();
return res;
}
//从寄存器读取1字节
unsigned char sccb_read_reg(unsigned char reg)
{
unsigned char res = 0;
sccb_start();
sccb_write_byte(sccb_dev_id);
delay_us(50);
sccb_write_byte(reg);
delay_us(50);
sccb_stop();
sccb_start();
sccb_write_byte(sccb_dev_id | 0x01);
delay_us(50);
res = sccb_read_byte();
sccb_na();
sccb_stop();
return res;
}