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}