Skip to content

Commit

Permalink
Implement SPI
Browse files Browse the repository at this point in the history
  • Loading branch information
jbeaurivage committed Jan 31, 2024
1 parent bebd7c3 commit de2d0ea
Show file tree
Hide file tree
Showing 2 changed files with 161 additions and 6 deletions.
35 changes: 29 additions & 6 deletions hal/src/sercom/spi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@ use core::marker::PhantomData;
use crate::ehal_02::spi;
pub use crate::ehal_02::spi::{Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
use bitflags::bitflags;
use num_traits::AsPrimitive;

use crate::sercom::*;
use crate::time::Hertz;
Expand Down Expand Up @@ -363,12 +364,6 @@ pub mod lengths {
});
}

#[cfg(feature = "thumbv6")]
#[path = "spi/impl_ehal_thumbv6m.rs"]
pub mod impl_ehal;

#[cfg(feature = "thumbv7")]
#[path = "spi/impl_ehal_thumbv7em.rs"]
pub mod impl_ehal;

//=============================================================================
Expand Down Expand Up @@ -646,6 +641,7 @@ where
mode: PhantomData<M>,
size: PhantomData<Z>,
freq: Hertz,
nop_word: DataWidth,
}

impl<P: ValidPads> Config<P> {
Expand All @@ -666,6 +662,7 @@ impl<P: ValidPads> Config<P> {
mode: PhantomData,
size: PhantomData,
freq: freq.into(),
nop_word: 0x00.as_(),
}
}

Expand Down Expand Up @@ -716,6 +713,7 @@ where
mode: PhantomData,
size: PhantomData,
freq: self.freq,
nop_word: self.nop_word,
}
}

Expand Down Expand Up @@ -867,6 +865,31 @@ where
self
}

/// Get the NOP word
///
/// This word is used when reading in Duplex mode, since an equal number of
/// words must be sent in order to avoid overflow errors.
pub fn get_nop_word(&self) -> DataWidth {
self.nop_word
}

/// Set the NOP word
///
/// This word is used when reading in Duplex mode, since an equal number of
/// words must be sent in order to avoid overflow errors.
pub fn set_nop_word(&mut self, nop_word: DataWidth) {
self.nop_word = nop_word;
}

/// Set the NOP word using the builder pattern
///
/// This word is used when reading in Duplex mode, since an equal number of
/// words must be sent in order to avoid overflow errors.
pub fn nop_word(mut self, nop_word: DataWidth) -> Self {
self.nop_word = nop_word;
self
}

/// Get the baud rate
///
/// The returned baud rate may not exactly match what was set.
Expand Down
132 changes: 132 additions & 0 deletions hal/src/sercom/spi/impl_ehal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
use super::*;
use crate::ehal::spi::{self, ErrorKind, ErrorType, SpiBus};
use num_traits::{AsPrimitive, PrimInt};

#[cfg(feature = "thumbv6")]
#[path = "impl_ehal_thumbv6m.rs"]
pub mod impl_ehal_02;

#[cfg(feature = "thumbv7")]
#[path = "impl_ehal_thumbv7em.rs"]
pub mod impl_ehal_02;

impl spi::Error for Error {
fn kind(&self) -> ErrorKind {
match self {
Error::Overflow => ErrorKind::Overrun,
Error::LengthError => ErrorKind::Other,
}
}
}

impl<P, M, C> ErrorType for Spi<Config<P, M, C>, Duplex>
where
Config<P, M, C>: ValidConfig,
P: ValidPads,
M: MasterMode,
C: Size,
{
type Error = Error;
}

impl<P, M, C> Spi<Config<P, M, C>, Duplex>
where
Config<P, M, C>: ValidConfig,
P: ValidPads,
M: MasterMode,
C: Size + Copy + 'static,
C::Word: PrimInt + AsPrimitive<DataWidth>,
DataWidth: AsPrimitive<C::Word>,
{
/// Read and write a single word to the bus simultaneously.
fn transfer_word_in_place(&mut self, word: C::Word) -> Result<C::Word, Error> {
while self.read_flags().contains(Flags::DRE) {
core::hint::spin_loop();
}
self.read_flags_errors()?;
unsafe {
self.write_data(word.as_());
}

while self.read_flags().contains(Flags::TXC | Flags::RXC) {
core::hint::spin_loop();
}
let word = unsafe { self.read_data().as_() };
Ok(word)
}

/// Perform a transfer, word by word.
///
/// No-op words will be written if `read` is longer than `write`. Extra
/// words are ignored if `write` is longer than `read`.
fn transfer_word_by_word(
&mut self,
read: &mut [C::Word],
write: &[C::Word],
) -> Result<(), Error> {
let nop_word = self.config.nop_word;
for (r, w) in read
.iter_mut()
.zip(write.iter().chain(core::iter::repeat(&nop_word.as_())))
{
*r = self.transfer_word_in_place(*w)?;
}

Ok(())
}
}

impl<P, M, C> SpiBus<Word<C>> for Spi<Config<P, M, C>, Duplex>
where
Config<P, M, C>: ValidConfig,
P: ValidPads,
M: MasterMode,
C: Size + Copy + 'static,
C::Word: PrimInt + AsPrimitive<DataWidth>,
DataWidth: AsPrimitive<C::Word>,
{
fn read(&mut self, words: &mut [Word<C>]) -> Result<(), Self::Error> {
for word in words.iter_mut() {
// Should replace todo with nop_word
*word = self.transfer_word_in_place(self.config.nop_word.as_())?;
}

Ok(())
}

#[inline]
fn write(&mut self, words: &[Word<C>]) -> Result<(), Self::Error> {
// We are `Duplex`, so we must receive as many words as we send,
// otherwise we could trigger an overflow
for word in words {
let _ = self.transfer_word_in_place(*word)?;
}
Ok(())
}

#[inline]
fn transfer(&mut self, read: &mut [Word<C>], write: &[Word<C>]) -> Result<(), Self::Error> {
self.transfer_word_by_word(read, write)
}

#[inline]
fn transfer_in_place<'w>(&mut self, words: &mut [Word<C>]) -> Result<(), Self::Error> {
// Can only ever do word-by-word to avoid DMA race conditions
for word in words {
let read = self.transfer_word_in_place(*word)?;
*word = read;
}

Ok(())
}

#[inline]
fn flush(&mut self) -> Result<(), Error> {
// Ignore buffer overflow errors
while !self.read_flags().contains(Flags::TXC) {
core::hint::spin_loop();
}

Ok(())
}
}

0 comments on commit de2d0ea

Please sign in to comment.