diff --git a/CHANGELOG.md b/CHANGELOG.md index e8becb3..a26c042 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add `defmt` features for enabling `defmt::Format` to most structs and errors by [@elpiel](https://github.com/elpiel) ([#39](https://github.com/kellerkindt/w5500/issues/39)) - 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]) ### Fixed diff --git a/src/device.rs b/src/device.rs index bf41a7c..e7112af 100644 --- a/src/device.rs +++ b/src/device.rs @@ -6,7 +6,10 @@ use crate::host::Host; use crate::net::Ipv4Addr; use crate::socket::Socket; use crate::uninitialized_device::UninitializedDevice; -use crate::{register, MacAddress}; +use crate::{ + register::{self, common::RetryTime}, + MacAddress, Mode, +}; pub enum ResetError { SocketsNotReleased, @@ -52,11 +55,8 @@ impl Device { } fn clear_mode(&mut self) -> Result<(), SpiBus::Error> { - // reset bit - let mode = [0b10000000]; - self.bus - .write_frame(register::COMMON, register::common::MODE, &mode)?; - Ok(()) + // Set RST common register of the w5500 + self.as_mut().reset_device() } #[inline] @@ -89,6 +89,47 @@ impl Device { self.as_mut().version() } + /// Get the currently set Retry Time-value Register. + /// + /// RTR (Retry Time-value Register) [R/W] [0x0019 – 0x001A] [0x07D0] + #[inline] + pub fn current_retry_timeout(&mut self) -> Result { + self.as_mut().current_retry_timeout() + } + + /// Set a new value for the Retry Time-value Register. + /// + /// RTR (Retry Time-value Register) [R/W] [0x0019 – 0x001A] [0x07D0] + /// + /// # Example + /// + /// ``` + /// use w5500::register::common::RetryTime; + /// + /// let default = RetryTime::from_millis(200); + /// assert_eq!(RetryTime::default(), default); + /// + /// // E.g. 4000 (register) = 400ms + /// let four_hundred_ms = RetryTime::from_millis(400); + /// assert_eq!(four_hundred_ms.to_u16(), 4000); + /// ``` + #[inline] + pub fn set_retry_timeout(&mut self, retry_time_value: RetryTime) -> Result<(), SpiBus::Error> { + self.as_mut().set_retry_timeout(retry_time_value) + } + + /// Get the current Retry Count Register value. + #[inline] + pub fn current_retry_count(&mut self) -> Result { + self.as_mut().current_retry_count() + } + + /// Set a new value for the Retry Count register. + #[inline] + pub fn set_retry_count(&mut self, retry_count: u8) -> Result<(), SpiBus::Error> { + self.as_mut().set_retry_count(retry_count) + } + #[inline] pub(crate) fn as_mut(&mut self) -> DeviceRefMut<'_, BusRef<'_, SpiBus>, HostImpl> { DeviceRefMut { @@ -181,10 +222,116 @@ impl DeviceRefMut<'_, SpiBus, HostImpl> { Ok(phy[0].into()) } - pub fn version(&mut self) -> Result { - let mut version = [0u8]; + #[inline] + pub fn reset_device(&mut self) -> Result<(), SpiBus::Error> { + // Set RST common register of the w5500 + let mode = [0b10000000]; self.bus - .read_frame(register::COMMON, register::common::VERSION, &mut version)?; - Ok(version[0]) + .write_frame(register::COMMON, register::common::MODE, &mode) + } + + #[inline] + pub fn set_mode(&mut self, mode_options: Mode) -> Result<(), SpiBus::Error> { + self.bus.write_frame( + register::COMMON, + register::common::MODE, + &mode_options.to_register(), + ) + } + + #[inline] + pub fn version(&mut self) -> Result { + let mut version_register = [0_u8]; + self.bus.read_frame( + register::COMMON, + register::common::VERSION, + &mut version_register, + )?; + + Ok(version_register[0]) + } + + /// RTR (Retry Time-value Register) [R/W] [0x0019 – 0x001A] [0x07D0] + /// + /// # Example + /// + /// ``` + /// use w5500::register::common::RetryTime; + /// + /// let default = RetryTime::from_millis(200); + /// assert_eq!(RetryTime::default(), default); + /// + /// // E.g. 4000 (register) = 400ms + /// let four_hundred_ms = RetryTime::from_millis(400); + /// assert_eq!(four_hundred_ms.to_u16(), 4000); + /// ``` + #[inline] + pub fn set_retry_timeout(&mut self, retry_time_value: RetryTime) -> Result<(), SpiBus::Error> { + self.bus.write_frame( + register::COMMON, + register::common::RETRY_TIME, + &retry_time_value.to_register(), + )?; + + Ok(()) + } + + /// RTR (Retry Time-value Register) [R/W] [0x0019 – 0x001A] [0x07D0] + /// + /// E.g. 4000 = 400ms + #[inline] + pub fn current_retry_timeout(&mut self) -> Result { + let mut retry_time_register: [u8; 2] = [0, 0]; + self.bus.read_frame( + register::COMMON, + register::common::RETRY_TIME, + &mut retry_time_register, + )?; + + Ok(RetryTime::from_register(retry_time_register)) + } + + /// Set a new value for the Retry Count register. + /// + /// RCR (Retry Count Register) [R/W] [0x001B] [0x08] + /// + /// For more details check out the rest of the datasheet documentation on the Retry count. + /// + /// From datasheet: + /// + /// RCR configures the number of time of retransmission. When retransmission occurs + /// as many as ‘RCR+1’, Timeout interrupt is issued (Sn_IR[TIMEOUT] = ‘1’). + /// + /// The timeout of W5500 can be configurable with RTR and RCR. W5500 has two kind + /// timeout such as Address Resolution Protocol (ARP) and TCP retransmission. + /// + /// E.g. In case of errors it will retry for 7 times: + /// `RCR = 0x0007` + pub fn set_retry_count(&mut self, retry_count: u8) -> Result<(), SpiBus::Error> { + self.bus.write_frame( + register::COMMON, + register::common::RETRY_COUNT, + &[retry_count], + )?; + + Ok(()) + } + + /// Get the current Retry Count value + /// + /// RCR (Retry Count Register) [R/W] [0x001B] [0x08] + /// + /// E.g. In case of errors it will retry for 7 times: + /// `RCR = 0x0007` + #[inline] + pub fn current_retry_count(&mut self) -> Result { + let mut retry_count_register: [u8; 1] = [0]; + self.bus.read_frame( + register::COMMON, + register::common::RETRY_COUNT, + &mut retry_count_register, + )?; + + Ok(retry_count_register[0]) } } diff --git a/src/lib.rs b/src/lib.rs index 475ba9e..124ff6d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] #![allow(unused)] #![deny(rustdoc::broken_intra_doc_links)] #![doc = include_str!("../README.md")] @@ -15,50 +15,66 @@ pub mod tcp; pub mod udp; mod uninitialized_device; -pub use device::{Device, DeviceRefMut, InactiveDevice}; -pub use host::{Dhcp, Host, HostConfig, Manual}; -pub use net::MacAddress; -pub use uninitialized_device::{InitializeError, UninitializedDevice}; +#[doc(inline)] +pub use self::{ + device::{Device, DeviceRefMut, InactiveDevice}, + host::{Dhcp, Host, HostConfig, Manual}, + net::MacAddress, + uninitialized_device::{InitializeError, UninitializedDevice}, +}; // TODO add better docs to all public items, add unit tests. /// Settings for wake on LAN. Allows the W5500 to optionally emit an interrupt upon receiving a packet #[repr(u8)] -#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)] +#[derive(Copy, Clone, Debug, PartialOrd, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum OnWakeOnLan { InvokeInterrupt = 0b00100000, Ignore = 0b00000000, } +/// Ping Block Mode +/// /// Settings for ping. Allows the W5500 to respond to or ignore network ping requests #[repr(u8)] -#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)] +#[derive(Copy, Clone, Debug, PartialOrd, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum OnPingRequest { + /// 0: Disable Ping block Respond = 0b00000000, + /// 1 : Enable Ping block + /// + /// If the bit is ‘1’, it blocks the response to a ping request. Ignore = 0b00010000, } /// Use [ConnectionType::PPoE] when talking /// to an ADSL modem. Otherwise use [ConnectionType::Ethernet] #[repr(u8)] -#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)] +#[derive(Copy, Clone, Debug, PartialOrd, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ConnectionType { PPoE = 0b00001000, Ethernet = 0b00000000, } -#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)] +/// Force ARP +/// +#[derive(Copy, Clone, Debug, PartialOrd, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(u8)] pub enum ArpResponses { + /// 0 : Disable Force ARP mode Cache = 0b00000000, + /// 1 : Enable Force ARP mode + /// + /// In Force ARP mode, It forces on sending ARP Request whenever data is + /// sent. DropAfterUse = 0b00000010, } -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Mode { pub on_wake_on_lan: OnWakeOnLan, @@ -67,6 +83,22 @@ pub struct Mode { pub arp_responses: ArpResponses, } +impl Mode { + pub fn to_register(self) -> [u8; 1] { + [self.to_u8()] + } + + pub fn to_u8(self) -> u8 { + let mut register = 0; + register |= self.on_wake_on_lan as u8; + register |= self.on_ping_request as u8; + register |= self.connection_type as u8; + register |= self.arp_responses as u8; + + register + } +} + impl Default for Mode { fn default() -> Self { Self { @@ -77,3 +109,43 @@ impl Default for Mode { } } } + +#[cfg(test)] +mod test { + use crate::Mode; + + #[test] + fn test_mode_register() { + let ping_respond_and_force_arp = Mode { + // Bit: 7 Reset (RST) should be 0 + // Bit: 6 reserved + // Bit: 5 should be 0 - Disable WOL mode + on_wake_on_lan: crate::OnWakeOnLan::Ignore, + // Bit: 4 should be 0 - Disable Ping Block Mode + on_ping_request: crate::OnPingRequest::Respond, + // Bit: 3 should be 0 - PPoE disabled + connection_type: crate::ConnectionType::Ethernet, + // Bit: 2 reserved + // Bit: 1 should be 0 - Disabled Force ARP + arp_responses: crate::ArpResponses::Cache, + // Bit: 0 reserved + }; + assert_eq!(0b0000_0000, ping_respond_and_force_arp.to_u8()); + + let all_enabled = Mode { + // Bit: 7 Reset (RST) should be 0 + // Bit: 6 reserved + // Bit: 5 should be 1 - Enable WOL mode + on_wake_on_lan: crate::OnWakeOnLan::InvokeInterrupt, + // Bit: 4 should be 0 - Disable Ping Block Mode + on_ping_request: crate::OnPingRequest::Respond, + // Bit: 3 should be 1 - PPoE enable + connection_type: crate::ConnectionType::PPoE, + // Bit: 2 reserved + // Bit: 1 should be 1 - Enable Force ARP + arp_responses: crate::ArpResponses::DropAfterUse, + // Bit: 0 reserved + }; + assert_eq!(0b0010_1010, all_enabled.to_u8()); + } +} diff --git a/src/register.rs b/src/register.rs index d1bfef2..210e4bd 100644 --- a/src/register.rs +++ b/src/register.rs @@ -6,13 +6,84 @@ pub mod common { use bit_field::BitArray; pub const MODE: u16 = 0x0; + + /// Register: GAR (Gateway IP Address Register) [R/W] [0x0001 – 0x0004] [0x00] pub const GATEWAY: u16 = 0x01; + + /// Register: SUBR (Subnet Mask Register) [R/W] [0x0005 – 0x0008] [0x00] pub const SUBNET_MASK: u16 = 0x05; + + /// Register: SHAR (Source Hardware Address Register) [R/W] [0x0009 – 0x000E] [0x00] pub const MAC: u16 = 0x09; + + /// Register: SIPR (Source IP Address Register) [R/W] [0x000F – 0x0012] [0x00] pub const IP: u16 = 0x0F; + + /// Register: INTLEVEL (Interrupt Low Level Timer Register) [R/W] [0x0013 – 0x0014] [0x0000] + pub const INTERRUPT_TIMER: u16 = 0x13; + + /// Register: RTR (Retry Time-value Register) [R/W] [0x0019 – 0x001A] [0x07D0] + pub const RETRY_TIME: u16 = 0x19; + + /// Register: RCR (Retry Count Register) [R/W] [0x001B] [0x08] + pub const RETRY_COUNT: u16 = 0x1B; + pub const PHY_CONFIG: u16 = 0x2E; pub const VERSION: u16 = 0x39; + /// A Retry Time-value + /// + /// RTR (Retry Time-value Register) [R/W] [0x0019 – 0x001A] [0x07D0] + /// + /// From datasheet: + /// + /// RTR configures the retransmission timeout period. The unit of timeout period is + /// 100us and the default of RTR is ‘0x07D0’ or ‘2000’. And so the default timeout period + /// is 200ms(100us X 2000). + /// During the time configured by RTR, W5500 waits for the peer response to the packet + /// that is transmitted by Sn_CR(CONNECT, DISCON, CLOSE, SEND, SEND_MAC, SEND_KEEP + /// command). If the peer does not respond within the RTR time, W5500 retransmits the + /// packet or issues timeout. + /// + /// + /// > Ex) When timeout-period is set as 400ms, RTR = (400ms / 1ms) X 10 = 4000(0x0FA0) + #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct RetryTime(pub(crate) u16); + + impl RetryTime { + #[inline] + pub fn to_u16(&self) -> u16 { + self.0 + } + + #[inline] + pub fn to_register(&self) -> [u8; 2] { + self.0.to_be_bytes() + } + + #[inline] + pub fn from_register(register: [u8; 2]) -> Self { + Self(u16::from_be_bytes(register)) + } + + #[inline] + pub fn from_millis(milliseconds: u16) -> Self { + Self(milliseconds * 10) + } + + #[inline] + pub fn to_millis(&self) -> u16 { + self.0 / 10 + } + } + + impl Default for RetryTime { + fn default() -> Self { + Self::from_millis(200) + } + } + #[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] #[repr(u8)] pub enum PhyOperationMode { diff --git a/src/uninitialized_device.rs b/src/uninitialized_device.rs index ae230a3..1149e3d 100644 --- a/src/uninitialized_device.rs +++ b/src/uninitialized_device.rs @@ -6,8 +6,10 @@ use crate::bus::{Bus, FourWire, ThreeWire}; use crate::device::Device; use crate::host::{Dhcp, Host, Manual}; use crate::raw_device::RawDevice; -use crate::register; -use crate::{MacAddress, Mode}; +use crate::{ + register::{self, common::RetryTime}, + MacAddress, Mode, +}; #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -92,9 +94,7 @@ impl UninitializedDevice { self.assert_chip_version(0x4)?; // RESET - let mode = [0b10000000]; - self.bus - .write_frame(register::COMMON, register::common::MODE, &mode)?; + self.reset()?; self.set_mode(mode_options)?; host.refresh(&mut self.bus)?; @@ -115,31 +115,131 @@ impl UninitializedDevice { RawDevice::new(self.bus) } + /// Reset the device + #[inline] + pub fn reset(&mut self) -> Result<(), SpiBus::Error> { + // Set RST common register of the w5500 + let mode = [0b10000000]; + self.bus + .write_frame(register::COMMON, register::common::MODE, &mode) + } + #[inline] + pub fn set_mode(&mut self, mode_options: Mode) -> Result<(), SpiBus::Error> { + self.bus.write_frame( + register::COMMON, + register::common::MODE, + &mode_options.to_register(), + ) + } + + #[inline] + pub fn version(&mut self) -> Result { + let mut version_register = [0_u8]; + self.bus.read_frame( + register::COMMON, + register::common::VERSION, + &mut version_register, + )?; + + Ok(version_register[0]) + } + + /// RTR (Retry Time-value Register) [R/W] [0x0019 – 0x001A] [0x07D0] + /// + /// # Example + /// + /// ``` + /// use w5500::register::common::RetryTime; + /// + /// let default = RetryTime::from_millis(200); + /// assert_eq!(RetryTime::default(), default); + /// + /// // E.g. 4000 (register) = 400ms + /// let four_hundred_ms = RetryTime::from_millis(400); + /// assert_eq!(four_hundred_ms.to_u16(), 4000); + /// ``` + #[inline] + pub fn set_retry_timeout(&mut self, retry_time_value: RetryTime) -> Result<(), SpiBus::Error> { + self.bus.write_frame( + register::COMMON, + register::common::RETRY_TIME, + &retry_time_value.to_register(), + )?; + + Ok(()) + } + + /// RTR (Retry Time-value Register) [R/W] [0x0019 – 0x001A] [0x07D0] + /// + /// E.g. 4000 = 400ms + #[inline] + pub fn current_retry_timeout(&mut self) -> Result { + let mut retry_time_register: [u8; 2] = [0, 0]; + self.bus.read_frame( + register::COMMON, + register::common::RETRY_TIME, + &mut retry_time_register, + )?; + + Ok(RetryTime::from_register(retry_time_register)) + } + + /// Set a new value for the Retry Count register. + /// + /// RCR (Retry Count Register) [R/W] [0x001B] [0x08] + /// + /// For more details check out the rest of the datasheet documentation on the Retry count. + /// + /// From datasheet: + /// + /// RCR configures the number of time of retransmission. When retransmission occurs + /// as many as ‘RCR+1’, Timeout interrupt is issued (Sn_IR[TIMEOUT] = ‘1’). + /// + /// The timeout of W5500 can be configurable with RTR and RCR. W5500 has two kind + /// timeout such as Address Resolution Protocol (ARP) and TCP retransmission. + /// + /// E.g. In case of errors it will retry for 7 times: + /// `RCR = 0x0007` + pub fn set_retry_count(&mut self, retry_count: u8) -> Result<(), SpiBus::Error> { + self.bus.write_frame( + register::COMMON, + register::common::RETRY_COUNT, + &[retry_count], + )?; + + Ok(()) + } + + /// Get the current Retry Count value + /// RCR (Retry Count Register) [R/W] [0x001B] [0x08] + /// + /// E.g. In case of errors it will retry for 7 times: + /// `RCR = 0x0007` + #[inline] + pub fn current_retry_count(&mut self) -> Result { + let mut retry_count_register: [u8; 1] = [0]; + self.bus.read_frame( + register::COMMON, + register::common::RETRY_COUNT, + &mut retry_count_register, + )?; + + Ok(retry_count_register[0]) + } + #[cfg(not(feature = "no-chip-version-assertion"))] fn assert_chip_version( &mut self, expected_version: u8, ) -> Result<(), InitializeError> { - let mut version = [0]; - self.bus - .read_frame(register::COMMON, register::common::VERSION, &mut version)?; - if version[0] != expected_version { + let version = self.version()?; + + if version != expected_version { Err(InitializeError::ChipNotConnected) } else { Ok(()) } } - - fn set_mode(&mut self, mode_options: Mode) -> Result<(), SpiBus::Error> { - let mut mode = [0]; - mode[0] |= mode_options.on_wake_on_lan as u8; - mode[0] |= mode_options.on_ping_request as u8; - mode[0] |= mode_options.connection_type as u8; - mode[0] |= mode_options.arp_responses as u8; - self.bus - .write_frame(register::COMMON, register::common::MODE, &mode)?; - Ok(()) - } } impl + Write, ChipSelect: OutputPin>