embassy_net_wiznet/
lib.rs

1#![no_std]
2#![allow(async_fn_in_trait)]
3#![doc = include_str!("../README.md")]
4#![warn(missing_docs)]
5
6pub mod chip;
7mod device;
8
9use embassy_futures::select::{select3, Either3};
10use embassy_net_driver_channel as ch;
11use embassy_net_driver_channel::driver::LinkState;
12use embassy_time::{Duration, Ticker, Timer};
13use embedded_hal::digital::OutputPin;
14use embedded_hal_async::digital::Wait;
15use embedded_hal_async::spi::SpiDevice;
16
17use crate::chip::Chip;
18pub use crate::device::InitError;
19use crate::device::WiznetDevice;
20
21// If you change this update the docs of State
22const MTU: usize = 1514;
23
24/// Type alias for the embassy-net driver.
25pub type Device<'d> = embassy_net_driver_channel::Device<'d, MTU>;
26
27/// Internal state for the embassy-net integration.
28///
29/// The two generic arguments `N_RX` and `N_TX` set the size of the receive and
30/// send packet queue. With a the ethernet MTU of _1514_ this takes up `N_RX +
31/// NTX * 1514` bytes. While setting these both to 1 is the minimum this might
32/// hurt performance as a packet can not be received while processing another.
33///
34/// # Warning
35/// On devices with a small amount of ram (think ~64k) watch out with the size
36/// of there parameters. They will quickly use too much RAM.
37pub struct State<const N_RX: usize, const N_TX: usize> {
38    ch_state: ch::State<MTU, N_RX, N_TX>,
39}
40
41impl<const N_RX: usize, const N_TX: usize> State<N_RX, N_TX> {
42    /// Create a new `State`.
43    pub const fn new() -> Self {
44        Self {
45            ch_state: ch::State::new(),
46        }
47    }
48}
49
50/// Background runner for the driver.
51///
52/// You must call `.run()` in a background task for the driver to operate.
53pub struct Runner<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> {
54    mac: WiznetDevice<C, SPI>,
55    ch: ch::Runner<'d, MTU>,
56    int: INT,
57    _reset: RST,
58}
59
60/// You must call this in a background task for the driver to operate.
61impl<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, C, SPI, INT, RST> {
62    /// Run the driver.
63    pub async fn run(mut self) -> ! {
64        let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split();
65        let mut tick = Ticker::every(Duration::from_millis(500));
66        loop {
67            match select3(
68                async {
69                    self.int.wait_for_low().await.ok();
70                    rx_chan.rx_buf().await
71                },
72                tx_chan.tx_buf(),
73                tick.next(),
74            )
75            .await
76            {
77                Either3::First(p) => {
78                    if let Ok(n) = self.mac.read_frame(p).await {
79                        rx_chan.rx_done(n);
80                    }
81                }
82                Either3::Second(p) => {
83                    self.mac.write_frame(p).await.ok();
84                    tx_chan.tx_done();
85                }
86                Either3::Third(()) => {
87                    if self.mac.is_link_up().await {
88                        state_chan.set_link_state(LinkState::Up);
89                    } else {
90                        state_chan.set_link_state(LinkState::Down);
91                    }
92                }
93            }
94        }
95    }
96}
97
98/// Create a Wiznet ethernet chip driver for [`embassy-net`](https://crates.io/crates/embassy-net).
99///
100/// This returns two structs:
101/// - a `Device` that you must pass to the `embassy-net` stack.
102/// - a `Runner`. You must call `.run()` on it in a background task.
103pub async fn new<'a, const N_RX: usize, const N_TX: usize, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin>(
104    mac_addr: [u8; 6],
105    state: &'a mut State<N_RX, N_TX>,
106    spi_dev: SPI,
107    int: INT,
108    mut reset: RST,
109) -> Result<(Device<'a>, Runner<'a, C, SPI, INT, RST>), InitError<SPI::Error>> {
110    // Reset the chip.
111    reset.set_low().ok();
112    // Ensure the reset is registered.
113    Timer::after_millis(1).await;
114    reset.set_high().ok();
115
116    // Wait for PLL lock. Some chips are slower than others.
117    // Slowest is w5100s which is 100ms, so let's just wait that.
118    Timer::after_millis(100).await;
119
120    let mac = WiznetDevice::new(spi_dev, mac_addr).await?;
121
122    let (runner, device) = ch::new(&mut state.ch_state, ch::driver::HardwareAddress::Ethernet(mac_addr));
123
124    Ok((
125        device,
126        Runner {
127            ch: runner,
128            mac,
129            int,
130            _reset: reset,
131        },
132    ))
133}