#![no_std]
use consts::*;
use core::fmt::Debug;
use embedded_hal_async::spi::{self, Operation};
use errors::As8510Error;
mod consts;
mod errors;
#[derive(Clone, Copy)]
pub enum Gain {
Gain1 = 1,
Gain25 = 25,
Gain40 = 40,
Gain100 = 100,
}
impl From<Gain> for i32 {
fn from(gain: Gain) -> Self {
gain as i32
}
}
pub struct As8510<SPI> {
peri: SPI,
data: [u8; 5],
current_gain: Gain,
voltage_gain: Gain,
}
impl<SPI, E> As8510<SPI>
where
SPI: spi::SpiDevice<u8, Error = E>,
As8510Error: From<E>,
{
pub async fn new(
peri: SPI,
current_gain: Gain,
voltage_gain: Gain,
) -> Result<Self, As8510Error> {
let mut device = Self {
peri,
data: [0; 5],
current_gain,
voltage_gain,
};
assert!(
matches!(voltage_gain, Gain::Gain40) | matches!(voltage_gain, Gain::Gain25),
"Voltage gain must be Gain25 or Gain40"
);
device.write(AS8510_DEC_REG_R1_I, 0b0100_0101).await?;
device.write(AS8510_DEC_REG_R2_I, 0b1100_0101).await?;
device.write(AS8510_FIR_CTL_REG_I, 0b0000_0100).await?;
device.write(AS8510_CLK_REG, 0b0010_0000).await?;
device.write(AS8510_MOD_CTL_REG, 0).await?;
device.write(AS8510_MOD_TA_REG1, 0b1000_0000).await?;
device.write(AS8510_MOD_TA_REG2, 0).await?;
device.write(AS8510_MOD_ITH_REG1, 0b0101_0000).await?;
device.write(AS8510_MOD_ITH_REG2, 0b1100_1111).await?;
device.write(AS8510_MOD_TMC_REG1, 0b1111_0011).await?;
device.write(AS8510_MOD_TMC_REG2, 0b1111_1000).await?;
device.write(AS8510_NOM_ITH_REG1, 0).await?;
device.write(AS8510_NOM_ITH_REG2, 0).await?;
device.write(AS8510_PGA_CTL_REG, device.reg_0x13()).await?;
device.write(AS8510_PD_CTL_REG_1, 0b1100_1111).await?;
device.write(AS8510_PD_CTL_REG_2, device.reg_0x15()).await?;
device.write(AS8510_PD_CTL_REG_3, 0b1111_1000).await?;
device.write(AS8510_ACH_CTL_REG, 0).await?;
device.write(AS8510_ISC_CTL_REG, 0).await?;
device.write(AS8510_OTP_EN_REG, 0).await?;
device.write(AS8510_MOD_CTL_REG, 0x01).await?;
Ok(device)
}
#[inline]
fn reg_0x15(&self) -> u8 {
0xf0 | if matches!(self.current_gain, Gain::Gain1) {
0xf
} else {
0b0010
} | if matches!(self.voltage_gain, Gain::Gain1) {
0
} else {
0b0001
}
}
#[inline]
fn reg_0x13(&self) -> u8 {
(match self.current_gain {
Gain::Gain1 => 0,
Gain::Gain25 => 0b0100,
Gain::Gain40 => 0b1000,
Gain::Gain100 => 0b1100,
} | match self.voltage_gain {
Gain::Gain1 => 0,
Gain::Gain25 => 0b0001,
Gain::Gain40 => 0b001,
Gain::Gain100 => 0b0011,
}) << 4
}
async fn write(&mut self, addr: u8, data: u8) -> Result<(), As8510Error> {
if addr > 0x47 {
return Err(As8510Error::IllegalAddress(addr));
}
let txdata = [addr, data];
self.peri
.transaction(&mut [Operation::Write(&txdata)])
.await?;
Ok(())
}
async fn read(&mut self, addr: u8, len: u8) -> Result<&[u8], As8510Error> {
if addr + len > 0x48 {
return Err(As8510Error::IllegalAddress(addr + len));
}
self.data.fill(0);
let txaddr = addr | 0x80;
let len = len as usize;
self.peri
.transaction(&mut [
Operation::Write(&[txaddr]),
Operation::Read(&mut self.data[..len]),
])
.await?;
Ok(&self.data[..len])
}
pub async fn get_current(&mut self) -> Result<f32, As8510Error> {
if !self.get_status().await?.current_channel_updated() {
return Err(As8510Error::NotReady);
}
self.read(ADDR_CURRENT, 2).await?;
let adc =
i16::from_be_bytes([self.data[0], self.data[1]]).wrapping_add(0x8000u16 as i16) as i32;
let val = ((adc * CALIBRATION as i32) / i32::from(self.current_gain) / 2000) as f32 * 0.1;
Ok(val)
}
pub async fn get_voltage(&mut self) -> Result<f32, As8510Error> {
if !self.get_status().await?.voltage_channel_updated() {
return Err(As8510Error::NotReady);
}
self.read(ADDR_VOLTAGE, 2).await?;
let adc =
i16::from_be_bytes([self.data[0], self.data[1]]).wrapping_add(0x8000u16 as i16) as i32;
let val = ((adc * CALIBRATION as i32) / i32::from(self.current_gain) / 2000) as f32 * 0.1;
Ok(val)
}
async fn get_status(&mut self) -> Result<StatusReg, As8510Error> {
self.read(ADDR_STATUS, 1).await?;
Ok(StatusReg::new(self.data[0]))
}
}
#[repr(transparent)]
#[derive(Debug, Clone, Copy)]
pub struct StatusReg(u8);
impl StatusReg {
pub const CURRENT_CHANNEL_UPDATED: u8 = 1 << 2;
pub const VOLTAGE_CHANNEL_UPDATED: u8 = 1 << 1;
pub fn new(value: u8) -> Self {
StatusReg(value)
}
pub fn current_channel_updated(&self) -> bool {
self.0 & Self::CURRENT_CHANNEL_UPDATED != 0
}
pub fn voltage_channel_updated(&self) -> bool {
self.0 & Self::VOLTAGE_CHANNEL_UPDATED != 0
}
}