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.rs
和 lib.rs
中的导入语句。导入语句已经存在,你只需要注释掉当前的导入语句,并取消注释标记为解答的几行。
驱动 API
传感器实例
✅ 创建一个结构体来表示传感器。它有两个字段,一个表示传感器的设备地址,另一个表示 I²C
总线。这是使用 embedded-hal
crate 中定义的 trait 来实现的。该结构体是公有的,因为我们需要从这个 crate 外访问它,但它的字段是私有的。
✅ 在 impl
块里实现一个实例化方法。这个方法需要从外部访问,所以它被标记为 pub
。这个方法获取 I²C 总线的所有权,然后创建前面定义的结构体的实例。
设备地址
✅ 这个 I²C 设备有两个可能的地址,在数据手册的 9.3 节里找到它们。
🔎 我们通过向设备上的 AP_AD0
引脚施加 0V
或 3.3V
来告诉设备我们希望它使用哪一个地址。如果我们施加 0V
,它会监听地址 0x68
。如果我们施加 3.3V
,它会监听地址 0x69
。因此,可以将引脚 AD_AD0
视为一位输入,用于设置设备地址的最低位。
✅ 创建一个枚举来表示两种地址。变体的值需要用二进制表示。
寄存器的表示
✅ 创建一个枚举来表示传感器的寄存器。每个变体都将寄存器的地址作为它的值。目前,我们只需要 WhoAmI
寄存器。到数据手册里查找它的地址。
✅ 实现一个方法,用于将变体的地址以 u8
的形式提供出来。
read_register()
和 write_register()
✅ 查看 embedded-hal
中的 write
和 write_read
函数。为什么是 write_read
而不是简单的 read
?
解答
原因在于 I²C 协议的特性。我们需要先在 I²C 总线上写一个命令,来指定我们想要读取哪个寄存器。✅ 给传感器实例定义 read_register
和 write_register
方法。使用 embedded-hal
crate 提供的方法。它们将作为更具体的方法的基础,并作为一个抽象层,用于适配具有 8 位寄存器的传感器。这意味着,读取和写入的数据都是无符号8位的整数。这些辅助方法可以保持私有,因为我们不需要从这个 crate 外访问它们。
✅ 实现一个公有方法来读取地址为 0x75
的 WhoAmI
寄存器。使用上面的 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; }