#")]
#![cfg_attr(not(feature = "dma"), doc = "`DMA`")]
use crate::ehal_02;
use crate::ehal_nb;
#[cfg(doc)]
use crate::sercom::spi::Capability;
use crate::sercom::spi::{
AtomicSize, Config, DataWidth, Duplex, DynLength, Error, Flags, GreaterThan4, Length,
MasterMode, OpMode, Receive, Rx, Slave, Spi, Status, Tx, ValidConfig, ValidPads, Word,
};
use nb::Error::WouldBlock;
use num_traits::{AsPrimitive, PrimInt};
use typenum::{U1, U2, U3, U4};
use crate::pac::sercom0::RegisterBlock;
impl<P, M, L> ehal_nb::serial::Read<L::Word> for Spi<Config<P, M, L>, Rx>
where
Config<P, M, L>: ValidConfig,
P: ValidPads,
M: MasterMode,
L: Length,
L::Word: PrimInt,
DataWidth: AsPrimitive<L::Word>,
{
fn read(&mut self) -> nb::Result<L::Word, Self::Error> {
let in_progress = self.capability.in_progress;
let flags = self.read_flags_errors()?;
if !in_progress && flags.contains(Flags::DRE) {
unsafe { self.write_data(0) };
self.capability.in_progress = true;
Err(WouldBlock)
} else if in_progress && flags.contains(Flags::RXC) {
self.capability.in_progress = false;
unsafe { Ok(self.read_data().as_()) }
} else {
Err(WouldBlock)
}
}
}
impl<P, M, L> ehal_02::serial::Read<L::Word> for Spi<Config<P, M, L>, Rx>
where
Config<P, M, L>: ValidConfig,
P: ValidPads,
M: MasterMode,
L: Length,
L::Word: PrimInt,
DataWidth: AsPrimitive<L::Word>,
{
type Error = Error;
#[inline]
fn read(&mut self) -> nb::Result<L::Word, Error> {
<Self as ehal_nb::serial::Read<L::Word>>::read(self)
}
}
impl<P, L> ehal_nb::serial::Read<L::Word> for Spi<Config<P, Slave, L>, Rx>
where
Config<P, Slave, L>: ValidConfig,
P: ValidPads,
L: Length,
L::Word: PrimInt,
DataWidth: AsPrimitive<L::Word>,
{
#[inline]
fn read(&mut self) -> nb::Result<L::Word, Error> {
let flags = self.read_flags_errors()?;
if flags.contains(Flags::RXC) {
unsafe { Ok(self.read_data().as_()) }
} else {
Err(WouldBlock)
}
}
}
impl<P, L> ehal_02::serial::Read<L::Word> for Spi<Config<P, Slave, L>, Rx>
where
Config<P, Slave, L>: ValidConfig,
P: ValidPads,
L: Length,
L::Word: PrimInt,
DataWidth: AsPrimitive<L::Word>,
{
type Error = Error;
#[inline]
fn read(&mut self) -> nb::Result<L::Word, Error> {
<Self as ehal_nb::serial::Read<L::Word>>::read(self)
}
}
impl<C> ehal_nb::serial::Write<C::Word> for Spi<C, Tx>
where
C: ValidConfig,
C::Size: AtomicSize,
C::Word: PrimInt + AsPrimitive<DataWidth>,
{
#[inline]
fn write(&mut self, word: C::Word) -> nb::Result<(), Error> {
if self.read_status().contains(Status::LENERR) {
Err(Error::LengthError.into())
} else if self.read_flags().contains(Flags::DRE) {
self.config.as_mut().regs.write_data(word.as_());
Ok(())
} else {
Err(WouldBlock)
}
}
#[inline]
fn flush(&mut self) -> nb::Result<(), Error> {
if self.read_status().contains(Status::LENERR) {
Err(Error::LengthError.into())
} else if self.read_flags().contains(Flags::TXC) {
Ok(())
} else {
Err(WouldBlock)
}
}
}
impl<C> ehal_02::serial::Write<C::Word> for Spi<C, Tx>
where
C: ValidConfig,
C::Size: AtomicSize,
C::Word: PrimInt + AsPrimitive<DataWidth>,
{
type Error = Error;
#[inline]
fn write(&mut self, word: C::Word) -> nb::Result<(), Error> {
<Self as ehal_nb::serial::Write<C::Word>>::write(self, word)
}
#[inline]
fn flush(&mut self) -> nb::Result<(), Error> {
<Self as ehal_nb::serial::Write<C::Word>>::flush(self)
}
}
impl<C> ehal_02::blocking::serial::write::Default<C::Word> for Spi<C, Tx>
where
C: ValidConfig,
Spi<C, Tx>: ehal_02::serial::Write<C::Word>,
{
}
impl<C> ehal_nb::spi::FullDuplex<C::Word> for Spi<C, Duplex>
where
C: ValidConfig,
C::Size: AtomicSize,
C::Word: PrimInt + AsPrimitive<DataWidth>,
DataWidth: AsPrimitive<C::Word>,
{
#[inline]
fn read(&mut self) -> nb::Result<C::Word, Error> {
let flags = self.read_flags_errors()?;
if flags.contains(Flags::RXC) {
Ok(self.config.as_mut().regs.read_data().as_())
} else {
Err(WouldBlock)
}
}
#[inline]
fn write(&mut self, word: C::Word) -> nb::Result<(), Error> {
let flags = self.read_flags_errors()?;
if flags.contains(Flags::DRE) {
self.config.as_mut().regs.write_data(word.as_());
Ok(())
} else {
Err(WouldBlock)
}
}
}
impl<C> ehal_02::spi::FullDuplex<C::Word> for Spi<C, Duplex>
where
C: ValidConfig,
C::Size: AtomicSize,
C::Word: PrimInt + AsPrimitive<DataWidth>,
DataWidth: AsPrimitive<C::Word>,
{
type Error = Error;
#[inline]
fn read(&mut self) -> nb::Result<C::Word, Error> {
let flags = self.read_flags_errors()?;
if flags.contains(Flags::RXC) {
Ok(self.config.as_mut().regs.read_data().as_())
} else {
Err(WouldBlock)
}
}
#[inline]
fn send(&mut self, word: C::Word) -> nb::Result<(), Error> {
let flags = self.read_flags_errors()?;
if flags.contains(Flags::DRE) {
self.config.as_mut().regs.write_data(word.as_());
Ok(())
} else {
Err(WouldBlock)
}
}
}
macro_rules! impl_blocking_spi_transfer {
( $($Length:ident),+ ) => {
$(
impl<P, M, A> $crate::ehal_02::blocking::spi::Transfer<Word<$Length>> for Spi<Config<P, M, $Length>, A>
where
Config<P, M, $Length>: ValidConfig,
P: ValidPads,
M: OpMode,
A: Receive,
{
type Error = Error;
#[inline]
fn transfer<'w>(&mut self, words: &'w mut [Word<$Length>]) -> Result<&'w [Word<$Length>], Error> {
let cells = core::cell::Cell::from_mut(words).as_slice_of_cells();
let mut to_send = cells.iter();
let mut to_recv = cells.iter();
while to_recv.len() > 0 {
let flags = self.read_flags_errors()?;
if to_send.len() > 0 && flags.contains(Flags::DRE) {
let word = match to_send.next() {
Some(cell) => cell.get(),
None => unreachable!(),
};
self.config.as_mut().regs.write_data(word as DataWidth);
}
if to_recv.len() > to_send.len() && flags.contains(Flags::RXC) {
let word = self.config.as_mut().regs.read_data() as Word<$Length>;
match to_recv.next() {
Some(cell) => cell.set(word),
None => unreachable!(),
}
}
}
Ok(words)
}
}
)+
}
}
impl_blocking_spi_transfer!(U1, U2, U3, U4);
impl<P, M, L, A> ehal_02::blocking::spi::Transfer<u8> for Spi<Config<P, M, L>, A>
where
Config<P, M, L>: ValidConfig,
P: ValidPads,
M: OpMode,
L: GreaterThan4,
A: Receive,
{
type Error = Error;
#[inline]
fn transfer<'w>(&mut self, buf: &'w mut [u8]) -> Result<&'w [u8], Error> {
assert_eq!(buf.len(), L::USIZE);
let sercom = unsafe { self.config.as_ref().sercom() };
transfer_slice(sercom, buf)
}
}
impl<P, M, A> ehal_02::blocking::spi::Transfer<u8> for Spi<Config<P, M, DynLength>, A>
where
Config<P, M, DynLength>: ValidConfig,
P: ValidPads,
M: OpMode,
A: Receive,
{
type Error = Error;
#[inline]
fn transfer<'w>(&mut self, buf: &'w mut [u8]) -> Result<&'w [u8], Error> {
assert_eq!(buf.len(), self.get_dyn_length() as usize);
let sercom = unsafe { self.config.as_ref().sercom() };
transfer_slice(sercom, buf)
}
}
macro_rules! impl_blocking_spi_write {
( $($Length:ident),+ ) => {
$(
impl<P, M> $crate::ehal_02::blocking::spi::Write<Word<$Length>> for Spi<Config<P, M, $Length>, Duplex>
where
Config<P, M, $Length>: ValidConfig,
P: ValidPads,
M: OpMode,
{
type Error = Error;
#[inline]
fn write(&mut self, words: &[Word<$Length>]) -> Result<(), Error> {
let mut to_send = words.iter();
let mut to_recv = to_send.len();
while to_recv > 0 {
let flags = self.read_flags_errors()?;
if to_send.len() > 0 && flags.contains(Flags::DRE) {
let word = match to_send.next() {
Some(word) => *word,
None => unreachable!(),
};
self.config.as_mut().regs.write_data(word as DataWidth);
}
if to_recv > to_send.len() && flags.contains(Flags::RXC) {
self.config.as_mut().regs.read_data() as Word<$Length>;
to_recv -= 1;
}
}
Ok(())
}
}
impl<P, M> $crate::ehal_02::blocking::spi::Write<Word<$Length>> for Spi<Config<P, M, $Length>, Tx>
where
Config<P, M, $Length>: ValidConfig,
P: ValidPads,
M: OpMode,
{
type Error = Error;
#[inline]
fn write(&mut self, words: &[Word<$Length>]) -> Result<(), Error> {
for word in words {
loop {
if self.read_status().contains(Status::LENERR) {
return Err(Error::LengthError)
} else if self.read_flags().contains(Flags::DRE) {
self.config.as_mut().regs.write_data(*word as DataWidth);
break
}
}
}
while !self.read_flags().contains(Flags::TXC) {}
Ok(())
}
}
)+
}
}
impl_blocking_spi_write!(U1, U2, U3, U4);
impl<P, M, L> ehal_02::blocking::spi::Write<u8> for Spi<Config<P, M, L>, Duplex>
where
Config<P, M, L>: ValidConfig,
P: ValidPads,
M: OpMode,
L: GreaterThan4,
{
type Error = Error;
#[inline]
fn write(&mut self, buf: &[u8]) -> Result<(), Error> {
if buf.len() != L::USIZE {
panic!("Slice length does not equal SPI transfer length");
}
let sercom = unsafe { self.config.as_ref().sercom() };
write_slice(sercom, buf, true)
}
}
impl<P, M, L> ehal_02::blocking::spi::Write<u8> for Spi<Config<P, M, L>, Tx>
where
Config<P, M, L>: ValidConfig,
P: ValidPads,
M: OpMode,
L: GreaterThan4,
{
type Error = Error;
#[inline]
fn write(&mut self, buf: &[u8]) -> Result<(), Error> {
if buf.len() != L::USIZE {
panic!("Slice length does not equal SPI transfer length");
}
let sercom = unsafe { self.config.as_ref().sercom() };
write_slice(sercom, buf, false)?;
while !self.read_flags().contains(Flags::TXC) {}
Ok(())
}
}
impl<P, M> ehal_02::blocking::spi::Write<u8> for Spi<Config<P, M, DynLength>, Duplex>
where
Config<P, M, DynLength>: ValidConfig,
P: ValidPads,
M: OpMode,
{
type Error = Error;
#[inline]
fn write(&mut self, buf: &[u8]) -> Result<(), Error> {
if buf.len() != self.get_dyn_length() as usize {
panic!("Slice length does not equal SPI transfer length");
}
let sercom = unsafe { self.config.as_ref().sercom() };
write_slice(sercom, buf, true)
}
}
impl<P, M> ehal_02::blocking::spi::Write<u8> for Spi<Config<P, M, DynLength>, Tx>
where
Config<P, M, DynLength>: ValidConfig,
P: ValidPads,
M: OpMode,
{
type Error = Error;
#[inline]
fn write(&mut self, buf: &[u8]) -> Result<(), Error> {
if buf.len() != self.get_dyn_length() as usize {
panic!("Slice length does not equal SPI transfer length");
}
let sercom = unsafe { self.config.as_ref().sercom() };
write_slice(sercom, buf, false)?;
while !self.read_flags().contains(Flags::TXC) {}
Ok(())
}
}
macro_rules! impl_blocking_spi_write_iter {
( $($Length:ident),+ ) => {
$(
impl<P, M> $crate::ehal_02::blocking::spi::WriteIter<Word<$Length>> for Spi<Config<P, M, $Length>, Duplex>
where
Config<P, M, $Length>: ValidConfig,
P: ValidPads,
M: OpMode,
{
type Error = Error;
#[inline]
fn write_iter<WI>(&mut self, words: WI) -> Result<(), Error>
where
WI: IntoIterator<Item = Word<$Length>>,
{
for word in words.into_iter() {
loop {
let flags = self.read_flags_errors()?;
if flags.contains(Flags::DRE) {
unsafe { self.write_data(word as DataWidth) };
break
}
}
loop {
let flags = self.read_flags_errors()?;
if flags.contains(Flags::RXC) {
self.config.as_mut().regs.read_data() as Word<$Length>;
break
}
}
}
Ok(())
}
}
impl<P, M> $crate::ehal_02::blocking::spi::WriteIter<Word<$Length>> for Spi<Config<P, M, $Length>, Tx>
where
Config<P, M, $Length>: ValidConfig,
P: ValidPads,
M: OpMode,
{
type Error = Error;
#[inline]
fn write_iter<WI>(&mut self, words: WI) -> Result<(), Error>
where
WI: IntoIterator<Item = Word<$Length>>,
{
for word in words.into_iter() {
loop {
if self.read_status().contains(Status::LENERR) {
return Err(Error::LengthError)
} else if self.read_flags().contains(Flags::DRE) {
unsafe { self.write_data(word as DataWidth) };
break
}
}
}
while !self.read_flags().contains(Flags::TXC) {}
Ok(())
}
}
)+
};
}
impl_blocking_spi_write_iter!(U1, U2, U3, U4);
fn transfer_slice<'w>(sercom: &RegisterBlock, buf: &'w mut [u8]) -> Result<&'w [u8], Error> {
let cells = core::cell::Cell::from_mut(buf).as_slice_of_cells();
let mut to_send = cells.iter();
let mut to_recv = cells.iter();
while to_recv.len() > 0 {
let errors = sercom.spim().status().read();
if errors.bufovf().bit_is_set() {
return Err(Error::Overflow);
}
if errors.lenerr().bit_is_set() {
return Err(Error::LengthError);
}
let flags = sercom.spim().intflag().read();
if to_send.len() > 0 && flags.dre().bit_is_set() {
let mut bytes = [0; 4];
for byte in &mut bytes {
match to_send.next() {
Some(cell) => *byte = cell.get(),
None => break,
}
}
let word = u32::from_le_bytes(bytes);
sercom
.spim()
.data()
.write(|w| unsafe { w.data().bits(word) });
}
if to_recv.len() > to_send.len() && flags.rxc().bit_is_set() {
let word = sercom.spim().data().read().data().bits();
let bytes = word.to_le_bytes();
for byte in bytes.iter() {
match to_recv.next() {
Some(cell) => cell.set(*byte),
None => break,
}
}
}
}
Ok(buf)
}
fn write_slice(sercom: &RegisterBlock, buf: &[u8], duplex: bool) -> Result<(), Error> {
let mut to_send = buf.iter();
let mut to_recv: usize = to_send.len();
while to_recv > 0 {
let errors = sercom.spim().status().read();
if duplex && errors.bufovf().bit_is_set() {
return Err(Error::Overflow);
}
if errors.lenerr().bit_is_set() {
return Err(Error::LengthError);
}
let flags = sercom.spim().intflag().read();
if to_send.len() > 0 && flags.dre().bit_is_set() {
let mut bytes = [0; 4];
for byte in &mut bytes {
match to_send.next() {
Some(d) => *byte = *d,
None => break,
}
}
let word = u32::from_le_bytes(bytes);
sercom
.spim()
.data()
.write(|w| unsafe { w.data().bits(word) });
}
if duplex && to_recv > to_send.len() && flags.rxc().bit_is_set() {
sercom.spim().data().read().data().bits();
let diff = to_recv - to_send.len();
to_recv -= if diff < 4 { diff } else { 4 };
}
}
Ok(())
}