Merge pull request #1746 from embassy-rs/enc28j60-v2
wip: enc28j60 driver.
This commit is contained in:
commit
03576b9e83
16 changed files with 1722 additions and 7 deletions
18
.vscode/settings.json
vendored
18
.vscode/settings.json
vendored
|
@ -6,16 +6,21 @@
|
|||
"rust-analyzer.check.allTargets": false,
|
||||
"rust-analyzer.check.noDefaultFeatures": true,
|
||||
"rust-analyzer.cargo.noDefaultFeatures": true,
|
||||
"rust-analyzer.cargo.target": "thumbv7m-none-eabi",
|
||||
"rust-analyzer.showUnlinkedFileNotification": false,
|
||||
// uncomment the target of your chip.
|
||||
//"rust-analyzer.cargo.target": "thumbv6m-none-eabi",
|
||||
//"rust-analyzer.cargo.target": "thumbv7m-none-eabi",
|
||||
"rust-analyzer.cargo.target": "thumbv7em-none-eabi",
|
||||
//"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf",
|
||||
"rust-analyzer.cargo.features": [
|
||||
///"nightly",
|
||||
// Uncomment if the example has a "nightly" feature.
|
||||
"nightly",
|
||||
],
|
||||
"rust-analyzer.linkedProjects": [
|
||||
// Declare for the target you wish to develop
|
||||
// "embassy-executor/Cargo.toml",
|
||||
// "embassy-sync/Cargo.toml",
|
||||
"examples/stm32wl/Cargo.toml",
|
||||
// Uncomment ONE line for the chip you want to work on.
|
||||
// This makes rust-analyzer work on the example crate and all its dependencies.
|
||||
"examples/nrf52840/Cargo.toml",
|
||||
// "examples/nrf52840-rtic/Cargo.toml",
|
||||
// "examples/nrf5340/Cargo.toml",
|
||||
// "examples/nrf-rtos-trace/Cargo.toml",
|
||||
// "examples/rp/Cargo.toml",
|
||||
|
@ -41,5 +46,4 @@
|
|||
// "examples/stm32wl/Cargo.toml",
|
||||
// "examples/wasm/Cargo.toml",
|
||||
],
|
||||
"rust-analyzer.showUnlinkedFileNotification": false,
|
||||
}
|
23
embassy-net-enc28j60/Cargo.toml
Normal file
23
embassy-net-enc28j60/Cargo.toml
Normal file
|
@ -0,0 +1,23 @@
|
|||
[package]
|
||||
name = "embassy-net-enc28j60"
|
||||
version = "0.1.0"
|
||||
description = "embassy-net driver for the ENC28J60 ethernet chip"
|
||||
keywords = ["embedded", "enc28j60", "embassy-net", "embedded-hal-async", "ethernet", "async"]
|
||||
categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
embedded-hal = { version = "1.0.0-alpha.11" }
|
||||
embedded-hal-async = { version = "=0.2.0-alpha.2" }
|
||||
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
|
||||
embassy-time = { version = "0.1.2", path = "../embassy-time" }
|
||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
||||
|
||||
[package.metadata.embassy_docs]
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-enc28j60-v$VERSION/embassy-net-enc28j60/src/"
|
||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-enc28j60/src/"
|
||||
target = "thumbv7em-none-eabi"
|
19
embassy-net-enc28j60/README.md
Normal file
19
embassy-net-enc28j60/README.md
Normal file
|
@ -0,0 +1,19 @@
|
|||
# `embassy-net-enc28j60`
|
||||
|
||||
[`embassy-net`](https://crates.io/crates/embassy-net) integration for the Microchip ENC28J60 Ethernet chip.
|
||||
|
||||
Based on [@japaric](https://github.com/japaric)'s [`enc28j60`](https://github.com/japaric/enc28j60) crate.
|
||||
|
||||
## Interoperability
|
||||
|
||||
This crate can run on any executor.
|
||||
|
||||
## License
|
||||
|
||||
This work is licensed under either of
|
||||
|
||||
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
69
embassy-net-enc28j60/src/bank0.rs
Normal file
69
embassy-net-enc28j60/src/bank0.rs
Normal file
|
@ -0,0 +1,69 @@
|
|||
#[allow(dead_code)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Register {
|
||||
ERDPTL = 0x00,
|
||||
ERDPTH = 0x01,
|
||||
EWRPTL = 0x02,
|
||||
EWRPTH = 0x03,
|
||||
ETXSTL = 0x04,
|
||||
ETXSTH = 0x05,
|
||||
ETXNDL = 0x06,
|
||||
ETXNDH = 0x07,
|
||||
ERXSTL = 0x08,
|
||||
ERXSTH = 0x09,
|
||||
ERXNDL = 0x0a,
|
||||
ERXNDH = 0x0b,
|
||||
ERXRDPTL = 0x0c,
|
||||
ERXRDPTH = 0x0d,
|
||||
ERXWRPTL = 0x0e,
|
||||
ERXWRPTH = 0x0f,
|
||||
EDMASTL = 0x10,
|
||||
EDMASTH = 0x11,
|
||||
EDMANDL = 0x12,
|
||||
EDMANDH = 0x13,
|
||||
EDMADSTL = 0x14,
|
||||
EDMADSTH = 0x15,
|
||||
EDMACSL = 0x16,
|
||||
EDMACSH = 0x17,
|
||||
}
|
||||
|
||||
impl Register {
|
||||
pub(crate) fn addr(&self) -> u8 {
|
||||
*self as u8
|
||||
}
|
||||
|
||||
pub(crate) fn is_eth_register(&self) -> bool {
|
||||
match *self {
|
||||
Register::ERDPTL => true,
|
||||
Register::ERDPTH => true,
|
||||
Register::EWRPTL => true,
|
||||
Register::EWRPTH => true,
|
||||
Register::ETXSTL => true,
|
||||
Register::ETXSTH => true,
|
||||
Register::ETXNDL => true,
|
||||
Register::ETXNDH => true,
|
||||
Register::ERXSTL => true,
|
||||
Register::ERXSTH => true,
|
||||
Register::ERXNDL => true,
|
||||
Register::ERXNDH => true,
|
||||
Register::ERXRDPTL => true,
|
||||
Register::ERXRDPTH => true,
|
||||
Register::ERXWRPTL => true,
|
||||
Register::ERXWRPTH => true,
|
||||
Register::EDMASTL => true,
|
||||
Register::EDMASTH => true,
|
||||
Register::EDMANDL => true,
|
||||
Register::EDMANDH => true,
|
||||
Register::EDMADSTL => true,
|
||||
Register::EDMADSTH => true,
|
||||
Register::EDMACSL => true,
|
||||
Register::EDMACSH => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<super::Register> for Register {
|
||||
fn into(self) -> super::Register {
|
||||
super::Register::Bank0(self)
|
||||
}
|
||||
}
|
84
embassy-net-enc28j60/src/bank1.rs
Normal file
84
embassy-net-enc28j60/src/bank1.rs
Normal file
|
@ -0,0 +1,84 @@
|
|||
#[allow(dead_code)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Register {
|
||||
EHT0 = 0x00,
|
||||
EHT1 = 0x01,
|
||||
EHT2 = 0x02,
|
||||
EHT3 = 0x03,
|
||||
EHT4 = 0x04,
|
||||
EHT5 = 0x05,
|
||||
EHT6 = 0x06,
|
||||
EHT7 = 0x07,
|
||||
EPMM0 = 0x08,
|
||||
EPMM1 = 0x09,
|
||||
EPMM2 = 0x0a,
|
||||
EPMM3 = 0x0b,
|
||||
EPMM4 = 0x0c,
|
||||
EPMM5 = 0x0d,
|
||||
EPMM6 = 0x0e,
|
||||
EPMM7 = 0x0f,
|
||||
EPMCSL = 0x10,
|
||||
EPMCSH = 0x11,
|
||||
EPMOL = 0x14,
|
||||
EPMOH = 0x15,
|
||||
ERXFCON = 0x18,
|
||||
EPKTCNT = 0x19,
|
||||
}
|
||||
|
||||
impl Register {
|
||||
pub(crate) fn addr(&self) -> u8 {
|
||||
*self as u8
|
||||
}
|
||||
|
||||
pub(crate) fn is_eth_register(&self) -> bool {
|
||||
match *self {
|
||||
Register::EHT0 => true,
|
||||
Register::EHT1 => true,
|
||||
Register::EHT2 => true,
|
||||
Register::EHT3 => true,
|
||||
Register::EHT4 => true,
|
||||
Register::EHT5 => true,
|
||||
Register::EHT6 => true,
|
||||
Register::EHT7 => true,
|
||||
Register::EPMM0 => true,
|
||||
Register::EPMM1 => true,
|
||||
Register::EPMM2 => true,
|
||||
Register::EPMM3 => true,
|
||||
Register::EPMM4 => true,
|
||||
Register::EPMM5 => true,
|
||||
Register::EPMM6 => true,
|
||||
Register::EPMM7 => true,
|
||||
Register::EPMCSL => true,
|
||||
Register::EPMCSH => true,
|
||||
Register::EPMOL => true,
|
||||
Register::EPMOH => true,
|
||||
Register::ERXFCON => true,
|
||||
Register::EPKTCNT => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<super::Register> for Register {
|
||||
fn into(self) -> super::Register {
|
||||
super::Register::Bank1(self)
|
||||
}
|
||||
}
|
||||
|
||||
register!(ERXFCON, 0b1010_0001, u8, {
|
||||
#[doc = "Broadcast Filter Enable bit"]
|
||||
bcen @ 0,
|
||||
#[doc = "Multicast Filter Enable bit"]
|
||||
mcen @ 1,
|
||||
#[doc = "Hash Table Filter Enable bit"]
|
||||
hten @ 2,
|
||||
#[doc = "Magic Packet™ Filter Enable bit"]
|
||||
mpen @ 3,
|
||||
#[doc = "Pattern Match Filter Enable bit"]
|
||||
pmen @ 4,
|
||||
#[doc = "Post-Filter CRC Check Enable bit"]
|
||||
crcen @ 5,
|
||||
#[doc = "AND/OR Filter Select bit"]
|
||||
andor @ 6,
|
||||
#[doc = "Unicast Filter Enable bit"]
|
||||
ucen @ 7,
|
||||
});
|
86
embassy-net-enc28j60/src/bank2.rs
Normal file
86
embassy-net-enc28j60/src/bank2.rs
Normal file
|
@ -0,0 +1,86 @@
|
|||
#[allow(dead_code)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Register {
|
||||
MACON1 = 0x00,
|
||||
MACON3 = 0x02,
|
||||
MACON4 = 0x03,
|
||||
MABBIPG = 0x04,
|
||||
MAIPGL = 0x06,
|
||||
MAIPGH = 0x07,
|
||||
MACLCON1 = 0x08,
|
||||
MACLCON2 = 0x09,
|
||||
MAMXFLL = 0x0a,
|
||||
MAMXFLH = 0x0b,
|
||||
MICMD = 0x12,
|
||||
MIREGADR = 0x14,
|
||||
MIWRL = 0x16,
|
||||
MIWRH = 0x17,
|
||||
MIRDL = 0x18,
|
||||
MIRDH = 0x19,
|
||||
}
|
||||
|
||||
impl Register {
|
||||
pub(crate) fn addr(&self) -> u8 {
|
||||
*self as u8
|
||||
}
|
||||
|
||||
pub(crate) fn is_eth_register(&self) -> bool {
|
||||
match *self {
|
||||
Register::MACON1 => false,
|
||||
Register::MACON3 => false,
|
||||
Register::MACON4 => false,
|
||||
Register::MABBIPG => false,
|
||||
Register::MAIPGL => false,
|
||||
Register::MAIPGH => false,
|
||||
Register::MACLCON1 => false,
|
||||
Register::MACLCON2 => false,
|
||||
Register::MAMXFLL => false,
|
||||
Register::MAMXFLH => false,
|
||||
Register::MICMD => false,
|
||||
Register::MIREGADR => false,
|
||||
Register::MIWRL => false,
|
||||
Register::MIWRH => false,
|
||||
Register::MIRDL => false,
|
||||
Register::MIRDH => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<super::Register> for Register {
|
||||
fn into(self) -> super::Register {
|
||||
super::Register::Bank2(self)
|
||||
}
|
||||
}
|
||||
|
||||
register!(MACON1, 0, u8, {
|
||||
#[doc = "Enable packets to be received by the MAC"]
|
||||
marxen @ 0,
|
||||
#[doc = "Control frames will be discarded after being processed by the MAC"]
|
||||
passall @ 1,
|
||||
#[doc = "Inhibit transmissions when pause control frames are received"]
|
||||
rxpaus @ 2,
|
||||
#[doc = "Allow the MAC to transmit pause control frames"]
|
||||
txpaus @ 3,
|
||||
});
|
||||
|
||||
register!(MACON3, 0, u8, {
|
||||
#[doc = "MAC will operate in Full-Duplex mode"]
|
||||
fuldpx @ 0,
|
||||
#[doc = "The type/length field of transmitted and received frames will be checked"]
|
||||
frmlnen @ 1,
|
||||
#[doc = "Frames bigger than MAMXFL will be aborted when transmitted or received"]
|
||||
hfrmen @ 2,
|
||||
#[doc = "No proprietary header is present"]
|
||||
phdren @ 3,
|
||||
#[doc = "MAC will append a valid CRC to all frames transmitted regardless of PADCFG bit"]
|
||||
txcrcen @ 4,
|
||||
#[doc = "All short frames will be zero-padded to 64 bytes and a valid CRC will then be appended"]
|
||||
padcfg @ 5..7,
|
||||
});
|
||||
|
||||
register!(MICMD, 0, u8, {
|
||||
#[doc = "MII Read Enable bit"]
|
||||
miird @ 0,
|
||||
#[doc = "MII Scan Enable bit"]
|
||||
miiscan @ 1,
|
||||
});
|
53
embassy-net-enc28j60/src/bank3.rs
Normal file
53
embassy-net-enc28j60/src/bank3.rs
Normal file
|
@ -0,0 +1,53 @@
|
|||
#[allow(dead_code)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Register {
|
||||
MAADR5 = 0x00,
|
||||
MAADR6 = 0x01,
|
||||
MAADR3 = 0x02,
|
||||
MAADR4 = 0x03,
|
||||
MAADR1 = 0x04,
|
||||
MAADR2 = 0x05,
|
||||
EBSTSD = 0x06,
|
||||
EBSTCON = 0x07,
|
||||
EBSTCSL = 0x08,
|
||||
EBSTCSH = 0x09,
|
||||
MISTAT = 0x0a,
|
||||
EREVID = 0x12,
|
||||
ECOCON = 0x15,
|
||||
EFLOCON = 0x17,
|
||||
EPAUSL = 0x18,
|
||||
EPAUSH = 0x19,
|
||||
}
|
||||
|
||||
impl Register {
|
||||
pub(crate) fn addr(&self) -> u8 {
|
||||
*self as u8
|
||||
}
|
||||
|
||||
pub(crate) fn is_eth_register(&self) -> bool {
|
||||
match *self {
|
||||
Register::MAADR5 => false,
|
||||
Register::MAADR6 => false,
|
||||
Register::MAADR3 => false,
|
||||
Register::MAADR4 => false,
|
||||
Register::MAADR1 => false,
|
||||
Register::MAADR2 => false,
|
||||
Register::EBSTSD => true,
|
||||
Register::EBSTCON => true,
|
||||
Register::EBSTCSL => true,
|
||||
Register::EBSTCSH => true,
|
||||
Register::MISTAT => false,
|
||||
Register::EREVID => true,
|
||||
Register::ECOCON => true,
|
||||
Register::EFLOCON => true,
|
||||
Register::EPAUSL => true,
|
||||
Register::EPAUSH => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<super::Register> for Register {
|
||||
fn into(self) -> super::Register {
|
||||
super::Register::Bank3(self)
|
||||
}
|
||||
}
|
106
embassy-net-enc28j60/src/common.rs
Normal file
106
embassy-net-enc28j60/src/common.rs
Normal file
|
@ -0,0 +1,106 @@
|
|||
#[allow(dead_code)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Register {
|
||||
ECON1 = 0x1f,
|
||||
ECON2 = 0x1e,
|
||||
EIE = 0x1b,
|
||||
EIR = 0x1c,
|
||||
ESTAT = 0x1d,
|
||||
}
|
||||
|
||||
impl Register {
|
||||
pub(crate) fn addr(&self) -> u8 {
|
||||
*self as u8
|
||||
}
|
||||
|
||||
pub(crate) fn is_eth_register(&self) -> bool {
|
||||
match *self {
|
||||
Register::ECON1 => true,
|
||||
Register::ECON2 => true,
|
||||
Register::EIE => true,
|
||||
Register::EIR => true,
|
||||
Register::ESTAT => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<super::Register> for Register {
|
||||
fn into(self) -> super::Register {
|
||||
super::Register::Common(self)
|
||||
}
|
||||
}
|
||||
|
||||
register!(EIE, 0, u8, {
|
||||
#[doc = "Receive Error Interrupt Enable bit"]
|
||||
rxerie @ 0,
|
||||
#[doc = "Transmit Error Interrupt Enable bit"]
|
||||
txerie @ 1,
|
||||
#[doc = "Transmit Enable bit"]
|
||||
txie @ 3,
|
||||
#[doc = "Link Status Change Interrupt Enable bit"]
|
||||
linkie @ 4,
|
||||
#[doc = "DMA Interrupt Enable bit"]
|
||||
dmaie @ 5,
|
||||
#[doc = "Receive Packet Pending Interrupt Enable bit"]
|
||||
pktie @ 6,
|
||||
#[doc = "Global INT Interrupt Enable bit"]
|
||||
intie @ 7,
|
||||
});
|
||||
|
||||
register!(EIR, 0, u8, {
|
||||
#[doc = "Receive Error Interrupt Flag bit"]
|
||||
rxerif @ 0,
|
||||
#[doc = "Transmit Error Interrupt Flag bit"]
|
||||
txerif @ 1,
|
||||
#[doc = "Transmit Interrupt Flag bit"]
|
||||
txif @ 3,
|
||||
#[doc = "Link Change Interrupt Flag bit"]
|
||||
linkif @ 4,
|
||||
#[doc = "DMA Interrupt Flag bit"]
|
||||
dmaif @ 5,
|
||||
#[doc = "Receive Packet Pending Interrupt Flag bit"]
|
||||
pktif @ 6,
|
||||
});
|
||||
|
||||
register!(ESTAT, 0, u8, {
|
||||
#[doc = "Clock Ready bit"]
|
||||
clkrdy @ 0,
|
||||
#[doc = "Transmit Abort Error bit"]
|
||||
txabrt @ 1,
|
||||
#[doc = "Receive Busy bit"]
|
||||
rxbusy @ 2,
|
||||
#[doc = "Late Collision Error bit"]
|
||||
latecol @ 4,
|
||||
#[doc = "Ethernet Buffer Error Status bit"]
|
||||
bufer @ 6,
|
||||
#[doc = "INT Interrupt Flag bit"]
|
||||
int @ 7,
|
||||
});
|
||||
|
||||
register!(ECON2, 0b1000_0000, u8, {
|
||||
#[doc = "Voltage Regulator Power Save Enable bit"]
|
||||
vrps @ 3,
|
||||
#[doc = "Power Save Enable bit"]
|
||||
pwrsv @ 5,
|
||||
#[doc = "Packet Decrement bit"]
|
||||
pktdec @ 6,
|
||||
#[doc = "Automatic Buffer Pointer Increment Enable bit"]
|
||||
autoinc @ 7,
|
||||
});
|
||||
|
||||
register!(ECON1, 0, u8, {
|
||||
#[doc = "Bank Select bits"]
|
||||
bsel @ 0..1,
|
||||
#[doc = "Receive Enable bi"]
|
||||
rxen @ 2,
|
||||
#[doc = "Transmit Request to Send bit"]
|
||||
txrts @ 3,
|
||||
#[doc = "DMA Checksum Enable bit"]
|
||||
csumen @ 4,
|
||||
#[doc = "DMA Start and Busy Status bit"]
|
||||
dmast @ 5,
|
||||
#[doc = "Receive Logic Reset bit"]
|
||||
rxrst @ 6,
|
||||
#[doc = "Transmit Logic Reset bit"]
|
||||
txrst @ 7,
|
||||
});
|
225
embassy-net-enc28j60/src/fmt.rs
Normal file
225
embassy-net-enc28j60/src/fmt.rs
Normal file
|
@ -0,0 +1,225 @@
|
|||
#![macro_use]
|
||||
#![allow(unused_macros)]
|
||||
|
||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||
|
||||
macro_rules! assert {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::assert!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::assert!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! assert_eq {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::assert_eq!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::assert_eq!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! assert_ne {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::assert_ne!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::assert_ne!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! debug_assert {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::debug_assert!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug_assert!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! debug_assert_eq {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::debug_assert_eq!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug_assert_eq!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! debug_assert_ne {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::debug_assert_ne!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug_assert_ne!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! todo {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::todo!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::todo!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! unreachable {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::unreachable!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::unreachable!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! panic {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::panic!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::panic!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! trace {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::trace!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::trace!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! debug {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::debug!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! info {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::info!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::info!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! warn {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::warn!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::warn!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! error {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::error!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::error!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
macro_rules! unwrap {
|
||||
($($x:tt)*) => {
|
||||
::defmt::unwrap!($($x)*)
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
macro_rules! unwrap {
|
||||
($arg:expr) => {
|
||||
match $crate::fmt::Try::into_result($arg) {
|
||||
::core::result::Result::Ok(t) => t,
|
||||
::core::result::Result::Err(e) => {
|
||||
::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
|
||||
}
|
||||
}
|
||||
};
|
||||
($arg:expr, $($msg:expr),+ $(,)? ) => {
|
||||
match $crate::fmt::Try::into_result($arg) {
|
||||
::core::result::Result::Ok(t) => t,
|
||||
::core::result::Result::Err(e) => {
|
||||
::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct NoneError;
|
||||
|
||||
pub trait Try {
|
||||
type Ok;
|
||||
type Error;
|
||||
fn into_result(self) -> Result<Self::Ok, Self::Error>;
|
||||
}
|
||||
|
||||
impl<T> Try for Option<T> {
|
||||
type Ok = T;
|
||||
type Error = NoneError;
|
||||
|
||||
#[inline]
|
||||
fn into_result(self) -> Result<T, NoneError> {
|
||||
self.ok_or(NoneError)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> Try for Result<T, E> {
|
||||
type Ok = T;
|
||||
type Error = E;
|
||||
|
||||
#[inline]
|
||||
fn into_result(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
30
embassy-net-enc28j60/src/header.rs
Normal file
30
embassy-net-enc28j60/src/header.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
register!(RxStatus, 0, u32, {
|
||||
#[doc = "Indicates length of the received frame"]
|
||||
byte_count @ 0..15,
|
||||
#[doc = "Indicates a packet over 50,000 bit times occurred or that a packet was dropped since the last receive"]
|
||||
long_event @ 16,
|
||||
#[doc = "Indicates that at some time since the last receive, a carrier event was detected"]
|
||||
carrier_event @ 18,
|
||||
#[doc = "Indicates that frame CRC field value does not match the CRC calculated by the MAC"]
|
||||
crc_error @ 20,
|
||||
#[doc = "Indicates that frame length field value in the packet does not match the actual data byte length and specifies a valid length"]
|
||||
length_check_error @ 21,
|
||||
#[doc = "Indicates that frame type/length field was larger than 1500 bytes (type field)"]
|
||||
length_out_of_range @ 22,
|
||||
#[doc = "Indicates that at the packet had a valid CRC and no symbol errors"]
|
||||
received_ok @ 23,
|
||||
#[doc = "Indicates packet received had a valid Multicast address"]
|
||||
multicast @ 24,
|
||||
#[doc = "Indicates packet received had a valid Broadcast address."]
|
||||
broadcast @ 25,
|
||||
#[doc = "Indicates that after the end of this packet, an additional 1 to 7 bits were received"]
|
||||
dribble_nibble @ 26,
|
||||
#[doc = "Current frame was recognized as a control frame for having a valid type/length designating it as a control frame"]
|
||||
receive_control_frame @ 27,
|
||||
#[doc = "Current frame was recognized as a control frame containing a valid pause frame opcode and a valid destination address"]
|
||||
receive_pause_control_frame @ 28,
|
||||
#[doc = "Current frame was recognized as a control frame but it contained an unknown opcode"]
|
||||
receive_unknown_opcode @ 29,
|
||||
#[doc = "Current frame was recognized as a VLAN tagged frame"]
|
||||
receive_vlan_type_detected @ 30,
|
||||
});
|
707
embassy-net-enc28j60/src/lib.rs
Normal file
707
embassy-net-enc28j60/src/lib.rs
Normal file
|
@ -0,0 +1,707 @@
|
|||
#![no_std]
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
// must go first.
|
||||
mod fmt;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
mod bank0;
|
||||
mod bank1;
|
||||
mod bank2;
|
||||
mod bank3;
|
||||
mod common;
|
||||
mod header;
|
||||
mod phy;
|
||||
mod traits;
|
||||
|
||||
use core::cmp;
|
||||
use core::convert::TryInto;
|
||||
|
||||
use embassy_net_driver::{Capabilities, HardwareAddress, LinkState, Medium};
|
||||
use embassy_time::Duration;
|
||||
use embedded_hal::digital::OutputPin;
|
||||
use embedded_hal::spi::{Operation, SpiDevice};
|
||||
use traits::U16Ext;
|
||||
|
||||
// Total buffer size (see section 3.2)
|
||||
const BUF_SZ: u16 = 8 * 1024;
|
||||
|
||||
// Maximum frame length
|
||||
const MAX_FRAME_LENGTH: u16 = 1518; // value recommended in the data sheet
|
||||
|
||||
// Size of the Frame check sequence (32-bit CRC)
|
||||
const CRC_SZ: u16 = 4;
|
||||
|
||||
// define the boundaries of the TX and RX buffers
|
||||
// to workaround errata #5 we do the opposite of what section 6.1 of the data sheet
|
||||
// says: we place the RX buffer at address 0 and the TX buffer after it
|
||||
const RXST: u16 = 0x0000;
|
||||
const RXND: u16 = 0x19ff;
|
||||
const TXST: u16 = 0x1a00;
|
||||
const _TXND: u16 = 0x1fff;
|
||||
|
||||
const MTU: usize = 1514; // 1500 IP + 14 ethernet header
|
||||
|
||||
/// ENC28J60 embassy-net driver
|
||||
pub struct Enc28j60<S, O> {
|
||||
mac_addr: [u8; 6],
|
||||
|
||||
spi: S,
|
||||
rst: Option<O>,
|
||||
|
||||
bank: Bank,
|
||||
|
||||
// address of the next packet in buffer memory
|
||||
next_packet: u16,
|
||||
}
|
||||
|
||||
impl<S, O> Enc28j60<S, O>
|
||||
where
|
||||
S: SpiDevice,
|
||||
O: OutputPin,
|
||||
{
|
||||
/// Create a new ENC28J60 driver instance.
|
||||
///
|
||||
/// The RST pin is optional. If None, reset will be done with a SPI
|
||||
/// soft reset command, instead of via the RST pin.
|
||||
pub fn new(spi: S, rst: Option<O>, mac_addr: [u8; 6]) -> Self {
|
||||
let mut res = Self {
|
||||
mac_addr,
|
||||
spi,
|
||||
rst,
|
||||
|
||||
bank: Bank::Bank0,
|
||||
next_packet: RXST,
|
||||
};
|
||||
res.init();
|
||||
res
|
||||
}
|
||||
|
||||
fn init(&mut self) {
|
||||
if let Some(rst) = &mut self.rst {
|
||||
rst.set_low().unwrap();
|
||||
embassy_time::block_for(Duration::from_millis(5));
|
||||
rst.set_high().unwrap();
|
||||
embassy_time::block_for(Duration::from_millis(5));
|
||||
} else {
|
||||
embassy_time::block_for(Duration::from_millis(5));
|
||||
self.soft_reset();
|
||||
embassy_time::block_for(Duration::from_millis(5));
|
||||
}
|
||||
|
||||
debug!(
|
||||
"enc28j60: erevid {=u8:x}",
|
||||
self.read_control_register(bank3::Register::EREVID)
|
||||
);
|
||||
debug!("enc28j60: waiting for clk");
|
||||
while common::ESTAT(self.read_control_register(common::Register::ESTAT)).clkrdy() == 0 {}
|
||||
debug!("enc28j60: clk ok");
|
||||
|
||||
if self.read_control_register(bank3::Register::EREVID) == 0 {
|
||||
panic!("ErevidIsZero");
|
||||
}
|
||||
|
||||
// disable CLKOUT output
|
||||
self.write_control_register(bank3::Register::ECOCON, 0);
|
||||
|
||||
// RX start
|
||||
// "It is recommended that the ERXST Pointer be programmed with an even address"
|
||||
self.write_control_register(bank0::Register::ERXSTL, RXST.low());
|
||||
self.write_control_register(bank0::Register::ERXSTH, RXST.high());
|
||||
|
||||
// RX read pointer
|
||||
// NOTE Errata #14 so we are using an *odd* address here instead of ERXST
|
||||
self.write_control_register(bank0::Register::ERXRDPTL, RXND.low());
|
||||
self.write_control_register(bank0::Register::ERXRDPTH, RXND.high());
|
||||
|
||||
// RX end
|
||||
self.write_control_register(bank0::Register::ERXNDL, RXND.low());
|
||||
self.write_control_register(bank0::Register::ERXNDH, RXND.high());
|
||||
|
||||
// TX start
|
||||
// "It is recommended that an even address be used for ETXST"
|
||||
debug_assert_eq!(TXST % 2, 0);
|
||||
self.write_control_register(bank0::Register::ETXSTL, TXST.low());
|
||||
self.write_control_register(bank0::Register::ETXSTH, TXST.high());
|
||||
|
||||
// TX end is set in `transmit`
|
||||
|
||||
// MAC initialization (see section 6.5)
|
||||
// 1. Set the MARXEN bit in MACON1 to enable the MAC to receive frames.
|
||||
self.write_control_register(
|
||||
bank2::Register::MACON1,
|
||||
bank2::MACON1::default().marxen(1).passall(0).rxpaus(1).txpaus(1).bits(),
|
||||
);
|
||||
|
||||
// 2. Configure the PADCFG, TXCRCEN and FULDPX bits of MACON3.
|
||||
self.write_control_register(
|
||||
bank2::Register::MACON3,
|
||||
bank2::MACON3::default().frmlnen(1).txcrcen(1).padcfg(0b001).bits(),
|
||||
);
|
||||
|
||||
// 4. Program the MAMXFL registers with the maximum frame length to be permitted to be
|
||||
// received or transmitted
|
||||
self.write_control_register(bank2::Register::MAMXFLL, MAX_FRAME_LENGTH.low());
|
||||
self.write_control_register(bank2::Register::MAMXFLH, MAX_FRAME_LENGTH.high());
|
||||
|
||||
// 5. Configure the Back-to-Back Inter-Packet Gap register, MABBIPG.
|
||||
// Use recommended value of 0x12
|
||||
self.write_control_register(bank2::Register::MABBIPG, 0x12);
|
||||
|
||||
// 6. Configure the Non-Back-to-Back Inter-Packet Gap register low byte, MAIPGL.
|
||||
// Use recommended value of 0x12
|
||||
self.write_control_register(bank2::Register::MAIPGL, 0x12);
|
||||
self.write_control_register(bank2::Register::MAIPGH, 0x0c);
|
||||
|
||||
// 9. Program the local MAC address into the MAADR1:MAADR6 registers
|
||||
self.write_control_register(bank3::Register::MAADR1, self.mac_addr[0]);
|
||||
self.write_control_register(bank3::Register::MAADR2, self.mac_addr[1]);
|
||||
self.write_control_register(bank3::Register::MAADR3, self.mac_addr[2]);
|
||||
self.write_control_register(bank3::Register::MAADR4, self.mac_addr[3]);
|
||||
self.write_control_register(bank3::Register::MAADR5, self.mac_addr[4]);
|
||||
self.write_control_register(bank3::Register::MAADR6, self.mac_addr[5]);
|
||||
|
||||
// Set the PHCON2.HDLDIS bit to prevent automatic loopback of the data which is transmitted
|
||||
self.write_phy_register(phy::Register::PHCON2, phy::PHCON2::default().hdldis(1).bits());
|
||||
|
||||
// Globally enable interrupts
|
||||
//self.bit_field_set(common::Register::EIE, common::EIE::mask().intie());
|
||||
|
||||
// Set the per packet control byte; we'll always use the value 0
|
||||
self.write_buffer_memory(Some(TXST), &mut [0]);
|
||||
|
||||
// decrease the packet count to 0
|
||||
while self.read_control_register(bank1::Register::EPKTCNT) != 0 {
|
||||
self.bit_field_set(common::Register::ECON2, common::ECON2::mask().pktdec());
|
||||
}
|
||||
|
||||
// Enable reception
|
||||
self.bit_field_set(common::Register::ECON1, common::ECON1::mask().rxen());
|
||||
}
|
||||
|
||||
/// Flushes the transmit buffer, ensuring all pending transmissions have completed
|
||||
/// NOTE: The returned packet *must* be `read` or `ignore`-d, otherwise this method will always
|
||||
/// return `None` on subsequent invocations
|
||||
pub fn receive<'a>(&mut self, buf: &'a mut [u8]) -> Option<&'a mut [u8]> {
|
||||
if self.pending_packets() == 0 {
|
||||
// Errata #6: we can't rely on PKTIF so we check PKTCNT
|
||||
return None;
|
||||
}
|
||||
|
||||
let curr_packet = self.next_packet;
|
||||
|
||||
// read out the first 6 bytes
|
||||
let mut temp_buf = [0; 6];
|
||||
self.read_buffer_memory(Some(curr_packet), &mut temp_buf);
|
||||
|
||||
// next packet pointer
|
||||
let next_packet = u16::from_parts(temp_buf[0], temp_buf[1]);
|
||||
if next_packet > RXND {
|
||||
panic!("CorruptRxBuffer");
|
||||
}
|
||||
|
||||
// status vector
|
||||
let status = header::RxStatus(u32::from_le_bytes(temp_buf[2..].try_into().unwrap()));
|
||||
let len = status.byte_count() as u16 - CRC_SZ;
|
||||
|
||||
if len > RXND {
|
||||
panic!("CorruptRxBuffer 2");
|
||||
}
|
||||
|
||||
self.read_buffer_memory(None, &mut buf[..len as usize]);
|
||||
|
||||
// update ERXRDPT
|
||||
// due to Errata #14 we must write an odd address to ERXRDPT
|
||||
// we know that ERXST = 0, that ERXND is odd and that next_packet is even
|
||||
let rxrdpt = if self.next_packet < 1 || self.next_packet > RXND + 1 {
|
||||
RXND
|
||||
} else {
|
||||
self.next_packet - 1
|
||||
};
|
||||
// "To move ERXRDPT, the host controller must write to ERXRDPTL first."
|
||||
self.write_control_register(bank0::Register::ERXRDPTL, rxrdpt.low());
|
||||
self.write_control_register(bank0::Register::ERXRDPTH, rxrdpt.high());
|
||||
|
||||
// decrease the packet count
|
||||
self.bit_field_set(common::Register::ECON2, common::ECON2::mask().pktdec());
|
||||
|
||||
self.next_packet = next_packet;
|
||||
|
||||
Some(&mut buf[..len as usize])
|
||||
}
|
||||
|
||||
fn wait_tx_ready(&mut self) {
|
||||
for _ in 0u32..10000 {
|
||||
if common::ECON1(self.read_control_register(common::Register::ECON1)).txrts() == 0 {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// work around errata #12 by resetting the transmit logic before every new
|
||||
// transmission
|
||||
self.bit_field_set(common::Register::ECON1, common::ECON1::mask().txrst());
|
||||
self.bit_field_clear(common::Register::ECON1, common::ECON1::mask().txrst());
|
||||
//self.bit_field_clear(common::Register::EIR, {
|
||||
// let mask = common::EIR::mask();
|
||||
// mask.txerif() | mask.txif()
|
||||
//});
|
||||
}
|
||||
|
||||
/// Starts the transmission of `bytes`
|
||||
///
|
||||
/// It's up to the caller to ensure that `bytes` is a valid Ethernet frame. The interface will
|
||||
/// take care of appending a (4 byte) CRC to the frame and of padding the frame to the minimum
|
||||
/// size allowed by the Ethernet specification (64 bytes, or 46 bytes of payload).
|
||||
///
|
||||
/// NOTE This method will flush any previous transmission that's in progress
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If `bytes` length is greater than 1514, the maximum frame length allowed by the interface,
|
||||
/// or greater than the transmit buffer
|
||||
pub fn transmit(&mut self, bytes: &[u8]) {
|
||||
assert!(bytes.len() <= self.mtu() as usize);
|
||||
|
||||
self.wait_tx_ready();
|
||||
|
||||
// NOTE the plus one is to not overwrite the per packet control byte
|
||||
let wrpt = TXST + 1;
|
||||
|
||||
// 1. ETXST was set during initialization
|
||||
|
||||
// 2. write the frame to the IC memory
|
||||
self.write_buffer_memory(Some(wrpt), bytes);
|
||||
|
||||
let txnd = wrpt + bytes.len() as u16 - 1;
|
||||
|
||||
// 3. Set the end address of the transmit buffer
|
||||
self.write_control_register(bank0::Register::ETXNDL, txnd.low());
|
||||
self.write_control_register(bank0::Register::ETXNDH, txnd.high());
|
||||
|
||||
// 4. reset interrupt flag
|
||||
//self.bit_field_clear(common::Register::EIR, common::EIR::mask().txif());
|
||||
|
||||
// 5. start transmission
|
||||
self.bit_field_set(common::Register::ECON1, common::ECON1::mask().txrts());
|
||||
|
||||
// Wait until transmission finishes
|
||||
//while common::ECON1(self.read_control_register(common::Register::ECON1)).txrts() == 1 {}
|
||||
|
||||
/*
|
||||
// read the transmit status vector
|
||||
let mut tx_stat = [0; 7];
|
||||
self.read_buffer_memory(None, &mut tx_stat);
|
||||
|
||||
let stat = common::ESTAT(self.read_control_register(common::Register::ESTAT));
|
||||
|
||||
if stat.txabrt() == 1 {
|
||||
// work around errata #12 by reading the transmit status vector
|
||||
if stat.latecol() == 1 || (tx_stat[2] & (1 << 5)) != 0 {
|
||||
panic!("LateCollision")
|
||||
} else {
|
||||
panic!("TransmitAbort")
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
/// Get whether the link is up
|
||||
pub fn is_link_up(&mut self) -> bool {
|
||||
let bits = self.read_phy_register(phy::Register::PHSTAT2);
|
||||
phy::PHSTAT2(bits).lstat() == 1
|
||||
}
|
||||
|
||||
/// Returns the interface Maximum Transmission Unit (MTU)
|
||||
///
|
||||
/// The value returned by this function will never exceed 1514 bytes. The actual value depends
|
||||
/// on the memory assigned to the transmission buffer when initializing the device
|
||||
pub fn mtu(&self) -> u16 {
|
||||
cmp::min(BUF_SZ - RXND - 1, MAX_FRAME_LENGTH - CRC_SZ)
|
||||
}
|
||||
|
||||
/* Miscellaneous */
|
||||
/// Returns the number of packets that have been received but have not been processed yet
|
||||
pub fn pending_packets(&mut self) -> u8 {
|
||||
self.read_control_register(bank1::Register::EPKTCNT)
|
||||
}
|
||||
|
||||
/// Adjusts the receive filter to *accept* these packet types
|
||||
pub fn accept(&mut self, packets: &[Packet]) {
|
||||
let mask = bank1::ERXFCON::mask();
|
||||
let mut val = 0;
|
||||
for packet in packets {
|
||||
match packet {
|
||||
Packet::Broadcast => val |= mask.bcen(),
|
||||
Packet::Multicast => val |= mask.mcen(),
|
||||
Packet::Unicast => val |= mask.ucen(),
|
||||
}
|
||||
}
|
||||
|
||||
self.bit_field_set(bank1::Register::ERXFCON, val)
|
||||
}
|
||||
|
||||
/// Adjusts the receive filter to *ignore* these packet types
|
||||
pub fn ignore(&mut self, packets: &[Packet]) {
|
||||
let mask = bank1::ERXFCON::mask();
|
||||
let mut val = 0;
|
||||
for packet in packets {
|
||||
match packet {
|
||||
Packet::Broadcast => val |= mask.bcen(),
|
||||
Packet::Multicast => val |= mask.mcen(),
|
||||
Packet::Unicast => val |= mask.ucen(),
|
||||
}
|
||||
}
|
||||
|
||||
self.bit_field_clear(bank1::Register::ERXFCON, val)
|
||||
}
|
||||
|
||||
/* Private */
|
||||
/* Read */
|
||||
fn read_control_register<R>(&mut self, register: R) -> u8
|
||||
where
|
||||
R: Into<Register>,
|
||||
{
|
||||
self._read_control_register(register.into())
|
||||
}
|
||||
|
||||
fn _read_control_register(&mut self, register: Register) -> u8 {
|
||||
self.change_bank(register);
|
||||
|
||||
if register.is_eth_register() {
|
||||
let mut buffer = [Instruction::RCR.opcode() | register.addr(), 0];
|
||||
self.spi.transfer_in_place(&mut buffer).unwrap();
|
||||
buffer[1]
|
||||
} else {
|
||||
// MAC, MII regs need a dummy byte.
|
||||
let mut buffer = [Instruction::RCR.opcode() | register.addr(), 0, 0];
|
||||
self.spi.transfer_in_place(&mut buffer).unwrap();
|
||||
buffer[2]
|
||||
}
|
||||
}
|
||||
|
||||
fn read_phy_register(&mut self, register: phy::Register) -> u16 {
|
||||
embassy_time::block_for(Duration::from_millis(1));
|
||||
|
||||
// set PHY register address
|
||||
self.write_control_register(bank2::Register::MIREGADR, register.addr());
|
||||
|
||||
// start read operation
|
||||
self.write_control_register(bank2::Register::MICMD, bank2::MICMD::default().miird(1).bits());
|
||||
|
||||
// wait until the read operation finishes
|
||||
while self.read_control_register(bank3::Register::MISTAT) & 0b1 != 0 {}
|
||||
|
||||
self.write_control_register(bank2::Register::MICMD, bank2::MICMD::default().miird(0).bits());
|
||||
|
||||
let l = self.read_control_register(bank2::Register::MIRDL);
|
||||
let h = self.read_control_register(bank2::Register::MIRDH);
|
||||
(l as u16) | (h as u16) << 8
|
||||
}
|
||||
|
||||
/* Write */
|
||||
fn _write_control_register(&mut self, register: Register, value: u8) {
|
||||
self.change_bank(register);
|
||||
|
||||
let buffer = [Instruction::WCR.opcode() | register.addr(), value];
|
||||
self.spi.write(&buffer).unwrap();
|
||||
}
|
||||
|
||||
fn write_control_register<R>(&mut self, register: R, value: u8)
|
||||
where
|
||||
R: Into<Register>,
|
||||
{
|
||||
self._write_control_register(register.into(), value)
|
||||
}
|
||||
|
||||
fn write_phy_register(&mut self, register: phy::Register, value: u16) {
|
||||
// set PHY register address
|
||||
self.write_control_register(bank2::Register::MIREGADR, register.addr());
|
||||
|
||||
self.write_control_register(bank2::Register::MIWRL, (value & 0xff) as u8);
|
||||
// this starts the write operation
|
||||
self.write_control_register(bank2::Register::MIWRH, (value >> 8) as u8);
|
||||
|
||||
// wait until the write operation finishes
|
||||
while self.read_control_register(bank3::Register::MISTAT) & 0b1 != 0 {}
|
||||
}
|
||||
|
||||
/* RMW */
|
||||
fn modify_control_register<R, F>(&mut self, register: R, f: F)
|
||||
where
|
||||
F: FnOnce(u8) -> u8,
|
||||
R: Into<Register>,
|
||||
{
|
||||
self._modify_control_register(register.into(), f)
|
||||
}
|
||||
|
||||
fn _modify_control_register<F>(&mut self, register: Register, f: F)
|
||||
where
|
||||
F: FnOnce(u8) -> u8,
|
||||
{
|
||||
let r = self._read_control_register(register);
|
||||
self._write_control_register(register, f(r))
|
||||
}
|
||||
|
||||
/* Auxiliary */
|
||||
fn change_bank(&mut self, register: Register) {
|
||||
let bank = register.bank();
|
||||
|
||||
if let Some(bank) = bank {
|
||||
if self.bank == bank {
|
||||
// already on the register bank
|
||||
return;
|
||||
}
|
||||
|
||||
// change bank
|
||||
self.bank = bank;
|
||||
match bank {
|
||||
Bank::Bank0 => self.bit_field_clear(common::Register::ECON1, 0b11),
|
||||
Bank::Bank1 => self.modify_control_register(common::Register::ECON1, |r| (r & !0b11) | 0b01),
|
||||
Bank::Bank2 => self.modify_control_register(common::Register::ECON1, |r| (r & !0b11) | 0b10),
|
||||
Bank::Bank3 => self.bit_field_set(common::Register::ECON1, 0b11),
|
||||
}
|
||||
} else {
|
||||
// common register
|
||||
}
|
||||
}
|
||||
|
||||
/* Primitive operations */
|
||||
fn bit_field_clear<R>(&mut self, register: R, mask: u8)
|
||||
where
|
||||
R: Into<Register>,
|
||||
{
|
||||
self._bit_field_clear(register.into(), mask)
|
||||
}
|
||||
|
||||
fn _bit_field_clear(&mut self, register: Register, mask: u8) {
|
||||
debug_assert!(register.is_eth_register());
|
||||
|
||||
self.change_bank(register);
|
||||
|
||||
self.spi
|
||||
.write(&[Instruction::BFC.opcode() | register.addr(), mask])
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn bit_field_set<R>(&mut self, register: R, mask: u8)
|
||||
where
|
||||
R: Into<Register>,
|
||||
{
|
||||
self._bit_field_set(register.into(), mask)
|
||||
}
|
||||
|
||||
fn _bit_field_set(&mut self, register: Register, mask: u8) {
|
||||
debug_assert!(register.is_eth_register());
|
||||
|
||||
self.change_bank(register);
|
||||
|
||||
self.spi
|
||||
.write(&[Instruction::BFS.opcode() | register.addr(), mask])
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn read_buffer_memory(&mut self, addr: Option<u16>, buf: &mut [u8]) {
|
||||
if let Some(addr) = addr {
|
||||
self.write_control_register(bank0::Register::ERDPTL, addr.low());
|
||||
self.write_control_register(bank0::Register::ERDPTH, addr.high());
|
||||
}
|
||||
|
||||
self.spi
|
||||
.transaction(&mut [Operation::Write(&[Instruction::RBM.opcode()]), Operation::Read(buf)])
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn soft_reset(&mut self) {
|
||||
self.spi.write(&[Instruction::SRC.opcode()]).unwrap();
|
||||
}
|
||||
|
||||
fn write_buffer_memory(&mut self, addr: Option<u16>, buffer: &[u8]) {
|
||||
if let Some(addr) = addr {
|
||||
self.write_control_register(bank0::Register::EWRPTL, addr.low());
|
||||
self.write_control_register(bank0::Register::EWRPTH, addr.high());
|
||||
}
|
||||
|
||||
self.spi
|
||||
.transaction(&mut [Operation::Write(&[Instruction::WBM.opcode()]), Operation::Write(buffer)])
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
enum Bank {
|
||||
Bank0,
|
||||
Bank1,
|
||||
Bank2,
|
||||
Bank3,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum Instruction {
|
||||
/// Read Control Register
|
||||
RCR = 0b000_00000,
|
||||
/// Read Buffer Memory
|
||||
RBM = 0b001_11010,
|
||||
/// Write Control Register
|
||||
WCR = 0b010_00000,
|
||||
/// Write Buffer Memory
|
||||
WBM = 0b011_11010,
|
||||
/// Bit Field Set
|
||||
BFS = 0b100_00000,
|
||||
/// Bit Field Clear
|
||||
BFC = 0b101_00000,
|
||||
/// System Reset Command
|
||||
SRC = 0b111_11111,
|
||||
}
|
||||
|
||||
impl Instruction {
|
||||
fn opcode(&self) -> u8 {
|
||||
*self as u8
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum Register {
|
||||
Bank0(bank0::Register),
|
||||
Bank1(bank1::Register),
|
||||
Bank2(bank2::Register),
|
||||
Bank3(bank3::Register),
|
||||
Common(common::Register),
|
||||
}
|
||||
|
||||
impl Register {
|
||||
fn addr(&self) -> u8 {
|
||||
match *self {
|
||||
Register::Bank0(r) => r.addr(),
|
||||
Register::Bank1(r) => r.addr(),
|
||||
Register::Bank2(r) => r.addr(),
|
||||
Register::Bank3(r) => r.addr(),
|
||||
Register::Common(r) => r.addr(),
|
||||
}
|
||||
}
|
||||
|
||||
fn bank(&self) -> Option<Bank> {
|
||||
Some(match *self {
|
||||
Register::Bank0(_) => Bank::Bank0,
|
||||
Register::Bank1(_) => Bank::Bank1,
|
||||
Register::Bank2(_) => Bank::Bank2,
|
||||
Register::Bank3(_) => Bank::Bank3,
|
||||
Register::Common(_) => return None,
|
||||
})
|
||||
}
|
||||
|
||||
fn is_eth_register(&self) -> bool {
|
||||
match *self {
|
||||
Register::Bank0(r) => r.is_eth_register(),
|
||||
Register::Bank1(r) => r.is_eth_register(),
|
||||
Register::Bank2(r) => r.is_eth_register(),
|
||||
Register::Bank3(r) => r.is_eth_register(),
|
||||
Register::Common(r) => r.is_eth_register(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Packet type, used to configure receive filters
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub enum Packet {
|
||||
/// Broadcast packets
|
||||
Broadcast,
|
||||
/// Multicast packets
|
||||
Multicast,
|
||||
/// Unicast packets
|
||||
Unicast,
|
||||
}
|
||||
|
||||
static mut TX_BUF: [u8; MTU] = [0; MTU];
|
||||
static mut RX_BUF: [u8; MTU] = [0; MTU];
|
||||
|
||||
impl<S, O> embassy_net_driver::Driver for Enc28j60<S, O>
|
||||
where
|
||||
S: SpiDevice,
|
||||
O: OutputPin,
|
||||
{
|
||||
type RxToken<'a> = RxToken<'a>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
type TxToken<'a> = TxToken<'a, S, O>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn receive(&mut self, cx: &mut core::task::Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
|
||||
let rx_buf = unsafe { &mut RX_BUF };
|
||||
let tx_buf = unsafe { &mut TX_BUF };
|
||||
if let Some(pkt) = self.receive(rx_buf) {
|
||||
let n = pkt.len();
|
||||
Some((RxToken { buf: &mut pkt[..n] }, TxToken { buf: tx_buf, eth: self }))
|
||||
} else {
|
||||
cx.waker().wake_by_ref();
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn transmit(&mut self, _cx: &mut core::task::Context) -> Option<Self::TxToken<'_>> {
|
||||
let tx_buf = unsafe { &mut TX_BUF };
|
||||
Some(TxToken { buf: tx_buf, eth: self })
|
||||
}
|
||||
|
||||
fn link_state(&mut self, cx: &mut core::task::Context) -> LinkState {
|
||||
cx.waker().wake_by_ref();
|
||||
match self.is_link_up() {
|
||||
true => LinkState::Up,
|
||||
false => LinkState::Down,
|
||||
}
|
||||
}
|
||||
|
||||
fn capabilities(&self) -> Capabilities {
|
||||
let mut caps = Capabilities::default();
|
||||
caps.max_transmission_unit = MTU;
|
||||
caps.medium = Medium::Ethernet;
|
||||
caps
|
||||
}
|
||||
|
||||
fn hardware_address(&self) -> HardwareAddress {
|
||||
HardwareAddress::Ethernet(self.mac_addr)
|
||||
}
|
||||
}
|
||||
|
||||
/// embassy-net RX token.
|
||||
pub struct RxToken<'a> {
|
||||
buf: &'a mut [u8],
|
||||
}
|
||||
|
||||
impl<'a> embassy_net_driver::RxToken for RxToken<'a> {
|
||||
fn consume<R, F>(self, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> R,
|
||||
{
|
||||
f(self.buf)
|
||||
}
|
||||
}
|
||||
|
||||
/// embassy-net TX token.
|
||||
pub struct TxToken<'a, S, O>
|
||||
where
|
||||
S: SpiDevice,
|
||||
O: OutputPin,
|
||||
{
|
||||
eth: &'a mut Enc28j60<S, O>,
|
||||
buf: &'a mut [u8],
|
||||
}
|
||||
|
||||
impl<'a, S, O> embassy_net_driver::TxToken for TxToken<'a, S, O>
|
||||
where
|
||||
S: SpiDevice,
|
||||
O: OutputPin,
|
||||
{
|
||||
fn consume<R, F>(self, len: usize, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> R,
|
||||
{
|
||||
assert!(len <= self.buf.len());
|
||||
let r = f(&mut self.buf[..len]);
|
||||
self.eth.transmit(&self.buf[..len]);
|
||||
r
|
||||
}
|
||||
}
|
89
embassy-net-enc28j60/src/macros.rs
Normal file
89
embassy-net-enc28j60/src/macros.rs
Normal file
|
@ -0,0 +1,89 @@
|
|||
macro_rules! register {
|
||||
($REGISTER:ident, $reset_value:expr, $uxx:ty, {
|
||||
$(#[$($attr:tt)*] $bitfield:ident @ $range:expr,)+
|
||||
}) => {
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) struct $REGISTER<MODE> {
|
||||
bits: $uxx,
|
||||
_mode: ::core::marker::PhantomData<MODE>,
|
||||
}
|
||||
|
||||
impl $REGISTER<super::traits::Mask> {
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn mask() -> $REGISTER<super::traits::Mask> {
|
||||
$REGISTER { bits: 0, _mode: ::core::marker::PhantomData }
|
||||
}
|
||||
|
||||
$(
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn $bitfield(&self) -> $uxx {
|
||||
use super::traits::OffsetSize;
|
||||
|
||||
let size = $range.size();
|
||||
let offset = $range.offset();
|
||||
((1 << size) - 1) << offset
|
||||
}
|
||||
)+
|
||||
}
|
||||
|
||||
impl ::core::default::Default for $REGISTER<super::traits::W> {
|
||||
fn default() -> Self {
|
||||
$REGISTER { bits: $reset_value, _mode: ::core::marker::PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn $REGISTER(bits: $uxx) -> $REGISTER<super::traits::R> {
|
||||
$REGISTER { bits, _mode: ::core::marker::PhantomData }
|
||||
}
|
||||
|
||||
impl $REGISTER<super::traits::R> {
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn modify(self) -> $REGISTER<super::traits::W> {
|
||||
$REGISTER { bits: self.bits, _mode: ::core::marker::PhantomData }
|
||||
}
|
||||
|
||||
$(
|
||||
#[$($attr)*]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn $bitfield(&self) -> $uxx {
|
||||
use super::traits::OffsetSize;
|
||||
|
||||
let offset = $range.offset();
|
||||
let size = $range.size();
|
||||
let mask = (1 << size) - 1;
|
||||
|
||||
(self.bits >> offset) & mask
|
||||
}
|
||||
)+
|
||||
}
|
||||
|
||||
impl $REGISTER<super::traits::W> {
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn bits(self) -> $uxx {
|
||||
self.bits
|
||||
}
|
||||
|
||||
$(
|
||||
#[$($attr)*]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn $bitfield(&mut self, mut bits: $uxx) -> &mut Self {
|
||||
use super::traits::OffsetSize;
|
||||
|
||||
let offset = $range.offset();
|
||||
let size = $range.size();
|
||||
let mask = (1 << size) - 1;
|
||||
|
||||
debug_assert!(bits <= mask);
|
||||
bits &= mask;
|
||||
|
||||
self.bits &= !(mask << offset);
|
||||
self.bits |= bits << offset;
|
||||
|
||||
self
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
}
|
35
embassy-net-enc28j60/src/phy.rs
Normal file
35
embassy-net-enc28j60/src/phy.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
#[allow(dead_code)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Register {
|
||||
PHCON1 = 0x00,
|
||||
PHSTAT1 = 0x01,
|
||||
PHID1 = 0x02,
|
||||
PHID2 = 0x03,
|
||||
PHCON2 = 0x10,
|
||||
PHSTAT2 = 0x11,
|
||||
PHIE = 0x12,
|
||||
PHIR = 0x13,
|
||||
PHLCON = 0x14,
|
||||
}
|
||||
|
||||
impl Register {
|
||||
pub(crate) fn addr(&self) -> u8 {
|
||||
*self as u8
|
||||
}
|
||||
}
|
||||
|
||||
register!(PHCON2, 0, u16, {
|
||||
#[doc = "PHY Half-Duplex Loopback Disable bit"]
|
||||
hdldis @ 8,
|
||||
#[doc = "Jabber Correction Disable bit"]
|
||||
jabber @ 10,
|
||||
#[doc = "Twisted-Pair Transmitter Disable bit"]
|
||||
txdis @ 13,
|
||||
#[doc = "PHY Force Linkup bit"]
|
||||
frclnk @ 14,
|
||||
});
|
||||
|
||||
register!(PHSTAT2, 0, u16, {
|
||||
#[doc = "Link Status bit"]
|
||||
lstat @ 10,
|
||||
});
|
57
embassy-net-enc28j60/src/traits.rs
Normal file
57
embassy-net-enc28j60/src/traits.rs
Normal file
|
@ -0,0 +1,57 @@
|
|||
use core::ops::Range;
|
||||
|
||||
pub(crate) trait OffsetSize {
|
||||
fn offset(self) -> u8;
|
||||
fn size(self) -> u8;
|
||||
}
|
||||
|
||||
impl OffsetSize for u8 {
|
||||
fn offset(self) -> u8 {
|
||||
self
|
||||
}
|
||||
|
||||
fn size(self) -> u8 {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
impl OffsetSize for Range<u8> {
|
||||
fn offset(self) -> u8 {
|
||||
self.start
|
||||
}
|
||||
|
||||
fn size(self) -> u8 {
|
||||
self.end - self.start
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait U16Ext {
|
||||
fn from_parts(low: u8, high: u8) -> Self;
|
||||
|
||||
fn low(self) -> u8;
|
||||
|
||||
fn high(self) -> u8;
|
||||
}
|
||||
|
||||
impl U16Ext for u16 {
|
||||
fn from_parts(low: u8, high: u8) -> u16 {
|
||||
((high as u16) << 8) + low as u16
|
||||
}
|
||||
|
||||
fn low(self) -> u8 {
|
||||
(self & 0xff) as u8
|
||||
}
|
||||
|
||||
fn high(self) -> u8 {
|
||||
(self >> 8) as u8
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Mask;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct R;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct W;
|
|
@ -12,6 +12,7 @@ nightly = [
|
|||
"embassy-nrf/nightly",
|
||||
"embassy-net/nightly",
|
||||
"embassy-net-esp-hosted",
|
||||
"embassy-net-enc28j60",
|
||||
"embassy-nrf/unstable-traits",
|
||||
"embassy-time/nightly",
|
||||
"embassy-time/unstable-traits",
|
||||
|
@ -40,6 +41,7 @@ lora-phy = { version = "1", optional = true }
|
|||
lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true }
|
||||
lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true }
|
||||
embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"], optional = true }
|
||||
embassy-net-enc28j60 = { version = "0.1.0", path = "../../embassy-net-enc28j60", features = ["defmt"], optional = true }
|
||||
|
||||
defmt = "0.3"
|
||||
defmt-rtt = "0.4"
|
||||
|
@ -54,7 +56,9 @@ rand = { version = "0.8.4", default-features = false }
|
|||
embedded-storage = "0.3.0"
|
||||
usbd-hid = "0.6.0"
|
||||
serde = { version = "1.0.136", default-features = false }
|
||||
embedded-hal = { version = "1.0.0-alpha.11" }
|
||||
embedded-hal-async = { version = "0.2.0-alpha.2", optional = true }
|
||||
embedded-hal-bus = { version = "0.1.0-alpha.3" }
|
||||
num-integer = { version = "0.1.45", default-features = false }
|
||||
microfft = "0.5.0"
|
||||
|
||||
|
|
124
examples/nrf52840/src/bin/ethernet_enc28j60.rs
Normal file
124
examples/nrf52840/src/bin/ethernet_enc28j60.rs
Normal file
|
@ -0,0 +1,124 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_net::tcp::TcpSocket;
|
||||
use embassy_net::{Stack, StackResources};
|
||||
use embassy_net_enc28j60::Enc28j60;
|
||||
use embassy_nrf::gpio::{Level, Output, OutputDrive};
|
||||
use embassy_nrf::rng::Rng;
|
||||
use embassy_nrf::spim::Spim;
|
||||
use embassy_nrf::{bind_interrupts, peripherals, spim};
|
||||
use embassy_time::Delay;
|
||||
use embedded_hal_bus::spi::ExclusiveDevice;
|
||||
use embedded_io_async::Write;
|
||||
use static_cell::make_static;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
SPIM3 => spim::InterruptHandler<peripherals::SPI3>;
|
||||
RNG => embassy_nrf::rng::InterruptHandler<peripherals::RNG>;
|
||||
});
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn net_task(
|
||||
stack: &'static Stack<
|
||||
Enc28j60<
|
||||
ExclusiveDevice<Spim<'static, peripherals::SPI3>, Output<'static, peripherals::P0_15>, Delay>,
|
||||
Output<'static, peripherals::P0_13>,
|
||||
>,
|
||||
>,
|
||||
) -> ! {
|
||||
stack.run().await
|
||||
}
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(spawner: Spawner) {
|
||||
let p = embassy_nrf::init(Default::default());
|
||||
info!("running!");
|
||||
|
||||
let eth_sck = p.P0_20;
|
||||
let eth_mosi = p.P0_22;
|
||||
let eth_miso = p.P0_24;
|
||||
let eth_cs = p.P0_15;
|
||||
let eth_rst = p.P0_13;
|
||||
let _eth_irq = p.P0_12;
|
||||
|
||||
let mut config = spim::Config::default();
|
||||
config.frequency = spim::Frequency::M16;
|
||||
let spi = spim::Spim::new(p.SPI3, Irqs, eth_sck, eth_miso, eth_mosi, config);
|
||||
let cs = Output::new(eth_cs, Level::High, OutputDrive::Standard);
|
||||
let spi = ExclusiveDevice::new(spi, cs, Delay);
|
||||
|
||||
let rst = Output::new(eth_rst, Level::High, OutputDrive::Standard);
|
||||
let mac_addr = [2, 3, 4, 5, 6, 7];
|
||||
let device = Enc28j60::new(spi, Some(rst), mac_addr);
|
||||
|
||||
let config = embassy_net::Config::dhcpv4(Default::default());
|
||||
// let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 {
|
||||
// address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),
|
||||
// dns_servers: Vec::new(),
|
||||
// gateway: Some(Ipv4Address::new(10, 42, 0, 1)),
|
||||
// });
|
||||
|
||||
// Generate random seed
|
||||
let mut rng = Rng::new(p.RNG, Irqs);
|
||||
let mut seed = [0; 8];
|
||||
rng.blocking_fill_bytes(&mut seed);
|
||||
let seed = u64::from_le_bytes(seed);
|
||||
|
||||
// Init network stack
|
||||
let stack = &*make_static!(Stack::new(
|
||||
device,
|
||||
config,
|
||||
make_static!(StackResources::<2>::new()),
|
||||
seed
|
||||
));
|
||||
|
||||
unwrap!(spawner.spawn(net_task(stack)));
|
||||
|
||||
// And now we can use it!
|
||||
|
||||
let mut rx_buffer = [0; 4096];
|
||||
let mut tx_buffer = [0; 4096];
|
||||
let mut buf = [0; 4096];
|
||||
|
||||
loop {
|
||||
let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
|
||||
socket.set_timeout(Some(embassy_time::Duration::from_secs(10)));
|
||||
|
||||
info!("Listening on TCP:1234...");
|
||||
if let Err(e) = socket.accept(1234).await {
|
||||
warn!("accept error: {:?}", e);
|
||||
continue;
|
||||
}
|
||||
|
||||
info!("Received connection from {:?}", socket.remote_endpoint());
|
||||
|
||||
loop {
|
||||
let n = match socket.read(&mut buf).await {
|
||||
Ok(0) => {
|
||||
warn!("read EOF");
|
||||
break;
|
||||
}
|
||||
Ok(n) => n,
|
||||
Err(e) => {
|
||||
warn!("read error: {:?}", e);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
info!("rxd {:02x}", &buf[..n]);
|
||||
|
||||
match socket.write_all(&buf[..n]).await {
|
||||
Ok(()) => {}
|
||||
Err(e) => {
|
||||
warn!("write error: {:?}", e);
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue