RT-Thread论坛
直播中

李娟

8年用户 1462经验值
私信 关注
[问答]

rt_i2c_transfer函数执行返回-8是怎么回事?

请教个问题,
最近打算将RT-Thread移植到ufun开发板上,在调试IIC外设(OLED模块,IIC接口)时,遇到个问题。
首先,硬件设备是ok的,已经用测试代码调通了,能正常显示,只是RT-thread的IIC未调通。
1.注册设备是成功的:

2.然而在使用rt_i2c_transfer()函数时,遇到了问题:


3.采用模拟IIC,硬件使用PB6,PB7引脚。

  • #define BSP_I2C1_SCL_PIN     22
  • #define BSP_I2C1_SDA_PIN     23


请教下,可能是哪些地方出了问题呢?


回帖(1)

打马过草原

2025-10-11 16:04:25

在 RT-Thread 中,rt_i2c_transfer 函数返回错误码 -8(即 -RT_EIO 表示 输入输出错误(I/O Error)。这种错误通常与硬件通信故障相关,尤其是在模拟 I2C(软件 I2C)驱动场景下。结合您描述的问题(已通过裸机测试验证硬件正常),以下是可能的原因及解决方案:




可能原因分析


1. 模拟 I2C 引脚配置问题



  • 开漏模式缺失:I2C 协议要求 SDA/SCL 引脚配置为开漏输出(Open-Drain),但模拟 I2C 驱动中可能误设为推挽输出。

  • 输入/输出模式切换失效:读取 SDA 信号时需切换为输入模式,但驱动未正确处理。

  • 检查点
     rt_pin_mode(scl_pin, PIN_MODE_OUTPUT_OD);   // 确保为开漏输出
    rt_pin_mode(sda_pin, PIN_MODE_OUTPUT_OD);   // 非推挽


2. 时序与延时不当



  • 延时不匹配:I2C 的起始、停止、数据读写等时序需精确延时(微秒级),udelay 未适配硬件频率(如 Ufun 主频)。

  • 超时设置过小timeout 参数设置过短,导致未收到 ACK 时判断超时。


  • 示例对比


     static void i2c_delay(rt_uint32_t us) {
         rt_hw_us_delay(us);  // 必须实现精确的微秒延时
    }

    struct rt_i2c_bit_ops bit_ops = {
         .udelay  = i2c_delay,
         .delay_us = i2c_delay,
         .timeout = 100,      // 超时值建议 ≥100us
         // ...
    };



3. 设备地址错误



  • 7-bit vs 8-bit 地址混淆:OLED 的 I2C 地址通常为 7 位(如 0x3C),但 msg.addr 要求原生地址(非左移后)。

  • 重要验证
     struct rt_i2c_msg msg = {
         .addr = 0x3C,        // 7-bit 地址(非 0x78)
         .flags = RT_I2C_WRITE,
         .buf = oled_data,
         .len = len
    };


4. GPIO 引脚冲突



  • 引脚被其他驱动占用:PB6/PB7 可能已被 SPI、UART 或定时器复用。

  • 确认方法:检查 /drivers/pin_map.c 中的引脚复用表。


5. 信号完整性问题



  • 上拉电阻缺失:需接 4.7kΩ 上拉电阻至 VCC(软件无法替代硬件)。

  • 电压电平匹配:目标设备是否支持开发板的工作电压(如 3.3V vs 5V)。




解决方案步骤


✅ 1. 检查引脚初始化


确保在 rt_i2c_bit_add_bus() 前完成开漏输出初始化:


   void i2c_init(void) {
       rt_pin_mode(BSP_I2C1_SCL_PIN, PIN_MODE_OUTPUT_OD);
       rt_pin_mode(BSP_I2C1_SDA_PIN, PIN_MODE_OUTPUT_OD);
       rt_pin_write(BSP_I2C1_SCL_PIN, PIN_HIGH);  // 总线空闲时拉高
       rt_pin_write(BSP_I2C1_SDA_PIN, PIN_HIGH);
   }

✅ 2. 验证时序延时函数


确认延时函数与主频匹配(避免 while 循环延时不精确):


   void i2c_delay(rt_uint32_t us) {
       rt_uint32_t ticks = us * (SystemCoreClock / 1000000) / 5; // 根据主频校准
       while (ticks--) __NOP();
   }

评分建议:超时值 timeout ≥100us,起始/停止信号延时 ≥5us。


✅ 3. 修正设备地址


OLED 地址常为 0x3C(7-bit),示例消息结构:


   msg.addr = 0x3C;           // 正确:7-bit 地址
   msg.flags = RT_I2C_WRITE;   // 写操作

✅ 4. 排查总线占用


调用 list_device 命令查看所有设备:


   msh > list_device

确保 i2c1 存在且未被锁定(其他线程需释放总线)。


✅ 5. 信号逻辑分析(关键)



  • 逻辑分析仪抓取波形,确认:

    • START 条件:SCL 高时 SDA 跳低。

    • ACK 信号:第 9 个时钟周期 SDA 被拉低。

    • 数据有效性:SDA 在 SCL 高电平期间保持稳定。


  • 若无设备,用 LED+定时器模拟信号变化调试。




调试参考代码(模拟 I2C 核心)


static void set_sda(void *data, rt_int32_t state) {
    rt_pin_write(sda_pin, state);
}

static void set_scl(void *data, rt_int32_t state) {
    rt_pin_write(scl_pin, state);
}

static rt_int32_t get_sda(void *data) {
    return rt_pin_read(sda_pin);  // 读取前自动切换输入模式
}

struct rt_i2c_bit_ops bit_ops = {
    .set_sda = set_sda,
    .set_scl = set_scl,
    .get_sda = get_sda,
    .udelay = i2c_delay,          // 关键延时函数
    .scl_pin = BSP_I2C1_SCL_PIN,  // 22
    .sda_pin = BSP_I2C1_SDA_PIN,  // 23
};



总结要点



  1. 引脚模式:务必配置为开漏输出(PIN_MODE_OUTPUT_OD)。

  2. 地址与时序:检查 7-bit 地址与环境延时。

  3. 硬件信号:通过物理手段(示波器/逻辑分析仪)验证波形。

  4. 冲突规避:确保 SCL/SDA 引脚未复用。


按上述步骤逐步排查,尤其是信号时序分析和物理连接验证(上拉电阻),可解决返回 -RT_EIO 的问题。

举报

更多回帖

发帖
×
20
完善资料,
赚取积分