1. STM32F103 I2C模块深度解析与HAL库实战指南
作为一名嵌入式开发工程师,我经常需要在STM32项目中使用I2C总线与各种传感器和外围设备通信。在最近的一个智能家居项目中,我深入研究了STM32F103的I2C模块,并整理了这份详细的笔记。本文将分享我在使用HAL库进行I2C开发时遇到的典型问题及其解决方案。
1.1 I2C基础概念回顾
I2C(Inter-Integrated Circuit)是一种同步、多主从、半双工的串行通信总线,由Philips(现NXP)在1980年代开发。它只需要两根线(SDA-数据线和SCL-时钟线)就能实现设备间的通信,非常适合嵌入式系统中的短距离低速通信。
STM32F103的I2C模块支持:
- 标准模式(100kHz)
- 快速模式(400kHz)
- 多主机通信
- 7位和10位地址模式
- 时钟延展功能
2. ACK/NACK机制详解
2.1 ACK/NACK的基本原理
在I2C协议中,每个字节传输后都会跟随一个应答位(ACK)或非应答位(NACK)。这是I2C确保数据可靠传输的重要机制。
ACK表示接收方已成功接收数据并准备好接收下一个字节,NACK则表示接收方无法接收更多数据或通信需要终止。
2.2 HAL库中的ACK控制
在STM32 HAL库中,ACK/NACK主要通过控制寄存器1的ACK位(I2C_CR1.ACK)来管理:
c复制typedef struct {
// 其他配置项...
uint32_t AckMode; /*!< 应答模式选择 */
} I2C_InitTypeDef;
具体控制场景:
-
主机接收最后一个字节前:
- 必须先将ACK位清零,指示硬件在接收下一个字节后回复NACK
- 示例代码:
c复制__HAL_I2C_DISABLE_ACK(&hi2c1); // 禁用ACK
-
主动终止接收:
- 任何时候想强制终止接收,可立即清零ACK位
- 硬件将在接收完当前字节后回复NACK
2.3 硬件自动ACK场景
STM32硬件在以下情况会自动处理ACK/NACK,无需软件干预:
-
作为从机地址匹配时:
- 硬件检测到地址匹配后自动拉低SDA线发出ACK
-
作为接收方成功接收数据后:
- 硬件默认自动回复ACK,表示"请继续发送"
-
启用PEC且校验失败时:
- 硬件自动回复NACK,保证数据完整性
注意:在从机模式下,硬件会自动处理地址匹配的ACK,但数据接收后的ACK可以通过软件配置。
3. 主发送模式:普通模式与存储器模式
3.1 普通主发送模式
普通主发送模式适用于大多数标准I2C设备通信:
c复制HAL_I2C_Master_Transmit(&hi2c1, DevAddress, pData, Size, Timeout);
通信流程:
code复制START → 发送从设备地址+写位 → 等待ACK → 发送数据字节 → 等待ACK → ... → STOP
特点:
- 支持7位或10位地址
- 适用于传感器等标准外设
- 流程简单直接
3.2 存储器模式发送
存储器模式专为EEPROM类设备设计:
c复制HAL_I2C_Mem_Write(&hi2c1, DevAddress, MemAddress, MemAddSize, pData, Size, Timeout);
通信流程:
code复制START → 发送设备地址+写位 → 等待ACK → 发送存储器内部地址(高字节) → 等待ACK →
发送存储器内部地址(低字节) → 等待ACK → 发送数据字节 → 等待ACK → ... → STOP
特点:
- 使用双地址模式(设备地址+存储器内部地址)
- 支持页写入操作
- 专为24Cxx系列EEPROM优化
3.3 两种模式的选择建议
-
普通模式适用场景:
- 与传感器通信(如温度、湿度传感器)
- 简单的寄存器读写操作
- 不需要内部地址寻址的设备
-
存储器模式适用场景:
- EEPROM读写
- 需要先指定内部地址再读写数据的设备
- 需要页写入操作的情况
实际项目中,我建议优先使用HAL库提供的Mem_Read/Mem_Write函数,即使对于非存储器设备,因为它们提供了更统一的接口。
4. 广播呼叫模式解析
4.1 广播呼叫的基本概念
广播呼叫(General Call)是I2C的一种特殊功能,允许主机同时向总线上所有从机发送数据或命令,而无需指定具体地址。
关键特性:
- 特殊保留地址:0x00
