#![no_std]
#![deny(warnings)]
use embedded_hal::i2c::{ErrorType, I2c};
pub const MAX_VOLTAGE: f32 = 2.5;
pub struct Ad5627<I2C> {
i2c: I2C,
address: u8,
}
#[doc(hidden)]
#[allow(dead_code)]
enum Command {
WriteInput = 0b000,
WriteDac = 0b001,
WriteInputUpdate = 0b010,
WriteDacUpdate = 0b011,
PowerUpdate = 0b100,
Reset = 0b101,
LdacSetup = 0b110,
InternalRefSetup = 0b111,
}
pub enum Dac {
A = 0b000,
B = 0b001,
Both = 0b111,
}
pub enum Error<E> {
Range,
I2c(E),
}
impl<E> From<E> for Error<E> {
fn from(e: E) -> Self {
Error::I2c(e)
}
}
impl<I2C> Ad5627<I2C>
where
I2C: I2c,
{
pub fn new(i2c: I2C, address: u8) -> Result<Self, <I2C as ErrorType>::Error> {
let mut device = Ad5627 { i2c, address };
device.write(Command::Reset, Dac::Both, [0, 0])?;
device.write(Command::InternalRefSetup, Dac::Both, [0, 1])?;
Ok(device)
}
fn write(
&mut self,
command: Command,
dac: Dac,
payload: [u8; 2],
) -> Result<(), <I2C as ErrorType>::Error> {
let write: [u8; 3] = [((command as u8) << 3) | dac as u8, payload[0], payload[1]];
self.i2c.write(self.address, &write)
}
pub fn default(i2c: I2C) -> Result<Self, <I2C as ErrorType>::Error> {
Ad5627::new(i2c, 0b0001110)
}
pub fn set_voltage(
&mut self,
voltage: f32,
dac: Dac,
) -> Result<f32, Error<<I2C as ErrorType>::Error>> {
if !(0.0..=crate::MAX_VOLTAGE).contains(&voltage) {
return Err(Error::Range);
}
let code = ((voltage / 2.5) * (0x1000 as f32)) as u16;
if code > 0xFFF {
return Err(Error::Range);
}
let code = code << 4;
self.write(Command::WriteInput, dac, code.to_be_bytes())?;
let programmed_voltage = ((code >> 4) as f32) / (0x1000 as f32) * 2.5;
Ok(programmed_voltage)
}
}