Skip to content

Commit

Permalink
generic: Implement the SPI traits from embedded-hal 1.0
Browse files Browse the repository at this point in the history
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 <ghislain@ghislainmary.fr>
  • Loading branch information
rubend056 and ghismary committed Mar 12, 2024
1 parent d4ba40d commit 47cbf96
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 10 deletions.
1 change: 1 addition & 0 deletions avr-hal-generic/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
5 changes: 3 additions & 2 deletions avr-hal-generic/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}
Expand Down
145 changes: 137 additions & 8 deletions avr-hal-generic/src/spi.rs
Original file line number Diff line number Diff line change
@@ -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.
///
Expand Down Expand Up @@ -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<H, SCLK, MOSI, MISO, CS> {
/// 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
Expand Down Expand Up @@ -114,11 +124,45 @@ impl<CSPIN: port::PinOps> embedded_hal_v0::digital::v2::ToggleableOutputPin for
}
}

impl<CSPIN: port::PinOps> embedded_hal::digital::ErrorType for ChipSelectPin<CSPIN> {
type Error = core::convert::Infallible;
}

impl<CSPIN: port::PinOps> embedded_hal::digital::OutputPin for ChipSelectPin<CSPIN> {
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<CSPIN: port::PinOps> embedded_hal::digital::StatefulOutputPin for ChipSelectPin<CSPIN> {
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
Ok(self.0.is_set_high())
}

fn is_set_low(&mut self) -> Result<bool, Self::Error> {
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<H, SPI, SCLKPIN, MOSIPIN, MISOPIN, CSPIN> {
p: SPI,
sclk: port::Pin<port::mode::Output, SCLKPIN>,
Expand Down Expand Up @@ -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<H, SPI, SCLKPIN, MOSIPIN, MISOPIN, CSPIN> spi::FullDuplex<u8>
impl<H, SPI, SCLKPIN, MOSIPIN, MISOPIN, CSPIN> embedded_hal_v0::spi::FullDuplex<u8>
for Spi<H, SPI, SCLKPIN, MOSIPIN, MISOPIN, CSPIN>
where
SPI: SpiOps<H, SCLKPIN, MOSIPIN, MISOPIN, CSPIN>,
Expand All @@ -266,6 +310,89 @@ where
}
}

impl<H, SPI, SCLKPIN, MOSIPIN, MISOPIN, CSPIN> embedded_hal::spi::ErrorType
for Spi<H, SPI, SCLKPIN, MOSIPIN, MISOPIN, CSPIN>
where
SPI: SpiOps<H, SCLKPIN, MOSIPIN, MISOPIN, CSPIN>,
SCLKPIN: port::PinOps,
MOSIPIN: port::PinOps,
MISOPIN: port::PinOps,
CSPIN: port::PinOps,
{
type Error = core::convert::Infallible;
}

impl<H, SPI, SCLKPIN, MOSIPIN, MISOPIN, CSPIN> SpiBus
for Spi<H, SPI, SCLKPIN, MOSIPIN, MISOPIN, CSPIN>
where
SPI: SpiOps<H, SCLKPIN, MOSIPIN, MISOPIN, CSPIN>,
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<H, SPI, SCLKPIN, MOSIPIN, MISOPIN, CSPIN> embedded_hal_v0::blocking::spi::transfer::Default<u8>
for Spi<H, SPI, SCLKPIN, MOSIPIN, MISOPIN, CSPIN>
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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()
}
}
};
}

0 comments on commit 47cbf96

Please sign in to comment.