From 81a8241e0c0c7d7860f91600801394c32c4aa800 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Schulz-Andres?= Date: Wed, 19 Mar 2025 18:49:41 +0100 Subject: [PATCH 01/10] ignore dead register to suppress warning --- src/registers.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/registers.rs b/src/registers.rs index b7cf67b..e779cf1 100644 --- a/src/registers.rs +++ b/src/registers.rs @@ -15,6 +15,7 @@ impl Registers { pub const INTERNAL_STATUS: u8 = 0x21; pub const TEMPERATURE_0: u8 = 0x22; pub const FIFO_LENGTH_0: u8 = 0x24; + #[allow(dead_code)] pub const FIFO_DATA: u8 = 0x26; pub const ACC_CONF: u8 = 0x40; pub const ACC_RANGE: u8 = 0x41; From cd45069a4b42c2fb5a86f70334c1e1524810202b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Schulz-Andres?= Date: Wed, 19 Mar 2025 18:59:40 +0100 Subject: [PATCH 02/10] update impl signature to take delay and buffer size --- src/bmi2.rs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/bmi2.rs b/src/bmi2.rs index ce4515b..d0e5be6 100644 --- a/src/bmi2.rs +++ b/src/bmi2.rs @@ -1,4 +1,5 @@ use fixedvec::FixedVec; +use embedded_hal::delay::DelayNs; use crate::interface::{I2cAddr, I2cInterface, ReadData, SpiInterface, WriteData}; use crate::registers::Registers; @@ -11,20 +12,24 @@ use crate::types::{ Saturation, Status, WristGestureActivity, FIFO_LENGTH_1_MASK, }; -pub struct Bmi2 { +pub struct Bmi2 { iface: I, max_burst: u16, + buffer: [u8; N], + delay: D, } -impl Bmi2> { +impl Bmi2, D, N> { /// Create a new Bmi270 device with I2C communication. - pub fn new_i2c(i2c: I2C, address: I2cAddr, burst: Burst) -> Self { + pub fn new_i2c(i2c: I2C, delay: D, address: I2cAddr, burst: Burst) -> Self { Bmi2 { iface: I2cInterface { i2c, address: address.addr(), }, max_burst: burst.val(), + buffer: [0; N], + delay, } } @@ -34,12 +39,17 @@ impl Bmi2> { } } -impl Bmi2> { +impl Bmi2, D, N> +where + D: embedded_hal::delay::DelayNs +{ /// Create a new Bmi270 device with SPI communication. - pub fn new_spi(spi: SPI, burst: Burst) -> Self { + pub fn new_spi(spi: SPI, delay: D, burst: Burst) -> Self { Bmi2 { iface: SpiInterface { spi }, max_burst: burst.val(), + buffer: [0; N], + delay, } } @@ -49,9 +59,10 @@ impl Bmi2> { } } -impl Bmi2 +impl Bmi2 where I: ReadData> + WriteData>, + D: embedded_hal::delay::DelayNs, // Add constraint for D { /// Get the chip id. pub fn get_chip_id(&mut self) -> Result> { From ff33ce9cb97523222c22ce25a170c8e037ff75ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Schulz-Andres?= Date: Wed, 19 Mar 2025 19:01:57 +0100 Subject: [PATCH 03/10] Update Readme.md to reflect new interface --- README.md | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 2c4ea28..cb5597c 100644 --- a/README.md +++ b/README.md @@ -8,32 +8,39 @@ This is an [embedded-hal](https://github.com/rust-embedded/embedded-hal) driver ```rust // ... - use bmi2::Bmi2; use bmi2::config; use bmi2::{types::Burst, I2cAddr, types::PwrCtrl}; +use embedded_hal::delay::DelayNs; + +// Specify your delay type, for example: +let delay = MyDelay::new(); // Replace with your actual delay implementation + +// Choose a buffer size (e.g., 512 bytes), needs to be >= max data burst +const BUFFER_SIZE: usize = 512; /// Create a new Bmi2 device using I2C with its alternative address (0x69). /// Configure the max data burst to 255 bytes: /// - used for the upload of the configuration during initialization. -/// - This is a limitation from your device or its HAL. -let mut bmi = Bmi2::new_i2c(i2c, I2cAddr::Alternative, Burst::Other(255)); +/// - This is a limitation from your device or its HAL. +let mut bmi = Bmi2::<_, _, BUFFER_SIZE>::new_i2c( + i2c, + I2cAddr::Alternative, + Burst::Other(255), + delay +); /// Get the chip id. Should be 0x24 or 36 in decimal let chip_id = bmi.get_chip_id().unwrap(); - /// Initialize the senor. /// During this process a configuration of > 8kB is uploaded to the sensor. /// Alternatively, for the BMI260 call its dedicated config: /// bmi.init(&config::BMI260_CONFIG_FILE).unwrap(); bmi.init(&config::BMI270_CONFIG_FILE).unwrap(); - /// Enable power for the accelerometer and the gyroscope. let pwr_ctrl = PwrCtrl { aux_en: false, gyr_en: true, acc_en: true, temp_en: false }; bmi.set_pwr_ctrl(pwr_ctrl).unwrap(); - /// Read the raw data let data = bmi.get_data().unwrap(); - // ... ``` From acd02dac92b35eb3d3e7d788f937c7306fada80e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Schulz-Andres?= Date: Wed, 19 Mar 2025 19:11:54 +0100 Subject: [PATCH 04/10] implement more flexible bursts --- src/types.rs | 41 ++++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/src/types.rs b/src/types.rs index 7857382..84f1e1e 100644 --- a/src/types.rs +++ b/src/types.rs @@ -8,28 +8,51 @@ pub enum Error { } /// Data burst. -pub enum Burst { - /// Burst of 512 bytes. +pub struct Burst { + max: u16, + kind: BurstKind, +} + +enum BurstKind { Max, - /// An other burst amount under 512 bytes. Other(u16), } -impl Default for Burst { +impl Default for BurstKind { fn default() -> Self { - Burst::Max + Self::Max } } impl Burst { - pub fn val(self) -> u16 { - match self { - Burst::Max => 512, - Burst::Other(v) => v % 512, + pub fn new(max: u16) -> Self { + Self { + max, + kind: BurstKind::default(), + } + } + + pub fn custom(max: u16, value: u16) -> Self { + Self { + max, + kind: BurstKind::Other(value), + } + } + + pub fn val(&self) -> u16 { + match self.kind { + BurstKind::Max => self.max, + BurstKind::Other(v) => v.min(self.max), } } } +impl Default for Burst { + fn default() -> Self { + Self::new(512) + } +} + pub struct ErrRegMask; impl ErrRegMask { pub const AUX_ERR: u8 = 1 << 7; From 02c2dfafc2043cbb3a0d4cacb673b4faa9f80032 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Schulz-Andres?= Date: Thu, 20 Mar 2025 13:40:20 +0100 Subject: [PATCH 05/10] allow config of prealloc --- src/bmi2.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/bmi2.rs b/src/bmi2.rs index d0e5be6..942a1f1 100644 --- a/src/bmi2.rs +++ b/src/bmi2.rs @@ -15,7 +15,6 @@ use crate::types::{ pub struct Bmi2 { iface: I, max_burst: u16, - buffer: [u8; N], delay: D, } @@ -28,7 +27,6 @@ impl Bmi2, D, N> { address: address.addr(), }, max_burst: burst.val(), - buffer: [0; N], delay, } } @@ -48,7 +46,6 @@ where Bmi2 { iface: SpiInterface { spi }, max_burst: burst.val(), - buffer: [0; N], delay, } } @@ -722,8 +719,7 @@ where pwr_conf.power_save = false; self.set_pwr_conf(pwr_conf)?; - // TODO allow config of pre alloc - let mut preallocated_space = alloc_stack!([u8; 512]); + let mut preallocated_space = alloc_stack!([u8; N]); let mut vec = FixedVec::new(&mut preallocated_space); let mut offset = 0u16; From fdb6fefccd29bcf5b3655d97a8ce8fe8729f9b28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Schulz-Andres?= Date: Thu, 20 Mar 2025 13:45:10 +0100 Subject: [PATCH 06/10] add power_save convenience functions --- src/bmi2.rs | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/bmi2.rs b/src/bmi2.rs index 942a1f1..db11549 100644 --- a/src/bmi2.rs +++ b/src/bmi2.rs @@ -711,13 +711,32 @@ where Ok(()) } + /// Disable power save mode. + pub fn disable_power_save(&mut self) -> Result<(), Error> { + let mut pwr_conf = self.get_pwr_conf()?; + pwr_conf.power_save = false; + self.set_pwr_conf(pwr_conf)?; + // Critical delay after disabling power save + self.delay.delay_us(450); + Ok(()) + } + + /// Enable power save mode. + pub fn enable_power_save(&mut self) -> Result<(), Error> { + let mut pwr_conf = self.get_pwr_conf()?; + pwr_conf.power_save = true; + self.set_pwr_conf(pwr_conf)?; + // Critical delay after enabling power save + self.delay.delay_us(450); + Ok(()) + } + + /// Initialize sensor. pub fn init(&mut self, config_file: &[u8]) -> Result<(), Error> { // Disable advanced power mode - let mut pwr_conf = self.get_pwr_conf()?; - pwr_conf.power_save = false; - self.set_pwr_conf(pwr_conf)?; + self.disable_power_save()?; let mut preallocated_space = alloc_stack!([u8; N]); let mut vec = FixedVec::new(&mut preallocated_space); From da0cf65f0f5a362d11cd26eb30280e7959eaffe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Schulz-Andres?= Date: Thu, 20 Mar 2025 14:09:58 +0100 Subject: [PATCH 07/10] add more errors and chip id constants --- src/types.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/types.rs b/src/types.rs index 84f1e1e..93f9032 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,3 +1,7 @@ +pub const BMI160_CHIP_ID: u8 = 0xD1; +pub const BMI260_CHIP_ID: u8 = 0x27; +pub const BMI270_CHIP_ID: u8 = 0x24; + /// The possible errors that could be encountered. #[derive(Debug)] pub enum Error { @@ -5,6 +9,12 @@ pub enum Error { Comm(CommE), /// Memory allocation error during initialization. Alloc, + /// Invalid Chip Id. + InvalidChipId, + /// Buffer too small. + BufferTooSmall, + /// Initialization Failed. + InitFailed, } /// Data burst. From 8424243efd158e1326d48b7474abd18bd1772221 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Schulz-Andres?= Date: Thu, 20 Mar 2025 14:11:10 +0100 Subject: [PATCH 08/10] more careful initialization --- src/bmi2.rs | 85 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 63 insertions(+), 22 deletions(-) diff --git a/src/bmi2.rs b/src/bmi2.rs index db11549..f154496 100644 --- a/src/bmi2.rs +++ b/src/bmi2.rs @@ -5,11 +5,7 @@ use crate::interface::{I2cAddr, I2cInterface, ReadData, SpiInterface, WriteData} use crate::registers::Registers; use crate::types::{ - AccConf, AccOffsets, AccRange, AccSelfTest, AuxConf, AuxData, AuxIfConf, AxisData, Burst, Cmd, - Data, Drv, Error, ErrorReg, ErrorRegMsk, Event, FifoConf, FifoDowns, GyrConf, GyrCrtConf, - GyrOffsets, GyrRange, GyrSelfTest, IfConf, IntIoCtrl, IntLatch, IntMapData, IntMapFeat, - InternalError, InternalStatus, InterruptStatus, NvConf, PullUpConf, PwrConf, PwrCtrl, - Saturation, Status, WristGestureActivity, FIFO_LENGTH_1_MASK, + AccConf, AccOffsets, AccRange, AccSelfTest, AuxConf, AuxData, AuxIfConf, AxisData, Burst, Cmd, Data, Drv, Error, ErrorReg, ErrorRegMsk, Event, FifoConf, FifoDowns, GyrConf, GyrCrtConf, GyrOffsets, GyrRange, GyrSelfTest, IfConf, IntIoCtrl, IntLatch, IntMapData, IntMapFeat, InternalError, InternalStatus, InterruptStatus, NvConf, PullUpConf, PwrConf, PwrCtrl, Saturation, Status, WristGestureActivity, BMI160_CHIP_ID, BMI260_CHIP_ID, BMI270_CHIP_ID, FIFO_LENGTH_1_MASK, }; pub struct Bmi2 { @@ -39,7 +35,7 @@ impl Bmi2, D, N> { impl Bmi2, D, N> where - D: embedded_hal::delay::DelayNs + D: DelayNs { /// Create a new Bmi270 device with SPI communication. pub fn new_spi(spi: SPI, delay: D, burst: Burst) -> Self { @@ -734,41 +730,86 @@ where /// Initialize sensor. pub fn init(&mut self, config_file: &[u8]) -> Result<(), Error> { + // Verify chip ID first + let chip_id = self.iface.read_reg(Registers::CHIP_ID)?; + if !(chip_id == BMI160_CHIP_ID || chip_id == BMI260_CHIP_ID || chip_id == BMI270_CHIP_ID) { + return Err(Error::InvalidChipId); + } + + // Reset Chip, mandatory per datasheet + self.send_cmd(Cmd::SoftReset)?; + self.delay.delay_us(2000); // Disable advanced power mode self.disable_power_save()?; + // Verify buffer size + if self.max_burst as usize > N { + return Err(Error::::BufferTooSmall); + } + let mut preallocated_space = alloc_stack!([u8; N]); let mut vec = FixedVec::new(&mut preallocated_space); + // Offset and burst calculation let mut offset = 0u16; let max_len = config_file.len() as u16; - let burst = self.max_burst - 1; // Remove 1 for address byte - - self.set_init_ctrl(0)?; + let burst = if self.max_burst % 2 == 0 { + self.max_burst - 1 // Address byte + even number of data bytes + } else { + self.max_burst - 2 // Make sure we have even data bytes + }; + + let init_ctrl= self.get_init_ctrl()?; + self.set_init_ctrl(init_ctrl & 0b1111_1110)?; + self.delay.delay_us(450); while offset < max_len { - self.set_init_addr(offset / 2)?; - - let end = if (offset + burst) > max_len { - max_len + // INIT_ADDR should point to 16-bit words + self.set_init_addr(offset / 2)?; // needs to be divided by 2 because offset is in bytes + + // Ensure we're writing complete 16-bit words + let mut chunk_size = burst; + if (chunk_size % 2) != 0 { + // If burst size would result in odd number of bytes, reduce by 1 + chunk_size -= 1; + } + + let end = if (offset + chunk_size) > max_len { + // For the last chunk, ensure we still write complete 16-bit words + let remaining = max_len - offset; + offset + (remaining - (remaining % 2)) } else { - offset + burst + offset + chunk_size }; - + + vec.clear(); vec.push(Registers::INIT_DATA) - .map_err(|_| Error::::Alloc)?; - + .map_err(|_| Error::Alloc)?; + vec.push_all(&config_file[offset as usize..end as usize]) - .map_err(|_| Error::::Alloc)?; - + .map_err(|_| Error::Alloc)?; + self.iface.write(&mut vec.as_mut_slice())?; - - offset += burst; - vec.clear(); + + offset += chunk_size; + self.delay.delay_us(2); } + // This operation must not be performed more than once after POR or soft reset. self.set_init_ctrl(1)?; + self.delay.delay_us(2); + + self.enable_power_save()?; + + // Initialization takes at most 20ms per datasheet + self.delay.delay_us(20_000); + + let internal_status = self.iface.read_reg(Registers::INTERNAL_STATUS)?; + + if internal_status & 0b0000_0001 != 1 { + return Err(Error::InitFailed); + } Ok(()) } From 68069f13c96af29e5a3b13a94e5d9e5dbbe188f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Schulz-Andres?= Date: Thu, 20 Mar 2025 14:30:25 +0100 Subject: [PATCH 09/10] cargo clippy fix --- src/bmi2.rs | 2 +- src/interface.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/bmi2.rs b/src/bmi2.rs index f154496..bdbf875 100644 --- a/src/bmi2.rs +++ b/src/bmi2.rs @@ -790,7 +790,7 @@ where vec.push_all(&config_file[offset as usize..end as usize]) .map_err(|_| Error::Alloc)?; - self.iface.write(&mut vec.as_mut_slice())?; + self.iface.write(vec.as_mut_slice())?; offset += chunk_size; self.delay.delay_us(2); diff --git a/src/interface.rs b/src/interface.rs index 677f390..232d92d 100644 --- a/src/interface.rs +++ b/src/interface.rs @@ -75,18 +75,18 @@ where payload[0] += 0x80; // `write` asserts and deasserts CS for us. No need to do it manually! - let res = self.spi.write(&payload).map_err(Error::Comm); + - res + self.spi.write(payload).map_err(Error::Comm) } fn write_reg(&mut self, register: u8, data: u8) -> Result<(), Self::Error> { let payload: [u8; 2] = [register + 0x80, data]; // `write` asserts and deasserts CS for us. No need to do it manually! - let res = self.spi.write(&payload).map_err(Error::Comm); + - res + self.spi.write(&payload).map_err(Error::Comm) } } From 935406d55bfb15531753271dccddf0346fe6862d Mon Sep 17 00:00:00 2001 From: Joel Schulz-Andres Date: Thu, 27 Mar 2025 11:16:32 +0100 Subject: [PATCH 10/10] update Readme.md and basic_read example with new function signature --- README.md | 4 ++-- examples/nrf52840/basic_read/src/main.rs | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cb5597c..a606784 100644 --- a/README.md +++ b/README.md @@ -25,9 +25,9 @@ const BUFFER_SIZE: usize = 512; /// - This is a limitation from your device or its HAL. let mut bmi = Bmi2::<_, _, BUFFER_SIZE>::new_i2c( i2c, + delay, I2cAddr::Alternative, - Burst::Other(255), - delay + Burst::new(255), ); /// Get the chip id. Should be 0x24 or 36 in decimal diff --git a/examples/nrf52840/basic_read/src/main.rs b/examples/nrf52840/basic_read/src/main.rs index 47f7ada..6d403ca 100644 --- a/examples/nrf52840/basic_read/src/main.rs +++ b/examples/nrf52840/basic_read/src/main.rs @@ -11,6 +11,7 @@ use nrf52840_hal::{ pac, timer::Timer, twim::{self, Twim}, + Delay, }; use defmt_rtt as _; @@ -22,6 +23,7 @@ use bmi2::{types::Burst, types::PwrCtrl, I2cAddr}; #[entry] fn main() -> ! { let p = pac::Peripherals::take().unwrap(); + let core = pac::CorePeripherals::take().unwrap(); let mut timer = Timer::new(p.TIMER0); let port0 = p0::Parts::new(p.P0); @@ -33,7 +35,11 @@ fn main() -> ! { let i2c = Twim::new(p.TWIM0, twim_pins, twim::Frequency::K100); - let mut bmi = Bmi2::new_i2c(i2c, I2cAddr::Alternative, Burst::Other(255)); + let delay = Delay::new(core.SYST); + + const BUFFER_SIZE: usize = 256; + + let mut bmi = Bmi2::<_, _, BUFFER_SIZE>::new_i2c(i2c, delay, I2cAddr::Alternative, Burst::new(255)); let chip_id = bmi.get_chip_id().unwrap(); defmt::info!("chip id: {}", chip_id);