bitbang_hal/
spi.rs

1//! Serial Peripheral Interface
2//!
3//! This implementation consumes the following hardware resources:
4//! - Periodic timer to mark clock cycles
5//! - Output GPIO pin for clock signal (SCLK)
6//! - Output GPIO pin for data transmission (Master Output Slave Input - MOSI)
7//! - Input GPIO pin for data reception (Master Input Slave Output - MISO)
8//!
9//! The timer must be configured to twice the desired communication frequency.
10//!
11//! SS/CS (slave select) must be handled independently.
12//!
13//! MSB-first and LSB-first bit orders are supported.
14//!
15
16pub use embedded_hal::spi::{MODE_0, MODE_1, MODE_2, MODE_3};
17
18use embedded_hal::digital::v2::{InputPin, OutputPin};
19use embedded_hal::spi::{FullDuplex, Mode, Polarity};
20use embedded_hal::timer::{CountDown, Periodic};
21use nb::block;
22
23/// Error type
24#[derive(Debug)]
25pub enum Error<E> {
26    /// Communication error
27    Bus(E),
28    /// Attempted read without input data
29    NoData,
30}
31
32/// Transmission bit order
33#[derive(Debug)]
34pub enum BitOrder {
35    /// Most significant bit first
36    MSBFirst,
37    /// Least significant bit first
38    LSBFirst,
39}
40
41impl Default for BitOrder {
42    /// Default bit order: MSB first
43    fn default() -> Self {
44        BitOrder::MSBFirst
45    }
46}
47
48/// A Full-Duplex SPI implementation, takes 3 pins, and a timer running at 2x
49/// the desired SPI frequency.
50pub struct SPI<Miso, Mosi, Sck, Timer>
51where
52    Miso: InputPin,
53    Mosi: OutputPin,
54    Sck: OutputPin,
55    Timer: CountDown + Periodic,
56{
57    mode: Mode,
58    miso: Miso,
59    mosi: Mosi,
60    sck: Sck,
61    timer: Timer,
62    read_val: Option<u8>,
63    bit_order: BitOrder,
64}
65
66impl<Miso, Mosi, Sck, Timer, E> SPI<Miso, Mosi, Sck, Timer>
67where
68    Miso: InputPin<Error = E>,
69    Mosi: OutputPin<Error = E>,
70    Sck: OutputPin<Error = E>,
71    Timer: CountDown + Periodic,
72{
73    /// Create instance
74    pub fn new(mode: Mode, miso: Miso, mosi: Mosi, sck: Sck, timer: Timer) -> Self {
75        let mut spi = SPI {
76            mode,
77            miso,
78            mosi,
79            sck,
80            timer,
81            read_val: None,
82            bit_order: BitOrder::default(),
83        };
84
85        match mode.polarity {
86            Polarity::IdleLow => spi.sck.set_low(),
87            Polarity::IdleHigh => spi.sck.set_high(),
88        }
89        .unwrap_or(());
90
91        spi
92    }
93
94    /// Set transmission bit order
95    pub fn set_bit_order(&mut self, order: BitOrder) {
96        self.bit_order = order;
97    }
98
99    /// Allows for an access to the timer type.
100    /// This can be used to change the speed.
101    ///
102    /// In closure you get ownership of the timer
103    /// so you can destruct it and build it up again if necessary.
104    ///
105    /// # Example
106    ///
107    /// ```Rust
108    ///spi.access_timer(|mut timer| {
109    ///    timer.set_freq(4.mhz());
110    ///    timer
111    ///});
112    ///```
113    ///
114    pub fn access_timer<F>(&mut self, f: F)
115    where
116        F: FnOnce(Timer) -> Timer,
117    {
118        // Create a zeroed timer.
119        // This is unsafe, but its safety is guaranteed, though, because the zeroed timer is never used.
120        let timer = unsafe { core::mem::zeroed() };
121        // Get the timer in the struct.
122        let timer = core::mem::replace(&mut self.timer, timer);
123        // Give the timer to the closure and put the result back into the struct.
124        self.timer = f(timer);
125    }
126
127    fn read_bit(&mut self) -> nb::Result<(), crate::spi::Error<E>> {
128        let is_miso_high = self.miso.is_high().map_err(Error::Bus)?;
129        let shifted_value = self.read_val.unwrap_or(0) << 1;
130        if is_miso_high {
131            self.read_val = Some(shifted_value | 1);
132        } else {
133            self.read_val = Some(shifted_value);
134        }
135        Ok(())
136    }
137
138    #[inline]
139    fn set_clk_high(&mut self) -> Result<(), crate::spi::Error<E>> {
140        self.sck.set_high().map_err(Error::Bus)
141    }
142
143    #[inline]
144    fn set_clk_low(&mut self) -> Result<(), crate::spi::Error<E>> {
145        self.sck.set_low().map_err(Error::Bus)
146    }
147
148    #[inline]
149    fn wait_for_timer(&mut self) {
150        block!(self.timer.wait()).ok();
151    }
152}
153
154impl<Miso, Mosi, Sck, Timer, E> FullDuplex<u8> for SPI<Miso, Mosi, Sck, Timer>
155where
156    Miso: InputPin<Error = E>,
157    Mosi: OutputPin<Error = E>,
158    Sck: OutputPin<Error = E>,
159    Timer: CountDown + Periodic,
160{
161    type Error = crate::spi::Error<E>;
162
163    #[inline]
164    fn read(&mut self) -> nb::Result<u8, Self::Error> {
165        match self.read_val {
166            Some(val) => Ok(val),
167            None => Err(nb::Error::Other(crate::spi::Error::NoData)),
168        }
169    }
170
171    fn send(&mut self, byte: u8) -> nb::Result<(), Self::Error> {
172        for bit_offset in 0..8 {
173            let out_bit = match self.bit_order {
174                BitOrder::MSBFirst => (byte >> (7 - bit_offset)) & 0b1,
175                BitOrder::LSBFirst => (byte >> bit_offset) & 0b1,
176            };
177
178            if out_bit == 1 {
179                self.mosi.set_high().map_err(Error::Bus)?;
180            } else {
181                self.mosi.set_low().map_err(Error::Bus)?;
182            }
183
184            match self.mode {
185                MODE_0 => {
186                    self.wait_for_timer();
187                    self.set_clk_high()?;
188                    self.read_bit()?;
189                    self.wait_for_timer();
190                    self.set_clk_low()?;
191                }
192                MODE_1 => {
193                    self.set_clk_high()?;
194                    self.wait_for_timer();
195                    self.read_bit()?;
196                    self.set_clk_low()?;
197                    self.wait_for_timer();
198                }
199                MODE_2 => {
200                    self.wait_for_timer();
201                    self.set_clk_low()?;
202                    self.read_bit()?;
203                    self.wait_for_timer();
204                    self.set_clk_high()?;
205                }
206                MODE_3 => {
207                    self.set_clk_low()?;
208                    self.wait_for_timer();
209                    self.read_bit()?;
210                    self.set_clk_high()?;
211                    self.wait_for_timer();
212                }
213            }
214        }
215
216        Ok(())
217    }
218}
219
220impl<Miso, Mosi, Sck, Timer, E> embedded_hal::blocking::spi::transfer::Default<u8>
221    for SPI<Miso, Mosi, Sck, Timer>
222where
223    Miso: InputPin<Error = E>,
224    Mosi: OutputPin<Error = E>,
225    Sck: OutputPin<Error = E>,
226    Timer: CountDown + Periodic,
227{
228}
229
230impl<Miso, Mosi, Sck, Timer, E> embedded_hal::blocking::spi::write::Default<u8>
231    for SPI<Miso, Mosi, Sck, Timer>
232where
233    Miso: InputPin<Error = E>,
234    Mosi: OutputPin<Error = E>,
235    Sck: OutputPin<Error = E>,
236    Timer: CountDown + Periodic,
237{
238}