pub const CHIP_ID: u8 = 0x24;
pub const ADDRESSES: [u8; 2] = [0x68, 0x69];
pub mod register {
pub const CHIP_ID: u8 = 0x00;
pub const ACC_DATA: u8 = 0x0C;
pub const GYR_DATA: u8 = 0x12;
pub const INTERNAL_STATUS: u8 = 0x21;
pub const TEMPERATURE: u8 = 0x22;
pub const ACC_CONF: u8 = 0x40;
pub const ACC_RANGE: u8 = 0x41;
pub const GYR_CONF: u8 = 0x42;
pub const GYR_RANGE: u8 = 0x43;
pub const INIT_CTRL: u8 = 0x59;
pub const INIT_ADDR_0: u8 = 0x5B;
pub const INIT_DATA: u8 = 0x5E;
pub const PWR_CONF: u8 = 0x7C;
pub const PWR_CTRL: u8 = 0x7D;
pub const CMD: u8 = 0x7E;
}
pub const SOFT_RESET: u8 = 0xB6;
pub const ADVANCED_POWER_SAVE: u8 = 1 << 0;
pub const GYROSCOPE_ON: u8 = 1 << 1;
pub const ACCELEROMETER_ON: u8 = 1 << 2;
pub const INITIALISED: u8 = 1 << 0;
pub const ACC_RANGE_8G: u8 = 0x02;
pub const GYR_RANGE_2000: u8 = 1 << 3;
const CONF_100HZ: u8 = 0x08 | 0x02 << 4;
const FILTER_PERF: u8 = 1 << 7;
const NOISE_PERF: u8 = 1 << 6;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum ImuMode {
LowPower,
Normal,
#[default]
Performance,
}
impl ImuMode {
pub(crate) const fn acc_conf(self) -> u8 {
match self {
Self::LowPower => CONF_100HZ,
Self::Normal | Self::Performance => CONF_100HZ | FILTER_PERF,
}
}
pub(crate) const fn gyr_conf(self) -> u8 {
match self {
Self::LowPower => CONF_100HZ,
Self::Normal => CONF_100HZ | FILTER_PERF,
Self::Performance => CONF_100HZ | FILTER_PERF | NOISE_PERF,
}
}
}
pub const UPLOAD_CHUNK: usize = 126;
#[expect(
clippy::cast_possible_truncation,
reason = "the address is 12 bits: the configuration is 8 kB, 4096 words"
)]
pub fn upload_chunks(config: &[u8]) -> impl Iterator<Item = ([u8; 2], &[u8])> {
config
.chunks(UPLOAD_CHUNK)
.enumerate()
.map(|(index, chunk)| {
let word = index * UPLOAD_CHUNK / 2;
let address = [(word & 0x0F) as u8, (word >> 4) as u8];
(address, chunk)
})
}
pub fn axes(data: [u8; 6]) -> [i32; 3] {
let [x0, x1, y0, y1, z0, z1] = data;
[[x0, x1], [y0, y1], [z0, z1]].map(|bytes| i32::from(i16::from_le_bytes(bytes)))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_configuration_goes_in_word_addressed_chunks() {
let config: [u8; 8192] = core::array::from_fn(|index| index.to_le_bytes()[0]);
let mut chunks = upload_chunks(&config);
assert_eq!(chunks.next(), Some(([0, 0], &config[..UPLOAD_CHUNK])));
assert_eq!(
chunks.next(),
Some(([0x0F, 0x03], &config[UPLOAD_CHUNK..2 * UPLOAD_CHUNK]))
);
assert_eq!(upload_chunks(&config).count(), 66);
assert_eq!(chunks.last(), Some(([0x0F, 0xFF], &config[8190..])));
}
#[test]
fn a_reading_is_three_little_endian_axes() {
assert_eq!(
axes([0x00, 0x10, 0xFF, 0xFF, 0x00, 0x80]),
[4096, -1, -32768]
);
}
#[test]
fn the_settings_are_the_datasheets() {
assert_eq!(ImuMode::Performance.acc_conf(), 0xA8);
assert_eq!(ImuMode::Performance.gyr_conf(), 0xE8);
assert_eq!(ImuMode::Normal.acc_conf(), 0xA8);
assert_eq!(ImuMode::Normal.gyr_conf(), 0xA8);
assert_eq!(ImuMode::LowPower.acc_conf(), 0x28);
assert_eq!(ImuMode::LowPower.gyr_conf(), 0x28);
assert_eq!(GYR_RANGE_2000, 0x08);
}
}