Merge pull request #12 from jonahbron/improve-docs
Improved documentation in README and doc comments in library
This commit is contained in:
commit
517da55f3c
2 changed files with 119 additions and 35 deletions
91
README.md
91
README.md
|
|
@ -1,47 +1,82 @@
|
||||||
# Example usage
|
# W5500 Driver
|
||||||
|
|
||||||
Below some really basic usage how I am ca using it:
|
This crate is a driver for the WIZnet W5500 chips. The W5500 chip is a hardwired TCP/IP embedded Ethernet controller
|
||||||
|
that enables easier internet connection for embedded systems using SPI (Serial Peripheral Interface). It is one of the
|
||||||
|
more popular platforms for Ethernet modules on Arduino platforms.
|
||||||
|
|
||||||
|
## Embedded-HAL
|
||||||
|
|
||||||
|
Embedded-HAL is a standard set of traits meant to permit communication between MCU implementations and hardware drivers
|
||||||
|
like this one. Any microcontroller that implements the
|
||||||
|
[`spi::FullDuplex<u8>`](https://docs.rs/embedded-hal/0.2.3/embedded_hal/spi/trait.FullDuplex.html) interface can use
|
||||||
|
this driver.
|
||||||
|
|
||||||
|
## Implementation
|
||||||
|
|
||||||
|
This driver is built in several layers of structs.
|
||||||
|
|
||||||
|
The lowest level (and the first a program would instantiate) is the `W5500` struct. It contains a reference to the
|
||||||
|
chip-select [pin](https://docs.rs/embedded-hal/0.2.3/embedded_hal/digital/v2/trait.OutputPin.html).
|
||||||
|
|
||||||
|
The next layer is the `ActiveW5500` struct. It contains a reference to a `W5500` instance, and an implementation of
|
||||||
|
the [`spi::FullDuplex<u8>`](https://docs.rs/embedded-hal/0.2.3/embedded_hal/spi/trait.FullDuplex.html) trait. It has
|
||||||
|
the ability to actually communicate with the chip. It has general methods for reading/writing to the chip, and
|
||||||
|
higher-level functions that can set up specific configuration, like the MAC address, etc.
|
||||||
|
|
||||||
|
The last layer is the network protocol. Currently that is only `Udp`. `Udp` is a tuple struct made up of an
|
||||||
|
`ActiveW5500` and a `Socket`. This last layer can be used to send and receive UDP packets over the network via the
|
||||||
|
`receive` and `blocking_send` methods.
|
||||||
|
|
||||||
|
# Example Usage
|
||||||
|
|
||||||
|
Below is a basic example of listening for UDP packets and replying. An important thing to confirm is the configuration
|
||||||
|
of the SPI implementation. It must be set up to work as the W5500 chip requires. That configuration is as follows:
|
||||||
|
|
||||||
|
* Data Order: Most significant bit first
|
||||||
|
* Clock Polarity: Idle low
|
||||||
|
* Clock Phase: Sample leading edge
|
||||||
|
* Clock speed: 33MHz maximum
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let mut spi = ...; // SPI interface to use
|
let mut spi = ...; // SPI interface to use
|
||||||
let mut cs_w5500 : OutputPin = ...; // chip select
|
let mut cs_w5500 : OutputPin = ...; // chip select
|
||||||
|
|
||||||
let mut w5500: Option<W5500> = W5500::with_initialisation(
|
let mut w5500 = W5500::with_initialisation(
|
||||||
&mut cs_w5500, // borrowed for whole W5500 lifetime
|
&mut cs_w5500, // borrowed for whole W5500 lifetime
|
||||||
&mut spi, // borrowed for call to `with_initialisation` only
|
&mut spi, // borrowed for call to `with_initialisation` only
|
||||||
OnWakeOnLan::Ignore,
|
OnWakeOnLan::Ignore,
|
||||||
OnPingRequest::Respond,
|
OnPingRequest::Respond,
|
||||||
ConnectionType::Ethernet,
|
ConnectionType::Ethernet,
|
||||||
ArpResponses::Cache,
|
ArpResponses::Cache,
|
||||||
)
|
).unwrap();
|
||||||
.ok();
|
|
||||||
|
|
||||||
if let Some(ref mut w5500) = w5500 {
|
let mut active = w5500.activate(&mut spi).unwrap();
|
||||||
let mut w5500: ActiveW5500<_> = w5500.activate(&mut spi).unwrap();
|
// using a 'locally administered' MAC address
|
||||||
// using a 'locally administered' MAC address
|
active.set_mac(MacAddress::new(0x02, 0x01, 0x02, 0x03, 0x04, 0x05)).unwrap();
|
||||||
active.set_mac(MacAddress::new(0x02, 0x01, 0x02, 0x03, 0x04, 0x05)).unwrap();
|
active.set_ip(IpAddress::new(192, 168, 0, 222)).unwrap();
|
||||||
active.set_ip(IpAddress::new(192, 168, 0, 222)).unwrap();
|
active.set_subnet(IpAddress::new(255, 255, 255, 0)).unwrap();
|
||||||
active.set_subnet(IpAddress::new(255, 255, 255, 0)).unwrap();
|
active.set_gateway(IpAddress::new(192, 168, 0, 1)).unwrap();
|
||||||
active.set_gateway(IpAddress::new(192, 168, 0, 1)).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut udp_server_socket: Option<UdpSocket> = w5500.as_mut().and_then(|w5500| {
|
let socket0: UninitializedSocket = w5500.take_socket(Socket::Socket0).unwrap();
|
||||||
let mut w5500: ActiveW5500<_> = w5500.activate(&mut spi).ok()?;
|
let udp_server_socket = (&mut w5500, socket0).try_into_udp_server_socket(1234).unwrap();
|
||||||
let socket0: UninitializedSocket = w5500.take_socket(Socket::Socket0)?;
|
|
||||||
(&mut w5500, socket0).try_into_udp_server_socket(1234).ok()
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut buffer = [0u8; 256];
|
let mut buffer = [0u8; 256];
|
||||||
if let (Some(ref mut w5500), Some(ref socket)) = (
|
let response = [104, 101, 108, 108, 111, 10];// "hello" as ASCII
|
||||||
w5500.as_mut().and_then(w5500.activate(&mut spi).ok()),
|
loop {
|
||||||
udp_server_socket,
|
if let Ok(Some((ip, port, len))) = udp_server_socket.receive(&mut buffer[..]) {
|
||||||
) {
|
udp_server_socket.blocking_send(ip, port, response[..]).unwrap();
|
||||||
if let Ok(Some((ip, port, len))) = (w5500, socket).receive(&mut buffer[..]) {
|
|
||||||
let (request_buffer, response_buffer) = buffer.split_mut_at(len);
|
|
||||||
|
|
||||||
// ... fill the response_buffer with some data ...
|
|
||||||
|
|
||||||
(w5500, socket).blocking_send(ip, port, response_buffer[..response_len]).unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## To To
|
||||||
|
|
||||||
|
In no particular order, things to do to improve this driver.
|
||||||
|
|
||||||
|
* Add support for TCP
|
||||||
|
* Add support for DHCP
|
||||||
|
* Method to return socket back to the pool
|
||||||
|
* Make reset safe by requiring that all sockets be returned to the pool first
|
||||||
|
* Support a 3-wire SPI bus
|
||||||
|
* Sane defaults for IP/Gateway/Subnet
|
||||||
|
* Improve documentation
|
||||||
|
|
|
||||||
63
src/lib.rs
63
src/lib.rs
|
|
@ -21,12 +21,14 @@ const FIXED_DATA_LENGTH_1_BYTE: u8 = 0b_01;
|
||||||
const FIXED_DATA_LENGTH_2_BYTES: u8 = 0b_10;
|
const FIXED_DATA_LENGTH_2_BYTES: u8 = 0b_10;
|
||||||
const FIXED_DATA_LENGTH_4_BYTES: u8 = 0b_11;
|
const FIXED_DATA_LENGTH_4_BYTES: u8 = 0b_11;
|
||||||
|
|
||||||
|
/// IP Address struct. Represents an IP address as a u8 array of length 4. Can be instantiated with `IpAddress::new`
|
||||||
#[derive(Copy, Clone, PartialOrd, PartialEq, Default, Debug)]
|
#[derive(Copy, Clone, PartialOrd, PartialEq, Default, Debug)]
|
||||||
pub struct IpAddress {
|
pub struct IpAddress {
|
||||||
pub address: [u8; 4],
|
pub address: [u8; 4],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IpAddress {
|
impl IpAddress {
|
||||||
|
/// Instantiate a new IP address with u8s for each address fragment
|
||||||
pub fn new(a0: u8, a1: u8, a2: u8, a3: u8) -> IpAddress {
|
pub fn new(a0: u8, a1: u8, a2: u8, a3: u8) -> IpAddress {
|
||||||
IpAddress {
|
IpAddress {
|
||||||
address: [a0, a1, a2, a3],
|
address: [a0, a1, a2, a3],
|
||||||
|
|
@ -35,6 +37,7 @@ impl IpAddress {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ::core::fmt::Display for IpAddress {
|
impl ::core::fmt::Display for IpAddress {
|
||||||
|
/// String formatter for IP addresses, useful for debugging output
|
||||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
|
|
@ -44,12 +47,14 @@ impl ::core::fmt::Display for IpAddress {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// MAC address struct. Represents a MAC address as a u8 array of length 6. Can be instantiated with `MacAddress::new`
|
||||||
#[derive(Copy, Clone, PartialOrd, PartialEq, Default, Debug)]
|
#[derive(Copy, Clone, PartialOrd, PartialEq, Default, Debug)]
|
||||||
pub struct MacAddress {
|
pub struct MacAddress {
|
||||||
pub address: [u8; 6],
|
pub address: [u8; 6],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MacAddress {
|
impl MacAddress {
|
||||||
|
/// Instantiate a new MAC address with u8s for each address fragment
|
||||||
pub fn new(a0: u8, a1: u8, a2: u8, a3: u8, a4: u8, a5: u8) -> MacAddress {
|
pub fn new(a0: u8, a1: u8, a2: u8, a3: u8, a4: u8, a5: u8) -> MacAddress {
|
||||||
MacAddress {
|
MacAddress {
|
||||||
address: [a0, a1, a2, a3, a4, a5],
|
address: [a0, a1, a2, a3, a4, a5],
|
||||||
|
|
@ -58,6 +63,7 @@ impl MacAddress {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ::core::fmt::Display for MacAddress {
|
impl ::core::fmt::Display for MacAddress {
|
||||||
|
/// String formatter for MAC addresses, useful for debugging output
|
||||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
|
|
@ -72,18 +78,22 @@ impl ::core::fmt::Display for MacAddress {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Error enum that represents the union between SPI hardware errors and digital IO pin errors. Returned as an Error
|
||||||
|
/// type by many W5500 that talk to the chip
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub enum TransferError<SpiError, ChipSelectError> {
|
pub enum TransferError<SpiError, ChipSelectError> {
|
||||||
SpiError(SpiError),
|
SpiError(SpiError),
|
||||||
ChipSelectError(ChipSelectError),
|
ChipSelectError(ChipSelectError),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Settings for wake on LAN. Allows the W5500 to optionally emit an interrupt upon receiving a packet
|
||||||
#[derive(Copy, Clone, PartialOrd, PartialEq)]
|
#[derive(Copy, Clone, PartialOrd, PartialEq)]
|
||||||
pub enum OnWakeOnLan {
|
pub enum OnWakeOnLan {
|
||||||
InvokeInterrupt,
|
InvokeInterrupt,
|
||||||
Ignore,
|
Ignore,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Settings for ping. Allows the W5500 to respond to or ignore network ping requests
|
||||||
#[derive(Copy, Clone, PartialOrd, PartialEq)]
|
#[derive(Copy, Clone, PartialOrd, PartialEq)]
|
||||||
pub enum OnPingRequest {
|
pub enum OnPingRequest {
|
||||||
Respond,
|
Respond,
|
||||||
|
|
@ -104,9 +114,14 @@ pub enum ArpResponses {
|
||||||
DropAfterUse,
|
DropAfterUse,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents a socket that has not yet been initialized for a particular protocol
|
||||||
pub struct UninitializedSocket(Socket);
|
pub struct UninitializedSocket(Socket);
|
||||||
|
|
||||||
|
/// Represents a socket that has been initialized to use the UDP protocol
|
||||||
pub struct UdpSocket(Socket);
|
pub struct UdpSocket(Socket);
|
||||||
|
|
||||||
|
/// The first level of instantiating communication with the W5500. It can not communicate by itself, but calling
|
||||||
|
/// `activate` will return an `ActiveW5500` which can.
|
||||||
pub struct W5500<'a, ChipSelect: OutputPin> {
|
pub struct W5500<'a, ChipSelect: OutputPin> {
|
||||||
chip_select: &'a mut ChipSelect,
|
chip_select: &'a mut ChipSelect,
|
||||||
sockets: u8, // each bit represents whether the corresponding socket is available for take
|
sockets: u8, // each bit represents whether the corresponding socket is available for take
|
||||||
|
|
@ -122,6 +137,7 @@ impl<'b, 'a: 'b, ChipSelectError, ChipSelect: OutputPin<Error = ChipSelectError>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Primary method for instantiating. Briefly activates the W5500, and sets it up with the specified configuration
|
||||||
pub fn with_initialisation<'c, Spi: FullDuplex<u8>>(
|
pub fn with_initialisation<'c, Spi: FullDuplex<u8>>(
|
||||||
chip_select: &'a mut ChipSelect,
|
chip_select: &'a mut ChipSelect,
|
||||||
spi: &'c mut Spi,
|
spi: &'c mut Spi,
|
||||||
|
|
@ -141,6 +157,7 @@ impl<'b, 'a: 'b, ChipSelectError, ChipSelect: OutputPin<Error = ChipSelectError>
|
||||||
Ok(w5500)
|
Ok(w5500)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the requested socket if it is not already used.
|
||||||
pub fn take_socket(&mut self, socket: Socket) -> Option<UninitializedSocket> {
|
pub fn take_socket(&mut self, socket: Socket) -> Option<UninitializedSocket> {
|
||||||
let mask = (0x01 << socket.number());
|
let mask = (0x01 << socket.number());
|
||||||
if self.sockets & mask == mask {
|
if self.sockets & mask == mask {
|
||||||
|
|
@ -151,6 +168,7 @@ impl<'b, 'a: 'b, ChipSelectError, ChipSelect: OutputPin<Error = ChipSelectError>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a new `ActiveW5500` with the provided `FullDuplex` implementation
|
||||||
pub fn activate<'c, Spi: FullDuplex<u8>>(
|
pub fn activate<'c, Spi: FullDuplex<u8>>(
|
||||||
&'b mut self,
|
&'b mut self,
|
||||||
spi: &'c mut Spi,
|
spi: &'c mut Spi,
|
||||||
|
|
@ -160,6 +178,7 @@ impl<'b, 'a: 'b, ChipSelectError, ChipSelect: OutputPin<Error = ChipSelectError>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Struct that can communicate with the W5500 chip, configuring it and reading/writing to the registers on a low level
|
||||||
pub struct ActiveW5500<'a, 'b: 'a, 'c, ChipSelect: OutputPin, Spi: FullDuplex<u8>>(
|
pub struct ActiveW5500<'a, 'b: 'a, 'c, ChipSelect: OutputPin, Spi: FullDuplex<u8>>(
|
||||||
&'a mut W5500<'b, ChipSelect>,
|
&'a mut W5500<'b, ChipSelect>,
|
||||||
&'c mut Spi,
|
&'c mut Spi,
|
||||||
|
|
@ -172,10 +191,12 @@ impl<
|
||||||
Spi: FullDuplex<u8, Error = SpiError>,
|
Spi: FullDuplex<u8, Error = SpiError>,
|
||||||
> ActiveW5500<'_, '_, '_, ChipSelect, Spi>
|
> ActiveW5500<'_, '_, '_, ChipSelect, Spi>
|
||||||
{
|
{
|
||||||
|
/// Returns the requested socket if it is not already used
|
||||||
pub fn take_socket(&mut self, socket: Socket) -> Option<UninitializedSocket> {
|
pub fn take_socket(&mut self, socket: Socket) -> Option<UninitializedSocket> {
|
||||||
self.0.take_socket(socket)
|
self.0.take_socket(socket)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set up basic configuration of the W5500 chip
|
||||||
pub fn update_operation_mode(
|
pub fn update_operation_mode(
|
||||||
&mut self,
|
&mut self,
|
||||||
wol: OnWakeOnLan,
|
wol: OnWakeOnLan,
|
||||||
|
|
@ -204,6 +225,7 @@ impl<
|
||||||
self.write_to(Register::CommonRegister(0x00_00_u16), &[value])
|
self.write_to(Register::CommonRegister(0x00_00_u16), &[value])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the IP address of the network gateway (router)
|
||||||
pub fn set_gateway(
|
pub fn set_gateway(
|
||||||
&mut self,
|
&mut self,
|
||||||
gateway: IpAddress,
|
gateway: IpAddress,
|
||||||
|
|
@ -211,6 +233,7 @@ impl<
|
||||||
self.write_to(Register::CommonRegister(0x00_01_u16), &gateway.address)
|
self.write_to(Register::CommonRegister(0x00_01_u16), &gateway.address)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the subnet on the network
|
||||||
pub fn set_subnet(
|
pub fn set_subnet(
|
||||||
&mut self,
|
&mut self,
|
||||||
subnet: IpAddress,
|
subnet: IpAddress,
|
||||||
|
|
@ -218,6 +241,7 @@ impl<
|
||||||
self.write_to(Register::CommonRegister(0x00_05_u16), &subnet.address)
|
self.write_to(Register::CommonRegister(0x00_05_u16), &subnet.address)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the MAC address of the W5500 device on the network
|
||||||
pub fn set_mac(
|
pub fn set_mac(
|
||||||
&mut self,
|
&mut self,
|
||||||
mac: MacAddress,
|
mac: MacAddress,
|
||||||
|
|
@ -225,6 +249,7 @@ impl<
|
||||||
self.write_to(Register::CommonRegister(0x00_09_u16), &mac.address)
|
self.write_to(Register::CommonRegister(0x00_09_u16), &mac.address)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the IP address of the W5500 device on network. Must be within the range permitted by the gateway
|
||||||
pub fn set_ip(
|
pub fn set_ip(
|
||||||
&mut self,
|
&mut self,
|
||||||
ip: IpAddress,
|
ip: IpAddress,
|
||||||
|
|
@ -232,6 +257,7 @@ impl<
|
||||||
self.write_to(Register::CommonRegister(0x00_0F_u16), &ip.address)
|
self.write_to(Register::CommonRegister(0x00_0F_u16), &ip.address)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reads 4 bytesfrom any register location and returns the value as an IP address
|
||||||
pub fn read_ip(
|
pub fn read_ip(
|
||||||
&mut self,
|
&mut self,
|
||||||
register: Register,
|
register: Register,
|
||||||
|
|
@ -255,6 +281,7 @@ impl<
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// TODO document
|
||||||
fn is_interrupt_set(
|
fn is_interrupt_set(
|
||||||
&mut self,
|
&mut self,
|
||||||
socket: Socket,
|
socket: Socket,
|
||||||
|
|
@ -265,6 +292,7 @@ impl<
|
||||||
Ok(state[0] & interrupt as u8 != 0)
|
Ok(state[0] & interrupt as u8 != 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// TODO document
|
||||||
pub fn reset_interrupt(
|
pub fn reset_interrupt(
|
||||||
&mut self,
|
&mut self,
|
||||||
socket: Socket,
|
socket: Socket,
|
||||||
|
|
@ -273,6 +301,7 @@ impl<
|
||||||
self.write_to(socket.at(SocketRegister::Interrupt), &[interrupt as u8])
|
self.write_to(socket.at(SocketRegister::Interrupt), &[interrupt as u8])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reads one byte from any register address as a u8
|
||||||
fn read_u8(
|
fn read_u8(
|
||||||
&mut self,
|
&mut self,
|
||||||
register: Register,
|
register: Register,
|
||||||
|
|
@ -282,6 +311,7 @@ impl<
|
||||||
Ok(buffer[0])
|
Ok(buffer[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reads two bytes from any register address and returns as a u16
|
||||||
fn read_u16(
|
fn read_u16(
|
||||||
&mut self,
|
&mut self,
|
||||||
register: Register,
|
register: Register,
|
||||||
|
|
@ -291,6 +321,7 @@ impl<
|
||||||
Ok(BigEndian::read_u16(&buffer))
|
Ok(BigEndian::read_u16(&buffer))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reads enough bytes from any register address to fill the `target` u8 slice
|
||||||
fn read_from(
|
fn read_from(
|
||||||
&mut self,
|
&mut self,
|
||||||
register: Register,
|
register: Register,
|
||||||
|
|
@ -316,6 +347,7 @@ impl<
|
||||||
result.map_err(|error| TransferError::SpiError(error))
|
result.map_err(|error| TransferError::SpiError(error))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reads enough bytes over SPI to fill the `target` u8 slice
|
||||||
fn read_bytes(&mut self, bytes: &mut [u8]) -> Result<(), SpiError> {
|
fn read_bytes(&mut self, bytes: &mut [u8]) -> Result<(), SpiError> {
|
||||||
for byte in bytes {
|
for byte in bytes {
|
||||||
*byte = self.read()?;
|
*byte = self.read()?;
|
||||||
|
|
@ -323,11 +355,13 @@ impl<
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reads a single byte over SPI by writing a zero and reading the response
|
||||||
fn read(&mut self) -> Result<u8, SpiError> {
|
fn read(&mut self) -> Result<u8, SpiError> {
|
||||||
block!(self.1.send(0x00))?;
|
block!(self.1.send(0x00))?;
|
||||||
block!(self.1.read())
|
block!(self.1.read())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Write a single u8 byte to any register address
|
||||||
fn write_u8(
|
fn write_u8(
|
||||||
&mut self,
|
&mut self,
|
||||||
register: Register,
|
register: Register,
|
||||||
|
|
@ -336,6 +370,7 @@ impl<
|
||||||
self.write_to(register, &[value])
|
self.write_to(register, &[value])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Write a u16 as two bytes o any register address
|
||||||
fn write_u16(
|
fn write_u16(
|
||||||
&mut self,
|
&mut self,
|
||||||
register: Register,
|
register: Register,
|
||||||
|
|
@ -346,6 +381,7 @@ impl<
|
||||||
self.write_to(register, &data)
|
self.write_to(register, &data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Write a slice of u8 bytes to any register address
|
||||||
fn write_to(
|
fn write_to(
|
||||||
&mut self,
|
&mut self,
|
||||||
register: Register,
|
register: Register,
|
||||||
|
|
@ -371,6 +407,7 @@ impl<
|
||||||
result.map_err(|error| TransferError::SpiError(error))
|
result.map_err(|error| TransferError::SpiError(error))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Write a slice of u8 bytes over SPI
|
||||||
fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), SpiError> {
|
fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), SpiError> {
|
||||||
for b in bytes {
|
for b in bytes {
|
||||||
self.write(*b)?;
|
self.write(*b)?;
|
||||||
|
|
@ -378,16 +415,19 @@ impl<
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Write a single byte over SPI
|
||||||
fn write(&mut self, byte: u8) -> Result<(), SpiError> {
|
fn write(&mut self, byte: u8) -> Result<(), SpiError> {
|
||||||
block!(self.1.send(byte))?;
|
block!(self.1.send(byte))?;
|
||||||
block!(self.1.read())?;
|
block!(self.1.read())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Begin a SPI frame by setting the CS signal to low
|
||||||
fn chip_select(&mut self) -> Result<(), ChipSelectError> {
|
fn chip_select(&mut self) -> Result<(), ChipSelectError> {
|
||||||
self.0.chip_select.set_low()
|
self.0.chip_select.set_low()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// End a SPI frame by setting the CS signal to high
|
||||||
fn chip_deselect(&mut self) -> Result<(), ChipSelectError> {
|
fn chip_deselect(&mut self) -> Result<(), ChipSelectError> {
|
||||||
self.0.chip_select.set_high()
|
self.0.chip_select.set_high()
|
||||||
}
|
}
|
||||||
|
|
@ -405,6 +445,7 @@ impl<ChipSelect: OutputPin, Spi: FullDuplex<u8>> IntoUdpSocket<UninitializedSock
|
||||||
UninitializedSocket,
|
UninitializedSocket,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
/// Initialize a socket to operate in UDP mode
|
||||||
fn try_into_udp_server_socket(self, port: u16) -> Result<UdpSocket, UninitializedSocket> {
|
fn try_into_udp_server_socket(self, port: u16) -> Result<UdpSocket, UninitializedSocket> {
|
||||||
let socket = (self.1).0;
|
let socket = (self.1).0;
|
||||||
(|| {
|
(|| {
|
||||||
|
|
@ -425,6 +466,7 @@ impl<ChipSelect: OutputPin, Spi: FullDuplex<u8>> IntoUdpSocket<UninitializedSock
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// UDP trait that can send and receive UDP packets
|
||||||
pub trait Udp {
|
pub trait Udp {
|
||||||
type Error;
|
type Error;
|
||||||
|
|
||||||
|
|
@ -446,6 +488,7 @@ impl<ChipSelect: OutputPin, Spi: FullDuplex<u8>> Udp
|
||||||
{
|
{
|
||||||
type Error = TransferError<Spi::Error, ChipSelect::Error>;
|
type Error = TransferError<Spi::Error, ChipSelect::Error>;
|
||||||
|
|
||||||
|
/// Returns a UDP packet if one is available. Will return `None` if no UDP packets are in the socket's buffer
|
||||||
fn receive(
|
fn receive(
|
||||||
&mut self,
|
&mut self,
|
||||||
destination: &mut [u8],
|
destination: &mut [u8],
|
||||||
|
|
@ -481,9 +524,6 @@ impl<ChipSelect: OutputPin, Spi: FullDuplex<u8>> Udp
|
||||||
&mut destination[..data_length],
|
&mut destination[..data_length],
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// self.read_from(socket.register_at(0x00_0C), &mut ip.address)?;
|
|
||||||
// self.read_u16(socket.register_at(0x00_10))?;
|
|
||||||
|
|
||||||
// reset
|
// reset
|
||||||
w5500.write_u16(
|
w5500.write_u16(
|
||||||
socket.at(SocketRegister::RxReadPointer),
|
socket.at(SocketRegister::RxReadPointer),
|
||||||
|
|
@ -500,6 +540,7 @@ impl<ChipSelect: OutputPin, Spi: FullDuplex<u8>> Udp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sends a UDP packet to the specified IP and port, and blocks until it is sent
|
||||||
fn blocking_send(
|
fn blocking_send(
|
||||||
&mut self,
|
&mut self,
|
||||||
host: &IpAddress,
|
host: &IpAddress,
|
||||||
|
|
@ -564,10 +605,6 @@ impl<ChipSelect: OutputPin, Spi: FullDuplex<u8>> Udp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// restore listen state
|
// restore listen state
|
||||||
/*
|
|
||||||
self.network
|
|
||||||
.listen_udp(self.spi, SOCKET_UDP, SOCKET_UDP_PORT)
|
|
||||||
*/
|
|
||||||
w5500.write_to(
|
w5500.write_to(
|
||||||
socket.at(SocketRegister::Mode),
|
socket.at(SocketRegister::Mode),
|
||||||
&[
|
&[
|
||||||
|
|
@ -579,6 +616,7 @@ impl<ChipSelect: OutputPin, Spi: FullDuplex<u8>> Udp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Offset addresses in each socket register
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||||
pub enum SocketRegister {
|
pub enum SocketRegister {
|
||||||
|
|
@ -609,6 +647,7 @@ pub enum SocketRegister {
|
||||||
// Reserved 0x0030 - 0xFFFF
|
// Reserved 0x0030 - 0xFFFF
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Interrupt state bits
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||||
pub enum Interrupt {
|
pub enum Interrupt {
|
||||||
|
|
@ -619,6 +658,7 @@ pub enum Interrupt {
|
||||||
Connected = 1, // 1 << 0
|
Connected = 1, // 1 << 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Register protocol mode bits
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||||
pub enum Protocol {
|
pub enum Protocol {
|
||||||
|
|
@ -627,6 +667,7 @@ pub enum Protocol {
|
||||||
MACRAW = 0b0100,
|
MACRAW = 0b0100,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Bits for socket commands
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||||
pub enum SocketCommand {
|
pub enum SocketCommand {
|
||||||
|
|
@ -641,6 +682,7 @@ pub enum SocketCommand {
|
||||||
Recv = 0x40,
|
Recv = 0x40,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Identifiers for each socket on the W5500
|
||||||
#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
|
#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
|
||||||
pub enum Socket {
|
pub enum Socket {
|
||||||
Socket0,
|
Socket0,
|
||||||
|
|
@ -654,6 +696,7 @@ pub enum Socket {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Socket {
|
impl Socket {
|
||||||
|
/// Gets the number of any given socket
|
||||||
pub fn number(self) -> usize {
|
pub fn number(self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Socket::Socket0 => 0,
|
Socket::Socket0 => 0,
|
||||||
|
|
@ -667,6 +710,7 @@ impl Socket {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the register address for a socket instance's TX
|
||||||
fn tx_register_at(self, address: u16) -> Register {
|
fn tx_register_at(self, address: u16) -> Register {
|
||||||
match self {
|
match self {
|
||||||
Socket::Socket0 => Register::Socket0TxBuffer(address),
|
Socket::Socket0 => Register::Socket0TxBuffer(address),
|
||||||
|
|
@ -680,6 +724,7 @@ impl Socket {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the register address for a socket instance's RX
|
||||||
fn rx_register_at(self, address: u16) -> Register {
|
fn rx_register_at(self, address: u16) -> Register {
|
||||||
match self {
|
match self {
|
||||||
Socket::Socket0 => Register::Socket0RxBuffer(address),
|
Socket::Socket0 => Register::Socket0RxBuffer(address),
|
||||||
|
|
@ -693,6 +738,7 @@ impl Socket {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the register address for a socket instance's register
|
||||||
fn register_at(self, address: u16) -> Register {
|
fn register_at(self, address: u16) -> Register {
|
||||||
match self {
|
match self {
|
||||||
Socket::Socket0 => Register::Socket0Register(address),
|
Socket::Socket0 => Register::Socket0Register(address),
|
||||||
|
|
@ -711,6 +757,7 @@ impl Socket {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Chip register names
|
||||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||||
pub enum Register {
|
pub enum Register {
|
||||||
CommonRegister(u16),
|
CommonRegister(u16),
|
||||||
|
|
@ -749,6 +796,7 @@ pub enum Register {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Register {
|
impl Register {
|
||||||
|
/// Gets the control bits to identify any given register
|
||||||
fn control_byte(self) -> u8 {
|
fn control_byte(self) -> u8 {
|
||||||
#[allow(clippy::inconsistent_digit_grouping)]
|
#[allow(clippy::inconsistent_digit_grouping)]
|
||||||
match self {
|
match self {
|
||||||
|
|
@ -788,6 +836,7 @@ impl Register {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the associated address as a u16
|
||||||
fn address(self) -> u16 {
|
fn address(self) -> u16 {
|
||||||
match self {
|
match self {
|
||||||
Register::CommonRegister(address) => address,
|
Register::CommonRegister(address) => address,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue