Skip to main content
RKTK API Docs RKTK Home Repo

rktk_drivers_common/
spi.rs

1use embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice;
2use embassy_sync::{blocking_mutex::raw::RawMutex, mutex::Mutex};
3use embedded_hal::{
4    digital::OutputPin,
5    spi::{ErrorType, Operation},
6};
7use embedded_hal_async::spi::{SpiBus, SpiDevice as SpiDeviceTrait};
8
9#[derive(Debug, PartialEq, Eq)]
10#[cfg_attr(feature = "defmt", derive(defmt::Format))]
11pub enum IterOperation<Word: 'static> {
12    Write(Word),
13    DelayNs(u32),
14}
15
16/// Extended SPI trait with additional functionality
17///
18/// This trait exists to resolve the issue that standard [`SpiDeviceTrait`] traits cannot perform
19/// streaming transfers (https://github.com/rust-embedded/embedded-hal/issues/583).
20/// This operation is required for some drivers (e.g., [Pmw3360](crate::mouse::pmw3360::Pmw3360) SROM download).
21///
22/// [`IterOperation`] has more limited capabilities than a standard Operation, but since the
23/// `transaction_iter` method accepts an iterator rather than an array, it enables streaming transfers while keeping the CS active.
24///
25/// The primary implementation of this trait is for [`EmbassySpiDevice`]. It performs extended operations by utilizing the SPI implementation provided by [`embassy_embedded_hal`].
26///
27/// The ExtendedSpi trait is also implemented for regular [`SpiDeviceTrait`] for convenience.
28/// Since these SpiDevice instances cannot actually perform extended operations, the `transaction_iter_supported` method always returns false.
29#[allow(async_fn_in_trait)]
30pub trait ExtendedSpi<Word: Copy + 'static = u8> {
31    type Error: core::fmt::Debug;
32
33    async fn transaction(
34        &mut self,
35        operations: &mut [Operation<'_, Word>],
36    ) -> Result<(), Self::Error>;
37
38    fn transaction_iter_supported(&self) -> bool {
39        false
40    }
41
42    async fn transaction_iter(
43        &mut self,
44        _operations: impl Iterator<Item = IterOperation<Word>>,
45    ) -> Result<(), Self::Error> {
46        Ok(())
47    }
48}
49
50pub struct EmbassySpiDevice<
51    'a,
52    M: RawMutex,
53    BUS: SpiBus<Word>,
54    CS: OutputPin,
55    Word: Copy + 'static = u8,
56> {
57    bus: &'a Mutex<M, BUS>,
58    cs: CS,
59    _word: core::marker::PhantomData<Word>,
60}
61
62impl<'a, M: RawMutex, BUS: SpiBus<Word>, CS: OutputPin, Word: Copy + 'static>
63    EmbassySpiDevice<'a, M, BUS, CS, Word>
64{
65    pub fn new(bus: &'a Mutex<M, BUS>, cs: CS) -> Self {
66        Self { bus, cs, _word: core::marker::PhantomData }
67    }
68}
69
70impl<'a, M: RawMutex + 'a, BUS: SpiBus<Word>, CS: OutputPin + 'a, Word: Copy + 'static>
71    ExtendedSpi<Word> for EmbassySpiDevice<'a, M, BUS, CS, Word>
72{
73    type Error = <SpiDevice<'a, M, BUS, CS> as ErrorType>::Error;
74
75    async fn transaction(
76        &mut self,
77        operations: &mut [Operation<'_, Word>],
78    ) -> Result<(), Self::Error> {
79        let mut spi = SpiDevice::new(self.bus, &mut self.cs);
80        SpiDeviceTrait::transaction(&mut spi, operations).await
81    }
82
83    fn transaction_iter_supported(&self) -> bool {
84        true
85    }
86
87    async fn transaction_iter(
88        &mut self,
89        operations: impl Iterator<Item = IterOperation<Word>>,
90    ) -> Result<(), Self::Error> {
91        self.cs.set_low().map_err(Self::Error::Cs)?;
92        {
93            let mut bus = self.bus.lock().await;
94            for op in operations {
95                match op {
96                    IterOperation::Write(item) => {
97                        bus.write(&[item]).await.map_err(Self::Error::Spi)?
98                    }
99                    IterOperation::DelayNs(n) => embassy_time::Timer::after_nanos(n.into()).await,
100                }
101            }
102        }
103        self.cs.set_high().map_err(Self::Error::Cs)?;
104
105        Ok(())
106    }
107}
108
109impl<T: SpiDeviceTrait<Word>, Word: Copy + 'static> ExtendedSpi<Word> for T {
110    type Error = T::Error;
111
112    async fn transaction(
113        &mut self,
114        operations: &mut [Operation<'_, Word>],
115    ) -> Result<(), Self::Error> {
116        SpiDeviceTrait::transaction(self, operations).await
117    }
118}