w5100-rs/src/uninitialized_device.rs
Lachezar Lechev 545debf0e3
chore: simplify
Signed-off-by: Lachezar Lechev <elpiel93@gmail.com>
2023-07-13 12:10:46 +03:00

257 lines
7.5 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use embedded_hal::blocking::spi::{Transfer, Write};
use embedded_hal::digital::v2::OutputPin;
use embedded_nal::Ipv4Addr;
use crate::bus::{Bus, FourWire, ThreeWire};
use crate::device::Device;
use crate::host::{Dhcp, Host, Manual};
use crate::raw_device::RawDevice;
use crate::{
register::{self, common::RetryTime},
MacAddress, Mode,
};
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct UninitializedDevice<SpiBus: Bus> {
bus: SpiBus,
}
#[derive(Debug)]
#[repr(u8)]
pub enum InitializeError<SpiError> {
SpiError(SpiError),
ChipNotConnected,
}
impl<SpiError> From<SpiError> for InitializeError<SpiError> {
fn from(error: SpiError) -> InitializeError<SpiError> {
InitializeError::SpiError(error)
}
}
impl<SpiBus: Bus> UninitializedDevice<SpiBus> {
pub fn new(bus: SpiBus) -> UninitializedDevice<SpiBus> {
UninitializedDevice { bus }
}
/// Initialize the device with a MAC address and mode settings.
///
/// Consider using freely available private/locally administered mac
/// addresses that match the following hex pattern:
///
/// ```code
/// x2-xx-xx-xx-xx-xx
/// x6-xx-xx-xx-xx-xx
/// xA-xx-xx-xx-xx-xx
/// xE-xx-xx-xx-xx-xx
/// ```
///
/// "Universally administered and locally administered addresses are
/// distinguished by setting the second-least-significant bit of the first
/// octet of the address"
/// [Wikipedia](https://en.wikipedia.org/wiki/MAC_address#Universal_vs._local)
pub fn initialize(
self,
mac: MacAddress,
mode_options: Mode,
) -> Result<Device<SpiBus, Dhcp>, InitializeError<SpiBus::Error>> {
let host = Dhcp::new(mac);
self.initialize_with_host(host, mode_options)
}
pub fn initialize_manual(
self,
mac: MacAddress,
ip: Ipv4Addr,
mode_options: Mode,
) -> Result<Device<SpiBus, Manual>, InitializeError<SpiBus::Error>> {
let mut ip_bytes = ip.octets();
ip_bytes[3] = 1;
let gateway = Ipv4Addr::from(ip_bytes);
let subnet = Ipv4Addr::new(255, 255, 255, 0);
self.initialize_advanced(mac, ip, gateway, subnet, mode_options)
}
pub fn initialize_advanced(
self,
mac: MacAddress,
ip: Ipv4Addr,
gateway: Ipv4Addr,
subnet: Ipv4Addr,
mode_options: Mode,
) -> Result<Device<SpiBus, Manual>, InitializeError<SpiBus::Error>> {
let host = Manual::new(mac, ip, gateway, subnet);
self.initialize_with_host(host, mode_options)
}
fn initialize_with_host<HostImpl: Host>(
mut self,
mut host: HostImpl,
mode_options: Mode,
) -> Result<Device<SpiBus, HostImpl>, InitializeError<SpiBus::Error>> {
#[cfg(not(feature = "no-chip-version-assertion"))]
self.assert_chip_version(0x4)?;
// RESET
self.reset()?;
self.set_mode(mode_options)?;
host.refresh(&mut self.bus)?;
Ok(Device::new(self.bus, host))
}
pub fn initialize_macraw(
mut self,
mac: MacAddress,
) -> Result<RawDevice<SpiBus>, InitializeError<SpiBus::Error>> {
// Reset the device.
self.bus
.write_frame(register::COMMON, register::common::MODE, &[0x80])?;
self.bus
.write_frame(register::COMMON, register::common::MAC, &mac.octets)?;
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 version = self.version()?;
if version != expected_version {
Err(InitializeError::ChipNotConnected)
} else {
Ok(())
}
}
}
impl<Spi: Transfer<u8> + Write<u8>, ChipSelect: OutputPin>
UninitializedDevice<FourWire<Spi, ChipSelect>>
{
pub fn deactivate(self) -> (Spi, ChipSelect) {
self.bus.release()
}
}
impl<Spi: Transfer<u8> + Write<u8>> UninitializedDevice<ThreeWire<Spi>> {
pub fn deactivate(self) -> Spi {
self.bus.release()
}
}