diff --git a/hal/src/sercom/spi.rs b/hal/src/sercom/spi.rs index d2ee666768f5..bb7f8354c860 100644 --- a/hal/src/sercom/spi.rs +++ b/hal/src/sercom/spi.rs @@ -1515,6 +1515,76 @@ where } } +/// Wrapper type around a [`Spi`] that allows using +/// [`embedded_hal::spi::SpiBus`] even though it only has RX capability. Will +/// panic if any write-adjacent method is used (ie, `write`, `transfer`, +/// `transfer_in_place`, and `flush`). +/// +/// Also implements `Into, `AsRef` and `AsMut` if you need to use +/// `Spi` methods. +/// +/// [`embedded_hal::spi::SpiBus`]: crate::ehal::spi::SpiBus +pub struct PanicOnWrite(T); + +impl From>> for Spi { + fn from(value: PanicOnWrite>) -> Self { + value.0 + } +} + +impl AsRef> for PanicOnWrite> { + fn as_ref(&self) -> &Spi { + &self.0 + } +} +impl AsMut> for PanicOnWrite> { + fn as_mut(&mut self) -> &mut Spi { + &mut self.0 + } +} + +impl Spi { + /// Turn a [`Tx`] [`Spi`] into a [`PanicOnWrite`] + pub fn into_panic_on_write(self) -> PanicOnWrite { + PanicOnWrite(self) + } +} + +/// Wrapper type around a [`Spi`] that allows using +/// [`embedded_hal::spi::SpiBus`] even though it only has TX capability. Will +/// panic if any write-adjacent method is used (ie, `read`, `transfer`, and +/// `transfer_in_place`). +/// +/// Also implements `Into, `AsRef` and `AsMut` if you need to use +/// `Spi` methods. +/// +/// [`embedded_hal::spi::SpiBus`]: crate::ehal::spi::SpiBus +pub struct PanicOnRead(T); + +impl From>> for Spi { + fn from(value: PanicOnRead>) -> Self { + value.0 + } +} + +impl AsRef> for PanicOnRead> { + fn as_ref(&self) -> &Spi { + &self.0 + } +} + +impl AsMut> for PanicOnRead> { + fn as_mut(&mut self) -> &mut Spi { + &mut self.0 + } +} + +impl Spi { + /// Turn a [`Rx`] [`Spi`] into a [`PanicOnRead`] + pub fn into_panic_on_read(self) -> PanicOnRead { + PanicOnRead(self) + } +} #[hal_cfg("sercom0-d5x")] impl Spi, A> diff --git a/hal/src/sercom/spi/impl_ehal.rs b/hal/src/sercom/spi/impl_ehal.rs index a67d18f1476c..5fac9bee2c9e 100644 --- a/hal/src/sercom/spi/impl_ehal.rs +++ b/hal/src/sercom/spi/impl_ehal.rs @@ -7,6 +7,7 @@ use num_traits::PrimInt; #[cfg(feature = "dma")] mod dma; +mod panic_on; #[hal_module( any("sercom0-d11", "sercom0-d21") => "impl_ehal_thumbv6m.rs", diff --git a/hal/src/sercom/spi/impl_ehal/panic_on.rs b/hal/src/sercom/spi/impl_ehal/panic_on.rs new file mode 100644 index 000000000000..d02ff1b79cab --- /dev/null +++ b/hal/src/sercom/spi/impl_ehal/panic_on.rs @@ -0,0 +1,191 @@ +//! [`SpiBus`] implementations for [`PanicOnWrite`] and [`PanicOnRead`] + +use num_traits::{AsPrimitive, PrimInt}; + +use crate::ehal::spi::{ErrorType, SpiBus}; + +use super::{ + Config, DataWidth, Flags, MasterMode, NoneT, PanicOnRead, PanicOnWrite, Rx, Sercom, Size, Spi, + Tx, ValidConfig, ValidPads, Word, +}; + +impl ErrorType for PanicOnRead { + type Error = ::Error; +} + +impl ErrorType for PanicOnWrite { + type Error = ::Error; +} + +/// [`SpiBus`] implementation for [`PanicOnRead`] using word-by-word transfers +impl SpiBus> for PanicOnRead, Tx>> +where + Config: ValidConfig, + P: ValidPads, + M: MasterMode, + C: Size + 'static, + C::Word: PrimInt + AsPrimitive + Copy, + DataWidth: AsPrimitive, +{ + #[inline] + fn read(&mut self, _words: &mut [Word]) -> Result<(), Self::Error> { + unimplemented!("`PanicOnRead` panics on SPI reads"); + } + + #[inline] + fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> { + for word in words { + self.0.block_on_flags(Flags::DRE)?; + unsafe { self.0.write_data(word.as_()) }; + } + + // No need to check for RX buffer overflows, receiver is always disabled for + // TX-only SPIs + self.0.flush_tx(); + Ok(()) + } + + #[inline] + fn transfer(&mut self, _read: &mut [Word], _write: &[Word]) -> Result<(), Self::Error> { + unimplemented!("`PanicOnRead` panics on SPI reads"); + } + + #[inline] + fn transfer_in_place(&mut self, _words: &mut [Word]) -> Result<(), Self::Error> { + unimplemented!("`PanicOnRead` panics on SPI reads"); + } + + #[inline] + fn flush(&mut self) -> Result<(), Self::Error> { + self.0.flush_tx(); + Ok(()) + } +} + +/// [`SpiBus`] implementation for [`PanicOnWrite`] using word-by-word transfers +impl SpiBus> for PanicOnWrite, Rx>> +where + Config: ValidConfig, + P: ValidPads, + M: MasterMode, + C: Size + 'static, + C::Word: PrimInt + AsPrimitive + Copy, + DataWidth: AsPrimitive, +{ + #[inline] + fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { + self.0.read_word_by_word(words) + } + + #[inline] + fn write(&mut self, _words: &[Word]) -> Result<(), Self::Error> { + unimplemented!("`PanicOnWrite` panics on SPI writes"); + } + + #[inline] + fn transfer(&mut self, _read: &mut [Word], _write: &[Word]) -> Result<(), Self::Error> { + unimplemented!("`PanicOnWrite` panics on SPI writes"); + } + + #[inline] + fn transfer_in_place(&mut self, _words: &mut [Word]) -> Result<(), Self::Error> { + unimplemented!("`PanicOnWrite` panics on SPI writes"); + } + + #[inline] + fn flush(&mut self) -> Result<(), Self::Error> { + unimplemented!("`PanicOnWrite` panics on SPI writes"); + } +} + +#[cfg(feature = "dma")] +mod dma { + use super::*; + use crate::dmac::{AnyChannel, Beat, Ready}; + + /// [`SpiBus`] implementation for [`PanicOnRead`] using DMA transfers + impl SpiBus> for PanicOnRead, Tx, NoneT, T>> + where + Config: ValidConfig, + P: ValidPads, + M: MasterMode, + C: Size + 'static, + C::Word: PrimInt + AsPrimitive + Beat, + S: Sercom, + DataWidth: AsPrimitive, + T: AnyChannel, + { + #[inline] + fn read(&mut self, _words: &mut [Word]) -> Result<(), Self::Error> { + unimplemented!("`PanicOnRead` panics on SPI reads"); + } + + #[inline] + fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> { + self.0.write_dma(words)?; + Ok(()) + } + + #[inline] + fn transfer( + &mut self, + _read: &mut [Word], + _write: &[Word], + ) -> Result<(), Self::Error> { + unimplemented!("`PanicOnRead` panics on SPI reads"); + } + + #[inline] + fn transfer_in_place(&mut self, _words: &mut [Word]) -> Result<(), Self::Error> { + unimplemented!("`PanicOnRead` panics on SPI reads"); + } + + #[inline] + fn flush(&mut self) -> Result<(), Self::Error> { + self.0.flush_tx(); + Ok(()) + } + } + + /// [`SpiBus`] implementation for [`PanicOnWrite`] using DMA transfers + impl SpiBus> for PanicOnWrite, Rx, R, T>> + where + Config: ValidConfig, + P: ValidPads, + M: MasterMode, + C: Size + 'static, + C::Word: PrimInt + AsPrimitive + Beat, + DataWidth: AsPrimitive, + R: AnyChannel, + T: AnyChannel, + { + #[inline] + fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { + self.0.read_dma_master(words) + } + + #[inline] + fn write(&mut self, _words: &[Word]) -> Result<(), Self::Error> { + unimplemented!("`PanicOnWrite` panics on SPI writes"); + } + + #[inline] + fn transfer( + &mut self, + _read: &mut [Word], + _write: &[Word], + ) -> Result<(), Self::Error> { + unimplemented!("`PanicOnWrite` panics on SPI writes"); + } + + #[inline] + fn transfer_in_place(&mut self, _words: &mut [Word]) -> Result<(), Self::Error> { + unimplemented!("`PanicOnWrite` panics on SPI writes"); + } + + #[inline] + fn flush(&mut self) -> Result<(), Self::Error> { + unimplemented!("`PanicOnWrite` panics on SPI writes"); + } + } +}