STM32 I2C通信协议+CubeMx配置

概念

两线式串行总线。由数据线SDA时钟SCL构成的串行总线,可发送和接收数据。(半双工通信 )在CPU与被控IC之间、IC与IC之间进行双向传送,高速IIC总线一般可以达到400kbps以上。

I2C协议

空闲状态

I2C总线的SDA和SCL两条信号线同时处于高电平时,规定为总线的空闲状态。此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。

起始信号与停止信号

  • 起始信号:SCL为高期间,SDA由高到低的跳变;启动信号是一种电平跳变时序信号,而不是一个电平信号。
  • 停止信号:SCL为高期间,SDA由低到高的跳变;停止信号也是一种电平跳变时序信号,而不是一个电平信号。
    I2C图解

如何通俗地理解起始信号和停止信号呢?

我们可以把SDA当作一个单刀单掷的开关,我把开关放下去(就是SDA由高到低的过程),SDA和SCL就接通了,就可以看成是开始(起始)。反之,我把开关抬起来(就是SDA由低到高的过程),SDA和SCL就断开了,就可以看成是结束(停止)。

应答信号ACK

发送器每发送一个字节(8位),就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。应答信号为低电平时,规定为有效应答位(ACK简称应答位),表示接收器已经成功地接受了该字节(就是说当已经接收完第八位信号的时候,也就是已经完整接收一个字节了,就要开始把SDA数据线下拉了);应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。

对于反馈有效应答位ACK的要求是,接收器在第9个时钟脉冲之前的低电平期间将SDA线拉低,并且确保在该时钟的高电平期间为稳定的低电平。

如果接收器是主控器,则在它收到最后一个字节后,发送一个NACK信号,以通知被控发送器结束数据发送,并释放SDA线,以便主控接收器发送一个停止信号P。
I2C总线的响应

数据的有效性

I2C总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。

即:数据在SCL的上升沿到来之前就需准备好,并在在下降沿到来之前必须稳定。

数据的有效性

数据传输

在I2C总线上传送的每一位数据都有一个时钟脉冲相对应(或同步控制),即在SCL串行时钟的配合下,在SDA上逐位地串行传送每一位数据。数据位的传输是边沿触发

IIC读写过程

主向从写数据

主向从写数据

(灰色的表示主→从,白色的表示从→主;A表示ACK,非A表示NACK,读写位:0写/1读)

过程:首先主机会产生一个起始信号,开始数据的传输。然后主机向从机发送地址位和读写位0(因为要确定哪个从机以及做什么操作),应答成功后,就会开始发送数据,从机会根据接收到的数据返回有效应答或者无效应答,直到主机产生停止信号,就表示这段传输结束了。

从被主读数据

从被主读数据
(灰色的表示主→从,白色的表示从→主;A表示ACK,非A表示NACK,读写位:0写/1读)

过程:大概的逻辑跟上述一样,这里就不做过多的赘述了。

多主机IIC总线仲裁

当IIC总线上挂在多个器件的时候,有时候会发生两个或多个主器件同时想占用总线的情况(总线竞争)。IIC总线具有多主控的能力,那么这个时候就需要进行总线仲裁了。

当多个主器件同时想占用总线时,如果某个主器件发送高电平,而另一个主器件发送低电平,则发送电平与此时SDA总线电平不符的那个器件将自动关闭其输出级

总线竞争的仲裁在两个层次上进行:首先是地址位的比较,如果主器件寻址同一个从器件,则进入数据位比较,从而确保竞争仲裁的可靠性。由于是利用IIC总线上的信息进行仲裁,不会造成信息的丢失

举个例子

假设主控器1要发送的数据DATA1为"101…“,主控器2要发送的数据DATA2为"1001…”,总线被启动后两个主控器在每发送一个数据位时都要对自己的输出电平进行检测,只要检测的电平与自己发出的电平一致,他们就会继续占用总线,在这种情况下还是得不到仲裁,当主控器1发送第3位数据"1"时(主控器2发送"0"),由于"线与"的结果SDA上的电平为"0",这样当主控器1检测自己的输出电平时,就会测到一个与自身不相符的"0"电平,这时主控器1只好放弃对总线的控制权;因此主控器2就成为总线的唯一主宰者。

不难看出:

  1. 对于整个仲裁过程主控器1和主控器2都不会丢失数据。
  2. 各个主控器没有对总线实施控制的优先级别。
  3. 总线控制随即而定,他们遵循"低电平优先"的原则,即谁先发送低电平谁就会掌握对总线的控制权。

总结:

  1. 主控器通过检测SCL的电平来调节与从器件的速度同步问题——时钟同步。
  2. 主控器通过检测SDA上自身发送的电平来判断是否发生总线"冲突"——总线仲裁。因此,I2C总线的"时钟同步"与"总线仲裁"是靠器件自身接口的特殊结构得以实现的。

IIC CubeMX配置

配置

1.设置RCC时钟,将其配置为高速外部时钟HSE选择外部时钟源
I2C配置

2.IIC的配置
IIC配置2
这里要说一下Clock stretching(时钟拉伸)clock stretching通过将SCL线拉低来暂停一个传输,直到释放SCL线为高电平,传输才继续执行,clock streting是可选的,实际上大多数从设备不包括SCL驱动,所以他们不能stretch时钟。

重要函数

其实像发送中断,接收中断这些也算是重要,但是由于篇幅太长了就不过多叙述,其实跟串口中断是异曲同工罢了。下面介绍一些最基本的通信函数:

HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c,uint16_t DevAddress,uint8_t *pData,uint16_t Size,uint32_t Timeout);
- 功能: 写数据
- 参数:
  *hi2c      设置使用的是那个IIC 例:&hi2c1
  DevAddress 写入的地址 设置写入数据的地址 例:0xA0
  *pData     需要写入的数据
  Size       需要发送的字节数
  Timeout    最大传输时间,超过传输时间将自动退出传输函数
  
  // 发送两个字节数据,IIC写数据函数
- 例如:HAL_I2C_Master_Transmit(&hi2c1,0xA0,(uint8_t*)TxData,2,1000);
HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c,uint16_t DevAddress,uint8_t *pData,uint16_t Size,uint32_t Timeout);
- 功能: 接收数据
- 参数:
  *hi2c      设置使用的是那个IIC 例:&hi2c2
  DevAddress 写入的地址 设置写入数据的地址 例:0xA0
  *pData     存储读取到的数据
  Size       需要发送的字节数
  Timeout    最大传输时间,超过传输时间将自动退出传输函数
HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout);
- 功能:IIC写多个数据,该函数适用于IIC外设里面还有子地址寄存器的地址,比如E2PROM,除了
       设备地址,每个存储字节都有其对应的地址。
- 参数:
  *hi2c      设置使用的是那个IIC 例:&hi2c2
  DevAddress 写入的地址 设置写入数据的地址 例:0xA0
  MemAddress 从机寄存器地址,每写入一个字节数据,地址就会自动+1
  MemAddSize 从机寄存器地址字节长度 8/16位
             写入数据的字节类型 8/16I2C_MEMADD_SIZE_8BIT
             I2C_MEMADD_SIZE_16BIT
  *pData     需要写入的数据的起始地址
  Size       传输数据的大小,需要发送的字节数
  Timeout    最大传输时间,超过传输时间将自动退出传输函数
- 例如:HAL_I2C_Mem_Write(&hi2c1,ADDR,i,I2C_MEMADD_SIZE_8BIT,&(I2C_Buffer_Write[i]),8,1000);

其实HAL_I2C_Mem_Write就是等价于先用HAL_I2C_MHALaster_Transmit传输第一个寄存器地址,再用HAL_I2C_MHALaster_Transmit传输写入第一个寄存器的数据。

所以,如果只是往某个外设中读/写数据,则用Master_Receive/Master_Transmit就OK了,如果是外设里面还有子地址(像E2PROM),有设备地址,还有每个数据的寄存器存储地址,则用Mem_Write。(Mem_Write是两个地址,Master_Transmit只有从机地址)。

猜你喜欢

转载自blog.csdn.net/dbqwcl/article/details/127094071