From 47cbf96a2abd4f404a6a416973bf862b509b56ea Mon Sep 17 00:00:00 2001 From: RubenD Date: Tue, 12 Mar 2024 19:05:03 -0400 Subject: [PATCH] generic: Implement the SPI traits from embedded-hal 1.0 In addition to the traits from embedded-hal 0.2, also implement the traits from 1.0 for compatibility with newer HAL drivers. Compatibility between the implementations is ensured, so an application may use traits from both e-h versions interspersed. Co-authored-by: Ghislain MARY --- avr-hal-generic/Cargo.toml | 1 + avr-hal-generic/src/lib.rs | 5 +- avr-hal-generic/src/spi.rs | 145 +++++++++++++++++++++++++++++++++++-- 3 files changed, 141 insertions(+), 10 deletions(-) diff --git a/avr-hal-generic/Cargo.toml b/avr-hal-generic/Cargo.toml index b8fc17f419..11942101fd 100644 --- a/avr-hal-generic/Cargo.toml +++ b/avr-hal-generic/Cargo.toml @@ -12,6 +12,7 @@ paste = "1.0.0" avr-device = "0.5.4" embedded-storage = "0.2" embedded-hal = "1.0" +embedded-hal-bus = "0.1" unwrap-infallible = "0.1.5" [dependencies.embedded-hal-v0] diff --git a/avr-hal-generic/src/lib.rs b/avr-hal-generic/src/lib.rs index c9d5e3e66c..41d3c9d688 100644 --- a/avr-hal-generic/src/lib.rs +++ b/avr-hal-generic/src/lib.rs @@ -2,7 +2,8 @@ #![cfg_attr(avr_hal_asm_macro, feature(asm_experimental_arch))] #![cfg_attr(not(avr_hal_asm_macro), feature(llvm_asm))] -pub use embedded_hal_v0 as hal; +pub use embedded_hal as hal; +pub use embedded_hal_v0 as hal_v0; #[doc(hidden)] pub use avr_device; @@ -24,7 +25,7 @@ pub mod wdt; /// Prelude containing all HAL traits pub mod prelude { - pub use crate::hal::prelude::*; + pub use crate::hal_v0::prelude::*; pub use ufmt::uWrite as _ufmt_uWrite; pub use unwrap_infallible::UnwrapInfallible as _unwrap_infallible_UnwrapInfallible; } diff --git a/avr-hal-generic/src/spi.rs b/avr-hal-generic/src/spi.rs index 3bd7fe347f..fd7665b424 100644 --- a/avr-hal-generic/src/spi.rs +++ b/avr-hal-generic/src/spi.rs @@ -1,7 +1,7 @@ //! SPI Implementation use crate::port; use core::marker::PhantomData; -use embedded_hal_v0::spi; +use embedded_hal::spi::{self,SpiBus}; /// Oscillator Clock Frequency division options. /// @@ -69,12 +69,22 @@ impl Default for Settings { /// intermediate abstraction ontop of which the [`Spi`] API is built. **Prefer using the /// [`Spi`] API instead of this trait.** pub trait SpiOps { + /// Sets up the control/status registers with the right settings for this secondary device fn raw_setup(&mut self, settings: &Settings); + /// Disable the peripheral fn raw_release(&mut self); + /// Check the interrupt flag to see if the write has completed + /// + /// Returns `true` if the bus is idle fn raw_check_iflag(&self) -> bool; + /// Read a byte from the data register fn raw_read(&self) -> u8; + /// Write a byte to the data register, which begins transmission + /// automatically. fn raw_write(&mut self, byte: u8); + /// Perform a transaction of a single byte + fn raw_transaction(&mut self, byte: u8) -> u8; } /// Wrapper for the CS pin @@ -114,11 +124,45 @@ impl embedded_hal_v0::digital::v2::ToggleableOutputPin for } } +impl embedded_hal::digital::ErrorType for ChipSelectPin { + type Error = core::convert::Infallible; +} + +impl embedded_hal::digital::OutputPin for ChipSelectPin { + fn set_high(&mut self) -> Result<(), Self::Error> { + self.0.set_high(); + Ok(()) + } + + fn set_low(&mut self) -> Result<(), Self::Error> { + self.0.set_low(); + Ok(()) + } +} + +impl embedded_hal::digital::StatefulOutputPin for ChipSelectPin { + fn is_set_high(&mut self) -> Result { + Ok(self.0.is_set_high()) + } + + fn is_set_low(&mut self) -> Result { + Ok(self.0.is_set_low()) + } +} + + + /// Behavior for a SPI interface. /// /// Stores the SPI peripheral for register access. In addition, it takes /// ownership of the MOSI and MISO pins to ensure they are in the correct mode. /// Instantiate with the `new` method. +/// +/// This can be used both with the embedded-hal 0.2 [`spi::FullDuplex`] trait, and +/// with the embedded-hal 1.0 [`spi::SpiBus`] trait. +/// +/// [`spi::FullDuplex`]: `embedded_hal_v0::spi::FullDuplex` +/// [`spi::SpiBus`]: `embedded_hal::spi::SpiBus` pub struct Spi { p: SPI, sclk: port::Pin, @@ -241,7 +285,7 @@ where /// FullDuplex trait implementation, allowing this struct to be provided to /// drivers that require it for operation. Only 8-bit word size is supported /// for now. -impl spi::FullDuplex +impl embedded_hal_v0::spi::FullDuplex for Spi where SPI: SpiOps, @@ -266,6 +310,89 @@ where } } +impl embedded_hal::spi::ErrorType + for Spi +where + SPI: SpiOps, + SCLKPIN: port::PinOps, + MOSIPIN: port::PinOps, + MISOPIN: port::PinOps, + CSPIN: port::PinOps, +{ + type Error = core::convert::Infallible; +} + +impl SpiBus + for Spi +where + SPI: SpiOps, + SCLKPIN: port::PinOps, + MOSIPIN: port::PinOps, + MISOPIN: port::PinOps, + CSPIN: port::PinOps, +{ + fn flush(&mut self) -> Result<(), Self::Error> { + if self.write_in_progress { + while !self.p.raw_check_iflag() {} + self.write_in_progress = false; + } + + Ok(()) + } + fn read(&mut self, read: &mut [u8]) -> Result<(), Self::Error> { + // Must flush() first, if asynchronous operations happened before this. + // To be removed if in the future we "only" implement embedded_hal 1.0 + SpiBus::flush(self)?; + + for b in read.iter_mut() { + // We send 0x00 on MOSI during "pure" reading + *b = self.p.raw_transaction(0x00); + } + + Ok(()) + } + + fn write(&mut self, write: &[u8]) -> Result<(), Self::Error> { + // Must flush() first, if asynchronous operations happened before this. + // To be removed if in the future we "only" implement embedded_hal 1.0 + SpiBus::flush(self)?; + + for b in write.iter() { + self.p.raw_transaction(*b); + } + + Ok(()) + } + + fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { + // Must flush() first, if asynchronous operations happened before this. + // To be removed if in the future we "only" implement embedded_hal 1.0 + SpiBus::flush(self)?; + + let longest = read.len().max(write.len()); + for i in 0..longest { + let r = self.p.raw_transaction(*write.get(i).unwrap_or(&0x00)); + if i < read.len() { + read[i] = r; + } + } + + Ok(()) + } + fn transfer_in_place(&mut self, buffer: &mut [u8]) -> Result<(), Self::Error> { + // Must flush() first, if asynchronous operations happened before this. + // To be removed if in the future we "only" implement embedded_hal 1.0 + SpiBus::flush(self)?; + + for b in buffer.iter_mut() { + *b = self.p.raw_transaction(*b) + } + + Ok(()) + } + +} + /// Default Transfer trait implementation. Only 8-bit word size is supported for now. impl embedded_hal_v0::blocking::spi::transfer::Default for Spi @@ -302,7 +429,7 @@ macro_rules! impl_spi { cs: $cspin:ty, ) => { impl $crate::spi::SpiOps<$HAL, $sclkpin, $mosipin, $misopin, $cspin> for $SPI { - /// Sets up the control/status registers with the right settings for this secondary device + fn raw_setup(&mut self, settings: &Settings) { use $crate::hal::spi; @@ -350,26 +477,28 @@ macro_rules! impl_spi { }); } - /// Disable the peripheral fn raw_release(&mut self) { self.spcr.write(|w| w.spe().clear_bit()); } - /// Check the interrupt flag to see if the write has completed fn raw_check_iflag(&self) -> bool { self.spsr.read().spif().bit_is_set() } - /// Read a byte from the data register fn raw_read(&self) -> u8 { self.spdr.read().bits() } - /// Write a byte to the data register, which begins transmission - /// automatically. + fn raw_write(&mut self, byte: u8) { self.spdr.write(|w| unsafe { w.bits(byte) }); } + + fn raw_transaction(&mut self, byte: u8) -> u8 { + self.raw_write(byte); + while !self.raw_check_iflag() {} + self.raw_read() + } } }; }