w5500_ll/eh0/
vdm.rs

1//! Variable data length implementation of the [`Registers`] trait using the
2//! [`embedded-hal`] blocking SPI trait, and a fallible GPIO pin.
3//!
4//! This uses the W5500 variable data length mode (VDM).
5//! In VDM mode the SPI frame data length is determined by the chip select pin.
6//! This is the preferred blocking implementation if your W5500 has a fallible
7//! chip select pin.
8//!
9//! [`embedded-hal`]: https://github.com/rust-embedded/embedded-hal
10//! [`Registers`]: crate::Registers
11
12use crate::spi::{vdm_header, AccessMode};
13use eh0::digital::v2::OutputPin;
14
15/// W5500 blocking variable data length implementation.
16#[derive(Debug)]
17#[cfg_attr(feature = "defmt", derive(defmt::Format))]
18pub struct W5500<SPI, CS> {
19    /// SPI bus.
20    spi: SPI,
21    /// GPIO for chip select.
22    cs: CS,
23}
24
25/// W5500 blocking implementation error type.
26#[derive(Debug)]
27#[cfg_attr(feature = "defmt", derive(defmt::Format))]
28pub enum Error<SpiError, PinError> {
29    /// SPI bus error wrapper.
30    Spi(SpiError),
31    /// GPIO pin error wrapper.
32    Pin(PinError),
33}
34
35impl<SPI, CS, SpiError, PinError> W5500<SPI, CS>
36where
37    SPI: eh0::blocking::spi::Transfer<u8, Error = SpiError>
38        + eh0::blocking::spi::Write<u8, Error = SpiError>,
39    CS: OutputPin<Error = PinError>,
40{
41    /// Creates a new `W5500` driver from a SPI peripheral and a chip select
42    /// digital I/O pin.
43    ///
44    /// # Safety
45    ///
46    /// The chip select pin must be high before being passed to this function.
47    ///
48    /// # Example
49    ///
50    /// ```
51    /// # use ehm::eh0 as hal;
52    /// # let spi = hal::spi::Mock::new(&[]);
53    /// # let mut pin = hal::pin::Mock::new(&[
54    /// #    hal::pin::Transaction::set(hal::pin::State::High),
55    /// # ]);
56    /// use eh0::digital::v2::OutputPin;
57    /// use w5500_ll::eh0::vdm::W5500;
58    ///
59    /// pin.set_high()?;
60    /// let mut w5500: W5500<_, _> = W5500::new(spi, pin);
61    /// # let (mut spi, mut pin) = w5500.free();
62    /// # spi.done(); pin.done();
63    /// # Ok::<(), hal::MockError>(())
64    /// ```
65    #[inline]
66    #[allow(clippy::unnecessary_safety_doc)]
67    pub fn new(spi: SPI, cs: CS) -> Self {
68        W5500 { spi, cs }
69    }
70
71    /// Free the SPI bus and CS pin from the W5500.
72    ///
73    /// # Example
74    ///
75    /// ```
76    /// # use ehm::eh0 as hal;
77    /// # let spi = hal::spi::Mock::new(&[]);
78    /// # let pin = hal::pin::Mock::new(&[]);
79    /// use w5500_ll::eh0::vdm::W5500;
80    ///
81    /// let mut w5500 = W5500::new(spi, pin);
82    /// let (mut spi, mut pin) = w5500.free();
83    /// # spi.done(); pin.done();
84    /// ```
85    #[inline]
86    pub fn free(self) -> (SPI, CS) {
87        (self.spi, self.cs)
88    }
89
90    #[inline]
91    fn with_chip_enable<T, E, F>(&mut self, mut f: F) -> Result<T, E>
92    where
93        F: FnMut(&mut SPI) -> Result<T, E>,
94        E: core::convert::From<Error<SpiError, PinError>>,
95    {
96        self.cs.set_low().map_err(Error::Pin)?;
97        let result = f(&mut self.spi);
98        self.cs.set_high().map_err(Error::Pin)?;
99        self.cs.set_high().map_err(Error::Pin)?;
100        result
101    }
102}
103
104impl<SPI, CS, SpiError, PinError> crate::Registers for W5500<SPI, CS>
105where
106    SPI: eh0::blocking::spi::Transfer<u8, Error = SpiError>
107        + eh0::blocking::spi::Write<u8, Error = SpiError>,
108    CS: OutputPin<Error = PinError>,
109{
110    /// SPI IO error type.
111    type Error = Error<SpiError, PinError>;
112
113    /// Read from the W5500.
114    #[inline]
115    fn read(&mut self, address: u16, block: u8, data: &mut [u8]) -> Result<(), Self::Error> {
116        let header = vdm_header(address, block, AccessMode::Read);
117        self.with_chip_enable(|spi| {
118            spi.write(&header).map_err(Error::Spi)?;
119            spi.transfer(data).map_err(Error::Spi)?;
120            Ok(())
121        })
122    }
123
124    /// Write to the W5500.
125    #[inline]
126    fn write(&mut self, address: u16, block: u8, data: &[u8]) -> Result<(), Self::Error> {
127        let header = vdm_header(address, block, AccessMode::Write);
128        self.with_chip_enable(|spi| {
129            spi.write(&header).map_err(Error::Spi)?;
130            spi.write(data).map_err(Error::Spi)?;
131            Ok(())
132        })
133    }
134}