//! Blocking driver for MCP2746 use embedded_hal::blocking::i2c::{Write, Read}; const COMMAND_WRITE_VOLATILE_DAC: u8 = 0x00; const COMMAND_WRITE_VOLATILE_MEM: u8 = 0x40; const COMMAND_WRITE_ALL_MEM: u8 = 0x60; const COMMAND_WRITE_VOLATILE_CONFIG: u8 = 0x80; const BASE_ADDRESS: u8 = 0b110_0000; #[derive(Clone, Copy, Debug, PartialEq)] pub enum VRef { UnbufferedVDD, UnbufferedVRef, BufferedVRef, } #[derive(Clone, Copy, Debug, PartialEq)] pub enum PowerDown { NormalOperation, PoweredDownVout1k, PoweredDownVout100k, PoweredDownVout500k, } /// Configuration to apply #[derive(Clone, Copy, Debug, PartialEq)] pub struct Config { pub vref_source: VRef, /// Set device operation pub operation: PowerDown, /// True to set gain 2x, false for 1x pub use_2x_gain: bool, } #[derive(Clone, Copy, Debug, PartialEq)] pub struct Status { /// Is the device ready to use? Only false in an EEPROM programming cycle pub ready: bool, /// Power-on reset status indicator. Low if VDD is below the minimum operational /// voltage. pub device_powered: bool, } pub struct MCP4726 { address: u8, power_status: PowerDown, } impl MCP4726 { pub fn new(sub_address: u8) -> Self { Self { address: BASE_ADDRESS + (sub_address & 0x07), // TODO maybe come up with better semantics for setting this on init power_status: PowerDown::NormalOperation, } } pub fn read_status(&self, i2c: &mut dyn Read) -> Result { let mut buf = [0u8; 1]; self.read_all_mem(i2c, &mut buf)?; Ok(Status { ready: buf[0] & 0x80 != 0, device_powered: buf[0] & 0x40 != 0, }) } pub fn write_config(&mut self, i2c: &mut dyn Write, config: Config) -> Result<(), Error> { let mut buf = [0u8; 1]; buf[0] |= COMMAND_WRITE_VOLATILE_CONFIG; buf[0] |= MCP4726::vref_config(config.vref_source) << 3; buf[0] |= MCP4726::power_config(config.operation) << 1; if config.use_2x_gain { buf[0] |= 1; } // Make sure power config is set properly self.power_status = config.operation; i2c.write(self.address, &buf)?; Ok(()) } pub fn write_dac(&self, i2c: &mut dyn Write, dac: u16) -> Result<(), Error> { let mut buf = [0u8; 2]; buf[0] |= COMMAND_WRITE_VOLATILE_DAC; // Note this "overlaps" the command bits, the lowest command bit is not used for this one buf[0] |= MCP4726::power_config(self.power_status) << 4; buf[0] |= (dac >> 8) as u8 & 0x0F; buf[1] = (dac & 0xFF) as u8; i2c.write(self.address, &buf)?; Ok(()) } pub fn write_nonvolatile(&mut self, i2c: &mut dyn Write, config: Config, dac: u16) -> Result<(), Error> { todo!() } /// Reads all memory, up to 6 bytes into the out buffer. fn read_all_mem(&self, i2c: &mut dyn Read, out: &mut [u8]) -> Result { let buf = if out.len() <= 6 { out } else { &mut out[0..6] }; i2c.read(self.address, buf)?; Ok(buf.len() as u8) } fn vref_config(vref: VRef) -> u8 { match vref { VRef::UnbufferedVDD => 0b00, VRef::UnbufferedVRef => 0b10, VRef::BufferedVRef => 0b11, } } fn power_config(pd: PowerDown) -> u8 { match pd { PowerDown::NormalOperation => 0b00, PowerDown::PoweredDownVout1k => 0b01, PowerDown::PoweredDownVout100k => 0b10, PowerDown::PoweredDownVout500k => 0b11, } } }