use embedded_hal_async::spi::SpiDevice;
use crate::{diagnostics::Diagnostics, error::Error, register::Register, utils};
const READ_BIT: u16 = 0x4000;
const PARITY_BIT: u16 = 0x8000;
const ERROR_FLAG: u16 = 0x4000;
const DATA_MASK: u16 = 0x3FFF;
const NOP_COMMAND: u16 = 0x0000;
pub const ANGLE_MAX: u16 = 0x3FFF + 1;
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct As5048a<SPI> {
spi: SPI,
}
impl<SPI, E> As5048a<SPI>
where
SPI: SpiDevice<u8, Error = E>,
{
pub fn new(spi: SPI) -> Self {
Self { spi }
}
pub fn release(self) -> SPI {
self.spi
}
async fn read_register(&mut self, register: Register) -> Result<u16, Error<E>> {
let address = u16::from(register);
let command = READ_BIT | address;
let command = if utils::calculate_parity(command) {
PARITY_BIT | command
} else {
command
};
#[cfg(feature = "defmt")]
defmt::trace!(
"Reading register 0x{:04X}, command: 0x{:04X}",
address,
command
);
let tx_cmd = command.to_be_bytes();
let mut rx_cmd = [0u8; 2];
self.spi
.transfer(&mut rx_cmd, &tx_cmd)
.await
.map_err(Error::Communication)?;
let tx_nop = NOP_COMMAND.to_be_bytes();
let mut rx_data = [0u8; 2];
self.spi
.transfer(&mut rx_data, &tx_nop)
.await
.map_err(Error::Communication)?;
let response = u16::from_be_bytes(rx_data);
#[cfg(feature = "defmt")]
defmt::trace!("Received response: 0x{:04X}", response);
if !utils::verify_parity(response) {
#[cfg(feature = "defmt")]
defmt::warn!("Parity error in response: 0x{:04X}", response);
return Err(Error::ParityError);
}
if response & ERROR_FLAG != 0 {
#[cfg(feature = "defmt")]
defmt::warn!("Sensor error flag set in response");
return Err(Error::SensorError);
}
let data = response & DATA_MASK;
#[cfg(feature = "defmt")]
defmt::debug!("Register 0x{:04X} value: 0x{:04X}", address, data);
Ok(data)
}
#[allow(dead_code)]
async fn write_register(&mut self, register: Register, data: u16) -> Result<(), Error<E>> {
let address = u16::from(register);
#[cfg(feature = "defmt")]
defmt::debug!("Writing 0x{:04X} to register 0x{:04X}", data, address);
let command = address;
let command = if utils::calculate_parity(command) {
PARITY_BIT | command
} else {
command
};
let tx_cmd = command.to_be_bytes();
let mut rx_cmd = [0u8; 2];
self.spi
.transfer(&mut rx_cmd, &tx_cmd)
.await
.map_err(Error::Communication)?;
let data_frame = data & DATA_MASK;
let data_frame = if utils::calculate_parity(data_frame) {
PARITY_BIT | data_frame
} else {
data_frame
};
let tx_data = data_frame.to_be_bytes();
let mut rx_old = [0u8; 2];
self.spi
.transfer(&mut rx_old, &tx_data)
.await
.map_err(Error::Communication)?;
let tx_nop = NOP_COMMAND.to_be_bytes();
let mut rx_verify = [0u8; 2];
self.spi
.transfer(&mut rx_verify, &tx_nop)
.await
.map_err(Error::Communication)?;
let response = u16::from_be_bytes(rx_verify);
if !utils::verify_parity(response) {
#[cfg(feature = "defmt")]
defmt::warn!("Parity error in write verification: 0x{:04X}", response);
return Err(Error::ParityError);
}
if response & ERROR_FLAG != 0 {
#[cfg(feature = "defmt")]
defmt::warn!("Sensor error flag set during write");
return Err(Error::SensorError);
}
#[cfg(feature = "defmt")]
defmt::trace!("Write to register 0x{:04X} successful", address);
Ok(())
}
pub async fn angle(&mut self) -> Result<u16, Error<E>> {
self.read_register(Register::Angle).await
}
pub async fn angle_degrees(&mut self) -> Result<u16, Error<E>> {
let angle = self.angle().await?;
let degrees = (u32::from(angle).saturating_mul(360)) / u32::from(ANGLE_MAX);
#[allow(clippy::cast_possible_truncation)]
Ok(degrees as u16)
}
pub async fn magnitude(&mut self) -> Result<u16, Error<E>> {
self.read_register(Register::Magnitude).await
}
pub async fn diagnostics(&mut self) -> Result<Diagnostics, Error<E>> {
self.read_register(Register::DiagAgc)
.await
.map(Diagnostics::new)
}
pub async fn clear_error_flag(&mut self) -> Result<(), Error<E>> {
self.read_register(Register::ClearErrorFlag).await?;
Ok(())
}
}