use embedded_hal::i2c::I2c;
const REG_ZMCO: u8 = 0x00;
const REG_ZPOS_H: u8 = 0x01;
const REG_ZPOS_L: u8 = 0x02;
const REG_MPOS_H: u8 = 0x03;
const REG_MPOS_L: u8 = 0x04;
const REG_MANG_H: u8 = 0x05;
const REG_MANG_L: u8 = 0x06;
const REG_CONF_H: u8 = 0x07;
const REG_CONF_L: u8 = 0x08;
const REG_STATUS: u8 = 0x0B;
const REG_RAW_ANGLE_H: u8 = 0x0C;
const REG_RAW_ANGLE_L: u8 = 0x0D;
const REG_ANGLE_H: u8 = 0x0E;
const REG_ANGLE_L: u8 = 0x0F;
const REG_AGC: u8 = 0x1A;
const REG_MAGNITUDE_H: u8 = 0x1B;
const REG_MAGNITUDE_L: u8 = 0x1C;
const REG_BURN: u8 = 0xFF;
const STATUS_MD: u8 = 0x08;
const STATUS_ML: u8 = 0x10;
const STATUS_MH: u8 = 0x20;
pub struct As5600Minimal<I2C> {
i2c: I2C,
addr: u8,
}
impl<I2C: I2c> As5600Minimal<I2C> {
pub fn new(mut i2c: I2C, addr: u8) -> Result<Self, I2C::Error> {
let status = read_reg8(&mut i2c, addr, REG_STATUS)?;
if status & STATUS_MD == 0 {
}
Ok(Self { i2c, addr })
}
pub fn angle(&mut self) -> Result<f32, I2C::Error> {
Ok(self.angle_raw()? as f32 * 360.0 / 4096.0)
}
pub fn angle_raw(&mut self) -> Result<u16, I2C::Error> {
let raw = read_reg16(&mut self.i2c, self.addr, REG_ANGLE_H)?;
Ok((raw >> 4) & 0x0FFF)
}
pub fn is_magnet_detected(&mut self) -> Result<bool, I2C::Error> {
Ok(read_reg8(&mut self.i2c, self.addr, REG_STATUS)? & STATUS_MD != 0)
}
pub fn is_magnet_too_strong(&mut self) -> Result<bool, I2C::Error> {
Ok(read_reg8(&mut self.i2c, self.addr, REG_STATUS)? & STATUS_MH != 0)
}
pub fn is_magnet_too_weak(&mut self) -> Result<bool, I2C::Error> {
Ok(read_reg8(&mut self.i2c, self.addr, REG_STATUS)? & STATUS_ML != 0)
}
}
pub struct As5600Full<I2C> {
inner: As5600Minimal<I2C>,
}
impl<I2C: I2c> As5600Full<I2C> {
pub fn new(i2c: I2C, addr: u8) -> Result<Self, I2C::Error> {
let inner = As5600Minimal::new(i2c, addr)?;
Ok(Self { inner })
}
pub fn angle(&mut self) -> Result<f32, I2C::Error> {
self.inner.angle()
}
pub fn angle_raw(&mut self) -> Result<u16, I2C::Error> {
self.inner.angle_raw()
}
pub fn is_magnet_detected(&mut self) -> Result<bool, I2C::Error> {
self.inner.is_magnet_detected()
}
pub fn is_magnet_too_strong(&mut self) -> Result<bool, I2C::Error> {
self.inner.is_magnet_too_strong()
}
pub fn is_magnet_too_weak(&mut self) -> Result<bool, I2C::Error> {
self.inner.is_magnet_too_weak()
}
pub fn raw_angle(&mut self) -> Result<u16, I2C::Error> {
let raw = read_reg16(&mut self.inner.i2c, self.inner.addr, REG_RAW_ANGLE_H)?;
Ok((raw >> 4) & 0x0FFF)
}
pub fn raw_angle_degrees(&mut self) -> Result<f32, I2C::Error> {
Ok(self.raw_angle()? as f32 * 360.0 / 4096.0)
}
pub fn agc(&mut self) -> Result<u8, I2C::Error> {
read_reg8(&mut self.inner.i2c, self.inner.addr, REG_AGC)
}
pub fn magnitude(&mut self) -> Result<u16, I2C::Error> {
let raw = read_reg16(&mut self.inner.i2c, self.inner.addr, REG_MAGNITUDE_H)?;
Ok((raw >> 4) & 0x0FFF)
}
pub fn status_byte(&mut self) -> Result<u8, I2C::Error> {
read_reg8(&mut self.inner.i2c, self.inner.addr, REG_STATUS)
}
pub fn configure(&mut self, pm: u8, hyst: u8, outs: u8, pwmf: u8, sf: u8, fth: u8, wd: bool) -> Result<(), I2C::Error> {
let conf_h = read_reg8(&mut self.inner.i2c, self.inner.addr, REG_CONF_H)?;
let conf_l = read_reg8(&mut self.inner.i2c, self.inner.addr, REG_CONF_L)?;
let conf_h = (conf_h & 0xC0) | ((wd as u8) << 5) | ((fth & 0x07) << 2) | (sf & 0x03);
let conf_l = ((pwmf & 0x03) << 6) | ((outs & 0x03) << 4) | ((hyst & 0x03) << 2) | (pm & 0x03);
write_reg16(&mut self.inner.i2c, self.inner.addr, REG_CONF_H, (conf_h as u16) << 8 | conf_l as u16)
}
pub fn set_zero_position(&mut self, pos: u16) -> Result<(), I2C::Error> {
write_reg8(&mut self.inner.i2c, self.inner.addr, REG_ZPOS_H, ((pos >> 8) & 0x0F) as u8)?;
write_reg8(&mut self.inner.i2c, self.inner.addr, REG_ZPOS_L, (pos & 0xFF) as u8)
}
pub fn set_max_position(&mut self, pos: u16) -> Result<(), I2C::Error> {
write_reg8(&mut self.inner.i2c, self.inner.addr, REG_MPOS_H, ((pos >> 8) & 0x0F) as u8)?;
write_reg8(&mut self.inner.i2c, self.inner.addr, REG_MPOS_L, (pos & 0xFF) as u8)
}
pub fn set_max_angle(&mut self, span: u16) -> Result<(), I2C::Error> {
write_reg8(&mut self.inner.i2c, self.inner.addr, REG_MANG_H, ((span >> 8) & 0x0F) as u8)?;
write_reg8(&mut self.inner.i2c, self.inner.addr, REG_MANG_L, (span & 0xFF) as u8)
}
pub fn zero_position(&mut self) -> Result<u16, I2C::Error> {
let raw = read_reg16(&mut self.inner.i2c, self.inner.addr, REG_ZPOS_H)?;
Ok((raw >> 4) & 0x0FFF)
}
pub fn max_position(&mut self) -> Result<u16, I2C::Error> {
let raw = read_reg16(&mut self.inner.i2c, self.inner.addr, REG_MPOS_H)?;
Ok((raw >> 4) & 0x0FFF)
}
pub fn max_angle(&mut self) -> Result<u16, I2C::Error> {
let raw = read_reg16(&mut self.inner.i2c, self.inner.addr, REG_MANG_H)?;
Ok((raw >> 4) & 0x0FFF)
}
pub fn burn_count(&mut self) -> Result<u8, I2C::Error> {
Ok(read_reg8(&mut self.inner.i2c, self.inner.addr, REG_ZMCO)? & 0x03)
}
pub fn burn_angle(&mut self) -> Result<(), I2C::Error> {
let status = read_reg8(&mut self.inner.i2c, self.inner.addr, REG_STATUS)?;
if status & STATUS_MD == 0 {
return Ok(()); }
let zmco = read_reg8(&mut self.inner.i2c, self.inner.addr, REG_ZMCO)? & 0x03;
if zmco >= 3 {
return Ok(());
}
write_reg8(&mut self.inner.i2c, self.inner.addr, REG_BURN, 0x80)
}
pub fn burn_setting(&mut self) -> Result<(), I2C::Error> {
let zmco = read_reg8(&mut self.inner.i2c, self.inner.addr, REG_ZMCO)? & 0x03;
if zmco != 0 {
return Ok(());
}
write_reg8(&mut self.inner.i2c, self.inner.addr, REG_BURN, 0x40)
}
}
fn write_reg8<I2C: I2c>(i2c: &mut I2C, addr: u8, reg: u8, value: u8) -> Result<(), I2C::Error> {
let buf = [reg, value];
i2c.write(addr, &buf)
}
fn write_reg16<I2C: I2c>(i2c: &mut I2C, addr: u8, reg: u8, value: u16) -> Result<(), I2C::Error> {
let buf = [reg, (value >> 8) as u8, (value & 0xFF) as u8];
i2c.write(addr, &buf)
}
fn read_reg8<I2C: I2c>(i2c: &mut I2C, addr: u8, reg: u8) -> Result<u8, I2C::Error> {
let mut buf = [0u8; 1];
i2c.write_read(addr, &[reg], &mut buf)?;
Ok(buf[0])
}
fn read_reg16<I2C: I2c>(i2c: &mut I2C, addr: u8, reg: u8) -> Result<u16, I2C::Error> {
let mut buf = [0u8; 2];
i2c.write_read(addr, &[reg], &mut buf)?;
Ok(((buf[0] as u16) << 8) | buf[1] as u16)
}