avr_hal_generic/
spi.rs

1//! SPI Implementation
2use crate::port;
3use core::marker::PhantomData;
4use embedded_hal::spi::{self, SpiBus};
5
6/// Oscillator Clock Frequency division options.
7///
8/// The bus speed is calculated by dividing the IO clock by the prescaler:
9///
10/// ```text
11/// F_sck = CLK_io / Prescaler
12/// ```
13///
14/// Please note that the overall transfer speed might be lower due to software overhead while
15/// sending / receiving.
16///
17/// | Prescale | 16 MHz Clock | 8 MHz Clock |
18/// | --- | --- | --- |
19/// | `OscfOver2` | 8 MHz | 4 MHz |
20/// | `OscfOver4` | 4 MHz | 2 MHz |
21/// | `OscfOver8` | 2 MHz | 1 MHz |
22/// | `OscfOver16` | 1 MHz | 500 kHz |
23/// | `OscfOver32` | 500 kHz | 250 kHz |
24/// | `OscfOver64` | 250 kHz | 125 kHz |
25/// | `OscfOver128` | 125 kHz | 62.5 kHz |
26#[derive(Clone, Copy, Debug, PartialEq, Eq)]
27pub enum SerialClockRate {
28    OscfOver2 = 1,
29    OscfOver4 = 2,
30    OscfOver8 = 3,
31    OscfOver16 = 4,
32    OscfOver32 = 5,
33    OscfOver64 = 6,
34    OscfOver128 = 7,
35}
36
37impl SerialClockRate {
38    pub fn into_divider(self) -> u8 {
39        2u8.pow(self as u32)
40    }
41}
42
43/// Order of data transmission, either MSB first or LSB first
44#[derive(Clone, Copy, Debug, PartialEq, Eq)]
45pub enum DataOrder {
46    MostSignificantFirst,
47    LeastSignificantFirst,
48}
49
50/// Settings to pass to Spi.
51///
52/// Easiest way to initialize is with
53/// `Settings::default()`.  Otherwise can be instantiated with alternate
54/// settings directly.
55#[derive(Clone, PartialEq, Eq)]
56pub struct Settings {
57    pub data_order: DataOrder,
58    pub clock: SerialClockRate,
59    pub mode: spi::Mode,
60}
61
62impl Default for Settings {
63    fn default() -> Self {
64        Settings {
65            data_order: DataOrder::MostSignificantFirst,
66            clock: SerialClockRate::OscfOver4,
67            mode: spi::MODE_1,
68        }
69    }
70}
71
72/// Internal trait for low-level SPI peripherals
73///
74/// This trait defines the common interface for all SPI peripheral variants.  It is used as an
75/// intermediate abstraction ontop of which the [`Spi`] API is built.  **Prefer using the
76/// [`Spi`] API instead of this trait.**
77pub trait SpiOps<H, SCLK, MOSI, MISO, CS> {
78    /// Sets up the control/status registers with the right settings for this secondary device
79    fn raw_setup(&mut self, settings: &Settings);
80    /// Disable the peripheral
81    fn raw_release(&mut self);
82
83    /// Check the interrupt flag to see if the write has completed
84    ///
85    /// Returns `true` if the bus is idle
86    fn raw_check_iflag(&self) -> bool;
87    /// Read a byte from the data register
88    fn raw_read(&self) -> u8;
89    /// Write a byte to the data register, which begins transmission
90    /// automatically.
91    fn raw_write(&mut self, byte: u8);
92    /// Perform a transaction of a single byte
93    fn raw_transaction(&mut self, byte: u8) -> u8;
94}
95
96/// Wrapper for the CS pin
97///
98/// Used to contain the chip-select pin during operation to prevent its mode from being
99/// changed from Output. This is necessary because the SPI state machine would otherwise
100/// reset itself to SPI slave mode immediately. This wrapper can be used just like an
101/// output pin, because it implements all the same traits from embedded-hal.
102pub struct ChipSelectPin<CSPIN>(port::Pin<port::mode::Output, CSPIN>);
103
104impl<CSPIN: port::PinOps> ChipSelectPin<CSPIN> {
105    /// Convert this `ChipSelectPin` into the underlying "real" `Pin<>` object.
106    ///
107    /// Safety
108    /// ======
109    /// This function is unsafe because the underlying pin can be converted into a non-output mode
110    /// which would break SPI functionality.  The user must ensure the pin is only used in output
111    /// modes after calling this function.
112    pub unsafe fn into_pin_unchecked(self) -> port::Pin<port::mode::Output, CSPIN> {
113        self.0
114    }
115
116    /// (Re)create a `ChipSelectPin` from a real `Pin<>` object.
117    ///
118    /// This function is only meant to be used when the pin was previously moved out of the
119    /// `ChipSelectPin` using [`ChipSelectPin::into_pin_unchecked()`].
120    pub unsafe fn from_pin(pin: port::Pin<port::mode::Output, CSPIN>) -> Self {
121        Self(pin)
122    }
123}
124
125impl<CSPIN: port::PinOps> embedded_hal_v0::digital::v2::OutputPin for ChipSelectPin<CSPIN> {
126    type Error = core::convert::Infallible;
127    fn set_low(&mut self) -> Result<(), Self::Error> {
128        self.0.set_low();
129        Ok(())
130    }
131    fn set_high(&mut self) -> Result<(), Self::Error> {
132        self.0.set_high();
133        Ok(())
134    }
135}
136
137impl<CSPIN: port::PinOps> embedded_hal_v0::digital::v2::StatefulOutputPin for ChipSelectPin<CSPIN> {
138    fn is_set_low(&self) -> Result<bool, Self::Error> {
139        Ok(self.0.is_set_low())
140    }
141    fn is_set_high(&self) -> Result<bool, Self::Error> {
142        Ok(self.0.is_set_high())
143    }
144}
145
146impl<CSPIN: port::PinOps> embedded_hal_v0::digital::v2::ToggleableOutputPin
147    for ChipSelectPin<CSPIN>
148{
149    type Error = core::convert::Infallible;
150    fn toggle(&mut self) -> Result<(), Self::Error> {
151        self.0.toggle();
152        Ok(())
153    }
154}
155
156impl<CSPIN: port::PinOps> embedded_hal::digital::ErrorType for ChipSelectPin<CSPIN> {
157    type Error = core::convert::Infallible;
158}
159
160impl<CSPIN: port::PinOps> embedded_hal::digital::OutputPin for ChipSelectPin<CSPIN> {
161    fn set_high(&mut self) -> Result<(), Self::Error> {
162        self.0.set_high();
163        Ok(())
164    }
165
166    fn set_low(&mut self) -> Result<(), Self::Error> {
167        self.0.set_low();
168        Ok(())
169    }
170}
171
172impl<CSPIN: port::PinOps> embedded_hal::digital::StatefulOutputPin for ChipSelectPin<CSPIN> {
173    fn is_set_high(&mut self) -> Result<bool, Self::Error> {
174        Ok(self.0.is_set_high())
175    }
176
177    fn is_set_low(&mut self) -> Result<bool, Self::Error> {
178        Ok(self.0.is_set_low())
179    }
180}
181
182/// Behavior for a SPI interface.
183///
184/// Stores the SPI peripheral for register access.  In addition, it takes
185/// ownership of the MOSI and MISO pins to ensure they are in the correct mode.
186/// Instantiate with the `new` method.
187///
188/// This can be used both with the embedded-hal 0.2 [`spi::FullDuplex`] trait, and
189/// with the embedded-hal 1.0 [`spi::SpiBus`] trait.
190///
191/// [`spi::FullDuplex`]: `embedded_hal_v0::spi::FullDuplex`
192/// [`spi::SpiBus`]: `embedded_hal::spi::SpiBus`
193pub struct Spi<H, SPI, SCLKPIN, MOSIPIN, MISOPIN, CSPIN> {
194    p: SPI,
195    sclk: port::Pin<port::mode::Output, SCLKPIN>,
196    mosi: port::Pin<port::mode::Output, MOSIPIN>,
197    miso: port::Pin<port::mode::Input, MISOPIN>,
198    write_in_progress: bool,
199    _cs: PhantomData<CSPIN>,
200    _h: PhantomData<H>,
201}
202
203impl<H, SPI, SCLKPIN, MOSIPIN, MISOPIN, CSPIN> Spi<H, SPI, SCLKPIN, MOSIPIN, MISOPIN, CSPIN>
204where
205    SPI: SpiOps<H, SCLKPIN, MOSIPIN, MISOPIN, CSPIN>,
206    SCLKPIN: port::PinOps,
207    MOSIPIN: port::PinOps,
208    MISOPIN: port::PinOps,
209    CSPIN: port::PinOps,
210{
211    /// Instantiate an SPI with the registers, SCLK/MOSI/MISO/CS pins, and settings,
212    /// with the internal pull-up enabled on the MISO pin.
213    ///
214    /// The pins are not actually used directly, but they are moved into the struct in
215    /// order to enforce that they are in the correct mode, and cannot be used by anyone
216    /// else while SPI is active.  CS is placed into a `ChipSelectPin` instance and given
217    /// back so that its output state can be changed as needed.
218    pub fn new(
219        p: SPI,
220        sclk: port::Pin<port::mode::Output, SCLKPIN>,
221        mosi: port::Pin<port::mode::Output, MOSIPIN>,
222        miso: port::Pin<port::mode::Input<port::mode::PullUp>, MISOPIN>,
223        cs: port::Pin<port::mode::Output, CSPIN>,
224        settings: Settings,
225    ) -> (Self, ChipSelectPin<CSPIN>) {
226        let mut spi = Self {
227            p,
228            sclk,
229            mosi,
230            miso: miso.forget_imode(),
231            write_in_progress: false,
232            _cs: PhantomData,
233            _h: PhantomData,
234        };
235        spi.p.raw_setup(&settings);
236        (spi, ChipSelectPin(cs))
237    }
238
239    /// Instantiate an SPI with the registers, SCLK/MOSI/MISO/CS pins, and settings,
240    /// with an external pull-up on the MISO pin.
241    ///
242    /// The pins are not actually used directly, but they are moved into the struct in
243    /// order to enforce that they are in the correct mode, and cannot be used by anyone
244    /// else while SPI is active.
245    pub fn with_external_pullup(
246        p: SPI,
247        sclk: port::Pin<port::mode::Output, SCLKPIN>,
248        mosi: port::Pin<port::mode::Output, MOSIPIN>,
249        miso: port::Pin<port::mode::Input<port::mode::Floating>, MISOPIN>,
250        cs: port::Pin<port::mode::Output, CSPIN>,
251        settings: Settings,
252    ) -> (Self, ChipSelectPin<CSPIN>) {
253        let mut spi = Self {
254            p,
255            sclk,
256            mosi,
257            miso: miso.forget_imode(),
258            write_in_progress: false,
259            _cs: PhantomData,
260            _h: PhantomData,
261        };
262        spi.p.raw_setup(&settings);
263        (spi, ChipSelectPin(cs))
264    }
265
266    /// Reconfigure the SPI peripheral after initializing
267    pub fn reconfigure(&mut self, settings: Settings) -> nb::Result<(), core::convert::Infallible> {
268        // wait for any in-flight writes to complete
269        self.flush()?;
270        self.p.raw_setup(&settings);
271        Ok(())
272    }
273
274    /// Disable the SPI device and release ownership of the peripheral
275    /// and pins.  Instance can no-longer be used after this is
276    /// invoked.
277    pub fn release(
278        mut self,
279        cs: ChipSelectPin<CSPIN>,
280    ) -> (
281        SPI,
282        port::Pin<port::mode::Output, SCLKPIN>,
283        port::Pin<port::mode::Output, MOSIPIN>,
284        port::Pin<port::mode::Input, MISOPIN>,
285        port::Pin<port::mode::Output, CSPIN>,
286    ) {
287        self.p.raw_release();
288        (self.p, self.sclk, self.mosi, self.miso, cs.0)
289    }
290
291    fn flush(&mut self) -> nb::Result<(), core::convert::Infallible> {
292        if self.write_in_progress {
293            if self.p.raw_check_iflag() {
294                self.write_in_progress = false;
295            } else {
296                return Err(nb::Error::WouldBlock);
297            }
298        }
299        Ok(())
300    }
301
302    fn receive(&mut self) -> u8 {
303        self.p.raw_read()
304    }
305
306    fn write(&mut self, byte: u8) {
307        self.write_in_progress = true;
308        self.p.raw_write(byte);
309    }
310}
311
312/// FullDuplex trait implementation, allowing this struct to be provided to
313/// drivers that require it for operation.  Only 8-bit word size is supported
314/// for now.
315impl<H, SPI, SCLKPIN, MOSIPIN, MISOPIN, CSPIN> embedded_hal_v0::spi::FullDuplex<u8>
316    for Spi<H, SPI, SCLKPIN, MOSIPIN, MISOPIN, CSPIN>
317where
318    SPI: SpiOps<H, SCLKPIN, MOSIPIN, MISOPIN, CSPIN>,
319    SCLKPIN: port::PinOps,
320    MOSIPIN: port::PinOps,
321    MISOPIN: port::PinOps,
322    CSPIN: port::PinOps,
323{
324    type Error = core::convert::Infallible;
325
326    /// Sets up the device for transmission and sends the data
327    fn send(&mut self, byte: u8) -> nb::Result<(), Self::Error> {
328        self.flush()?;
329        self.write(byte);
330        Ok(())
331    }
332
333    /// Reads and returns the response in the data register
334    fn read(&mut self) -> nb::Result<u8, Self::Error> {
335        self.flush()?;
336        Ok(self.receive())
337    }
338}
339
340impl<H, SPI, SCLKPIN, MOSIPIN, MISOPIN, CSPIN> embedded_hal::spi::ErrorType
341    for Spi<H, SPI, SCLKPIN, MOSIPIN, MISOPIN, CSPIN>
342where
343    SPI: SpiOps<H, SCLKPIN, MOSIPIN, MISOPIN, CSPIN>,
344    SCLKPIN: port::PinOps,
345    MOSIPIN: port::PinOps,
346    MISOPIN: port::PinOps,
347    CSPIN: port::PinOps,
348{
349    type Error = core::convert::Infallible;
350}
351
352impl<H, SPI, SCLKPIN, MOSIPIN, MISOPIN, CSPIN> SpiBus
353    for Spi<H, SPI, SCLKPIN, MOSIPIN, MISOPIN, CSPIN>
354where
355    SPI: SpiOps<H, SCLKPIN, MOSIPIN, MISOPIN, CSPIN>,
356    SCLKPIN: port::PinOps,
357    MOSIPIN: port::PinOps,
358    MISOPIN: port::PinOps,
359    CSPIN: port::PinOps,
360{
361    fn flush(&mut self) -> Result<(), Self::Error> {
362        if self.write_in_progress {
363            while !self.p.raw_check_iflag() {}
364            self.write_in_progress = false;
365        }
366
367        Ok(())
368    }
369    fn read(&mut self, read: &mut [u8]) -> Result<(), Self::Error> {
370        // Must flush() first, if asynchronous operations happened before this.
371        // To be removed if in the future we "only" implement embedded_hal 1.0
372        SpiBus::flush(self)?;
373
374        for b in read.iter_mut() {
375            // We send 0x00 on MOSI during "pure" reading
376            *b = self.p.raw_transaction(0x00);
377        }
378
379        Ok(())
380    }
381
382    fn write(&mut self, write: &[u8]) -> Result<(), Self::Error> {
383        // Must flush() first, if asynchronous operations happened before this.
384        // To be removed if in the future we "only" implement embedded_hal 1.0
385        SpiBus::flush(self)?;
386
387        for b in write.iter() {
388            self.p.raw_transaction(*b);
389        }
390
391        Ok(())
392    }
393
394    fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
395        // Must flush() first, if asynchronous operations happened before this.
396        // To be removed if in the future we "only" implement embedded_hal 1.0
397        SpiBus::flush(self)?;
398
399        let longest = read.len().max(write.len());
400        for i in 0..longest {
401            let r = self.p.raw_transaction(*write.get(i).unwrap_or(&0x00));
402            if i < read.len() {
403                read[i] = r;
404            }
405        }
406
407        Ok(())
408    }
409
410    fn transfer_in_place(&mut self, buffer: &mut [u8]) -> Result<(), Self::Error> {
411        // Must flush() first, if asynchronous operations happened before this.
412        // To be removed if in the future we "only" implement embedded_hal 1.0
413        SpiBus::flush(self)?;
414
415        for b in buffer.iter_mut() {
416            *b = self.p.raw_transaction(*b)
417        }
418
419        Ok(())
420    }
421}
422
423/// Default Transfer trait implementation. Only 8-bit word size is supported for now.
424impl<H, SPI, SCLKPIN, MOSIPIN, MISOPIN, CSPIN> embedded_hal_v0::blocking::spi::transfer::Default<u8>
425    for Spi<H, SPI, SCLKPIN, MOSIPIN, MISOPIN, CSPIN>
426where
427    SPI: SpiOps<H, SCLKPIN, MOSIPIN, MISOPIN, CSPIN>,
428    SCLKPIN: port::PinOps,
429    MOSIPIN: port::PinOps,
430    MISOPIN: port::PinOps,
431    CSPIN: port::PinOps,
432{
433}
434
435/// Default Write trait implementation. Only 8-bit word size is supported for now.
436impl<H, SPI, SCLKPIN, MOSIPIN, MISOPIN, CSPIN> embedded_hal_v0::blocking::spi::write::Default<u8>
437    for Spi<H, SPI, SCLKPIN, MOSIPIN, MISOPIN, CSPIN>
438where
439    SPI: SpiOps<H, SCLKPIN, MOSIPIN, MISOPIN, CSPIN>,
440    SCLKPIN: port::PinOps,
441    MOSIPIN: port::PinOps,
442    MISOPIN: port::PinOps,
443    CSPIN: port::PinOps,
444{
445}
446
447/// Implement traits for a SPI interface
448#[macro_export]
449macro_rules! impl_spi {
450    (
451        hal: $HAL:ty,
452        peripheral: $SPI:ty,
453        sclk: $sclkpin:ty,
454        mosi: $mosipin:ty,
455        miso: $misopin:ty,
456        cs: $cspin:ty,
457    ) => {
458        impl $crate::spi::SpiOps<$HAL, $sclkpin, $mosipin, $misopin, $cspin> for $SPI {
459            fn raw_setup(&mut self, settings: &Settings) {
460                use $crate::hal::spi;
461
462                // set up control register
463                self.spcr.write(|w| {
464                    // enable SPI
465                    w.spe().set_bit();
466                    // Set to primary mode
467                    w.mstr().set_bit();
468                    // set up data order control bit
469                    match settings.data_order {
470                        DataOrder::MostSignificantFirst => w.dord().clear_bit(),
471                        DataOrder::LeastSignificantFirst => w.dord().set_bit(),
472                    };
473                    // set up polarity control bit
474                    match settings.mode.polarity {
475                        spi::Polarity::IdleHigh => w.cpol().set_bit(),
476                        spi::Polarity::IdleLow => w.cpol().clear_bit(),
477                    };
478                    // set up phase control bit
479                    match settings.mode.phase {
480                        spi::Phase::CaptureOnFirstTransition => w.cpha().clear_bit(),
481                        spi::Phase::CaptureOnSecondTransition => w.cpha().set_bit(),
482                    };
483                    // set up clock rate control bit
484                    match settings.clock {
485                        SerialClockRate::OscfOver2 => w.spr().fosc_4_2(),
486                        SerialClockRate::OscfOver4 => w.spr().fosc_4_2(),
487                        SerialClockRate::OscfOver8 => w.spr().fosc_16_8(),
488                        SerialClockRate::OscfOver16 => w.spr().fosc_16_8(),
489                        SerialClockRate::OscfOver32 => w.spr().fosc_64_32(),
490                        SerialClockRate::OscfOver64 => w.spr().fosc_64_32(),
491                        SerialClockRate::OscfOver128 => w.spr().fosc_128_64(),
492                    }
493                });
494                // set up 2x clock rate status bit
495                self.spsr.write(|w| match settings.clock {
496                    SerialClockRate::OscfOver2 => w.spi2x().set_bit(),
497                    SerialClockRate::OscfOver4 => w.spi2x().clear_bit(),
498                    SerialClockRate::OscfOver8 => w.spi2x().set_bit(),
499                    SerialClockRate::OscfOver16 => w.spi2x().clear_bit(),
500                    SerialClockRate::OscfOver32 => w.spi2x().set_bit(),
501                    SerialClockRate::OscfOver64 => w.spi2x().clear_bit(),
502                    SerialClockRate::OscfOver128 => w.spi2x().clear_bit(),
503                });
504            }
505
506            fn raw_release(&mut self) {
507                self.spcr.write(|w| w.spe().clear_bit());
508            }
509
510            fn raw_check_iflag(&self) -> bool {
511                self.spsr.read().spif().bit_is_set()
512            }
513
514            fn raw_read(&self) -> u8 {
515                self.spdr.read().bits()
516            }
517
518            fn raw_write(&mut self, byte: u8) {
519                self.spdr.write(|w| unsafe { w.bits(byte) });
520            }
521
522            fn raw_transaction(&mut self, byte: u8) -> u8 {
523                self.raw_write(byte);
524                while !self.raw_check_iflag() {}
525                self.raw_read()
526            }
527        }
528    };
529}