LV020-硬件I2C与FSMC的PB7引脚冲突
一、 问题现象
1. 硬件连接
战舰 V3 上的 AT24C02 挂在 PB6/PB7,正是 I2C1 的默认引脚:原理图 第 3 页 EEPROM 区显示 SCL 接 PB6、SDA 接 PB7,A0~A2 与 WP 全部接地。按 LV015-AT24C02使用实例 里的 HAL 硬件 I2C 流程写完驱动后,读 EEPROM 的每一次调用都超时返回 HAL_ERROR。
2. 实测表现
用寄存器级 trace(直接读 I2C1->CR1、SR1、SR2)看到的并不是“从机不应答”,而是更靠前的一步就卡死了:
| 观测点 | 现象 |
|---|---|
置 CR1.START 后轮询 SR1.SB | 永远为 0 |
| 轮询期间读引脚状态 | SDA、SCL 都静止,没有任何翻转 |
| 同一份代码把 I2C1 重映射到 PB8/PB9 | SB 立刻置位,START 正常发出 |
前两行说明主机连起始条件都没发出去,第三行说明外设本身是好的——问题出在 PB7 这根线被别人占住了。
二、 原因分析
1. PB7 的复用冲突
STM32F103ZE 上 PB7 的复用功能不止 I2C1_SDA,复用功能表见 数据手册 第 3 章 Pinouts and pin descriptions:
| 引脚 | 复用功能 | 本板占用情况 |
|---|---|---|
| PB6 | I2C1_SCL / TIM4_CH1 | 只与 TIM4 冲突,本板没用 TIM4 |
| PB7 | I2C1_SDA / TIM4_CH2 / FSMC_NADV | 与 FSMC 的地址锁存信号撞车 |
关键在 FSMC 是“上电即占用”:FSMC_BCR1 的复位值是 0x0000_30DB,其中 MBKEN = 1、MUXEN = 1,即复位后 Bank1 已经使能、且工作在复用(NOR/PSRAM)模式。本板 LCD 与外部 SRAM 都挂在 FSMC 上,时钟必须常开。此时 NADV 作为 FSMC 输出被配成复用推挽,空闲时默认输出高电平,把 SDA 钉死在高电平,I2C1 的开漏下拉根本拉不动它,自然永远等不到 SB。
FSMC 一侧的结构见 LV001-FSMC简介,信号定义见 LV005-存储器映射与控制器,LCD 用 FSMC 模拟 8080 时序的例子见 LV020-FSMC模拟8080时序。
2. 官方勘误的定性
《STM32F10xxCDE 勘误手册》V5 第 2.4.9 节「FSMC 和 I2C1 以及 TIM4_CH2」原文(中译):
当使用 FSMC 功能时,NADV 被配置成复用输出,该信号会被默认地置位为 '1',这样就和 TIM4_CH2 和 I2C1 的 SDA 信号发生冲突。
暂时解决办法:当使用 FSMC 的同时,不要使用 TIM4_CH2 作为输出。如果要使用 I2C1 并且封装允许,就把该功能重映射到 PB8/PB9。
对照本板,官方给的两条出路:
| 官方出路 | 本板可行性 |
|---|---|
| 做 FSMC 时不用 TIM4_CH2 输出 | 与 EEPROM 无关,解决不了问题 |
| I2C1 重映射到 PB8/PB9 | 24C02 的 SCL/SDA 焊死在 PB6/PB7,除非飞线改板 |
一条不相关、一条要动硬件,剩下的只有参考手册里的另一个软件开关。
3. AFIO_MAPR2 逃生通道失效
3.1 寄存器定义
《STM32 英文参考手册》V15 第 9.4.7 节「AF remap and debug I/O configuration register2(AFIO_MAPR2)」,偏移 0x1C:
Bit 10 FSMC_NADV: NADV connect/disconnect This bit is set and cleared by software. It controls the use of the optional FSMC_NADV signal. 0: The NADV signal is connected to the output (default) 1: The NADV signal is not connected. The I/O pin can be used by another peripheral.
按手册,往 AFIO->MAPR2 的 bit10 写 1 就能断开 NADV、把 PB7 还给 I2C1,CubeMX 生成的 __HAL_AFIO_FSMCNADV_DISCONNECTED() 做的正是这件事。
3.2 实测结果
// flash-app/Drivers/BSP/EEPROM(诊断代码,结论确认后已删除)
AFIO->MAPR2 = 0x00000400U; /* bit10 = 1,请求断开 NADV */
/* 读回: 0x00000000 */
AFIO->MAPR2 = 0x000003E0U; /* 非保留位全部写 1,排除掩码问题 */
/* 读回: 0x00000000 */
AFIO->MAPR = 0x00000001U; /* 同一个 AFIO 里的 MAPR,作为对照组 */
/* 读回: 0x00000001 */整个 MAPR2 不响应写操作,而同一个 AFIO 里的 MAPR(偏移 0x04)读写正常,因此可以排除 AFIO 时钟未开、总线错误、地址算错这类原因。
3.3 两个容易踩的坑
(1)中文参考手册里查不到 AFIO_MAPR2。《STM32 中文参考手册》V10 对应的是 2009 年的 RM0008 英文第 10 版,而 MAPR2(含 FSMC_NADV、TIM9~14 重映射)是之后的版本才补进去的,查这些位必须看英文 V15。手册里搜不到不等于寄存器不存在。
(2)手册之间说法不一致时,以硅片实测为准。勘误只说“NADV 默认置 1 会与 SDA 冲突”,给出的解法是重映射,从未承诺 FSMC_NADV 位一定写得进去;实测写不进去,属于文档之外的行为,只能当作本片的既成事实记下来。
三、 方案与实现
1. 方案对比
| 方案 | 前提 | 代价 |
|---|---|---|
| 硬件 I2C1 走 PB8/PB9 | 飞线把 EEPROM 两脚从 PB6/PB7 挪到 PB8/PB9,并置 I2C1_REMAP | 改板,量产不现实 |
| 软件模拟 I2C 走 PB6/PB7 | 无,直接位操作 | 占 CPU,速率只有几十 kHz |
24C02 容量仅 256 字节、单次读写几毫秒,软件模拟完全够用,于是把驱动固定在软件模拟这条路上。
2. 工程实现
驱动用宏把两条实现隔离,公共层只依赖“总线层接口”,两份实现各自用 #if 整体包起来,切换只改一个宏。宏为 0 时硬件版文件整份不参与编译,main() 里的 MX_I2C1_Init() 调用也被同一个宏跳过——I2C1 外设既不开时钟也不占引脚。宏定义与结论记录在 bsp_eeprom.h 的注释块里,软件模拟实现见 bsp_eeprom_sw.c:
// flash-app/Drivers/BSP/EEPROM/bsp_eeprom.h
/* 0 = 软件模拟(PB6/PB7 位操作), 1 = 硬件 I2C1 */
#ifndef BSP_EEPROM_USE_HW_I2C
#define BSP_EEPROM_USE_HW_I2C 0
#endif软件模拟的时序细节见 LV010-I2C时序与软件模拟,器件本身的读写流程见同目录的 LV010-AT24C02简介。
四、 验证与结论
1. 三组对照实验
| 实验 | 操作 | 结果 |
|---|---|---|
| 1 | 保留 FSMC,把 I2C1 重映射到 PB8/PB9 | SB = 1、MSL = 1,START 正常,证明外设无故障 |
| 2 | 关掉 FSMC 时钟(清 RCC->AHBENR 的 FSMCEN) | 立刻恢复正常,START 能发、EEPROM 能读能写 |
| 3 | 保留 FSMC,I2C1 用默认的 PB6/PB7 | SB 永不置位,SDA/SCL 静止 |
实验 1、2 证明 I2C 外设和 EEPROM 都是好的,实验 3 复现问题,变量唯一地锁定在 PB7 的 FSMC 争用上。
2. 运行验证
换回软件模拟后,用串口 CLI 读 0x0C 起的 8 个字节:
> eeprom read 0x0C 8
000C 01 02 03 04 05 06 07 083. 结论与适用边界
- 只要 LCD 或外部 SRAM 走 FSMC、而 EEPROM 又焊在 PB6/PB7,硬件 I2C1 就不可用:NADV 的默认高电平与 SDA 直接冲突,且
FSMC_NADV位在本片写不进去。 - 想回到硬件 I2C,必须同时满足两个前提:EEPROM 改到 PB8/PB9(飞线改板),并置
I2C1_REMAP。 - 用软件模拟期间,PB6/PB7 不能再当普通 IO 使用。
五、 延伸与参考
1. 其它 I2C 勘误
将来若真要用硬件 I2C,同一份勘误手册里还有两条会影响驱动写法:
| 节号 | 内容 | 对驱动的影响 |
|---|---|---|
| 2.9.1 | EV7、EV7_1、EV6_1、EV2、EV8、EV3 必须抢在当前字节传输之前处理 | 逐字节查询死等会出问题,官方建议用 DMA,或把 I2C 中断设为最高优先级 |
| 2.9.2 | 不完全兼容 SMBus v2.0,对非法字节不发 NACK | 需要上层校验机制(SMBALERT、ARA、主机提醒协议) |
2. 参考资料
- 勘误手册:STM32F10xxCDE_Errata_CH_V5.pdf,引用第 2.4.9、2.9.1、2.9.2 节;同系列的
STM32F10xx8B、STM32F10xx46、STM32F107三本对应其它容量档,与本板无关,仅存档备查 - 参考手册(英文):STM32英文参考手册_V15.pdf,引用第 9.4.7 节(
AFIO_MAPR2)、第 9.4.2 节(AFIO_MAPR)、第 19.5.1 节(FSMC 外部存储器接口信号)、第 24 章(I2C 接口) - 参考手册(中文):STM32中文参考手册_V10.pdf,第 19 章 FSMC(该版尚无
AFIO_MAPR2) - 数据手册:STM32F103xCDE_DS_CH_V5.pdf,第 3 章 Pinouts and pin descriptions
- 原理图:WarShip STM32F1_V3.4_SCH.pdf,第 3 页 EEPROM 区
- 驱动源码:bsp_eeprom.h、bsp_eeprom_sw.c、bsp_eeprom_hw.c
本文档由 markdowncli 技能辅助生成