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 {
67            bus,
68            cs,
69            _word: core::marker::PhantomData,
70        }
71    }
72}
73
74impl<'a, M: RawMutex + 'a, BUS: SpiBus<Word>, CS: OutputPin + 'a, Word: Copy + 'static>
75    ExtendedSpi<Word> for EmbassySpiDevice<'a, M, BUS, CS, Word>
76{
77    type Error = <SpiDevice<'a, M, BUS, CS> as ErrorType>::Error;
78
79    async fn transaction(
80        &mut self,
81        operations: &mut [Operation<'_, Word>],
82    ) -> Result<(), Self::Error> {
83        let mut spi = SpiDevice::new(self.bus, &mut self.cs);
84        SpiDeviceTrait::transaction(&mut spi, operations).await
85    }
86
87    fn transaction_iter_supported(&self) -> bool {
88        true
89    }
90
91    async fn transaction_iter(
92        &mut self,
93        operations: impl Iterator<Item = IterOperation<Word>>,
94    ) -> Result<(), Self::Error> {
95        self.cs.set_low().map_err(Self::Error::Cs)?;
96        {
97            let mut bus = self.bus.lock().await;
98            for op in operations {
99                match op {
100                    IterOperation::Write(item) => {
101                        bus.write(&[item]).await.map_err(Self::Error::Spi)?
102                    }
103                    IterOperation::DelayNs(n) => embassy_time::Timer::after_nanos(n.into()).await,
104                }
105            }
106        }
107        self.cs.set_high().map_err(Self::Error::Cs)?;
108
109        Ok(())
110    }
111}
112
113impl<T: SpiDeviceTrait<Word>, Word: Copy + 'static> ExtendedSpi<Word> for T {
114    type Error = T::Error;
115
116    async fn transaction(
117        &mut self,
118        operations: &mut [Operation<'_, Word>],
119    ) -> Result<(), Self::Error> {
120        SpiDeviceTrait::transaction(self, operations).await
121    }
122}