1use embedded_hal::{delay::DelayNs, i2c::I2c};
8
9use crate::devices;
10
11const BMI270_CHIP_ID: u8 = 0x00;
12const BMI270_STATUS: u8 = 0x03;
13const BMI270_AUX_DATA: u8 = 0x04;
14const BMI270_ACC_DATA: u8 = 0x0C;
15const BMI270_GYR_DATA: u8 = 0x12;
16const BMI270_ACC_CONF: u8 = 0x40;
17const BMI270_ACC_RANGE: u8 = 0x41;
18const BMI270_GYR_CONF: u8 = 0x42;
19const BMI270_GYR_RANGE: u8 = 0x43;
20const BMI270_AUX_CONF: u8 = 0x44;
21const BMI270_AUX_DEV_ID: u8 = 0x4B;
22const BMI270_AUX_IF_CONF: u8 = 0x4C;
23const BMI270_AUX_RD_ADDR: u8 = 0x4D;
24const BMI270_AUX_WR_ADDR: u8 = 0x4E;
25const BMI270_AUX_WR_DATA: u8 = 0x4F;
26const BMI270_INT_STATUS_1: u8 = 0x1D;
27const BMI270_INTERNAL_STATUS: u8 = 0x21;
28const BMI270_INT_MAP_DATA: u8 = 0x58;
29const BMI270_INIT_CTRL: u8 = 0x59;
30const BMI270_INIT_ADDR_0: u8 = 0x5B;
31const BMI270_INIT_DATA: u8 = 0x5E;
32const BMI270_CONFIG_LEN: usize = 8192;
33const BMI270_IF_CONF: u8 = 0x6B;
34const BMI270_PWR_CONF: u8 = 0x7C;
35const BMI270_PWR_CTRL: u8 = 0x7D;
36const BMI270_CMD: u8 = 0x7E;
37const BMI270_EXPECTED_CHIP_ID: u8 = 0x24;
38
39const BMM150_CHIP_ID: u8 = 0x40;
40const BMM150_DATA_X_LSB: u8 = 0x42;
41const BMM150_POWER_CONTROL: u8 = 0x4B;
42const BMM150_OP_MODE: u8 = 0x4C;
43const BMM150_EXPECTED_CHIP_ID: u8 = 0x32;
44const AUX_BUSY_MASK: u8 = 0x04;
45
46#[repr(C, align(4))]
47struct AlignedBytes<const N: usize>([u8; N]);
48
49static BMI270_CONFIG_FILE: AlignedBytes<BMI270_CONFIG_LEN> =
50 AlignedBytes(*include_bytes!("../firmware/bmi270_config.bin"));
51
52#[cfg_attr(feature = "defmt", derive(defmt::Format))]
53#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
54pub struct Vector3 {
55 pub x: i32,
56 pub y: i32,
57 pub z: i32,
58}
59
60impl Vector3 {
61 pub const fn new(x: i32, y: i32, z: i32) -> Self {
62 Self { x, y, z }
63 }
64
65 pub const fn offset(self, offsets: Self) -> Self {
66 Self {
67 x: self.x - offsets.x,
68 y: self.y - offsets.y,
69 z: self.z - offsets.z,
70 }
71 }
72}
73
74#[cfg_attr(feature = "defmt", derive(defmt::Format))]
75#[derive(Clone, Copy, Debug, Eq, PartialEq)]
76pub enum AccelRange {
77 G2,
78 G4,
79 G8,
80 G16,
81}
82
83#[cfg_attr(feature = "defmt", derive(defmt::Format))]
84#[derive(Clone, Copy, Debug, Eq, PartialEq)]
85pub enum GyroRange {
86 Dps125,
87 Dps250,
88 Dps500,
89 Dps1000,
90 Dps2000,
91}
92
93#[cfg_attr(feature = "defmt", derive(defmt::Format))]
94#[derive(Clone, Copy, Debug, Eq, PartialEq)]
95pub enum SampleRate {
96 Hz25,
97 Hz50,
98 Hz100,
99 Hz200,
100 Hz400,
101}
102
103#[cfg_attr(feature = "defmt", derive(defmt::Format))]
104#[derive(Clone, Copy, Debug, Eq, PartialEq)]
105pub struct Bmi270Config {
106 pub accel_range: AccelRange,
107 pub gyro_range: GyroRange,
108 pub sample_rate: SampleRate,
109}
110
111impl Bmi270Config {
112 pub const DEFAULT: Self = Self {
113 accel_range: AccelRange::G4,
114 gyro_range: GyroRange::Dps500,
115 sample_rate: SampleRate::Hz100,
116 };
117}
118
119#[cfg_attr(feature = "defmt", derive(defmt::Format))]
120#[derive(Clone, Copy, Debug, Eq, PartialEq)]
121pub struct MotionThreshold {
122 pub accel_delta_mg: i32,
123}
124
125pub struct Bmi270<I2C> {
126 i2c: I2C,
127 address: u8,
128 accel_offset: Vector3,
129 gyro_offset: Vector3,
130}
131
132impl<I2C> Bmi270<I2C> {
133 pub const fn new(i2c: I2C) -> Self {
134 Self {
135 i2c,
136 address: devices::i2c::BMI270_IMU,
137 accel_offset: Vector3::new(0, 0, 0),
138 gyro_offset: Vector3::new(0, 0, 0),
139 }
140 }
141
142 pub fn set_accel_offset(&mut self, offset: Vector3) {
143 self.accel_offset = offset;
144 }
145 pub fn set_gyro_offset(&mut self, offset: Vector3) {
146 self.gyro_offset = offset;
147 }
148 pub fn release(self) -> I2C {
149 self.i2c
150 }
151}
152
153impl<I2C, Error> Bmi270<I2C>
154where
155 I2C: I2c<Error = Error>,
156{
157 pub fn init(&mut self, config: Bmi270Config) -> Result<(), Error> {
162 self.write_register(BMI270_PWR_CONF, 0x00)?;
163 self.configure(config)?;
164 self.write_register(BMI270_PWR_CTRL, 0x0E)
165 }
166
167 pub fn init_with_delay(
168 &mut self,
169 config: Bmi270Config,
170 delay: &mut impl DelayNs,
171 ) -> Result<(), Error> {
172 self.write_register(BMI270_CMD, 0xB6)?;
173 delay.delay_ms(2);
174 self.write_register(BMI270_PWR_CONF, 0x00)?;
175 delay.delay_ms(2);
176 self.upload_config(delay)?;
177 self.write_register(BMI270_INT_MAP_DATA, 0xFF)?;
178 self.configure(config)?;
179 self.write_register(BMI270_PWR_CTRL, 0x0E)?;
180 delay.delay_ms(20);
181 Ok(())
182 }
183
184 pub fn configure(&mut self, config: Bmi270Config) -> Result<(), Error> {
185 self.write_register(BMI270_ACC_CONF, accel_conf_code(config.sample_rate))?;
186 self.write_register(BMI270_GYR_CONF, gyro_conf_code(config.sample_rate))?;
187 self.write_register(BMI270_ACC_RANGE, accel_range_code(config.accel_range))?;
188 self.write_register(BMI270_GYR_RANGE, gyro_range_code(config.gyro_range))
189 }
190
191 pub fn chip_id(&mut self) -> Result<u8, Error> {
192 self.read_register(BMI270_CHIP_ID)
193 }
194 pub fn is_expected_chip(&mut self) -> Result<bool, Error> {
195 Ok(self.chip_id()? == BMI270_EXPECTED_CHIP_ID)
196 }
197
198 pub fn internal_status(&mut self) -> Result<u8, Error> {
204 self.read_register(BMI270_INTERNAL_STATUS)
205 }
206
207 pub fn interrupt_status_1(&mut self) -> Result<u8, Error> {
209 self.read_register(BMI270_INT_STATUS_1)
210 }
211
212 pub fn power_control(&mut self) -> Result<u8, Error> {
214 self.read_register(BMI270_PWR_CTRL)
215 }
216
217 pub fn init_bmm150_aux(&mut self, delay: &mut impl DelayNs) -> Result<u8, Error> {
222 self.aux_setup_mode(devices::i2c::BMM150_MAGNETOMETER)?;
223 self.aux_write_register(BMM150_POWER_CONTROL, 0x83, delay)?;
224 delay.delay_ms(2);
225 let chip_id = self.aux_read_register(BMM150_CHIP_ID, delay)?;
226 if chip_id == BMM150_EXPECTED_CHIP_ID {
227 self.aux_write_register(BMM150_OP_MODE, 0x38, delay)?;
228 self.write_register(BMI270_AUX_IF_CONF, 0x4F)?;
229 self.write_register(BMI270_AUX_RD_ADDR, BMM150_DATA_X_LSB)?;
230 self.write_register(BMI270_AUX_CONF, 0x0B)?;
231 self.write_register(BMI270_PWR_CTRL, 0x0F)?;
232 delay.delay_ms(50);
233 }
234 Ok(chip_id)
235 }
236
237 pub fn bmm150_aux_magnetic_raw(&mut self) -> Result<Vector3, Error> {
239 let mut data = [0u8; 8];
240 self.i2c
241 .write_read(self.address, &[BMI270_AUX_DATA], &mut data)?;
242 Ok(decode_bmm150_mag_data(data))
243 }
244
245 pub fn bmm150_aux_magnetic_raw_manual(
250 &mut self,
251 delay: &mut impl DelayNs,
252 ) -> Result<Vector3, Error> {
253 self.write_register(BMI270_AUX_IF_CONF, 0x4F)?;
254 self.write_register(BMI270_AUX_RD_ADDR, BMM150_DATA_X_LSB)?;
255 self.wait_aux_ready(delay)?;
256 delay.delay_ms(4);
257 self.bmm150_aux_magnetic_raw()
258 }
259
260 pub fn acceleration_raw(&mut self) -> Result<Vector3, Error> {
261 let raw = self.read_vector(BMI270_ACC_DATA)?;
262 Ok(raw.offset(self.accel_offset))
263 }
264
265 pub fn gyroscope_raw(&mut self) -> Result<Vector3, Error> {
266 let raw = self.read_vector(BMI270_GYR_DATA)?;
267 Ok(raw.offset(self.gyro_offset))
268 }
269
270 fn upload_config(&mut self, delay: &mut impl DelayNs) -> Result<(), Error> {
271 self.write_register(BMI270_INIT_CTRL, 0x00)?;
272 delay.delay_ms(2);
273
274 let config_file = &BMI270_CONFIG_FILE.0;
275 let mut offset = 0usize;
276 while offset < config_file.len() {
277 let chunk_len = (config_file.len() - offset).min(16);
278 self.i2c.write(
279 self.address,
280 &[
281 BMI270_INIT_ADDR_0,
282 ((offset >> 1) & 0x0F) as u8,
283 (offset >> 5) as u8,
284 ],
285 )?;
286
287 let mut packet = [0u8; 17];
288 packet[0] = BMI270_INIT_DATA;
289 packet[1..1 + chunk_len].copy_from_slice(&config_file[offset..offset + chunk_len]);
290 self.i2c.write(self.address, &packet[..1 + chunk_len])?;
291 offset += chunk_len;
292 }
293
294 self.write_register(BMI270_INIT_CTRL, 0x01)?;
295 delay.delay_ms(20);
296 Ok(())
297 }
298
299 fn aux_setup_mode(&mut self, i2c_address: u8) -> Result<(), Error> {
300 self.write_register(BMI270_IF_CONF, 0x20)?;
301 self.write_register(BMI270_PWR_CONF, 0x00)?;
302 self.write_register(BMI270_PWR_CTRL, 0x0E)?;
303 self.write_register(BMI270_AUX_IF_CONF, 0x80)?;
304 self.write_register(BMI270_AUX_DEV_ID, i2c_address << 1)
305 }
306
307 fn aux_write_register(
308 &mut self,
309 register: u8,
310 value: u8,
311 delay: &mut impl DelayNs,
312 ) -> Result<(), Error> {
313 self.write_register(BMI270_AUX_WR_DATA, value)?;
314 self.write_register(BMI270_AUX_WR_ADDR, register)?;
315 self.wait_aux_ready(delay)
316 }
317
318 fn aux_read_register(&mut self, register: u8, delay: &mut impl DelayNs) -> Result<u8, Error> {
319 self.write_register(BMI270_AUX_IF_CONF, 0x80)?;
320 self.write_register(BMI270_AUX_RD_ADDR, register)?;
321 self.wait_aux_ready(delay)?;
322 self.read_register(BMI270_AUX_DATA)
323 }
324
325 fn wait_aux_ready(&mut self, delay: &mut impl DelayNs) -> Result<(), Error> {
326 for _ in 0..3 {
327 if self.read_register(BMI270_STATUS)? & AUX_BUSY_MASK == 0 {
328 return Ok(());
329 }
330 delay.delay_ms(1);
331 }
332 Ok(())
333 }
334
335 fn read_vector(&mut self, start: u8) -> Result<Vector3, Error> {
336 let mut data = [0u8; 6];
337 self.i2c.write_read(self.address, &[start], &mut data)?;
338 Ok(Vector3::new(
339 i32::from(i16::from_le_bytes([data[0], data[1]])),
340 i32::from(i16::from_le_bytes([data[2], data[3]])),
341 i32::from(i16::from_le_bytes([data[4], data[5]])),
342 ))
343 }
344
345 fn read_register(&mut self, register: u8) -> Result<u8, Error> {
346 let mut value = [0u8];
347 self.i2c.write_read(self.address, &[register], &mut value)?;
348 Ok(value[0])
349 }
350
351 fn write_register(&mut self, register: u8, value: u8) -> Result<(), Error> {
352 self.i2c.write(self.address, &[register, value])
353 }
354}
355
356pub struct Bmm150<I2C> {
357 i2c: I2C,
358 address: u8,
359 hard_iron_offset: Vector3,
360}
361
362impl<I2C> Bmm150<I2C> {
363 pub const fn new(i2c: I2C) -> Self {
364 Self {
365 i2c,
366 address: devices::i2c::BMM150_MAGNETOMETER,
367 hard_iron_offset: Vector3::new(0, 0, 0),
368 }
369 }
370
371 pub fn set_hard_iron_offset(&mut self, offset: Vector3) {
372 self.hard_iron_offset = offset;
373 }
374 pub fn release(self) -> I2C {
375 self.i2c
376 }
377}
378
379impl<I2C, Error> Bmm150<I2C>
380where
381 I2C: I2c<Error = Error>,
382{
383 pub fn init(&mut self) -> Result<(), Error> {
384 self.write_register(BMM150_POWER_CONTROL, 0x01)?;
385 self.write_register(BMM150_OP_MODE, 0x00)
386 }
387
388 pub fn chip_id(&mut self) -> Result<u8, Error> {
389 self.read_register(BMM150_CHIP_ID)
390 }
391 pub fn is_expected_chip(&mut self) -> Result<bool, Error> {
392 Ok(self.chip_id()? == BMM150_EXPECTED_CHIP_ID)
393 }
394
395 pub fn magnetic_raw(&mut self) -> Result<Vector3, Error> {
396 let mut data = [0u8; 6];
397 self.i2c
398 .write_read(self.address, &[BMM150_DATA_X_LSB], &mut data)?;
399 let raw = Vector3::new(
400 i32::from(i16::from_le_bytes([data[0] & 0xF8, data[1]]) >> 3),
401 i32::from(i16::from_le_bytes([data[2] & 0xF8, data[3]]) >> 3),
402 i32::from(i16::from_le_bytes([data[4] & 0xFE, data[5]]) >> 1),
403 );
404 Ok(raw.offset(self.hard_iron_offset))
405 }
406
407 fn read_register(&mut self, register: u8) -> Result<u8, Error> {
408 let mut value = [0u8];
409 self.i2c.write_read(self.address, &[register], &mut value)?;
410 Ok(value[0])
411 }
412
413 fn write_register(&mut self, register: u8, value: u8) -> Result<(), Error> {
414 self.i2c.write(self.address, &[register, value])
415 }
416}
417
418pub fn motion_detected(previous: Vector3, current: Vector3, threshold: MotionThreshold) -> bool {
419 (current.x - previous.x).abs() >= threshold.accel_delta_mg
420 || (current.y - previous.y).abs() >= threshold.accel_delta_mg
421 || (current.z - previous.z).abs() >= threshold.accel_delta_mg
422}
423
424pub fn heading_centidegrees(magnetic: Vector3) -> Option<u16> {
426 if magnetic.x == 0 && magnetic.y == 0 {
427 return None;
428 }
429 let angle = atan2_centidegrees(magnetic.y, magnetic.x);
430 Some(if angle < 0 {
431 (angle + 36_000) as u16
432 } else {
433 angle as u16
434 })
435}
436
437fn sample_rate_code(rate: SampleRate) -> u8 {
438 match rate {
439 SampleRate::Hz25 => 0x06,
440 SampleRate::Hz50 => 0x07,
441 SampleRate::Hz100 => 0x08,
442 SampleRate::Hz200 => 0x09,
443 SampleRate::Hz400 => 0x0A,
444 }
445}
446
447fn accel_conf_code(rate: SampleRate) -> u8 {
448 0xA0 | sample_rate_code(rate)
449}
450
451fn gyro_conf_code(rate: SampleRate) -> u8 {
452 0xA0 | sample_rate_code(rate)
453}
454fn accel_range_code(range: AccelRange) -> u8 {
455 match range {
456 AccelRange::G2 => 0x00,
457 AccelRange::G4 => 0x01,
458 AccelRange::G8 => 0x02,
459 AccelRange::G16 => 0x03,
460 }
461}
462fn decode_bmm150_mag_data(data: [u8; 8]) -> Vector3 {
463 Vector3::new(
464 i32::from(i16::from_le_bytes([data[0], data[1]]) >> 2),
465 i32::from(i16::from_le_bytes([data[2], data[3]]) >> 2),
466 i32::from(i16::from_le_bytes([data[4], data[5]]) & !1),
467 )
468}
469
470fn gyro_range_code(range: GyroRange) -> u8 {
471 match range {
472 GyroRange::Dps2000 => 0x00,
473 GyroRange::Dps1000 => 0x01,
474 GyroRange::Dps500 => 0x02,
475 GyroRange::Dps250 => 0x03,
476 GyroRange::Dps125 => 0x04,
477 }
478}
479
480fn atan2_centidegrees(y: i32, x: i32) -> i32 {
481 let abs_y = y.abs();
483 let angle = if x >= 0 {
484 let r = ((x - abs_y) * 1000) / (x + abs_y).max(1);
485 4500 - (4500 * r / 1000)
486 } else {
487 let r = ((x + abs_y) * 1000) / (abs_y - x).max(1);
488 13_500 - (4500 * r / 1000)
489 };
490 if y < 0 { -angle } else { angle }
491}
492
493#[cfg(test)]
494mod tests {
495 use super::*;
496
497 #[test]
498 fn detects_motion_threshold() {
499 assert!(motion_detected(
500 Vector3::new(0, 0, 0),
501 Vector3::new(101, 0, 0),
502 MotionThreshold {
503 accel_delta_mg: 100
504 }
505 ));
506 assert!(!motion_detected(
507 Vector3::new(0, 0, 0),
508 Vector3::new(10, 0, 0),
509 MotionThreshold {
510 accel_delta_mg: 100
511 }
512 ));
513 }
514
515 #[test]
516 fn heading_handles_cardinal_directions() {
517 assert_eq!(heading_centidegrees(Vector3::new(1, 0, 0)), Some(0));
518 assert_eq!(heading_centidegrees(Vector3::new(0, 1, 0)), Some(9000));
519 }
520
521 #[test]
522 fn decodes_bmm150_aux_window() {
523 let data = [0x04, 0x00, 0x08, 0x00, 0x06, 0x00, 0x00, 0x00];
524 assert_eq!(decode_bmm150_mag_data(data), Vector3::new(1, 2, 6));
525 }
526}