本文是我自己实际工作中,对I2C通信协议的学习,实现过程的一个总结。它记录了我从对I2C一无所知到最终能够熟练实现I2C协议的一个过程。希望能够帮到不了解I2C通信协议却正好要使用I2C的一些小伙伴们。叙述的方式还是一点一点来,尽量简单,用到哪儿再详细说哪儿。
一提到通信我们自然会想到要有两个设备,在它们之间相互传递数据的过程就叫通信。那么它们怎么传递数据呢?硬件上怎么连接?什么时候开始发送数据?什么时候结束发送?先发送高位还是低位?等等这一系列问题都要事先约定好,设备双方才能进行通信,那么这一系列事先约定好的通信规则就叫做通信协议,这里我们介绍的通信协议叫I2C通信协议。
我们要将协议中规定的这些规则告诉这两个设备,以便它们在互相通信时能按照这套规则进行运转,最终达到收发数据的目的。怎么告诉它们呢?设备听不懂中国话,英国话......,但是它们能听懂C语言(当然最终会由编译器转换为机器指令),我们使用C语言编写程序,程序的功能是让这两个设备按照I2C协议规定的规则进行收/发数据,那么这个过程就叫做在这两个设备上实现I2C通信协议。为了分辨两个设备,我们把一个叫做主机(Master),另一个叫做从机(Slaver),它们的行为不太一样,所以所需要的程序也不同,一个叫做主机端的I2C协议实现,另一个叫做从机端的I2C协议实现,也就是说我们要写两套程序。注意:在实际项目中一般我们只涉及到一端。
对于通信协议的实现,其实最终的表现形式就是提供几个用于通信的接口函数,以便主程序通过调用这些接口来实现在两个设备间通信的目的。在I2C中一般应提供如下最基本的通信接口函数:
void i2c_master_init(char addr); /*设置从机的地址,即,对方的地址*/
void i2c_master_read(char* buf, int len);
void i2c_master_write(char* buf, int len);
void i2c_slaver_init(char addr); /*初始化自己的地址*/
void i2c_slaver_read(char* buf, int len);
void i2c_slaver_write(char* buf, int len);
通过函数名称我们可以知道它们的作用。例如,如果Master想要向Slaver发送数据,就可以调用i2c_master_write(...)函数,如果想从Slaver接收数据,就调用i2c_master_read(...)。I2C的主机一般无需设置地址,随着对I2C的逐步理解,我们自然会明白为什么。
讲到这里,我们要开始学习一点协议了,因为我们要让设备按照协议中规定的规则运转,那么作为开发人员首先我们自己应该懂协议,才能使用C语言编写程序来控制设备。在I2C协议中,物理上在两个设备之间由两条线连接——数据线(SDA),时钟线(SCL)。GND和VCC那是硬件上的事,和协议无关,编程时当然也不涉及,所以我们不管。
那么这两条线有什么作用呢?SDA用来传递数据——高电平代表1,低电平代表0。SCL用来控制时序——按照一定频率荡起来。
我们再来聊一聊时钟线的作用,为什么在通信过程中时钟线一定要均匀稳定的荡起来才行?只用一根数据线行不行?如果你传输的是0101010...或101010...那还是可以的,但如果传输了一串0或一串1,那么接收方怎么知道你到底传输了多少位0或1呢。如果有了时钟就可以解决这个问题了,在一个时钟周期内采样得到的数据就是对方要传输的位数据,如果一共进行了4个周期的采样,那对方就是发送了4个bit,也就是说我们可以以时钟的脉冲为单位去对数据线采样,I2C协议规定在SCL为高电平时对SDA进行采样。在I2C协议中,主机可以控制(拉低或者释放)SDA和SCL这两条线,而从机只能控制SDA线。当主机发送数据时,从机会适时地将SDA拉低或释放(拉高)。主机向从机发送数据前要先发送一个起始信号,发送完数据后发送一个结束信号。(这一段描述的有错误,之所以要有时钟,简单的说是因为计算机的相关器件需要一个0到1的上升沿,这样各个器件才会集体工作一下。就像军训时教官喊1,我们摆左臂,教官喊2,我们摆右臂......<

最低0.47元/天 解锁文章
2337

被折叠的 条评论
为什么被折叠?



