diff --git a/embassy-net-w5500/Cargo.toml b/embassy-net-w5500/Cargo.toml index 8972b814a..c45598684 100644 --- a/embassy-net-w5500/Cargo.toml +++ b/embassy-net-w5500/Cargo.toml @@ -18,4 +18,5 @@ defmt = { version = "0.3", optional = true } [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-w5500-v$VERSION/embassy-net-w5500/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-w5500/src/" -target = "thumbv7em-none-eabi" \ No newline at end of file +target = "thumbv7em-none-eabi" +features = ["defmt"] \ No newline at end of file diff --git a/embassy-net-w5500/src/chip/mod.rs b/embassy-net-w5500/src/chip/mod.rs index b7d550e26..562db515a 100644 --- a/embassy-net-w5500/src/chip/mod.rs +++ b/embassy-net-w5500/src/chip/mod.rs @@ -1,5 +1,7 @@ mod w5500; pub use w5500::W5500; +mod w5100s; +pub use w5100s::W5100S; pub(crate) mod sealed { use embedded_hal_async::spi::SpiDevice; @@ -22,6 +24,11 @@ pub(crate) mod sealed { const SOCKET_INTR_MASK: Self::Address; const SOCKET_INTR: Self::Address; + const SOCKET_MODE_VALUE: u8; + + const BUF_SIZE: u16; + const AUTO_WRAP: bool; + fn rx_addr(addr: u16) -> Self::Address; fn tx_addr(addr: u16) -> Self::Address; diff --git a/embassy-net-w5500/src/chip/w5100s.rs b/embassy-net-w5500/src/chip/w5100s.rs new file mode 100644 index 000000000..07a840370 --- /dev/null +++ b/embassy-net-w5500/src/chip/w5100s.rs @@ -0,0 +1,61 @@ +use embedded_hal_async::spi::{Operation, SpiDevice}; + +const SOCKET_BASE: u16 = 0x400; +const TX_BASE: u16 = 0x4000; +const RX_BASE: u16 = 0x6000; + +pub enum W5100S {} + +impl super::Chip for W5100S {} +impl super::sealed::Chip for W5100S { + type Address = u16; + + const COMMON_MODE: Self::Address = 0x00; + const COMMON_MAC: Self::Address = 0x09; + const COMMON_SOCKET_INTR: Self::Address = 0x16; + const COMMON_PHY_CFG: Self::Address = 0x3c; + + const SOCKET_MODE: Self::Address = SOCKET_BASE + 0x00; + const SOCKET_COMMAND: Self::Address = SOCKET_BASE + 0x01; + const SOCKET_RXBUF_SIZE: Self::Address = SOCKET_BASE + 0x1E; + const SOCKET_TXBUF_SIZE: Self::Address = SOCKET_BASE + 0x1F; + const SOCKET_TX_FREE_SIZE: Self::Address = SOCKET_BASE + 0x20; + const SOCKET_TX_DATA_WRITE_PTR: Self::Address = SOCKET_BASE + 0x24; + const SOCKET_RECVD_SIZE: Self::Address = SOCKET_BASE + 0x26; + const SOCKET_RX_DATA_READ_PTR: Self::Address = SOCKET_BASE + 0x28; + const SOCKET_INTR_MASK: Self::Address = SOCKET_BASE + 0x2C; + const SOCKET_INTR: Self::Address = SOCKET_BASE + 0x02; + + const SOCKET_MODE_VALUE: u8 = (1 << 2) | (1 << 6); + + const BUF_SIZE: u16 = 0x2000; + const AUTO_WRAP: bool = false; + + fn rx_addr(addr: u16) -> Self::Address { + RX_BASE + addr + } + + fn tx_addr(addr: u16) -> Self::Address { + TX_BASE + addr + } + + async fn bus_read( + spi: &mut SPI, + address: Self::Address, + data: &mut [u8], + ) -> Result<(), SPI::Error> { + spi.transaction(&mut [ + Operation::Write(&[0x0F, (address >> 8) as u8, address as u8]), + Operation::Read(data), + ]) + .await + } + + async fn bus_write(spi: &mut SPI, address: Self::Address, data: &[u8]) -> Result<(), SPI::Error> { + spi.transaction(&mut [ + Operation::Write(&[0xF0, (address >> 8) as u8, address as u8]), + Operation::Write(data), + ]) + .await + } +} diff --git a/embassy-net-w5500/src/chip/w5500.rs b/embassy-net-w5500/src/chip/w5500.rs index f514e12a7..61e512946 100644 --- a/embassy-net-w5500/src/chip/w5500.rs +++ b/embassy-net-w5500/src/chip/w5500.rs @@ -30,6 +30,11 @@ impl super::sealed::Chip for W5500 { const SOCKET_INTR_MASK: Self::Address = (RegisterBlock::Socket0, 0x2C); const SOCKET_INTR: Self::Address = (RegisterBlock::Socket0, 0x02); + const SOCKET_MODE_VALUE: u8 = (1 << 2) | (1 << 7); + + const BUF_SIZE: u16 = 0x4000; + const AUTO_WRAP: bool = true; + fn rx_addr(addr: u16) -> Self::Address { (RegisterBlock::RxBuf, addr) } diff --git a/embassy-net-w5500/src/device.rs b/embassy-net-w5500/src/device.rs index a6ee8f8f7..f367bc3eb 100644 --- a/embassy-net-w5500/src/device.rs +++ b/embassy-net-w5500/src/device.rs @@ -43,13 +43,13 @@ impl WiznetDevice { // Set MAC address this.bus_write(C::COMMON_MAC, &mac_addr).await?; - // Set the raw socket RX/TX buffer sizes to 16KB - this.bus_write(C::SOCKET_TXBUF_SIZE, &[16]).await?; - this.bus_write(C::SOCKET_RXBUF_SIZE, &[16]).await?; + // Set the raw socket RX/TX buffer sizes. + let buf_kbs = (C::BUF_SIZE / 1024) as u8; + this.bus_write(C::SOCKET_TXBUF_SIZE, &[buf_kbs]).await?; + this.bus_write(C::SOCKET_RXBUF_SIZE, &[buf_kbs]).await?; // MACRAW mode with MAC filtering. - let mode: u8 = (1 << 2) | (1 << 7); - this.bus_write(C::SOCKET_MODE, &[mode]).await?; + this.bus_write(C::SOCKET_MODE, &[C::SOCKET_MODE_VALUE]).await?; this.command(Command::Open).await?; Ok(this) @@ -114,9 +114,21 @@ impl WiznetDevice { Ok(u16::from_be_bytes(data)) } - /// Read bytes from the RX buffer. Returns the number of bytes read. + /// Read bytes from the RX buffer. async fn read_bytes(&mut self, read_ptr: &mut u16, buffer: &mut [u8]) -> Result<(), SPI::Error> { - self.bus_read(C::rx_addr(*read_ptr), buffer).await?; + if C::AUTO_WRAP { + self.bus_read(C::rx_addr(*read_ptr), buffer).await?; + } else { + let addr = *read_ptr % C::BUF_SIZE; + if addr as usize + buffer.len() <= C::BUF_SIZE as usize { + self.bus_read(C::rx_addr(addr), buffer).await?; + } else { + let n = C::BUF_SIZE - addr; + self.bus_read(C::rx_addr(addr), &mut buffer[..n as usize]).await?; + self.bus_read(C::rx_addr(0), &mut buffer[n as usize..]).await?; + } + } + *read_ptr = (*read_ptr).wrapping_add(buffer.len() as u16); Ok(()) @@ -155,7 +167,20 @@ impl WiznetDevice { 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(C::tx_addr(write_ptr), frame).await?; + + if C::AUTO_WRAP { + self.bus_write(C::tx_addr(write_ptr), frame).await?; + } else { + let addr = write_ptr % C::BUF_SIZE; + if addr as usize + frame.len() <= C::BUF_SIZE as usize { + self.bus_write(C::tx_addr(addr), frame).await?; + } else { + let n = C::BUF_SIZE - addr; + self.bus_write(C::tx_addr(addr), &frame[..n as usize]).await?; + self.bus_write(C::tx_addr(0), &frame[n as usize..]).await?; + } + } + self.set_tx_write_ptr(write_ptr.wrapping_add(frame.len() as u16)) .await?; self.command(Command::Send).await?; diff --git a/embassy-net-w5500/src/lib.rs b/embassy-net-w5500/src/lib.rs index 9b53e9618..3030dfb90 100644 --- a/embassy-net-w5500/src/lib.rs +++ b/embassy-net-w5500/src/lib.rs @@ -1,4 +1,4 @@ -//! [`embassy-net`](https://crates.io/crates/embassy-net) driver for the WIZnet W5500 ethernet chip. +//! [`embassy-net`](https://crates.io/crates/embassy-net) driver for WIZnet ethernet chips. #![no_std] #![feature(async_fn_in_trait)] @@ -18,7 +18,7 @@ use crate::device::WiznetDevice; const MTU: usize = 1514; -/// Type alias for the embassy-net driver for W5500 +/// Type alias for the embassy-net driver. pub type Device<'d> = embassy_net_driver_channel::Device<'d, MTU>; /// Internal state for the embassy-net integration. @@ -35,9 +35,9 @@ impl State { } } -/// Background runner for the W5500. +/// Background runner for the driver. /// -/// You must call `.run()` in a background task for the W5500 to operate. +/// You must call `.run()` in a background task for the driver to operate. pub struct Runner<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> { mac: WiznetDevice, ch: ch::Runner<'d, MTU>, @@ -45,7 +45,7 @@ pub struct Runner<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> { _reset: RST, } -/// You must call this in a background task for the W5500 to operate. +/// You must call this in a background task for the driver to operate. 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(); @@ -80,7 +80,11 @@ impl<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, C, SPI, } } -/// Obtain a driver for using the W5500 with [`embassy-net`](https://crates.io/crates/embassy-net). +/// Create a Wiznet ethernet chip driver for [`embassy-net`](https://crates.io/crates/embassy-net). +/// +/// This returns two structs: +/// - a `Device` that you must pass to the `embassy-net` stack. +/// - a `Runner`. You must call `.run()` on it in a background task. 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, @@ -88,13 +92,15 @@ pub async fn new<'a, const N_RX: usize, const N_TX: usize, C: Chip, SPI: SpiDevi int: INT, mut reset: RST, ) -> (Device<'a>, Runner<'a, C, SPI, INT, RST>) { - // Reset the W5500. + // Reset the chip. reset.set_low().ok(); // Ensure the reset is registered. Timer::after(Duration::from_millis(1)).await; reset.set_high().ok(); - // Wait for the W5500 to achieve PLL lock. - Timer::after(Duration::from_millis(2)).await; + + // Wait for PLL lock. Some chips are slower than others. + // Slowest is w5100s which is 100ms, so let's just wait that. + Timer::after(Duration::from_millis(100)).await; let mac = WiznetDevice::new(spi_dev, mac_addr).await.unwrap();