IIC通信
• I2C(Inter IC Bus)是由Philips公司开发的一种通用数据总线
• 两根通信线:SCL(串行时钟线)、SDA(串行数据线)
• 同步,半双工
• 带数据应答
• 支持总线挂载多设备(一主多从、多主多从)
一主多从:单片机作为主机,主导I2C总线的运行,挂载在I2C总线的所有外部模块都是从机,从机只有被主机点名之后才能控制I2C总线,不能在未经允许的情况下去碰I2C总线,防止冲突。
多主多从:在总线上的任何一个模块都可以是主机。

硬件电路
• 所有I2C设备的SCL连在一起,SDA连在一起
• 设备的SCL和SDA均要配置成开漏输出模式
• SCL和SDA各添加一个上拉电阻,阻值一般为4.7KΩ左右

左边的CPU就是单片机,作为总线的主机。总线的权利很大,包括对SCL线的完全控制,任何时候,都是主机完全掌握SCL线。另外在空闲状态下,主机可以主动发起对SDA的控制,只有在从机发送数据和从机应答的时候,主机才会转交SDA的控制给从机。
从机:挂载在I2C总线上的从机,这些从机可以是姿态传感器、OLED\存储器、时钟模块等。从机的权利比较小,对于SCL时钟线,在任何时候都只能被动的读取,从机不允许控制SCL线。对于SDA数据线,从机不允许主动发起对SDA的控制,只有在主机发送读取从机的命令后,或者从机应答的时候,从机才能短暂地获取到SDA的控制权

所有I2C设备的SCL连在一起,SDA连在一起。


所有的设备,CPU和被控IC,他引脚的内部结构都是上图所示:
左边的一部分是SCL,右边是SDA的结构。

首先引脚的信号进来,都可以通过一个数据缓冲器或者施密特触发器,进行输入,因为输入对电路没有任何影响,所以任何设备在任何时候都是可以输入的,但是在输出的部分,采用的是开漏输出的配置。

推挽输出:上面一个开关管接到正极,下面一个开关管接到负极。



开漏输出:去掉强上拉的开关管,输出低电平时,下管导通,是强下拉;输出高电平时,下管断开,但是没有上管了,此时引脚处于浮空的状态,这就是开漏输出。

输出低电平,这个开关管导通,引脚直接接地,是强下拉;输出高电平,引脚管断开,处于浮空状态,这样所有的设备都只能输出低电平而不能输出高电平。但这样的话,为了避免高电平造成的引脚浮空,这时就需要在总线外面SCL和SDA各外置一个上拉电阻。
I2C时序基本单元
• 起始条件:SCL高电平期间,SDA从高电平切换到低电平
• 终止条件:SCL高电平期间,SDA从低电平切换到高电平

起始条件:
在I2C 总线处于空闲状态时:SCL和SDA都处于高电平状态,是由SCL和SDA由外挂的上拉电阻拉高至高电平,总线处于平静的高电平状态。
主机需要进行数据收发时,当从机捕获到SCL高电平,SDA下降沿信号时,就会进行自身的复位,等待主机的召唤。然后在SDA下降沿之后,主机要再把SCL拽下来,拽下SCL:1、占用总线,2、方便拼接。
终止条件
SCL先放手,回到高电平;SDA再放手,回到高电平,产生一个上升沿。上升沿触发终止条件,同时终止条件之后,SCL和SDA都是高电平。
发送一个字节
SCL低电平期间,主机将数据位依次放到SDA线上 (高位先行),然后释放SCL,从机将在SCL高电平期间读取数据位, 所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,
即可发送一个字节.。

主机如果想发送0,就拉底SDA到低电平;如果想发送1,就放手,SDA回到高电平,在SCL低电平期间,允许改变SDA的电平。当这一位的数据放好之后,主机就松手时钟线,SCL回到高电平。在高电平期间,是从机读取SDA的时候,所以高电平期间,SDA不允许变化。SCL处于高电平之后,从机需要尽快地读取SDA,一般在上升沿这个时刻,从机就已经读取完成了。主机在放手SCL一段时间后,就可以继续拉低SCL,然后传输下一位。
接收一个字节
SCL低电平期间,从机将数据位依次放到SDA线上 (高位先行),然后释放SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,
即可接收一个字节(主机在接收之前,需要释放SDA)

SCL低电平期间,从机将数据位依次到SDA线上(高位先行),然后释放SCL,主机在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化。
发送应答和接收应答
• 发送应答:主机在接收完一个字节之后,在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答
• 接收应答:主机在发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA)
I2C时序
• 指定地址写
• 对于指定设备(Slave Address),在指定地址(Reg Address)下,写入指定数据(Data)
空闲状态:SCL和SDA都是高电平。
主机给从机写入数据的时候:1、SCL高电平期间,拉低SDA,产生起始条件,紧跟着的时序,必须是发送一个字节的时序,字节的内容,必须是从机地址+读写位。从机地址是7位,读写位是1位。加起来刚好一个字节。发送从机地址,就是确定通信的对象。发送读写位,就是确认我接下来是要写入还是要读出。
I2C的硬件实现
I2C外设简介
• STM32 内部集成了硬件 I2C 收发电路,可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能,减轻 CPU 的负担
• 支持多主机模型
• 支持 7 位 /10 位地址模式
• 支持不同的通讯速度,标准速度 ( 高达 100 kHz) ,快速 ( 高达 400 kHz)
• 支持 DMA
• 兼容 SMBus 协议
• STM32F103C8T6 硬件 I2C 资源: I2C1 、 I2C2
(硬件I2C的资源是有限的,这也是硬件和软件的区别)
I2C框图
I2C基本结构
发送:移位寄存器中的数据由高位到低位先移出去,移8次就可以移一个字节,有高位到低位依次放到SDA线上。
接收:数据从GPIO口从右边依次移进来,最终移8次,一个字节就接收完成了。