Merge pull request #54 from LechevSpace/feat/retry-time-and-count

Feat: Retry time and count
This commit is contained in:
Ryan Summers 2023-07-25 17:25:04 +02:00 committed by GitHub
commit 42791c27e5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 430 additions and 39 deletions

View file

@ -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

View file

@ -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,
};
#[derive(Debug, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@ -58,11 +61,8 @@ impl<SpiBus: Bus, HostImpl: Host> Device<SpiBus, HostImpl> {
}
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]
@ -95,6 +95,47 @@ impl<SpiBus: Bus, HostImpl: Host> Device<SpiBus, HostImpl> {
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<RetryTime, SpiBus::Error> {
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<u8, SpiBus::Error> {
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 {
@ -187,10 +228,116 @@ impl<SpiBus: Bus, HostImpl: Host> DeviceRefMut<'_, SpiBus, HostImpl> {
Ok(phy[0].into())
}
pub fn version(&mut self) -> Result<u8, SpiBus::Error> {
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<u8, SpiBus::Error> {
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<RetryTime, SpiBus::Error> {
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<u8, SpiBus::Error> {
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])
}
}

View file

@ -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());
}
}

View file

@ -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 {

View file

@ -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))]
@ -95,9 +97,7 @@ impl<SpiBus: Bus> UninitializedDevice<SpiBus> {
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)?;
@ -118,31 +118,131 @@ impl<SpiBus: Bus> UninitializedDevice<SpiBus> {
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<u8, SpiBus::Error> {
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<RetryTime, SpiBus::Error> {
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<u8, SpiBus::Error> {
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<SpiBus::Error>> {
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<Spi: Transfer<u8> + Write<u8>, ChipSelect: OutputPin>