Merge branch 'master' into fix/udp
This commit is contained in:
commit
84757c552e
8 changed files with 803 additions and 58 deletions
|
|
@ -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))
|
- 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))
|
- 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 `Udp::get_port` and `Udp::set_port` by [@elpiel](https://github.com/elpiel) ([#57](https://github.com/kellerkindt/w5500/pull/57))
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
|
||||||
11
Cargo.toml
11
Cargo.toml
|
|
@ -10,14 +10,17 @@ license = "MIT OR Apache-2.0"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
no-chip-version-assertion = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = { version = "1.3.4", default-features = false }
|
byteorder = { version = "1.3.4", default-features = false }
|
||||||
embedded-hal = "0.2.4"
|
embedded-hal = "0.2"
|
||||||
embedded-nal = "0.6.0"
|
embedded-nal = "0.6.0"
|
||||||
bit_field = "0.10.1"
|
bit_field = "0.10"
|
||||||
derive-try-from-primitive = "1"
|
derive-try-from-primitive = "1"
|
||||||
nb = "1.0.0"
|
nb = "1.0.0"
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
|
|
||||||
[features]
|
[dev-dependencies]
|
||||||
no-chip-version-assertion = []
|
embedded-hal-mock = "0.9"
|
||||||
|
|
@ -29,10 +29,13 @@ impl<Spi: Transfer<u8> + Write<u8>, ChipSelect: OutputPin> FourWire<Spi, ChipSel
|
||||||
impl<Spi: Transfer<u8> + Write<u8>, ChipSelect: OutputPin> Bus for FourWire<Spi, ChipSelect> {
|
impl<Spi: Transfer<u8> + Write<u8>, ChipSelect: OutputPin> Bus for FourWire<Spi, ChipSelect> {
|
||||||
type Error =
|
type Error =
|
||||||
FourWireError<<Spi as Transfer<u8>>::Error, <Spi as Write<u8>>::Error, ChipSelect::Error>;
|
FourWireError<<Spi as Transfer<u8>>::Error, <Spi as Write<u8>>::Error, ChipSelect::Error>;
|
||||||
|
|
||||||
fn read_frame(&mut self, block: u8, address: u16, data: &mut [u8]) -> Result<(), Self::Error> {
|
fn read_frame(&mut self, block: u8, address: u16, data: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
let address_phase = address.to_be_bytes();
|
let address_phase = address.to_be_bytes();
|
||||||
let control_phase = block << 3;
|
let control_phase = block << 3;
|
||||||
let data_phase = data;
|
let data_phase = data;
|
||||||
|
|
||||||
|
// set Chip select to Low, i.e. prepare to receive data
|
||||||
self.cs.set_low().map_err(FourWireError::ChipSelectError)?;
|
self.cs.set_low().map_err(FourWireError::ChipSelectError)?;
|
||||||
let result = (|| {
|
let result = (|| {
|
||||||
self.spi
|
self.spi
|
||||||
|
|
@ -44,29 +47,39 @@ impl<Spi: Transfer<u8> + Write<u8>, ChipSelect: OutputPin> Bus for FourWire<Spi,
|
||||||
.map_err(FourWireError::TransferError)?;
|
.map_err(FourWireError::TransferError)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
// set Chip select to High, i.e. we've finished listening
|
||||||
self.cs.set_high().map_err(FourWireError::ChipSelectError)?;
|
self.cs.set_high().map_err(FourWireError::ChipSelectError)?;
|
||||||
|
|
||||||
|
// then return the result of the transmission
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_frame(&mut self, block: u8, address: u16, data: &[u8]) -> Result<(), Self::Error> {
|
fn write_frame(&mut self, block: u8, address: u16, data: &[u8]) -> Result<(), Self::Error> {
|
||||||
let address_phase = address.to_be_bytes();
|
let address_phase = address.to_be_bytes();
|
||||||
let control_phase = block << 3 | WRITE_MODE_MASK;
|
let control_phase = block << 3 | WRITE_MODE_MASK;
|
||||||
let data_phase = data;
|
let data_phase = data;
|
||||||
|
|
||||||
|
// set Chip select to Low, i.e. prepare to transmit
|
||||||
self.cs.set_low().map_err(FourWireError::ChipSelectError)?;
|
self.cs.set_low().map_err(FourWireError::ChipSelectError)?;
|
||||||
let result = (|| {
|
let result = self
|
||||||
self.spi
|
.spi
|
||||||
.write(&address_phase)
|
.write(&address_phase)
|
||||||
.and_then(|_| self.spi.write(&[control_phase]))
|
.and_then(|_| self.spi.write(&[control_phase]))
|
||||||
.and_then(|_| self.spi.write(data_phase))
|
.and_then(|_| self.spi.write(data_phase))
|
||||||
.map_err(FourWireError::WriteError)?;
|
.map_err(FourWireError::WriteError);
|
||||||
Ok(())
|
|
||||||
})();
|
// set Chip select to High, i.e. we've finished transmitting
|
||||||
self.cs.set_high().map_err(FourWireError::ChipSelectError)?;
|
self.cs.set_high().map_err(FourWireError::ChipSelectError)?;
|
||||||
|
|
||||||
|
// then return the result of the transmission
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must use map_err, ambiguity prevents From from being implemented
|
// Must use map_err, ambiguity prevents From from being implemented
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum FourWireError<TransferError, WriteError, ChipSelectError> {
|
pub enum FourWireError<TransferError, WriteError, ChipSelectError> {
|
||||||
TransferError(TransferError),
|
TransferError(TransferError),
|
||||||
WriteError(WriteError),
|
WriteError(WriteError),
|
||||||
|
|
@ -91,3 +104,90 @@ impl<TransferError, WriteError, ChipSelectError> fmt::Debug
|
||||||
|
|
||||||
// TODO Improved error rendering could be done with specialization.
|
// TODO Improved error rendering could be done with specialization.
|
||||||
// https://github.com/rust-lang/rust/issues/31844
|
// https://github.com/rust-lang/rust/issues/31844
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use embedded_hal::digital::v2::OutputPin;
|
||||||
|
use embedded_hal_mock::{
|
||||||
|
pin::{Mock as PinMock, State as PinState, Transaction as PinTransaction},
|
||||||
|
spi::{Mock as SpiMock, Transaction as SpiTransaction},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
bus::{four_wire::WRITE_MODE_MASK, Bus},
|
||||||
|
register,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::FourWire;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_read_frame() {
|
||||||
|
let mut cs_pin = PinMock::new(&[
|
||||||
|
// we begin with pin HIGH
|
||||||
|
PinTransaction::set(PinState::High),
|
||||||
|
// When reading
|
||||||
|
PinTransaction::set(PinState::Low),
|
||||||
|
// When finished reading
|
||||||
|
PinTransaction::set(PinState::High),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// initiate the pin to high.
|
||||||
|
cs_pin.set_high().expect("Should set pin to high");
|
||||||
|
|
||||||
|
let mut actual_version = [0_u8; 1];
|
||||||
|
let mut expected_version = 5;
|
||||||
|
|
||||||
|
let expectations = [
|
||||||
|
SpiTransaction::write(register::common::VERSION.to_be_bytes().to_vec()),
|
||||||
|
SpiTransaction::write(vec![register::COMMON << 3]),
|
||||||
|
SpiTransaction::transfer(actual_version.to_vec(), vec![expected_version]),
|
||||||
|
];
|
||||||
|
|
||||||
|
let mock_spi = SpiMock::new(&expectations);
|
||||||
|
|
||||||
|
let mut four_wire = FourWire::new(mock_spi, cs_pin);
|
||||||
|
|
||||||
|
four_wire.read_frame(
|
||||||
|
register::COMMON,
|
||||||
|
register::common::VERSION,
|
||||||
|
&mut actual_version,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(expected_version, actual_version[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_write_frame() {
|
||||||
|
let mut cs_pin = PinMock::new(&[
|
||||||
|
// we begin with pin HIGH
|
||||||
|
PinTransaction::set(PinState::High),
|
||||||
|
// When reading
|
||||||
|
PinTransaction::set(PinState::Low),
|
||||||
|
// When finished reading
|
||||||
|
PinTransaction::set(PinState::High),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// initiate the pin to high.
|
||||||
|
cs_pin.set_high().expect("Should set pin to high");
|
||||||
|
|
||||||
|
let socket_0_reg = 0x01_u8;
|
||||||
|
let socket_1_reg = 0x05_u8;
|
||||||
|
let source_port = 49849_u16;
|
||||||
|
|
||||||
|
let expectations = [
|
||||||
|
SpiTransaction::write(register::socketn::SOURCE_PORT.to_be_bytes().to_vec()),
|
||||||
|
SpiTransaction::write(vec![socket_1_reg << 3 | WRITE_MODE_MASK]),
|
||||||
|
SpiTransaction::write(source_port.to_be_bytes().to_vec()),
|
||||||
|
];
|
||||||
|
|
||||||
|
let mock_spi = SpiMock::new(&expectations);
|
||||||
|
|
||||||
|
let mut four_wire = FourWire::new(mock_spi, cs_pin);
|
||||||
|
|
||||||
|
four_wire.write_frame(
|
||||||
|
socket_1_reg,
|
||||||
|
register::socketn::SOURCE_PORT,
|
||||||
|
&source_port.to_be_bytes(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
175
src/device.rs
175
src/device.rs
|
|
@ -6,8 +6,13 @@ use crate::host::Host;
|
||||||
use crate::net::Ipv4Addr;
|
use crate::net::Ipv4Addr;
|
||||||
use crate::socket::Socket;
|
use crate::socket::Socket;
|
||||||
use crate::uninitialized_device::UninitializedDevice;
|
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))]
|
||||||
pub enum ResetError<E> {
|
pub enum ResetError<E> {
|
||||||
SocketsNotReleased,
|
SocketsNotReleased,
|
||||||
Other(E),
|
Other(E),
|
||||||
|
|
@ -21,7 +26,7 @@ impl<E> From<E> for ResetError<E> {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub(crate) struct DeviceState<HostImpl: Host> {
|
pub struct DeviceState<HostImpl: Host> {
|
||||||
host: HostImpl,
|
host: HostImpl,
|
||||||
sockets: [u8; 1],
|
sockets: [u8; 1],
|
||||||
}
|
}
|
||||||
|
|
@ -42,6 +47,10 @@ impl<SpiBus: Bus, HostImpl: Host> Device<SpiBus, HostImpl> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_state(&self) -> &DeviceState<HostImpl> {
|
||||||
|
&self.state
|
||||||
|
}
|
||||||
|
|
||||||
pub fn reset(mut self) -> Result<UninitializedDevice<SpiBus>, ResetError<SpiBus::Error>> {
|
pub fn reset(mut self) -> Result<UninitializedDevice<SpiBus>, ResetError<SpiBus::Error>> {
|
||||||
if self.state.sockets != [0b11111111] {
|
if self.state.sockets != [0b11111111] {
|
||||||
Err(ResetError::SocketsNotReleased)
|
Err(ResetError::SocketsNotReleased)
|
||||||
|
|
@ -52,11 +61,8 @@ impl<SpiBus: Bus, HostImpl: Host> Device<SpiBus, HostImpl> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear_mode(&mut self) -> Result<(), SpiBus::Error> {
|
fn clear_mode(&mut self) -> Result<(), SpiBus::Error> {
|
||||||
// reset bit
|
// Set RST common register of the w5500
|
||||||
let mode = [0b10000000];
|
self.as_mut().reset_device()
|
||||||
self.bus
|
|
||||||
.write_frame(register::COMMON, register::common::MODE, &mode)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -89,6 +95,47 @@ impl<SpiBus: Bus, HostImpl: Host> Device<SpiBus, HostImpl> {
|
||||||
self.as_mut().version()
|
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]
|
#[inline]
|
||||||
pub(crate) fn as_mut(&mut self) -> DeviceRefMut<'_, BusRef<'_, SpiBus>, HostImpl> {
|
pub(crate) fn as_mut(&mut self) -> DeviceRefMut<'_, BusRef<'_, SpiBus>, HostImpl> {
|
||||||
DeviceRefMut {
|
DeviceRefMut {
|
||||||
|
|
@ -181,10 +228,116 @@ impl<SpiBus: Bus, HostImpl: Host> DeviceRefMut<'_, SpiBus, HostImpl> {
|
||||||
Ok(phy[0].into())
|
Ok(phy[0].into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn version(&mut self) -> Result<u8, SpiBus::Error> {
|
#[inline]
|
||||||
let mut version = [0u8];
|
pub fn reset_device(&mut self) -> Result<(), SpiBus::Error> {
|
||||||
|
// Set RST common register of the w5500
|
||||||
|
let mode = [0b10000000];
|
||||||
self.bus
|
self.bus
|
||||||
.read_frame(register::COMMON, register::common::VERSION, &mut version)?;
|
.write_frame(register::COMMON, register::common::MODE, &mode)
|
||||||
Ok(version[0])
|
}
|
||||||
|
|
||||||
|
#[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])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
92
src/lib.rs
92
src/lib.rs
|
|
@ -1,4 +1,4 @@
|
||||||
#![no_std]
|
#![cfg_attr(not(test), no_std)]
|
||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
#![deny(rustdoc::broken_intra_doc_links)]
|
#![deny(rustdoc::broken_intra_doc_links)]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
|
|
@ -15,50 +15,66 @@ pub mod tcp;
|
||||||
pub mod udp;
|
pub mod udp;
|
||||||
mod uninitialized_device;
|
mod uninitialized_device;
|
||||||
|
|
||||||
pub use device::{Device, DeviceRefMut, InactiveDevice};
|
#[doc(inline)]
|
||||||
pub use host::{Dhcp, Host, HostConfig, Manual};
|
pub use self::{
|
||||||
pub use net::MacAddress;
|
device::{Device, DeviceRefMut, InactiveDevice},
|
||||||
pub use uninitialized_device::{InitializeError, UninitializedDevice};
|
host::{Dhcp, Host, HostConfig, Manual},
|
||||||
|
net::MacAddress,
|
||||||
|
uninitialized_device::{InitializeError, UninitializedDevice},
|
||||||
|
};
|
||||||
|
|
||||||
// TODO add better docs to all public items, add unit tests.
|
// 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
|
/// Settings for wake on LAN. Allows the W5500 to optionally emit an interrupt upon receiving a packet
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum OnWakeOnLan {
|
pub enum OnWakeOnLan {
|
||||||
InvokeInterrupt = 0b00100000,
|
InvokeInterrupt = 0b00100000,
|
||||||
Ignore = 0b00000000,
|
Ignore = 0b00000000,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ping Block Mode
|
||||||
|
///
|
||||||
/// Settings for ping. Allows the W5500 to respond to or ignore network ping requests
|
/// Settings for ping. Allows the W5500 to respond to or ignore network ping requests
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum OnPingRequest {
|
pub enum OnPingRequest {
|
||||||
|
/// 0: Disable Ping block
|
||||||
Respond = 0b00000000,
|
Respond = 0b00000000,
|
||||||
|
/// 1 : Enable Ping block
|
||||||
|
///
|
||||||
|
/// If the bit is ‘1’, it blocks the response to a ping request.
|
||||||
Ignore = 0b00010000,
|
Ignore = 0b00010000,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Use [ConnectionType::PPoE] when talking
|
/// Use [ConnectionType::PPoE] when talking
|
||||||
/// to an ADSL modem. Otherwise use [ConnectionType::Ethernet]
|
/// to an ADSL modem. Otherwise use [ConnectionType::Ethernet]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum ConnectionType {
|
pub enum ConnectionType {
|
||||||
PPoE = 0b00001000,
|
PPoE = 0b00001000,
|
||||||
Ethernet = 0b00000000,
|
Ethernet = 0b00000000,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
|
/// Force ARP
|
||||||
|
///
|
||||||
|
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum ArpResponses {
|
pub enum ArpResponses {
|
||||||
|
/// 0 : Disable Force ARP mode
|
||||||
Cache = 0b00000000,
|
Cache = 0b00000000,
|
||||||
|
/// 1 : Enable Force ARP mode
|
||||||
|
///
|
||||||
|
/// In Force ARP mode, It forces on sending ARP Request whenever data is
|
||||||
|
/// sent.
|
||||||
DropAfterUse = 0b00000010,
|
DropAfterUse = 0b00000010,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub struct Mode {
|
pub struct Mode {
|
||||||
pub on_wake_on_lan: OnWakeOnLan,
|
pub on_wake_on_lan: OnWakeOnLan,
|
||||||
|
|
@ -67,6 +83,22 @@ pub struct Mode {
|
||||||
pub arp_responses: ArpResponses,
|
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 {
|
impl Default for Mode {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
232
src/register.rs
232
src/register.rs
|
|
@ -6,13 +6,84 @@ pub mod common {
|
||||||
use bit_field::BitArray;
|
use bit_field::BitArray;
|
||||||
|
|
||||||
pub const MODE: u16 = 0x0;
|
pub const MODE: u16 = 0x0;
|
||||||
|
|
||||||
|
/// Register: GAR (Gateway IP Address Register) [R/W] [0x0001 – 0x0004] [0x00]
|
||||||
pub const GATEWAY: u16 = 0x01;
|
pub const GATEWAY: u16 = 0x01;
|
||||||
|
|
||||||
|
/// Register: SUBR (Subnet Mask Register) [R/W] [0x0005 – 0x0008] [0x00]
|
||||||
pub const SUBNET_MASK: u16 = 0x05;
|
pub const SUBNET_MASK: u16 = 0x05;
|
||||||
|
|
||||||
|
/// Register: SHAR (Source Hardware Address Register) [R/W] [0x0009 – 0x000E] [0x00]
|
||||||
pub const MAC: u16 = 0x09;
|
pub const MAC: u16 = 0x09;
|
||||||
|
|
||||||
|
/// Register: SIPR (Source IP Address Register) [R/W] [0x000F – 0x0012] [0x00]
|
||||||
pub const IP: u16 = 0x0F;
|
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 PHY_CONFIG: u16 = 0x2E;
|
||||||
pub const VERSION: u16 = 0x39;
|
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)]
|
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum PhyOperationMode {
|
pub enum PhyOperationMode {
|
||||||
|
|
@ -156,7 +227,10 @@ pub const SOCKET7_BUFFER_RX: u8 = 0b000_11111;
|
||||||
pub mod socketn {
|
pub mod socketn {
|
||||||
use derive_try_from_primitive::TryFromPrimitive;
|
use derive_try_from_primitive::TryFromPrimitive;
|
||||||
|
|
||||||
|
/// The Protocol mode
|
||||||
pub const MODE: u16 = 0x00;
|
pub const MODE: u16 = 0x00;
|
||||||
|
|
||||||
|
/// The protocol modes that can be used with the `w5500`
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum Protocol {
|
pub enum Protocol {
|
||||||
Closed = 0b00,
|
Closed = 0b00,
|
||||||
|
|
@ -164,33 +238,100 @@ pub mod socketn {
|
||||||
Udp = 0b10,
|
Udp = 0b10,
|
||||||
MacRaw = 0b100,
|
MacRaw = 0b100,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Socket n Command Register
|
||||||
|
///
|
||||||
|
/// `Sn_CR`
|
||||||
pub const COMMAND: u16 = 0x01;
|
pub const COMMAND: u16 = 0x01;
|
||||||
|
|
||||||
|
/// Socket n Commands
|
||||||
|
///
|
||||||
|
/// `Sn_CR` register
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum Command {
|
pub enum Command {
|
||||||
Open = 0x01,
|
Open = 0x01,
|
||||||
|
/// [Datasheet page 46](https://docs.wiznet.io/img/products/w5500/W5500_ds_v110e.pdf):
|
||||||
|
///
|
||||||
|
/// > This is valid only in TCP mode (Sn_MR(P3:P0) = Sn_MR_TCP). In this
|
||||||
|
/// > mode, Socket n operates as a ‘TCP server’ and waits for connection-
|
||||||
|
/// > request (SYN packet) from any ‘TCP client
|
||||||
Listen = 0x02,
|
Listen = 0x02,
|
||||||
Connect = 0x04,
|
Connect = 0x04,
|
||||||
Discon = 0x08,
|
Discon = 0x08,
|
||||||
Close = 0x10,
|
Close = 0x10,
|
||||||
Send = 0x20,
|
Send = 0x20,
|
||||||
|
|
||||||
|
/// [Datasheet page 48](https://docs.wiznet.io/img/products/w5500/W5500_ds_v110e.pdf):
|
||||||
|
///
|
||||||
|
/// > RECV completes the processing of the received data in Socket n RX
|
||||||
|
/// > Buffer by using a RX read pointer register (Sn_RX_RD).
|
||||||
|
/// > For more details, refer to Socket n RX Received Size Register
|
||||||
|
/// > (Sn_RX_RSR), Socket n RX Write Pointer Register (Sn_RX_WR), and
|
||||||
|
/// > Socket n RX Read Pointer Register (Sn_RX_RD).
|
||||||
Receive = 0x40,
|
Receive = 0x40,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const INTERRUPT: u16 = 0x02;
|
pub const INTERRUPT: u16 = 0x02;
|
||||||
|
/// | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
||||||
|
/// | Reserved | Reserved | Reserved | SEND_OK | TIMEOUT | RECV | DISCON | CON |
|
||||||
|
///
|
||||||
|
/// | Bit | Symbol | Description |
|
||||||
|
/// | 7~5 | Reserved | Reserved |
|
||||||
|
/// | 4 | SENDOK | Sn_IR(SENDOK) | Interrupt Mask |
|
||||||
|
/// | 3 | TIMEOUT | Sn_IR(TIMEOUT) | Interrupt Mask |
|
||||||
|
/// | 2 | RECV | Sn_IR(RECV) | Interrupt Mask |
|
||||||
|
/// | 1 | DISCON | Sn_IR(DISCON) | Interrupt Mask |
|
||||||
|
/// | 0 | CON | Sn_IR(CON) | Interrupt Mask |
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum Interrupt {
|
pub enum Interrupt {
|
||||||
All = 0b11111111u8,
|
All = 0b11111111u8,
|
||||||
SendOk = 0b010000u8,
|
SendOk = 0b10000u8,
|
||||||
Timeout = 0b01000u8,
|
Timeout = 0b1000u8,
|
||||||
Receive = 0b00100u8,
|
|
||||||
|
/// Receive data
|
||||||
|
///
|
||||||
|
/// bit 2, symbol `RECV`, `Sn_IR(RECV) Interrupt Mask`
|
||||||
|
Receive = 0b100u8,
|
||||||
|
|
||||||
|
/// Disconnect
|
||||||
|
///
|
||||||
|
/// bit 1, symbol `DISCON`, `Sn_IR(DISCON) Interrupt Mask`
|
||||||
|
Disconnect = 0b10u8,
|
||||||
|
|
||||||
|
/// Connect
|
||||||
|
///
|
||||||
|
/// bit 0, symbol `CON`, `Sn_IR(CON) Interrupt Mask`
|
||||||
|
Connect = 0b1u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const STATUS: u16 = 0x03;
|
pub const STATUS: u16 = 0x03;
|
||||||
|
|
||||||
|
/// Socket status register
|
||||||
|
///
|
||||||
|
/// `W5500 Datasheet Version 1.1.0` page 49:
|
||||||
|
///
|
||||||
|
/// > Sn_SR (Socket n Status Register) [R] [0x0003] [0x00]
|
||||||
|
///
|
||||||
|
/// - 0x18 SOCK_FIN_WAIT
|
||||||
|
/// - 0x1A SOCK_CLOSING
|
||||||
|
/// - 0X1B SOCK_TIME_WAIT
|
||||||
|
/// > These indicate Socket n is closing.
|
||||||
|
/// > These are shown in disconnect-process such as active-close
|
||||||
|
/// > and passive-close.
|
||||||
|
/// > When Disconnect-process is successfully completed, or
|
||||||
|
/// > when timeout occurs, these change to SOCK_CLOSED.
|
||||||
|
///
|
||||||
|
#[derive(TryFromPrimitive, Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[derive(TryFromPrimitive)]
|
|
||||||
pub enum Status {
|
pub enum Status {
|
||||||
Closed = 0x00,
|
Closed = 0x00,
|
||||||
Init = 0x13,
|
Init = 0x13,
|
||||||
|
|
||||||
|
/// [Datasheet page 49](https://docs.wiznet.io/img/products/w5500/W5500_ds_v110e.pdf):
|
||||||
|
///
|
||||||
|
/// > This indicates Socket n is operating as ‘TCP server’ mode and
|
||||||
|
/// > waiting for connection-request (SYN packet) from a peer
|
||||||
|
/// > (‘TCP client’).
|
||||||
Listen = 0x14,
|
Listen = 0x14,
|
||||||
Established = 0x17,
|
Established = 0x17,
|
||||||
CloseWait = 0x1c,
|
CloseWait = 0x1c,
|
||||||
|
|
@ -206,6 +347,19 @@ pub mod socketn {
|
||||||
LastAck = 0x1d,
|
LastAck = 0x1d,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
impl defmt::Format for Status {
|
||||||
|
fn format(&self, fmt: defmt::Formatter) {
|
||||||
|
// Format as hexadecimal.
|
||||||
|
defmt::write!(
|
||||||
|
fmt,
|
||||||
|
"Status::{} ({=u8:#x})",
|
||||||
|
defmt::Debug2Format(self),
|
||||||
|
*self as u8
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub const SOURCE_PORT: u16 = 0x04;
|
pub const SOURCE_PORT: u16 = 0x04;
|
||||||
|
|
||||||
pub const DESTINATION_IP: u16 = 0x0C;
|
pub const DESTINATION_IP: u16 = 0x0C;
|
||||||
|
|
@ -214,17 +368,87 @@ pub mod socketn {
|
||||||
|
|
||||||
pub const RXBUF_SIZE: u16 = 0x1E;
|
pub const RXBUF_SIZE: u16 = 0x1E;
|
||||||
|
|
||||||
|
/// Socket n TX Buffer Size Register
|
||||||
|
///
|
||||||
|
/// `Sn_TXBUF_SIZE`
|
||||||
|
///
|
||||||
|
/// From datasheet:
|
||||||
|
///
|
||||||
|
/// > .. can be configured with 1,2,4,8, and 16 Kbytes.
|
||||||
|
/// >
|
||||||
|
/// > Although Socket n TX Buffer Block size is initially configured to 2Kbytes, user can
|
||||||
|
/// > be re-configure its size using Sn_TXBUF_SIZE. The total sum of Sn_TXBUF_SIZE
|
||||||
|
/// > cannot be exceed 16Kbytes. When exceeded, the data transmission error is
|
||||||
|
/// > occurred.
|
||||||
pub const TXBUF_SIZE: u16 = 0x1F;
|
pub const TXBUF_SIZE: u16 = 0x1F;
|
||||||
|
|
||||||
|
/// TX Free Size Register
|
||||||
|
///
|
||||||
|
/// `Sn_TX_FSR`
|
||||||
|
///
|
||||||
|
/// Socket n TX Free Size
|
||||||
|
///
|
||||||
|
/// offset (register)
|
||||||
|
/// 0x0020 (Sn_TX_FSR0)
|
||||||
|
/// 0x0021 (Sn_TX_FSR1)
|
||||||
pub const TX_FREE_SIZE: u16 = 0x20;
|
pub const TX_FREE_SIZE: u16 = 0x20;
|
||||||
|
|
||||||
|
/// Socket n TX Read Pointer
|
||||||
|
///
|
||||||
|
/// offset (register)
|
||||||
|
/// 0x0022 (Sn_TX_RD0)
|
||||||
|
/// 0x0023 (Sn_TX_RD1)
|
||||||
pub const TX_DATA_READ_POINTER: u16 = 0x22;
|
pub const TX_DATA_READ_POINTER: u16 = 0x22;
|
||||||
|
|
||||||
|
/// Socket n TX Write Pointer
|
||||||
|
///
|
||||||
|
/// offset (register)
|
||||||
|
/// 0x0024 (Sn_TX_WR0)
|
||||||
|
/// 0x0025 (Sn_TX_WR1)
|
||||||
|
///
|
||||||
|
/// [Datasheet page 54](https://docs.wiznet.io/img/products/w5500/W5500_ds_v110e.pdf):
|
||||||
|
///
|
||||||
|
/// > Sn_TX_WR (Socket n TX Write Pointer Register) [R/W] [0x0024-0x0025] [0x0000]
|
||||||
|
/// >
|
||||||
|
/// > Sn_TX_WR is initialized by OPEN command. However, if Sn_MR(P[3:0]) is TCP
|
||||||
|
/// > mode(‘0001’), it is re-initialized while connecting with TCP.
|
||||||
|
/// > It should be read or to be updated like as follows.
|
||||||
|
/// > 1. Read the starting address for saving the transmitting data.
|
||||||
|
/// > 2. Save the transmitting data from the starting address of Socket n TX
|
||||||
|
/// > buffer.
|
||||||
|
/// > 3. After saving the transmitting data, update Sn_TX_WR to the
|
||||||
|
/// > increased value as many as transmitting data size. If the increment value
|
||||||
|
/// > exceeds the maximum value 0xFFFF(greater than 0x10000 and the carry
|
||||||
|
/// > bit occurs), then the carry bit is ignored and will automatically update
|
||||||
|
/// > with the lower 16bits value.
|
||||||
|
/// > 4. Transmit the saved data in Socket n TX Buffer by using SEND/SEND
|
||||||
|
/// command
|
||||||
pub const TX_DATA_WRITE_POINTER: u16 = 0x24;
|
pub const TX_DATA_WRITE_POINTER: u16 = 0x24;
|
||||||
|
|
||||||
|
/// Socket n Received Size Register
|
||||||
|
///
|
||||||
|
/// `Sn_RX_RSR`
|
||||||
pub const RECEIVED_SIZE: u16 = 0x26;
|
pub const RECEIVED_SIZE: u16 = 0x26;
|
||||||
|
|
||||||
pub const RX_DATA_READ_POINTER: u16 = 0x28;
|
pub const RX_DATA_READ_POINTER: u16 = 0x28;
|
||||||
|
|
||||||
|
/// Socket n Interrupt Mask
|
||||||
|
///
|
||||||
|
/// offset (register)
|
||||||
|
/// 0x002C (Sn_IMR)
|
||||||
pub const INTERRUPT_MASK: u16 = 0x2C;
|
pub const INTERRUPT_MASK: u16 = 0x2C;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use core::convert::TryFrom;
|
||||||
|
|
||||||
|
use super::Status;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_status_from_byte() {
|
||||||
|
let udp = 0x22_u8;
|
||||||
|
let status = Status::try_from(udp).expect("Should parse to Status");
|
||||||
|
assert_eq!(status, Status::Udp);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ pub struct Socket {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Socket {
|
impl Socket {
|
||||||
|
/// 8 sockets available on the w5500
|
||||||
pub fn new(index: u8) -> Self {
|
pub fn new(index: u8) -> Self {
|
||||||
/*
|
/*
|
||||||
* Socket 0 is at address 0x01
|
* Socket 0 is at address 0x01
|
||||||
|
|
@ -75,6 +76,7 @@ impl Socket {
|
||||||
) -> Result<bool, SpiBus::Error> {
|
) -> Result<bool, SpiBus::Error> {
|
||||||
let mut data = [0u8];
|
let mut data = [0u8];
|
||||||
bus.read_frame(self.register(), socketn::INTERRUPT, &mut data)?;
|
bus.read_frame(self.register(), socketn::INTERRUPT, &mut data)?;
|
||||||
|
|
||||||
Ok(data[0] & code as u8 != 0)
|
Ok(data[0] & code as u8 != 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -179,9 +181,11 @@ impl Socket {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the received bytes size of the socket's RX buffer.
|
||||||
|
///
|
||||||
|
/// Section 4.2 of datasheet, Sn_TX_FSR address docs indicate that read must be repeated until two sequential reads are stable
|
||||||
pub fn get_receive_size<SpiBus: Bus>(&self, bus: &mut SpiBus) -> Result<u16, SpiBus::Error> {
|
pub fn get_receive_size<SpiBus: Bus>(&self, bus: &mut SpiBus) -> Result<u16, SpiBus::Error> {
|
||||||
loop {
|
loop {
|
||||||
// Section 4.2 of datasheet, Sn_TX_FSR address docs indicate that read must be repeated until two sequential reads are stable
|
|
||||||
let mut sample_0 = [0u8; 2];
|
let mut sample_0 = [0u8; 2];
|
||||||
bus.read_frame(self.register(), socketn::RECEIVED_SIZE, &mut sample_0)?;
|
bus.read_frame(self.register(), socketn::RECEIVED_SIZE, &mut sample_0)?;
|
||||||
let mut sample_1 = [0u8; 2];
|
let mut sample_1 = [0u8; 2];
|
||||||
|
|
@ -192,9 +196,94 @@ impl Socket {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the free TX buffer size still available for this socket.
|
||||||
|
///
|
||||||
|
/// It's cleared once we `SEND` the buffer over the socket.
|
||||||
pub fn get_tx_free_size<SpiBus: Bus>(&self, bus: &mut SpiBus) -> Result<u16, SpiBus::Error> {
|
pub fn get_tx_free_size<SpiBus: Bus>(&self, bus: &mut SpiBus) -> Result<u16, SpiBus::Error> {
|
||||||
let mut data = [0; 2];
|
let mut data = [0; 2];
|
||||||
bus.read_frame(self.register(), socketn::TX_FREE_SIZE, &mut data)?;
|
bus.read_frame(self.register(), socketn::TX_FREE_SIZE, &mut data)?;
|
||||||
Ok(u16::from_be_bytes(data))
|
Ok(u16::from_be_bytes(data))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::register::*;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_socket_registers() {
|
||||||
|
// Socket 0
|
||||||
|
{
|
||||||
|
let socket_0 = Socket::new(0);
|
||||||
|
|
||||||
|
assert_eq!(socket_0.register, SOCKET0);
|
||||||
|
assert_eq!(socket_0.tx_buffer, SOCKET0_BUFFER_TX);
|
||||||
|
assert_eq!(socket_0.rx_buffer, SOCKET0_BUFFER_RX);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Socket 1
|
||||||
|
{
|
||||||
|
let socket_1 = Socket::new(1);
|
||||||
|
|
||||||
|
assert_eq!(socket_1.register, SOCKET1);
|
||||||
|
assert_eq!(socket_1.tx_buffer, SOCKET1_BUFFER_TX);
|
||||||
|
assert_eq!(socket_1.rx_buffer, SOCKET1_BUFFER_RX);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Socket 2
|
||||||
|
{
|
||||||
|
let socket_2 = Socket::new(2);
|
||||||
|
|
||||||
|
assert_eq!(socket_2.register, SOCKET2);
|
||||||
|
assert_eq!(socket_2.tx_buffer, SOCKET2_BUFFER_TX);
|
||||||
|
assert_eq!(socket_2.rx_buffer, SOCKET2_BUFFER_RX);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Socket 3
|
||||||
|
{
|
||||||
|
let socket_3 = Socket::new(3);
|
||||||
|
|
||||||
|
assert_eq!(socket_3.register, SOCKET3);
|
||||||
|
assert_eq!(socket_3.tx_buffer, SOCKET3_BUFFER_TX);
|
||||||
|
assert_eq!(socket_3.rx_buffer, SOCKET3_BUFFER_RX);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Socket 4
|
||||||
|
{
|
||||||
|
let socket_4 = Socket::new(4);
|
||||||
|
|
||||||
|
assert_eq!(socket_4.register, SOCKET4);
|
||||||
|
assert_eq!(socket_4.tx_buffer, SOCKET4_BUFFER_TX);
|
||||||
|
assert_eq!(socket_4.rx_buffer, SOCKET4_BUFFER_RX);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Socket 5
|
||||||
|
{
|
||||||
|
let socket_5 = Socket::new(5);
|
||||||
|
|
||||||
|
assert_eq!(socket_5.register, SOCKET5);
|
||||||
|
assert_eq!(socket_5.tx_buffer, SOCKET5_BUFFER_TX);
|
||||||
|
assert_eq!(socket_5.rx_buffer, SOCKET5_BUFFER_RX);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Socket 6
|
||||||
|
{
|
||||||
|
let socket_6 = Socket::new(6);
|
||||||
|
|
||||||
|
assert_eq!(socket_6.register, SOCKET6);
|
||||||
|
assert_eq!(socket_6.tx_buffer, SOCKET6_BUFFER_TX);
|
||||||
|
assert_eq!(socket_6.rx_buffer, SOCKET6_BUFFER_RX);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Socket 7
|
||||||
|
{
|
||||||
|
let socket_7 = Socket::new(7);
|
||||||
|
|
||||||
|
assert_eq!(socket_7.register, SOCKET7);
|
||||||
|
assert_eq!(socket_7.tx_buffer, SOCKET7_BUFFER_TX);
|
||||||
|
assert_eq!(socket_7.rx_buffer, SOCKET7_BUFFER_RX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,10 @@ use crate::bus::{Bus, FourWire, ThreeWire};
|
||||||
use crate::device::Device;
|
use crate::device::Device;
|
||||||
use crate::host::{Dhcp, Host, Manual};
|
use crate::host::{Dhcp, Host, Manual};
|
||||||
use crate::raw_device::RawDevice;
|
use crate::raw_device::RawDevice;
|
||||||
use crate::register;
|
use crate::{
|
||||||
use crate::{MacAddress, Mode};
|
register::{self, common::RetryTime},
|
||||||
|
MacAddress, Mode,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
|
@ -58,6 +60,9 @@ impl<SpiBus: Bus> UninitializedDevice<SpiBus> {
|
||||||
self.initialize_with_host(host, mode_options)
|
self.initialize_with_host(host, mode_options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The gateway overrides the passed `ip` ([`Ip4Addr`]) to end with `.1`.
|
||||||
|
///
|
||||||
|
/// E.g. `let ip = "192.168.0.201".parse::<Ip4Addr>()` will become a device with a gateway `192.168.0.1`.
|
||||||
pub fn initialize_manual(
|
pub fn initialize_manual(
|
||||||
self,
|
self,
|
||||||
mac: MacAddress,
|
mac: MacAddress,
|
||||||
|
|
@ -92,9 +97,7 @@ impl<SpiBus: Bus> UninitializedDevice<SpiBus> {
|
||||||
self.assert_chip_version(0x4)?;
|
self.assert_chip_version(0x4)?;
|
||||||
|
|
||||||
// RESET
|
// RESET
|
||||||
let mode = [0b10000000];
|
self.reset()?;
|
||||||
self.bus
|
|
||||||
.write_frame(register::COMMON, register::common::MODE, &mode)?;
|
|
||||||
|
|
||||||
self.set_mode(mode_options)?;
|
self.set_mode(mode_options)?;
|
||||||
host.refresh(&mut self.bus)?;
|
host.refresh(&mut self.bus)?;
|
||||||
|
|
@ -115,31 +118,131 @@ impl<SpiBus: Bus> UninitializedDevice<SpiBus> {
|
||||||
RawDevice::new(self.bus)
|
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"))]
|
#[cfg(not(feature = "no-chip-version-assertion"))]
|
||||||
fn assert_chip_version(
|
fn assert_chip_version(
|
||||||
&mut self,
|
&mut self,
|
||||||
expected_version: u8,
|
expected_version: u8,
|
||||||
) -> Result<(), InitializeError<SpiBus::Error>> {
|
) -> Result<(), InitializeError<SpiBus::Error>> {
|
||||||
let mut version = [0];
|
let version = self.version()?;
|
||||||
self.bus
|
|
||||||
.read_frame(register::COMMON, register::common::VERSION, &mut version)?;
|
if version != expected_version {
|
||||||
if version[0] != expected_version {
|
|
||||||
Err(InitializeError::ChipNotConnected)
|
Err(InitializeError::ChipNotConnected)
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
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>
|
impl<Spi: Transfer<u8> + Write<u8>, ChipSelect: OutputPin>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue