细说STM32F407单片机IIC总线基础知识

目录

[一、 I2C总线结构](#一、 I2C总线结构)

1、I2C总线的特点

2、I2C总线通信协议

[3、 STM32F407的I2C接口](#3、 STM32F407的I2C接口)

[二、 I2C的HAL驱动程序](#二、 I2C的HAL驱动程序)

[1、 I2C接口的初始化](#1、 I2C接口的初始化)

2、阻塞式数据传输

(1)函数HAL_I2C_IsDeviceReady()

(2)主设备发送和接收数据

(3)从设备发送和接收数据

(4)I2C存储器数据传输

3、中断方式数据传输

[4、 DMA方式数据传输](#4、 DMA方式数据传输)

5、其它


I2C(Inter-Integrated Circuit)接口,有时也写作IIC或I²C接口,是一种串行数字总线接口。I2C接口只有2根信号线,总线上可以连接多个设备,硬件实现简单,可扩展性强。I2C接口主要用于通信速率要求不高,以及多个器件之通信的应用场景。

本文使用的开发板:旺宝红龙STM32F407ZGT6 KIT V1.0。

一、 I2C总线结构

一个器件的I2C接口只有2根信号线,即双向串行数据线SDA和时钟信号线SCL。I2C是种多设备总线,一根I2C总线上可以挂载多个设备。

1、I2C总线的特点

  • I2C总线只有两根信号线,SDA是双向串行数据线,SCL是时钟信号线,用于数据收发的同步。
  • I2C总线上可以挂载多个设备,一般有一个主设备、多个从设备。MCU一般作为主设备,外围器件作为从设备。在I2C通信协议中,主动发起通信的器件就是主设备,被动进行响应的器件就是从设备。
  • I2C总线上每个器件有一个7位或10位的地址,主设备发起通信时,会首先发送目标设备地址,只有地址对应的从设备才会做出响应。
  • I2C总线的两根信号线有上拉电阻。当I2C器件空闲时,其输出接口是高阻态。当所有设备都空闲时,I2C总线上是高电平。
  • I2C通信有标准模式和快速模式,标准模式传输速率为100kbit/s,快速模式传输速率为400kbit/s。

2、I2C总线通信协议

I2C通信总是由主机启动,每个通信过程由起始信号开始,由停止信号结束。一个数据包有8位,每个数据包后有一个应答位(ACK)或非应答位(NACK)。例如,主设备向从设备发送1字节数据的时序图:

  • 起始位:当SCL是高电平时,SDA的下跳沿就是起始位,是启动一次I2C通信的起始信号。
  • 停止位:当SCL为高电平时,SDA的上跳沿就是停止位,是停止一次I2C通信的结束信号。
  • 数据位:在SCL的一个时钟周期内传输一个数据位,当SCL为低电平时,发送设备更新SDA的电平,当SCL为高电平时,接收设备读取SDA的电平就是有效的一位数据。
  • 数据包:I2C数据通信一个数据包总是8位,也就是1字节的数据。
  • 应答信号:在发送完8位数据包后,发送设备在第9个SCL时钟周期采集接收设备的应答信号。若在SCL的第9个周期采集的SDA为低电平,就是应答信号ACK,如果采集的SDA是高电平,就是非应答信号NACK。

在一次I2C通信过程中,可以传输多字节的数据。主机启动I2C通信后,发送的第一个字节是目标设备地址,后面再发送或接收的数据由具体器件的指令定义决定。I2C通信协议只是定义了基本的数据传输时序,并且通信时序由MCU的硬件I2C接口实现。

3、 STM32F407的I2C接口

STM32F407芯片上有3个硬件I2C接口,记作I2C1、I2C2和I2C3,均支持I2C标准模式和I2C快速模式,还与系统管理总线(System Management Bus,SMBus)2.0兼容。STM32F407上的I2C接口具有如下特性。

  • 同一个I2C接口既可以工作于主模式,又可以工作于从模式。
  • 工作于从模式时,可以设置两个从设备地址,从而对两个从地址应答。
  • 使用7位或10位设备地址,还可以进行广播呼叫。
  • 支持不同的通信速度:标准模式传输速率为100kbit/s,快速模式传输速率为400kbit/s。
  • 带DMA功能的1字节缓存。

二、 I2C的HAL驱动程序

I2C的HAL驱动程序头文件是stm32f4xx_hal_i2c.h和stm32f4xx_hal_i2c_ex.h。I2C的HAL驱动程序包括宏定义、结构体定义、宏函数和功能函数。I2C的数据传输有阻塞式、中断方式和DMA方式。

1、 I2C接口的初始化

对I2C接口进行初始化配置的函数是HAL_I2C_Init(),其函数原型定义如下:

cpp 复制代码
HAL_StatusTypeDef HAL_I2C_Init(I2C_HandleTypeDef *hi2c)

其中,hi2c是I2C接口的对象指针,是I2C_HandleTypeDef结构体类型指针。在STM32CubeIDE自动生成的文件i2c.c中,会为启用的I2C接口定义外设对象变量,例如,为I2C1接口定义的变量如下:

cpp 复制代码
I2C_HandleTypeDef hi2c1; //I2C1接口的外设对象变量

结构体I2C_HandleTypeDef的成员变量主要是HAL程序内部用到的一些定义,只有成员变量Init是需要用户配置的I2C通信参数,是I2C_InitTypeDef结构体类型。

2、阻塞式数据传输

阻塞式数据传输使用方便,且I2C接口的传输速率不高,一般传输数据量也不大,阻塞式传输是常用的数据传输方式。

|---------------------------|--------------------------|
| 函数名 | 功能描述 |
| HAL_I2C_IsDeviceReady() | 检查某个从设备是否准备好了I2C通信 |
| HAL_I2C_Master_Transmit() | 作为主设备向某个地址的从设备发送一定长度的数据 |
| HAL_I2C_Master_Receive() | 作为主设备从某个地址的从设备接收一定长度的数据 |
| HAL_I2C_Slave_Transmit() | 作为从设备发送一定长度的数据 |
| HAL_I2C_Slave_Receive() | 作为从设备接收一定长度的数据 |
| HAL_I2C_Mem_Write() | 向某个从设备的指定存储地址开始写入一定长度的数据 |
| HAL_I2C_Mem_Read() | 从某个从设备的指定存储地址开始读取一定长度的数据 |

(1)函数HAL_I2C_IsDeviceReady()

函数HAL_I2C_IsDeviceReady()用于检查I2C网络上一个从设备是否做好了I2C通信准备,其函数原型定义如下:

cpp 复制代码
HAL_StatusTypeDef HAL_I2C_IsDeviceReady(I2C_HandleTypeDef *hi2c,uint16_t DevAddress,uint32_t Trials,uint32_t Timeout);

其中,hi2c是I2C接口对象指针,DevAddress是从设备地址,Trials是尝试的次数,Timeout是超时等待时间(单位是嘀嗒信号节拍数),当SysTick定时器频率为默认的1000Hz时,Timeout的单位就是ms。

(2)主设备发送和接收数据

一个I2C总线上有一个主设备,可能有多个从设备。主设备与从设备通信时,必须指定设备地址。I2C主设备发送和接收数据的两个函数的原型定义如下:

cpp 复制代码
HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c,uint16_t DevAddress,uint8_t *pData,uint16_t Size,uint32_t Timeout);

HAL_StatusTypeDef HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c,uint16_t DevAddress,uint8_t *pData,uint16_t Size,uint32_t Timeout);

其中,pData是发送或接收数据的缓冲区,Size是缓冲区大小。DevAddress是从设备地址无论是发送还是接收,这个地址都要设置为I2C设备的写操作地址。Timeout为超时等待时间单位是嘀嗒信号节拍数。

阻塞式操作函数在数据发送或接收完成后才返回,返回值为HAL_OK时表示传输成功,否则可能是出现错误或超时。

(3)从设备发送和接收数据

I2C从设备发送和接收数据的两个函数的原型定义如下:

cpp 复制代码
HAL_StatusTypeDef HAL_I2C_slave_Transmit(I2C_HandleTypeDef *hi2c,uint8_t *pData,uint16_t Size,uint32_t Timeout);

HAL_StatusTypeDef HAL_I2C_slave_Receive(I2C_HandleTypeDef *hi2c,uint8_t *pData,uint16_t size,uint32_t Timeout)

I2C从设备是应答式地响应主设备的传输要求,发送和接收数据的对象总是主设备,所以函数中无须设置目标设备地址。

(4)I2C存储器数据传输

对于I2C接口的存储器,例如EEPROM芯片24C02,有两个专门的函数用于存储器数据读写。向存储器写入数据的函数是HAL_I2C_Mem_Write(),其原型定义如下:

cpp 复制代码
HAL_StatusTypeDef 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);

其中,DevAddress是I2C从设备地址,MemAddress是存储器内部写入数据的起始地址,MemAddSize是存储器内部地址大小,即8位地址或16位地址,有两个宏定义表示存储器内部地址大小。

cpp 复制代码
#define I2C_MEMADD_SIZE_8BIT 0x00000001U  //8位存储器地址
#define I2C_MEMADD_SIZE_16BIT 0x000000100 //16位存储器地址

参数pData是待写入数据的缓冲区指针,Size是待写入数据的字节数,Timeout是超时等待时间。使用这个函数可以很方便地向I2C接口存储器一次性写入多字节的数据。从存储器读取数据的函数是HAL_I2C_Mem_Read),其原型定义如下:

cpp 复制代码
HAL_StatusTypeDef HAL_I2C_Mem_Read(I2C_HandleTypeDef *hi2c,uint16_t DevAddress,uint16_t MemAddress,uint16_t MemAddSize,uint8_t *pData,uint16_t Size,uint32_t Timeout);

使用I2C存储器数据传输函数的好处是,可以一次性传递地址和数据,函数会根据存储器的I2C通信协议依次传输地址和数据,而不需要用户自己分解通信过程。

3、中断方式数据传输

一个I2C接口有两个中断号,一个用于事件中断,另一个用于错误中断。HAL_I2C_EV_IRQHandler()是事件中断ISR中调用的通用处理函数,HAL_I2C_ER_IRQHandler()是错误中断ISR中调用的通用处理函数。

I2C接口的中断方式数据传输函数,以及各个传输函数关联的回调函数如表:

|------------------------------|---------------------------|--------------------------------|
| 函数名 | 函数功能描述 | 关联的回调函数 |
| HAL_I2C_Master_Transmit_IT() | 主设备向某个地址的从设备发送 一定长度的数据 | HAL_I2C_MasterTxCpltCallback() |
| HAL_I2C_Master_Receive_IT() | 主设备从某个地址的从设备接收 一定长度的数据 | HAL_I2C_MasterRxCpltCallback() |
| HAL_I2C_Master_Abort_IT() | 主设备主动中止中断传输过程 | HAL_I2C_AbortCpltCallback() |
| HAL_I2C_Slave_Transmit_IT() | 作为从设备发送一定长度的数据 | HAL_I2C_SlaveTxCpltCallback() |
| HAL_I2C_Slave_Receive_IT() | 作为从设备接收一定长度的数据 | HAL_I2C_SlaveRxCpltCallback() |
| HAL_I2C_Mem_Write_IT() | 向某个从设备的指定存储地址开 始写入一定长度的数据 | HAL_I2C_MemTxCpltCallback() |
| HAL_I2C_Mem_Read_IT() | 从某个从设备的指定存储地址开 始读取一定长度的数据 | HAL_I2C_MemRxCpltCallback() |
| 所有中断方式传输函数 | 中断方式传输过程出现错误 | HAL_I2C_ErrorCallback() |

中断方式数据传输函数的参数定义与对应的阻塞式传输函数类似,只是没有超时等待参数Timeout。例如,以中断方式读写I2C接口存储器的两个函数的原型定义如下:

cpp 复制代码
HAL_StatusTypeDef HAL_I2C_Mem_Write_IT(I2C_HandleTypeDef *hi2c,uint16_t DevAddress,uint16_t MemAddress,uint16_t MemAddSize,uint8_t *pData,uint16_t Size);

HAL_StatusTypeDef HAL_I2C_Mem_Read_IT(I2C_HandleTypeDef *hi2c,uint16_t DevAddress,int16_t MemAddress,uint16_t MemAddSize,uint8_t *pData,uint16_t Size);

中断方式数据传输是非阻塞式的,函数返回HAL_OK只是表示函数操作成功,并不表示数据传输完成,只有相关联的回调函数被调用时,才表示数据传输完成。

4、 DMA方式数据传输

一个I2C接口有I2C_TX和I2C_RX两个DMA请求,可以为DMA请求配置DMA流,从而进行DMA方式数据传输。I2C接口的DMA方式数据传输函数,以及DMA流发生传输完成事件(DMA_IT_TC)中断时的回调函数如表:

|-------------------------------|---------------------------|--------------------------------|
| 函数名 | 函数功能描述 | 关联的回调函数 |
| HAL_I2C_Master_Transmit_DMA() | 向某个地址的从设备发送一 定长度的数据 | HAL_I2C_MasterTxCpltCallback() |
| HAL_I2C_Master_Receive_DMA() | 从某个地址的从设备接收一 定长度的数据 | HAL_I2C_MasterRxCpltCallback() |
| HAL_I2C_Slave_Transmit_DMA() | 作为从设备发送一定长度的 数据 | HAL_I2C_SlaveTxCpltCallback() |
| HAL_I2C_Slave_Receive_DMA() | 作为从设备接收一定长度的 数据 | HAL_I2C_SlaveRxCpltCallback() |
| HAL_I2C_Mem_Write_DMA() | 向某个从设备的指定存储地 址开始写入一定长度的数据 | HAL_I2C_MemTxCpltCallback() |
| HAL_I2C_Mem_Read_DMA() | 从某个从设备的指定存储地 址开始读取一定长度的数据 | HAL_I2C_MemRxCpltCallback() |

DMA传输函数的参数形式与中断方式传输函数的参数形式相同,例如,以DMA方式读写I2C接口存储器的两个函数的原型定义如下:

cpp 复制代码
HAL_StatusTypeDef HAL_I2C_Mem_Write_DMA(I2C_HandleTypeDef *hi2c,uint16_t DevAddress,uint16_t MemAddress,uint16_t MemAddSize,uint8_t *pData,uint16_t Size);

HAL_StatusTypeDef HAL_I2C_Mem_Read_DMA(I2C_HandleTypeDef *hi2c,uint16_t DevAddress,uint16_t MemAddress,uint16_t MemAddSize,uint8_t *pData,uint16_t Size);

DMA传输是非阻塞式传输,函数返回HAL_OK时只表示函数操作完成,并不表示数据传输完成。DMA传输过程由DMA流产生中断事件,DMA流的中断函数指针指向I2C驱动程序中定义的一些回调函数。I2C的HAL驱动程序中并没有为DMA传输半完成中断事件设计和关联回调函数。

5、其它

一个I2C从设备有两个地址,一个是写操作地址,另一个是读操作地址。例如开发板上的EEPROM芯片24C02的写操作地址是0xA0,读操作地址是0xA1,也就是在写操作地址上加1。在I2C的HAL驱动程序中,传递从设备地址参数时,只需设置写操作地址,函数内部会根据读写操作类型,自动使用写操作地址或读操作地址。但是在软件模拟I2C接口通信时,必须明确使用相应的地址。

IIC总线判断高低电平;SPI总线判断边沿。

相关推荐
国科安芯1 小时前
航天器多路并联大功率电源系统设计与ASP4644均流特性分析
单片机·嵌入式硬件·fpga开发·安全性测试
茯苓gao3 小时前
嵌入式开发笔记:CANopen相关移位运算与通信协议术语详解
网络·嵌入式硬件·学习·信息与通信
梁朝辉6 小时前
STM32H750VBT6中ADCINP与INN什么区别
stm32·单片机·嵌入式硬件
省四收割者7 小时前
一文详解信号完整性(1)
python·嵌入式硬件·数学建模·信息与通信·信号处理·智能硬件
崇山峻岭之间8 小时前
单片机无感FOC驱动之ADC
单片机·嵌入式硬件
Ligocious8 小时前
1.点亮一颗小小的LED
单片机·嵌入式硬件
KaifuZeng12 小时前
通信与接口协议面试八、CAN通信
单片机·嵌入式硬件·面试·通信与接口协议
智源单片机设计13 小时前
基于单片机的直流电机双闭环调速系统设计
单片机·嵌入式硬件
kebidaixu13 小时前
两轮车换电 BMS 的模拟前端中颖 SH367306 AFE 芯片详解
stm32
kebidaixu14 小时前
两轮车换电 BMS 的模拟前端中颖 SH367306 AFE 芯片调试实录:四个关键问题与解决方案
stm32