Zephyr驱动SCCB设备

Creative Commons
本作品采用知识共享署名

本文简要介绍I2C和SCCB的关系,并说明如何使用Zephyr的i2c驱动SCCB设备。

本文通过分析I2C和SCCB协议内容,说明为何I2C可以用于驱动SCCB设备,并给出了Zephyr下的实现代码。
本文所介绍的I2C和SCCB协议内容只是为了说明为何I2C可以驱动SCCB设备,这些内容只是其原本协议的一小部分,更多详细的内容请参考文后链接。

I2C和SCCB

由于SCCB在数据传输上几乎和I2C一致,大多数情况下大家都将SCCB当作I2C来使用,其实质上二者还是有一定区别的。

I2C

I2C是飞利浦推出的一种总线协议,最早是在使用在电视上的设备,后面被推广到其它设备。I2C总线由SDA和SCL两条线组成,SDA传输数据信号,SCL传输时钟信号,数据在Master和Salve进行双向同步传输。

I2C的基本信号

  • 起始位:Master发送,开始一次传输,SCL高,SDA从高到底
  • 结束位:Master发送,结束一次传输,SCL高,SDA从低到高
  • 数据位:Master/Salve发送,有效数据bit,SCL高时,SDA的数据有效
  • ACK:Salve每接收到一个字节发送一个ACK,SCL高,SDA低
  • NACK:Master/Salve不再接收下一个字节,SCL高,SDA高

I2C的传输

I2C最基本的两种传输结构如下
写数据

起始位+7bit从设备地址+1bit 0 + 1bit ACK + N*(8bit数据 + 1bit ACK) + 8bit数据 + 1bit ACK/NACK + 停止位

读数据

起始位+7bit从设备地址+1bit 1 + 1bit ACK + N*(8bit数据 + 1bit ACK) + 8bit数据 + 1bit NACK + 停止位

可见在传输过程中:第一个字节的高7bit指定Salve地址,最低的1bit指定读写方向,0表示写,1表示读。每发送/接收一个字节,就需要有一个ACK。最后一个字节传输完后有一个ACK/NACK,如果是Master写ACK和NACK可选,如果是Master读必须是NACK。

SCCB

SCCB全称叫Serial Camera Control Bus,是OmniVision推出专门用于摄像头控制的总线协议,在数据通信格式上和I2C非常类似,但SCCB和I2C是两个不一样的协议。

硬件

SCCB总线在硬件上是三线,除了数据和时钟外还有一条片选信号

SCCB在简化情况下,可以只使用时钟和数据两条信号线,这也是可以将SCCB当作I2C使用的原因之一。

SCCB的基本信号

SCCB和I2C一样有起始位,结束位,数据位。
SCCB没有定义ACK和NACK。在SCCB Master写完8bit数据后, 第9bit时Salve会可以选择将数据线拉低,也可以选择让数据线处于浮动状态,数据线处于浮动状态的情况下Master并不会取读(关心)第9bit,因此叫Don’t Care bit。在SCCB Master在读完8bit数据后,第9bit把数据线拉高,叫做NA。
OV的摄像头支持Don’t care bit拉低,在这种情况下Don’t care与I2C的ACK的定义一致。而NA与I2C的NACK定义一致。这也就是为什么可以用I2C来驱动SCCB设备的原因。
但请注意,Don’t care bit有第二种用法,Salve让Don’t care处于浮动时,Salve接收发生错误时会在内部寄存器记录该错误状态,SCCB Master可以通过主动读该寄存器了解传输是否成功。该部分内容可以阅读SCCB spec进行详细了解.

SCCB的传输

SCCB将传输定义为3种: Phase 3, Phase 2 write, Phase 2 read,
Phase 3传输3个字节,用于写数据:

Phase 2 write传输2个字节,用于写访问的寄存器地址

Phase 2 read传输2个字节,用于读访问的寄存器地址

通常情况下Phase2 Write后跟随Phase2 Read来完成一次SCCB完整的读。

I2C驱动SCCB

通过上面的分析,可以得到以下结论:SCCB简化为2线通信后,当Salve支持Don’t care bit 数据信号拉低,SCCB的基本信号和I2C完全一致,而SCCB的传输可以视作I2C传输的组合。那么在Zephyr下我们就可以使用I2C来驱动这种SCCB设备了。

有了前面的分析原理,代码实现很简单,见下面注释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
int sccb_write(struct device *i2c, unsigned char addr,
unsigned int reg_addr,
unsigned char reg_addr_size,
unsigned char *buf,
unsigned char size)
{
//直接实现phase 3

struct i2c_msg msgs[2];
msgs[0].buf = (uint8_t *)&reg_address;
msgs[0].len = reg_addr_size;
msgs[0].flags = I2C_MSG_WRITE;

msgs[1].buf = (uint8_t *)buf;
msgs[1].len = size;
msgs[1].flags = I2C_MSG_WRITE | I2C_MSG_STOP;

return i2c_transfer(i2c, msgs, 2, addr);
}

int sccb_read(struct device *i2c, unsigned char addr,
unsigned int reg_addr,
unsigned char reg_addr_size,
unsigned char *buf,
unsigned char size)
{
struct i2c_msg msgs[2];

//Phase 2写
msgs[0].buf = (uint8_t *)&reg_address;
msgs[0].len = reg_addr_size;
msgs[0].flags = I2C_MSG_WRITE | I2C_MSG_STOP;

//Phase 2读
msgs[1].buf = (uint8_t *)buf;
msgs[1].len = size;
msgs[1].flags = I2C_MSG_READ | I2C_MSG_STOP | I2C_MSG_RESTART;

return i2c_transfer(i2c, msgs, 2, addr);
}

实际运行在OV7725上面的时序图:
写寄存器,典型的Phase 3

读寄存器,Phase 2 Write + Phase 2 Read

参考

https://www.nxp.com/docs/en/user-guide/UM10204.pdf
http://www4.cs.umanitoba.ca/~jacky/Teaching/Courses/74.795-LocalVision/ReadingList/ov-sccb.pdf