drogue_grove_uart_spi/
lib.rs

1#![no_std]
2
3//! SPI over UART
4//!
5//! The `UARTSPI` struct implements an SPI interface on top of a `UART`, using the protocol
6//! used by Grove.
7//!
8//! Also see: https://github.com/Seeed-Studio/Grove_LoRa_433MHz_and_915MHz_RF/blob/master/examples/Grove_LoRa_firmware/Grove_LoRa_firmware.ino
9//!
10//! ## Protocol
11//!
12//! When writing, this is:
13//!
14//! ~~~
15//! | 'W' | <reg> | <len> | <data>... |
16//! ~~~
17//!
18//! There is no response after a write request has been processed.
19//!
20//! When reading, it is:
21//!
22//! ~~~
23//! | 'R' | <reg> | <len> |
24//! ~~~
25//!
26//! This will follow <len> bytes, which is the data read from SPI.
27//!
28
29use embedded_hal::blocking::spi::{Transfer, Write};
30use embedded_hal::digital::v2::OutputPin;
31use embedded_hal::serial;
32
33/// SPI over UART
34pub struct UARTSPI<UART> {
35    uart: UART,
36}
37
38impl<UART> UARTSPI<UART> {
39    /// Wrap the SPI protocol around the UART interface.
40    pub fn new(uart: UART) -> Self {
41        UARTSPI { uart }
42    }
43
44    /// Free the SPI interface and get back the UART
45    pub fn free(self) -> UART {
46        self.uart
47    }
48}
49
50impl<UART, E> Transfer<u8> for UARTSPI<UART>
51where
52    UART: serial::Read<u8, Error = E> + serial::Write<u8, Error = E>,
53{
54    type Error = nb::Error<E>;
55
56    fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> {
57        let mut len = words.len();
58        if len == 0 {
59            return Ok(words);
60        }
61
62        // the buffer also contains the register as the first byte, but this is treated specially
63        // on the protocol level
64        len -= 1;
65
66        // FIXME: handle with grace
67        assert!(len <= 255);
68
69        let reg = words[0];
70
71        // write read request
72        nb::block!(self.uart.write(b'R'))?;
73        nb::block!(self.uart.write(reg))?;
74        nb::block!(self.uart.write(len as u8))?;
75        // flush
76        nb::block!(self.uart.flush())?;
77
78        #[cfg(feature = "dump")]
79        log::info!("Sent R-request (reg: 0x{:02x}, len: {})", reg, len);
80
81        // read len bytes
82
83        for i in 0..len {
84            words[i + 1] = nb::block!(self.uart.read())?;
85            #[cfg(feature = "dump")]
86            log::info!("Received: {:02x}", words[i + 1]);
87        }
88
89        Ok(words)
90    }
91}
92
93impl<UART> Write<u8> for UARTSPI<UART>
94where
95    UART: serial::Write<u8>,
96{
97    type Error = nb::Error<UART::Error>;
98
99    fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
100        let mut len = words.len();
101        if len == 0 {
102            return Ok(());
103        }
104
105        // the buffer also contains the register as the first byte, but this is treated specially
106        // on the protocol level
107        len -= 1;
108
109        // FIXME: handle with grace
110        assert!(len <= 255);
111
112        let reg = words[0];
113        nb::block!(self.uart.write(b'W'))?;
114        nb::block!(self.uart.write(reg))?;
115        nb::block!(self.uart.write(len as u8))?;
116        for b in &words[1..] {
117            nb::block!(self.uart.write(*b))?;
118        }
119        nb::block!(self.uart.flush())?;
120
121        #[cfg(feature = "dump")]
122        log::info!("Sent W-request (reg: 0x{:02x}, len: {})", reg, len);
123
124        Ok(())
125    }
126}
127
128/// A no-op pin, which does nothing.
129///
130/// This may be useful in the context of [`UARTSPI`], to provide e.g. a CS or reset pin, which does
131/// nothing in this case.
132pub struct NoOpPin;
133
134impl OutputPin for NoOpPin {
135    type Error = ();
136
137    fn set_low(&mut self) -> Result<(), Self::Error> {
138        Ok(())
139    }
140
141    fn set_high(&mut self) -> Result<(), Self::Error> {
142        Ok(())
143    }
144}