1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
//! Platform-agnostic KXTJ3-1057 accelerometer driver which uses I²C via
//! [embedded-hal]. This driver implements the [`Accelerometer`][acc-trait]
//! and [`RawAccelerometer`][raw-trait] traits from the [accelerometer] crate.
//!
//! [embedded-hal]: https://docs.rs/embedded-hal
//! [accelerometer]: https://docs.rs/accelerometer
//! [acc-trait]: https://docs.rs/accelerometer/latest/accelerometer/trait.Accelerometer.html
//! [raw-trait]: https://docs.rs/accelerometer/latest/accelerometer/trait.RawAccelerometer.html
//!
#![no_std]
pub use accelerometer;
use accelerometer::error::Error as AccelerometerError;
use accelerometer::vector::{F32x3, I16x3};
use accelerometer::{Accelerometer, RawAccelerometer};
use embedded_hal::blocking::i2c::{self, WriteRead};
mod register;
use register::*;
pub use register::{DataRate, Mode, Range, Register, SlaveAddr};
/// Accelerometer errors, generic around another error type `E` representing
/// an (optional) cause of this error.
#[derive(Debug)]
pub enum Error<BusError, PinError> {
/// I²C bus error
Bus(BusError),
Pin(PinError),
/// Invalid data rate selection
InvalidDataRate,
/// Invalid selects the acceleration range
InvalidRange,
/// Attempted to write to a read-only register
WriteToReadOnly,
/// Invalid address provided
WrongAddress,
}
/// `KXTJ3-1057` driver.
pub struct Kxtj3<I2C> {
/// Underlying I²C device
i2c: I2C,
/// Current I²C slave address
address: u8,
}
impl<I2C, E> Kxtj3<I2C>
where
I2C: WriteRead<Error = E> + i2c::Write<Error = E>,
{
/// Create a new KXTJ3-1057 driver from the given I2C peripheral.
/// Default is Hz_400 LowPower.
/// An example using the [esp_idf_hal](https://esp-rs.github.io/esp-idf-hal/esp_idf_hal):
///
/// use esp_idf_hal::i2c::*;
/// use esp_idf_hal::peripherals::Peripherals;
/// use esp_idf_hal::prelude::*;
/// use kxtj3_1057::Kxtj3;
///
/// let peripherals = Peripherals::take().unwrap();
/// let i2c = peripherals.i2c0;
///
/// let sda = peripherals.pins.gpio10;
/// let scl = peripherals.pins.gpio8;
/// let config = I2cConfig::new().baudrate(400.kHz().into()).scl_enable_pullup(true).sda_enable_pullup(true);
///
/// let i2c = I2cDriver::new(i2c, sda, scl, &config).unwrap();
/// let kxtj3 = Kxtj3::new(i2c, kxtj3_1057::SlaveAddr::Default).unwrap();
pub fn new(i2c: I2C, address: SlaveAddr) -> Result<Self, Error<E, core::convert::Infallible>> {
let mut kxtj3 = Kxtj3 {
i2c,
address: address.addr(),
};
if kxtj3.get_device_id()? != DEVICE_ID {
return Err(Error::WrongAddress);
}
Ok(kxtj3)
}
/// Write a byte to the given register.
fn write_register(
&mut self,
register: Register,
value: u8,
) -> Result<(), Error<E, core::convert::Infallible>> {
if register.read_only() {
return Err(Error::WriteToReadOnly);
}
self.i2c
.write(self.address, &[register.addr(), value])
.map_err(Error::Bus)
}
/// Read a byte from the given register.
fn read_register(
&mut self,
register: Register,
) -> Result<u8, Error<E, core::convert::Infallible>> {
let mut data = [0];
self.i2c
.write_read(self.address, &[register.addr()], &mut data)
.map_err(Error::Bus)
.and(Ok(data[0]))
}
/// `WHO_AM_I` register.
pub fn get_device_id(&mut self) -> Result<u8, Error<E, core::convert::Infallible>> {
self.read_register(Register::WHOAMI)
}
/// Controls the operating mode of the KXTJ3 .
/// `CTRL_REG1`: `PC1` bit , CTRL_REG1`: `RES` bit.
pub fn set_mode(&mut self, mode: Mode) -> Result<(), Error<E, core::convert::Infallible>> {
self.register_clear_bits(Register::CTRL1, PC1_EN)?;
match mode {
Mode::LowPower => {
self.register_clear_bits(Register::CTRL1, RES_EN)?;
self.register_set_bits(Register::CTRL1, PC1_EN)?;
}
Mode::Standby => {}
Mode::HighResolution => {
self.register_set_bits(Register::CTRL1, RES_EN)?;
self.register_set_bits(Register::CTRL1, PC1_EN)?;
}
}
Ok(())
}
/// Read the current operating mode.
pub fn get_mode(&mut self) -> Result<Mode, Error<E, core::convert::Infallible>> {
let ctrl1 = self.read_register(Register::CTRL1)?;
let is_pc1_set = (ctrl1 >> 7) & 0x01 != 0;
let is_res_set = (ctrl1 >> 6) & 0x01 != 0;
let mode = match (is_pc1_set, is_res_set) {
(false, _) => Mode::Standby,
(true, false) => Mode::LowPower,
(true, true) => Mode::HighResolution,
};
Ok(mode)
}
/// Data rate selection.
pub fn set_datarate(
&mut self,
datarate: DataRate,
) -> Result<(), Error<E, core::convert::Infallible>> {
self.register_clear_bits(Register::CTRL1, PC1_EN)?;
self.modify_register(Register::DATA_CTRL, |mut data_ctrl| {
// Mask off highest 4 bits
data_ctrl &= !ODR_MASK;
// Write in lowest 4 bits
data_ctrl |= datarate.bits();
data_ctrl
})?;
self.register_set_bits(Register::CTRL1, PC1_EN)
}
/// Read the current data selection rate.
pub fn get_datarate(&mut self) -> Result<DataRate, Error<E, core::convert::Infallible>> {
let data_ctrl = self.read_register(Register::DATA_CTRL)?;
let odr = data_ctrl & 0x0F;
DataRate::try_from(odr).map_err(|_| Error::InvalidDataRate)
}
/// Set the acceleration Range
pub fn set_range(&mut self, range: Range) -> Result<(), Error<E, core::convert::Infallible>> {
self.register_clear_bits(Register::CTRL1, PC1_EN)?;
self.modify_register(Register::CTRL1, |mut ctrl1| {
ctrl1 &= !GSEL_MASK;
ctrl1 |= range.bits() << 2;
ctrl1
})?;
self.register_set_bits(Register::CTRL1, PC1_EN)
}
/// Read the acceleration Range
pub fn get_range(&mut self) -> Result<Range, Error<E, core::convert::Infallible>> {
let ctrl1 = self.read_register(Register::CTRL1)?;
let gsel = (ctrl1 >> 2) & 0x07;
Range::try_from(gsel).map_err(|_| Error::InvalidRange)
}
/// Read from the registers for each of the 3 axes.
fn read_accel_bytes(&mut self) -> Result<[u8; 6], Error<E, core::convert::Infallible>> {
let mut data = [0u8; 6];
self.i2c
.write_read(self.address, &[Register::XOUT_L.addr() | 0x80], &mut data)
.map_err(Error::Bus)
.and(Ok(data))
}
/// Modify a register's value. Read the current value of the register,
/// update the value with the provided function, and set the register to
/// the return value.
fn modify_register<F>(
&mut self,
register: Register,
f: F,
) -> Result<(), Error<E, core::convert::Infallible>>
where
F: FnOnce(u8) -> u8,
{
let value = self.read_register(register)?;
self.write_register(register, f(value))
}
/// Clear the given bits in the given register. For example:
///
/// kxtj3.register_clear_bits(0b0110)
///
/// This call clears (sets to 0) the bits at index 1 and 2. Other bits of the register are not touched.
pub fn register_clear_bits(
&mut self,
reg: Register,
bits: u8,
) -> Result<(), Error<E, core::convert::Infallible>> {
self.modify_register(reg, |v| v & !bits)
}
/// Set the given bits in the given register. For example:
///
/// kxtj3.register_set_bits(0b0110)
///
/// This call sets to 1 the bits at index 1 and 2. Other bits of the register are not touched.
pub fn register_set_bits(
&mut self,
reg: Register,
bits: u8,
) -> Result<(), Error<E, core::convert::Infallible>> {
self.modify_register(reg, |v| v | bits)
}
}
impl<I2C, E> RawAccelerometer<I16x3> for Kxtj3<I2C>
where
I2C: WriteRead<Error = E> + i2c::Write<Error = E>,
E: core::fmt::Debug,
{
type Error = Error<E, core::convert::Infallible>;
fn accel_raw(&mut self) -> Result<I16x3, AccelerometerError<Self::Error>> {
let accel_bytes = self.read_accel_bytes()?;
let x = i16::from_le_bytes(accel_bytes[0..2].try_into().unwrap());
let y = i16::from_le_bytes(accel_bytes[2..4].try_into().unwrap());
let z = i16::from_le_bytes(accel_bytes[4..6].try_into().unwrap());
Ok(I16x3::new(x, y, z))
}
}
impl<I2C, E> Accelerometer for Kxtj3<I2C>
where
I2C: WriteRead<Error = E> + i2c::Write<Error = E>,
E: core::fmt::Debug,
{
type Error = Error<E, core::convert::Infallible>;
/// Get normalized ±g reading from the accelerometer.
fn accel_norm(&mut self) -> Result<F32x3, AccelerometerError<Self::Error>> {
let mode = self.get_mode()?;
let range = self.get_range()?;
// See "Mechanical Specifications" in the datasheet to find the values below.
// Depending on which Mode we are operating in, the data has different
// resolution. Using this knowledge, we determine how many bits the
// data needs to be shifted. This is necessary because the raw data
// is in left-justified two's complement and we would like for it to be
// right-justified instead.
let (scale, shift) = match (mode, range) {
// High Resolution mode - 14-bit data output
(Mode::HighResolution, Range::G8_14Bit) => (0.001, 2),
(Mode::HighResolution, Range::G16_14Bit) => (0.002, 2),
// High Resolution mode-12 bit data output
(Mode::HighResolution, Range::G2) => (0.001, 4),
(Mode::HighResolution, Range::G4) => (0.002, 4),
(Mode::HighResolution, Range::G8) => (0.004, 4),
(Mode::HighResolution, Range::G16_1)
| (Mode::HighResolution, Range::G16_2)
| (Mode::HighResolution, Range::G16_3) => (0.008, 4),
// Low power mode
(Mode::LowPower, Range::G2) => (0.015, 8),
(Mode::LowPower, Range::G4) => (0.031, 8),
(Mode::LowPower, Range::G8) => (0.062, 8),
(Mode::LowPower, Range::G16_1)
| (Mode::LowPower, Range::G16_2)
| (Mode::LowPower, Range::G16_3) => (0.125, 8),
_ => (0.0, 0),
};
let acc_raw = self.accel_raw()?;
let x = (acc_raw.x >> shift) as f32 * scale;
let y = (acc_raw.y >> shift) as f32 * scale;
let z = (acc_raw.z >> shift) as f32 * scale;
Ok(F32x3::new(x, y, z))
}
/// Get the sample rate of the accelerometer data.
fn sample_rate(&mut self) -> Result<f32, AccelerometerError<Self::Error>> {
Ok(self.get_datarate()?.sample_rate())
}
}