1#![cfg_attr(not(feature = "std"), no_std)]
31#![allow(dead_code)]
32
33use core::fmt::Debug;
34
35pub use cu_sensor_payloads::ImuPayload;
36use cu29::prelude::*;
37use embedded_hal::blocking::delay::DelayMs;
38use embedded_hal::blocking::spi::Transfer;
39use embedded_hal::digital::v2::OutputPin;
40
41const BMI088_ACC_CHIP_ID: u8 = 0x1E;
43const BMI088_GYR_CHIP_ID: u8 = 0x0F;
44
45const BMI088_ACC_REG_CHIP_ID: u8 = 0x00;
47const BMI088_ACC_REG_ACCEL_X_LSB: u8 = 0x12;
48const BMI088_ACC_REG_TEMP_MSB: u8 = 0x22;
49const BMI088_ACC_REG_TEMP_LSB: u8 = 0x23;
50const BMI088_ACC_REG_ACC_RANGE: u8 = 0x41;
51const BMI088_ACC_REG_PWR_CONF: u8 = 0x7C;
52const BMI088_ACC_REG_PWR_CTRL: u8 = 0x7D;
53const BMI088_ACC_REG_SOFT_RESET: u8 = 0x7E;
54
55const BMI088_GYR_REG_CHIP_ID: u8 = 0x00;
57const BMI088_GYR_REG_X_LSB: u8 = 0x02;
58const BMI088_GYR_REG_RANGE: u8 = 0x0F;
59const BMI088_GYR_REG_BANDWIDTH: u8 = 0x10;
60const BMI088_GYR_REG_POWER_MODE: u8 = 0x11;
61const BMI088_GYR_REG_SOFT_RESET: u8 = 0x14;
62
63const BMI088_SOFT_RESET_CMD: u8 = 0xB6;
65const BMI088_ACC_PWR_CONF_ACTIVE: u8 = 0x00;
66const BMI088_ACC_PWR_CTRL_ON: u8 = 0x04;
67const BMI088_GYR_RANGE_2000: u8 = 0x00;
68const BMI088_GYR_BANDWIDTH: u8 = 0x07; const BMI088_GYR_PWR_NORMAL: u8 = 0x00;
70
71const GYRO_RAD_PER_LSB: f32 = (2000.0 / 32_768.0) * (core::f32::consts::PI / 180.0);
73
74resources!(for <SPI, ACC, GYR, D>
81where
82 SPI: Transfer<u8> + Send + Sync + 'static,
83 SPI::Error: Debug + Send + 'static,
84 ACC: OutputPin + Send + Sync + 'static,
85 ACC::Error: Debug + Send + 'static,
86 GYR: OutputPin + Send + Sync + 'static,
87 GYR::Error: Debug + Send + 'static,
88 D: DelayMs<u32> + Send + Sync + 'static,
89{
90 spi => Owned<SPI>,
91 acc_cs => Owned<ACC>,
92 gyr_cs => Owned<GYR>,
93 delay => Owned<D>,
94});
95
96#[derive(Reflect)]
109#[reflect(no_field_bounds, from_reflect = false, type_path = false)]
110pub struct Bmi088Source<SPI, ACC, GYR, D> {
111 #[reflect(ignore)]
112 driver: Bmi088Driver<SPI, ACC, GYR, D>,
113}
114
115impl<SPI, ACC, GYR, D> TypePath for Bmi088Source<SPI, ACC, GYR, D>
116where
117 SPI: 'static,
118 ACC: 'static,
119 GYR: 'static,
120 D: 'static,
121{
122 fn type_path() -> &'static str {
123 "cu_bmi088::Bmi088Source"
124 }
125
126 fn short_type_path() -> &'static str {
127 "Bmi088Source"
128 }
129
130 fn type_ident() -> Option<&'static str> {
131 Some("Bmi088Source")
132 }
133
134 fn crate_name() -> Option<&'static str> {
135 Some("cu_bmi088")
136 }
137
138 fn module_path() -> Option<&'static str> {
139 Some("")
140 }
141}
142
143impl<SPI, ACC, GYR, D> Freezable for Bmi088Source<SPI, ACC, GYR, D>
144where
145 SPI: Transfer<u8> + Send + Sync + 'static,
146 SPI::Error: Debug + Send + 'static,
147 ACC: OutputPin + Send + Sync + 'static,
148 ACC::Error: Debug + Send + 'static,
149 GYR: OutputPin + Send + Sync + 'static,
150 GYR::Error: Debug + Send + 'static,
151 D: DelayMs<u32> + Send + Sync + 'static,
152{
153}
154
155impl<SPI, ACC, GYR, D> CuSrcTask for Bmi088Source<SPI, ACC, GYR, D>
156where
157 SPI: Transfer<u8> + Send + Sync + 'static,
158 SPI::Error: Debug + Send + 'static,
159 ACC: OutputPin + Send + Sync + 'static,
160 ACC::Error: Debug + Send + 'static,
161 GYR: OutputPin + Send + Sync + 'static,
162 GYR::Error: Debug + Send + 'static,
163 D: DelayMs<u32> + Send + Sync + 'static,
164{
165 type Resources<'r> = Resources<SPI, ACC, GYR, D>;
166 type Output<'m> = output_msg!(ImuPayload);
167
168 fn new(_config: Option<&ComponentConfig>, resources: Self::Resources<'_>) -> CuResult<Self>
169 where
170 Self: Sized,
171 {
172 let driver = Bmi088Driver::new(
173 resources.spi.0,
174 resources.acc_cs.0,
175 resources.gyr_cs.0,
176 resources.delay.0,
177 )?;
178 Ok(Self { driver })
179 }
180
181 fn start(&mut self, _ctx: &CuContext) -> CuResult<()> {
182 Ok(())
183 }
184
185 fn process<'o>(&mut self, ctx: &CuContext, output: &mut Self::Output<'o>) -> CuResult<()> {
186 let tov = ctx.now();
187 let payload = self.driver.read_measure()?;
188 output.tov = Tov::Time(tov);
189 output.set_payload(payload);
190 Ok(())
191 }
192}
193
194struct Bmi088Driver<SPI, ACC, GYR, D> {
196 spi: SPI,
197 acc_cs: ACC,
198 gyr_cs: GYR,
199 #[allow(dead_code)]
200 delay: D,
201 acc_mps2_per_lsb: f32,
202}
203
204impl<SPI, ACC, GYR, D> Bmi088Driver<SPI, ACC, GYR, D>
205where
206 SPI: Transfer<u8>,
207 SPI::Error: Debug,
208 ACC: OutputPin,
209 ACC::Error: Debug,
210 GYR: OutputPin,
211 GYR::Error: Debug,
212 D: DelayMs<u32>,
213{
214 fn new(mut spi: SPI, mut acc_cs: ACC, mut gyr_cs: GYR, mut delay: D) -> CuResult<Self> {
222 acc_cs
224 .set_high()
225 .map_err(|err| map_error("bmi088 acc cs high", err))?;
226 gyr_cs
227 .set_high()
228 .map_err(|err| map_error("bmi088 gyr cs high", err))?;
229
230 let acc_id = spi_read_reg_2(&mut spi, &mut acc_cs, BMI088_ACC_REG_CHIP_ID)
232 .map_err(|err| map_error("bmi088 acc chip id", err))?;
233 let gyr_id = spi_read_reg_1(&mut spi, &mut gyr_cs, BMI088_GYR_REG_CHIP_ID)
234 .map_err(|err| map_error("bmi088 gyr chip id", err))?;
235
236 if acc_id != BMI088_ACC_CHIP_ID {
237 return Err(CuError::from("bmi088 accel id mismatch"));
238 }
239 if gyr_id != BMI088_GYR_CHIP_ID {
240 return Err(CuError::from("bmi088 gyro id mismatch"));
241 }
242
243 spi_write_reg(
245 &mut spi,
246 &mut acc_cs,
247 BMI088_ACC_REG_SOFT_RESET,
248 BMI088_SOFT_RESET_CMD,
249 )
250 .map_err(|err| map_error("bmi088 acc reset", err))?;
251 delay.delay_ms(10);
252
253 spi_write_reg(
255 &mut spi,
256 &mut acc_cs,
257 BMI088_ACC_REG_PWR_CONF,
258 BMI088_ACC_PWR_CONF_ACTIVE,
259 )
260 .map_err(|err| map_error("bmi088 acc pwr conf", err))?;
261 delay.delay_ms(1);
262 spi_write_reg(
263 &mut spi,
264 &mut acc_cs,
265 BMI088_ACC_REG_PWR_CTRL,
266 BMI088_ACC_PWR_CTRL_ON,
267 )
268 .map_err(|err| map_error("bmi088 acc pwr ctrl", err))?;
269 delay.delay_ms(50);
270
271 spi_write_reg(
273 &mut spi,
274 &mut gyr_cs,
275 BMI088_GYR_REG_SOFT_RESET,
276 BMI088_SOFT_RESET_CMD,
277 )
278 .map_err(|err| map_error("bmi088 gyro reset", err))?;
279 delay.delay_ms(100);
280 spi_write_reg(
281 &mut spi,
282 &mut gyr_cs,
283 BMI088_GYR_REG_RANGE,
284 BMI088_GYR_RANGE_2000,
285 )
286 .map_err(|err| map_error("bmi088 gyro range", err))?;
287 spi_write_reg(
288 &mut spi,
289 &mut gyr_cs,
290 BMI088_GYR_REG_BANDWIDTH,
291 BMI088_GYR_BANDWIDTH,
292 )
293 .map_err(|err| map_error("bmi088 gyro bandwidth", err))?;
294 spi_write_reg(
295 &mut spi,
296 &mut gyr_cs,
297 BMI088_GYR_REG_POWER_MODE,
298 BMI088_GYR_PWR_NORMAL,
299 )
300 .map_err(|err| map_error("bmi088 gyro pwr mode", err))?;
301
302 let acc_range_reg = spi_read_reg_2(&mut spi, &mut acc_cs, BMI088_ACC_REG_ACC_RANGE)
304 .map_err(|err| map_error("bmi088 acc range", err))?;
305 let acc_range_g = accel_range_g_from_reg(acc_range_reg);
306 let acc_mps2_per_lsb = acc_range_g * 9.806_65 / 32_768.0;
307 debug!(
308 "bmi088 accel range reg={} -> ±{}g",
309 acc_range_reg, acc_range_g
310 );
311
312 Ok(Self {
313 spi,
314 acc_cs,
315 gyr_cs,
316 delay,
317 acc_mps2_per_lsb,
318 })
319 }
320
321 fn read_measure(&mut self) -> CuResult<ImuPayload> {
328 let mut acc_buf = [0_u8; 6];
330 spi_read_burst_2(
331 &mut self.spi,
332 &mut self.acc_cs,
333 BMI088_ACC_REG_ACCEL_X_LSB,
334 &mut acc_buf,
335 )
336 .map_err(|err| map_error("bmi088 acc burst", err))?;
337 let ax = bytes_to_i16(acc_buf[0], acc_buf[1]);
338 let ay = bytes_to_i16(acc_buf[2], acc_buf[3]);
339 let az = bytes_to_i16(acc_buf[4], acc_buf[5]);
340
341 let mut gyr_buf = [0_u8; 6];
343 spi_read_burst_1(
344 &mut self.spi,
345 &mut self.gyr_cs,
346 BMI088_GYR_REG_X_LSB,
347 &mut gyr_buf,
348 )
349 .map_err(|err| map_error("bmi088 gyro burst", err))?;
350 let gx = bytes_to_i16(gyr_buf[0], gyr_buf[1]);
351 let gy = bytes_to_i16(gyr_buf[2], gyr_buf[3]);
352 let gz = bytes_to_i16(gyr_buf[4], gyr_buf[5]);
353
354 let temp_msb = spi_read_reg_2(&mut self.spi, &mut self.acc_cs, BMI088_ACC_REG_TEMP_MSB)
356 .map_err(|err| map_error("bmi088 temp msb", err))?;
357 let temp_lsb = spi_read_reg_2(&mut self.spi, &mut self.acc_cs, BMI088_ACC_REG_TEMP_LSB)
358 .map_err(|err| map_error("bmi088 temp lsb", err))?;
359 let temp_raw = (temp_msb as i16) * 8 + (temp_lsb as i16) / 32;
360 let temp_c = (temp_raw as f32) * 0.125 + 23.0;
361
362 let accel_mps2 = [
365 accel_raw_to_mps2(ay, self.acc_mps2_per_lsb),
366 accel_raw_to_mps2(ax, self.acc_mps2_per_lsb),
367 accel_raw_to_mps2(az, self.acc_mps2_per_lsb),
368 ];
369 let gyro_rad = [
370 -gyro_raw_to_rad(gy),
371 -gyro_raw_to_rad(gx),
372 -gyro_raw_to_rad(gz),
373 ];
374
375 Ok(ImuPayload::from_raw(accel_mps2, gyro_rad, temp_c))
376 }
377}
378
379#[derive(Debug)]
382enum SpiCsError<SpiErr, CsErr> {
383 Spi(SpiErr),
384 Cs(CsErr),
385}
386
387fn map_error<E: Debug>(context: &'static str, _err: E) -> CuError {
388 CuError::from(context)
389}
390
391fn spi_transfer<SPI, CS>(
392 spi: &mut SPI,
393 cs: &mut CS,
394 buf: &mut [u8],
395) -> Result<(), SpiCsError<SPI::Error, CS::Error>>
396where
397 SPI: Transfer<u8>,
398 CS: OutputPin,
399{
400 cs.set_low().map_err(SpiCsError::Cs)?;
401 let transfer_res = spi.transfer(buf).map_err(SpiCsError::Spi);
402 let cs_res = cs.set_high().map_err(SpiCsError::Cs);
403 if let Err(err) = transfer_res {
404 let _ = cs_res;
405 return Err(err);
406 }
407 cs_res?;
408 Ok(())
409}
410
411fn spi_write_reg<SPI, CS>(
412 spi: &mut SPI,
413 cs: &mut CS,
414 reg: u8,
415 value: u8,
416) -> Result<(), SpiCsError<SPI::Error, CS::Error>>
417where
418 SPI: Transfer<u8>,
419 CS: OutputPin,
420{
421 let mut buf = [reg & 0x7f, value];
422 spi_transfer(spi, cs, &mut buf)
423}
424
425fn spi_read_reg_1<SPI, CS>(
427 spi: &mut SPI,
428 cs: &mut CS,
429 reg: u8,
430) -> Result<u8, SpiCsError<SPI::Error, CS::Error>>
431where
432 SPI: Transfer<u8>,
433 CS: OutputPin,
434{
435 let mut buf = [reg | 0x80, 0x00];
436 spi_transfer(spi, cs, &mut buf)?;
437 Ok(buf[1])
438}
439
440fn spi_read_reg_2<SPI, CS>(
442 spi: &mut SPI,
443 cs: &mut CS,
444 reg: u8,
445) -> Result<u8, SpiCsError<SPI::Error, CS::Error>>
446where
447 SPI: Transfer<u8>,
448 CS: OutputPin,
449{
450 let mut buf = [reg | 0x80, 0x00, 0x00];
451 spi_transfer(spi, cs, &mut buf)?;
452 Ok(buf[2])
453}
454
455fn spi_read_burst_1<SPI, CS>(
457 spi: &mut SPI,
458 cs: &mut CS,
459 reg: u8,
460 out: &mut [u8; 6],
461) -> Result<(), SpiCsError<SPI::Error, CS::Error>>
462where
463 SPI: Transfer<u8>,
464 CS: OutputPin,
465{
466 let mut buf = [0_u8; 7];
467 buf[0] = reg | 0x80;
468 spi_transfer(spi, cs, &mut buf)?;
469 out.copy_from_slice(&buf[1..7]);
470 Ok(())
471}
472
473fn spi_read_burst_2<SPI, CS>(
475 spi: &mut SPI,
476 cs: &mut CS,
477 reg: u8,
478 out: &mut [u8; 6],
479) -> Result<(), SpiCsError<SPI::Error, CS::Error>>
480where
481 SPI: Transfer<u8>,
482 CS: OutputPin,
483{
484 let mut buf = [0_u8; 8];
485 buf[0] = reg | 0x80;
486 spi_transfer(spi, cs, &mut buf)?;
487 out.copy_from_slice(&buf[2..8]);
488 Ok(())
489}
490
491fn bytes_to_i16(lsb: u8, msb: u8) -> i16 {
494 i16::from_le_bytes([lsb, msb])
495}
496
497fn accel_raw_to_mps2(raw: i16, acc_mps2_per_lsb: f32) -> f32 {
498 raw as f32 * acc_mps2_per_lsb
499}
500
501fn gyro_raw_to_rad(raw: i16) -> f32 {
502 raw as f32 * GYRO_RAD_PER_LSB
503}
504
505fn accel_range_g_from_reg(range_reg: u8) -> f32 {
506 match range_reg & 0x03 {
507 0x00 => 3.0,
508 0x01 => 6.0,
509 0x02 => 12.0,
510 _ => 24.0,
511 }
512}