1use embedded_hal_async::delay::DelayNs;
11use embedded_hal_async::i2c::{I2c, Operation};
12
13pub const ADDRESS_SDO_HIGH: u8 = 0x69;
15pub const ADDRESS_SDO_LOW: u8 = 0x68;
17pub const CHIP_ID: u8 = 0x24;
19
20pub static CONFIG_FILE: &[u8; 8192] = include_bytes!("config.bin");
24
25pub const UPLOAD_CHUNK: usize = 256;
28const _: () =
29 assert!(UPLOAD_CHUNK.is_multiple_of(2) && CONFIG_FILE.len().is_multiple_of(UPLOAD_CHUNK));
30
31mod reg {
32 pub const CHIP_ID: u8 = 0x00;
33 pub const ACC_X_LSB: u8 = 0x0C;
34 pub const INTERNAL_STATUS: u8 = 0x21;
35 pub const TEMPERATURE_0: u8 = 0x22;
36 pub const ACC_CONF: u8 = 0x40;
37 pub const ACC_RANGE: u8 = 0x41;
38 pub const GYR_CONF: u8 = 0x42;
39 pub const GYR_RANGE: u8 = 0x43;
40 pub const INIT_CTRL: u8 = 0x59;
41 pub const INIT_ADDR_0: u8 = 0x5B;
42 pub const INIT_DATA: u8 = 0x5E;
43 pub const PWR_CONF: u8 = 0x7C;
44 pub const PWR_CTRL: u8 = 0x7D;
45 pub const CMD: u8 = 0x7E;
46}
47
48const CMD_SOFT_RESET: u8 = 0xB6;
49const PWR_CTRL_GYR_ACC_TEMP: u8 = 0x0E;
51const PWR_CONF_PERFORMANCE: u8 = 0x02;
54const STATUS_INIT_OK: u8 = 0x01;
56
57#[derive(Clone, Copy, Debug, PartialEq, Eq)]
59#[cfg_attr(feature = "defmt", derive(defmt::Format))]
60pub enum AccelRange {
61 G2,
63 G4,
65 G8,
67 G16,
69}
70
71impl AccelRange {
72 const fn lsb_per_g(self) -> f32 {
73 match self {
74 AccelRange::G2 => 16384.0,
75 AccelRange::G4 => 8192.0,
76 AccelRange::G8 => 4096.0,
77 AccelRange::G16 => 2048.0,
78 }
79 }
80}
81
82#[derive(Clone, Copy, Debug, PartialEq, Eq)]
84#[cfg_attr(feature = "defmt", derive(defmt::Format))]
85pub enum GyroRange {
86 Dps2000,
88 Dps1000,
90 Dps500,
92 Dps250,
94 Dps125,
96}
97
98impl GyroRange {
99 const fn lsb_per_dps(self) -> f32 {
100 match self {
101 GyroRange::Dps2000 => 16.384,
102 GyroRange::Dps1000 => 32.768,
103 GyroRange::Dps500 => 65.536,
104 GyroRange::Dps250 => 131.072,
105 GyroRange::Dps125 => 262.144,
106 }
107 }
108}
109
110#[derive(Clone, Copy, Debug, PartialEq, Eq)]
112#[cfg_attr(feature = "defmt", derive(defmt::Format))]
113#[allow(missing_docs)]
114pub enum DataRate {
115 Hz25 = 0x06,
116 Hz50 = 0x07,
117 Hz100 = 0x08,
118 Hz200 = 0x09,
119 Hz400 = 0x0A,
120 Hz800 = 0x0B,
121 Hz1600 = 0x0C,
122}
123
124#[derive(Clone, Copy, Debug, PartialEq, Eq)]
126#[cfg_attr(feature = "defmt", derive(defmt::Format))]
127pub struct Config {
128 pub accel_range: AccelRange,
130 pub gyro_range: GyroRange,
132 pub rate: DataRate,
134}
135
136impl Default for Config {
137 fn default() -> Self {
139 Self {
140 accel_range: AccelRange::G8,
141 gyro_range: GyroRange::Dps2000,
142 rate: DataRate::Hz100,
143 }
144 }
145}
146
147impl Config {
148 const fn acc_conf(&self) -> u8 {
150 0x80 | (0x02 << 4) | self.rate as u8
151 }
152
153 const fn gyr_conf(&self) -> u8 {
155 0x80 | (0x02 << 4) | self.rate as u8
156 }
157}
158
159#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
161#[cfg_attr(feature = "defmt", derive(defmt::Format))]
162pub struct RawSample {
163 pub accel: [i16; 3],
165 pub gyro: [i16; 3],
167}
168
169#[derive(Clone, Copy, Debug, Default, PartialEq)]
171#[cfg_attr(feature = "defmt", derive(defmt::Format))]
172pub struct Sample {
173 pub accel: [f32; 3],
175 pub gyro: [f32; 3],
177}
178
179#[derive(Clone, Copy, Debug, PartialEq, Eq)]
181#[cfg_attr(feature = "defmt", derive(defmt::Format))]
182pub enum Error<E> {
183 I2c(E),
185 WrongChip(u8),
187 InitFailed(u8),
189}
190
191impl<E> From<E> for Error<E> {
192 fn from(e: E) -> Self {
193 Error::I2c(e)
194 }
195}
196
197pub struct Bmi270<I2C> {
199 i2c: I2C,
200 addr: u8,
201 config: Config,
202}
203
204impl<I2C: I2c> Bmi270<I2C> {
205 pub const fn new(i2c: I2C, addr: u8) -> Self {
207 Self {
208 i2c,
209 addr,
210 config: Config {
211 accel_range: AccelRange::G8,
212 gyro_range: GyroRange::Dps2000,
213 rate: DataRate::Hz100,
214 },
215 }
216 }
217
218 pub async fn chip_id(&mut self) -> Result<u8, I2C::Error> {
220 self.read_u8(reg::CHIP_ID).await
221 }
222
223 pub async fn init(
226 &mut self,
227 config: Config,
228 delay: &mut impl DelayNs,
229 ) -> Result<(), Error<I2C::Error>> {
230 let id = self.chip_id().await?;
231 if id != CHIP_ID {
232 return Err(Error::WrongChip(id));
233 }
234
235 self.write(reg::CMD, CMD_SOFT_RESET).await?;
236 delay.delay_ms(2).await;
237 self.write(reg::PWR_CONF, 0x00).await?;
239 delay.delay_us(450).await;
240
241 self.write(reg::INIT_CTRL, 0x00).await?;
242 for (i, chunk) in CONFIG_FILE.chunks(UPLOAD_CHUNK).enumerate() {
243 let word = (i * UPLOAD_CHUNK / 2) as u16;
244 let addr = [reg::INIT_ADDR_0, (word & 0x0F) as u8, (word >> 4) as u8];
246 self.i2c.write(self.addr, &addr).await?;
247 self.i2c
250 .transaction(
251 self.addr,
252 &mut [Operation::Write(&[reg::INIT_DATA]), Operation::Write(chunk)],
253 )
254 .await?;
255 }
256 self.write(reg::INIT_CTRL, 0x01).await?;
257
258 let mut status = 0;
260 for _ in 0..25 {
261 delay.delay_ms(1).await;
262 status = self.read_u8(reg::INTERNAL_STATUS).await? & 0x0F;
263 if status == STATUS_INIT_OK {
264 break;
265 }
266 }
267 if status != STATUS_INIT_OK {
268 return Err(Error::InitFailed(status));
269 }
270
271 self.configure(config).await
272 }
273
274 pub async fn configure(&mut self, config: Config) -> Result<(), Error<I2C::Error>> {
276 self.write(reg::ACC_CONF, config.acc_conf()).await?;
277 self.write(reg::ACC_RANGE, config.accel_range as u8).await?;
278 self.write(reg::GYR_CONF, config.gyr_conf()).await?;
279 self.write(reg::GYR_RANGE, config.gyro_range as u8).await?;
280 self.write(reg::PWR_CTRL, PWR_CTRL_GYR_ACC_TEMP).await?;
281 self.write(reg::PWR_CONF, PWR_CONF_PERFORMANCE).await?;
282 self.config = config;
283 Ok(())
284 }
285
286 pub async fn read_raw(&mut self) -> Result<RawSample, I2C::Error> {
288 let mut b = [0u8; 12];
289 self.i2c
290 .write_read(self.addr, &[reg::ACC_X_LSB], &mut b)
291 .await?;
292 let v = |i: usize| i16::from_le_bytes([b[i], b[i + 1]]);
293 Ok(RawSample {
294 accel: [v(0), v(2), v(4)],
295 gyro: [v(6), v(8), v(10)],
296 })
297 }
298
299 pub async fn read(&mut self) -> Result<Sample, I2C::Error> {
301 let raw = self.read_raw().await?;
302 Ok(self.scale(raw))
303 }
304
305 pub fn scale(&self, raw: RawSample) -> Sample {
307 let (a, g) = (
308 self.config.accel_range.lsb_per_g(),
309 self.config.gyro_range.lsb_per_dps(),
310 );
311 Sample {
312 accel: raw.accel.map(|v| f32::from(v) / a),
313 gyro: raw.gyro.map(|v| f32::from(v) / g),
314 }
315 }
316
317 pub async fn temperature(&mut self) -> Result<Option<f32>, I2C::Error> {
319 let mut b = [0u8; 2];
320 self.i2c
321 .write_read(self.addr, &[reg::TEMPERATURE_0], &mut b)
322 .await?;
323 let raw = i16::from_le_bytes(b);
324 Ok((raw != i16::MIN).then(|| 23.0 + f32::from(raw) / 512.0))
326 }
327
328 pub fn release(self) -> I2C {
330 self.i2c
331 }
332
333 async fn write(&mut self, reg: u8, value: u8) -> Result<(), I2C::Error> {
334 self.i2c.write(self.addr, &[reg, value]).await
335 }
336
337 async fn read_u8(&mut self, reg: u8) -> Result<u8, I2C::Error> {
338 let mut b = [0];
339 self.i2c.write_read(self.addr, &[reg], &mut b).await?;
340 Ok(b[0])
341 }
342}
343
344#[cfg(test)]
345mod tests {
346 extern crate std;
347 use super::*;
348 use crate::test_util::block_on;
349 use core::convert::Infallible;
350 use std::vec::Vec;
351
352 struct FakeImu {
355 regs: [u8; 128],
356 init_mem: Vec<u8>,
357 sample: [u8; 12],
358 }
359
360 impl FakeImu {
361 fn new() -> Self {
362 let mut regs = [0; 128];
363 regs[usize::from(reg::CHIP_ID)] = CHIP_ID;
364 Self {
365 regs,
366 init_mem: Vec::new(),
367 sample: [0; 12],
368 }
369 }
370
371 fn write(&mut self, bytes: &[u8]) {
372 let (start, data) = (bytes[0], &bytes[1..]);
373 if start == reg::INIT_DATA {
374 let word = usize::from(self.regs[0x5B] & 0x0F) | usize::from(self.regs[0x5C]) << 4;
375 assert_eq!(
376 word * 2,
377 self.init_mem.len(),
378 "chunk written at the wrong offset"
379 );
380 self.init_mem.extend_from_slice(data);
381 return;
382 }
383 for (i, v) in data.iter().enumerate() {
384 self.regs[usize::from(start) + i] = *v;
385 }
386 if start == reg::INIT_CTRL && data == [0x01] && self.init_mem.as_slice() == CONFIG_FILE
387 {
388 self.regs[usize::from(reg::INTERNAL_STATUS)] = STATUS_INIT_OK;
389 }
390 }
391 }
392
393 impl embedded_hal_async::i2c::ErrorType for FakeImu {
394 type Error = Infallible;
395 }
396
397 impl I2c for FakeImu {
398 async fn transaction(
399 &mut self,
400 addr: u8,
401 ops: &mut [Operation<'_>],
402 ) -> Result<(), Infallible> {
403 assert_eq!(addr, ADDRESS_SDO_HIGH);
404 let mut burst = Vec::new();
406 let mut ptr = 0;
407 for op in ops.iter_mut() {
408 match op {
409 Operation::Write(b) => burst.extend_from_slice(b),
410 Operation::Read(out) => {
411 if !burst.is_empty() {
412 ptr = burst[0];
413 burst.clear();
414 }
415 for (i, o) in out.iter_mut().enumerate() {
416 let r = usize::from(ptr) + i;
417 *o = if (0x0C..0x18).contains(&r) {
418 self.sample[r - 0x0C]
419 } else {
420 self.regs[r]
421 };
422 }
423 }
424 }
425 }
426 if burst.len() > 1 {
427 self.write(&burst);
428 }
429 Ok(())
430 }
431 }
432
433 struct NoDelay;
434 impl DelayNs for NoDelay {
435 async fn delay_ns(&mut self, _ns: u32) {}
436 }
437
438 #[test]
439 fn init_uploads_whole_config_and_configures() {
440 let mut imu = Bmi270::new(FakeImu::new(), ADDRESS_SDO_HIGH);
441 let cfg = Config {
442 accel_range: AccelRange::G4,
443 gyro_range: GyroRange::Dps500,
444 rate: DataRate::Hz200,
445 };
446 block_on(imu.init(cfg, &mut NoDelay)).unwrap();
447 let chip = imu.release();
448 assert_eq!(chip.init_mem.len(), 8192);
449 assert_eq!(chip.regs[usize::from(reg::ACC_CONF)], 0xA9);
450 assert_eq!(chip.regs[usize::from(reg::ACC_RANGE)], 0x01);
451 assert_eq!(chip.regs[usize::from(reg::GYR_RANGE)], 0x02);
452 assert_eq!(chip.regs[usize::from(reg::PWR_CTRL)], 0x0E);
453 }
454
455 #[test]
456 fn init_rejects_other_chips() {
457 let mut fake = FakeImu::new();
458 fake.regs[0] = 0x26;
459 let mut imu = Bmi270::new(fake, ADDRESS_SDO_HIGH);
460 assert_eq!(
461 block_on(imu.init(Config::default(), &mut NoDelay)),
462 Err(Error::WrongChip(0x26))
463 );
464 }
465
466 #[test]
467 fn samples_are_little_endian_and_scaled() {
468 let mut fake = FakeImu::new();
469 fake.sample[4..6].copy_from_slice(&4096i16.to_le_bytes());
471 fake.sample[6..8].copy_from_slice(&(-164i16).to_le_bytes());
472 let mut imu = Bmi270::new(fake, ADDRESS_SDO_HIGH);
473 let s = block_on(imu.read()).unwrap();
474 assert_eq!(s.accel, [0.0, 0.0, 1.0]);
475 assert!((s.gyro[0] + 10.0).abs() < 0.01);
476 }
477}