From d34ef053fd499c6302fb955498d6d91913a91a58 Mon Sep 17 00:00:00 2001 From: Peter Hartley Date: Mon, 15 Apr 2024 10:47:53 +0100 Subject: [PATCH 1/3] Allow interrupt-driven MACRAW operation This commit adds methods to RawDevice that enable interrupt-driven operation. The enable_interrupt() method sets up SIMR so that socket-level (internal) interrupts on Socket 0 cause chip-level (external) interrupts (and as a convenience also sets S0_IR as required). The disable_interrupt() method reverses those changes. The clear_interrupt() method acknowledges all interrupts and is intended to be called from the interrupt handler (or from thread mode soon afterwards). There is no change to existing functionality or operation if enable_interrupt() is never called. I did see PR#34 before filing this, but that change is focused on TCP and UDP sockets, and my use case is MACRAW mode. Tested on a W5500-Pico-EVB board with the RP2040 successfully receiving and acting on active-low GPIO interrupts from W5500 via the INTn signal on W5500 pin 36. --- src/raw_device.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ src/register.rs | 3 +++ 2 files changed, 45 insertions(+) diff --git a/src/raw_device.rs b/src/raw_device.rs index 25b09a9..c9e1224 100644 --- a/src/raw_device.rs +++ b/src/raw_device.rs @@ -39,6 +39,48 @@ impl RawDevice { Ok(Self { bus, raw_socket }) } + /// Enable one or more interrupts + /// + /// # Args + /// * `which` - The interrupts to enable; see `register::socketn::Interrupt` + /// For instance, pass `Interrupt::Receive` to get interrupts + /// on packet reception only. + /// + pub fn enable_interrupt(&mut self, which: u8) -> Result<(), SpiBus::Error> { + self.raw_socket.set_interrupt_mask(&mut self.bus, which)?; + self.bus.write_frame( + register::COMMON, + register::common::SOCKET_INTERRUPT_MASK, + &[1], + )?; + Ok(()) + } + + /// Clear pending interrupts + /// + /// If using RTIC or similar, this should be called from the + /// interrupt handler. If not (i.e., if there's concern that this + /// use of the SPI bus will clobber someone else's use), then you + /// can mask the interrupt *at microcontroller level* in the + /// interrupt handler, then call this from thread mode before + /// unmasking again. + pub fn clear_interrupt(&mut self) -> Result<(), SpiBus::Error> { + self.raw_socket + .reset_interrupt(&mut self.bus, register::socketn::Interrupt::All) + } + + /// Disable all interrupts + /// + pub fn disable_interrupt(&mut self) -> Result<(), SpiBus::Error> { + self.bus.write_frame( + register::COMMON, + register::common::SOCKET_INTERRUPT_MASK, + &[0], + )?; + self.raw_socket.set_interrupt_mask(&mut self.bus, 0xFF)?; + Ok(()) + } + /// Read an ethernet frame from the device. /// /// # Args diff --git a/src/register.rs b/src/register.rs index 3a1acfa..03b885b 100644 --- a/src/register.rs +++ b/src/register.rs @@ -22,6 +22,9 @@ pub mod common { /// Register: INTLEVEL (Interrupt Low Level Timer Register) [R/W] [0x0013 – 0x0014] [0x0000] pub const INTERRUPT_TIMER: u16 = 0x13; + /// Register: SIMR (Socket Interrupt Mask Register) [R/W] [0x0018] [0x00] + pub const SOCKET_INTERRUPT_MASK: u16 = 0x18; + /// Register: RTR (Retry Time-value Register) [R/W] [0x0019 – 0x001A] [0x07D0] pub const RETRY_TIME: u16 = 0x19; From fd6d254d5293fd7b3bdd52d5d949597958e99741 Mon Sep 17 00:00:00 2001 From: Peter Hartley Date: Mon, 15 Apr 2024 12:10:12 +0100 Subject: [PATCH 2/3] Add CHANGELOG entry and fix review comments --- CHANGELOG.md | 1 + src/raw_device.rs | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 010d5dd..7df0d22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed an issue where internal function names were conflicting with trait names by [@ryan-summers](https://github.com/ryan-summers) ([#36](https://github.com/kellerkindt/w5500/issues/36)) - Add `RetryTime` and `RetryCount` common register methods to `Device` and `UninitializedDevice` by [@elpiel](https://github.com/elpiel) ([#54][PR54]) - Add `Udp::get_port` and `Udp::set_port` by [@elpiel](https://github.com/elpiel) ([#57](https://github.com/kellerkindt/w5500/pull/57)) +- Add `RawDevice::enable_interrupts` (and `clear_interrupts`, `disable_interrupts`) for interrupt-driven MACRAW mode by [@pdh11](https://github.com/pdh11) ([#60](https://github.com/kellerkindt/w5000/pull/60)) ### Fixed diff --git a/src/raw_device.rs b/src/raw_device.rs index c9e1224..e9cec64 100644 --- a/src/raw_device.rs +++ b/src/raw_device.rs @@ -46,7 +46,7 @@ impl RawDevice { /// For instance, pass `Interrupt::Receive` to get interrupts /// on packet reception only. /// - pub fn enable_interrupt(&mut self, which: u8) -> Result<(), SpiBus::Error> { + pub fn enable_interrupts(&mut self, which: u8) -> Result<(), SpiBus::Error> { self.raw_socket.set_interrupt_mask(&mut self.bus, which)?; self.bus.write_frame( register::COMMON, @@ -64,14 +64,14 @@ impl RawDevice { /// can mask the interrupt *at microcontroller level* in the /// interrupt handler, then call this from thread mode before /// unmasking again. - pub fn clear_interrupt(&mut self) -> Result<(), SpiBus::Error> { + pub fn clear_interrupts(&mut self) -> Result<(), SpiBus::Error> { self.raw_socket .reset_interrupt(&mut self.bus, register::socketn::Interrupt::All) } /// Disable all interrupts /// - pub fn disable_interrupt(&mut self) -> Result<(), SpiBus::Error> { + pub fn disable_interrupts(&mut self) -> Result<(), SpiBus::Error> { self.bus.write_frame( register::COMMON, register::common::SOCKET_INTERRUPT_MASK, From 25683d610adbc9ee7170d71f30ab7ef88a3ea10a Mon Sep 17 00:00:00 2001 From: Peter Hartley Date: Mon, 15 Apr 2024 12:10:46 +0100 Subject: [PATCH 3/3] Fix new cargo fmt issues --- src/tcp.rs | 2 +- src/udp.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tcp.rs b/src/tcp.rs index 7f1075c..e57fca2 100644 --- a/src/tcp.rs +++ b/src/tcp.rs @@ -223,7 +223,7 @@ impl TcpClientStack for DeviceRefMut<'_, SpiBus, Ho remote: SocketAddr, ) -> nb::Result<(), Self::Error> { let SocketAddr::V4(remote) = remote else { - return Err(nb::Error::Other(Self::Error::UnsupportedAddress)) + return Err(nb::Error::Other(Self::Error::UnsupportedAddress)); }; // TODO dynamically select a random port socket.open(&mut self.bus, 49849 + u16::from(socket.socket.index))?; // chosen by fair dice roll. diff --git a/src/udp.rs b/src/udp.rs index aa47008..7937f55 100644 --- a/src/udp.rs +++ b/src/udp.rs @@ -534,7 +534,7 @@ where remote: SocketAddr, ) -> Result<(), Self::Error> { let SocketAddr::V4(remote) = remote else { - return Err(Self::Error::UnsupportedAddress) + return Err(Self::Error::UnsupportedAddress); }; socket.open(&mut self.bus)?; socket.set_destination(&mut self.bus, remote)?; @@ -602,7 +602,7 @@ where buffer: &[u8], ) -> nb::Result<(), Self::Error> { let SocketAddr::V4(remote) = remote else { - return Err(nb::Error::Other(Self::Error::UnsupportedAddress)) + return Err(nb::Error::Other(Self::Error::UnsupportedAddress)); }; socket.socket_send_to(&mut self.bus, remote, buffer)?;