#![no_std] use core::{marker::PhantomData, slice::IterMut}; use embedded_hal::{blocking::spi::Transfer, digital::v2::InputPin, spi::FullDuplex}; //================================================================================================== // Type-level support //================================================================================================== pub trait HasChannels: private::Sealed { const NUM: u8; } pub struct Max11618; pub struct Max11619; pub struct Max11620; pub struct Max11621; pub struct Max11624; pub struct Max11625; pub struct WithWakeupDelay; pub struct WithoutWakeupDelay; impl private::Sealed for Max11618 {} impl HasChannels for Max11618 { const NUM: u8 = 4; } impl private::Sealed for Max11619 {} impl HasChannels for Max11619 { const NUM: u8 = 4; } impl private::Sealed for Max11620 {} impl HasChannels for Max11620 { const NUM: u8 = 8; } impl private::Sealed for Max11621 {} impl HasChannels for Max11621 { const NUM: u8 = 8; } impl private::Sealed for Max11624 {} impl HasChannels for Max11624 { const NUM: u8 = 16; } impl private::Sealed for Max11625 {} impl HasChannels for Max11625 { const NUM: u8 = 16; } pub trait Clocked: private::Sealed { const CLK_SEL: ClockMode; } pub trait InternallyClocked: Clocked {} pub struct InternallyClockedInternallyTimedCnvst {} impl private::Sealed for InternallyClockedInternallyTimedCnvst {} impl Clocked for InternallyClockedInternallyTimedCnvst { const CLK_SEL: ClockMode = ClockMode::InternalClockInternallyTimedCnvst; } impl InternallyClocked for InternallyClockedInternallyTimedCnvst {} pub struct InternallyClockedExternallyTimedCnvst {} impl private::Sealed for InternallyClockedExternallyTimedCnvst {} impl Clocked for InternallyClockedExternallyTimedCnvst { const CLK_SEL: ClockMode = ClockMode::InternalClockExternallyTimedCnvst; } impl InternallyClocked for InternallyClockedExternallyTimedCnvst {} pub struct InternallyClockedInternallyTimedSerialInterface {} impl private::Sealed for InternallyClockedInternallyTimedSerialInterface {} impl Clocked for InternallyClockedInternallyTimedSerialInterface { const CLK_SEL: ClockMode = ClockMode::InternalClockInternallyTimedSerialInterface; } impl InternallyClocked for InternallyClockedInternallyTimedSerialInterface {} pub struct ExternallyClocked {} impl private::Sealed for ExternallyClocked {} impl Clocked for ExternallyClocked { const CLK_SEL: ClockMode = ClockMode::ExternalClockExternallyTimedSclk; } //================================================================================================== // Definitions //================================================================================================== #[derive(Debug, PartialEq)] pub enum PendingOp { None, SingleChannel, MultiChannel, } /// Clock modes for the MAX116XX devices #[derive(Debug, PartialEq, Clone, Copy)] pub enum ClockMode { /// CNVST Configuration: CNVST active low InternalClockInternallyTimedCnvst = 0b00, /// Externally timed through CNVST. CNVST Configuration: CNVST active low InternalClockExternallyTimedCnvst = 0b01, /// Default mode at power-up. CNVST Configuration: AIN15/AIN11/AIN7 /// Start conversions using the serial interface instead of CNVST InternalClockInternallyTimedSerialInterface = 0b10, ExternalClockExternallyTimedSclk = 0b11, } /// Voltage reference modes #[derive(Debug, PartialEq, Clone, Copy)] pub enum RefMode { /// Auto-Shutdown is on, wake-up delay of 65 us InternalRefWithWakeupDelay = 0b00, ExternalSingleEndedNoWakeupDelay = 0b01, InternalRefWithoutWakeupDelay = 0b10, } /// Specifies how many conversions are performed and then averaged for each /// requested result pub enum AveragingConversions { OneConversion = 0b000, FourConversions = 0b100, EightConversions = 0b101, SixteenConversions = 0b110, ThirtytwoConversions = 0b111, } /// Specifies the number of returned result in single scan mode pub enum AveragingResults { FourResults = 0b00, EightResults = 0b01, TwelveResults = 0b10, SixteenResults = 0b11, } pub enum ScanMode { Scan0ToChannelN = 0b00, ScanChannelNToHighest = 0b01, ScanChnanelNRepeatedly = 0b10, ConvertChannelNOnce = 0b11, } #[derive(Debug)] pub enum AdcError { InvalidChannel, CmdBufTooSmall, ResulBufTooSmall, PendingOperation, NoPendingOperation, InvalidClockMode, } #[derive(Debug)] pub enum Error { Adc(AdcError), Spi(SpiE), } pub enum ErrorWithEoc { Error(Error), Pin(PinE), } impl From for ErrorWithEoc { fn from(other: AdcError) -> Self { ErrorWithEoc::Error(Error::Adc(other)) } } impl From for Error { fn from(other: AdcError) -> Self { Error::Adc(other) } } //================================================================================================== // ADc implementation //================================================================================================== pub struct Max116xx10Bit< SPI, MAX: HasChannels, CLOCKED = ExternallyClocked, DELAY = WithoutWakeupDelay, > { pub clk_mode: ClockMode, pub ref_mode: RefMode, spi: SPI, pending_op: PendingOp, max: PhantomData, clocked: PhantomData, delay: PhantomData, } impl Max116xx10Bit where SPI: Transfer + FullDuplex, { /// Create a new generic MAX116xx instance. pub fn new(spi: SPI, ref_mode: RefMode) -> Result> { let mut max_dev: Max116xx10Bit = Max116xx10Bit { clk_mode: CLOCKED::CLK_SEL, ref_mode, spi, pending_op: PendingOp::None, max: PhantomData, delay: PhantomData, clocked: PhantomData, }; max_dev.setup()?; Ok(max_dev) } #[inline] fn send_wrapper(&mut self, byte: u8) -> Result<(), Error> { nb::block!(self.spi.send(byte)).map_err(|e| Error::Spi(e)) } #[inline] pub fn setup(&mut self) -> Result<(), Error> { self.send_wrapper(self.get_setup_byte()) } #[inline] pub fn averaging( &mut self, avg_conv: AveragingConversions, avg_res: AveragingResults, ) -> Result<(), Error> { self.send_wrapper(Self::get_averaging_byte(avg_conv, avg_res)) } #[inline] pub fn reset_adc(&mut self, fifo_only: bool) -> Result<(), Error> { let mut reset_byte = 0b0001_0000; if fifo_only { reset_byte |= 1 << 3; } self.send_wrapper(reset_byte) } #[inline] pub fn get_setup_byte(&self) -> u8 { ((1 << 6) as u8) | ((self.clk_mode as u8) << 4) | ((self.ref_mode as u8) << 2) } #[inline] pub fn get_averaging_byte(avg_conv: AveragingConversions, avg_res: AveragingResults) -> u8 { ((1 << 5) as u8) | ((avg_conv as u8) << 2) | avg_res as u8 } #[inline] pub fn get_conversion_byte(scan_mode: ScanMode, channel_num: u8) -> Result { if channel_num > MAX::NUM { return Err(AdcError::InvalidChannel); } Ok((1 << 7) | (channel_num << 3) | ((scan_mode as u8) << 1)) } } impl Max116xx10Bit where SPI: Transfer + FullDuplex, { #[inline] fn request_wrapper( &mut self, channel_num: u8, scan_mode: ScanMode, op_type: PendingOp, ) -> Result<(), Error> { if self.pending_op != PendingOp::None { return Err(Error::Adc(AdcError::PendingOperation)); } let conv_byte = Self::get_conversion_byte(scan_mode, channel_num).map_err(|e| Error::Adc(e))?; self.send_wrapper(conv_byte)?; self.pending_op = op_type; Ok(()) } pub fn request_single_channel(&mut self, channel_num: u8) -> Result<(), Error> { self.request_wrapper( channel_num, ScanMode::ConvertChannelNOnce, PendingOp::SingleChannel, ) } pub fn request_multiple_channels_0_to_n(&mut self, n: u8) -> Result<(), Error> { self.request_wrapper(n, ScanMode::Scan0ToChannelN, PendingOp::MultiChannel) } pub fn request_multiple_channels_n_to_highest(&mut self, n: u8) -> Result<(), Error> { self.request_wrapper(n, ScanMode::ScanChannelNToHighest, PendingOp::MultiChannel) } pub fn get_single_channel( &mut self, eoc_pin: &mut I, ) -> nb::Result> { if self.pending_op != PendingOp::SingleChannel { return Err(nb::Error::Other(ErrorWithEoc::Error(Error::Adc( AdcError::NoPendingOperation, )))); } let is_low = match eoc_pin.is_low() { Ok(low) => low, Err(e) => { return Err(nb::Error::Other(ErrorWithEoc::Pin(e))); } }; if is_low { let mut dummy_cmd: [u8; 2] = [0; 2]; match self.spi.transfer(&mut dummy_cmd) { Ok(reply) => { self.pending_op = PendingOp::None; Ok(((reply[0] as u16) << 6) | (reply[1] as u16 >> 2)) } Err(e) => Err(nb::Error::Other(ErrorWithEoc::Error(Error::Spi(e)))), } } else { Err(nb::Error::WouldBlock) } } } impl Max116xx10Bit where SPI: Transfer + FullDuplex, { pub fn read_single_channel( &mut self, buf: &mut [u8], channel_num: u8, ) -> Result> { if buf.len() < 3 { return Err(Error::Adc(AdcError::CmdBufTooSmall)); } match Self::get_conversion_byte(ScanMode::ConvertChannelNOnce, channel_num) { Ok(byte) => buf[0] = byte, Err(e) => { return Err(Error::Adc(e)); } }; buf[1] = 0x00; buf[2] = 0x00; let reply = self.spi.transfer(&mut buf[0..3]).ok().unwrap(); Ok(((reply[1] as u16) << 6) | (reply[2] as u16 >> 2)) } pub fn read_multiple_channels_0_to_n( &mut self, buf: &mut [u8], result_iter: &mut IterMut, n: u8, ) -> Result<(), Error> { let mut iter = buf.iter_mut(); let mut next_byte: &mut u8; for idx in 0..n + 1 { next_byte = iter.next().ok_or(Error::Adc(AdcError::CmdBufTooSmall))?; *next_byte = Self::get_conversion_byte(ScanMode::ConvertChannelNOnce, idx)?; next_byte = iter.next().ok_or(Error::Adc(AdcError::CmdBufTooSmall))?; *next_byte = 0x00; } next_byte = iter.next().ok_or(Error::Adc(AdcError::CmdBufTooSmall))?; *next_byte = 0x00; let reply = self .spi .transfer(&mut buf[0..((n + 1) * 2 + 1) as usize]) .map_err(|e| Error::Spi(e))?; let mut reply_iter = reply.iter(); // Skip first reply byte reply_iter.next().unwrap(); for _ in 0..n + 1 { let next_res = result_iter .next() .ok_or(Error::Adc(AdcError::ResulBufTooSmall))?; *next_res = ((*reply_iter.next().unwrap() as u16) << 6) | (*reply_iter.next().unwrap() as u16 >> 2); } Ok(()) } pub fn read_multiple_channels_n_to_highest( &mut self, buf: &mut [u8], result_iter: &mut IterMut, n: u8, ) -> Result<(), Error> { let mut iter = buf.iter_mut(); let mut next_byte: &mut u8; if n > MAX::NUM - 1 { return Err(Error::Adc(AdcError::InvalidChannel)); } let conversions = MAX::NUM - n; for idx in n..MAX::NUM { next_byte = iter.next().ok_or(Error::Adc(AdcError::CmdBufTooSmall))?; *next_byte = Self::get_conversion_byte(ScanMode::ConvertChannelNOnce, idx)?; next_byte = iter.next().ok_or(Error::Adc(AdcError::CmdBufTooSmall))?; *next_byte = 0x00; } next_byte = iter.next().ok_or(Error::Adc(AdcError::CmdBufTooSmall))?; *next_byte = 0x00; let reply = self .spi .transfer(&mut buf[0..(conversions * 2 + 1) as usize]) .ok() .unwrap(); let mut reply_iter = reply.iter(); // Skip first reply byte reply_iter.next().unwrap(); for _ in 0..conversions { let next_res = result_iter .next() .ok_or(Error::Adc(AdcError::ResulBufTooSmall))?; *next_res = ((*reply_iter.next().unwrap() as u16) << 6) | (*reply_iter.next().unwrap() as u16 >> 2); } Ok(()) } } mod private { pub trait Sealed {} }