HAL_IIC (EEPROM)

文章目录

一、IIC 简介

1、IIC

IIC(Inter-Integrated Circuit,集成电路总线,常写为I2C),是一种低速、半双工、同步串行通信总线,专为板内短距离设备通信设计,是嵌入式最常用的传感器通信协议之一。

IIC 仅需两根信号线即可实现多设备组网,极大节省单片机引脚资源,广泛用于各类传感器、EEPROM、RTC、OLED、气压、温湿度等外设通信。

2、IIC 核心硬件特点

双线通信:SCL(时钟线)、SDA(数据线)。

半双工通信:同一时间只能收或发,不能同时收发。

同步通信:通信时序完全跟随 SCL 时钟节拍。

多主多从架构:一条总线挂载多个设备,依靠设备地址区分通信对象。

开漏输出:必须外接上拉电阻,总线空闲时保持高电平。

无片选引脚:依靠软件地址寻址,节省硬件引脚。

3、两根总线功能详解

SCL(Serial Clock)时钟线:由主机产生时钟信号,控制通信速率与时序。

SDA(Serial Data)数据线:双向传输数据、地址、应答信号。

4、IIC 通信层级角色

主机(Master):STM32单片机,负责产生时钟、发起通信、控制总线。

从机(Slave):传感器、EEPROM、OLED等外设,被动响应主机指令。

5、典型应用场景

传感器采集:温湿度、气压、陀螺仪、加速度计。

存储设备:AT24C02 EEPROM 数据存储。

显示设备:IIC 协议 OLED 屏幕。

板内低速近距离数据交互。

二、IIC 模块详解

1、IIC功能框图

  

① 物理引脚接口区(橙色)

外部总线输入输出端口,3 根硬件引脚:

  SDA:串行数据,双向传输地址 + 数据 + 应答位。

  SCL:串行时钟,同步总线时序。

  SMBA:SMBus 专用告警引脚,标准 I2C 场景悬空不使用。

  功能:连接外部 I2C 总线,完成电平采样与驱动输出。

② 时钟生成模块(浅紫色)

负责生成标准 100kHz/400kHz SCL 时钟:

  时钟控制寄存器CCR:对 APB1 时钟分频,配置 SCL 通信速率、400kHz 占空比。

  时钟控制逻辑:处理时钟拉伸、总线同步、多主机仲裁时序。

输出 SCL 时序给到数据控制模块,同步 8bit 移位传输。

时钟计算公式:

  

复制代码
		计算实例:
			PCLK1=36MHz,想要配置400Kbit/s的速率,计算方式如下:
				PCLK时钟周期: TPCLK1 = 1/36000000。
				目标SCL时钟周期: TSCL = 1/400000。
				SCL时钟周期内的高电平时间: THIGH = TSCL/3。
				SCL时钟周期内的低电平时间: TLOW = 2*TSCL/3。
				计算CCR的值: CCR = THIGH/TPCLK1 = 30。

③ 数据收发与地址校验模块(绿色,核心数据通路)

完整实现 I2C 字节收发、地址匹配、PEC 硬件校验:

内部子模块 核心功能
数据寄存器 DR 读写缓冲,CPU 读写的唯一数据交互寄存器;写 = 发送,读 = 接收
数据移位寄存器 硬件自动完成 8bit 串行移位,无需 CPU 逐位操作 SDA
比较器 匹配总线地址与本机地址寄存器,匹配成功置 ADDR 标志
自身地址寄存器 OAR1 存储本机 7/10 位从机地址
双地址寄存器 OAR2 第二路从机地址,实现双地址同时响应
PEC 计算单元 + PEC 寄存器 SMBus 硬件 CRC8 校验,存储校验结果
数据控制逻辑 控制 SDA 电平、应答 ACK/NACK、起始 / 停止信号时序

④ 全局控制与状态中断模块(黄色,顶层调度)

I2C 外设总控单元,对接 CPU 内核、中断、DMA:

  控制寄存器CR1+CR2:外设总开关 PE、中断使能、DMA 开关、START/STOP 信号、应答控制。

  状态寄存器SR1+SR2:存储事件标志 (SB/ADDR/TxE/RxNE)、错误标志 (BERR/AF/ARLO)、主机 / 总线状态。

  控制逻辑核心:统筹全部模块时序,产生中断信号、DMA 握手、ACK 控制信号。

2、模块工作原理

时钟来源:APB1 外设时钟,通过 CCR 寄存器分频得到标准 100kHz/400kHz SCL;

电平机制:双线开漏总线,仅能拉低引脚,靠外部上拉电阻拉高;

主机模式流程:主机发送 S 起始信号→发送 7/10 位从地址 + 读写位→等待从机 ACK 应答→收发 N 字节数据→发送 P 停止信号;

从机模式流程:总线检测 S 信号,接收地址与 OAR 寄存器本机地址对比,匹配后产生应答,等待主机读写数据;

仲裁机制:多主机同时发送数据时,若主机输出高电平但总线被其他主机拉低,判定仲裁失败,立即停止传输并置 ARLO 标志;

时钟拉伸:从机处理数据未就绪时拉低 SCL,主机停止发送,直到从机释放 SCL 继续传输;

应答机制:每传输 8bit 数据后,接收方拉低 SDA 产生 ACK,高电平为 NACK;主机读最后一字节时发送 NACK 告知从机传输结束。

3、IIC 通信速率

标准模式:100Kbit/s(默认标配)。

快速模式:400Kbit/s(大部分传感器支持)。

高速模式:3.4Mbit/s(极少使用)。

4、硬件IIC 与 软件IIC 区别

对比项 硬件IIC 软件模拟IIC
实现方式 片上外设硬件时序,无需CPU干预 GPIO模拟高低电平,软件延时时序
占用资源 占用硬件IIC引脚,CPU占用低 任意GPIO,占用CPU资源高
稳定性 F103硬件IIC存在BUG,易卡死 时序灵活、兼容性强、极度稳定
速率 速度快,最高400K 速度慢,延时可控
工程使用 极少使用(F1通病) 项目90%使用软件模拟IIC

三、CubeMX 标准 IIC 配置步骤

1、基础配置

1.Master Features 主机配置板块

CubeMX 配置项 当前取值 映射 HAL 结构体字段 详细说明
I2C Speed Mode Standard Mode(标准模式) I2C_InitTypeDef.ClockSpeed 标准模式速率上限 100kHz,电磁兼容性更强,走线较长、环境干扰偏大的场景适配性好;备选 Fast Mode 可达到 400kHz,适合短走线高速刷屏 OLED
I2C Clock Speed (Hz) 100000 I2C_InitTypeDef.ClockSpeed 设置 SCL 时钟频率为 100kHz;F103 APB1=36MHz 时硬件分频可以稳定输出该速率,是工业 I2C 外设通用保守速率
Duty Cycle Fast Mode I2C_InitTypeDef.DutyCycle 400kHz 时钟高低电平占空比 DutyCycle_2 / DutyCycle_16_9(选 16_9 适配 SSD1306)

2.Slave Features 从机配置板块

**CubeMX **配置项 当前取值 映射 ** HAL **结构体字段 详细说明
Clock No Stretch Mode Disabled I2C_InitTypeDef.NoStretchMode 关闭禁止时钟拉伸,允许从设备通过拉低 SCL 放缓时钟;SSD1306、24C02 处理数据耗时较长,开启时钟拉伸能够大幅降低应答失败的概率,日常不要修改为 Enabled
Primary Address Length selection 7-bit I2C_InitTypeDef.AddressingMode 主本机从地址采用业界通用 7bit 格式;绝大多数 I2C 外设(OLED、EEPROM、各类传感器)全部使用 7 位地址,10bit 拓展地址极少用到
Dual Address Acknowledged Disabled I2C_InitTypeDef.DualAddressMode 禁用第二路从地址 OAR2;只有 MCU 自身需要响应两个不同 I2C 从地址的场景才启用,作为主机操控外设时无需开启
Primary slave address 0 I2C_InitTypeDef.OwnAddress1 MCU 自身充当从机的主地址;主机模式必须填 0,主机不会被外部设备寻址,填写非零值反而会引发总线地址冲突
General Call address detection Disabled I2C_InitTypeDef.GeneralCallMode 关闭广播地址 0x00 检测;广播模式适配多从机批量下发指令的特殊工业场景,常规项目关闭可以减少多余中断触发

2、中断配置

  

1. I2C1 event interrupt(I2C1 事件中断)

触发场景:

  出现正常传输类事件时触发,涵盖起始发送完成、从机地址匹配、发送缓冲区为空、接收缓冲区存有数据、字节传输完成等常规通信流程事件;

使用场景:

  仅采用中断模式(_IT后缀 API)、DMA 模式进行 I2C 收发才需要勾选开启;

  如果日常使用阻塞轮询形式(HAL_I2C_Master_Transmit这类轮询 API),无需启用该项;

2. I2C1 error interrupt(I2C1 错误中断)

触发场景:

  出现总线异常才触发,包含 BERR 总线错误、AF 从机无应答、ARLO 多主机仲裁丢失、OVR 接收溢出等故障情况;

使用场景:

  想要借助中断捕获 I2C 通信异常、做故障自愈逻辑时开启;

  纯轮询开发时可以关闭,代码主动调用HAL_I2C_GetError()即可排查故障;

3、关键配置参数

Clock Speed:100000(100K)通用配置。

Duty Cycle:2:1 标准占空比。

自身从地址:主机模式可默认0x00。

4、配置注意事项

IIC总线必须外接4.7K~10K上拉电阻,否则通信异常。

STM32F103硬件IIC存在硬件缺陷,大批量项目建议用软件IIC。

多条IIC设备挂载时,必须保证设备地址不冲突。

通信速率不宜过高,否则容易丢包、无应答。

四、IIC编程模块

1、IIC数据结构

1.IIC句柄结构体

复制代码
		typedef struct __I2C_HandleTypeDef
		{
		  I2C_TypeDef               *Instance;        // 外设寄存器基地址
		  I2C_InitTypeDef           Init;             // I2C时序、模式配置参数
		  HAL_LockTypeDef           Lock;             // 多线程互斥锁,防止并发访问冲突
		  __IO HAL_I2C_StateTypeDef State;           // 外设实时运行状态机
		  __IO uint32_t             ErrorCode;        // 硬件故障错误标志寄存器
		  DMA_HandleTypeDef         *hdmatx;          // 发送DMA句柄(DMA收发时绑定)
		  DMA_HandleTypeDef         *hdmarx;          // 接收DMA句柄(DMA收发时绑定)
		  HAL_I2C_CallbackTypeDef   TxCpltCallback;  // 发送完成用户回调函数指针
		  HAL_I2C_CallbackTypeDef   RxCpltCallback;  // 接收完成用户回调函数指针
		  HAL_I2C_CallbackTypeDef   ErrorCallback;    // 传输错误用户回调函数指针
		  HAL_I2C_CallbackTypeDef   AbortCallback;   // 传输中止用户回调函数指针
		} I2C_HandleTypeDef;

2.IIC初始化结构体

复制代码
		typedef struct
		{
		  uint32_t ClockSpeed;          // I2C SCL总线时钟速率
		  uint32_t DutyCycle;          // 400K快速模式时钟占空比
		  uint32_t OwnAddress1;        // 本机从机7/10位地址
		  uint32_t AddressingMode;     // 本机从地址位宽模式
		  uint32_t Mode;               // 通信协议:标准I2C / SMBus
		  uint32_t NoStretchMode;      // 时钟拉伸使能开关
		  uint32_t OwnAddress2;        // 第二路本机从地址(双地址从机模式)
		  uint32_t DualAddressMode;    // 双从地址匹配开关
		} I2C_InitTypeDef;

2、HAL_IIC_API

1.阻塞轮询模式 Blocking mode: Polling(线程同步,占用CPU直到传输完成)

复制代码
		HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
		功能:I2C主机阻塞发送批量数据,自动生成起始信号+从机写地址+数据+停止信号;Timeout超时未完成返回HAL_TIMEOUT,OLED发指令/数据底层基础接口。
		
		HAL_StatusTypeDef HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
		功能:I2C主机阻塞读取从机数据,自动处理ACK/NACK应答时序,适合读取传感器寄存器。
		
		HAL_StatusTypeDef HAL_I2C_Slave_Transmit(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t Timeout);
		功能:I2C从机模式阻塞发送数据,MCU作为从设备时使用,OLED/24C02主机工程几乎不用。
		
		HAL_StatusTypeDef HAL_I2C_Slave_Receive(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t Timeout);
		功能:I2C从机模式阻塞接收主机下发数据,仅多MCU互联从机场景使用。
		
		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);
		功能:主机带片内寄存器地址阻塞写入,自动时序:起始→设备写地址→内部存储地址→用户数据→停止;适配24C02、带寄存器的OLED、温感芯片。
		
		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);
		功能:主机带片内寄存器地址阻塞读取,自动生成重复起始信号切换读写方向,是EEPROM读取专用接口。
		
		HAL_StatusTypeDef HAL_I2C_IsDeviceReady(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint32_t Trials, uint32_t Timeout);
		功能:循环发送从机地址检测设备ACK应答,判断I2C外设是否在线;硬件调试排查无应答故障专用。

2.非阻塞中断模式 Non-Blocking mode: Interrupt(函数立即返回,传输靠中断完成)

基础收发

复制代码
			HAL_StatusTypeDef HAL_I2C_Master_Transmit_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
			功能:主机中断异步发送,不阻塞主循环,全部数据发送完毕触发HAL_I2C_MasterTxCpltCallback回调。
			
			HAL_StatusTypeDef HAL_I2C_Master_Receive_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
			功能:主机中断异步接收,收到指定长度数据后进入HAL_I2C_MasterRxCpltCallback。
			
			HAL_StatusTypeDef HAL_I2C_Slave_Transmit_IT(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size);
			功能:从机中断异步发送,仅多MCU主从互联场景使用。
			
			HAL_StatusTypeDef HAL_I2C_Slave_Receive_IT(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size);
			功能:从机中断异步接收主机数据。
			
			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_I2C_MemTxCpltCallback。
			
			HAL_StatusTypeDef HAL_I2C_Mem_Read_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size);
			功能:中断模式带内部地址读取寄存器型外设,完成触发HAL_I2C_MemRxCpltCallback。

序列分段传输(Seq,自定义起始/停止时序,多帧复合通信)

复制代码
			HAL_StatusTypeDef HAL_I2C_Master_Seq_Transmit_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
			功能:中断序列发送,XferOptions自定义传输规则(传输后是否发停止、重复起始),适配复杂多段I2C时序。
			
			HAL_StatusTypeDef HAL_I2C_Master_Seq_Receive_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
			功能:中断序列接收,可控制传输结束是否发送NACK/停止信号。
			
			HAL_StatusTypeDef HAL_I2C_Slave_Seq_Transmit_IT(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
			功能:从机中断序列发送。
			
			HAL_StatusTypeDef HAL_I2C_Slave_Seq_Receive_IT(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint16_t XferOptions);
			功能:从机中断序列接收。

从机监听、传输中止

复制代码
			HAL_StatusTypeDef HAL_I2C_EnableListen_IT(I2C_HandleTypeDef *hi2c);
			功能:开启从机地址监听中断,等待主机访问本机从地址,匹配地址触发AddrCallback。
			
			HAL_StatusTypeDef HAL_I2C_DisableListen_IT(I2C_HandleTypeDef *hi2c);
			功能:关闭从机地址监听中断,不再响应主机寻址。
			
			HAL_StatusTypeDef HAL_I2C_Master_Abort_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress);
			功能:中止当前正在进行的中断异步传输,强制释放总线,传输中止完成触发AbortCpltCallback。

3.非阻塞DMA模式 Non-Blocking mode: DMA(零CPU搬运,高速大批量传输)

基础DMA收发

复制代码
			HAL_StatusTypeDef HAL_I2C_Master_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
			功能:主机DMA异步发送,数据由DMA硬件直接搬运至I2C_DR,CPU全程不参与;OLED全屏刷屏最优接口。
			
			HAL_StatusTypeDef HAL_I2C_Master_Receive_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
			功能:主机DMA异步接收,硬件自动搬运总线数据至内存数组。
			
			HAL_StatusTypeDef HAL_I2C_Slave_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size);
			功能:从机DMA发送。
			
			HAL_StatusTypeDef HAL_I2C_Slave_Receive_DMA(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size);
			功能:从机DMA接收。
			
			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);
			功能:DMA模式带内部地址写入寄存器外设,大批量EEPROM页写入专用。
			
			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模式带内部地址读取寄存器外设。

DMA序列分段传输

复制代码
			HAL_StatusTypeDef HAL_I2C_Master_Seq_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
			功能:DMA序列发送,自定义停止/重复起始时序。
			
			HAL_StatusTypeDef HAL_I2C_Master_Seq_Receive_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
			功能:DMA序列接收,自定义应答、停止时序。
			
			HAL_StatusTypeDef HAL_I2C_Slave_Seq_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
			功能:从机DMA序列发送。
			
			HAL_StatusTypeDef HAL_I2C_Slave_Seq_Receive_DMA(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint16_t XferOptions);
			功能:从机DMA序列接收。

4.中断服务入口函数(IT.c中全局中断调用)

复制代码
		void HAL_I2C_EV_IRQHandler(I2C_HandleTypeDef *hi2c);
		功能:I2C事件中断总入口,处理发送空、接收满、地址匹配、传输完成等事件,自动分发对应完成回调。
		
		void HAL_I2C_ER_IRQHandler(I2C_HandleTypeDef *hi2c);
		功能:I2C错误中断总入口,应答失败、总线错误、仲裁丢失时进入,分发错误回调。

5.用户重写回调函数(中断/DMA传输完成/故障触发,用户自定义业务)

主机收发完成回调

复制代码
			void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c);
			功能:主机中断/DMA普通发送全部数据完成回调。
			
			void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c);
			功能:主机中断/DMA普通接收指定长度数据完成回调。

从机收发完成回调

复制代码
			void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *hi2c);
			功能:从机发送数据完成回调。
			
			void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c);
			功能:从机接收数据完成回调。

从机地址匹配、监听完成回调

复制代码
			void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode);
			功能:从机模式下主机寻址本机地址时触发,参数区分读/写方向、匹配的本机地址。
			
			void HAL_I2C_ListenCpltCallback(I2C_HandleTypeDef *hi2c);
			功能:从机完整一帧主机读写传输结束回调。

Mem寄存器读写专用完成回调

复制代码
			void HAL_I2C_MemTxCpltCallback(I2C_HandleTypeDef *hi2c);
			功能:Mem_Write_IT/DMA带地址写入全部数据完成回调。
			
			void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef *hi2c);
			功能:Mem_Read_IT/DMA带地址读取全部数据完成回调。

故障、传输中止回调

复制代码
			void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c);
			功能:I2C硬件错误触发(无应答AF、总线错误BERR、仲裁丢失ARLO、超时),可读取HAL_I2C_GetError()判断故障类型。
			
			void HAL_I2C_AbortCpltCallback(I2C_HandleTypeDef *hi2c);
			功能:调用HAL_I2C_Master_Abort_IT()中止传输完成后触发。

6.状态/模式/错误查询API

复制代码
		HAL_I2C_StateTypeDef HAL_I2C_GetState(I2C_HandleTypeDef *hi2c);
		功能:读取I2C外设当前运行状态(就绪/发送忙/接收忙/错误/超时),传输前判断总线是否空闲。
		
		HAL_I2C_ModeTypeDef HAL_I2C_GetMode(I2C_HandleTypeDef *hi2c);
		功能:查询当前I2C工作模式:主机模式/从机模式。
		
		uint32_t HAL_I2C_GetError(I2C_HandleTypeDef *hi2c);
		功能:读取句柄内硬件错误标志位,定位无应答、总线冲突、DMA故障等问题。

五、IIC 应用实例

1、硬件原理图EEPROM

  

实现EEPROM指定地址写入、读取,掉电保存数据.

2、EEPROM设备地址

EEPROM芯片的设备地址一共有7位,其中高4位固定为:1010 b, 低3位则由A0/A1/A2信号线的电平决定。

图 EEPROM设备地址 ,图中的R/W是读写方向位,与地址无关。

  

A0/A1/A2均为0,EEPROM的7位设备地址是:101 0000b,即0x50。

I2C通讯地址跟读写方向连在一起构成一个8位数:

  当R/W位为0时,表示写方向, 加上7位地址,其值为"0xA0",常称该值为"写地址";

  当R/W位为1时,表示读方向, 加上7位地址,其值为"0xA1",常称该值为"读地址"。

3、CubeMX配置

  

1. RCC 时钟配置

选择 HSE 外部高速晶振,系统主频配置 72MHz,APB1 预分频系数设置为 2,APB1 总线时钟最终得到 36MHz,适配 I2C 外设时钟需求。

2. I2C1 Parameter Settings 参数配置

配置项 设置值
I2C Speed Mode Standard Mode
I2C Clock Speed 100000 Hz
Clock No Stretch Mode Disabled
Primary Address Length selection 7-bit
Dual Address Acknowledged Disabled
Primary slave address 0
General Call address detection Disabled

3. NVIC 中断配置

普通阻塞读写方案:两个中断复选框全部保持未勾选状态;

若后续打算切换中断 / DMA 模式,再勾选开启event interrupt与error interrupt。

4. DMA Settings

阻塞模式保持 DMA 关闭;大批量页写入场景再配置 DMA1 通道。

5. GPIO Settings

PB6、PB7 自动复用为 I2C 功能,电气模式固定为复用开漏(Alternate Open Drain);硬件实物 SDA、SCL 需要外接 4.7kΩ 上拉电阻到 3.3V。

6. 工程生成设置

生成代码选择 HAL 库,勾选生成独立.c/.h 文件。

4、工程源码

1.eeprom.h 头文件

复制代码
		#ifndef __EEPROM_H
		#define __EEPROM_H
		
		#include "main.h"
		#include "i2c.h"
		
		/* 24C02基础宏定义 */
		#define EEPROM_DEV_ADDR        0xA0U
		#define EEPROM_PAGE_SIZE       8U
		#define EEPROM_WRITE_DELAY_MS  5U
		#define EEPROM_CAPACITY        256U
		
		HAL_StatusTypeDef EEPROM_ByteWrite(uint8_t memAddr, uint8_t data);
		HAL_StatusTypeDef EEPROM_PageWrite(uint8_t startAddr, uint8_t *pData, uint8_t len);
		HAL_StatusTypeDef EEPROM_ByteRead(uint8_t memAddr, uint8_t *pData);
		HAL_StatusTypeDef EEPROM_BufferRead(uint8_t startAddr, uint8_t *pData, uint8_t len);
		HAL_StatusTypeDef EEPROM_CheckDevice(void);
		
		#endif

2. eeprom.c 功能源码

复制代码
		#include "eeprom.h"
		extern I2C_HandleTypeDef hi2c1;
		
		/**
		 * @brief 检测EEPROM设备是否在线
		 */
		HAL_StatusTypeDef EEPROM_CheckDevice(void)
		{
		    return HAL_I2C_IsDeviceReady(&hi2c1, EEPROM_DEV_ADDR, 5U, 100U);
		}
		
		/**
		 * @brief 单字节写入EEPROM
		 * @param memAddr 片内存储地址 0~255
		 * @param data 需要写入的单字节数据
		 */
		HAL_StatusTypeDef EEPROM_ByteWrite(uint8_t memAddr, uint8_t data)
		{
		    HAL_StatusTypeDef ret;
		    ret = HAL_I2C_Mem_Write(&hi2c1, EEPROM_DEV_ADDR, memAddr, I2C_MEMADD_SIZE_8BIT, &data, 1U, 100U);
		    HAL_Delay(EEPROM_WRITE_DELAY_MS);
		    return ret;
		}
		
		/**
		 * @brief 分页批量写入,规避跨页覆盖数据的问题
		 */
		HAL_StatusTypeDef EEPROM_PageWrite(uint8_t startAddr, uint8_t *pData, uint8_t len)
		{
		    HAL_StatusTypeDef status = HAL_OK;
		    uint8_t writeLen;
		    while(len > 0U)
		    {
		        /* 计算当前页面剩余可写入字节 */
		        writeLen = EEPROM_PAGE_SIZE - (startAddr % EEPROM_PAGE_SIZE);
		        writeLen = writeLen > len ? len : writeLen;
		
		        status = HAL_I2C_Mem_Write(&hi2c1, EEPROM_DEV_ADDR, startAddr, I2C_MEMADD_SIZE_8BIT, pData, writeLen, 100U);
		        if(status != HAL_OK)
		            break;
		        HAL_Delay(EEPROM_WRITE_DELAY_MS);
		
		        startAddr += writeLen;
		        pData += writeLen;
		        len -= writeLen;
		    }
		    return status;
		}
		
		/**
		 * @brief 读取单个字节
		 */
		HAL_StatusTypeDef EEPROM_ByteRead(uint8_t memAddr, uint8_t *pData)
		{
		    return HAL_I2C_Mem_Read(&hi2c1, EEPROM_DEV_ADDR, memAddr, I2C_MEMADD_SIZE_8BIT, pData, 1U, 100U);
		}
		
		/**
		 * @brief 任意长度连续读取数据,读取阶段不需要延时等待
		 */
		HAL_StatusTypeDef EEPROM_BufferRead(uint8_t startAddr, uint8_t *pData, uint8_t len)
		{
		    return HAL_I2C_Mem_Read(&hi2c1, EEPROM_DEV_ADDR, startAddr, I2C_MEMADD_SIZE_8BIT, pData, len, 200U);
		}

3.main.c 测试调用示例

复制代码
		#include "main.h"
		#include "i2c.h"
		#include "gpio.h"
		#include "eeprom.h"
		#include <stdio.h>
		#include <string.h>
		
		/* 重定向printf */
		#ifdef __GNUC__
		#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
		#else
		#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
		#endif
		PUTCHAR_PROTOTYPE
		{
			HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
			return ch;
		}
		
		
		uint8_t writeBuf[6] = {0x10,0x20,0x30,0x40,0x50,0x60};
		uint8_t readBuf[6];
		
		int main(void)
		{
		  HAL_Init();
		  SystemClock_Config();
		  MX_GPIO_Init();
		  MX_I2C1_Init();
		 
		  // 1、检测EEPROM硬件是否在线
		  printf("===== AT24C02 EEPROM TEST =====\r\n");
		  if(EEPROM_CheckDevice() == HAL_OK)
		  {
		      printf("OK\r\n");
		      // 2、分页批量写入EEPROM 0x00起始地址
		      EEPROM_PageWrite(0x00, writeBuf, 6);
		      HAL_Delay(10);
		      printf("DATA:");
		      for(uint8_t i=0; i<6; i++)
		      {
		          printf("0x%02X ", writeBuf[i]);
		      }
		      printf("\r\n");
		
		      // 3、从EEPROM读取数据
		      EEPROM_BufferRead(0x00, readBuf, 6);
		      printf("DATA:");
		      for(uint8_t i=0; i<6; i++)
		      {
		          printf("0x%02X ", readBuf[i]);
		      }
		      printf("\r\n");
		
		      // 4、数据一致性校验
		      if(memcmp(writeBuf,readBuf, 6) == 0)
		      {
		          printf("OK\r\n");
		      }
		      else
		      {
		          printf("ERR\r\n");
		      }
		  }
		  else
		  {
		      printf("EEPROM ERROR!\r\n");
		  }
		
		//16 字节一行批量打印全部 24C02 存储空间
		/*
		uint8_t fullBuf[16];
		for(uint8_t page=0; page<16; page++)
		{
		    EEPROM_BufferRead(page*16, fullBuf, 16);
		    printf("地址0x%02X ~ 0x%02X:", page*16, page*16+15);
		    for(uint8_t i=0; i<16; i++)
		    {
		        printf("0x%02X ", fullBuf[i]);
		    }
		    printf("\r\n");
		}*/
		
		
		  while (1)
		  {
			// 循环间隔打印状态   
			HAL_Delay(2000);
			printf("Run......\r\n");
		  }
		}

5、实验结果

  

六、IIC 核心要点与避坑大全

1、核心知识点

IIC为双线同步半双工通信,SCL时钟、SDA数据。

开漏输出必须外接上拉电阻,总线空闲高电平。

依靠设备地址寻址,一条总线可挂载多个设备。

通信核心时序:起始、停止、应答、高位先行。

F103硬件IIC有BUG,工程优先使用软件模拟IIC。

2、高频坑点(新手必错)

未焊接上拉电阻,导致总线电平异常、完全无法通信。

设备地址读写位混淆,写地址、读地址错位导致无应答。

硬件IIC卡死、总线忙死锁,无法恢复通信。

超时时间设置过短,高速通信频繁报错。

多设备挂载地址冲突,数据接收错乱。

读写寄存器后未加延时,外设未完成操作导致读取错误。

3、工程最优解决方案

STM32F1系列放弃硬件IIC,全程使用软件IIC。

统一封装寄存器读写函数,适配所有IIC传感器。

通信失败增加重试机制,提升稳定性。

每次读写操作增加适当延时,适配低速外设。

设备上电后先检测设备在线状态,再执行通信。

4、总结

IIC是双线同步半双工串行总线,依靠SCL时钟、SDA数据实现多设备组网通信,通过设备地址寻址,具备起始、停止、应答标准时序,资源占用少、扩展性强,是嵌入式传感器、存储设备最主流的低速通信协议。

七、全篇总结

IIC 是嵌入式开发使用频率最高的通信协议,几乎所有传感器均基于IIC协议开发。

掌握IIC时序原理、CubeMX配置、寄存器读写API、工程避坑方案、软件IIC替代思路,即可完成99%的传感器、OLED、EEPROM项目开发,是嵌入式进阶的核心必备技能。

相关推荐
国科安芯9 小时前
ASC4T245S分组双向控制架构深度解析:独立DIR/OE控制、QFN16封装与混合方向总线桥接
单片机·嵌入式硬件·物联网·fpga开发·架构·risc-v
JNX_SEMI11 小时前
AT2401C 2.4GHz 全集成射频前端单芯片技术解析
前端·单片机·嵌入式硬件·物联网·硬件工程
电子工程师成长日记-C5113 小时前
51单片机智能灯光控制系统
单片机·嵌入式硬件·51单片机
狂奔蜗牛(bradley)14 小时前
嵌入式软件编程思想之事件驱动+表驱动状态机+事件参数+优先级FIFO
单片机·mcu
secondyoung14 小时前
Cortex-R52学习:存储系统
arm开发·单片机·学习·arm
开发笔记-阿牛16 小时前
CK6159A 语音主控 USB 恒温热敷控制器硬件设计(原理图 + PCB + 温控安全方案)
单片机·嵌入式硬件
sramdram18 小时前
低功耗串口通信蓝牙模块应用原理
单片机·嵌入式硬件·蓝牙模块·通信蓝牙模块·串口蓝牙模块
__Rhaast丶19 小时前
set_data_check用法解析(一) lib库中的data check解析
单片机·嵌入式硬件
wuyk55520 小时前
21. 嵌入式面试避坑指南:sizeof 是关键字,不是函数!
c语言·开发语言·stm32·单片机·嵌入式硬件