From 76276c326aaa4fd64a73253a480e2ea22f5ff740 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 15 Aug 2023 17:50:56 +0200 Subject: [PATCH] net-w5500: extract chip-specific stuff to a trait. --- embassy-net-w5500/src/chip/mod.rs | 41 ++++++++ embassy-net-w5500/src/chip/w5500.rs | 67 +++++++++++++ embassy-net-w5500/src/device.rs | 94 +++++++++---------- embassy-net-w5500/src/lib.rs | 19 ++-- embassy-net-w5500/src/spi.rs | 34 ------- .../rp/src/bin/ethernet_w5500_multisocket.rs | 2 + .../rp/src/bin/ethernet_w5500_tcp_client.rs | 2 + .../rp/src/bin/ethernet_w5500_tcp_server.rs | 2 + examples/rp/src/bin/ethernet_w5500_udp.rs | 2 + 9 files changed, 169 insertions(+), 94 deletions(-) create mode 100644 embassy-net-w5500/src/chip/mod.rs create mode 100644 embassy-net-w5500/src/chip/w5500.rs delete mode 100644 embassy-net-w5500/src/spi.rs diff --git a/embassy-net-w5500/src/chip/mod.rs b/embassy-net-w5500/src/chip/mod.rs new file mode 100644 index 00000000..b7d550e2 --- /dev/null +++ b/embassy-net-w5500/src/chip/mod.rs @@ -0,0 +1,41 @@ +mod w5500; +pub use w5500::W5500; + +pub(crate) mod sealed { + use embedded_hal_async::spi::SpiDevice; + + pub trait Chip { + type Address; + + const COMMON_MODE: Self::Address; + const COMMON_MAC: Self::Address; + const COMMON_SOCKET_INTR: Self::Address; + const COMMON_PHY_CFG: Self::Address; + const SOCKET_MODE: Self::Address; + const SOCKET_COMMAND: Self::Address; + const SOCKET_RXBUF_SIZE: Self::Address; + const SOCKET_TXBUF_SIZE: Self::Address; + const SOCKET_TX_FREE_SIZE: Self::Address; + const SOCKET_TX_DATA_WRITE_PTR: Self::Address; + const SOCKET_RECVD_SIZE: Self::Address; + const SOCKET_RX_DATA_READ_PTR: Self::Address; + const SOCKET_INTR_MASK: Self::Address; + const SOCKET_INTR: Self::Address; + + fn rx_addr(addr: u16) -> Self::Address; + fn tx_addr(addr: u16) -> Self::Address; + + async fn bus_read( + spi: &mut SPI, + address: Self::Address, + data: &mut [u8], + ) -> Result<(), SPI::Error>; + async fn bus_write( + spi: &mut SPI, + address: Self::Address, + data: &[u8], + ) -> Result<(), SPI::Error>; + } +} + +pub trait Chip: sealed::Chip {} diff --git a/embassy-net-w5500/src/chip/w5500.rs b/embassy-net-w5500/src/chip/w5500.rs new file mode 100644 index 00000000..f514e12a --- /dev/null +++ b/embassy-net-w5500/src/chip/w5500.rs @@ -0,0 +1,67 @@ +use embedded_hal_async::spi::{Operation, SpiDevice}; + +#[repr(u8)] +pub enum RegisterBlock { + Common = 0x00, + Socket0 = 0x01, + TxBuf = 0x02, + RxBuf = 0x03, +} + +pub enum W5500 {} + +impl super::Chip for W5500 {} +impl super::sealed::Chip for W5500 { + type Address = (RegisterBlock, u16); + + const COMMON_MODE: Self::Address = (RegisterBlock::Common, 0x00); + const COMMON_MAC: Self::Address = (RegisterBlock::Common, 0x09); + const COMMON_SOCKET_INTR: Self::Address = (RegisterBlock::Common, 0x18); + const COMMON_PHY_CFG: Self::Address = (RegisterBlock::Common, 0x2E); + + const SOCKET_MODE: Self::Address = (RegisterBlock::Socket0, 0x00); + const SOCKET_COMMAND: Self::Address = (RegisterBlock::Socket0, 0x01); + const SOCKET_RXBUF_SIZE: Self::Address = (RegisterBlock::Socket0, 0x1E); + const SOCKET_TXBUF_SIZE: Self::Address = (RegisterBlock::Socket0, 0x1F); + const SOCKET_TX_FREE_SIZE: Self::Address = (RegisterBlock::Socket0, 0x20); + const SOCKET_TX_DATA_WRITE_PTR: Self::Address = (RegisterBlock::Socket0, 0x24); + const SOCKET_RECVD_SIZE: Self::Address = (RegisterBlock::Socket0, 0x26); + const SOCKET_RX_DATA_READ_PTR: Self::Address = (RegisterBlock::Socket0, 0x28); + const SOCKET_INTR_MASK: Self::Address = (RegisterBlock::Socket0, 0x2C); + const SOCKET_INTR: Self::Address = (RegisterBlock::Socket0, 0x02); + + fn rx_addr(addr: u16) -> Self::Address { + (RegisterBlock::RxBuf, addr) + } + + fn tx_addr(addr: u16) -> Self::Address { + (RegisterBlock::TxBuf, addr) + } + + async fn bus_read( + spi: &mut SPI, + address: Self::Address, + data: &mut [u8], + ) -> Result<(), SPI::Error> { + let address_phase = address.1.to_be_bytes(); + let control_phase = [(address.0 as u8) << 3]; + let operations = &mut [ + Operation::Write(&address_phase), + Operation::Write(&control_phase), + Operation::TransferInPlace(data), + ]; + spi.transaction(operations).await + } + + async fn bus_write(spi: &mut SPI, address: Self::Address, data: &[u8]) -> Result<(), SPI::Error> { + let address_phase = address.1.to_be_bytes(); + let control_phase = [(address.0 as u8) << 3 | 0b0000_0100]; + let data_phase = data; + let operations = &mut [ + Operation::Write(&address_phase[..]), + Operation::Write(&control_phase), + Operation::Write(&data_phase), + ]; + spi.transaction(operations).await + } +} diff --git a/embassy-net-w5500/src/device.rs b/embassy-net-w5500/src/device.rs index cf451b33..a6ee8f8f 100644 --- a/embassy-net-w5500/src/device.rs +++ b/embassy-net-w5500/src/device.rs @@ -1,117 +1,107 @@ +use core::marker::PhantomData; + use embedded_hal_async::spi::SpiDevice; -use crate::spi::{Address, SpiInterface}; - -pub const COMMON_MODE: Address = (RegisterBlock::Common, 0x00); -pub const COMMON_MAC: Address = (RegisterBlock::Common, 0x09); -pub const COMMON_SOCKET_INTR: Address = (RegisterBlock::Common, 0x18); -pub const COMMON_PHY_CFG: Address = (RegisterBlock::Common, 0x2E); - -pub const SOCKET_MODE: Address = (RegisterBlock::Socket0, 0x00); -pub const SOCKET_COMMAND: Address = (RegisterBlock::Socket0, 0x01); -pub const SOCKET_RXBUF_SIZE: Address = (RegisterBlock::Socket0, 0x1E); -pub const SOCKET_TXBUF_SIZE: Address = (RegisterBlock::Socket0, 0x1F); -pub const SOCKET_TX_FREE_SIZE: Address = (RegisterBlock::Socket0, 0x20); -pub const SOCKET_TX_DATA_WRITE_PTR: Address = (RegisterBlock::Socket0, 0x24); -pub const SOCKET_RECVD_SIZE: Address = (RegisterBlock::Socket0, 0x26); -pub const SOCKET_RX_DATA_READ_PTR: Address = (RegisterBlock::Socket0, 0x28); -pub const SOCKET_INTR_MASK: Address = (RegisterBlock::Socket0, 0x2C); -pub const SOCKET_INTR: Address = (RegisterBlock::Socket0, 0x02); +use crate::chip::Chip; #[repr(u8)] -pub enum Command { +enum Command { Open = 0x01, Send = 0x20, Receive = 0x40, } #[repr(u8)] -pub enum Interrupt { +enum Interrupt { Receive = 0b00100_u8, } -#[repr(u8)] -pub enum RegisterBlock { - Common = 0x00, - Socket0 = 0x01, - TxBuf = 0x02, - RxBuf = 0x03, -} - /// W5500 in MACRAW mode #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct W5500 { - bus: SpiInterface, +pub(crate) struct WiznetDevice { + spi: SPI, + _phantom: PhantomData, } -impl W5500 { +impl WiznetDevice { /// Create and initialize the W5500 driver - pub async fn new(spi: SPI, mac_addr: [u8; 6]) -> Result, SPI::Error> { - let mut bus = SpiInterface(spi); + pub async fn new(spi: SPI, mac_addr: [u8; 6]) -> Result { + let mut this = Self { + spi, + _phantom: PhantomData, + }; + // Reset device - bus.write_frame(COMMON_MODE, &[0x80]).await?; + this.bus_write(C::COMMON_MODE, &[0x80]).await?; // Enable interrupt pin - bus.write_frame(COMMON_SOCKET_INTR, &[0x01]).await?; + this.bus_write(C::COMMON_SOCKET_INTR, &[0x01]).await?; // Enable receive interrupt - bus.write_frame(SOCKET_INTR_MASK, &[Interrupt::Receive as u8]).await?; + this.bus_write(C::SOCKET_INTR_MASK, &[Interrupt::Receive as u8]).await?; // Set MAC address - bus.write_frame(COMMON_MAC, &mac_addr).await?; + this.bus_write(C::COMMON_MAC, &mac_addr).await?; // Set the raw socket RX/TX buffer sizes to 16KB - bus.write_frame(SOCKET_TXBUF_SIZE, &[16]).await?; - bus.write_frame(SOCKET_RXBUF_SIZE, &[16]).await?; + this.bus_write(C::SOCKET_TXBUF_SIZE, &[16]).await?; + this.bus_write(C::SOCKET_RXBUF_SIZE, &[16]).await?; // MACRAW mode with MAC filtering. let mode: u8 = (1 << 2) | (1 << 7); - bus.write_frame(SOCKET_MODE, &[mode]).await?; - let mut this = Self { bus }; + this.bus_write(C::SOCKET_MODE, &[mode]).await?; this.command(Command::Open).await?; Ok(this) } + async fn bus_read(&mut self, address: C::Address, data: &mut [u8]) -> Result<(), SPI::Error> { + C::bus_read(&mut self.spi, address, data).await + } + + async fn bus_write(&mut self, address: C::Address, data: &[u8]) -> Result<(), SPI::Error> { + C::bus_write(&mut self.spi, address, data).await + } + async fn reset_interrupt(&mut self, code: Interrupt) -> Result<(), SPI::Error> { let data = [code as u8]; - self.bus.write_frame(SOCKET_INTR, &data).await + self.bus_write(C::SOCKET_INTR, &data).await } async fn get_tx_write_ptr(&mut self) -> Result { let mut data = [0u8; 2]; - self.bus.read_frame(SOCKET_TX_DATA_WRITE_PTR, &mut data).await?; + self.bus_read(C::SOCKET_TX_DATA_WRITE_PTR, &mut data).await?; Ok(u16::from_be_bytes(data)) } async fn set_tx_write_ptr(&mut self, ptr: u16) -> Result<(), SPI::Error> { let data = ptr.to_be_bytes(); - self.bus.write_frame(SOCKET_TX_DATA_WRITE_PTR, &data).await + self.bus_write(C::SOCKET_TX_DATA_WRITE_PTR, &data).await } async fn get_rx_read_ptr(&mut self) -> Result { let mut data = [0u8; 2]; - self.bus.read_frame(SOCKET_RX_DATA_READ_PTR, &mut data).await?; + self.bus_read(C::SOCKET_RX_DATA_READ_PTR, &mut data).await?; Ok(u16::from_be_bytes(data)) } async fn set_rx_read_ptr(&mut self, ptr: u16) -> Result<(), SPI::Error> { let data = ptr.to_be_bytes(); - self.bus.write_frame(SOCKET_RX_DATA_READ_PTR, &data).await + self.bus_write(C::SOCKET_RX_DATA_READ_PTR, &data).await } async fn command(&mut self, command: Command) -> Result<(), SPI::Error> { let data = [command as u8]; - self.bus.write_frame(SOCKET_COMMAND, &data).await + self.bus_write(C::SOCKET_COMMAND, &data).await } async fn get_rx_size(&mut self) -> Result { loop { // Wait until two sequential reads are equal let mut res0 = [0u8; 2]; - self.bus.read_frame(SOCKET_RECVD_SIZE, &mut res0).await?; + self.bus_read(C::SOCKET_RECVD_SIZE, &mut res0).await?; let mut res1 = [0u8; 2]; - self.bus.read_frame(SOCKET_RECVD_SIZE, &mut res1).await?; + self.bus_read(C::SOCKET_RECVD_SIZE, &mut res1).await?; if res0 == res1 { break Ok(u16::from_be_bytes(res0)); } @@ -120,13 +110,13 @@ impl W5500 { async fn get_tx_free_size(&mut self) -> Result { let mut data = [0; 2]; - self.bus.read_frame(SOCKET_TX_FREE_SIZE, &mut data).await?; + self.bus_read(C::SOCKET_TX_FREE_SIZE, &mut data).await?; Ok(u16::from_be_bytes(data)) } /// Read bytes from the RX buffer. Returns the number of bytes read. async fn read_bytes(&mut self, read_ptr: &mut u16, buffer: &mut [u8]) -> Result<(), SPI::Error> { - self.bus.read_frame((RegisterBlock::RxBuf, *read_ptr), buffer).await?; + self.bus_read(C::rx_addr(*read_ptr), buffer).await?; *read_ptr = (*read_ptr).wrapping_add(buffer.len() as u16); Ok(()) @@ -165,7 +155,7 @@ impl W5500 { pub async fn write_frame(&mut self, frame: &[u8]) -> Result { while self.get_tx_free_size().await? < frame.len() as u16 {} let write_ptr = self.get_tx_write_ptr().await?; - self.bus.write_frame((RegisterBlock::TxBuf, write_ptr), frame).await?; + self.bus_write(C::tx_addr(write_ptr), frame).await?; self.set_tx_write_ptr(write_ptr.wrapping_add(frame.len() as u16)) .await?; self.command(Command::Send).await?; @@ -174,7 +164,7 @@ impl W5500 { pub async fn is_link_up(&mut self) -> bool { let mut link = [0]; - self.bus.read_frame(COMMON_PHY_CFG, &mut link).await.ok(); + self.bus_read(C::COMMON_PHY_CFG, &mut link).await.ok(); link[0] & 1 == 1 } } diff --git a/embassy-net-w5500/src/lib.rs b/embassy-net-w5500/src/lib.rs index 3c54777d..9b53e961 100644 --- a/embassy-net-w5500/src/lib.rs +++ b/embassy-net-w5500/src/lib.rs @@ -1,8 +1,9 @@ //! [`embassy-net`](https://crates.io/crates/embassy-net) driver for the WIZnet W5500 ethernet chip. #![no_std] +#![feature(async_fn_in_trait)] +pub mod chip; mod device; -mod spi; use embassy_futures::select::{select, Either}; use embassy_net_driver_channel as ch; @@ -12,7 +13,9 @@ use embedded_hal::digital::OutputPin; use embedded_hal_async::digital::Wait; use embedded_hal_async::spi::SpiDevice; -use crate::device::W5500; +use crate::chip::Chip; +use crate::device::WiznetDevice; + const MTU: usize = 1514; /// Type alias for the embassy-net driver for W5500 @@ -35,15 +38,15 @@ impl State { /// Background runner for the W5500. /// /// You must call `.run()` in a background task for the W5500 to operate. -pub struct Runner<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> { - mac: W5500, +pub struct Runner<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> { + mac: WiznetDevice, ch: ch::Runner<'d, MTU>, int: INT, _reset: RST, } /// You must call this in a background task for the W5500 to operate. -impl<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, SPI, INT, RST> { +impl<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, C, SPI, INT, RST> { pub async fn run(mut self) -> ! { let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split(); loop { @@ -78,13 +81,13 @@ impl<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, SPI, INT, RST> { } /// Obtain a driver for using the W5500 with [`embassy-net`](https://crates.io/crates/embassy-net). -pub async fn new<'a, const N_RX: usize, const N_TX: usize, SPI: SpiDevice, INT: Wait, RST: OutputPin>( +pub async fn new<'a, const N_RX: usize, const N_TX: usize, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin>( mac_addr: [u8; 6], state: &'a mut State, spi_dev: SPI, int: INT, mut reset: RST, -) -> (Device<'a>, Runner<'a, SPI, INT, RST>) { +) -> (Device<'a>, Runner<'a, C, SPI, INT, RST>) { // Reset the W5500. reset.set_low().ok(); // Ensure the reset is registered. @@ -93,7 +96,7 @@ pub async fn new<'a, const N_RX: usize, const N_TX: usize, SPI: SpiDevice, INT: // Wait for the W5500 to achieve PLL lock. Timer::after(Duration::from_millis(2)).await; - let mac = W5500::new(spi_dev, mac_addr).await.unwrap(); + let mac = WiznetDevice::new(spi_dev, mac_addr).await.unwrap(); let (runner, device) = ch::new(&mut state.ch_state, ch::driver::HardwareAddress::Ethernet(mac_addr)); ( diff --git a/embassy-net-w5500/src/spi.rs b/embassy-net-w5500/src/spi.rs deleted file mode 100644 index 316c6521..00000000 --- a/embassy-net-w5500/src/spi.rs +++ /dev/null @@ -1,34 +0,0 @@ -use embedded_hal_async::spi::{Operation, SpiDevice}; - -use crate::device::RegisterBlock; - -pub type Address = (RegisterBlock, u16); - -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct SpiInterface(pub SPI); - -impl SpiInterface { - pub async fn read_frame(&mut self, address: Address, data: &mut [u8]) -> Result<(), SPI::Error> { - let address_phase = address.1.to_be_bytes(); - let control_phase = [(address.0 as u8) << 3]; - let operations = &mut [ - Operation::Write(&address_phase), - Operation::Write(&control_phase), - Operation::TransferInPlace(data), - ]; - self.0.transaction(operations).await - } - - pub async fn write_frame(&mut self, address: Address, data: &[u8]) -> Result<(), SPI::Error> { - let address_phase = address.1.to_be_bytes(); - let control_phase = [(address.0 as u8) << 3 | 0b0000_0100]; - let data_phase = data; - let operations = &mut [ - Operation::Write(&address_phase[..]), - Operation::Write(&control_phase), - Operation::Write(&data_phase), - ]; - self.0.transaction(operations).await - } -} diff --git a/examples/rp/src/bin/ethernet_w5500_multisocket.rs b/examples/rp/src/bin/ethernet_w5500_multisocket.rs index 9f800d0d..3677f3cd 100644 --- a/examples/rp/src/bin/ethernet_w5500_multisocket.rs +++ b/examples/rp/src/bin/ethernet_w5500_multisocket.rs @@ -10,6 +10,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_futures::yield_now; use embassy_net::{Stack, StackResources}; +use embassy_net_w5500::chip::W5500; use embassy_net_w5500::*; use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Input, Level, Output, Pull}; @@ -26,6 +27,7 @@ use {defmt_rtt as _, panic_probe as _}; async fn ethernet_task( runner: Runner< 'static, + W5500, ExclusiveDevice, Output<'static, PIN_17>, Delay>, Input<'static, PIN_21>, Output<'static, PIN_20>, diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs index fee84b61..b78a0977 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs @@ -12,6 +12,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_futures::yield_now; use embassy_net::{Stack, StackResources}; +use embassy_net_w5500::chip::W5500; use embassy_net_w5500::*; use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Input, Level, Output, Pull}; @@ -28,6 +29,7 @@ use {defmt_rtt as _, panic_probe as _}; async fn ethernet_task( runner: Runner< 'static, + W5500, ExclusiveDevice, Output<'static, PIN_17>, Delay>, Input<'static, PIN_21>, Output<'static, PIN_20>, diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs index 02457426..34f054d9 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs @@ -11,6 +11,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_futures::yield_now; use embassy_net::{Stack, StackResources}; +use embassy_net_w5500::chip::W5500; use embassy_net_w5500::*; use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Input, Level, Output, Pull}; @@ -26,6 +27,7 @@ use {defmt_rtt as _, panic_probe as _}; async fn ethernet_task( runner: Runner< 'static, + W5500, ExclusiveDevice, Output<'static, PIN_17>, Delay>, Input<'static, PIN_21>, Output<'static, PIN_20>, diff --git a/examples/rp/src/bin/ethernet_w5500_udp.rs b/examples/rp/src/bin/ethernet_w5500_udp.rs index 038432b1..8f38e453 100644 --- a/examples/rp/src/bin/ethernet_w5500_udp.rs +++ b/examples/rp/src/bin/ethernet_w5500_udp.rs @@ -11,6 +11,7 @@ use embassy_executor::Spawner; use embassy_futures::yield_now; use embassy_net::udp::{PacketMetadata, UdpSocket}; use embassy_net::{Stack, StackResources}; +use embassy_net_w5500::chip::W5500; use embassy_net_w5500::*; use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Input, Level, Output, Pull}; @@ -25,6 +26,7 @@ use {defmt_rtt as _, panic_probe as _}; async fn ethernet_task( runner: Runner< 'static, + W5500, ExclusiveDevice, Output<'static, PIN_17>, Delay>, Input<'static, PIN_21>, Output<'static, PIN_20>,