在 STM32L452 作为 I2C 从机的开发场景中,常出现 “与部分主机(如 FPGA)通信异常,与标准设备(如交换机)通信正常” 的问题。核心原因是 I2C 时钟延展功能的兼容性差异 —— 部分主机不支持该功能,导致 SCL 时序采样错误。本文基于 ST 官方 LAT1323 应用笔记,从问题根源、时钟延展关闭配置、OVR 错误解决到实操要点,系统拆解解决方案,助力快速实现稳定通信。
1. 核心问题:时钟延展功能的兼容性冲突
1.1 问题场景与现象
- 硬件配置:STM32L452 作为 I2C 从机,连接两种主机 —— 交换机(正常通信)、FPGA 测试机(通信异常);
- 关键现象:FPGA 作为主机时,I2C 波形的第 9 个 SCL 时钟脉宽变窄,且低电平时长变长(周期不变),导致主机固定频率采样 SDA 数据时出错;
- 初步排查:调整 I2C 的 Data setup time 和 Data hold time,仅能改变整体脉宽,无法解决通信异常。
1.2 根源分析:时钟延展功能的适配差异
- 时钟延展功能本质:I2C 从机可通过拉低 SCL 信号,请求主机延长时钟周期,为从机处理数据争取时间(默认开启);
- 冲突核心:FPGA 主机不支持时钟延展功能,未检测 SCL 被拉低的实际电平,仍按固定频率采样,导致数据读取错误;
- 解决方向:主机端无法修改,需让 STM32L452 从机关闭时钟延展功能,确保 SCL 时序不被主动拉低。
2. 第一步:关闭时钟延展功能的配置方法
关闭时钟延展功能仅需在 I2C 初始化阶段启用 “No Stretch Mode”,操作简洁,步骤如下:
2.1 核心配置逻辑
通过 I2C_CR1 寄存器的 NOSTRETCH 位控制,设置为 1 即可关闭时钟延展(默认 0 为开启)。
2.2 代码配置示例(STM32Cube 库)
// I2C初始化结构体配置
I2C_HandleTypeDef hi2c1;
void MX_I2C1_Init(void) {
hi2c1.Instance = I2C1;
hi2c1.Init.Timing = 0x00707CBB; // 按实际需求配置时序
hi2c1.Init.OwnAddress1 = 0x08; // 从机地址
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_ENABLE; // 关闭时钟延展功能
hi2c1.Init.DigitalFilter = 0;
hi2c1.Init.AnalogFilter = I2C_ANALOGFILTER_ENABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK) {
Error_Handler();
}
}
2.3 配置验证
- 时序变化:SCL 信号不再被从机主动拉低,脉宽稳定,与主机时序匹配;
- 注意事项:配置后需重新测试通信,若出现多发送 0xFF 的现象,需进一步解决 OVR 错误。
3. 关键问题:关闭时钟延展后的 OVR 错误与解决
关闭时钟延展(NOSTRETCH=1)后,部分场景会出现 “多发送 0xFF” 的异常,本质是Overrun/Underrun(OVR)错误,需针对性解决。
3.1 OVR 错误成因
根据 STM32L452 参考手册,NOSTRETCH=1 时,从机无时钟延展缓冲时间,以下情况会触发 OVR 错误:
- 接收阶段:新字节接收时,RXDR 寄存器未被读取,新数据丢失,自动返回 NACK;
- 发送阶段:
- STOPF 标志未清除时启动首次数据发送,若 TXDR 寄存器为空则发送 0xFF;
- 新字节需发送时,TXDR 寄存器未提前写入数据,触发欠载错误,发送 0xFF。
核心矛盾:从机无法在发送阶段第一个 SCL 脉冲前,提前获知需发送的数据,导致 TXDR 寄存器为空。
3.2 解决方案:Combined 模式提前填充数据
Combined 模式(I2C 组合格式)的核心是 “主机先写命令→从机提前准备数据”,流程如下:
(1)Combined 模式原理
- 主机通信流程:先发送 “从机地址 + 写命令”(传递数据请求)→ 发送 Repeat Start→ 再发送 “从机地址 + 读命令”→ 接收从机数据;
- 从机优势:在主机发送 Repeat Start 前(写命令阶段),即可根据接收的命令内容,提前填充 TXDR 寄存器的第一个发送数据,避免发送阶段 TXDR 为空。
(2)实操流程示例(3 字节数据发送)
| 阶段 | 主机操作 | 从机操作 |
|---|---|---|
| 1 | 发送从机地址 + 写命令(传递数据请求) | 接收命令,填充第一个发送数据到 TXDR |
| 2 | 发送 Repeat Start | - |
| 3 | 发送从机地址 + 读命令 | 检测到读请求,启动数据发送 |
| 4 | 接收第一个数据(TXDR 中预存) | 触发 TXIS 中断,填充第二个数据 |
| 5 | 接收第二个数据 | 触发 TXIS 中断,填充第三个数据 |
| 6 | 接收第三个数据,发送 NACK+STOP | 清除 STOPF 标志,准备下一次通信 |
(3)关键代码要点
- 使能 I2C 中断(TXIS、STOPF),在中断中处理数据填充;
- 写命令阶段接收主机指令后,立即填充首个发送数据;
- 发送阶段通过 TXIS 中断依次填充后续数据,确保 TXDR 非空。
4. 核心总结:关闭时钟延展的完整落地流程
- 配置关闭时钟延展:I2C 初始化时设置
NoStretchMode = I2C_NOSTRETCH_ENABLE; - 启用 Combined 模式:主机采用 “写命令 + 读数据” 的组合格式,从机提前准备首个数据;
- 中断处理优化:使能 TXIS、STOPF 中断,确保发送阶段 TXDR 寄存器持续有有效数据;
- 验证时序:通过示波器确认 SCL 脉宽稳定,无异常拉低,数据收发无 0xFF 冗余字节。
5. 避坑关键要点
- 时钟延展关闭后,从机无缓冲时间,必须提前准备数据,否则必触发 OVR 错误;
- Combined 模式是解决 OVR 错误的核心,不可直接关闭时钟延展后不调整通信格式;
- 若主机不支持 Combined 模式,需协调主机端适配,或更换支持时钟延展的主机设备;
- 中断优先级需合理配置,避免数据填充不及时导致的错误。
阅读全文
542