文章目录
- [一、IIC 简介](#一、IIC 简介)
-
- 1、IIC
- [2、IIC 核心硬件特点](#2、IIC 核心硬件特点)
- 3、两根总线功能详解
- [4、IIC 通信层级角色](#4、IIC 通信层级角色)
- 5、典型应用场景
- [二、IIC 模块详解](#二、IIC 模块详解)
- [三、CubeMX 标准 IIC 配置步骤](#三、CubeMX 标准 IIC 配置步骤)
- 四、IIC编程模块
-
- 1、IIC数据结构
- 2、HAL_IIC_API
-
- [1.阻塞轮询模式 Blocking mode: Polling(线程同步,占用CPU直到传输完成)](#1.阻塞轮询模式 Blocking mode: Polling(线程同步,占用CPU直到传输完成))
- [2.非阻塞中断模式 Non-Blocking mode: Interrupt(函数立即返回,传输靠中断完成)](#2.非阻塞中断模式 Non-Blocking mode: Interrupt(函数立即返回,传输靠中断完成))
- [3.非阻塞DMA模式 Non-Blocking mode: DMA(零CPU搬运,高速大批量传输)](#3.非阻塞DMA模式 Non-Blocking mode: DMA(零CPU搬运,高速大批量传输))
- 4.中断服务入口函数(IT.c中全局中断调用)
- 5.用户重写回调函数(中断/DMA传输完成/故障触发,用户自定义业务)
- 6.状态/模式/错误查询API
- [五、IIC 应用实例](#五、IIC 应用实例)
-
- 1、硬件原理图EEPROM
- 2、EEPROM设备地址
- 3、CubeMX配置
-
- [1. RCC 时钟配置](#1. RCC 时钟配置)
- [2. I2C1 Parameter Settings 参数配置](#2. I2C1 Parameter Settings 参数配置)
- [3. NVIC 中断配置](#3. NVIC 中断配置)
- [4. DMA Settings](#4. DMA Settings)
- [5. GPIO Settings](#5. GPIO Settings)
- [6. 工程生成设置](#6. 工程生成设置)
- 4、工程源码
-
- [1.eeprom.h 头文件](#1.eeprom.h 头文件)
- [2. eeprom.c 功能源码](#2. eeprom.c 功能源码)
- [3.main.c 测试调用示例](#3.main.c 测试调用示例)
- 5、实验结果
- [六、IIC 核心要点与避坑大全](#六、IIC 核心要点与避坑大全)
- 七、全篇总结
一、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项目开发,是嵌入式进阶的核心必备技能。