diff --git a/Cargo.toml b/Cargo.toml index 5c0ee04..366f211 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,9 +20,9 @@ embedded-hal-async = "1.0" embedded-hal = "1.0" heapless = "0.8" seq-macro = "0.3" -embassy-sync = "0.5" +embassy-sync = "0.6" [dev-dependencies] -embedded-hal-mock = { version = "0.10", features = ["eh1", "embedded-hal-async"] } +embedded-hal-mock = { version = "0.11", features = ["eh1", "embedded-hal-async"] } tokio = { version = "1.36", features = ["full"] } critical-section = { version = "1.1", features = ["std"] } diff --git a/src/config.rs b/src/config.rs index c5ba794..ebfc756 100644 --- a/src/config.rs +++ b/src/config.rs @@ -207,6 +207,7 @@ pub struct DeviceConfig { pub rscancel: RSCANCEL, pub lpen: LPEN, pub brst: BRST, + pub mode_transition_delay: u32, } impl DeviceConfig { @@ -237,6 +238,7 @@ impl Default for DeviceConfig { rscancel: RSCANCEL::Disabled, lpen: LPEN::Default, brst: BRST::Default, + mode_transition_delay: 5, } } } diff --git a/src/lib.rs b/src/lib.rs index ad5d5f8..f6e72e5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ mod port; use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, mutex::Mutex}; use embedded_hal::digital::OutputPin; -use embedded_hal_async::spi::SpiBus; +use embedded_hal_async::{delay::DelayNs, spi::SpiBus}; use heapless::Vec; use seq_macro::seq; @@ -31,25 +31,26 @@ pub enum Error { Port, } -pub type WrappedDriver = Mutex>; +pub type WrappedDriver = Mutex>; -pub struct Max11300 { - max: WrappedDriver, +pub struct Max11300 { + max: WrappedDriver, config: DeviceConfig, } seq!(N in 0..20 { - impl Max11300 + impl Max11300 where SPI: SpiBus, EN: OutputPin, + D: DelayNs, { - pub async fn new(spi: SPI, enable: EN, config: DeviceConfig) -> Result> { - let max = MaxDriver::try_new(spi, enable, config).await?; + pub async fn new(spi: SPI, enable: EN, delay: D, config: DeviceConfig) -> Result> { + let max = MaxDriver::try_new(spi, enable, delay, config).await?; Ok(Self { max: Mutex::new(max), config }) } - pub fn split(&mut self) -> Parts<'_, SPI, EN> { + pub fn split(&mut self) -> Parts<'_, SPI, EN, D> { Parts { #( port~N: Mode0Port::new(Port::P~N, &self.max), @@ -65,19 +66,32 @@ seq!(N in 0..20 { } }); -pub struct MaxDriver { +pub struct MaxDriver { enable: EN, spi: SPI, + delay: D, + config: DeviceConfig, } -impl MaxDriver +impl MaxDriver where SPI: SpiBus, EN: OutputPin, + D: DelayNs, { - async fn try_new(spi: SPI, mut enable: EN, config: DeviceConfig) -> Result> { + async fn try_new( + spi: SPI, + mut enable: EN, + delay: D, + config: DeviceConfig, + ) -> Result> { enable.set_high().map_err(Error::Pin)?; - let mut max = Self { enable, spi }; + let mut max = Self { + config, + delay, + enable, + spi, + }; if max.read_register(REG_DEVICE_ID).await? != DEVICE_ID { return Err(Error::Conn); } @@ -163,16 +177,17 @@ where } } -pub struct ReadInterrupts<'a, SPI, EN> { - max: &'a WrappedDriver, +pub struct ReadInterrupts<'a, SPI, EN, D> { + max: &'a WrappedDriver, } -impl<'a, SPI, EN, S, P> ReadInterrupts<'a, SPI, EN> +impl<'a, SPI, EN, D, S, P> ReadInterrupts<'a, SPI, EN, D> where SPI: SpiBus, EN: OutputPin, + D: DelayNs, { - pub fn new(max: &'a WrappedDriver) -> Self { + pub fn new(max: &'a WrappedDriver) -> Self { Self { max } } @@ -200,15 +215,16 @@ where } seq!(N in 0..20 { - pub struct Parts<'a, SPI, EN> + pub struct Parts<'a, SPI, EN, D> where SPI: SpiBus + 'a, EN: OutputPin, + D: DelayNs, { #( - pub port~N: Mode0Port<'a, SPI, EN>, + pub port~N: Mode0Port<'a, SPI, EN, D>, )* - pub interrupts: ReadInterrupts<'a, SPI, EN> + pub interrupts: ReadInterrupts<'a, SPI, EN, D> } }); @@ -220,13 +236,15 @@ mod tests { }; use super::*; - use embedded_hal_mock::eh1::pin::{ + use embedded_hal_mock::eh1::delay::NoopDelay; + use embedded_hal_mock::eh1::digital::{ Mock as PinMock, State as PinState, Transaction as PinTransaction, }; use embedded_hal_mock::eh1::spi::{Mock as SpiMock, Transaction as SpiTransaction}; #[tokio::test] async fn into_configured() { + let delay = NoopDelay::new(); let config = DeviceConfig::default(); let pin_expectations = [ PinTransaction::set(PinState::High), @@ -243,7 +261,7 @@ mod tests { ]; let mut pin = PinMock::new(&pin_expectations); let mut spi = SpiMock::new(&spi_expectations); - let _max = Max11300::new(spi.clone(), pin.clone(), config) + let _max = Max11300::new(spi.clone(), pin.clone(), delay, config) .await .unwrap(); pin.done(); @@ -252,6 +270,7 @@ mod tests { #[tokio::test] async fn config_modes() { + let delay = NoopDelay::new(); let config = DeviceConfig::default(); let pin_expectations = [ PinTransaction::set(PinState::High), @@ -276,7 +295,7 @@ mod tests { ]; let mut pin = PinMock::new(&pin_expectations); let mut spi = SpiMock::new(&spi_expectations); - let mut max = Max11300::new(spi.clone(), pin.clone(), config) + let mut max = Max11300::new(spi.clone(), pin.clone(), delay, config) .await .unwrap(); let ports = max.split(); @@ -297,6 +316,7 @@ mod tests { #[tokio::test] async fn config_mode_5() { + let delay = NoopDelay::new(); let config = DeviceConfig::default(); let pin_expectations = [ PinTransaction::set(PinState::High), @@ -321,7 +341,7 @@ mod tests { ]; let mut pin = PinMock::new(&pin_expectations); let mut spi = SpiMock::new(&spi_expectations); - let mut max = Max11300::new(spi.clone(), pin.clone(), config) + let mut max = Max11300::new(spi.clone(), pin.clone(), delay, config) .await .unwrap(); let ports = max.split(); @@ -337,6 +357,7 @@ mod tests { #[tokio::test] async fn config_mode_7() { + let delay = NoopDelay::new(); let config = DeviceConfig::default(); let pin_expectations = [ PinTransaction::set(PinState::High), @@ -361,7 +382,7 @@ mod tests { ]; let mut pin = PinMock::new(&pin_expectations); let mut spi = SpiMock::new(&spi_expectations); - let mut max = Max11300::new(spi.clone(), pin.clone(), config) + let mut max = Max11300::new(spi.clone(), pin.clone(), delay, config) .await .unwrap(); let ports = max.split(); @@ -382,6 +403,7 @@ mod tests { #[tokio::test] async fn multiport() { + let delay = NoopDelay::new(); let config = DeviceConfig::default(); let pin_expectations = [ PinTransaction::set(PinState::High), @@ -409,7 +431,7 @@ mod tests { ]; let mut pin = PinMock::new(&pin_expectations); let mut spi = SpiMock::new(&spi_expectations); - let mut max = Max11300::new(spi.clone(), pin.clone(), config) + let mut max = Max11300::new(spi.clone(), pin.clone(), delay, config) .await .unwrap(); let ports = max.split(); diff --git a/src/port.rs b/src/port.rs index d35ca0d..ed13433 100644 --- a/src/port.rs +++ b/src/port.rs @@ -1,5 +1,6 @@ use core::future::Future; use embedded_hal::digital::OutputPin; +use embedded_hal_async::delay::DelayNs; use embedded_hal_async::spi::SpiBus; use seq_macro::seq; @@ -10,39 +11,41 @@ use crate::config::{ }; use crate::{Error, WrappedDriver}; -pub struct Mode0Port<'a, SPI, EN> { +pub struct Mode0Port<'a, SPI, EN, D> { port: Port, - max: &'a WrappedDriver, + max: &'a WrappedDriver, } -impl<'a, SPI, EN> Mode0Port<'a, SPI, EN> +impl<'a, SPI, EN, D> Mode0Port<'a, SPI, EN, D> where SPI: SpiBus, EN: OutputPin, + D: DelayNs, { - pub(crate) fn new(port: Port, driver: &'a WrappedDriver) -> Self { + pub(crate) fn new(port: Port, driver: &'a WrappedDriver) -> Self { Self { port, max: driver } } } -pub trait IntoConfiguredPort<'a, CONFIG, SPI: 'a, EN: 'a, S, P> { +pub trait IntoConfiguredPort<'a, CONFIG, SPI: 'a, EN: 'a, D: 'a, S, P> { fn into_configured_port( self, config: CONFIG, - ) -> impl Future, Error>>; + ) -> impl Future, Error>>; } seq!(N in 0..=12 { - impl<'a, SPI, EN, S, P> IntoConfiguredPort<'a, ConfigMode~N, SPI, EN, S, P> - for Mode0Port<'a, SPI, EN> + impl<'a, SPI, EN, D, S, P> IntoConfiguredPort<'a, ConfigMode~N, SPI, EN, D, S, P> + for Mode0Port<'a, SPI, EN, D> where SPI: SpiBus + 'a, EN: OutputPin, + D: DelayNs, { async fn into_configured_port( self, config: ConfigMode~N, - ) -> Result, Error> { + ) -> Result, Error> { let mut locked_max = self.max.lock().await; locked_max.write_register(self.port.as_config_addr(), config.as_u16()).await?; Ok(MaxPort { @@ -54,31 +57,34 @@ seq!(N in 0..=12 { } }); -pub struct MaxPort<'a, CONFIG, SPI, EN> { +pub struct MaxPort<'a, CONFIG, SPI, EN, D> { config: CONFIG, port: Port, - max: &'a WrappedDriver, + max: &'a WrappedDriver, } -pub trait IntoMode<'a, CONFIG, SPI: 'a, EN: 'a, S, P> { +pub trait IntoMode<'a, CONFIG, SPI: 'a, EN: 'a, D: 'a, S, P> { fn into_mode( self, config: CONFIG, - ) -> impl Future, Error>>; + ) -> impl Future, Error>>; } seq!(N in 0..=12 { - impl<'a, CONFIG, SPI, EN, S, P> IntoMode<'a, ConfigMode~N, SPI, EN, S, P> - for MaxPort<'a, CONFIG, SPI, EN> + impl<'a, CONFIG, SPI, EN, D, S, P> IntoMode<'a, ConfigMode~N, SPI, EN, D, S, P> + for MaxPort<'a, CONFIG, SPI, EN, D> where SPI: SpiBus + 'a, EN: OutputPin, + D: DelayNs, { async fn into_mode( self, config: ConfigMode~N, - ) -> Result, Error> { + ) -> Result, Error> { let mut driver = self.max.lock().await; + let delay = driver.config.mode_transition_delay; + driver.delay.delay_ms(delay).await; driver.write_register(self.port.as_config_addr(), config.as_u16()).await?; Ok(MaxPort { config, @@ -89,18 +95,20 @@ seq!(N in 0..=12 { } }); -impl<'a, SPI, EN, S, P> MaxPort<'a, ConfigMode0, SPI, EN> +impl<'a, SPI, EN, D, S, P> MaxPort<'a, ConfigMode0, SPI, EN, D> where SPI: SpiBus, EN: OutputPin, + D: DelayNs, { // High impedance mode. Nothing can be done here. } -impl<'a, SPI, EN, S, P> MaxPort<'a, ConfigMode1, SPI, EN> +impl<'a, SPI, EN, D, S, P> MaxPort<'a, ConfigMode1, SPI, EN, D> where SPI: SpiBus, EN: OutputPin, + D: DelayNs, { /// Configure the value of the interrupt threshold and edge detection on this port pub async fn configure_threshold( @@ -127,10 +135,11 @@ where } } -impl<'a, SPI, EN, S, P> MaxPort<'a, ConfigMode3, SPI, EN> +impl<'a, SPI, EN, D, S, P> MaxPort<'a, ConfigMode3, SPI, EN, D> where SPI: SpiBus, EN: OutputPin, + D: DelayNs, { /// Configure the output level of the DAC when the port is set high pub async fn configure_level(&self, level: u16) -> Result<(), Error> { @@ -180,10 +189,11 @@ where } } -impl<'a, SPI, EN, S, P> MaxPort<'a, ConfigMode5, SPI, EN> +impl<'a, SPI, EN, D, S, P> MaxPort<'a, ConfigMode5, SPI, EN, D> where SPI: SpiBus, EN: OutputPin, + D: DelayNs, { /// Set the DAC output value pub async fn set_value(&self, data: u16) -> Result<(), Error> { @@ -203,10 +213,11 @@ where } } -impl<'a, SPI, EN, S, P> MaxPort<'a, ConfigMode6, SPI, EN> +impl<'a, SPI, EN, D, S, P> MaxPort<'a, ConfigMode6, SPI, EN, D> where SPI: SpiBus, EN: OutputPin, + D: DelayNs, { /// Set the DAC output value pub async fn set_value(&self, data: u16) -> Result<(), Error> { @@ -241,10 +252,11 @@ where } } -impl<'a, SPI, EN, S, P> MaxPort<'a, ConfigMode7, SPI, EN> +impl<'a, SPI, EN, D, S, P> MaxPort<'a, ConfigMode7, SPI, EN, D> where SPI: SpiBus, EN: OutputPin, + D: DelayNs, { /// Get the current value of the ADC pub async fn get_value(&self) -> Result> { @@ -280,16 +292,17 @@ where } } -pub struct Multiport<'a, CONFIG, SPI, EN, const N: usize> { - pub ports: [MaxPort<'a, CONFIG, SPI, EN>; N], +pub struct Multiport<'a, CONFIG, SPI, EN, D, const N: usize> { + pub ports: [MaxPort<'a, CONFIG, SPI, EN, D>; N], } -impl<'a, CONFIG, SPI, EN, S, P, const N: usize> Multiport<'a, CONFIG, SPI, EN, N> +impl<'a, CONFIG, SPI, EN, D, S, P, const N: usize> Multiport<'a, CONFIG, SPI, EN, D, N> where SPI: SpiBus, EN: OutputPin, + D: DelayNs, { - pub fn new(ports: [MaxPort<'a, CONFIG, SPI, EN>; N]) -> Result> { + pub fn new(ports: [MaxPort<'a, CONFIG, SPI, EN, D>; N]) -> Result> { // Check if all the ports are in a row // We might weaken this requirement in the future and use the context based burst mode for neighbours in ports.windows(2) { @@ -301,10 +314,11 @@ where } } -impl<'a, SPI, EN, S, P, const N: usize> Multiport<'a, ConfigMode5, SPI, EN, N> +impl<'a, SPI, EN, D, S, P, const N: usize> Multiport<'a, ConfigMode5, SPI, EN, D, N> where SPI: SpiBus, EN: OutputPin, + D: DelayNs, { /// Set the DAC output values for all owned ports pub async fn set_values(&mut self, data: &[u16; N]) -> Result<(), Error> { @@ -316,10 +330,11 @@ where } } -impl<'a, SPI, EN, S, P, const N: usize> Multiport<'a, ConfigMode7, SPI, EN, N> +impl<'a, SPI, EN, D, S, P, const N: usize> Multiport<'a, ConfigMode7, SPI, EN, D, N> where SPI: SpiBus, EN: OutputPin, + D: DelayNs, { /// Get the current values of the ADC for all owned ports pub async fn get_values(&mut self, buf: &'a mut [u16; N]) -> Result<&'a [u16], Error> {