Linux I2C 调试实录:用寄存器打印揪出 TRISE 配置过小

Linux I2C 调试实录:用寄存器打印揪出 TRISE 配置过小

平台:STM32 + Linux(i2c-stm32 / 设备树节点)

现象:偶发 EIO、从机无 ACK、示波器上 SCL 上升沿"拖尾"

结论:CCR 合理,但 TRISE 过小 → SCL 未拉高到有效高电平就读数据


1. 背景:为什么先打寄存器而不是先改代码

在 Linux 下做 I2C 从机或主机驱动时,上层往往只看到:

bash 复制代码
i2c i2c-1: transfer failed: -5 (EIO)

硬件侧(时钟、滤波、上升时间)和软件侧(消息长度、地址、DMA)都可能出错。
在改驱动逻辑之前,先把 I2C 外设关键寄存器 dump 出来,可以快速区分:

类别 典型寄存器 能回答的问题
时序 CCR, TRISE, FREQR 波特率、上升时间是否合规
状态 SR1, SR2 是否 BUSY、AF、BERR
控制 CR1, CR2 PE、ACK、中断/DMA 是否误开

下文寄存器名与数值按 STM32F1 系列 I2C 习惯书写(示例值可替换为你板子上的真实 dump)。


2. 在驱动里加"寄存器快照"打印

在 probe 或一次失败的 xfer 回调里,读 MMIO 并 dev_info 输出(示例):

bash 复制代码
static void i2c_dump_regs(struct device *dev, void __iomem *base)
{
    u32 cr1   = readl(base + 0x00);
    u32 cr2   = readl(base + 0x04);
    u32 oar1  = readl(base + 0x08);
    u32 oar2  = readl(base + 0x0C);
    u32 dr    = readl(base + 0x10);
    u32 sr1   = readl(base + 0x14);
    u32 sr2   = readl(base + 0x18);
    u32 ccr   = readl(base + 0x1C);
    u32 trir  = readl(base + 0x20);  /* TRISE */
    u32 fltr  = readl(base + 0x24);  /* 部分型号有 FLTR */

    dev_info(dev,
        "I2C regs @fail:\n"
        "  CR1=0x%08x CR2=0x%08x OAR1=0x%08x OAR2=0x%08x\n"
        "  DR=0x%08x SR1=0x%08x SR2=0x%08x\n"
        "  CCR=0x%08x TRISE=0x%08x FLTR=0x%08x\n",
        cr1, cr2, oar1, oar2, dr, sr1, sr2, ccr, trir, fltr);
}

一次失败时的示例输出(编造的典型现场):

bash 复制代码
[  312.448091] stm32-i2c 40005400.i2c: I2C regs @fail:
[  312.448120]   CR1=0x00000401 CR2=0x00000000 OAR1=0x00000040 OAR2=0x00000000
[  312.448145]   DR=0x000000A3 SR1=0x00000400 SR2=0x00000002
[  312.448168]   CCR=0x00000050 TRISE=0x00000002 FLTR=0x00000000

2.1 如何粗读这些数(示例解读)

寄存器 示例值 粗读
CR1 0x401 PE=1,外设已使能
SR1 0x400 AF=1(应答失败),常伴随时序/从机未就绪
SR2 0x2 主机模式、总线忙痕迹
CCR 0x50 对应约 100 kHz 量级(与 pclk 计算一致)
TRISE 0x2 仅 2 个时钟周期的上升时间 → 偏激进

注意:TRISE 的单位是 I2C 内核时钟(FREQR 对应的 MHz)周期数,不是纳秒;要和手册公式对照。


3. 总线时序与 TRISE 的关系(示意图)

I2C 规范要求:SCL/SDA 的 上升时间 Tr 有下限(标准模式常见 1000 ns 量级,与负载电容有关)。

MCU 用 TRISE 告诉硬件:"至少等这么久再认为高电平有效"。

3.1 正常:TRISE 足够

bash 复制代码
SCL  ────┐     ┌─────┐     ┌────
         │     │     │     │
         └─────┘     └─────┘
              ↑
         Tr 满足规范
         采样点在高电平稳定区

3.2 异常:TRISE 过小(本文问题)

bash 复制代码
SCL  ────┐  ╱╲  ┌─────
         │ ╱  ╲ │
         └╱    ╲┘
            ↑
    上升沿还在爬坡
    主机已采样 / 进入下一比特
    → 误码、AF、偶发 EIO

3.3 Mermaid:逻辑上的"抢跑读取"

从设备SCL 引脚I2C 硬件状态机从设备SCL 引脚I2C 硬件状态机TRISE 过小:等待计数不足释放 SCL,开始上升沿判定"高电平有效"过早采样 SDA / 发下一时钟实际仍为低或边沿区SR1.AF=1,传输失败


4. 完整调试流程(可按此复现)

上升沿慢/圆角

完全无波形

是

否

用户态 i2cget 失败 / dmesg EIO

示波器看 SCL/SDA

波形异常?

怀疑 Tr / 上拉 / TRISE

查引脚复用/时钟/电源

驱动中加寄存器 dump

对比 CCR 与 TRISE

TRISE 相对 CCR 过小?

按手册重算 TRISE

查 SR1 AF/BERR/仲裁/从机地址

改设备树或 clk 初始化

复测 + 长时间压力测试

通过

步骤 1:复现并保留日志

bash 复制代码
# 用户态复现
i2cget -y 1 0x50 0x00

# 同时抓内核
dmesg -w | tee /tmp/i2c_fail.log

步骤 2:示波器 / 逻辑分析仪

  • 通道 1:SCL,通道 2:SDA
  • 触发:下降沿或 I2C 解码器的 NACK
  • 重点看 SCL 上升沿 :是否未达到 Vih 就进入下一比特

步骤 3:失败瞬间 dump 寄存器

在 stm32_i2c_xfer 返回 -EIO 前调用 i2c_dump_regs()(见第 2 节)。

连续抓 20 次失败,统计 SR1、TRISE 是否稳定。

步骤 4:手算 TRISE(示例)

假设:

  • PCLK1 = 36 MHz → FREQR = 36(写入值 36)
  • 目标 Standard Mode 100 kHz
  • 手册要求:TRISE = FREQ(MHz) + 1(最小值,单位 cycles)
    对 36 MHz:至少 37(0x25)
  • 现场 dump 为 TRISE = 2 → 明显低于规范下限

CCR 示例计算(与 dump 一致):

bash 复制代码
Thigh = CCR * Tpclk1
      = 0x50 * (1/36µs) ≈ 2.2 µs  → 接近 100kHz 占空比之一半

CCR 看起来"像 100kHz",但 TRISE 没跟上 ,所以问题不在波特率而在 上升时间约束。

步骤 5:修改配置

设备树(示例):

bash 复制代码
&i2c1 {
    clock-frequency = <100000>;
    /* 部分 BSP 用自定义属性传递 tririse */
    st,i2c-trise-ns = <1000>;
    status = "okay";
};

或在 probe 里按手册编程:

bash 复制代码
/* 36MHz 时:TRISE 至少 37 */
writel(37, base + I2C_TRISE_OFFSET);

改完后再次 dump,期望看到:

bash 复制代码
CCR=0x00000050 TRISE=0x00000025   /* 0x25 = 37 */

步骤 6:验证

项 命令/方法 通过标准
单次读 i2cget -y 1 0x50 0x00 无 EIO
压力 for i in $(seq 1 10000); do i2cget -y 1 0x50 0; done 0 失败
波形 示波器 Tr 上升沿平顶后再采样
寄存器 失败时 dump SR1.AF 不再置位

5. 根因总结

项目 说明
现象 偶发 EIO,SR1 中 AF 置位,示波器 SCL 上升沿"未拉满"
误导项 CCR 计算正确,容易误以为波特率没问题
根因 TRISE 寄存器值过小 (示例 0x2),硬件在 SCL 尚未达到有效高电平时继续位时序
修复 按 FREQR 与模式(标准/快速)重算 TRISE,并考虑外部上拉与总线电容

6. 经验清单(Checklist)

  • 失败路径上 必打 SR1/SR2/CCR/TRISE,不要只打 CR1
  • CCR 与 TRISE 成对检查,二者对应不同物理约束
  • 示波器看 上升沿,不要只看频率
  • 修改后做 长时间循环 i2cget/i2cdetect,偶发问题需压力验证
  • 文档记录:当次 PCLK、FREQR、目标模式、最终 TRISE 写入值

7. 附录:与本文对应的"编造"寄存器对照表

阶段 CCR TRISE SR1 关键位 结果
调试前 0x50 0x02 AF=1 偶发失败
修复后 0x50 0x25 (37) 正常 稳定通过
相关推荐
xh didida7 小时前
Linux -- 基础IO
linux·服务器·开发语言·c++
zly35008 小时前
centos7 mount设备挂载 /dev/sda1/硬盘挂载
linux·运维·服务器
工业一体机老司机8 小时前
工业一体机选购后的系统部署:Ubuntu下Docker容器化运行与GPIO配置
linux·ubuntu·docker·人机交互
哎呦,帅小伙哦9 小时前
进程监控工具——htop
linux·运维·服务器
夏虫不可与之言冰9 小时前
Windows WSL2 (Ubuntu) 搭建 ESP‑IDF 6.1 开发环境全过程演示
linux·windows·ubuntu·esp32·乐鑫科技·乐鑫eim·乐鑫代理商
春涧草茶10 小时前
慢就是快12-12手动抛出异常
java·linux·前端
KING-WU51212 小时前
Linux 工具之Make 与 Makefile
java·linux·服务器
Lsetea12 小时前
OpenSSL报certificate is not yet valid:error 9与notBefore时间排查
linux·运维·https·ssl证书·openssl
魔法阵维护师12 小时前
Linux 命令行入门踩坑记录:从 VMware 装系统到文件管理
linux·运维·服务器
Apipi*12 小时前
30天速通Linux 第七章进程间通信
linux·运维·服务器