aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2023-08-15 12:34:19 +0000
committerGitHub <[email protected]>2023-08-15 12:34:19 +0000
commit03576b9e83a33466e839dabb8db4ca574654a0ff (patch)
tree8df4203aa5c0407c0d725adf616675220b4f27be
parent0fd9d7400b47f42d1043347cc2982862de7cffc7 (diff)
parentea9f887ee168aab502ab595aa460c7c0910ff6b9 (diff)
Merge pull request #1746 from embassy-rs/enc28j60-v2
wip: enc28j60 driver.
-rw-r--r--.vscode/settings.json18
-rw-r--r--embassy-net-enc28j60/Cargo.toml23
-rw-r--r--embassy-net-enc28j60/README.md19
-rw-r--r--embassy-net-enc28j60/src/bank0.rs69
-rw-r--r--embassy-net-enc28j60/src/bank1.rs84
-rw-r--r--embassy-net-enc28j60/src/bank2.rs86
-rw-r--r--embassy-net-enc28j60/src/bank3.rs53
-rw-r--r--embassy-net-enc28j60/src/common.rs106
-rw-r--r--embassy-net-enc28j60/src/fmt.rs225
-rw-r--r--embassy-net-enc28j60/src/header.rs30
-rw-r--r--embassy-net-enc28j60/src/lib.rs707
-rw-r--r--embassy-net-enc28j60/src/macros.rs89
-rw-r--r--embassy-net-enc28j60/src/phy.rs35
-rw-r--r--embassy-net-enc28j60/src/traits.rs57
-rw-r--r--examples/nrf52840/Cargo.toml4
-rw-r--r--examples/nrf52840/src/bin/ethernet_enc28j60.rs124
16 files changed, 1722 insertions, 7 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 139b432f4..d48f7ba1e 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -6,16 +6,21 @@
6 "rust-analyzer.check.allTargets": false, 6 "rust-analyzer.check.allTargets": false,
7 "rust-analyzer.check.noDefaultFeatures": true, 7 "rust-analyzer.check.noDefaultFeatures": true,
8 "rust-analyzer.cargo.noDefaultFeatures": true, 8 "rust-analyzer.cargo.noDefaultFeatures": true,
9 "rust-analyzer.cargo.target": "thumbv7m-none-eabi", 9 "rust-analyzer.showUnlinkedFileNotification": false,
10 // uncomment the target of your chip.
11 //"rust-analyzer.cargo.target": "thumbv6m-none-eabi",
12 //"rust-analyzer.cargo.target": "thumbv7m-none-eabi",
13 "rust-analyzer.cargo.target": "thumbv7em-none-eabi",
10 //"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", 14 //"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf",
11 "rust-analyzer.cargo.features": [ 15 "rust-analyzer.cargo.features": [
12 ///"nightly", 16 // Uncomment if the example has a "nightly" feature.
17 "nightly",
13 ], 18 ],
14 "rust-analyzer.linkedProjects": [ 19 "rust-analyzer.linkedProjects": [
15 // Declare for the target you wish to develop 20 // Uncomment ONE line for the chip you want to work on.
16 // "embassy-executor/Cargo.toml", 21 // This makes rust-analyzer work on the example crate and all its dependencies.
17 // "embassy-sync/Cargo.toml", 22 "examples/nrf52840/Cargo.toml",
18 "examples/stm32wl/Cargo.toml", 23 // "examples/nrf52840-rtic/Cargo.toml",
19 // "examples/nrf5340/Cargo.toml", 24 // "examples/nrf5340/Cargo.toml",
20 // "examples/nrf-rtos-trace/Cargo.toml", 25 // "examples/nrf-rtos-trace/Cargo.toml",
21 // "examples/rp/Cargo.toml", 26 // "examples/rp/Cargo.toml",
@@ -41,5 +46,4 @@
41 // "examples/stm32wl/Cargo.toml", 46 // "examples/stm32wl/Cargo.toml",
42 // "examples/wasm/Cargo.toml", 47 // "examples/wasm/Cargo.toml",
43 ], 48 ],
44 "rust-analyzer.showUnlinkedFileNotification": false,
45} \ No newline at end of file 49} \ No newline at end of file
diff --git a/embassy-net-enc28j60/Cargo.toml b/embassy-net-enc28j60/Cargo.toml
new file mode 100644
index 000000000..c502ed04b
--- /dev/null
+++ b/embassy-net-enc28j60/Cargo.toml
@@ -0,0 +1,23 @@
1[package]
2name = "embassy-net-enc28j60"
3version = "0.1.0"
4description = "embassy-net driver for the ENC28J60 ethernet chip"
5keywords = ["embedded", "enc28j60", "embassy-net", "embedded-hal-async", "ethernet", "async"]
6categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"]
7license = "MIT OR Apache-2.0"
8edition = "2021"
9
10[dependencies]
11embedded-hal = { version = "1.0.0-alpha.11" }
12embedded-hal-async = { version = "=0.2.0-alpha.2" }
13embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
14embassy-time = { version = "0.1.2", path = "../embassy-time" }
15embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
16
17defmt = { version = "0.3", optional = true }
18log = { version = "0.4.14", optional = true }
19
20[package.metadata.embassy_docs]
21src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-enc28j60-v$VERSION/embassy-net-enc28j60/src/"
22src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-enc28j60/src/"
23target = "thumbv7em-none-eabi" \ No newline at end of file
diff --git a/embassy-net-enc28j60/README.md b/embassy-net-enc28j60/README.md
new file mode 100644
index 000000000..39011ca13
--- /dev/null
+++ b/embassy-net-enc28j60/README.md
@@ -0,0 +1,19 @@
1# `embassy-net-enc28j60`
2
3[`embassy-net`](https://crates.io/crates/embassy-net) integration for the Microchip ENC28J60 Ethernet chip.
4
5Based on [@japaric](https://github.com/japaric)'s [`enc28j60`](https://github.com/japaric/enc28j60) crate.
6
7## Interoperability
8
9This crate can run on any executor.
10
11## License
12
13This work is licensed under either of
14
15- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
16 http://www.apache.org/licenses/LICENSE-2.0)
17- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
18
19at your option.
diff --git a/embassy-net-enc28j60/src/bank0.rs b/embassy-net-enc28j60/src/bank0.rs
new file mode 100644
index 000000000..1c1b3a7f6
--- /dev/null
+++ b/embassy-net-enc28j60/src/bank0.rs
@@ -0,0 +1,69 @@
1#[allow(dead_code)]
2#[derive(Clone, Copy)]
3pub enum Register {
4 ERDPTL = 0x00,
5 ERDPTH = 0x01,
6 EWRPTL = 0x02,
7 EWRPTH = 0x03,
8 ETXSTL = 0x04,
9 ETXSTH = 0x05,
10 ETXNDL = 0x06,
11 ETXNDH = 0x07,
12 ERXSTL = 0x08,
13 ERXSTH = 0x09,
14 ERXNDL = 0x0a,
15 ERXNDH = 0x0b,
16 ERXRDPTL = 0x0c,
17 ERXRDPTH = 0x0d,
18 ERXWRPTL = 0x0e,
19 ERXWRPTH = 0x0f,
20 EDMASTL = 0x10,
21 EDMASTH = 0x11,
22 EDMANDL = 0x12,
23 EDMANDH = 0x13,
24 EDMADSTL = 0x14,
25 EDMADSTH = 0x15,
26 EDMACSL = 0x16,
27 EDMACSH = 0x17,
28}
29
30impl Register {
31 pub(crate) fn addr(&self) -> u8 {
32 *self as u8
33 }
34
35 pub(crate) fn is_eth_register(&self) -> bool {
36 match *self {
37 Register::ERDPTL => true,
38 Register::ERDPTH => true,
39 Register::EWRPTL => true,
40 Register::EWRPTH => true,
41 Register::ETXSTL => true,
42 Register::ETXSTH => true,
43 Register::ETXNDL => true,
44 Register::ETXNDH => true,
45 Register::ERXSTL => true,
46 Register::ERXSTH => true,
47 Register::ERXNDL => true,
48 Register::ERXNDH => true,
49 Register::ERXRDPTL => true,
50 Register::ERXRDPTH => true,
51 Register::ERXWRPTL => true,
52 Register::ERXWRPTH => true,
53 Register::EDMASTL => true,
54 Register::EDMASTH => true,
55 Register::EDMANDL => true,
56 Register::EDMANDH => true,
57 Register::EDMADSTL => true,
58 Register::EDMADSTH => true,
59 Register::EDMACSL => true,
60 Register::EDMACSH => true,
61 }
62 }
63}
64
65impl Into<super::Register> for Register {
66 fn into(self) -> super::Register {
67 super::Register::Bank0(self)
68 }
69}
diff --git a/embassy-net-enc28j60/src/bank1.rs b/embassy-net-enc28j60/src/bank1.rs
new file mode 100644
index 000000000..30560edf6
--- /dev/null
+++ b/embassy-net-enc28j60/src/bank1.rs
@@ -0,0 +1,84 @@
1#[allow(dead_code)]
2#[derive(Clone, Copy)]
3pub enum Register {
4 EHT0 = 0x00,
5 EHT1 = 0x01,
6 EHT2 = 0x02,
7 EHT3 = 0x03,
8 EHT4 = 0x04,
9 EHT5 = 0x05,
10 EHT6 = 0x06,
11 EHT7 = 0x07,
12 EPMM0 = 0x08,
13 EPMM1 = 0x09,
14 EPMM2 = 0x0a,
15 EPMM3 = 0x0b,
16 EPMM4 = 0x0c,
17 EPMM5 = 0x0d,
18 EPMM6 = 0x0e,
19 EPMM7 = 0x0f,
20 EPMCSL = 0x10,
21 EPMCSH = 0x11,
22 EPMOL = 0x14,
23 EPMOH = 0x15,
24 ERXFCON = 0x18,
25 EPKTCNT = 0x19,
26}
27
28impl Register {
29 pub(crate) fn addr(&self) -> u8 {
30 *self as u8
31 }
32
33 pub(crate) fn is_eth_register(&self) -> bool {
34 match *self {
35 Register::EHT0 => true,
36 Register::EHT1 => true,
37 Register::EHT2 => true,
38 Register::EHT3 => true,
39 Register::EHT4 => true,
40 Register::EHT5 => true,
41 Register::EHT6 => true,
42 Register::EHT7 => true,
43 Register::EPMM0 => true,
44 Register::EPMM1 => true,
45 Register::EPMM2 => true,
46 Register::EPMM3 => true,
47 Register::EPMM4 => true,
48 Register::EPMM5 => true,
49 Register::EPMM6 => true,
50 Register::EPMM7 => true,
51 Register::EPMCSL => true,
52 Register::EPMCSH => true,
53 Register::EPMOL => true,
54 Register::EPMOH => true,
55 Register::ERXFCON => true,
56 Register::EPKTCNT => true,
57 }
58 }
59}
60
61impl Into<super::Register> for Register {
62 fn into(self) -> super::Register {
63 super::Register::Bank1(self)
64 }
65}
66
67register!(ERXFCON, 0b1010_0001, u8, {
68 #[doc = "Broadcast Filter Enable bit"]
69 bcen @ 0,
70 #[doc = "Multicast Filter Enable bit"]
71 mcen @ 1,
72 #[doc = "Hash Table Filter Enable bit"]
73 hten @ 2,
74 #[doc = "Magic Packetâ„¢ Filter Enable bit"]
75 mpen @ 3,
76 #[doc = "Pattern Match Filter Enable bit"]
77 pmen @ 4,
78 #[doc = "Post-Filter CRC Check Enable bit"]
79 crcen @ 5,
80 #[doc = "AND/OR Filter Select bit"]
81 andor @ 6,
82 #[doc = "Unicast Filter Enable bit"]
83 ucen @ 7,
84});
diff --git a/embassy-net-enc28j60/src/bank2.rs b/embassy-net-enc28j60/src/bank2.rs
new file mode 100644
index 000000000..74a1d245f
--- /dev/null
+++ b/embassy-net-enc28j60/src/bank2.rs
@@ -0,0 +1,86 @@
1#[allow(dead_code)]
2#[derive(Clone, Copy)]
3pub enum Register {
4 MACON1 = 0x00,
5 MACON3 = 0x02,
6 MACON4 = 0x03,
7 MABBIPG = 0x04,
8 MAIPGL = 0x06,
9 MAIPGH = 0x07,
10 MACLCON1 = 0x08,
11 MACLCON2 = 0x09,
12 MAMXFLL = 0x0a,
13 MAMXFLH = 0x0b,
14 MICMD = 0x12,
15 MIREGADR = 0x14,
16 MIWRL = 0x16,
17 MIWRH = 0x17,
18 MIRDL = 0x18,
19 MIRDH = 0x19,
20}
21
22impl Register {
23 pub(crate) fn addr(&self) -> u8 {
24 *self as u8
25 }
26
27 pub(crate) fn is_eth_register(&self) -> bool {
28 match *self {
29 Register::MACON1 => false,
30 Register::MACON3 => false,
31 Register::MACON4 => false,
32 Register::MABBIPG => false,
33 Register::MAIPGL => false,
34 Register::MAIPGH => false,
35 Register::MACLCON1 => false,
36 Register::MACLCON2 => false,
37 Register::MAMXFLL => false,
38 Register::MAMXFLH => false,
39 Register::MICMD => false,
40 Register::MIREGADR => false,
41 Register::MIWRL => false,
42 Register::MIWRH => false,
43 Register::MIRDL => false,
44 Register::MIRDH => false,
45 }
46 }
47}
48
49impl Into<super::Register> for Register {
50 fn into(self) -> super::Register {
51 super::Register::Bank2(self)
52 }
53}
54
55register!(MACON1, 0, u8, {
56 #[doc = "Enable packets to be received by the MAC"]
57 marxen @ 0,
58 #[doc = "Control frames will be discarded after being processed by the MAC"]
59 passall @ 1,
60 #[doc = "Inhibit transmissions when pause control frames are received"]
61 rxpaus @ 2,
62 #[doc = "Allow the MAC to transmit pause control frames"]
63 txpaus @ 3,
64});
65
66register!(MACON3, 0, u8, {
67 #[doc = "MAC will operate in Full-Duplex mode"]
68 fuldpx @ 0,
69 #[doc = "The type/length field of transmitted and received frames will be checked"]
70 frmlnen @ 1,
71 #[doc = "Frames bigger than MAMXFL will be aborted when transmitted or received"]
72 hfrmen @ 2,
73 #[doc = "No proprietary header is present"]
74 phdren @ 3,
75 #[doc = "MAC will append a valid CRC to all frames transmitted regardless of PADCFG bit"]
76 txcrcen @ 4,
77 #[doc = "All short frames will be zero-padded to 64 bytes and a valid CRC will then be appended"]
78 padcfg @ 5..7,
79});
80
81register!(MICMD, 0, u8, {
82 #[doc = "MII Read Enable bit"]
83 miird @ 0,
84 #[doc = "MII Scan Enable bit"]
85 miiscan @ 1,
86});
diff --git a/embassy-net-enc28j60/src/bank3.rs b/embassy-net-enc28j60/src/bank3.rs
new file mode 100644
index 000000000..4f7eb9406
--- /dev/null
+++ b/embassy-net-enc28j60/src/bank3.rs
@@ -0,0 +1,53 @@
1#[allow(dead_code)]
2#[derive(Clone, Copy)]
3pub enum Register {
4 MAADR5 = 0x00,
5 MAADR6 = 0x01,
6 MAADR3 = 0x02,
7 MAADR4 = 0x03,
8 MAADR1 = 0x04,
9 MAADR2 = 0x05,
10 EBSTSD = 0x06,
11 EBSTCON = 0x07,
12 EBSTCSL = 0x08,
13 EBSTCSH = 0x09,
14 MISTAT = 0x0a,
15 EREVID = 0x12,
16 ECOCON = 0x15,
17 EFLOCON = 0x17,
18 EPAUSL = 0x18,
19 EPAUSH = 0x19,
20}
21
22impl Register {
23 pub(crate) fn addr(&self) -> u8 {
24 *self as u8
25 }
26
27 pub(crate) fn is_eth_register(&self) -> bool {
28 match *self {
29 Register::MAADR5 => false,
30 Register::MAADR6 => false,
31 Register::MAADR3 => false,
32 Register::MAADR4 => false,
33 Register::MAADR1 => false,
34 Register::MAADR2 => false,
35 Register::EBSTSD => true,
36 Register::EBSTCON => true,
37 Register::EBSTCSL => true,
38 Register::EBSTCSH => true,
39 Register::MISTAT => false,
40 Register::EREVID => true,
41 Register::ECOCON => true,
42 Register::EFLOCON => true,
43 Register::EPAUSL => true,
44 Register::EPAUSH => true,
45 }
46 }
47}
48
49impl Into<super::Register> for Register {
50 fn into(self) -> super::Register {
51 super::Register::Bank3(self)
52 }
53}
diff --git a/embassy-net-enc28j60/src/common.rs b/embassy-net-enc28j60/src/common.rs
new file mode 100644
index 000000000..ef339dd2a
--- /dev/null
+++ b/embassy-net-enc28j60/src/common.rs
@@ -0,0 +1,106 @@
1#[allow(dead_code)]
2#[derive(Clone, Copy)]
3pub enum Register {
4 ECON1 = 0x1f,
5 ECON2 = 0x1e,
6 EIE = 0x1b,
7 EIR = 0x1c,
8 ESTAT = 0x1d,
9}
10
11impl Register {
12 pub(crate) fn addr(&self) -> u8 {
13 *self as u8
14 }
15
16 pub(crate) fn is_eth_register(&self) -> bool {
17 match *self {
18 Register::ECON1 => true,
19 Register::ECON2 => true,
20 Register::EIE => true,
21 Register::EIR => true,
22 Register::ESTAT => true,
23 }
24 }
25}
26
27impl Into<super::Register> for Register {
28 fn into(self) -> super::Register {
29 super::Register::Common(self)
30 }
31}
32
33register!(EIE, 0, u8, {
34 #[doc = "Receive Error Interrupt Enable bit"]
35 rxerie @ 0,
36 #[doc = "Transmit Error Interrupt Enable bit"]
37 txerie @ 1,
38 #[doc = "Transmit Enable bit"]
39 txie @ 3,
40 #[doc = "Link Status Change Interrupt Enable bit"]
41 linkie @ 4,
42 #[doc = "DMA Interrupt Enable bit"]
43 dmaie @ 5,
44 #[doc = "Receive Packet Pending Interrupt Enable bit"]
45 pktie @ 6,
46 #[doc = "Global INT Interrupt Enable bit"]
47 intie @ 7,
48});
49
50register!(EIR, 0, u8, {
51 #[doc = "Receive Error Interrupt Flag bit"]
52 rxerif @ 0,
53 #[doc = "Transmit Error Interrupt Flag bit"]
54 txerif @ 1,
55 #[doc = "Transmit Interrupt Flag bit"]
56 txif @ 3,
57 #[doc = "Link Change Interrupt Flag bit"]
58 linkif @ 4,
59 #[doc = "DMA Interrupt Flag bit"]
60 dmaif @ 5,
61 #[doc = "Receive Packet Pending Interrupt Flag bit"]
62 pktif @ 6,
63});
64
65register!(ESTAT, 0, u8, {
66 #[doc = "Clock Ready bit"]
67 clkrdy @ 0,
68 #[doc = "Transmit Abort Error bit"]
69 txabrt @ 1,
70 #[doc = "Receive Busy bit"]
71 rxbusy @ 2,
72 #[doc = "Late Collision Error bit"]
73 latecol @ 4,
74 #[doc = "Ethernet Buffer Error Status bit"]
75 bufer @ 6,
76 #[doc = "INT Interrupt Flag bit"]
77 int @ 7,
78});
79
80register!(ECON2, 0b1000_0000, u8, {
81 #[doc = "Voltage Regulator Power Save Enable bit"]
82 vrps @ 3,
83 #[doc = "Power Save Enable bit"]
84 pwrsv @ 5,
85 #[doc = "Packet Decrement bit"]
86 pktdec @ 6,
87 #[doc = "Automatic Buffer Pointer Increment Enable bit"]
88 autoinc @ 7,
89});
90
91register!(ECON1, 0, u8, {
92 #[doc = "Bank Select bits"]
93 bsel @ 0..1,
94 #[doc = "Receive Enable bi"]
95 rxen @ 2,
96 #[doc = "Transmit Request to Send bit"]
97 txrts @ 3,
98 #[doc = "DMA Checksum Enable bit"]
99 csumen @ 4,
100 #[doc = "DMA Start and Busy Status bit"]
101 dmast @ 5,
102 #[doc = "Receive Logic Reset bit"]
103 rxrst @ 6,
104 #[doc = "Transmit Logic Reset bit"]
105 txrst @ 7,
106});
diff --git a/embassy-net-enc28j60/src/fmt.rs b/embassy-net-enc28j60/src/fmt.rs
new file mode 100644
index 000000000..066970813
--- /dev/null
+++ b/embassy-net-enc28j60/src/fmt.rs
@@ -0,0 +1,225 @@
1#![macro_use]
2#![allow(unused_macros)]
3
4#[cfg(all(feature = "defmt", feature = "log"))]
5compile_error!("You may not enable both `defmt` and `log` features.");
6
7macro_rules! assert {
8 ($($x:tt)*) => {
9 {
10 #[cfg(not(feature = "defmt"))]
11 ::core::assert!($($x)*);
12 #[cfg(feature = "defmt")]
13 ::defmt::assert!($($x)*);
14 }
15 };
16}
17
18macro_rules! assert_eq {
19 ($($x:tt)*) => {
20 {
21 #[cfg(not(feature = "defmt"))]
22 ::core::assert_eq!($($x)*);
23 #[cfg(feature = "defmt")]
24 ::defmt::assert_eq!($($x)*);
25 }
26 };
27}
28
29macro_rules! assert_ne {
30 ($($x:tt)*) => {
31 {
32 #[cfg(not(feature = "defmt"))]
33 ::core::assert_ne!($($x)*);
34 #[cfg(feature = "defmt")]
35 ::defmt::assert_ne!($($x)*);
36 }
37 };
38}
39
40macro_rules! debug_assert {
41 ($($x:tt)*) => {
42 {
43 #[cfg(not(feature = "defmt"))]
44 ::core::debug_assert!($($x)*);
45 #[cfg(feature = "defmt")]
46 ::defmt::debug_assert!($($x)*);
47 }
48 };
49}
50
51macro_rules! debug_assert_eq {
52 ($($x:tt)*) => {
53 {
54 #[cfg(not(feature = "defmt"))]
55 ::core::debug_assert_eq!($($x)*);
56 #[cfg(feature = "defmt")]
57 ::defmt::debug_assert_eq!($($x)*);
58 }
59 };
60}
61
62macro_rules! debug_assert_ne {
63 ($($x:tt)*) => {
64 {
65 #[cfg(not(feature = "defmt"))]
66 ::core::debug_assert_ne!($($x)*);
67 #[cfg(feature = "defmt")]
68 ::defmt::debug_assert_ne!($($x)*);
69 }
70 };
71}
72
73macro_rules! todo {
74 ($($x:tt)*) => {
75 {
76 #[cfg(not(feature = "defmt"))]
77 ::core::todo!($($x)*);
78 #[cfg(feature = "defmt")]
79 ::defmt::todo!($($x)*);
80 }
81 };
82}
83
84macro_rules! unreachable {
85 ($($x:tt)*) => {
86 {
87 #[cfg(not(feature = "defmt"))]
88 ::core::unreachable!($($x)*);
89 #[cfg(feature = "defmt")]
90 ::defmt::unreachable!($($x)*);
91 }
92 };
93}
94
95macro_rules! panic {
96 ($($x:tt)*) => {
97 {
98 #[cfg(not(feature = "defmt"))]
99 ::core::panic!($($x)*);
100 #[cfg(feature = "defmt")]
101 ::defmt::panic!($($x)*);
102 }
103 };
104}
105
106macro_rules! trace {
107 ($s:literal $(, $x:expr)* $(,)?) => {
108 {
109 #[cfg(feature = "log")]
110 ::log::trace!($s $(, $x)*);
111 #[cfg(feature = "defmt")]
112 ::defmt::trace!($s $(, $x)*);
113 #[cfg(not(any(feature = "log", feature="defmt")))]
114 let _ = ($( & $x ),*);
115 }
116 };
117}
118
119macro_rules! debug {
120 ($s:literal $(, $x:expr)* $(,)?) => {
121 {
122 #[cfg(feature = "log")]
123 ::log::debug!($s $(, $x)*);
124 #[cfg(feature = "defmt")]
125 ::defmt::debug!($s $(, $x)*);
126 #[cfg(not(any(feature = "log", feature="defmt")))]
127 let _ = ($( & $x ),*);
128 }
129 };
130}
131
132macro_rules! info {
133 ($s:literal $(, $x:expr)* $(,)?) => {
134 {
135 #[cfg(feature = "log")]
136 ::log::info!($s $(, $x)*);
137 #[cfg(feature = "defmt")]
138 ::defmt::info!($s $(, $x)*);
139 #[cfg(not(any(feature = "log", feature="defmt")))]
140 let _ = ($( & $x ),*);
141 }
142 };
143}
144
145macro_rules! warn {
146 ($s:literal $(, $x:expr)* $(,)?) => {
147 {
148 #[cfg(feature = "log")]
149 ::log::warn!($s $(, $x)*);
150 #[cfg(feature = "defmt")]
151 ::defmt::warn!($s $(, $x)*);
152 #[cfg(not(any(feature = "log", feature="defmt")))]
153 let _ = ($( & $x ),*);
154 }
155 };
156}
157
158macro_rules! error {
159 ($s:literal $(, $x:expr)* $(,)?) => {
160 {
161 #[cfg(feature = "log")]
162 ::log::error!($s $(, $x)*);
163 #[cfg(feature = "defmt")]
164 ::defmt::error!($s $(, $x)*);
165 #[cfg(not(any(feature = "log", feature="defmt")))]
166 let _ = ($( & $x ),*);
167 }
168 };
169}
170
171#[cfg(feature = "defmt")]
172macro_rules! unwrap {
173 ($($x:tt)*) => {
174 ::defmt::unwrap!($($x)*)
175 };
176}
177
178#[cfg(not(feature = "defmt"))]
179macro_rules! unwrap {
180 ($arg:expr) => {
181 match $crate::fmt::Try::into_result($arg) {
182 ::core::result::Result::Ok(t) => t,
183 ::core::result::Result::Err(e) => {
184 ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
185 }
186 }
187 };
188 ($arg:expr, $($msg:expr),+ $(,)? ) => {
189 match $crate::fmt::Try::into_result($arg) {
190 ::core::result::Result::Ok(t) => t,
191 ::core::result::Result::Err(e) => {
192 ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
193 }
194 }
195 }
196}
197
198#[derive(Debug, Copy, Clone, Eq, PartialEq)]
199pub struct NoneError;
200
201pub trait Try {
202 type Ok;
203 type Error;
204 fn into_result(self) -> Result<Self::Ok, Self::Error>;
205}
206
207impl<T> Try for Option<T> {
208 type Ok = T;
209 type Error = NoneError;
210
211 #[inline]
212 fn into_result(self) -> Result<T, NoneError> {
213 self.ok_or(NoneError)
214 }
215}
216
217impl<T, E> Try for Result<T, E> {
218 type Ok = T;
219 type Error = E;
220
221 #[inline]
222 fn into_result(self) -> Self {
223 self
224 }
225}
diff --git a/embassy-net-enc28j60/src/header.rs b/embassy-net-enc28j60/src/header.rs
new file mode 100644
index 000000000..c2d4e468f
--- /dev/null
+++ b/embassy-net-enc28j60/src/header.rs
@@ -0,0 +1,30 @@
1register!(RxStatus, 0, u32, {
2 #[doc = "Indicates length of the received frame"]
3 byte_count @ 0..15,
4 #[doc = "Indicates a packet over 50,000 bit times occurred or that a packet was dropped since the last receive"]
5 long_event @ 16,
6 #[doc = "Indicates that at some time since the last receive, a carrier event was detected"]
7 carrier_event @ 18,
8 #[doc = "Indicates that frame CRC field value does not match the CRC calculated by the MAC"]
9 crc_error @ 20,
10 #[doc = "Indicates that frame length field value in the packet does not match the actual data byte length and specifies a valid length"]
11 length_check_error @ 21,
12 #[doc = "Indicates that frame type/length field was larger than 1500 bytes (type field)"]
13 length_out_of_range @ 22,
14 #[doc = "Indicates that at the packet had a valid CRC and no symbol errors"]
15 received_ok @ 23,
16 #[doc = "Indicates packet received had a valid Multicast address"]
17 multicast @ 24,
18 #[doc = "Indicates packet received had a valid Broadcast address."]
19 broadcast @ 25,
20 #[doc = "Indicates that after the end of this packet, an additional 1 to 7 bits were received"]
21 dribble_nibble @ 26,
22 #[doc = "Current frame was recognized as a control frame for having a valid type/length designating it as a control frame"]
23 receive_control_frame @ 27,
24 #[doc = "Current frame was recognized as a control frame containing a valid pause frame opcode and a valid destination address"]
25 receive_pause_control_frame @ 28,
26 #[doc = "Current frame was recognized as a control frame but it contained an unknown opcode"]
27 receive_unknown_opcode @ 29,
28 #[doc = "Current frame was recognized as a VLAN tagged frame"]
29 receive_vlan_type_detected @ 30,
30});
diff --git a/embassy-net-enc28j60/src/lib.rs b/embassy-net-enc28j60/src/lib.rs
new file mode 100644
index 000000000..09e77bafd
--- /dev/null
+++ b/embassy-net-enc28j60/src/lib.rs
@@ -0,0 +1,707 @@
1#![no_std]
2#![doc = include_str!("../README.md")]
3#![warn(missing_docs)]
4
5// must go first.
6mod fmt;
7
8#[macro_use]
9mod macros;
10mod bank0;
11mod bank1;
12mod bank2;
13mod bank3;
14mod common;
15mod header;
16mod phy;
17mod traits;
18
19use core::cmp;
20use core::convert::TryInto;
21
22use embassy_net_driver::{Capabilities, HardwareAddress, LinkState, Medium};
23use embassy_time::Duration;
24use embedded_hal::digital::OutputPin;
25use embedded_hal::spi::{Operation, SpiDevice};
26use traits::U16Ext;
27
28// Total buffer size (see section 3.2)
29const BUF_SZ: u16 = 8 * 1024;
30
31// Maximum frame length
32const MAX_FRAME_LENGTH: u16 = 1518; // value recommended in the data sheet
33
34// Size of the Frame check sequence (32-bit CRC)
35const CRC_SZ: u16 = 4;
36
37// define the boundaries of the TX and RX buffers
38// to workaround errata #5 we do the opposite of what section 6.1 of the data sheet
39// says: we place the RX buffer at address 0 and the TX buffer after it
40const RXST: u16 = 0x0000;
41const RXND: u16 = 0x19ff;
42const TXST: u16 = 0x1a00;
43const _TXND: u16 = 0x1fff;
44
45const MTU: usize = 1514; // 1500 IP + 14 ethernet header
46
47/// ENC28J60 embassy-net driver
48pub struct Enc28j60<S, O> {
49 mac_addr: [u8; 6],
50
51 spi: S,
52 rst: Option<O>,
53
54 bank: Bank,
55
56 // address of the next packet in buffer memory
57 next_packet: u16,
58}
59
60impl<S, O> Enc28j60<S, O>
61where
62 S: SpiDevice,
63 O: OutputPin,
64{
65 /// Create a new ENC28J60 driver instance.
66 ///
67 /// The RST pin is optional. If None, reset will be done with a SPI
68 /// soft reset command, instead of via the RST pin.
69 pub fn new(spi: S, rst: Option<O>, mac_addr: [u8; 6]) -> Self {
70 let mut res = Self {
71 mac_addr,
72 spi,
73 rst,
74
75 bank: Bank::Bank0,
76 next_packet: RXST,
77 };
78 res.init();
79 res
80 }
81
82 fn init(&mut self) {
83 if let Some(rst) = &mut self.rst {
84 rst.set_low().unwrap();
85 embassy_time::block_for(Duration::from_millis(5));
86 rst.set_high().unwrap();
87 embassy_time::block_for(Duration::from_millis(5));
88 } else {
89 embassy_time::block_for(Duration::from_millis(5));
90 self.soft_reset();
91 embassy_time::block_for(Duration::from_millis(5));
92 }
93
94 debug!(
95 "enc28j60: erevid {=u8:x}",
96 self.read_control_register(bank3::Register::EREVID)
97 );
98 debug!("enc28j60: waiting for clk");
99 while common::ESTAT(self.read_control_register(common::Register::ESTAT)).clkrdy() == 0 {}
100 debug!("enc28j60: clk ok");
101
102 if self.read_control_register(bank3::Register::EREVID) == 0 {
103 panic!("ErevidIsZero");
104 }
105
106 // disable CLKOUT output
107 self.write_control_register(bank3::Register::ECOCON, 0);
108
109 // RX start
110 // "It is recommended that the ERXST Pointer be programmed with an even address"
111 self.write_control_register(bank0::Register::ERXSTL, RXST.low());
112 self.write_control_register(bank0::Register::ERXSTH, RXST.high());
113
114 // RX read pointer
115 // NOTE Errata #14 so we are using an *odd* address here instead of ERXST
116 self.write_control_register(bank0::Register::ERXRDPTL, RXND.low());
117 self.write_control_register(bank0::Register::ERXRDPTH, RXND.high());
118
119 // RX end
120 self.write_control_register(bank0::Register::ERXNDL, RXND.low());
121 self.write_control_register(bank0::Register::ERXNDH, RXND.high());
122
123 // TX start
124 // "It is recommended that an even address be used for ETXST"
125 debug_assert_eq!(TXST % 2, 0);
126 self.write_control_register(bank0::Register::ETXSTL, TXST.low());
127 self.write_control_register(bank0::Register::ETXSTH, TXST.high());
128
129 // TX end is set in `transmit`
130
131 // MAC initialization (see section 6.5)
132 // 1. Set the MARXEN bit in MACON1 to enable the MAC to receive frames.
133 self.write_control_register(
134 bank2::Register::MACON1,
135 bank2::MACON1::default().marxen(1).passall(0).rxpaus(1).txpaus(1).bits(),
136 );
137
138 // 2. Configure the PADCFG, TXCRCEN and FULDPX bits of MACON3.
139 self.write_control_register(
140 bank2::Register::MACON3,
141 bank2::MACON3::default().frmlnen(1).txcrcen(1).padcfg(0b001).bits(),
142 );
143
144 // 4. Program the MAMXFL registers with the maximum frame length to be permitted to be
145 // received or transmitted
146 self.write_control_register(bank2::Register::MAMXFLL, MAX_FRAME_LENGTH.low());
147 self.write_control_register(bank2::Register::MAMXFLH, MAX_FRAME_LENGTH.high());
148
149 // 5. Configure the Back-to-Back Inter-Packet Gap register, MABBIPG.
150 // Use recommended value of 0x12
151 self.write_control_register(bank2::Register::MABBIPG, 0x12);
152
153 // 6. Configure the Non-Back-to-Back Inter-Packet Gap register low byte, MAIPGL.
154 // Use recommended value of 0x12
155 self.write_control_register(bank2::Register::MAIPGL, 0x12);
156 self.write_control_register(bank2::Register::MAIPGH, 0x0c);
157
158 // 9. Program the local MAC address into the MAADR1:MAADR6 registers
159 self.write_control_register(bank3::Register::MAADR1, self.mac_addr[0]);
160 self.write_control_register(bank3::Register::MAADR2, self.mac_addr[1]);
161 self.write_control_register(bank3::Register::MAADR3, self.mac_addr[2]);
162 self.write_control_register(bank3::Register::MAADR4, self.mac_addr[3]);
163 self.write_control_register(bank3::Register::MAADR5, self.mac_addr[4]);
164 self.write_control_register(bank3::Register::MAADR6, self.mac_addr[5]);
165
166 // Set the PHCON2.HDLDIS bit to prevent automatic loopback of the data which is transmitted
167 self.write_phy_register(phy::Register::PHCON2, phy::PHCON2::default().hdldis(1).bits());
168
169 // Globally enable interrupts
170 //self.bit_field_set(common::Register::EIE, common::EIE::mask().intie());
171
172 // Set the per packet control byte; we'll always use the value 0
173 self.write_buffer_memory(Some(TXST), &mut [0]);
174
175 // decrease the packet count to 0
176 while self.read_control_register(bank1::Register::EPKTCNT) != 0 {
177 self.bit_field_set(common::Register::ECON2, common::ECON2::mask().pktdec());
178 }
179
180 // Enable reception
181 self.bit_field_set(common::Register::ECON1, common::ECON1::mask().rxen());
182 }
183
184 /// Flushes the transmit buffer, ensuring all pending transmissions have completed
185 /// NOTE: The returned packet *must* be `read` or `ignore`-d, otherwise this method will always
186 /// return `None` on subsequent invocations
187 pub fn receive<'a>(&mut self, buf: &'a mut [u8]) -> Option<&'a mut [u8]> {
188 if self.pending_packets() == 0 {
189 // Errata #6: we can't rely on PKTIF so we check PKTCNT
190 return None;
191 }
192
193 let curr_packet = self.next_packet;
194
195 // read out the first 6 bytes
196 let mut temp_buf = [0; 6];
197 self.read_buffer_memory(Some(curr_packet), &mut temp_buf);
198
199 // next packet pointer
200 let next_packet = u16::from_parts(temp_buf[0], temp_buf[1]);
201 if next_packet > RXND {
202 panic!("CorruptRxBuffer");
203 }
204
205 // status vector
206 let status = header::RxStatus(u32::from_le_bytes(temp_buf[2..].try_into().unwrap()));
207 let len = status.byte_count() as u16 - CRC_SZ;
208
209 if len > RXND {
210 panic!("CorruptRxBuffer 2");
211 }
212
213 self.read_buffer_memory(None, &mut buf[..len as usize]);
214
215 // update ERXRDPT
216 // due to Errata #14 we must write an odd address to ERXRDPT
217 // we know that ERXST = 0, that ERXND is odd and that next_packet is even
218 let rxrdpt = if self.next_packet < 1 || self.next_packet > RXND + 1 {
219 RXND
220 } else {
221 self.next_packet - 1
222 };
223 // "To move ERXRDPT, the host controller must write to ERXRDPTL first."
224 self.write_control_register(bank0::Register::ERXRDPTL, rxrdpt.low());
225 self.write_control_register(bank0::Register::ERXRDPTH, rxrdpt.high());
226
227 // decrease the packet count
228 self.bit_field_set(common::Register::ECON2, common::ECON2::mask().pktdec());
229
230 self.next_packet = next_packet;
231
232 Some(&mut buf[..len as usize])
233 }
234
235 fn wait_tx_ready(&mut self) {
236 for _ in 0u32..10000 {
237 if common::ECON1(self.read_control_register(common::Register::ECON1)).txrts() == 0 {
238 return;
239 }
240 }
241
242 // work around errata #12 by resetting the transmit logic before every new
243 // transmission
244 self.bit_field_set(common::Register::ECON1, common::ECON1::mask().txrst());
245 self.bit_field_clear(common::Register::ECON1, common::ECON1::mask().txrst());
246 //self.bit_field_clear(common::Register::EIR, {
247 // let mask = common::EIR::mask();
248 // mask.txerif() | mask.txif()
249 //});
250 }
251
252 /// Starts the transmission of `bytes`
253 ///
254 /// It's up to the caller to ensure that `bytes` is a valid Ethernet frame. The interface will
255 /// take care of appending a (4 byte) CRC to the frame and of padding the frame to the minimum
256 /// size allowed by the Ethernet specification (64 bytes, or 46 bytes of payload).
257 ///
258 /// NOTE This method will flush any previous transmission that's in progress
259 ///
260 /// # Panics
261 ///
262 /// If `bytes` length is greater than 1514, the maximum frame length allowed by the interface,
263 /// or greater than the transmit buffer
264 pub fn transmit(&mut self, bytes: &[u8]) {
265 assert!(bytes.len() <= self.mtu() as usize);
266
267 self.wait_tx_ready();
268
269 // NOTE the plus one is to not overwrite the per packet control byte
270 let wrpt = TXST + 1;
271
272 // 1. ETXST was set during initialization
273
274 // 2. write the frame to the IC memory
275 self.write_buffer_memory(Some(wrpt), bytes);
276
277 let txnd = wrpt + bytes.len() as u16 - 1;
278
279 // 3. Set the end address of the transmit buffer
280 self.write_control_register(bank0::Register::ETXNDL, txnd.low());
281 self.write_control_register(bank0::Register::ETXNDH, txnd.high());
282
283 // 4. reset interrupt flag
284 //self.bit_field_clear(common::Register::EIR, common::EIR::mask().txif());
285
286 // 5. start transmission
287 self.bit_field_set(common::Register::ECON1, common::ECON1::mask().txrts());
288
289 // Wait until transmission finishes
290 //while common::ECON1(self.read_control_register(common::Register::ECON1)).txrts() == 1 {}
291
292 /*
293 // read the transmit status vector
294 let mut tx_stat = [0; 7];
295 self.read_buffer_memory(None, &mut tx_stat);
296
297 let stat = common::ESTAT(self.read_control_register(common::Register::ESTAT));
298
299 if stat.txabrt() == 1 {
300 // work around errata #12 by reading the transmit status vector
301 if stat.latecol() == 1 || (tx_stat[2] & (1 << 5)) != 0 {
302 panic!("LateCollision")
303 } else {
304 panic!("TransmitAbort")
305 }
306 }*/
307 }
308
309 /// Get whether the link is up
310 pub fn is_link_up(&mut self) -> bool {
311 let bits = self.read_phy_register(phy::Register::PHSTAT2);
312 phy::PHSTAT2(bits).lstat() == 1
313 }
314
315 /// Returns the interface Maximum Transmission Unit (MTU)
316 ///
317 /// The value returned by this function will never exceed 1514 bytes. The actual value depends
318 /// on the memory assigned to the transmission buffer when initializing the device
319 pub fn mtu(&self) -> u16 {
320 cmp::min(BUF_SZ - RXND - 1, MAX_FRAME_LENGTH - CRC_SZ)
321 }
322
323 /* Miscellaneous */
324 /// Returns the number of packets that have been received but have not been processed yet
325 pub fn pending_packets(&mut self) -> u8 {
326 self.read_control_register(bank1::Register::EPKTCNT)
327 }
328
329 /// Adjusts the receive filter to *accept* these packet types
330 pub fn accept(&mut self, packets: &[Packet]) {
331 let mask = bank1::ERXFCON::mask();
332 let mut val = 0;
333 for packet in packets {
334 match packet {
335 Packet::Broadcast => val |= mask.bcen(),
336 Packet::Multicast => val |= mask.mcen(),
337 Packet::Unicast => val |= mask.ucen(),
338 }
339 }
340
341 self.bit_field_set(bank1::Register::ERXFCON, val)
342 }
343
344 /// Adjusts the receive filter to *ignore* these packet types
345 pub fn ignore(&mut self, packets: &[Packet]) {
346 let mask = bank1::ERXFCON::mask();
347 let mut val = 0;
348 for packet in packets {
349 match packet {
350 Packet::Broadcast => val |= mask.bcen(),
351 Packet::Multicast => val |= mask.mcen(),
352 Packet::Unicast => val |= mask.ucen(),
353 }
354 }
355
356 self.bit_field_clear(bank1::Register::ERXFCON, val)
357 }
358
359 /* Private */
360 /* Read */
361 fn read_control_register<R>(&mut self, register: R) -> u8
362 where
363 R: Into<Register>,
364 {
365 self._read_control_register(register.into())
366 }
367
368 fn _read_control_register(&mut self, register: Register) -> u8 {
369 self.change_bank(register);
370
371 if register.is_eth_register() {
372 let mut buffer = [Instruction::RCR.opcode() | register.addr(), 0];
373 self.spi.transfer_in_place(&mut buffer).unwrap();
374 buffer[1]
375 } else {
376 // MAC, MII regs need a dummy byte.
377 let mut buffer = [Instruction::RCR.opcode() | register.addr(), 0, 0];
378 self.spi.transfer_in_place(&mut buffer).unwrap();
379 buffer[2]
380 }
381 }
382
383 fn read_phy_register(&mut self, register: phy::Register) -> u16 {
384 embassy_time::block_for(Duration::from_millis(1));
385
386 // set PHY register address
387 self.write_control_register(bank2::Register::MIREGADR, register.addr());
388
389 // start read operation
390 self.write_control_register(bank2::Register::MICMD, bank2::MICMD::default().miird(1).bits());
391
392 // wait until the read operation finishes
393 while self.read_control_register(bank3::Register::MISTAT) & 0b1 != 0 {}
394
395 self.write_control_register(bank2::Register::MICMD, bank2::MICMD::default().miird(0).bits());
396
397 let l = self.read_control_register(bank2::Register::MIRDL);
398 let h = self.read_control_register(bank2::Register::MIRDH);
399 (l as u16) | (h as u16) << 8
400 }
401
402 /* Write */
403 fn _write_control_register(&mut self, register: Register, value: u8) {
404 self.change_bank(register);
405
406 let buffer = [Instruction::WCR.opcode() | register.addr(), value];
407 self.spi.write(&buffer).unwrap();
408 }
409
410 fn write_control_register<R>(&mut self, register: R, value: u8)
411 where
412 R: Into<Register>,
413 {
414 self._write_control_register(register.into(), value)
415 }
416
417 fn write_phy_register(&mut self, register: phy::Register, value: u16) {
418 // set PHY register address
419 self.write_control_register(bank2::Register::MIREGADR, register.addr());
420
421 self.write_control_register(bank2::Register::MIWRL, (value & 0xff) as u8);
422 // this starts the write operation
423 self.write_control_register(bank2::Register::MIWRH, (value >> 8) as u8);
424
425 // wait until the write operation finishes
426 while self.read_control_register(bank3::Register::MISTAT) & 0b1 != 0 {}
427 }
428
429 /* RMW */
430 fn modify_control_register<R, F>(&mut self, register: R, f: F)
431 where
432 F: FnOnce(u8) -> u8,
433 R: Into<Register>,
434 {
435 self._modify_control_register(register.into(), f)
436 }
437
438 fn _modify_control_register<F>(&mut self, register: Register, f: F)
439 where
440 F: FnOnce(u8) -> u8,
441 {
442 let r = self._read_control_register(register);
443 self._write_control_register(register, f(r))
444 }
445
446 /* Auxiliary */
447 fn change_bank(&mut self, register: Register) {
448 let bank = register.bank();
449
450 if let Some(bank) = bank {
451 if self.bank == bank {
452 // already on the register bank
453 return;
454 }
455
456 // change bank
457 self.bank = bank;
458 match bank {
459 Bank::Bank0 => self.bit_field_clear(common::Register::ECON1, 0b11),
460 Bank::Bank1 => self.modify_control_register(common::Register::ECON1, |r| (r & !0b11) | 0b01),
461 Bank::Bank2 => self.modify_control_register(common::Register::ECON1, |r| (r & !0b11) | 0b10),
462 Bank::Bank3 => self.bit_field_set(common::Register::ECON1, 0b11),
463 }
464 } else {
465 // common register
466 }
467 }
468
469 /* Primitive operations */
470 fn bit_field_clear<R>(&mut self, register: R, mask: u8)
471 where
472 R: Into<Register>,
473 {
474 self._bit_field_clear(register.into(), mask)
475 }
476
477 fn _bit_field_clear(&mut self, register: Register, mask: u8) {
478 debug_assert!(register.is_eth_register());
479
480 self.change_bank(register);
481
482 self.spi
483 .write(&[Instruction::BFC.opcode() | register.addr(), mask])
484 .unwrap();
485 }
486
487 fn bit_field_set<R>(&mut self, register: R, mask: u8)
488 where
489 R: Into<Register>,
490 {
491 self._bit_field_set(register.into(), mask)
492 }
493
494 fn _bit_field_set(&mut self, register: Register, mask: u8) {
495 debug_assert!(register.is_eth_register());
496
497 self.change_bank(register);
498
499 self.spi
500 .write(&[Instruction::BFS.opcode() | register.addr(), mask])
501 .unwrap();
502 }
503
504 fn read_buffer_memory(&mut self, addr: Option<u16>, buf: &mut [u8]) {
505 if let Some(addr) = addr {
506 self.write_control_register(bank0::Register::ERDPTL, addr.low());
507 self.write_control_register(bank0::Register::ERDPTH, addr.high());
508 }
509
510 self.spi
511 .transaction(&mut [Operation::Write(&[Instruction::RBM.opcode()]), Operation::Read(buf)])
512 .unwrap();
513 }
514
515 fn soft_reset(&mut self) {
516 self.spi.write(&[Instruction::SRC.opcode()]).unwrap();
517 }
518
519 fn write_buffer_memory(&mut self, addr: Option<u16>, buffer: &[u8]) {
520 if let Some(addr) = addr {
521 self.write_control_register(bank0::Register::EWRPTL, addr.low());
522 self.write_control_register(bank0::Register::EWRPTH, addr.high());
523 }
524
525 self.spi
526 .transaction(&mut [Operation::Write(&[Instruction::WBM.opcode()]), Operation::Write(buffer)])
527 .unwrap();
528 }
529}
530
531#[derive(Clone, Copy, PartialEq)]
532enum Bank {
533 Bank0,
534 Bank1,
535 Bank2,
536 Bank3,
537}
538
539#[derive(Clone, Copy)]
540enum Instruction {
541 /// Read Control Register
542 RCR = 0b000_00000,
543 /// Read Buffer Memory
544 RBM = 0b001_11010,
545 /// Write Control Register
546 WCR = 0b010_00000,
547 /// Write Buffer Memory
548 WBM = 0b011_11010,
549 /// Bit Field Set
550 BFS = 0b100_00000,
551 /// Bit Field Clear
552 BFC = 0b101_00000,
553 /// System Reset Command
554 SRC = 0b111_11111,
555}
556
557impl Instruction {
558 fn opcode(&self) -> u8 {
559 *self as u8
560 }
561}
562
563#[derive(Clone, Copy)]
564enum Register {
565 Bank0(bank0::Register),
566 Bank1(bank1::Register),
567 Bank2(bank2::Register),
568 Bank3(bank3::Register),
569 Common(common::Register),
570}
571
572impl Register {
573 fn addr(&self) -> u8 {
574 match *self {
575 Register::Bank0(r) => r.addr(),
576 Register::Bank1(r) => r.addr(),
577 Register::Bank2(r) => r.addr(),
578 Register::Bank3(r) => r.addr(),
579 Register::Common(r) => r.addr(),
580 }
581 }
582
583 fn bank(&self) -> Option<Bank> {
584 Some(match *self {
585 Register::Bank0(_) => Bank::Bank0,
586 Register::Bank1(_) => Bank::Bank1,
587 Register::Bank2(_) => Bank::Bank2,
588 Register::Bank3(_) => Bank::Bank3,
589 Register::Common(_) => return None,
590 })
591 }
592
593 fn is_eth_register(&self) -> bool {
594 match *self {
595 Register::Bank0(r) => r.is_eth_register(),
596 Register::Bank1(r) => r.is_eth_register(),
597 Register::Bank2(r) => r.is_eth_register(),
598 Register::Bank3(r) => r.is_eth_register(),
599 Register::Common(r) => r.is_eth_register(),
600 }
601 }
602}
603
604/// Packet type, used to configure receive filters
605#[non_exhaustive]
606#[derive(Clone, Copy, Eq, PartialEq)]
607pub enum Packet {
608 /// Broadcast packets
609 Broadcast,
610 /// Multicast packets
611 Multicast,
612 /// Unicast packets
613 Unicast,
614}
615
616static mut TX_BUF: [u8; MTU] = [0; MTU];
617static mut RX_BUF: [u8; MTU] = [0; MTU];
618
619impl<S, O> embassy_net_driver::Driver for Enc28j60<S, O>
620where
621 S: SpiDevice,
622 O: OutputPin,
623{
624 type RxToken<'a> = RxToken<'a>
625 where
626 Self: 'a;
627
628 type TxToken<'a> = TxToken<'a, S, O>
629 where
630 Self: 'a;
631
632 fn receive(&mut self, cx: &mut core::task::Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
633 let rx_buf = unsafe { &mut RX_BUF };
634 let tx_buf = unsafe { &mut TX_BUF };
635 if let Some(pkt) = self.receive(rx_buf) {
636 let n = pkt.len();
637 Some((RxToken { buf: &mut pkt[..n] }, TxToken { buf: tx_buf, eth: self }))
638 } else {
639 cx.waker().wake_by_ref();
640 None
641 }
642 }
643
644 fn transmit(&mut self, _cx: &mut core::task::Context) -> Option<Self::TxToken<'_>> {
645 let tx_buf = unsafe { &mut TX_BUF };
646 Some(TxToken { buf: tx_buf, eth: self })
647 }
648
649 fn link_state(&mut self, cx: &mut core::task::Context) -> LinkState {
650 cx.waker().wake_by_ref();
651 match self.is_link_up() {
652 true => LinkState::Up,
653 false => LinkState::Down,
654 }
655 }
656
657 fn capabilities(&self) -> Capabilities {
658 let mut caps = Capabilities::default();
659 caps.max_transmission_unit = MTU;
660 caps.medium = Medium::Ethernet;
661 caps
662 }
663
664 fn hardware_address(&self) -> HardwareAddress {
665 HardwareAddress::Ethernet(self.mac_addr)
666 }
667}
668
669/// embassy-net RX token.
670pub struct RxToken<'a> {
671 buf: &'a mut [u8],
672}
673
674impl<'a> embassy_net_driver::RxToken for RxToken<'a> {
675 fn consume<R, F>(self, f: F) -> R
676 where
677 F: FnOnce(&mut [u8]) -> R,
678 {
679 f(self.buf)
680 }
681}
682
683/// embassy-net TX token.
684pub struct TxToken<'a, S, O>
685where
686 S: SpiDevice,
687 O: OutputPin,
688{
689 eth: &'a mut Enc28j60<S, O>,
690 buf: &'a mut [u8],
691}
692
693impl<'a, S, O> embassy_net_driver::TxToken for TxToken<'a, S, O>
694where
695 S: SpiDevice,
696 O: OutputPin,
697{
698 fn consume<R, F>(self, len: usize, f: F) -> R
699 where
700 F: FnOnce(&mut [u8]) -> R,
701 {
702 assert!(len <= self.buf.len());
703 let r = f(&mut self.buf[..len]);
704 self.eth.transmit(&self.buf[..len]);
705 r
706 }
707}
diff --git a/embassy-net-enc28j60/src/macros.rs b/embassy-net-enc28j60/src/macros.rs
new file mode 100644
index 000000000..8d0649572
--- /dev/null
+++ b/embassy-net-enc28j60/src/macros.rs
@@ -0,0 +1,89 @@
1macro_rules! register {
2 ($REGISTER:ident, $reset_value:expr, $uxx:ty, {
3 $(#[$($attr:tt)*] $bitfield:ident @ $range:expr,)+
4 }) => {
5 #[derive(Clone, Copy)]
6 pub(crate) struct $REGISTER<MODE> {
7 bits: $uxx,
8 _mode: ::core::marker::PhantomData<MODE>,
9 }
10
11 impl $REGISTER<super::traits::Mask> {
12 #[allow(dead_code)]
13 pub(crate) fn mask() -> $REGISTER<super::traits::Mask> {
14 $REGISTER { bits: 0, _mode: ::core::marker::PhantomData }
15 }
16
17 $(
18 #[allow(dead_code)]
19 pub(crate) fn $bitfield(&self) -> $uxx {
20 use super::traits::OffsetSize;
21
22 let size = $range.size();
23 let offset = $range.offset();
24 ((1 << size) - 1) << offset
25 }
26 )+
27 }
28
29 impl ::core::default::Default for $REGISTER<super::traits::W> {
30 fn default() -> Self {
31 $REGISTER { bits: $reset_value, _mode: ::core::marker::PhantomData }
32 }
33 }
34
35 #[allow(non_snake_case)]
36 #[allow(dead_code)]
37 pub(crate) fn $REGISTER(bits: $uxx) -> $REGISTER<super::traits::R> {
38 $REGISTER { bits, _mode: ::core::marker::PhantomData }
39 }
40
41 impl $REGISTER<super::traits::R> {
42 #[allow(dead_code)]
43 pub(crate) fn modify(self) -> $REGISTER<super::traits::W> {
44 $REGISTER { bits: self.bits, _mode: ::core::marker::PhantomData }
45 }
46
47 $(
48 #[$($attr)*]
49 #[allow(dead_code)]
50 pub(crate) fn $bitfield(&self) -> $uxx {
51 use super::traits::OffsetSize;
52
53 let offset = $range.offset();
54 let size = $range.size();
55 let mask = (1 << size) - 1;
56
57 (self.bits >> offset) & mask
58 }
59 )+
60 }
61
62 impl $REGISTER<super::traits::W> {
63 #[allow(dead_code)]
64 pub(crate) fn bits(self) -> $uxx {
65 self.bits
66 }
67
68 $(
69 #[$($attr)*]
70 #[allow(dead_code)]
71 pub(crate) fn $bitfield(&mut self, mut bits: $uxx) -> &mut Self {
72 use super::traits::OffsetSize;
73
74 let offset = $range.offset();
75 let size = $range.size();
76 let mask = (1 << size) - 1;
77
78 debug_assert!(bits <= mask);
79 bits &= mask;
80
81 self.bits &= !(mask << offset);
82 self.bits |= bits << offset;
83
84 self
85 }
86 )+
87 }
88 }
89}
diff --git a/embassy-net-enc28j60/src/phy.rs b/embassy-net-enc28j60/src/phy.rs
new file mode 100644
index 000000000..89144ada3
--- /dev/null
+++ b/embassy-net-enc28j60/src/phy.rs
@@ -0,0 +1,35 @@
1#[allow(dead_code)]
2#[derive(Clone, Copy)]
3pub enum Register {
4 PHCON1 = 0x00,
5 PHSTAT1 = 0x01,
6 PHID1 = 0x02,
7 PHID2 = 0x03,
8 PHCON2 = 0x10,
9 PHSTAT2 = 0x11,
10 PHIE = 0x12,
11 PHIR = 0x13,
12 PHLCON = 0x14,
13}
14
15impl Register {
16 pub(crate) fn addr(&self) -> u8 {
17 *self as u8
18 }
19}
20
21register!(PHCON2, 0, u16, {
22 #[doc = "PHY Half-Duplex Loopback Disable bit"]
23 hdldis @ 8,
24 #[doc = "Jabber Correction Disable bit"]
25 jabber @ 10,
26 #[doc = "Twisted-Pair Transmitter Disable bit"]
27 txdis @ 13,
28 #[doc = "PHY Force Linkup bit"]
29 frclnk @ 14,
30});
31
32register!(PHSTAT2, 0, u16, {
33 #[doc = "Link Status bit"]
34 lstat @ 10,
35});
diff --git a/embassy-net-enc28j60/src/traits.rs b/embassy-net-enc28j60/src/traits.rs
new file mode 100644
index 000000000..08f94045a
--- /dev/null
+++ b/embassy-net-enc28j60/src/traits.rs
@@ -0,0 +1,57 @@
1use core::ops::Range;
2
3pub(crate) trait OffsetSize {
4 fn offset(self) -> u8;
5 fn size(self) -> u8;
6}
7
8impl OffsetSize for u8 {
9 fn offset(self) -> u8 {
10 self
11 }
12
13 fn size(self) -> u8 {
14 1
15 }
16}
17
18impl OffsetSize for Range<u8> {
19 fn offset(self) -> u8 {
20 self.start
21 }
22
23 fn size(self) -> u8 {
24 self.end - self.start
25 }
26}
27
28pub(crate) trait U16Ext {
29 fn from_parts(low: u8, high: u8) -> Self;
30
31 fn low(self) -> u8;
32
33 fn high(self) -> u8;
34}
35
36impl U16Ext for u16 {
37 fn from_parts(low: u8, high: u8) -> u16 {
38 ((high as u16) << 8) + low as u16
39 }
40
41 fn low(self) -> u8 {
42 (self & 0xff) as u8
43 }
44
45 fn high(self) -> u8 {
46 (self >> 8) as u8
47 }
48}
49
50#[derive(Clone, Copy)]
51pub struct Mask;
52
53#[derive(Clone, Copy)]
54pub struct R;
55
56#[derive(Clone, Copy)]
57pub struct W;
diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml
index 15fe22d3a..8c6f6bccf 100644
--- a/examples/nrf52840/Cargo.toml
+++ b/examples/nrf52840/Cargo.toml
@@ -12,6 +12,7 @@ nightly = [
12 "embassy-nrf/nightly", 12 "embassy-nrf/nightly",
13 "embassy-net/nightly", 13 "embassy-net/nightly",
14 "embassy-net-esp-hosted", 14 "embassy-net-esp-hosted",
15 "embassy-net-enc28j60",
15 "embassy-nrf/unstable-traits", 16 "embassy-nrf/unstable-traits",
16 "embassy-time/nightly", 17 "embassy-time/nightly",
17 "embassy-time/unstable-traits", 18 "embassy-time/unstable-traits",
@@ -40,6 +41,7 @@ lora-phy = { version = "1", optional = true }
40lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } 41lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true }
41lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true } 42lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true }
42embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"], optional = true } 43embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"], optional = true }
44embassy-net-enc28j60 = { version = "0.1.0", path = "../../embassy-net-enc28j60", features = ["defmt"], optional = true }
43 45
44defmt = "0.3" 46defmt = "0.3"
45defmt-rtt = "0.4" 47defmt-rtt = "0.4"
@@ -54,7 +56,9 @@ rand = { version = "0.8.4", default-features = false }
54embedded-storage = "0.3.0" 56embedded-storage = "0.3.0"
55usbd-hid = "0.6.0" 57usbd-hid = "0.6.0"
56serde = { version = "1.0.136", default-features = false } 58serde = { version = "1.0.136", default-features = false }
59embedded-hal = { version = "1.0.0-alpha.11" }
57embedded-hal-async = { version = "0.2.0-alpha.2", optional = true } 60embedded-hal-async = { version = "0.2.0-alpha.2", optional = true }
61embedded-hal-bus = { version = "0.1.0-alpha.3" }
58num-integer = { version = "0.1.45", default-features = false } 62num-integer = { version = "0.1.45", default-features = false }
59microfft = "0.5.0" 63microfft = "0.5.0"
60 64
diff --git a/examples/nrf52840/src/bin/ethernet_enc28j60.rs b/examples/nrf52840/src/bin/ethernet_enc28j60.rs
new file mode 100644
index 000000000..d1b796fab
--- /dev/null
+++ b/examples/nrf52840/src/bin/ethernet_enc28j60.rs
@@ -0,0 +1,124 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_net::tcp::TcpSocket;
8use embassy_net::{Stack, StackResources};
9use embassy_net_enc28j60::Enc28j60;
10use embassy_nrf::gpio::{Level, Output, OutputDrive};
11use embassy_nrf::rng::Rng;
12use embassy_nrf::spim::Spim;
13use embassy_nrf::{bind_interrupts, peripherals, spim};
14use embassy_time::Delay;
15use embedded_hal_bus::spi::ExclusiveDevice;
16use embedded_io_async::Write;
17use static_cell::make_static;
18use {defmt_rtt as _, panic_probe as _};
19
20bind_interrupts!(struct Irqs {
21 SPIM3 => spim::InterruptHandler<peripherals::SPI3>;
22 RNG => embassy_nrf::rng::InterruptHandler<peripherals::RNG>;
23});
24
25#[embassy_executor::task]
26async fn net_task(
27 stack: &'static Stack<
28 Enc28j60<
29 ExclusiveDevice<Spim<'static, peripherals::SPI3>, Output<'static, peripherals::P0_15>, Delay>,
30 Output<'static, peripherals::P0_13>,
31 >,
32 >,
33) -> ! {
34 stack.run().await
35}
36
37#[embassy_executor::main]
38async fn main(spawner: Spawner) {
39 let p = embassy_nrf::init(Default::default());
40 info!("running!");
41
42 let eth_sck = p.P0_20;
43 let eth_mosi = p.P0_22;
44 let eth_miso = p.P0_24;
45 let eth_cs = p.P0_15;
46 let eth_rst = p.P0_13;
47 let _eth_irq = p.P0_12;
48
49 let mut config = spim::Config::default();
50 config.frequency = spim::Frequency::M16;
51 let spi = spim::Spim::new(p.SPI3, Irqs, eth_sck, eth_miso, eth_mosi, config);
52 let cs = Output::new(eth_cs, Level::High, OutputDrive::Standard);
53 let spi = ExclusiveDevice::new(spi, cs, Delay);
54
55 let rst = Output::new(eth_rst, Level::High, OutputDrive::Standard);
56 let mac_addr = [2, 3, 4, 5, 6, 7];
57 let device = Enc28j60::new(spi, Some(rst), mac_addr);
58
59 let config = embassy_net::Config::dhcpv4(Default::default());
60 // let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 {
61 // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),
62 // dns_servers: Vec::new(),
63 // gateway: Some(Ipv4Address::new(10, 42, 0, 1)),
64 // });
65
66 // Generate random seed
67 let mut rng = Rng::new(p.RNG, Irqs);
68 let mut seed = [0; 8];
69 rng.blocking_fill_bytes(&mut seed);
70 let seed = u64::from_le_bytes(seed);
71
72 // Init network stack
73 let stack = &*make_static!(Stack::new(
74 device,
75 config,
76 make_static!(StackResources::<2>::new()),
77 seed
78 ));
79
80 unwrap!(spawner.spawn(net_task(stack)));
81
82 // And now we can use it!
83
84 let mut rx_buffer = [0; 4096];
85 let mut tx_buffer = [0; 4096];
86 let mut buf = [0; 4096];
87
88 loop {
89 let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
90 socket.set_timeout(Some(embassy_time::Duration::from_secs(10)));
91
92 info!("Listening on TCP:1234...");
93 if let Err(e) = socket.accept(1234).await {
94 warn!("accept error: {:?}", e);
95 continue;
96 }
97
98 info!("Received connection from {:?}", socket.remote_endpoint());
99
100 loop {
101 let n = match socket.read(&mut buf).await {
102 Ok(0) => {
103 warn!("read EOF");
104 break;
105 }
106 Ok(n) => n,
107 Err(e) => {
108 warn!("read error: {:?}", e);
109 break;
110 }
111 };
112
113 info!("rxd {:02x}", &buf[..n]);
114
115 match socket.write_all(&buf[..n]).await {
116 Ok(()) => {}
117 Err(e) => {
118 warn!("write error: {:?}", e);
119 break;
120 }
121 };
122 }
123 }
124}