HAL库STM32常用外设教程(九)—— I2C通信(读写EEPROM)

HAL库函数中的HAL_I2C_Mem_Write /HAL_I2C_Mem_Read中的DevAddress解析 如果是写操作,即使传入的地址是0xA0,也不影响写操作,还是被处理成1010 0001;在HAL库函数中的HAL_I2C_Mem_Write /HAL_I2C_Mem_Read两个函数的作用就是玩IIC设备中写入/读取多个直接的数据,因此我们在定义EEPROM的设备地址时,可以仅定义设备的读地址,也就是 七位IIC地址+1/0;上面的这个是EEPROM设备的写地址和读地址,,一般是这种定义的,但是在野火的HAL案例中。这个是写地址,为什么不定义直接定义写地址和读地址呢? 阅读详情
AI摘要

本文围绕I2C通信展开,介绍了I2C定义、总线结构、通信协议及STM32F407的I2C接口。阐述了I2C的HAL驱动方式及程序,包括初始化、三种数据传输方式。还介绍了EEPROM芯片24C02的接口、通信协议及读写操作,最后给出通过I2C通信读写24C02的示例。


前言

一、I2C定义

  I2C(Inter-Integrated Circuit)接口,有时也写作IIC,是一种串行数字总线接口。I2C接口只有两根信号线,总线上可以连接多个设备,硬件实现简单,可拓展性强。I2C通信协议可以通过普通GPIO引脚进行软件模拟。I2C接口主要用于通信速率要求不高,以及多个器件之间通信的应用场景。

1.1 I2C总线结构

  一个器件的I2C接口只有2根信号线,即双向串行数据线SDA和时钟信号线SCL。I2C是一种多设备总线,一根I2C总线上挂载多个设备。例如,STM32F407开发板上的I2C总线通信系统如图1-1所示,它连接了多个I2C器件,从图中可以看出其中一个是EEPROM器件24C02,另一个是加速度计和陀螺仪传感器芯片MPU6050。
在这里插入图片描述

图1-1 MCU通过IIC总线的期间连接的示意图

I2C总线有如下的特点。

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

1.2 I2C总线通信协议

  I2C通信总是由主机启动,每个通信过程由起始信号开始,由停止信号结束。一个数据包有8位,每个数据包有一个应答位(ACK)或非应答位(NACK),例如,主设备向从设备发送1字节数据的时序图,如图1-2所示。
在这里插入图片描述

图1-2 IIC传输1字节的时序图
  • 起始位:当SCL是一个高电平时,SDA的下跳沿就是起始位,是启动一次I2C通信的起始信号。
  • 信号位:当SCL是高电平时,SDA的上跳沿就是停止位,是停止一次I2C通信的结束信号。
  • 数据位:在SCL的一个时钟周期内传输一个数据位,在SCL的一个时钟周期内传输一个数据位,当SCL为低电平时,发送设备更新SDA的电平,当SCL为高电平时,接收设备读取SDA的电平就是一次有效的一位数据。
  • 数据包:I2C数据通信一个数据包总是8位。也就是1字节的数据。
  • 应答信号:在发送完8位数据包后,发送设备在第9个SCL时钟周期采集接收设备的应答设备。若在SCL的第九个周期采集的SDA为低电平,就是应答信号ACK,表示接收设备 正确接收到数据包,并发出应答信号。如果采集的SDA是高电平,就是非应答信号NACK,表示接收设备未正确接收到数据包,或者不希望继续通信,故发出非应答信号。
      在一次I2C通信过程中,可以传输多个字节的数据。主机启动I2C通信后,发送的第一个字节是目标设备地址,后面在发送或接收的数据由具体器件的指令定义决定。I2C通信协议只是定义了基本的数据传输时序,图1-2的通信时序由MCU的硬件I2C接口实现,这就是软件模拟I2C通信时序2,这就是软件模拟I2C接口。

1.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库驱动程序头文件是stm32xx_hal_i2c.h和stm32xx_gal_i2c_ex.h。I2C的HAL驱动程序包括宏定义、结构体定义、宏定义和函数功能。。I2C的数据传输有阻塞式、中断方式和DMA方式

2.1 I2C接口的初始化

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

HAL_StatusTypeDef HAL_I2C_Init(I2C_HandleTypeDef *hi2c)

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

I2C_HandleTypeDef  hi2c1;     //I2C1接口的外盒对象变量

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

2.2 阻塞方式数据传输

  I2C接口的阻塞式数据传输相关函数如表1-1所示。阻塞式数据传输使用方便,且I2C接口的传输速率不高,一般传输数据量也不大。

表2-1 I2C接口的阻塞式数据传输相关函数
函数名功能描述
HAL_I2C_IsDeviceReady()检查某个从设备是否准备好了I2C通信
HAL_I2C_Master_Transmit()作为主设备向某个地址的从设备发送一定长度的数据
HAL_I2C_Master_Receive()作为主设备向某个地址的从设备接受一定长度的数据
_HAL_I2C_Slave_Transmit()开启某个中断事件源,允许事件产生硬件中断
HAL_I2C_Mem_Write()向某个从设备的指定存储地址开始写入一定长度的数据
HAL_I2C_Mem_Read()向某个从设备的指定存储地址开始读取一定长度的数据

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

 HAL_StatusTypeDef HAL_I2C_IsDeviceReady(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint32_t Trials, uint32_t Timeout);

  其中,hi2c是I2C接口对象指针,DevAddress是从设备地址,Trials是尝试的次数,Timeout是超时等待时间(单位是嘀嗒信号节拍数),当SysTicks定时器频率为默认的1000Hz时,Timeout的单位就是ms。
注:
  一个I2C从设备有两个地址,一个是写操作地址,另一个是读操作地址。例如,开发板上的EEPROM芯片24C02的写操作地址是0xA0,读操作地址是0xA1,也就是在写操作地址上加1。在I2C的HAL库驱动中,传递从设备地址参数时,只需要设置写操作地址,函数内部会根据读写操作类型,自动使用写操作地址或读操作地址。但是在软件模拟I2C接口通信时,必须明确使用相应的地址。

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

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从设备发送和接收数据的两个函数的原型定义如下:

HAL_StatusTypeDef HAL_I2C_Slave_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Slave_Receive_DMA(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size);

  I2C从设备是应答式地响应主设备的传输要求,发送和接收数据的对象总是主设备,所以函数中无需设置目标设备地址。
(4)I2C存储器数据传输
  对于I2C接口的存储器,例如EEPROM芯片24C02,有两个专门的函数用于存储器数据读写。向存储器写入数据的函数是HAL_I2C_Mem_Write(),其原型定义如下:

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位地址。在一些I2C设备中,存储器内部地址可能只需要8位来表示,而在另一些设备中,存储器内部地址可能需要16位来表示,8位地址可以表示256个不同的存储器位置,范围为0x00到0xFF,16位地址可以表示65536个不同的存储器位置,范围为0x0000到0xFFFF。程序中有两个宏定义表示存储器内部地址的大小。

#define I2C_MEMADD_SIZE_8BIT            0x00000001U
#define I2C_MEMADD_SIZE_16BIT           0x00000010U

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

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通信协议依次传输地址和数据,而不需要用户自己分解通信过程。

2.3 中断方式数据传输

  一个I2C接口有两个中断号,一个用于事件中断,另一个用于错误中断。HAL_I2C_EV_IRQHandler() 是事件中断ISR中调用处理函数,HAL_I2C_ER_IRQHandler() 是错误中断ISR中调用的通用处理函数。
I2C接口的中断方式数据传输函数,以及各个传输函数关联的回调函数如表2-2所示。

表2-2 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_Receive_IT()作为从设备接收一定长度的数据HAL_I2C_SlaveRxCpltCallback()
HAL_I2C_Slave_Transmit_IT()作为从设备发送一定长度的数据HAL_I2C_SlaveTxCpltCallback()
HAL_I2C_Mem_Write_IT()向某个从设备的指定存储地址开始写入一定长度的数据HAL_I2C_MemTxCpltCallback()
HAL_I2C_Mem_Read_IT从某个从设备的指定存储地址开始读一定长度的数据HAL_I2C_MemRxCpltCallback()
所有中断方式传输函数中断方式传输过程出现错误HAL_I2C_ErrorCallback()

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

HAL_StatusTypeDef HAL_I2C_Master_Seq_Transmit_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
HAL_StatusTypeDef HAL_I2C_Master_Seq_Receive_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t XferOptions);

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

2.4 DMA方式数据传输

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

表2-3 I2C接口的DMA方式数据传输函数以及关联的回调函数
函数名函数功能描述关联的回调函数
HAL_I2C_Master_Transmit_DMA()主设备向某个地址从设备发送一定长度的数据HAL_I2C_MasterTxCpltCallback()
HAL_I2C_Master_Receive_DMA()主设备从某个地址的从设备接收一定长度的数据HAL_I2C_MasterRxCpltCallback()
HAL_I2C_Slave_Receive_DMA()作为从设备接收一定长度的数据HAL_I2C_SlaveRxCpltCallback()
HAL_I2C_Slave_Transmit_DMA()作为从设备发送一定长度的数据HAL_I2C_SlaveTxCpltCallback()
HAL_I2C_Mem_Write_DMA()向某个从设备的指定存储地址开始写入一定长度的数据HAL_I2C_MemTxCpltCallback()
HAL_I2C_Mem_Read_DMA从某个从设备的指定存储地址开始读一定长度的数据HAL_I2C_MemRxCpltCallback()

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

HAL_StatusTypeDef HAL_I2C_Master_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Master_Receive_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);

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

三、EEPROM芯片 24C02

3.1 接口和通信协议

  开发板上有一个I2C接口的EEPROM芯片24C02,24C02存储容量为256字节,2K bit,它的电路连接如图3-1所示。其SDA和SCL引脚连接STM32F407芯片的I2C1接口,使用引脚PB9和PB8。WP是写保护引脚,WP接地时,对24C02芯片可读可写。
在这里插入图片描述

图 3-1 24C02引脚图

在这里插入图片描述

图 3-2 24C02开发板电路图

  上图中有一个 WP,这个是写保护引脚,接高电平只读,接地允许读和写,从开发板电路图中可以得出该引脚接地,可以进行读和写操作。
  每一个设备都有自己的设备地址, 24C02 也不例外,其设备地址格式如图 3-3所示,从图中可以看出,24C02 的设备地址是包括不可编程部分(1010)和可编程部分(A2 A1 A0),可编程部分是根据上图的硬件引脚 A0、 A1 和 A2 所决定 。通过图3-2的电路图可以得出, A0、 A1 和 A2 均接地处理,所以 24C02 设备的读操作地址为: 0xA1(二进制为1010 0001);写操作地址为: 0xA0(二进制为1010 0000)。
  设备地址最后一位(R/W)用于设置数据的传输方向,即读操作/写操作, 0 是写操作, 1 是读操作 设备地址最后一位(R/W)用于设置数据的传输方向,即读操作/写操作, 0 是写操作, 1 是读操作,具体格式如下图3-3所示:

在这里插入图片描述

图 3-3 设备地址格式

在这里插入图片描述

图3-3 读操作地址0xA1

  24C02的I2C设备地址组成如图3-2图所示,A2、A1、A0由芯片的引脚A2、A1、A0的电平决定,图3-1中这三个引脚都接地,所以都是0。最低位R/W是读写标志位时读写标志位,当主设备对24C02进行写操作时,R/W为0,进行读操作时,R/W为1。所以,24C02的写操作地址为0xA0,读操作地址为0xA1。HAL驱动程序函数中需要传递I2C从设备的地址时,都使用写操作地址。
  24C02的读写操作比较简单,存储空间可反复读写。以下是几种主要的读写操作定义。

3.1.1 写操作

  下面介绍一下 IIC 的基本的读写通讯过程,包括主机写数据到从机 (写操作),主机到从机读取数据(读操作)。下面先看一下写操作通讯过程图。
在这里插入图片描述

图3-4 写操作通讯过程图

  主机首先在 IIC 总线上发送起始信号,那么这时总线上的从机都会等待接收由主机发出的数据。主机接着发送从机地址+0(写操作)组成的 8bit 数据,所有从机接收到该 8bit 数据后,自行检验是否是自己的设备的地址,假如是自己的设备地址,那么从机就会发出应答信号。主机在总线上接收到有应答信号后,才能继续向从机发送数据。
注意: IIC 总线上传送的数据信号是广义的,既包括地址信号,又包括真正的数据信号。

3.1.2 读操作

在这里插入图片描述

图3-5 写操作通讯过程图

  主机向从机读取数据的操作,一开始的操作与写操作有点相似,观察两个图也可以发现,都是由主机发出起始信号,接着发送从机地址+1(读操作)组成的 8bit 数据,从机接收到数据验证是否是自身的地址。 那么在验证是自己的设备地址后,从机就会发出应答信号,并向主机返回 8bit 数据,发送完之后从机就会等待主机的应答信号。假如主机一直返回应答信号,那么从机可以一直发送数据,也就是图中的(n byte + 应答信号)情况,直到主机发出非应答信号,从机才会停止发送数据。

3.2 写一个字节数据

  MCU向24C02写入1字节数据的SDA传输内容的顺序如图所示。操作的顺序如下。

  • 主机发送起始信号,然后主机在 IIC 总线发送第 1 个字节的数据为 24C02
    的设备地址 0xA0,用于寻找总线上找到 24C02。
  • 在获得24C02应答ACK应答信号后,主机再发送8位字地址,这是24C02内部存储单元的地址。8位地址的范围是0~255,也就是24C02内256字节存储单元的地址。
  • 再等到24C02应答信号ACK后,主机再发送需要写入的1字节的数据,这里的数据即是写入在第 2 字节内存地址的数据。
  • 从机接收完数据后,应答ACK,主机发停止信号结束传输。

在这里插入图片描述

图3-6 写1个字节数据的SDA传输内容和顺序

3.3 连续写多字节数据

  上面的写操作只能单字节写入到 24C02,效率比较低,所以 24C02 有页写入时序,大大提高了写入效率。24C02内部存储区域划分,每页8字节,所以256字节的存储单元为32页,页的起始地址是8*N,其中N = 0,1,…,31。
  用户可以在一次I2C通信过程(一个起始信号与一个停止信号限定的通信过程)中向24C02连续写入多个字节的数据,SDA传输内容和顺序如图3-7所示。在单字节写时序时,每次写入数据时都需要先写入设备的内存地址才能实现,在页写时序中,只需要告诉 24C02 第一个内存地址 1,后面数据会按照顺序写入到内存地址 2,内存地址 3等,大大节省了通信时间,提高了时效性。图3-7中的n是数据存储的起始地址,存储的数据字节数为1+x,但是要注意,连续写入的数据的存储位置不超过页的边界,否则,将自动从这页的开始位置继续存储。

在这里插入图片描述

图3-7 24C02连续写入多个字节数据的SDA传输内容和顺序

  所以,在连续写数据时,如果数据起始地址在页的起始位置,则一次最多可写入8个字节的数据,当然,数据存储起始地址也可以不在页的起始位置,这时要注意,一次写入的数据不要超过页的边界。

3.4 读1字节数据

  用户可以从24C02的任何一个存储位置读取1字节的数据,读取1字节数据时,SDA传输的内容和顺序如图3-8 所示。
  24C02 读取数据的过程是一个复合的时序,其中包含写时序和读时序。先看第一个通信过程,这里是写时序,起始信号产生后,主机发送 24C02 设备地址 0xA0,获取从机应答信号后,接着发送需要读取的内存地址;在读时序中,起始信号产生后,主机发送 24C02 设备地址 0xA1,获取从机应答信号后,接着从机返回刚刚在写时序中内存地址的数据,以字节为单位传输在总线上,假如主机获取数据后返回的是应答信号,那么从机会一直传输数据,当主机发出的是非应答信号并以停止信号发出为结束,从机就会结束传输。

在这里插入图片描述

图3-8 读1个字节数据的SDA传输内容和顺序

3.5 连续读多字节数据

  用户可以从24C02一次性连续读取多字节的数据,且读取数据时不受页边界的影响,也就是读取数据的长度可以超过8个字节。连续读多个字节数据的SDA传输内容和顺存如图 所示。主设备先进行一次写操作,写入需要读取的存储单元的地址,然后再进行一次读操作,连续读取多字节,存储器内部将自动移动存储位置,且存储位置不受页边界的影响。
  在使用I2C的HAL库驱动函数进行24C02的数据读写时,图 的传输时序是由I2C硬件接口完成的,用户不需要这些时序。但如果是软件模拟I2C接口取读写24C02,则需要严格按照这些时序一个位一个位的操作。

四、示例:通过I2C通信 读写24C02

4.1 STM32CubeMx设置

  在CubeMx里面设置I2C1的模式和参数,设置界面如图4-1所示。在模式设置里中,设置接口类型为I2C。还有SMBus的选项,SMbus一般用于智能电池管理。
在这里插入图片描述

图4-1 I2C1的模式和参数设置

I2C1的参数设置分为2组,各组参数的意义和设置内容描述如下。
(1)Master Features组,主设备参数。
①I2C Speed Mode,速度模式。可选标准模式(Standard Mode)或快速模式(Fast Mode)。
②I2C Clock Speed(Hz),I2C时钟速度。标准模式下最大值为100kHz,快速模式下最大值为400kHz。
③Fast Mode Duty Cycle,快速模式占空比。选择快速模式后这个参数会出现,用于设置时钟信号的占空比,是一个周期内低电平与高电平的时间比,有2:1和16:9两种选项。本示例中,速度模式选择到标准模式,所以图4-1中没有这个参数。
(2)Slave Features组,从设备参数。
①Clock No Stretch Mode,禁止时钟延长。设置为Disable表示允许时钟延长。
②Primary Address Length selection,设备主地址长度。可选7-bit或10-bit,这里选择7-bit。
③Dual Address Acknowledge,双地址确认。从设备可以有两个地址,如果设置为Enabled,还会出现一个Secondary slave address参数,用于设置从设备副地址。
④General Call address detection,广播呼叫检测。设置为Disable表示禁止广播呼叫,不对地址0x00应答;否则,就是允许广播呼叫,对地址0x00应答。
  设置I2C的模式为I2C时,CubeMx自动分配的引脚可能是PB6和PB7,而不是开发板上实际用的PB8和PB9,这是因为一个外设有多组复合引脚。在引脚视图上直接将PB8为I2C_SCL,将PB9设置为I2C_SDA,PB6和PB7的设置就会自动取消。I2C1的GPIO引脚设置如图4-2所示,自动设置为复用功能开漏,并且有上拉。
  在开发板上,STM32F407是I2C主设备,所以无须设置从设备地址。24C02是I2C从设备,其从设备地址是0xA0。
  I2C的中断事件主要是表示传输过程中和错误的一些事件,由于I2C通信时一种应答式通信,与其他外设的轮询式操作类似,本示例不开启I2C1的中断。I2C也具有DMA功能,当时24C02操作的数据量小,没有使用DMA的必要。

4.2 程序设计

  在CubeMx里完成设置后生成代码,我们在Keil里面打开项目。
(1)先通过CubeMx自动生成的部分函数进一步谅解CubeMx的配置.
  在i2c.c文件中,函数MX_I2C1_Init()用于I2C1接口的初始化,如图4-2所示,MX_I2C1_Init()对hi2c1的各个成员变量赋值,各赋值语句与CubeMx里的配置是对应的。完成hic1的赋值后,执行HAL_I2C_Init()对I2C1接口进行初始化。HAL_I2C_MspInit()函数的是I2C接口的MSP初始化函数,在函数HAL_I2C_MspInit()里面被调用。函数HAL_I2C_MspInit() 的主要功能是对I2C1接口的复用引脚PB8和PB9进行GPIO引脚配置,如图4-3。
在这里插入图片描述

图4-2 I2C1的初始化设置① 在这里插入图片描述

图4-3 I2C1的初始化设置②

注:
  本示例是通过按键去触发相应的函数,再通过执行函数实现与EEPROM芯片24C02之间的读写操作,按键的实现过程已将在《HAL库STM32常用外设教程(二)—— GPIO输入\输出》中讲述(主要是keyled.c和keyled.h),如果又兴趣可以去了解一下,也可以直接下载其中的源码工程,在源码的基础上实现本章的项目。从另一个角度讲,如果不用按键的话同样是可以的,甚至在main函数的While循环里面直接通过延时去不断的循环这几个函数也是可以的,重点是明白其中读写EEPROM的过程是如何实现的 以及是如何通过I2C通信进行读写的。

(2)在本章的源码里面提供了两个文件,24xx.c和24cxx.h,这两个文件是24C02的驱动程序,是根据24C02的指令定义,实现器件具体功能操作的一系列函数。将这两个文件添加到本次项目的工程中,后面需要自己编写的几个函数都是调用的其提供的函数。具体要求及实现的功能如下:
  程序调用函数EP24C_IsDeviceReady()函数检查24C02器件是否正常,然后显示了一个菜单,菜单内容如下:

Divice id ready
[1]KeyUp=Write a number
[2]Key0=Read the number
[3]Key1=Write a Sting
[4]Key2=Read the string

在While循环里面检测按键输入,根据按下的按键执行相应的响应代码。

  • 按下KEY_UP键时,调用函数 EP24C_WriteOneByte() 向一个地址写入1字节的数据。
  • 按下KEY0键时,调用函数 EP24C_ReadOneByte() 从同一个地址读出数据,以检验写入和读出的是否一致。
  • 按下KEY1时,调用函数 EP24C_WriteLongData() 从Page2起始地址开始写入一个长字符串,字符串又结束字符‘\0’,函数内部会将长数据分解为多个页后分批写入。
  • 按下KEY2时 P24C_ReadBytes() 从Page2起始地址读取50个字节。

注:为了方便观察,本实例中加入了对灯的控制,即在按键按下时对应的灯的状态会发生变化。
①
在这里插入图片描述

	if(EP24C_IsDeviceReady()	== HAL_OK){
		printf("Divice id ready\n");
	}
	printf("[1]KeyUp=Write a number\n[2]Key0=Read the number\n[3]Key1=Write a Sting\n[4]Key2=Read the string");

②
在这里插入图片描述

下面将整个while(1)循环复制下来以便参考:

  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		uint16_t addr_any = 4;
		uint8_t num1 =107,num2;
		uint16_t addr_page = 2*8;
		 KEYS curKey=ScanPressedKey(KEY_WAIT_ALWAYS);

	  switch(curKey)
	  {
	  case KEY0:
		  LED0_Toggle();																		/* 切换LED0的状态 */
			if(EP24C_ReadOneByte(addr_any,&num2) == HAL_OK){	/* 从EE2PROM读取一个字节到num2,如果读取成功,返回HAL_OK */
				printf("Read %d at Arrress %d\n",num2,addr_any);
			}
		  break;

	  case KEY2:
		LED1_Toggle();																			/* 翻转LED1的状态 */
		uint8_t strOut[50];																	/* 因为case是标签,C语言中标签的下一句不能是定义变量的表达式,所以放在翻转灯后面 */
		if(EP24C_ReadBytes(addr_page,strOut,sizeof(strOut)) == HAL_OK){/* 从Page2起始地址读取50个字节 */
				printf("Read string from Page2 = %s\n",strOut);
			}
		break;

	  case KEY_UP:
		  LED0_Toggle();																	/* 翻转LED0的状态 */
		  LED1_Toggle();																	/* 翻转LED1的状态 */
			if(EP24C_WriteOneByte(addr_any,num1) == HAL_OK){/* 从EE2PROM写入一个字节到num1,如果写入成功,返回HAL_OK */
				printf("write %d at Arrress %d\n",num1,addr_any);
			}
		break;

	  case KEY1:
		{
		uint8_t strIn[50] = "University of CHAIN";      
		Buzzer_Toggle();																			/* 翻转蜂鸣器的状态 */
		if(EP24C_WriteLongData(addr_page,strIn,sizeof(strIn)) == HAL_OK){/* 从Page2起始地址开始写入2一个长字符串 */
				printf("Write string from Page2 %s\n",strIn);
			}
		}
		default:
			break;
	  }
  }
  /* USER CODE END 3 */
}  /* USER CODE BEGIN WHILE */

(3)因为上述需要用到串口,添加代码时需要注意要实现串口的重定向。

在这里插入图片描述
代码如下(示例):

/* 串口重定向 */
int fputc(int ch, FILE*f)
	{
		while (!(USART1->SR & (1 << 7)));
		USART1->DR = ch;
		return ch;
	}

4.2示例结果

  将上面的程序下载到下载板中,按下按键,在串口中进行显示,图4-4显示的是开发板的按键,图4-5是通过串口打印出来的信息,需要注意的是,由于开发板上的按钮没有进行消抖处理,所以每一次按下显示出来的次数可能有多次。
请添加图片描述

图4-4 开发板按键

在这里插入图片描述

图4-5 开发板按键

五、总结

  本文讲解了I2C的基础知识,其中涉及了I2C的通信原理、HAL库驱动程序、I2C的三种发送模式(轮询(阻塞)、中断、DMA)。然后又介绍了通过I2C通信方式的EEPROM芯片24C02,包括该芯片的 的读写等规则,然后通过一个示例 来讲解通过I2C方式读写24C02芯片得到内容。


项目源码:HAL库STM32常用外设教程(九)—— I2C通信(读写EEPROM)
参考书籍:
《STM32Cube高效开发教程(基础篇)》王维波
《STM32F4xx中文参考手册》
《STM32F407 探索者开发指南》


  无人问津也好,技不如人也罢。你都要试着安静下来,去做自己该做的事,而不是让内心烦躁,焦虑,毁掉你本就不多的热情和定力。昨日之深渊,今日之浅谈。路随远,行则将至。事随难,做则可成。

——《人民日报》

关于 STM32G0/G4/L4 HAL_IIC HAL_I2C_Mem_Read 函数读取M24C64存储芯片遇到的问题 STM32G0/G4/L4 HAL IIC HAL_I2C_Mem_Read() HAL_I2C_Mem_Write() 阅读详情

相关推荐

stm32使用i2c读写eeprom的HAL库实现

本文目标:基于HAL库的stm32实现eeprom的i2c实验按照本文的描述,应该可以跑通实验并举一反三。先决条件:装有编译和集成的开发环境,比如:Keil uVision5、STM32CubeMX使用外设:I2C1、USART1、GPIOEEPROM

weixin_44317448的博客 4032

【STM32CubeMX+HAL库】I2C详解+读写EEPROM

但是在我们的HAL库中,对硬件IIC做了全新的优化,使得之前软件IIC几百行代码,在HAL库中,只需要寥寥几行就可以完成 那么这篇文章将带你去感受下它的优异之处。AT24C设备地址为如下,前四位固定为1010,A2~A0为由管脚电平。I2C信号在进行数据传输时, 当SCL=1高电平时,数据线SDA必须保持稳定状态,不允许有电平跳变,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。所有接到I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。

2201_75394507的博客 1万+

STM32的串口I2C通信(详细介绍软件模拟+硬件实现:HAL库和寄存器三种方式)

M24C02这个EEPROM芯片啊,虽然只有8个脚,但挺能装的,有256个字节的存储空间,也就是2048个位。它的存储结构挺有意思的,像个小本子,每页有16个字节,总共16页。要找数据的时候,用8位地址就行,前4位是页码,后4位是页里的具体位置。写入数据的时候,不管是单个字节还是整页写,都得等5毫秒,就像等红灯一样,得让芯片把数据存稳了才能进行下一步。这种设计啊,虽然写入速度不算快,但挺适合那些不需要频繁改数据的应用场景。

2402_83192880的博客 3001

stm32 hal 库 iic 一直是 busy

void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle) { GPIO_InitTypeDef GPIO_InitStruct; if(i2cHandle->Instance == I2C1) { /* USER CODE BEGIN I2C1_MspInit 0 */ /* USER C...

m0_37056522的博客 1422

STM32 使用硬件I2C接口读写EEPROM

CPU作为老板,发钱需要自己转帐,收钱需要自己看信息,是一种悲哀。 上一篇《STM32 使用IO口模拟I2C时序》,我们讲述了“论老板如何自己发钱和收钱”。 这一篇《STM32 使用硬件I2C接口读写EEPROM》,我们来讲述“论老板如何雇佣员工帮忙发钱和收钱”。 我们先来招聘一个职位: 岗位职责: 1、自动产生I2C时序,向其他芯片发送(发钱)或者接收(收钱)数据。 任职要求: 1...

2985

STM32使用HAL库实现I2C通信完整指南

深入讲解STM32如何使用HAL库配置并实现I2C协议通信,涵盖初始化、读写操作与常见问题处理,帮助开发者快速掌握i2c协议在实际项目中的应用。

weixin_32312889的博客 1364

【亲测免费】 HAL库STM32常用外设教程(九)—— I2C通信(读写EEPROM)

HAL库STM32常用外设教程(九)—— I2C通信(读写EEPROM) 【下载地址】HAL库STM32常用外设教程九I2C通信读写EEPROM 本资源文件提供了关于使用HAL库在STM32微控制器上进行I2C通信的详细教程,特别是针对EEPROM的读写操作。通过本教程,您将学习如何配置STM32的I2C接口,并实现与E...

gitblog_06527的博客 782

STM32与EEPROM的I2C通信:多字节读写实战教程.zip

htmltable {th, td {th {pre {简介:本教程专注于STM32微控制器与EEPROM之间通过I2C协议实现的连续读写操作。介绍了I2C的基础知识、STM32中GPIO引脚与I2C外设的配置、EEPROM的特性及命令集,详细说明了如何在STM32平台上执行I2C通信、页写保护,以及使用HAL库进行读写操作的具体实现方法。这将帮助开发者高效掌握STM32与EEPROM的数据交互技术。

weixin_28235889的博客 1030

STM32F767+STM32CubeMX I2C通信读写EEPROM数据(采用轮询、DMA、中断三种方式)

STM32F767+STM32CubeMX I2C通信读写EEPROM数据(采用轮询、DMA、中断三种方式) 摘要-前言 作为一名STM32的初学者,在学习过程中会遇到很多问题,解决过程中会看到很多博主发过的文章,每次都是零零总总的学习各个大牛的经验。但时间久了就会忘记其中的一些关键点,所以才有了把自己解决问题的过程记录下来的想法,日后回忆起也很方便。 前人们做过很多STM32 I2C通信的努力,...

weixin_42778604的博客 1万+

Windows server 2012R2安装Oracle11g

打开 开始——所有程序——Oracle-OraDb11g_home1——配置和移植工具——Database Configuration Assistant。打开 开始——所有程序——Oracle-OraDb11g_home1——配置和移植工具——Net Configuration Assistant。首先打开Oracle的安装位置找到下面的位置找到tnsnames.ora文件右键编辑。打开Oracle的安装位置找到下面的位置找到tnsnames.ora文件右键编辑。选择仅安装数据库软件。

qq_74770105的博客 3595

基于FaceX-Zoo实现的Face Mask Adding人脸戴口罩

人脸系列文章目录 文章目录人脸系列文章目录前言一、加口罩效果二、实现原理二、代码实现 前言 目前人脸加口罩均为在人脸位置贴图式的,非常不真实。前段时间京东开源一个基于PRnet实现的3D口罩拟合人脸,加口罩非常逼真,但是只是单独一个加口罩demo,没有完整的流程。于是本人结合人脸检测,关键点对齐。PRnet加口罩实现一个完整的加口罩流程。只需一个命令简单方便调用,实现人脸加口罩。 https://github.com/JDAI-CV/FaceX-Zoo 一、加口罩效果 下图可以看出效果还是非常逼真的

zengwb的博客 3390

STM32HAL库学习笔记七——I2C通信

HAL库快速部署I2C 本文主要介绍如何使用STM32CubeMX快速部署I2C通信,并与EEPROM进行数据收发。 文章目录HAL库快速部署I2CI2C简介EEPROM简介HAL库部署IIC通信1.引入库2.程序主体 I2C简介 I2C是一种串行同步半双工通信方式。 I2C物理层是由一条双向数据总线SDA和一条双向时间总线SCL组成,I2C总线上可以挂载多个从机设备。 I2C协议层 EEPROM简介 EEPROM 是一种掉电后数据不丢失的存储器,最常用的通讯方式就是 I2C 协议,STM32F103

weixin_44762233的博客 1万+

【模拟电子设计实战】开源!基于Multisim与AD620的心电采集电路仿真与优化

本文详细介绍了基于Multisim与AD620的心电采集电路仿真与优化实战。通过分析心电信号采集的挑战,重点讲解了AD620仪表放大器和OP07运算放大器的选型优势及电路设计技巧,包括对抗50Hz工频干扰和基线漂移的优化方法。文章还分享了Multisim仿真参数设置与实测对比经验,最终实现性能达标的心电采集电路设计。

weixin_30555125的博客 95

STM32之HAL开发——I2C读写EEPROM

STM32 的 I2C 外设可用作通讯的主机及从机,支持 100Kbit/s 和 400Kbit/s 的速率,支持 7 位、 10位设备地址,支持 DMA 数据传输,并具有数据校验功能,I2C 外设还支持 SMBus2.0 协议。

纵行山河万里~ 3647

IGV实战指南:从数据导入到Peak可视化的全流程解析

本文提供了一份IGV实战指南,详细解析了从数据导入到Peak可视化的全流程。内容包括软件安装、参考基因组加载、测序数据处理、信号调整以及如何精准定位和可视化Peak区域,并分享了导出出版级矢量图的高级技巧,帮助用户高效完成基因组数据可视化分析。

weixin_29212867的博客 147

windows 应用爬虫: exe自动操作

之前写了那么多关于手机和网页爬取,今天就写一期关于exe的爬取,exe的爬取其实也是类似的,一个方式是抓包,还有一个模拟人工界面获取元素数据。抓包肯定是最直接,数据也是最全的, 有些数据不一定会显示在界面上,或者必须通过一定交付方式,才能显示到页面(比如移动鼠标到具体元素上)。本文依旧采用python方式,先说说怎么通过界面方式驱动应用点击和数据获取。

youbingchen的博客 888

STM32 CubeMx(六)I2C同步串行通信与EEPROM 24C02的读写

本篇博客讲解了I2C通信,实现了I2C对EEPROM 24C02的读写,并对I2C总线进行了总结。

三木今天学习了嘛の博客 5869

YOLOv11改进策略【卷积层】| DEConv: 强度信息与梯度特征融合的细节增强 适用于小目标与细节目标检测任务

本文记录的是利用 模块优化 的目标检测网络模型。(Detail-Enhanced Convolution,细节增强卷积)的设计旨在,解决 目标检测网络中传统普通卷积缺乏目标细节相关先验、对小目标边缘、目标与背景边界等关键细节捕捉能力不足的问题,同时避免引入额外参数量与计算开销,以适配 对实时推理性能与检测精度的双重需求。DEA-Net: Single image dehazing based on detail-enhanced convolution and content-guided atte

Limiiiing的博客 1496

stm32-HAL库IIC通信-at24c02

若配置的方向传输位为“写数据” 方向,即图22-2的情况,广播完地址,接收到应答 信号后,主机开始正式向从机传输数据(DATA),数据包的大小为8 位。若配置的方向传输位为 “读数据” 方向,即图22-3的情况,广播完地址,接收到应答信 号后,从机开始向主机返回数据,数据包大小也 8 位。在PC总线上,每个设备的地址都是唯一的,当主机广播的地址与某个设备地 址相同时,这个设备就被选中了,没被选中的设备将会忽略之后的数据信号。该位为1时,则相反,即主机由从机读取数据。就因为这个折腾我好久!

Hrilug的博客 3802

【Unity实战篇】| 游戏轮播图效果,多种实现思路及完整教程

在众多游戏的主界面、商城、图鉴或关卡选择界面中,我们常常能看到轮播图的身影。 它以动态、直观的方式展示游戏角色、皮肤、成就或场景,不仅极大地提升了界面的美观度和交互性,还能高效地引导玩家聚焦于核心内容。 本篇文章将从零开始,一步步在Unity中构建一个功能完备、性能优良的游戏轮播图。 使用UGUI系统,通过代码驱动,实现包括自动轮播、手动拖拽、分页指示器、无限循环等核心功能。 无论是UI新手还是希望优化现有方案的开发者,这篇文章都将为你提供清晰的思路和完整的解决方案。

努力前行,总会成为自己心中的那道光 4万+

python如何给声音增加回声效果

创建任何声音对象的回声效果。使用sndarray和numpy来创建原始声音的偏移和衰减副本。目前它只是使用硬编码的值来设置回声的数量和延迟。你可以根据自己的需要轻松地重新创建。这段注释说明了make_echo函数的用途和实现方式,即通过pygame的sndarray模块和numpy库来处理声音数据,生成具有回声效果的声音。函数中使用了固定的回声数量和延迟时间,但这些值可以根据用户的需求进行调整。

zhangzhechun的专栏 976

【亲测免费】 HM STM32F407 HAL库 I2C读写EEPROM AT24C02

本资源仓库提供了一套详细的示例代码,用于指导如何利用STM32F407系列微控制器的HAL库实现I2C接口对AT24C02 EEPROM进行读写操作。AT24C02是一种常用的串行EEPROM存储器,广泛应用于需要小容量非易失性存储的应用场景中。通过本资源,开发者可以快速学习并掌握在STM32平台下使用HAL库高效管理I2C通信协议,进而实现与外部设备的稳定数据交互。 ## 技术要点 - **

gitblog_09719的博客 1250

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

STM32CubeMX的I2C初始化到EEPROM的正常读写

本篇文章来讲解I2C的配置以及EEPROM的调通,还是先来分析我们的原理图,了解要用到哪个引脚 IIC对于EEPROM的读取,这里设计到IIC总线和寻址,具体的原理还有什么的可以去百度或者看手册,我就不说了。这里呢A0 A1 A2表示地址,0x00,这里后面要用到来寻址。 了解了之后呢,准备去选择,配置RCC还有SYS的部分别忘了,我就不写了,前两篇文章都有过了 因为要用到UART来打印结果,所以也不要忘记配置UART串口哦,这里可能会有很多的坑,我这篇文章写的比较详细,可以参考。 STM32CubeM

“逛丢一只鞋”的博客 3102

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

【STM32】HAL库中的实现(八):I2C通信(以 AT24C02 为例)

本文介绍了基于HAL库的STM32硬件I2C驱动AT24C02 EEPROM的实现方法。主要内容包括:1)通过CubeMX配置I2C1外设(PB6/PB7引脚)和时钟树;2)AT24C02硬件连接说明及地址设置;3)HAL库I2C函数的使用方法,重点讲解读写操作的代码实现;4)调试技巧和注意事项。文章强调编程思维的重要性,建议先理解代码逻辑再移植,而非直接复制代码。项目实现了从CubeMX配置到I2C读写EEPROM,再到串口输出验证的完整流程,适用于STM32F1/F4系列芯片。

猫猫的小茶馆 1945

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

探索STM32 I2C通信:高效读写AT24C02 EEPROM

在嵌入式系统开发中,I2C通信协议因其简单、高效的特点而被广泛应用。本项目“HM STM32F407 HAL库 I2C读写EEPROM AT24C02”提供了一套完整的示例代码,帮助开发者快速掌握如何利用STM32F407微控制器的HAL库实现I2C接口对AT24C02 EEPROM的读写操作。AT24C02是一种常见的串行EEPROM存储器,适用于需要小容量非易失性存储的应用场景。通过本项目,开

gitblog_09726的博客 1151

I2C通信的详细讲解:STM32与EEPROM读写操作指南

深入剖析i2c通信的详细讲解,结合STM32实现对EEPROM的稳定读写操作,涵盖时序控制与代码实现要点,适合嵌入式开发者掌握底层通信机制。

weixin_35757191的博客 935
上一篇: HAL库STM32常用外设教程(八)—— SPI (读写W25Q128)
下一篇: HAL库STM32常用外设教程(三)—— USART/UART通信
泰然码事
博客等级 码龄5年 427粉丝 15原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值