From a69671ce2245f46b4ce59f6db4082ba54ee4a7e4 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sat, 15 Jun 2024 19:05:50 +0200 Subject: [PATCH 1/3] bumped embedded HAL --- CHANGELOG.md | 5 + Cargo.toml | 7 +- src/lib.rs | 290 ++++++++++++++++++++++++--------------------------- 3 files changed, 143 insertions(+), 159 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d46aff..773aa5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [unreleased] +## [v0.3.0] + +- Update `embedded-hal` to v1. +- Add optional `defmt` feature. + ## [v0.2.1] - README tweaks diff --git a/Cargo.toml b/Cargo.toml index 7f786b3..519dbd6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "max116xx-10bit" -version = "0.2.1" +version = "0.3.0" authors = ["Robin Mueller "] edition = "2021" description = "Driver crate for the MAX116xx 10-bit ADC devices" @@ -12,5 +12,6 @@ categories = ["embedded", "no-std", "hardware-support"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -embedded-hal = { version = "0.2.6", features = ["unproven"] } -nb = "1.0.0" +embedded-hal = "1" +nb = "1" +defmt = { version = "0.3", optional = true } diff --git a/src/lib.rs b/src/lib.rs index 5421eae..903e423 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,12 +22,9 @@ //! using a [thin abstraction layer](https://egit.irs.uni-stuttgart.de/rust/vorago-reb1/src/branch/main/src/max11619.rs) #![no_std] use core::{marker::PhantomData, slice::IterMut}; -use embedded_hal::{ - blocking::delay::DelayUs, - blocking::spi::Transfer, - digital::v2::{InputPin, OutputPin}, - spi::FullDuplex, -}; +use embedded_hal::delay::DelayNs; +use embedded_hal::digital::{InputPin, OutputPin}; +use embedded_hal::spi::SpiDevice; //================================================================================================== // Type-level support @@ -90,15 +87,15 @@ impl HasChannels for Max11625 { const NUM: u8 = 16; } -pub trait Clocked: private::Sealed { +pub trait ClockedProvider: private::Sealed { const CLK_SEL: ClockMode; } -pub trait InternallyClocked: Clocked {} +pub trait InternallyClocked: ClockedProvider {} pub struct InternallyClockedInternallyTimedCnvst {} impl private::Sealed for InternallyClockedInternallyTimedCnvst {} -impl Clocked for InternallyClockedInternallyTimedCnvst { +impl ClockedProvider for InternallyClockedInternallyTimedCnvst { const CLK_SEL: ClockMode = ClockMode::InternalClockInternallyTimedCnvst; } impl InternallyClocked for InternallyClockedInternallyTimedCnvst {} @@ -106,7 +103,7 @@ type IntClkdIntTmdCnvst = InternallyClockedInternallyTimedCnvst; pub struct InternallyClockedExternallyTimedCnvst {} impl private::Sealed for InternallyClockedExternallyTimedCnvst {} -impl Clocked for InternallyClockedExternallyTimedCnvst { +impl ClockedProvider for InternallyClockedExternallyTimedCnvst { const CLK_SEL: ClockMode = ClockMode::InternalClockExternallyTimedCnvst; } impl InternallyClocked for InternallyClockedExternallyTimedCnvst {} @@ -114,7 +111,7 @@ type IntClkdExtTmdCnvst = InternallyClockedExternallyTimedCnvst; pub struct InternallyClockedInternallyTimedSerialInterface {} impl private::Sealed for InternallyClockedInternallyTimedSerialInterface {} -impl Clocked for InternallyClockedInternallyTimedSerialInterface { +impl ClockedProvider for InternallyClockedInternallyTimedSerialInterface { const CLK_SEL: ClockMode = ClockMode::InternalClockInternallyTimedSerialInterface; } impl InternallyClocked for InternallyClockedInternallyTimedSerialInterface {} @@ -122,7 +119,7 @@ type IntClkdIntTmdSerIF = InternallyClockedInternallyTimedSerialInterface; pub struct ExternallyClocked {} impl private::Sealed for ExternallyClocked {} -impl Clocked for ExternallyClocked { +impl ClockedProvider for ExternallyClocked { const CLK_SEL: ClockMode = ClockMode::ExternalClockExternallyTimedSclk; } type ExtClkd = ExternallyClocked; @@ -133,6 +130,7 @@ type ExtClkd = ExternallyClocked; /// Clock modes for the MAX116XX devices #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ClockMode { /// Internally timed, CNVST only needs to be pulsed for 40ns. /// CNVST Configuration: CNVST active low @@ -149,6 +147,7 @@ pub enum ClockMode { /// Voltage reference modes #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum VoltageRefMode { /// Auto-Shutdown is on, wake-up delay of 65 us InternalRefWithWakeupDelay = 0b00, @@ -158,6 +157,7 @@ pub enum VoltageRefMode { /// Specifies how many conversions are performed and then averaged for each /// requested result +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum AveragingConversions { OneConversion = 0b000, FourConversions = 0b100, @@ -168,6 +168,7 @@ pub enum AveragingConversions { /// Specifies the number of returned result in single scan mode #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum AveragingResults { FourResults = 0b00, EightResults = 0b01, @@ -176,6 +177,7 @@ pub enum AveragingResults { } #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ScanMode { Scan0ToChannelN = 0b00, ScanChannelNToHighest = 0b01, @@ -184,6 +186,7 @@ pub enum ScanMode { } #[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum AdcError { InvalidChannel, InvalidRefMode, @@ -208,6 +211,8 @@ impl From for Error { } } +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] struct InternalCfg { clk_mode: ClockMode, ref_mode: VoltageRefMode, @@ -216,16 +221,17 @@ struct InternalCfg { results_len: u8, requested_conversions: usize, } + //================================================================================================== // ADC implementation //================================================================================================== -pub struct Max116xx10Bit { - spi: SPI, - cs: CS, +pub struct Max116xx10Bit { + spi: Spi, + cs: Cs, cfg: InternalCfg, - clocked: PhantomData, - delay: PhantomData, + clocked: PhantomData, + delay: PhantomData, } pub struct Max116xx10BitEocExt { @@ -243,27 +249,23 @@ pub struct Max116xx10BitCnvstEocExt Max116xx10Bit -where - SPI: Transfer + FullDuplex, - CS: OutputPin, -{ - pub fn max11618(spi: SPI, cs: CS) -> Result> { +impl Max116xx10Bit { + pub fn max11618(spi: Spi, cs: Cs) -> Result> { Self::new::(spi, cs) } - pub fn max11619(spi: SPI, cs: CS) -> Result> { + pub fn max11619(spi: Spi, cs: Cs) -> Result> { Self::new::(spi, cs) } - pub fn max11620(spi: SPI, cs: CS) -> Result> { + pub fn max11620(spi: Spi, cs: Cs) -> Result> { Self::new::(spi, cs) } - pub fn max11621(spi: SPI, cs: CS) -> Result> { + pub fn max11621(spi: Spi, cs: Cs) -> Result> { Self::new::(spi, cs) } - pub fn max11624(spi: SPI, cs: CS) -> Result> { + pub fn max11624(spi: Spi, cs: Cs) -> Result> { Self::new::(spi, cs) } - pub fn max11625(spi: SPI, cs: CS) -> Result> { + pub fn max11625(spi: Spi, cs: Cs) -> Result> { Self::new::(spi, cs) } @@ -274,7 +276,7 @@ where /// /// The corresponding SETUP register is `0b0111_0100` /// Please note that you still might have to reset and setup the ADC. - pub fn new(spi: SPI, cs: CS) -> Result> { + pub fn new(spi: Spi, cs: Cs) -> Result> { let max_dev = Max116xx10Bit { spi, cs, @@ -298,7 +300,7 @@ where /// The corresponding SETUP register is `0b0111_0000` pub fn into_ext_clkd_with_int_ref_wakeup_delay( self, - ) -> Max116xx10Bit { + ) -> Max116xx10Bit { Max116xx10Bit { spi: self.spi, cs: self.cs, @@ -321,7 +323,7 @@ where /// The corresponding SETUP register is `0b0111_1000` pub fn into_ext_clkd_with_int_ref_no_wakeup_delay( self, - ) -> Max116xx10Bit { + ) -> Max116xx10Bit { Max116xx10Bit { spi: self.spi, cs: self.cs, @@ -342,10 +344,10 @@ where /// and a wakeup delay. This can be used to reduce power consumption /// /// The corresponding SETUP register is `0b0110_1100` - pub fn into_int_clkd_int_timed_through_ser_if_with_wakeup>( + pub fn into_int_clkd_int_timed_through_ser_if_with_wakeup( self, - eoc: EOC, - ) -> Max116xx10BitEocExt { + eoc: Eoc, + ) -> Max116xx10BitEocExt { Max116xx10BitEocExt { base: Max116xx10Bit { spi: self.spi, @@ -371,11 +373,11 @@ where /// The corresponding SETUP register can be one of the two /// - External Voltage reference: `0b0110_0100` /// - Internal Voltage reference always on: `0b0110_1000` - pub fn into_int_clkd_int_timed_through_ser_if_without_wakeup>( + pub fn into_int_clkd_int_timed_through_ser_if_without_wakeup( self, v_ref: VoltageRefMode, - eoc: EOC, - ) -> Result, AdcError> { + eoc: Eoc, + ) -> Result, AdcError> { if v_ref == VoltageRefMode::InternalRefWithWakeupDelay { return Err(AdcError::InvalidRefMode); } @@ -399,22 +401,20 @@ where } } -impl Max116xx10Bit -where - SPI: Transfer + FullDuplex, - CS: OutputPin, +impl + Max116xx10Bit { #[inline] - fn send_wrapper(&mut self, byte: u8) -> Result<(), Error> { + fn send_wrapper(&mut self, byte: u8) -> Result<(), Error> { self.cs.set_low().map_err(Error::Pin)?; - nb::block!(self.spi.send(byte)).map_err(Error::Spi)?; + self.spi.write(&[byte]).map_err(Error::Spi)?; self.cs.set_high().map_err(Error::Pin)?; Ok(()) } /// Set up the ADC depending on clock and reference configuration #[inline] - pub fn setup(&mut self) -> Result<(), Error> { + pub fn setup(&mut self) -> Result<(), Error> { self.send_wrapper(self.get_setup_byte()) } @@ -425,13 +425,13 @@ where &mut self, avg_conv: AveragingConversions, avg_res: AveragingResults, - ) -> Result<(), Error> { + ) -> Result<(), Error> { self.cfg.results_len = Self::get_results_len(avg_res); self.send_wrapper(Self::get_averaging_byte(avg_conv, avg_res)) } #[inline] - pub fn reset(&mut self, fifo_only: bool) -> Result<(), Error> { + pub fn reset(&mut self, fifo_only: bool) -> Result<(), Error> { let mut reset_byte = 0b0001_0000; if fifo_only { reset_byte |= 1 << 3; @@ -468,24 +468,24 @@ where /// Generic function which can be used a single result is available /// when EOC is low - fn internal_read_single_channel>( + fn internal_read_single_channel>( &mut self, - eoc: &mut EOC, - ) -> nb::Result> { + eoc: &mut Eoc, + ) -> nb::Result> { if self.cfg.pending_scan_mode.is_none() { return Err(nb::Error::Other(Error::Adc(AdcError::NoPendingOperation))); } else if self.cfg.pending_scan_mode != Some(ScanMode::ConvertChannelNOnce) { return Err(nb::Error::Other(Error::Adc(AdcError::PendingOperation))); } if eoc.is_low().map_err(Error::Pin)? { - let mut dummy_cmd: [u8; 2] = [0; 2]; + let mut reply_buf: [u8; 2] = [0; 2]; self.cs.set_low().map_err(Error::Pin)?; - let transfer_result = self.spi.transfer(&mut dummy_cmd); + let transfer_result = self.spi.read(&mut reply_buf); self.cs.set_high().map_err(Error::Pin)?; match transfer_result { - Ok(reply) => { + Ok(_) => { self.cfg.pending_scan_mode = None; - Ok(((reply[0] as u16) << 6) | (reply[1] as u16 >> 2)) + Ok(((reply_buf[0] as u16) << 6) | (reply_buf[1] as u16 >> 2)) } Err(e) => Err(nb::Error::Other(Error::Spi(e))), } @@ -499,7 +499,7 @@ macro_rules! ext_impl { () => { /// Set up the ADC depending on clock and reference configuration #[inline] - pub fn setup(&mut self) -> Result<(), Error> { + pub fn setup(&mut self) -> Result<(), Error> { self.base.send_wrapper(self.base.get_setup_byte()) } @@ -510,16 +510,16 @@ macro_rules! ext_impl { &mut self, avg_conv: AveragingConversions, avg_res: AveragingResults, - ) -> Result<(), Error> { - self.base.cfg.results_len = Max116xx10Bit::::get_results_len(avg_res); + ) -> Result<(), Error> { + self.base.cfg.results_len = Max116xx10Bit::::get_results_len(avg_res); self.base - .send_wrapper(Max116xx10Bit::::get_averaging_byte( + .send_wrapper(Max116xx10Bit::::get_averaging_byte( avg_conv, avg_res, )) } #[inline] - pub fn reset(&mut self, fifo_only: bool) -> Result<(), Error> { + pub fn reset(&mut self, fifo_only: bool) -> Result<(), Error> { let mut reset_byte = 0b0001_0000; if fifo_only { reset_byte |= 1 << 3; @@ -528,19 +528,14 @@ macro_rules! ext_impl { } }; } -impl Max116xx10BitEocExt -where - SPI: Transfer + FullDuplex, - CS: OutputPin, +impl + Max116xx10BitEocExt { ext_impl!(); } -impl - Max116xx10BitCnvstEocExt -where - SPI: Transfer + FullDuplex, - CS: OutputPin, +impl + Max116xx10BitCnvstEocExt { ext_impl!(); } @@ -550,16 +545,12 @@ where //================================================================================================== /// Implementations when using the external SPI clock to time the conversions -impl Max116xx10Bit -where - SPI: Transfer + FullDuplex, - CS: OutputPin, -{ +impl Max116xx10Bit { pub fn read_single_channel( &mut self, buf: &mut [u8], channel_num: u8, - ) -> Result> { + ) -> Result> { if buf.len() < 3 { return Err(Error::Adc(AdcError::CmdBufTooSmall)); } @@ -567,9 +558,9 @@ where buf[1] = 0x00; buf[2] = 0x00; self.cs.set_low().map_err(Error::Pin)?; - let reply = self.spi.transfer(&mut buf[0..3]).ok().unwrap(); + self.spi.transfer_in_place(&mut buf[0..3]).ok().unwrap(); self.cs.set_high().map_err(Error::Pin)?; - Ok(((reply[1] as u16) << 6) | (reply[2] as u16 >> 2)) + Ok(((buf[1] as u16) << 6) | (buf[2] as u16 >> 2)) } pub fn read_multiple_channels_0_to_n( @@ -577,7 +568,7 @@ where buf: &mut [u8], result_iter: &mut IterMut, n: u8, - ) -> Result<(), Error> { + ) -> Result<(), Error> { let mut iter = buf.iter_mut(); let mut next_byte: &mut u8; for idx in 0..n + 1 { @@ -589,12 +580,11 @@ where next_byte = iter.next().ok_or(Error::Adc(AdcError::CmdBufTooSmall))?; *next_byte = 0x00; self.cs.set_low().map_err(Error::Pin)?; - let reply = self - .spi - .transfer(&mut buf[0..((n + 1) * 2 + 1) as usize]) + self.spi + .transfer_in_place(&mut buf[0..((n + 1) * 2 + 1) as usize]) .map_err(Error::Spi)?; self.cs.set_high().map_err(Error::Pin)?; - let mut reply_iter = reply.iter(); + let mut reply_iter = buf.iter(); // Skip first reply byte reply_iter.next().unwrap(); for _ in 0..n + 1 { @@ -612,7 +602,7 @@ where buf: &mut [u8], result_iter: &mut IterMut, n: u8, - ) -> Result<(), Error> { + ) -> Result<(), Error> { let mut iter = buf.iter_mut(); let mut next_byte: &mut u8; if n > self.cfg.max_channels - 1 { @@ -628,12 +618,11 @@ where next_byte = iter.next().ok_or(Error::Adc(AdcError::CmdBufTooSmall))?; *next_byte = 0x00; self.cs.set_low().map_err(Error::Pin)?; - let reply = self - .spi - .transfer(&mut buf[0..(conversions * 2 + 1) as usize]) + self.spi + .transfer_in_place(&mut buf[0..(conversions * 2 + 1) as usize]) .map_err(Error::Spi)?; self.cs.set_high().map_err(Error::Pin)?; - let mut reply_iter = reply.iter(); + let mut reply_iter = buf.iter(); // Skip first reply byte reply_iter.next().unwrap(); for _ in 0..conversions { @@ -649,17 +638,13 @@ where /// Implementations when using the external SPI clock to time the conversions but also requiring /// a wakeup delay -impl Max116xx10Bit -where - SPI: Transfer + FullDuplex, - CS: OutputPin, -{ - pub fn read_single_channel>( +impl Max116xx10Bit { + pub fn read_single_channel( &mut self, buf: &mut [u8], channel_num: u8, - delay: &mut DELAY, - ) -> Result> { + delay: &mut Delay, + ) -> Result> { if buf.len() < 3 { return Err(Error::Adc(AdcError::CmdBufTooSmall)); } @@ -671,18 +656,20 @@ where buf[0] = 0x00; buf[1] = 0x00; self.cs.set_low().map_err(Error::Pin)?; - let reply = self.spi.transfer(&mut buf[0..2]).map_err(Error::Spi)?; + self.spi + .transfer_in_place(&mut buf[0..2]) + .map_err(Error::Spi)?; self.cs.set_high().map_err(Error::Pin)?; - Ok(((reply[0] as u16) << 6) | (reply[1] as u16 >> 2)) + Ok(((buf[0] as u16) << 6) | (buf[1] as u16 >> 2)) } - pub fn read_multiple_channels_0_to_n>( + pub fn read_multiple_channels_0_to_n( &mut self, buf: &mut [u8], result_iter: &mut IterMut, n: u8, - delay: &mut DELAY, - ) -> Result<(), Error> { + delay: &mut Delay, + ) -> Result<(), Error> { let mut iter = buf.iter_mut(); let mut next_byte: &mut u8; for idx in 0..n + 1 { @@ -698,12 +685,11 @@ where delay.delay_us(65); self.cs.set_low().map_err(Error::Pin)?; - let reply = self - .spi - .transfer(&mut buf[1..((n + 1) * 2 + 1) as usize]) + self.spi + .transfer_in_place(&mut buf[1..((n + 1) * 2 + 1) as usize]) .map_err(Error::Spi)?; self.cs.set_high().map_err(Error::Pin)?; - let mut reply_iter = reply.iter(); + let mut reply_iter = buf.iter(); for _ in 0..n + 1 { let next_res = result_iter .next() @@ -714,13 +700,13 @@ where Ok(()) } - pub fn read_multiple_channels_n_to_highest>( + pub fn read_multiple_channels_n_to_highest( &mut self, buf: &mut [u8], result_iter: &mut IterMut, n: u8, - delay: &mut DELAY, - ) -> Result<(), Error> { + delay: &mut Delay, + ) -> Result<(), Error> { let mut iter = buf.iter_mut(); let mut next_byte: &mut u8; if n > self.cfg.max_channels - 1 { @@ -740,12 +726,11 @@ where self.send_wrapper(buf[0])?; delay.delay_us(65); self.cs.set_low().map_err(Error::Pin)?; - let reply = self - .spi - .transfer(&mut buf[1..(conversions * 2 + 1) as usize]) + self.spi + .transfer_in_place(&mut buf[1..(conversions * 2 + 1) as usize]) .map_err(Error::Spi)?; self.cs.set_high().map_err(Error::Pin)?; - let mut reply_iter = reply.iter(); + let mut reply_iter = buf.iter(); for _ in 0..conversions { let next_res = result_iter .next() @@ -763,18 +748,15 @@ where /// Implementations when using the internal clock with a conversion started /// through the serial interface -impl Max116xx10BitEocExt -where - SPI: Transfer + FullDuplex, - CS: OutputPin, - EOC: InputPin, +impl, Eoc: InputPin, PinE> + Max116xx10BitEocExt { #[inline] fn request_wrapper( &mut self, channel_num: u8, scan_mode: ScanMode, - ) -> Result<(), Error> { + ) -> Result<(), Error> { if self.base.cfg.pending_scan_mode.is_some() { return Err(Error::Adc(AdcError::PendingOperation)); } @@ -787,7 +769,10 @@ where Ok(()) } - pub fn request_single_channel(&mut self, channel_num: u8) -> Result<(), Error> { + pub fn request_single_channel( + &mut self, + channel_num: u8, + ) -> Result<(), Error> { self.request_wrapper(channel_num, ScanMode::ConvertChannelNOnce) } @@ -797,11 +782,14 @@ where pub fn request_channel_n_repeatedly( &mut self, channel_num: u8, - ) -> Result<(), Error> { + ) -> Result<(), Error> { self.request_wrapper(channel_num, ScanMode::ScanChannelNRepeatedly) } - pub fn request_multiple_channels_0_to_n(&mut self, n: u8) -> Result<(), Error> { + pub fn request_multiple_channels_0_to_n( + &mut self, + n: u8, + ) -> Result<(), Error> { self.base.cfg.requested_conversions = n as usize + 1; self.request_wrapper(n, ScanMode::Scan0ToChannelN) } @@ -809,7 +797,7 @@ where pub fn request_multiple_channels_n_to_highest( &mut self, n: u8, - ) -> Result<(), Error> { + ) -> Result<(), Error> { self.base.cfg.requested_conversions = self.base.cfg.max_channels as usize + 1 - n as usize; self.request_wrapper(n, ScanMode::ScanChannelNToHighest) } @@ -818,7 +806,7 @@ where /// needs to be passed explicitely here. /// If no request was made, [AdcError::NoPendingOperation] is returned. /// If a request was made for multipel results, [AdcError::PendingOperation] will be returned. - pub fn get_single_channel(&mut self) -> nb::Result> { + pub fn get_single_channel(&mut self) -> nb::Result> { self.base.internal_read_single_channel(&mut self.eoc) } @@ -828,7 +816,7 @@ where pub fn get_multi_channel( &mut self, result_iter: &mut IterMut, - ) -> nb::Result<(), Error> { + ) -> nb::Result<(), Error> { if self.base.cfg.pending_scan_mode.is_none() { return Err(nb::Error::Other(Error::Adc(AdcError::NoPendingOperation))); } else if self.base.cfg.pending_scan_mode == Some(ScanMode::ConvertChannelNOnce) { @@ -836,7 +824,7 @@ where } if self.eoc.is_low().map_err(Error::Pin)? { // maximum length of reply is 32 for 16 channels - let mut dummy_cmd: [u8; 32] = [0; 32]; + let mut reply_buf: [u8; 32] = [0; 32]; let num_conv: usize = if self.base.cfg.pending_scan_mode == Some(ScanMode::ScanChannelNRepeatedly) { self.base.cfg.results_len as usize @@ -846,22 +834,20 @@ where self.base.cfg.pending_scan_mode = None; self.base.cfg.requested_conversions = 0; self.base.cs.set_low().map_err(Error::Pin)?; - let transfer_result = self.base.spi.transfer(&mut dummy_cmd[0..(num_conv * 2)]); + self.base + .spi + .read(&mut reply_buf[0..(num_conv * 2)]) + .map_err(Error::Spi)?; self.base.cs.set_high().map_err(Error::Pin)?; - match transfer_result { - Ok(reply) => { - let mut reply_iter = reply.iter(); - for _ in 0..num_conv { - 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(()) - } - Err(e) => Err(nb::Error::Other(Error::Spi(e))), + let mut reply_iter = reply_buf.iter(); + for _ in 0..num_conv { + 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(()) } else { Err(nb::Error::WouldBlock) } @@ -877,17 +863,9 @@ where /// /// TODO: Implement. Unfortunately, the test board used to verify this library did not have /// the CNVST connected, so I wouldn't be able to test an implementation easily. -impl - Max116xx10BitCnvstEocExt -where - SPI: Transfer + FullDuplex, - CS: OutputPin, - EOC: InputPin, - CNVST: OutputPin, +impl + Max116xx10BitCnvstEocExt { - pub fn dummy() { - todo!("Implement this") - } } /// Implementations when using the internal clock where CNVST is only pulsed to start acquisition @@ -895,13 +873,13 @@ where /// /// TODO: Test. Unfortunately, the test board used to verify this library did not have /// the CNVST connected, so I wouldn't be able to test an implementation easily. -impl - Max116xx10BitCnvstEocExt -where - SPI: Transfer + FullDuplex, - CS: OutputPin, - EOC: InputPin, - CNVST: OutputPin, +impl< + Spi: SpiDevice, + Cs: OutputPin, + Eoc: InputPin, + Cnvst: OutputPin, + PinE, + > Max116xx10BitCnvstEocExt { /// The pulse needs to be at least 40ns. A pulse cycle value can be used to increase /// the width of the pulse @@ -909,7 +887,7 @@ where &mut self, channel_num: u8, pulse_cycles: u8, - ) -> Result<(), Error> { + ) -> Result<(), Error> { self.request_wrapper(channel_num, ScanMode::ConvertChannelNOnce, pulse_cycles) } @@ -919,7 +897,7 @@ where channel_num: u8, scan_mode: ScanMode, pulse_cycles: u8, - ) -> Result<(), Error> { + ) -> Result<(), Error> { if self.base.cfg.pending_scan_mode.is_some() { return Err(Error::Adc(AdcError::PendingOperation)); } @@ -935,7 +913,7 @@ where Ok(()) } - pub fn get_single_channel(&mut self) -> nb::Result> { + pub fn get_single_channel(&mut self) -> nb::Result> { self.base.internal_read_single_channel(&mut self.eoc) } } From 88270085c97a6aa6698f24ce0352f23e6fba531a Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sat, 15 Jun 2024 19:30:49 +0200 Subject: [PATCH 2/3] remove chip select management --- src/lib.rs | 156 +++++++++++++++++++++-------------------------------- 1 file changed, 61 insertions(+), 95 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 903e423..d2d177d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,7 @@ //! You can find an example application [here](https://egit.irs.uni-stuttgart.de/rust/vorago-reb1/src/branch/main/examples/max11619-adc.rs) //! using a [thin abstraction layer](https://egit.irs.uni-stuttgart.de/rust/vorago-reb1/src/branch/main/src/max11619.rs) #![no_std] +use core::convert::Infallible; use core::{marker::PhantomData, slice::IterMut}; use embedded_hal::delay::DelayNs; use embedded_hal::digital::{InputPin, OutputPin}; @@ -226,21 +227,20 @@ struct InternalCfg { // ADC implementation //================================================================================================== -pub struct Max116xx10Bit { +pub struct Max116xx10Bit { spi: Spi, - cs: Cs, cfg: InternalCfg, clocked: PhantomData, delay: PhantomData, } -pub struct Max116xx10BitEocExt { - base: Max116xx10Bit, +pub struct Max116xx10BitEocExt { + base: Max116xx10Bit, eoc: EOC, } -pub struct Max116xx10BitCnvstEocExt { - base: Max116xx10Bit, +pub struct Max116xx10BitCnvstEocExt { + base: Max116xx10Bit, eoc: EOC, cnvst: CNVST, } @@ -249,24 +249,24 @@ pub struct Max116xx10BitCnvstEocExt Max116xx10Bit { - pub fn max11618(spi: Spi, cs: Cs) -> Result> { - Self::new::(spi, cs) +impl Max116xx10Bit { + pub fn max11618(spi: Spi) -> Result> { + Self::new::(spi) } - pub fn max11619(spi: Spi, cs: Cs) -> Result> { - Self::new::(spi, cs) + pub fn max11619(spi: Spi) -> Result> { + Self::new::(spi) } - pub fn max11620(spi: Spi, cs: Cs) -> Result> { - Self::new::(spi, cs) + pub fn max11620(spi: Spi) -> Result> { + Self::new::(spi) } - pub fn max11621(spi: Spi, cs: Cs) -> Result> { - Self::new::(spi, cs) + pub fn max11621(spi: Spi) -> Result> { + Self::new::(spi) } - pub fn max11624(spi: Spi, cs: Cs) -> Result> { - Self::new::(spi, cs) + pub fn max11624(spi: Spi) -> Result> { + Self::new::(spi) } - pub fn max11625(spi: Spi, cs: Cs) -> Result> { - Self::new::(spi, cs) + pub fn max11625(spi: Spi) -> Result> { + Self::new::(spi) } /// Create a new generic MAX116xx instance. By default the generated ADC struct is configured @@ -276,10 +276,9 @@ impl Max116xx10Bit(spi: Spi, cs: Cs) -> Result> { + pub fn new(spi: Spi) -> Result> { let max_dev = Max116xx10Bit { spi, - cs, cfg: InternalCfg { clk_mode: ExtClkd::CLK_SEL, ref_mode: VoltageRefMode::ExternalSingleEndedNoWakeupDelay, @@ -300,10 +299,9 @@ impl Max116xx10Bit Max116xx10Bit { + ) -> Max116xx10Bit { Max116xx10Bit { spi: self.spi, - cs: self.cs, cfg: InternalCfg { clk_mode: ExtClkd::CLK_SEL, ref_mode: VoltageRefMode::InternalRefWithWakeupDelay, @@ -323,10 +321,9 @@ impl Max116xx10Bit Max116xx10Bit { + ) -> Max116xx10Bit { Max116xx10Bit { spi: self.spi, - cs: self.cs, cfg: InternalCfg { clk_mode: ExtClkd::CLK_SEL, ref_mode: VoltageRefMode::InternalRefWithoutWakeupDelay, @@ -347,11 +344,10 @@ impl Max116xx10Bit( self, eoc: Eoc, - ) -> Max116xx10BitEocExt { + ) -> Max116xx10BitEocExt { Max116xx10BitEocExt { base: Max116xx10Bit { spi: self.spi, - cs: self.cs, cfg: InternalCfg { clk_mode: IntClkdIntTmdSerIF::CLK_SEL, ref_mode: VoltageRefMode::InternalRefWithWakeupDelay, @@ -377,14 +373,13 @@ impl Max116xx10Bit Result, AdcError> { + ) -> Result, AdcError> { if v_ref == VoltageRefMode::InternalRefWithWakeupDelay { return Err(AdcError::InvalidRefMode); } Ok(Max116xx10BitEocExt { base: Max116xx10Bit { spi: self.spi, - cs: self.cs, cfg: InternalCfg { clk_mode: IntClkdIntTmdSerIF::CLK_SEL, ref_mode: VoltageRefMode::InternalRefWithWakeupDelay, @@ -401,20 +396,16 @@ impl Max116xx10Bit - Max116xx10Bit -{ +impl Max116xx10Bit { #[inline] - fn send_wrapper(&mut self, byte: u8) -> Result<(), Error> { - self.cs.set_low().map_err(Error::Pin)?; + fn send_wrapper(&mut self, byte: u8) -> Result<(), Error> { self.spi.write(&[byte]).map_err(Error::Spi)?; - self.cs.set_high().map_err(Error::Pin)?; Ok(()) } /// Set up the ADC depending on clock and reference configuration #[inline] - pub fn setup(&mut self) -> Result<(), Error> { + pub fn setup(&mut self) -> Result<(), Error> { self.send_wrapper(self.get_setup_byte()) } @@ -425,13 +416,13 @@ impl &mut self, avg_conv: AveragingConversions, avg_res: AveragingResults, - ) -> Result<(), Error> { + ) -> Result<(), Error> { self.cfg.results_len = Self::get_results_len(avg_res); self.send_wrapper(Self::get_averaging_byte(avg_conv, avg_res)) } #[inline] - pub fn reset(&mut self, fifo_only: bool) -> Result<(), Error> { + pub fn reset(&mut self, fifo_only: bool) -> Result<(), Error> { let mut reset_byte = 0b0001_0000; if fifo_only { reset_byte |= 1 << 3; @@ -468,10 +459,10 @@ impl /// Generic function which can be used a single result is available /// when EOC is low - fn internal_read_single_channel>( + fn internal_read_single_channel( &mut self, eoc: &mut Eoc, - ) -> nb::Result> { + ) -> nb::Result> { if self.cfg.pending_scan_mode.is_none() { return Err(nb::Error::Other(Error::Adc(AdcError::NoPendingOperation))); } else if self.cfg.pending_scan_mode != Some(ScanMode::ConvertChannelNOnce) { @@ -479,9 +470,7 @@ impl } if eoc.is_low().map_err(Error::Pin)? { let mut reply_buf: [u8; 2] = [0; 2]; - self.cs.set_low().map_err(Error::Pin)?; let transfer_result = self.spi.read(&mut reply_buf); - self.cs.set_high().map_err(Error::Pin)?; match transfer_result { Ok(_) => { self.cfg.pending_scan_mode = None; @@ -499,7 +488,7 @@ macro_rules! ext_impl { () => { /// Set up the ADC depending on clock and reference configuration #[inline] - pub fn setup(&mut self) -> Result<(), Error> { + pub fn setup(&mut self) -> Result<(), Error> { self.base.send_wrapper(self.base.get_setup_byte()) } @@ -510,16 +499,16 @@ macro_rules! ext_impl { &mut self, avg_conv: AveragingConversions, avg_res: AveragingResults, - ) -> Result<(), Error> { - self.base.cfg.results_len = Max116xx10Bit::::get_results_len(avg_res); + ) -> Result<(), Error> { + self.base.cfg.results_len = Max116xx10Bit::::get_results_len(avg_res); self.base - .send_wrapper(Max116xx10Bit::::get_averaging_byte( + .send_wrapper(Max116xx10Bit::::get_averaging_byte( avg_conv, avg_res, )) } #[inline] - pub fn reset(&mut self, fifo_only: bool) -> Result<(), Error> { + pub fn reset(&mut self, fifo_only: bool) -> Result<(), Error> { let mut reset_byte = 0b0001_0000; if fifo_only { reset_byte |= 1 << 3; @@ -528,14 +517,12 @@ macro_rules! ext_impl { } }; } -impl - Max116xx10BitEocExt -{ +impl Max116xx10BitEocExt { ext_impl!(); } -impl - Max116xx10BitCnvstEocExt +impl + Max116xx10BitCnvstEocExt { ext_impl!(); } @@ -545,21 +532,19 @@ impl //================================================================================================== /// Implementations when using the external SPI clock to time the conversions -impl Max116xx10Bit { +impl Max116xx10Bit { pub fn read_single_channel( &mut self, buf: &mut [u8], channel_num: u8, - ) -> Result> { + ) -> Result> { if buf.len() < 3 { return Err(Error::Adc(AdcError::CmdBufTooSmall)); } buf[0] = self.get_conversion_byte(ScanMode::ConvertChannelNOnce, channel_num)?; buf[1] = 0x00; buf[2] = 0x00; - self.cs.set_low().map_err(Error::Pin)?; self.spi.transfer_in_place(&mut buf[0..3]).ok().unwrap(); - self.cs.set_high().map_err(Error::Pin)?; Ok(((buf[1] as u16) << 6) | (buf[2] as u16 >> 2)) } @@ -568,7 +553,7 @@ impl Max116xx10Bit, n: u8, - ) -> Result<(), Error> { + ) -> Result<(), Error> { let mut iter = buf.iter_mut(); let mut next_byte: &mut u8; for idx in 0..n + 1 { @@ -579,11 +564,9 @@ impl Max116xx10Bit Max116xx10Bit, n: u8, - ) -> Result<(), Error> { + ) -> Result<(), Error> { let mut iter = buf.iter_mut(); let mut next_byte: &mut u8; if n > self.cfg.max_channels - 1 { @@ -617,11 +600,9 @@ impl Max116xx10Bit Max116xx10Bit Max116xx10Bit { +impl Max116xx10Bit { pub fn read_single_channel( &mut self, buf: &mut [u8], channel_num: u8, delay: &mut Delay, - ) -> Result> { + ) -> Result> { if buf.len() < 3 { return Err(Error::Adc(AdcError::CmdBufTooSmall)); } @@ -655,11 +636,9 @@ impl Max116xx10Bit> 2)) } @@ -669,7 +648,7 @@ impl Max116xx10Bit, n: u8, delay: &mut Delay, - ) -> Result<(), Error> { + ) -> Result<(), Error> { let mut iter = buf.iter_mut(); let mut next_byte: &mut u8; for idx in 0..n + 1 { @@ -684,11 +663,9 @@ impl Max116xx10Bit Max116xx10Bit, n: u8, delay: &mut Delay, - ) -> Result<(), Error> { + ) -> Result<(), Error> { let mut iter = buf.iter_mut(); let mut next_byte: &mut u8; if n > self.cfg.max_channels - 1 { @@ -725,11 +702,9 @@ impl Max116xx10Bit Max116xx10Bit, Eoc: InputPin, PinE> - Max116xx10BitEocExt -{ +impl Max116xx10BitEocExt { #[inline] fn request_wrapper( &mut self, channel_num: u8, scan_mode: ScanMode, - ) -> Result<(), Error> { + ) -> Result<(), Error> { if self.base.cfg.pending_scan_mode.is_some() { return Err(Error::Adc(AdcError::PendingOperation)); } @@ -764,7 +737,7 @@ impl, Eoc: InputPin, P .base .get_conversion_byte(scan_mode, channel_num) .map_err(Error::Adc)?; - self.base.send_wrapper(conv_byte)?; + self.base.send_wrapper(conv_byte).ok(); self.base.cfg.pending_scan_mode = Some(scan_mode); Ok(()) } @@ -772,7 +745,7 @@ impl, Eoc: InputPin, P pub fn request_single_channel( &mut self, channel_num: u8, - ) -> Result<(), Error> { + ) -> Result<(), Error> { self.request_wrapper(channel_num, ScanMode::ConvertChannelNOnce) } @@ -782,14 +755,14 @@ impl, Eoc: InputPin, P pub fn request_channel_n_repeatedly( &mut self, channel_num: u8, - ) -> Result<(), Error> { + ) -> Result<(), Error> { self.request_wrapper(channel_num, ScanMode::ScanChannelNRepeatedly) } pub fn request_multiple_channels_0_to_n( &mut self, n: u8, - ) -> Result<(), Error> { + ) -> Result<(), Error> { self.base.cfg.requested_conversions = n as usize + 1; self.request_wrapper(n, ScanMode::Scan0ToChannelN) } @@ -797,7 +770,7 @@ impl, Eoc: InputPin, P pub fn request_multiple_channels_n_to_highest( &mut self, n: u8, - ) -> Result<(), Error> { + ) -> Result<(), Error> { self.base.cfg.requested_conversions = self.base.cfg.max_channels as usize + 1 - n as usize; self.request_wrapper(n, ScanMode::ScanChannelNToHighest) } @@ -806,7 +779,7 @@ impl, Eoc: InputPin, P /// needs to be passed explicitely here. /// If no request was made, [AdcError::NoPendingOperation] is returned. /// If a request was made for multipel results, [AdcError::PendingOperation] will be returned. - pub fn get_single_channel(&mut self) -> nb::Result> { + pub fn get_single_channel(&mut self) -> nb::Result> { self.base.internal_read_single_channel(&mut self.eoc) } @@ -816,7 +789,7 @@ impl, Eoc: InputPin, P pub fn get_multi_channel( &mut self, result_iter: &mut IterMut, - ) -> nb::Result<(), Error> { + ) -> nb::Result<(), Error> { if self.base.cfg.pending_scan_mode.is_none() { return Err(nb::Error::Other(Error::Adc(AdcError::NoPendingOperation))); } else if self.base.cfg.pending_scan_mode == Some(ScanMode::ConvertChannelNOnce) { @@ -833,12 +806,10 @@ impl, Eoc: InputPin, P }; self.base.cfg.pending_scan_mode = None; self.base.cfg.requested_conversions = 0; - self.base.cs.set_low().map_err(Error::Pin)?; self.base .spi .read(&mut reply_buf[0..(num_conv * 2)]) .map_err(Error::Spi)?; - self.base.cs.set_high().map_err(Error::Pin)?; let mut reply_iter = reply_buf.iter(); for _ in 0..num_conv { let next_res = result_iter @@ -863,8 +834,8 @@ impl, Eoc: InputPin, P /// /// TODO: Implement. Unfortunately, the test board used to verify this library did not have /// the CNVST connected, so I wouldn't be able to test an implementation easily. -impl - Max116xx10BitCnvstEocExt +impl + Max116xx10BitCnvstEocExt { } @@ -873,13 +844,8 @@ impl /// /// TODO: Test. Unfortunately, the test board used to verify this library did not have /// the CNVST connected, so I wouldn't be able to test an implementation easily. -impl< - Spi: SpiDevice, - Cs: OutputPin, - Eoc: InputPin, - Cnvst: OutputPin, - PinE, - > Max116xx10BitCnvstEocExt +impl, Cnvst: OutputPin, PinE> + Max116xx10BitCnvstEocExt { /// The pulse needs to be at least 40ns. A pulse cycle value can be used to increase /// the width of the pulse @@ -905,7 +871,7 @@ impl< .base .get_conversion_byte(scan_mode, channel_num) .map_err(Error::Adc)?; - self.base.send_wrapper(conv_byte)?; + self.base.send_wrapper(conv_byte).ok(); self.cnvst.set_low().map_err(Error::Pin)?; for _ in 0..pulse_cycles {} self.cnvst.set_high().map_err(Error::Pin)?; From aab40bc0278761a025ebe081d3b408ed0e160a82 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 16 Jun 2024 15:53:27 +0200 Subject: [PATCH 3/3] update CI and docs settings --- .github/bors.toml | 2 - .github/workflows/ci.yml | 87 ++++++++++++++++++++++++++-------------- Cargo.toml | 4 ++ src/lib.rs | 1 + 4 files changed, 63 insertions(+), 31 deletions(-) delete mode 100644 .github/bors.toml diff --git a/.github/bors.toml b/.github/bors.toml deleted file mode 100644 index 1779788..0000000 --- a/.github/bors.toml +++ /dev/null @@ -1,2 +0,0 @@ -status = ["ci"] -delete_merged_branches = true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 757f62a..d6ec01f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,43 +1,72 @@ -on: push - name: ci +on: [push, pull_request] jobs: - ci: + name: Check build + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - run: cargo check --release + + cross-check: + name: Check Cross-Compilation runs-on: ubuntu-latest strategy: matrix: - rust: - - stable target: - - x86_64-unknown-linux-gnu - - thumbv6m-none-eabi - armv7-unknown-linux-gnueabihf - thumbv7em-none-eabihf - steps: - - uses: actions/checkout@v2 - - - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable with: - profile: minimal - toolchain: ${{ matrix.rust }} - target: ${{ matrix.target }} - override: true - components: rustfmt, clippy + targets: "armv7-unknown-linux-gnueabihf, thumbv7em-none-eabihf" + - run: cargo check --release --target=${{matrix.target}} --no-default-features - - uses: actions-rs/cargo@v1 - with: - use-cross: true - command: check + check: + test: + name: Run Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - name: Install nextest + uses: taiki-e/install-action@nextest + - run: cargo nextest run --all-features + - run: cargo test --doc - - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --all -- --check + msrv: + name: Check MSRV + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@1.68.2 + - run: cargo check --release - - uses: actions-rs/cargo@v1 - with: - use-cross: true - command: clippy - args: -- -D warnings + fmt: + name: Check formatting + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - run: cargo fmt --all -- --check + + docs: + name: Check Documentation Build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + - run: cargo +nightly doc --all-features --config 'build.rustdocflags=["--cfg", "docs_rs"]' + + clippy: + name: Clippy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - run: cargo clippy -- -D warnings diff --git a/Cargo.toml b/Cargo.toml index 519dbd6..c1910e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,3 +15,7 @@ categories = ["embedded", "no-std", "hardware-support"] embedded-hal = "1" nb = "1" defmt = { version = "0.3", optional = true } + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docs_rs", "--generate-link-to-definition"] diff --git a/src/lib.rs b/src/lib.rs index d2d177d..3050dba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,7 @@ //! You can find an example application [here](https://egit.irs.uni-stuttgart.de/rust/vorago-reb1/src/branch/main/examples/max11619-adc.rs) //! using a [thin abstraction layer](https://egit.irs.uni-stuttgart.de/rust/vorago-reb1/src/branch/main/src/max11619.rs) #![no_std] +#![cfg_attr(docs_rs, feature(doc_auto_cfg))] use core::convert::Infallible; use core::{marker::PhantomData, slice::IterMut}; use embedded_hal::delay::DelayNs;