14 玩转STM32之IIC通信详解(软件模拟篇)看这篇就够了

14.1 IIC协议详细说明

IIC有模拟和芯片自带的两种方式。软件模拟方式具有引脚可以任意分配,软件修改灵活对PCB设计也有一定的优点,但是缺点也是非常明显的,因为采用的是软件进行会产生较长时间的延时,对于一些要求较高的场合并不适用,比如汽车电子方向、充电桩等这些就不适合用模拟电子还有就是一些医疗设备。因此建议在设计时优先芯片之中提供的IIC
这章我们先来说说软件模拟这部分,硬件IIC下一章节会讲。
IIC(Inter-Integrated Circuit BUS) 集成电路总线,该总线NXP(原PHILIPS)公司设计,多用于主控制器和从器件间的一种主从数据交互通信,在小数据量场合使用,传输距离短。
I2C可以支持0kHz~5MHz设备的多种模式:普通模式(100kHz)、快速模式(400kHz)、快速模式(1MHz)、高速模式(3.4MHz)和超高速模式(5MHz)。

14.1.1 I2C物理层

在I2C通信总线上,可连接多个I2C通信设备,支持多个通信主机和多个通信从机。I2C通信只需要2条双向总线:一条数据线SDA(Serial Data Line,串行数据线),一条时钟线SCL(Serial Clock Line,串行时钟线)。

其中:
SDA:用于数据传输(传输方式:大端传输(MSB),一次8bit,即1字节)
SCL:用于数据同步收发

IIC支持多主控,任何时间点都只有一个主控。每个连接到总线的设备都只有一个独立的地址,共7bit(也有10bit的,不过用的少点而已)主机正式利用该地址对设备进行访问。
先来看个图:
IIC中SDA和SCL引脚是开开漏电路形式,所以SDA和SCL都要接上拉电阻,当总线空闲,SDA和SCL都是高电平。

a.只要求两条总线线路,一条是串行数据线SDA,一条是串行时钟线SCL。(IIC是半双工,而不是全双工)。
b.每个连接到总线的器件都可以通过唯一的地址和其它器件通信,主机/从机角色和地址可配置,主机可以作为主机发送器和主机接收器。
c.IIC是真正的多主机总线,(而类似的SPI总线协议在每次通信前都需要把主机定死,IIC可以在通讯过程中,改变主机),如果两个或更多的主机同时请求总线,可以通过冲突检测和仲裁防止总线数据被破坏。但是任意时刻只能有一个主机。
d.传输速率在标准模式下可以达到100kb/s,快速模式下可以达到400kb/s。
e.连接到总线的IIC设备数量只是受到总线的最大负载电容400pf限制。

14.2.1 I2C协议层

一个完整的IIC数据传输含有开始信号、器件地址、读写控制、器件内地址访问、数据的有效性、信号应答、停止信号。

1.I2C总线的位传输

数据传输 :SDA的数据在SCL高电平期间被写入从机。所以SDA的数据变化要发生在SCL低电平期间。
在时钟的高电平周期内,SDA线上的数据必须保持稳定,SDA上传输1位数据(数据线仅可以在时钟SCL为低电平时改变)。
IIC位传输时序图

2.I2C总线的开始信号和结束信号

起始条件:当SCL为高电平的时候,SDA线上由高到低的跳变被定义为起始条件。
结束条件:当SCL为高电平的时候,SDA线上由低到高的跳变被定义为停止条件。
总线空闲状态 :SDA :高电平 SCL : 高电平

IIC开始信号和借宿信号时序图
根据上图我们分开进行分析:
当SCL为高电平的时候。SDA从高电平到低电平,这就是开始信号(中间会有极小的延时这点要注意,具体的请参考数据手册),如下图:

当SCL为高电平的时候。SDA从低电平到高电平,这就是停止信号(中间会有极小的延时这点要注意,具体的请参考数据手册),如下图:

3.I2C总线的字节格式


发送到SDA上的每个字节必须是8位,每次传输可以发送的字节数量不受限制,数据从量高有效位(MSB)开始传输。接收器在每成功接收一个字节后都会返回发送器一个应答位。如果从机要完成一些其他功能(如一个内部中断服务程序)才能接收或发送下一个完整的字节,则可以使SCL保持低电平,从而迫使主机进入等待状态。当从机准备好新的字节数据传输时,释放SCL,数据传输便继续进行。

总结:
当IIC主机(不一定是发送端还是接受端)将8位数据或命令传出后,会将SDA信号设置为输入,等待从机应答( 等待SDA由高电平拉为低电平 )。
若从机正确应答,表明数据或者命令传输成功,否则传输失败,注意,应答信号是数据接收方发送给数据发送方的。

4.I2C应答信号

在主机发送完每一个字节数据后,释放SDA(保持高电平),被寻址的接收器在成功接收到每个字节后,必须产生一个应答ACK(从机将SDA拉低,使它在这个时钟脉冲的高电平期间保持稳定的低电平)。当从机接收不到数据或通信故障时,从机必须使SDA保持高电平。主机产生一个结束信号终止传输或者产生重复开始信号开始新的传输。
SDA上发送的每个字节必须为8位,其后必须跟一个应答位。12C总线上的所有数量都是以8位字节传送的,发送器每发送一个字节,就在时钟脉冲9期间释放数据线,由接收器反一个应答信号。当应答信号为低电平时,规定为有效应答位(ACK),表示接收器已经成功地接收了该字节:当应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。
应答ACK要求接收器在第9个时钟脉冲之前的低电平期间将SDA拉低,并且确保在该时钟的高电平期间为稳定的低电平。如果接收器是主机,则在它收到最后一个字节后,发送一个NACK
信号,以通知从机发送器结束数据发送,并释放SDA,以便主机接收器发送一个结束信号。传输过程中每次可以发送的字节数量不受限制。首先传输的是数据的最高有效位(MSB)。
如果从机要在完成一些其他功能之后才能接收或发送下一个完整的数据字节,则可以使SCL保
持低电平,从而迫使主机进入等待状态。当从机准备好接收下一个数据字节,并且释放SCL后,数据传输继续。
12C总线必须由主器件控制,即必须由主机产生开始信号、结束信号和时钟信号。在时钟信
号为高电平时,SDA上的数据必须保持稳定,SDA上的数据状态仅在时钟信号为低电平时才可
以改变,而当SCL为高电平时,SDA上数据的改变被用来表示开始条件和停止条件。需要说明
的是,当主机接收数据时,在最后一个数据字节,必须发送一个非应答信号(NACK),使从机
释放SDA,以便主机产生一个结束信号来终止总线的数据传送,

总结下来就这两句话:
每当主机向从机发送完一个字节的数据,主机总是需要等待从机给出一个应答信号,以确认从机是否成功接收到了数据。
从机应答主机所需要的时钟仍是主机提供的,应答出现在每一次主机完成8个数据位传输后紧跟着的时钟周期,低电平0表示应答,1表示非应答。

5.I2C总线的仲裁机制

  1. 多主机 - 多个主机能够在没有冲突下的情况下,同时共存于总线上,能够通过总线仲裁避免碰撞或数据丢失。
  2. 仲裁 - 预先安排的时序,一次只授权一个主机控制总线。当SCL线是高电平时,仲裁在SDA线上发生。在其他主机发送低电平时,发送高电平的主机将会断开它的数据传输级,因为总线上的电平与它自己的电平不同(线与连接)。(也就是地址越小,竞争力越强)。

6.从机地址和子地址

在开始条件(S)后,主机发送一个从机地址(或叫作器件地址),指该器件在I2C总线上被主机寻址的地址,地址共有7bit,紧接着的第8bit是数据的读写标志位(0表示写,1表示读)。
从机地址构成:
从机地址构成

7.主机发送数据流程

(1)主机在检测到总线为空闲状态(即SDA、SCL均为高电平)时,发送一个开始信号(S),开始一次通信。
(2)主机接着发送一个命令字节。该字节由7位的器件地址和l位读写控制位R/W组成(此
时R/W=0)。
(3)相对应的从机收到命令字节后向主机回馈ACK信号(ACK=0)。
(4)主机收到从机的ACK信号后,开始发送操作器件内部存储空间的子地址或子地址的高8位。
例如,AT24C02 EEPROM器件内部存储空间访问只需要8位地址。而AT24C256EEPROM器件内部容量较大,子地址需要16位,那么这一个子地址就是16位子地址的高8位。
(5)从机成功接收后,返回一个ACK信号。
(6)主机收到ACK信号后再发送下一个数据字节或子地址的高8位。
(7)从机成功接收后,返回一个ACK信号。
(8)主机的一次发送通信,其发送的数据数量不受限制,当主机发送完最后一个数据字节并收到从机的ACK信号后,通过向从机发送一个结束信号(P)结束本次通信并释放总线。从机收到结束信号后也退出与主机之间的通信。
I2C总线主机发送数据流程(8位从机地址)如图所示。

I2C总线主机发送数据流程(16位从机地址)如图所示。

8.主机接收数据流程

(1)主机在检测到总线为空闲状态(即SDA、SCL均为高电平)时,发送一个开始信号,开始一次通信。
(2)主机接着发送一个命令字节。该字节由7位的器件地址和1位读写控制位R/W组成(此时R/w=0)。
(3)相对应的从机收到命令字节后向主机回馈ACK信号(ACK=0)。
(4)主机收到从机的ACK信号后开始发送操作器件内部存储空间的子地址。
(5)从机成功接收后,返回一个ACK信号。
(6)主机收到应答信号后,重新产生一个起始信号。
(7)主机接着发送一个命令字节。该字节由7位的器件地址和1位读写控制位R/W组成(此时R/W=1)。
(8)相对应的从机收到命令字节后向主机回馈ACK信号。
(9)接着,主机开始接收从机发送过来的数据,在主机成功接收数据后,如果需要再次接收数据的话,则主机需要向从机发送一个ACK信号。接收数据的数量不限。
(10)主机接收到最后一个数据的话,主机向从机发送一个NACK信号。
(11)从机发送一个结束信号结束本次通信并释放总线。从机收到结束信号后也退出与主机
之间的通信。
12C总线主机接收数据流程如图所示。

14.2 软件模拟I2C协议程序分析

14.2.1 模拟IIC协议简要说明

这点在开头已经说过这里再说一下:
由于I2C总线占用的I/O仅需要2根,在很多的实际使用过程中,会使用GPIO引脚来模拟I2C的SDA引脚和SCL引脚,并使用程序来实现I2C协议时序。
软件模拟I2C协议的优点如下。
(1)不需要专门的硬件I2C的控制器。
(2)引脚可以任意分配,方便PCB布线。
(3)软件修改灵活。
缺点:由于采用软件指令会产生时间的延时,不能用于一些时间要求较高的场合。

14.2.2 I2C引脚配置

说明:I2C引脚配置和普通IO配置的过程是一样的。

1.引脚工作模式初始化

将SDA设置为输出模式

void SDA_OUT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOH,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; 
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; 
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz; 
GPIO_Init(GPIOH, &GPIO_InitStructure);
   }

对于IIC主机来说,SCL在整个通信过程中都是作为输出模式的、

//将SCL设置为输出模式
void SCL_OUT(void)//PA1
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	RCC_AHB1PeriphClockCmd(EEPROM_I2C_SCL_GPIO_CLK,ENABLE);
	
	GPIO_InitStructure.GPIO_Pin = EEPROM_I2C_SCL_PIN; 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; 
	GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz; 
	GPIO_Init(EEPROM_I2C_SCL_GPIO_PORT, &GPIO_InitStructure);
}  

对IIC主机,SDA在整个通信过程中,输出数据的时候位输出模式,接収数据或检查ACK信号时为输入模式。

//将SDA设置为输入模式
void SDA_IN(void)  //PA0
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	RCC_AHB1PeriphClockCmd(EEPROM_I2C_SDA_GPIO_CLK,ENABLE);
	
	GPIO_InitStructure.GPIO_Pin = EEPROM_I2C_SDA_PIN; 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; 
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_Init(EEPROM_I2C_SDA_GPIO_PORT, &GPIO_InitStructure);
}  
//将SDA设置为输出模式
void SDA_OUT(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	RCC_AHB1PeriphClockCmd(EEPROM_I2C_SDA_GPIO_CLK,ENABLE);
	
	GPIO_InitStructure.GPIO_Pin = EEPROM_I2C_SDA_PIN; 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; 
	GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz; 
	GPIO_Init(EEPROM_I2C_SDA_GPIO_PORT, &GPIO_InitStructure);
 }  

//初始化IIC
void IIC_Init(void)
{					     
	SDA_OUT();
 	SCL_OUT();

	IIC_SCL_Hi     ;
	IIC_SDA_Hi;
}

2.I2C引脚读写控制

IO操作函数
	 
#define IIC_SCL_ Hi   GPIO_SetBits(GPIOH,GPIO_Pin_4) //SCL输出高电平
#define IIC_SCL_ Lo   GPIO_ResetBits(GPIOH,GPIO_Pin_4) //SCL输出低电平
#define IIC_SDA_ Hi   GPIO_SetBits(GPIOH,GPIO_Pin_5) //SDA输出高电平
#define IIC_SDA_ Lo   GPIO_ResetBits(GPIOH,GPIO_Pin_5) //SDA输出低电平

#define IIC_SDA_In    GPIO_ReadInputDataBit (GPIOH,GPIO_Pin_5) //输入SDA状态 

2.1主机产生IIC起始信号

起始信号:当SCL为高电平时,SDA由高电平转向低电平,开始进行数据的传输工作。开始信号由主机产生。

2.2主机产生IIC停止信号

停止信号:当SCL为高电平时,SDA由低电平转高向电平,结束数据的传输工作。停止信号由主机产生。

3 模拟检测ACK信号

主机每发送一个数据到总线,而且从机每成功收到数据后,都会有一个响应(ACK信号0)给主机,主机根据检测到的总线上的电平,判断通信是否成功(0成功,1失败)。

4 软件模拟产生ACK信号和NACK信号

当主机成功接收到从机发送过来的数据,若主机需要接収继续接收数据,则主机需要返回给从机一个应答信号,若接収的是最后一个数据,主机会返回给从机一个非应答信号。

5 软件模拟发送一个字节数据

数据传输的时候,若SCL为高,则SDA必须保持稳定,SDA上传输1位数据。若SCL为低电平时,SDA才可以改变电平。

6 软件模拟接收一个字节数据

读一个字节,当ACK=1,发送ACK信号,ACK=0,发送NACK。

7 软件模拟I2C完整写操作(以AT24xx为例子)

完整的IIC写操作:开始信号、发送器件地址+写控制、发送器件子地址(器件内寻址用)、写数据、检测ACK信号、信号停止。

8 软件模拟I2C完整读操作

完整的IIC读操作:开始信号、发送器件地址+写控制、发送器件子地址(器件内寻址用)、重启总线、读数据、检测ACK信号、信号停止。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南山府嵌入式

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值