#![no_std]
use core::convert::Infallible;
use embedded_hal::{blocking::spi, digital::v2::OutputPin};
pub const MA734_MODE: embedded_hal::spi::Mode = embedded_hal::spi::MODE_0;
#[derive(Debug)]
pub struct MA734<SPI, CS> {
spi: SPI,
cs: CS,
}
impl<SPI, CS> MA734<SPI, CS>
where
SPI: spi::Transfer<u8>,
CS: OutputPin,
{
pub fn new(spi: SPI, cs: CS) -> Self {
MA734 { spi, cs }
}
pub fn release(self) -> (SPI, CS) {
(self.spi, self.cs)
}
pub fn read_angle(&mut self) -> Result<u16, Error<SPI>> {
let mut buf = [0x00; 2];
self.tx(|spi| spi.transfer(&mut buf))?;
Ok(u16::from_be_bytes(buf))
}
pub fn get_zero_angle(&mut self) -> Result<u16, Error<SPI>> {
let angle = u16::from_le_bytes([self.read_register(0x00)?, self.read_register(0x01)?]);
Ok(angle)
}
pub fn set_zero_angle(&mut self, angle: u16) -> Result<(), Error<SPI>> {
self.write_register(0x00, angle as _)?;
self.write_register(0x01, (angle >> 8) as _)?;
Ok(())
}
pub fn get_bias_current_trimming(&mut self) -> Result<BiasCurrentTrimming, Error<SPI>> {
let val = self.read_register(0x02)?;
let axis_trims = self.read_register(0x03)?;
Ok(BiasCurrentTrimming {
val,
trim_x: axis_trims & 0b1 > 0,
trim_y: axis_trims & 0b10 > 0,
})
}
pub fn set_bias_current_trimming(
&mut self,
bct: BiasCurrentTrimming,
) -> Result<(), Error<SPI>> {
self.write_register(0x02, bct.val)?;
self.write_register(0x03, bct.trim_x as u8 | (bct.trim_y as u8) << 1)
.map(|_| ())
}
pub fn get_angle_change_interrupt(&mut self) -> Result<AngleChangeInterrupt, Error<SPI>> {
let aci = self.read_register(0x07)?;
let threshold = self.read_register(0x08)?;
let reference = self.read_register(0x0a)?;
Ok(AngleChangeInterrupt {
threshold,
reference,
hysteresis: aci & 0b11111,
autoupdate: aci & 0b1000000 > 0,
mode: if aci & 0b10000000 > 0 {
IRQMode::LatchOff
} else {
IRQMode::Logic
},
})
}
pub fn set_angle_change_interrupt(
&mut self,
aci: AngleChangeInterrupt,
) -> Result<(), Error<SPI>> {
let mode_bits = aci.hysteresis & 0b11111;
let mode_bit = (aci.mode == IRQMode::LatchOff) as u8;
let autoupdate_bit = aci.autoupdate as u8;
self.write_register(0x07, mode_bits | autoupdate_bit << 6 | mode_bit << 7)?;
self.write_register(0x08, aci.threshold)?;
self.write_register(0x0a, aci.reference).map(|_| ())
}
pub fn get_magnetic_thresholds(&mut self) -> Result<MagneticFieldThresholds, Error<SPI>> {
let val = self.read_register(0x06)?;
Ok(MagneticFieldThresholds {
enable: val & 0b1 > 0,
low: (val >> 2) & 0b111,
high: (val >> 5) & 0b111,
})
}
pub fn set_magnetic_thresholds(
&mut self,
mft: MagneticFieldThresholds,
) -> Result<(), Error<SPI>> {
let payload = mft.enable as u8 | (mft.low & 0b111) << 2 | (mft.high & 0b111) << 5;
self.write_register(0x06, payload).map(|_| ())
}
pub fn set_rotation_dir(&mut self, val: Dir) -> Result<(), Error<SPI>> {
self.write_register(0x09, val.into()).map(|_| ())
}
pub fn get_rotation_dir(&mut self) -> Result<Dir, Error<SPI>> {
self.read_register(0x09).map(From::from)
}
pub fn set_filter_window(&mut self, val: u8) -> Result<(), Error<SPI>> {
self.write_register(0x0e, val << 4).map(|_| ())
}
pub fn get_filter_window(&mut self) -> Result<u8, Error<SPI>> {
self.read_register(0x0e).map(|w| w >> 4)
}
pub fn write_register(&mut self, reg: u8, val: u8) -> Result<u8, Error<SPI>> {
self.cmd(0x80 | (reg & 0x1f), val)
}
pub fn read_register(&mut self, reg: u8) -> Result<u8, Error<SPI>> {
self.cmd(0x40 | (reg & 0x1f), 0x00)
}
pub fn store_register_into_nvm(&mut self, reg: u8) -> Result<(), Error<SPI>> {
self.cmd(0xe0 | (reg & 0x1f), 0x00).map(|_| ())
}
pub fn store_all_registers_into_nvm(&mut self) -> Result<(), Error<SPI>> {
self.cmd(0xc0, 0x00).map(|_| ())
}
pub fn restore_all_registers_from_nvm(&mut self) -> Result<(), Error<SPI>> {
self.cmd(0xa0, 0x00).map(|_| ())
}
pub fn get_magnetic_flags(&mut self) -> Result<MagneticFlags, Error<SPI>> {
self.read_register(0x1b).map(From::from)
}
pub fn get_error_flags(&mut self) -> Result<ErrorFlags, Error<SPI>> {
self.read_register(0x1a).map(From::from)
}
pub fn clear_error_flags(&mut self) -> Result<(), Error<SPI>> {
self.cmd(0x20, 0x00).map(|_| ())
}
fn cmd(&mut self, reg: u8, arg: u8) -> Result<u8, Error<SPI>> {
let mut buf = [reg, arg];
self.tx(|spi| spi.transfer(&mut buf))?;
let mut buf = [0x00; 2];
self.tx(|spi| spi.transfer(&mut buf))?;
Ok(buf[1])
}
fn tx<RES, TX: FnOnce(&mut SPI) -> Result<RES, SPI::Error>>(
&mut self,
tx: TX,
) -> Result<RES, Error<SPI>> {
self.cs.set_low().map_err(|_| Error::PinError)?;
let res = tx(&mut self.spi).map_err(Error::TransferError);
self.cs.set_high().map_err(|_| Error::PinError).and(res)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum IRQMode {
LatchOff,
Logic,
}
#[derive(Clone, Copy, Debug)]
pub struct AngleChangeInterrupt {
pub mode: IRQMode,
pub autoupdate: bool,
pub threshold: u8,
pub reference: u8,
pub hysteresis: u8,
}
impl AngleChangeInterrupt {
pub fn new(
mode: IRQMode,
autoupdate: bool,
threshold: u8,
reference: u8,
hysteresis: u8,
) -> Self {
Self {
mode,
autoupdate,
threshold,
reference,
hysteresis,
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct MagneticFieldThresholds {
pub enable: bool,
pub low: u8,
pub high: u8,
}
impl MagneticFieldThresholds {
pub fn new(enable: bool, low: u8, high: u8) -> Self {
Self { enable, low, high }
}
}
#[derive(Clone, Copy, Debug)]
pub struct BiasCurrentTrimming {
pub val: u8,
pub trim_x: bool,
pub trim_y: bool,
}
impl BiasCurrentTrimming {
pub fn new(val: u8, trim_x: bool, trim_y: bool) -> Self {
Self {
val,
trim_x,
trim_y,
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct ErrorFlags {
pub nvm_error: bool,
pub memory_error: bool,
pub parity_error: bool,
}
impl ErrorFlags {
pub fn new(nvm_error: bool, memory_error: bool, parity_error: bool) -> Self {
Self {
nvm_error,
memory_error,
parity_error,
}
}
}
impl From<u8> for ErrorFlags {
fn from(val: u8) -> Self {
Self {
nvm_error: val & 0b10 > 0,
memory_error: val & 0b100 > 0,
parity_error: val & 0b1000 > 0,
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct MagneticFlags {
pub low: bool,
pub high: bool,
}
impl MagneticFlags {
pub fn new(low: bool, high: bool) -> Self {
Self { low, high }
}
}
impl From<u8> for MagneticFlags {
fn from(val: u8) -> Self {
Self {
low: val & 0b1000000 > 0,
high: val & 0b10000000 > 0,
}
}
}
#[derive(Clone, Copy, Debug)]
pub enum Dir {
CW,
CCW,
}
impl From<Dir> for u8 {
fn from(val: Dir) -> u8 {
match val {
Dir::CW => 0,
Dir::CCW => 0x80,
}
}
}
impl From<u8> for Dir {
fn from(val: u8) -> Self {
if val & 0x80 == 0x80 {
Self::CCW
} else {
Self::CW
}
}
}
pub enum Error<SPI: spi::Transfer<u8>> {
PinError,
TransferError(<SPI as spi::Transfer<u8>>::Error),
}
impl<SPI: spi::Transfer<u8>> core::fmt::Debug for Error<SPI> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::PinError => write!(f, "GPIO Error"),
Self::TransferError(_) => write!(f, "SPI Transfer Error"),
}
}
}
pub struct NoCS;
impl OutputPin for NoCS {
type Error = Infallible;
fn set_low(&mut self) -> Result<(), Self::Error> {
Ok(())
}
fn set_high(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}