#![no_std]
use embedded_hal as hal;
use hal::blocking::delay::DelayMs;
use hal::digital::v2::OutputPin;
#[cfg(feature = "rttdebug")]
use panic_rtt_core::rprintln;
mod interface;
pub use interface::{I2cInterface, SensorInterface, SpiInterface};
#[derive(Debug)]
pub enum Error<CommE, PinE> {
Comm(CommE),
Pin(PinE),
UnknownChipId,
Unresponsive,
}
pub struct Builder {}
impl Builder {
pub fn new_i2c<I2C, CommE>(&self, i2c: I2C, address: u8) -> ICM20689<I2cInterface<I2C>>
where
I2C: hal::blocking::i2c::Write<Error = CommE>
+ hal::blocking::i2c::Read<Error = CommE>
+ hal::blocking::i2c::WriteRead<Error = CommE>,
CommE: core::fmt::Debug,
{
let iface = interface::I2cInterface::new(i2c, address);
ICM20689::new_with_interface(iface)
}
pub fn new_spi<SPI, CSN, CommE, PinE>(spi: SPI, csn: CSN) -> ICM20689<SpiInterface<SPI, CSN>>
where
SPI: hal::blocking::spi::Transfer<u8, Error = CommE>
+ hal::blocking::spi::Write<u8, Error = CommE>,
CSN: OutputPin<Error = PinE>,
CommE: core::fmt::Debug,
PinE: core::fmt::Debug,
{
let iface = interface::SpiInterface::new(spi, csn);
ICM20689::new_with_interface(iface)
}
}
pub struct ICM20689<SI> {
pub(crate) si: SI,
pub(crate) gyro_scale: f32,
pub(crate) accel_scale: f32,
}
impl<SI, CommE, PinE> ICM20689<SI>
where
SI: SensorInterface<InterfaceError = Error<CommE, PinE>>,
{
pub(crate) fn new_with_interface(sensor_interface: SI) -> Self {
Self {
si: sensor_interface,
gyro_scale: 0.0,
accel_scale: 0.0
}
}
pub fn check_identity(
&mut self,
delay_source: &mut impl DelayMs<u8>,
) -> Result<bool, SI::InterfaceError> {
for _ in 0..5 {
let chip_id = self.si.register_read(REG_WHO_AM_I)?;
match chip_id {
ICM20602_WAI | ICM20608_WAI | ICM20689_WAI => {
#[cfg(feature = "rttdebug")]
rprintln!("found device: 0x{:0x} ", chip_id);
return Ok(true);
}
_ => {
#[cfg(feature = "rttdebug")]
rprintln!("bogus whoami: 0x{:0x} ", chip_id);
}
}
delay_source.delay_ms(10);
}
Ok(false)
}
pub fn soft_reset(
&mut self,
delay_source: &mut impl DelayMs<u8>,
) -> Result<(), SI::InterfaceError> {
const I2C_IF_DIS: u8 = 1 << 4;
const PWR_DEVICE_RESET: u8 = 1 << 7;
const CLKSEL_AUTO: u8 = 0x01;
const SENSOR_ENABLE_ALL: u8 = 0x00;
self.si.register_write(REG_PWR_MGMT_1, PWR_DEVICE_RESET)?;
delay_source.delay_ms(110);
let mut reset_success = false;
for _ in 0..10 {
if let Ok(reg_val) = self.si.register_read(REG_PWR_MGMT_1) {
if reg_val & PWR_DEVICE_RESET == 0 {
reset_success = true;
break;
}
}
delay_source.delay_ms(10);
}
if !reset_success {
#[cfg(feature = "rttdebug")]
rprintln!("couldn't read REG_PWR_MGMT_1");
return Err(Error::Unresponsive);
}
if self.si.using_spi() {
self.si.register_write(REG_USER_CTRL, I2C_IF_DIS)?;
}
self.si.register_write(REG_PWR_MGMT_1, CLKSEL_AUTO)?;
self.si.register_write(REG_PWR_MGMT_2, SENSOR_ENABLE_ALL)?;
delay_source.delay_ms(200);
Ok(())
}
pub fn setup(&mut self, delay_source: &mut impl DelayMs<u8>) -> Result<(), SI::InterfaceError> {
const FIFO_RST: u8 = 1 << 2;
const DMP_RST: u8 = 1 << 3;
self.soft_reset(delay_source)?;
let supported = self.check_identity(delay_source)?;
if !supported {
return Err(Error::UnknownChipId);
}
self.si.register_write(REG_INT_ENABLE, 0x00)?;
self.si.register_write(REG_FIFO_EN, 0x7C)?;
let ctrl_flags = FIFO_RST | DMP_RST;
self.si.register_write(REG_USER_CTRL, ctrl_flags)?;
self.set_accel_range(AccelRange::default())?;
self.set_gyro_range(GyroRange::default())?;
Ok(())
}
pub fn set_accel_range(&mut self, range: AccelRange) -> Result<(), SI::InterfaceError> {
self.accel_scale = range.scale();
self.si.register_write(REG_ACCEL_CONFIG, (range as u8) << 3)
}
pub fn set_gyro_range(&mut self, range: GyroRange) -> Result<(), SI::InterfaceError> {
self.gyro_scale = range.scale();
self.si.register_write(REG_GYRO_CONFIG, (range as u8) << 2)
}
pub fn get_raw_accel(&mut self) -> Result<[i16; 3], SI::InterfaceError> {
self.si.read_vec3_i16(REG_ACCEL_START)
}
pub fn get_raw_gyro(&mut self) -> Result<[i16; 3], SI::InterfaceError> {
self.si.read_vec3_i16(REG_GYRO_START)
}
pub fn get_scaled_accel(&mut self) -> Result<[f32; 3], SI::InterfaceError> {
let raw_accel = self.get_raw_accel()?;
Ok([
self.accel_scale * (raw_accel[0] as f32),
self.accel_scale * (raw_accel[1] as f32),
self.accel_scale * (raw_accel[2] as f32),
])
}
pub fn get_scaled_gyro(&mut self) -> Result<[f32; 3], SI::InterfaceError> {
let raw_gyro = self.get_raw_gyro()?;
Ok([
self.gyro_scale * (raw_gyro[0] as f32),
self.gyro_scale * (raw_gyro[1] as f32),
self.gyro_scale * (raw_gyro[2] as f32),
])
}
}
const REG_USER_CTRL: u8 = 0x6A;
const REG_PWR_MGMT_1: u8 = 0x6B;
const REG_PWR_MGMT_2: u8 = 0x6C;
const REG_GYRO_CONFIG: u8 = 0x1B;
const REG_ACCEL_CONFIG: u8 = 0x1C;
const REG_FIFO_EN: u8 = 0x23;
const REG_INT_ENABLE: u8 = 0x38;
const REG_ACCEL_XOUT_H: u8 = 0x3B;
const REG_ACCEL_START: u8 = REG_ACCEL_XOUT_H;
const REG_GYRO_XOUT_H: u8 = 0x43;
const REG_GYRO_START: u8 = REG_GYRO_XOUT_H;
const REG_WHO_AM_I: u8 = 0x75;
const ICM20602_WAI: u8 = 0x12;
const ICM20608_WAI: u8 = 0xAF;
const ICM20689_WAI: u8 = 0x98;
#[repr(u8)]
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Debug)]
pub enum GyroRange {
Range_250dps = 0b00,
Range_500dps = 0b01,
Range_1000dps = 0b10,
Range_2000dps = 0b11,
}
impl Default for GyroRange {
fn default() -> Self {
GyroRange::Range_2000dps
}
}
impl GyroRange {
const RADIANS_PER_DEGREE: f32 = core::f32::consts::PI / 180.0;
pub(crate) fn scale(&self) -> f32 {
Self::RADIANS_PER_DEGREE * self.resolution()
}
pub(crate) fn resolution(&self) -> f32 {
match self {
GyroRange::Range_250dps => 250.0 / 32768.0,
GyroRange::Range_500dps => 500.0 / 32768.0,
GyroRange::Range_1000dps => 1000.0 / 32768.0,
GyroRange::Range_2000dps => 2000.0 / 32768.0,
}
}
}
#[repr(u8)]
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Debug)]
pub enum AccelRange {
Range_2g = 0b00,
Range_4g = 0b01,
Range_8g = 0b10,
Range_16g = 0b11,
}
impl Default for AccelRange {
fn default() -> Self {
AccelRange::Range_8g
}
}
impl AccelRange {
const EARTH_GRAVITY_ACCEL: f32 = 9.807;
pub(crate) fn scale(&self) -> f32 {
Self::EARTH_GRAVITY_ACCEL * self.resolution()
}
pub(crate) fn resolution(&self) -> f32 {
match self {
Self::Range_2g => 2.0 / 32768.0,
Self::Range_4g => 4.0 / 32768.0,
Self::Range_8g => 8.0 / 32768.0,
Self::Range_16g => 16.0 / 32768.0,
}
}
}