I²C 驱动练习 - 困难版

我们将不会编写整个驱动程序,只会做第一步:驱动程序编写的 hello world,即读取传感器的设备 ID。这个版本被标记为困难,因为你需要自己编写方法的内容,并在 embedded-hal 和数据手册里自己查找信息。两个版本使用的是相同的文件。

i2c-driver/src/icm42670p.rs 是一个非常基础的 I²C IMU 传感器驱动的填空版本。任务是补全这个文件,使得运行 main.rs 可以记录驱动的设备 ID。

i2c-driver/src/icm42670p_solution.rs 提供本练习的解答。如果要运行它,需要更改 main.rslib.rs 中的导入语句。导入语句已经存在,你只需要注释掉当前的导入语句,并取消注释标记为解答的几行。

驱动 API

传感器实例

✅ 创建一个结构体来表示传感器。它有两个字段,一个表示传感器的设备地址,另一个表示 I²C 总线。这是使用 embedded-hal crate 中定义的 trait 来实现的。该结构体是公有的,因为我们需要从这个 crate 外访问它,但它的字段是私有的。

✅ 在 impl 块里实现一个实例化方法。这个方法需要从外部访问,所以它被标记为 pub。这个方法获取 I²C 总线的所有权,然后创建前面定义的结构体的实例。

设备地址

✅ 这个 I²C 设备有两个可能的地址,在数据手册的 9.3 节里找到它们。

🔎 我们通过向设备上的 AP_AD0 引脚施加 0V3.3V 来告诉设备我们希望它使用哪一个地址。如果我们施加 0V,它会监听地址 0x68。如果我们施加 3.3V,它会监听地址 0x69。因此,可以将引脚 AD_AD0 视为一位输入,用于设置设备地址的最低位。

✅ 创建一个枚举来表示两种地址。变体的值需要用二进制表示。

寄存器的表示

✅ 创建一个枚举来表示传感器的寄存器。每个变体都将寄存器的地址作为它的值。目前,我们只需要 WhoAmI 寄存器。到数据手册里查找它的地址。

✅ 实现一个方法,用于将变体的地址以 u8 的形式提供出来。

read_register()write_register()

✅ 查看 embedded-hal 中的 writewrite_read 函数。为什么是 write_read 而不是简单的 read

解答 原因在于 I²C 协议的特性。我们需要先在 I²C 总线上写一个命令,来指定我们想要读取哪个寄存器。

✅ 给传感器实例定义 read_registerwrite_register 方法。使用 embedded-hal crate 提供的方法。它们将作为更具体的方法的基础,并作为一个抽象层,用于适配具有 8 位寄存器的传感器。这意味着,读取和写入的数据都是无符号8位的整数。这些辅助方法可以保持私有,因为我们不需要从这个 crate 外访问它们。

✅ 实现一个公有方法来读取地址为 0x75WhoAmI 寄存器。使用上面的 read_register() 方法。

✅ 可选:实现更多方法来向驱动程序添加功能。在文档中查阅相应寄存器及其地址。💡 一些点子:

  • 启用陀螺仪传感器或加速度计
  • 启动测量
  • 读取测得数据

🔎 有关外设寄存器的一般信息

  • 寄存器事实上就是少量的存储空间,可由处理器直接访问。这个传感器上的寄存器是 8 位的。
  • 可以通过地址访问这些寄存器
  • 在数据手册的第 14 节,有寄存器表
  • 为了得到一个由 MSB(最高有效位)和 LSB(最低有效位)组合而成的 16 位数,可以将 MSB 值移位,然后或上 LSB 值。

#![allow(unused)]
fn main() {
let GYRO_DATA_X: i16 = ((GYRO_DATA_X1 as i16) << 8) | GYRO_DATA_X0 as i16;
}