lospring 发表于 2026-8-11 00:57:28

【瑞萨RA-eco-RA6M4评测】ICM20948共I2C驱动

本帖最后由 lospring 于 2026-8-11 00:59 编辑

上一篇帖子记录了 RA6M4(R7FA6M4AF)驱动 SSD1315 OLED 的完整过程。这次在原有基础上,在**同一条 I2C 总线**上新增了一颗 ICM-20948 九轴惯性传感器(IMU),读取其内部温度传感器,每秒刷新一次并显示在 OLED 128×64 屏幕上。
OLED 与 ICM-20948 共用同一组 I2C 引脚(P400=SCL、P401=SDA,I2C0),靠不同从机地址区分,无需额外 GPIO。

| **设备**      | **引脚**                | **I2C地址** | **用途**      |
| ------------------ | -------------------------- | -------------- | ---------------- |
|                  |                        |            |                |
| OLED(SSD1315)| P400(SCL)/ P401(SDA) | 0x3C         | 显示         |
| IMU(ICM-20948) | 同上                     | 0x68         | 读取温度       |
| LED P002         | P0.2                     | —         | CPU运行指示    |
| LED P404         | P4.4                     | —         | OLED初始化成功 |
| LED P405         | P4.5                     | —         | 屏幕绘制完成   |

ICM-20948 的 AD0 引脚接高电平,地址变为 0x69。本次使用 AD0=GND,即 0x68。
!(https://www.eefocus.com/forum/data/attachment/forum/202608/11/005707crzzx1prdrrr64k1.jpg)

# **多设备共享一条 I2C 总线**

FSP 的 r_iic_master 单实例只能“记住”一个目标地址。要让 OLED(0x3C)和 ICM-20948(0x68)共用 g_i2c0,**每次传输前都要调用 slaveAddressSet 切换到目标设备**。两个驱动都这么做:

**SSD1315 驱动(ssd1315.c):每次写前切换地址**

```
static fsp\_err\_t i2c\_write\_bytes(uint8\_t \*p\_data, uint16\_t len)

{
/\* 先切到 OLED 地址,再发起传输 */

g\_i2c0.p\_api->slaveAddressSet(g\_i2c0.p\_ctrl, SSD1315\_ADDR,

                              I2C\_MASTER\_ADDR\_MODE\_7BIT);

...

g\_i2c0.p\_api->write(g\_i2c0.p\_ctrl, p\_data, len, false);
}

...
```

**ICM-20948 驱动(icm20948.c):同样的套路**

static fsp\_err\_t icm20948\_set\_addr(void)

```
{
return g\_i2c0.p\_api->slaveAddressSet(g\_i2c0.p\_ctrl,

                                    ICM20948\_ADDR\_DEFAULT,   /\* 0x68 \*/

                                    I2C\_MASTER\_ADDR\_MODE\_7BIT);
}
```

这样无论 OLED 还是 IMU,走同一套回调标志(i2c_done / i2c_error),由 ssd1315.c 中定义的 i2c_master_callback 统一通知完成/出错,两个驱动都能用(extern 声明)。

# **ICM-20948 读寄存器的 Restart 时序**

**ICM-20948 读取一个寄存器需要两条消息:先写寄存器地址,再读数据。中间不能发 STOP,而是发 **Restart(重复起始)**。FSP 的 write 函数第四个参数 restart 正是为此设计:写完后发 Restart 而不是 STOP,紧接读。**

```
static fsp\_err\_t icm20948\_read\_regs(uint8\_t reg, uint8\_t \*p\_data, uint16\_t len)

{
icm20948\_set\_addr();

/\* 阶段1:写寄存器地址,restart=true -> 写完后不发 STOP,发 Restart */

g\_i2c0.p\_api->write(g\_i2c0.p\_ctrl, ®, 1, true);

i2c\_wait(1000000UL);

/\* 阶段2:读数据 */

g\_i2c0.p\_api->read(g\_i2c0.p\_ctrl, p\_data, len, false);

return i2c\_wait(1000000UL);
}
```

# **温度读取与换算显示**

**温度寄存器 TEMP\_OUT\_H / TEMP\_OUT\_L(Bank0,0x39/0x3A),每次先切回 Bank0 再读。换算公式(官方 datasheet):T(℃) = raw / 333.87 + 21.0。**

```
fsp\_err\_t icm20948\_read\_temperature(float \*temp\_c)

{
uint8\_t buf;

icm20948\_select\_bank(ICM20948\_BANK\_0);

icm20948\_read\_regs(ICM20948\_REG\_TEMP\_OUT\_H, buf, 2);

int16\_t raw = (int16\_t)((uint16\_t)buf << 8 | (uint16\_t)buf);

\*temp\_c = ((float)raw / 333.87f) + 21.0f;   /\* 官方换算公式 */

return FSP\_SUCCESS;
}
```

# **主流程**

hal\_entry():

1. LED 初始化,点亮 P002      // CPU 运行中
2. ssd1315\_init()               // 打开 g_i2c0、OLED 初始化
   
   成功 -> P404 亮;失败 -> P002 闪 2 次并停住
3. 先显示 "ICM20948 Temp" 提示
4. icm20948\_init()               // WHO\_AM\_I 校验 + 唤醒 + 关惯性轴
   
   失败 -> 屏幕显示 "ICM20948 FAIL / Check wiring!"
5. 进入主循环,每 1s:
   
   icm20948\_read\_temperature(&t)
   
   -> format\_temp() 转字符串
   
   -> 清屏、绘制 "RA6M4 ICM20948" + 温度 + "I2C Bus Shared"
   
   -> ssd1315\_refresh()(页寻址逐页刷新)
   
   -> 延时 1000ms
   !(https://www.eefocus.com/forum/data/attachment/forum/202608/11/005720ziipnj9iqi6qvuwq.jpg)
   

页: [1]
查看完整版本: 【瑞萨RA-eco-RA6M4评测】ICM20948共I2C驱动