Compare commits

...

15 Commits

Author SHA1 Message Date
Ryan Summers 0ea7499543
Merge pull request #3 from quartiq/release/0.3.0
Prepping for release
2023-07-03 14:51:20 +02:00
Ryan Summers 237009b628 Reverting repo update 2023-07-03 14:48:02 +02:00
Ryan Summers b1f98c3622 Prepping for release 2023-07-03 13:50:06 +02:00
Ryan Summers 753112e7b0
Merge pull request #1 from quartiq/feature/nal-removal
Removing NAL, reordering dependencies
2023-07-03 13:45:28 +02:00
Ryan Summers 0ecfe84ea0 Fixing smoltcp features 2023-07-03 13:43:12 +02:00
Ryan Summers 207a75358f Merge branch 'master' into feature/nal-removal 2023-07-03 13:39:50 +02:00
Ryan Summers 21ab601b0d
Merge pull request #2 from quartiq/feature/ci
Adding CI, format
2023-07-03 13:37:12 +02:00
Ryan Summers fc43e3b5c5 Fixing example features 2023-07-03 12:32:54 +02:00
Ryan Summers e94d1df1ec Adding dummy commit 2023-07-03 12:28:47 +02:00
Ryan Summers 0fa5c8f81c Enabling features in examples 2023-07-03 12:27:35 +02:00
Ryan Summers ed42d185df Updating example features 2023-07-03 12:26:47 +02:00
Ryan Summers 253a1f6f1d Adding format 2023-07-03 12:25:46 +02:00
Ryan Summers 273cf09ad2 Removing nal module 2023-07-03 12:23:44 +02:00
Ryan Summers fb2fd17277 Removing NAL, reordering dependencies 2023-07-03 12:21:26 +02:00
Harry Ho 512fd154bd nix: Use lib in place of deprecated stdenv.lib 2021-09-02 17:10:03 +08:00
11 changed files with 371 additions and 584 deletions

117
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,117 @@
name: Continuous Integration
on:
push:
branches: [master]
pull_request:
branches: [master]
env:
CARGO_TERM_COLOR: always
jobs:
style:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
components: rustfmt
- name: Style Check
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
clippy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
target: thumbv7em-none-eabihf
override: true
components: clippy
- name: Clippy Check
uses: actions-rs/cargo@v1
with:
command: clippy
args: --all-features
documentation:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
target: thumbv7em-none-eabihf
override: true
- name: Cargo Doc
uses: actions-rs/cargo@v1
with:
command: doc
args: --all-features
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Cargo Audit
uses: actions-rs/audit-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
compile:
runs-on: ubuntu-latest
strategy:
matrix:
toolchain:
- stable
- beta
steps:
- uses: actions/checkout@v2
- name: Install Rust ${{ matrix.toolchain }}
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.toolchain }}
target: thumbv7em-none-eabihf
override: true
- name: Cargo Check
uses: actions-rs/cargo@v1
with:
command: check
args: --verbose --all-features
- name: Cargo Build
uses: actions-rs/cargo@v1
with:
command: build
args: --all-features
- name: Cargo Build [Release]
uses: actions-rs/cargo@v1
with:
command: build
args: --release --all-features
- name: Cargo Build [Examples]
uses: actions-rs/cargo@v1
with:
command: build
args: --examples --all-features

View File

@ -3,7 +3,7 @@ categories = ["embedded", "no-std"]
name = "enc424j600"
description = "Embbeded Rust Ethernet driver for ENC424J600 Ethernet controller with SPI interface"
authors = ["Harry Ho <hh@m-labs.hk>", "Dip Cheung <dc@m-labs.hk>"]
version = "0.2.0"
version = "0.3.0"
keywords = ["ethernet", "eth", "enc424j600", "stm32", "stm32f4xx"]
repository = "https://git.m-labs.hk/M-Labs/ENC424J600"
edition = "2018"
@ -13,46 +13,30 @@ license = "BSD-2-Clause"
volatile-register = "0.2"
aligned = "0.3"
embedded-hal = "0.2"
smoltcp = { version = "0.7.0", default-features = false, features = [ "socket-raw", "proto-ipv4", "proto-ipv6" ], optional = true }
# Dependencies for NAL
embedded-time = { version = "0.10.1", optional = true }
embedded-nal = { version = "0.1.0", optional = true }
heapless = { version = "0.5.6", optional = true }
smoltcp = { version = "0.7.0", default-features = false, features = [ "socket-raw", "proto-ipv4",
"proto-ipv6", "socket-tcp", "ethernet"], optional = true }
cortex-m = {version = "0.5", optional = true }
# Optional dependencies for building examples
stm32f4xx-hal = { version = "0.8", optional = true }
cortex-m = { version = "0.5", optional = true }
cortex-m-rt = { version = "0.6", optional = true }
cortex-m-rtic = { version = "0.5.3", optional = true }
panic-itm = { version = "0.4", optional = true }
log = { version = "0.4", optional = true }
[dev-dependencies]
stm32f4xx-hal = { version = "0.8", features = ["stm32f407", "rt"] }
cortex-m-rt = "0.6"
cortex-m-rtic = "0.5.3"
panic-itm = "0.4"
log = "0.4"
[features]
smoltcp-phy = ["smoltcp"]
nal = [
"embedded-time", "embedded-nal", "heapless",
"smoltcp-phy", "smoltcp/socket-tcp", "smoltcp/ethernet"
]
cortex-m-cpu = ["cortex-m"]
# Example-based features
smoltcp-examples = [
"smoltcp-phy", "smoltcp/socket-tcp", "smoltcp/ethernet"
]
tx_stm32f407 = [
"stm32f4xx-hal/stm32f407", "cortex-m", "cortex-m-rtic", "cortex-m-cpu",
"panic-itm", "log"
]
tcp_stm32f407 = [
"stm32f4xx-hal/stm32f407", "cortex-m", "cortex-m-rt", "cortex-m-rtic", "cortex-m-cpu",
"smoltcp-examples", "panic-itm", "log"]
default = []
[[example]]
name = "tx_stm32f407"
required-features = ["tx_stm32f407"]
required-features = ["smoltcp", "cortex-m-cpu"]
[[example]]
name = "tcp_stm32f407"
required-features = ["tcp_stm32f407"]
required-features = ["smoltcp", "cortex-m-cpu"]
[profile.release]
codegen-units = 1

View File

@ -2,42 +2,31 @@
#![no_main]
extern crate panic_itm;
use cortex_m::{iprintln, iprint};
use cortex_m::{iprint, iprintln};
use embedded_hal::{
digital::v2::OutputPin,
blocking::delay::DelayMs
};
use stm32f4xx_hal::{
rcc::RccExt,
gpio::GpioExt,
time::U32Ext,
stm32::ITM,
delay::Delay,
spi::Spi,
time::Hertz
};
use embedded_hal::{blocking::delay::DelayMs, digital::v2::OutputPin};
use enc424j600::smoltcp_phy;
use smoltcp::wire::{
EthernetAddress, IpAddress, IpCidr, Ipv6Cidr
use stm32f4xx_hal::{
delay::Delay, gpio::GpioExt, rcc::RccExt, spi::Spi, stm32::ITM, time::Hertz, time::U32Ext,
};
use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder, EthernetInterface};
use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer};
use core::str;
use core::fmt::Write;
use core::str;
use smoltcp::iface::{EthernetInterface, EthernetInterfaceBuilder, NeighborCache};
use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer};
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv6Cidr};
/// Timer
use core::cell::RefCell;
use cortex_m::interrupt::Mutex;
use cortex_m_rt::exception;
use smoltcp::time::Instant;
use stm32f4xx_hal::{
rcc::Clocks,
stm32::SYST,
time::MilliSeconds,
timer::{Timer, Event as TimerEvent},
stm32::SYST
timer::{Event as TimerEvent, Timer},
};
use smoltcp::time::Instant;
/// Rate in Hz
const TIMER_RATE: u32 = 20;
/// Interval duration in milliseconds
@ -55,31 +44,34 @@ fn timer_setup(syst: SYST, clocks: Clocks) {
#[exception]
fn SysTick() {
cortex_m::interrupt::free(|cs| {
*TIMER_MS.borrow(cs)
.borrow_mut() += TIMER_DELTA;
*TIMER_MS.borrow(cs).borrow_mut() += TIMER_DELTA;
});
}
/// Obtain current time in milliseconds
pub fn timer_now() -> MilliSeconds {
let ms = cortex_m::interrupt::free(|cs| {
*TIMER_MS.borrow(cs)
.borrow()
});
let ms = cortex_m::interrupt::free(|cs| *TIMER_MS.borrow(cs).borrow());
ms.ms()
}
///
use stm32f4xx_hal::{
stm32::SPI1,
gpio::{
gpioa::{PA5, PA6, PA7, PA4},
Alternate, AF5, Output, PushPull
}
gpioa::{PA4, PA5, PA6, PA7},
Alternate, Output, PushPull, AF5,
},
stm32::SPI1,
};
type SpiEth = enc424j600::Enc424j600<
Spi<SPI1, (PA5<Alternate<AF5>>, PA6<Alternate<AF5>>, PA7<Alternate<AF5>>)>,
PA4<Output<PushPull>>
Spi<
SPI1,
(
PA5<Alternate<AF5>>,
PA6<Alternate<AF5>>,
PA7<Alternate<AF5>>,
),
>,
PA4<Output<PushPull>>,
>;
pub struct NetStorage {
@ -89,19 +81,15 @@ pub struct NetStorage {
static mut NET_STORE: NetStorage = NetStorage {
// Placeholder for the real IP address, which is initialized at runtime.
ip_addrs: [IpCidr::Ipv6(
Ipv6Cidr::SOLICITED_NODE_PREFIX,
)],
ip_addrs: [IpCidr::Ipv6(Ipv6Cidr::SOLICITED_NODE_PREFIX)],
neighbor_cache: [None; 8],
};
#[rtic::app(device = stm32f4xx_hal::stm32, peripherals = true, monotonic = rtic::cyccnt::CYCCNT)]
const APP: () = {
struct Resources {
eth_iface: EthernetInterface<
'static,
smoltcp_phy::SmoltcpDevice<SpiEth>>,
itm: ITM
eth_iface: EthernetInterface<'static, smoltcp_phy::SmoltcpDevice<SpiEth>>,
itm: ITM,
}
#[init()]
@ -113,7 +101,10 @@ const APP: () = {
c.core.DWT.enable_cycle_counter();
c.core.DCB.enable_trace();
let clocks = c.device.RCC.constrain()
let clocks = c
.device
.RCC
.constrain()
.cfgr
.sysclk(168.mhz())
.hclk(168.mhz())
@ -126,8 +117,7 @@ const APP: () = {
let mut itm = c.core.ITM;
let stim0 = &mut itm.stim[0];
iprintln!(stim0,
"Eth TCP Server on STM32-F407 via NIC100/ENC424J600");
iprintln!(stim0, "Eth TCP Server on STM32-F407 via NIC100/ENC424J600");
// NIC100 / ENC424J600 Set-up
let spi1 = c.device.SPI1;
@ -147,13 +137,14 @@ const APP: () = {
let eth_iface = {
let mut spi_eth = {
let spi_eth_port = Spi::spi1(
spi1, (spi1_sck, spi1_miso, spi1_mosi),
spi1,
(spi1_sck, spi1_miso, spi1_mosi),
enc424j600::spi::interfaces::SPI_MODE,
Hertz(enc424j600::spi::interfaces::SPI_CLOCK_FREQ),
clocks);
clocks,
);
SpiEth::new(spi_eth_port, spi1_nss)
.cpu_freq_mhz(168)
SpiEth::new(spi_eth_port, spi1_nss).cpu_freq_mhz(168)
};
// Init controller
@ -175,7 +166,7 @@ const APP: () = {
0 => iprint!(stim0, "MAC Address = {:02x}-", byte),
1..=4 => iprint!(stim0, "{:02x}-", byte),
5 => iprint!(stim0, "{:02x}\n", byte),
_ => ()
_ => (),
};
}
@ -208,10 +199,7 @@ const APP: () = {
timer_setup(delay.free(), clocks);
iprintln!(stim0, "Timer initialized");
init::LateResources {
eth_iface,
itm
}
init::LateResources { eth_iface, itm }
}
#[idle(resources=[eth_iface, itm])]
@ -241,8 +229,11 @@ const APP: () = {
let greet_handle = socket_set.add(greet_socket);
{
let store = unsafe { &mut NET_STORE };
iprintln!(stim0,
"TCP sockets will listen at {}", store.ip_addrs[0].address());
iprintln!(
stim0,
"TCP sockets will listen at {}",
store.ip_addrs[0].address()
);
}
// Copied / modified from:
@ -254,8 +245,7 @@ const APP: () = {
let now = timer_now().0;
let instant = Instant::from_millis(now as i64);
match iface.poll(&mut socket_set, instant) {
Ok(_) => {
},
Ok(_) => {}
Err(e) => {
iprintln!(stim0, "[{}] Poll error: {:?}", instant, e)
}
@ -264,33 +254,40 @@ const APP: () = {
{
let mut socket = socket_set.get::<TcpSocket>(echo_handle);
if !socket.is_open() {
iprintln!(stim0,
"[{}] Listening to port 1234 for echoing, time-out in 10s", instant);
iprintln!(
stim0,
"[{}] Listening to port 1234 for echoing, time-out in 10s",
instant
);
socket.listen(1234).unwrap();
socket.set_timeout(Some(smoltcp::time::Duration::from_millis(10000)));
}
if socket.can_recv() {
iprintln!(stim0,
"[{}] Received packet: {:?}", instant, socket.recv(|buffer| {
(buffer.len(), str::from_utf8(buffer).unwrap())
}));
iprintln!(
stim0,
"[{}] Received packet: {:?}",
instant,
socket.recv(|buffer| { (buffer.len(), str::from_utf8(buffer).unwrap()) })
);
}
}
// Control the "greeting" socket (:4321)
{
let mut socket = socket_set.get::<TcpSocket>(greet_handle);
if !socket.is_open() {
iprintln!(stim0,
iprintln!(
stim0,
"[{}] Listening to port 4321 for greeting, \
please connect to the port", instant);
please connect to the port",
instant
);
socket.listen(4321).unwrap();
}
if socket.can_send() {
let greeting = "Welcome to the server demo for STM32-F407!";
write!(socket, "{}\n", greeting).unwrap();
iprintln!(stim0,
"[{}] Greeting sent, socket closed", instant);
iprintln!(stim0, "[{}] Greeting sent, socket closed", instant);
socket.close();
}
}

View File

@ -2,34 +2,32 @@
#![no_main]
extern crate panic_itm;
use cortex_m::{iprintln, iprint};
use cortex_m::{iprint, iprintln};
use embedded_hal::{
digital::v2::OutputPin,
blocking::delay::DelayMs
};
use stm32f4xx_hal::{
rcc::RccExt,
gpio::GpioExt,
time::U32Ext,
stm32::ITM,
delay::Delay,
spi::Spi,
time::Hertz
};
use embedded_hal::{blocking::delay::DelayMs, digital::v2::OutputPin};
use enc424j600::EthPhy;
use stm32f4xx_hal::{
delay::Delay, gpio::GpioExt, rcc::RccExt, spi::Spi, stm32::ITM, time::Hertz, time::U32Ext,
};
///
use stm32f4xx_hal::{
stm32::SPI1,
gpio::{
gpioa::{PA5, PA6, PA7, PA4},
Alternate, AF5, Output, PushPull
gpioa::{PA4, PA5, PA6, PA7},
Alternate, Output, PushPull, AF5,
},
stm32::SPI1,
};
type SpiEth = enc424j600::Enc424j600<
Spi<SPI1, (PA5<Alternate<AF5>>, PA6<Alternate<AF5>>, PA7<Alternate<AF5>>)>,
PA4<Output<PushPull>>
Spi<
SPI1,
(
PA5<Alternate<AF5>>,
PA6<Alternate<AF5>>,
PA7<Alternate<AF5>>,
),
>,
PA4<Output<PushPull>>,
>;
#[rtic::app(device = stm32f4xx_hal::stm32, peripherals = true, monotonic = rtic::cyccnt::CYCCNT)]
@ -45,7 +43,10 @@ const APP: () = {
c.core.SCB.enable_icache();
c.core.SCB.enable_dcache(&mut c.core.CPUID);
let clocks = c.device.RCC.constrain()
let clocks = c
.device
.RCC
.constrain()
.cfgr
.sysclk(168.mhz())
.hclk(168.mhz())
@ -59,8 +60,7 @@ const APP: () = {
// Init ITM
let mut itm = c.core.ITM;
let stim0 = &mut itm.stim[0];
iprintln!(stim0,
"Eth TX Pinging on STM32-F407 via NIC100/ENC424J600");
iprintln!(stim0, "Eth TX Pinging on STM32-F407 via NIC100/ENC424J600");
// NIC100 / ENC424J600 Set-up
let spi1 = c.device.SPI1;
@ -78,13 +78,14 @@ const APP: () = {
// Create SPI1 for HAL
let mut spi_eth = {
let spi_eth_port = Spi::spi1(
spi1, (spi1_sck, spi1_miso, spi1_mosi),
spi1,
(spi1_sck, spi1_miso, spi1_mosi),
enc424j600::spi::interfaces::SPI_MODE,
Hertz(enc424j600::spi::interfaces::SPI_CLOCK_FREQ),
clocks);
clocks,
);
SpiEth::new(spi_eth_port, spi1_nss)
.cpu_freq_mhz(168)
SpiEth::new(spi_eth_port, spi1_nss).cpu_freq_mhz(168)
};
// Init
@ -106,7 +107,7 @@ const APP: () = {
0 => iprint!(stim0, "MAC Address = {:02x}-", byte),
1..=4 => iprint!(stim0, "{:02x}-", byte),
5 => iprint!(stim0, "{:02x}\n", byte),
_ => ()
_ => (),
};
}
@ -127,20 +128,20 @@ const APP: () = {
let stim0 = &mut c.resources.itm.stim[0];
// Testing Eth TX
let eth_tx_dat: [u8; 64] = [
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x08, 0x60,
0x6e, 0x44, 0x42, 0x95, 0x08, 0x06, 0x00, 0x01,
0x08, 0x00, 0x06, 0x04, 0x00, 0x01, 0x08, 0x60,
0x6e, 0x44, 0x42, 0x95, 0xc0, 0xa8, 0x01, 0x64,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8,
0x01, 0xe7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x69, 0xd0, 0x85, 0x9f
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x08, 0x60, 0x6e, 0x44, 0x42, 0x95, 0x08, 0x06,
0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01, 0x08, 0x60, 0x6e, 0x44, 0x42, 0x95,
0xc0, 0xa8, 0x01, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x01, 0xe7,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x69, 0xd0, 0x85, 0x9f,
];
loop {
let mut eth_tx_packet = enc424j600::tx::TxPacket::new();
eth_tx_packet.update_frame(&eth_tx_dat, 64);
iprint!(stim0,
"Sending packet (len={:}): ", eth_tx_packet.get_frame_length());
iprint!(
stim0,
"Sending packet (len={:}): ",
eth_tx_packet.get_frame_length()
);
for i in 0..20 {
let byte = eth_tx_packet.get_frame_byte(i);
match i {
@ -151,7 +152,7 @@ const APP: () = {
13..=14 | 16..=18 => iprint!(stim0, "{:02x}", byte),
5 | 11 | 15 => iprint!(stim0, "{:02x} ", byte),
19 => iprint!(stim0, "{:02x} ...\n", byte),
_ => ()
_ => (),
};
}
c.resources.spi_eth.send_packet(&eth_tx_packet);

View File

@ -10,7 +10,7 @@ let
];
rustChannel =
lib.rustLib.fromManifestFile rustManifest {
inherit stdenv fetchurl patchelf;
inherit stdenv lib fetchurl patchelf;
};
rust =
rustChannel.rust.override {

View File

@ -2,22 +2,16 @@
pub mod spi;
use embedded_hal::{
blocking::{
spi::Transfer,
delay::DelayUs,
},
blocking::{delay::DelayUs, spi::Transfer},
digital::v2::OutputPin,
};
pub mod rx;
pub mod tx;
#[cfg(feature="smoltcp")]
#[cfg(feature = "smoltcp")]
pub mod smoltcp_phy;
#[cfg(feature="nal")]
pub mod nal;
/// Max raw frame array size
pub const RAW_FRAME_LENGTH_MAX: usize = 1518;
@ -33,7 +27,7 @@ pub enum Error {
SpiPortError,
RegisterError,
// TODO: Better name?
NoRxPacketError
NoRxPacketError,
}
impl From<spi::Error> for Error {
@ -43,15 +37,13 @@ impl From<spi::Error> for Error {
}
/// ENC424J600 controller in SPI mode
pub struct Enc424j600<SPI: Transfer<u8>,
NSS: OutputPin> {
pub struct Enc424j600<SPI: Transfer<u8>, NSS: OutputPin> {
spi_port: spi::SpiPort<SPI, NSS>,
rx_buf: rx::RxBuffer,
tx_buf: tx::TxBuffer,
}
impl <SPI: Transfer<u8>,
NSS: OutputPin> Enc424j600<SPI, NSS> {
impl<SPI: Transfer<u8>, NSS: OutputPin> Enc424j600<SPI, NSS> {
pub fn new(spi: SPI, nss: NSS) -> Self {
Enc424j600 {
spi_port: spi::SpiPort::new(spi, nss),
@ -79,12 +71,14 @@ impl <SPI: Transfer<u8>,
// Verify that EUDAST is 0x1234
let mut eudast = self.spi_port.read_reg_16b(spi::addrs::EUDAST)?;
if eudast != 0x1234 {
return Err(Error::RegisterError)
return Err(Error::RegisterError);
}
// Poll CLKRDY (ESTAT<12>) to check if it is set
loop {
let estat = self.spi_port.read_reg_16b(spi::addrs::ESTAT)?;
if estat & 0x1000 == 0x1000 { break }
if estat & 0x1000 == 0x1000 {
break;
}
}
// Issue system reset - set ETHRST (ECON2<4>) to 1
self.spi_port.send_opcode(spi::opcodes::SETETHRST)?;
@ -92,7 +86,7 @@ impl <SPI: Transfer<u8>,
// Verify that EUDAST is 0x0000
eudast = self.spi_port.read_reg_16b(spi::addrs::EUDAST)?;
if eudast != 0x0000 {
return Err(Error::RegisterError)
return Err(Error::RegisterError);
}
delay.delay_us(256);
Ok(())
@ -100,11 +94,14 @@ impl <SPI: Transfer<u8>,
pub fn init_rxbuf(&mut self) -> Result<(), Error> {
// Set ERXST pointer
self.spi_port.write_reg_16b(spi::addrs::ERXST, self.rx_buf.get_start_addr())?;
self.spi_port
.write_reg_16b(spi::addrs::ERXST, self.rx_buf.get_start_addr())?;
// Set ERXTAIL pointer
self.spi_port.write_reg_16b(spi::addrs::ERXTAIL, self.rx_buf.get_tail_addr())?;
self.spi_port
.write_reg_16b(spi::addrs::ERXTAIL, self.rx_buf.get_tail_addr())?;
// Set MAMXFL to maximum number of bytes in each accepted packet
self.spi_port.write_reg_16b(spi::addrs::MAMXFL, RAW_FRAME_LENGTH_MAX as u16)?;
self.spi_port
.write_reg_16b(spi::addrs::MAMXFL, RAW_FRAME_LENGTH_MAX as u16)?;
// Enable RX - set RXEN (ECON1<0>) to 1
self.spi_port.send_opcode(spi::opcodes::ENABLERX)?;
Ok(())
@ -122,7 +119,10 @@ impl <SPI: Transfer<u8>,
// "To accept all incoming frames regardless of content (Promiscuous mode),
// set the CRCEN, RUNTEN, UCEN, NOTMEEN and MCEN bits."
let erxfcon_lo = self.spi_port.read_reg_8b(spi::addrs::ERXFCON)?;
self.spi_port.write_reg_8b(spi::addrs::ERXFCON, 0b0101_1110 | (erxfcon_lo & 0b1010_0001))?;
self.spi_port.write_reg_8b(
spi::addrs::ERXFCON,
0b0101_1110 | (erxfcon_lo & 0b1010_0001),
)?;
Ok(())
}
@ -148,8 +148,7 @@ impl <SPI: Transfer<u8>,
}
}
impl <SPI: Transfer<u8>,
NSS: OutputPin> EthPhy for Enc424j600<SPI, NSS> {
impl<SPI: Transfer<u8>, NSS: OutputPin> EthPhy for Enc424j600<SPI, NSS> {
/// Receive the next packet and return it
/// Set is_poll to true for returning until PKTIF is set;
/// Set is_poll to false for returning Err when PKTIF is not set
@ -157,17 +156,21 @@ impl <SPI: Transfer<u8>,
// Poll PKTIF (EIR<4>) to check if it is set
loop {
let eir = self.spi_port.read_reg_16b(spi::addrs::EIR)?;
if eir & 0x40 == 0x40 { break }
if eir & 0x40 == 0x40 {
break;
}
if !is_poll {
return Err(Error::NoRxPacketError)
return Err(Error::NoRxPacketError);
}
}
// Set ERXRDPT pointer to next_addr
self.spi_port.write_reg_16b(spi::addrs::ERXRDPT, self.rx_buf.get_next_addr())?;
self.spi_port
.write_reg_16b(spi::addrs::ERXRDPT, self.rx_buf.get_next_addr())?;
// Read 2 bytes to update next_addr
let mut next_addr_buf = [0; 3];
self.spi_port.read_rxdat(&mut next_addr_buf, 2)?;
self.rx_buf.set_next_addr((next_addr_buf[1] as u16) | ((next_addr_buf[2] as u16) << 8));
self.rx_buf
.set_next_addr((next_addr_buf[1] as u16) | ((next_addr_buf[2] as u16) << 8));
// Read 6 bytes to update rsv
let mut rsv_buf = [0; 7];
self.spi_port.read_rxdat(&mut rsv_buf, 6)?;
@ -179,16 +182,19 @@ impl <SPI: Transfer<u8>,
rx_packet.update_frame_length();
// Read frame bytes
let mut frame_buf = [0; RAW_FRAME_LENGTH_MAX];
self.spi_port.read_rxdat(&mut frame_buf, rx_packet.get_frame_length())?;
self.spi_port
.read_rxdat(&mut frame_buf, rx_packet.get_frame_length())?;
rx_packet.copy_frame_from(&frame_buf[1..]);
// Set ERXTAIL pointer to (next_addr - 2)
// * Assume head, tail, next and wrap addresses are word-aligned (even)
// - If next_addr is at least (start_addr+2), then set tail pointer to the word right before next_addr
if self.rx_buf.get_next_addr() > self.rx_buf.get_start_addr() {
self.spi_port.write_reg_16b(spi::addrs::ERXTAIL, self.rx_buf.get_next_addr() - 2)?;
self.spi_port
.write_reg_16b(spi::addrs::ERXTAIL, self.rx_buf.get_next_addr() - 2)?;
// - Otherwise, next_addr will wrap, so set tail pointer to the last word address of RX buffer
} else {
self.spi_port.write_reg_16b(spi::addrs::ERXTAIL, rx::RX_MAX_ADDRESS - 1)?;
self.spi_port
.write_reg_16b(spi::addrs::ERXTAIL, rx::RX_MAX_ADDRESS - 1)?;
}
// Decrement PKTCNT - set PKTDEC (ECON1<8>)
self.spi_port.send_opcode(spi::opcodes::SETPKTDEC)?;
@ -199,29 +205,38 @@ impl <SPI: Transfer<u8>,
/// Send an established packet
fn send_packet(&mut self, packet: &tx::TxPacket) -> Result<(), Error> {
// Set EGPWRPT pointer to next_addr
self.spi_port.write_reg_16b(spi::addrs::EGPWRPT, self.tx_buf.get_next_addr())?;
self.spi_port
.write_reg_16b(spi::addrs::EGPWRPT, self.tx_buf.get_next_addr())?;
// Copy packet data to SRAM Buffer
// 1-byte Opcode is included
let mut txdat_buf: [u8; RAW_FRAME_LENGTH_MAX + 1] = [0; RAW_FRAME_LENGTH_MAX + 1];
packet.write_frame_to(&mut txdat_buf[1..]);
self.spi_port.write_txdat(&mut txdat_buf, packet.get_frame_length())?;
self.spi_port
.write_txdat(&mut txdat_buf, packet.get_frame_length())?;
// Set ETXST to packet start address
self.spi_port.write_reg_16b(spi::addrs::ETXST, self.tx_buf.get_next_addr())?;
self.spi_port
.write_reg_16b(spi::addrs::ETXST, self.tx_buf.get_next_addr())?;
// Set ETXLEN to packet length
self.spi_port.write_reg_16b(spi::addrs::ETXLEN, packet.get_frame_length() as u16)?;
self.spi_port
.write_reg_16b(spi::addrs::ETXLEN, packet.get_frame_length() as u16)?;
// Send packet - set TXRTS (ECON1<1>) to start transmission
self.spi_port.send_opcode(spi::opcodes::SETTXRTS)?;
// Poll TXRTS (ECON1<1>) to check if it is reset
loop {
let econ1_lo = self.spi_port.read_reg_8b(spi::addrs::ECON1)?;
if econ1_lo & 0x02 == 0 { break }
if econ1_lo & 0x02 == 0 {
break;
}
}
// TODO: Read ETXSTAT to understand Ethernet transmission status
// (See: Register 9-2, ENC424J600 Data Sheet)
// Update TX buffer start address
// * Assume TX buffer consumes the entire general-purpose SRAM block
self.tx_buf.set_next_addr((self.tx_buf.get_next_addr() + packet.get_frame_length() as u16) %
self.rx_buf.get_start_addr() - self.tx_buf.get_start_addr());
self.tx_buf.set_next_addr(
(self.tx_buf.get_next_addr() + packet.get_frame_length() as u16)
% self.rx_buf.get_start_addr()
- self.tx_buf.get_start_addr(),
);
Ok(())
}
}

View File

@ -1,314 +0,0 @@
use core::cell::RefCell;
use core::convert::TryInto;
use heapless::{consts, Vec};
use embedded_nal as nal;
use nal::nb;
use smoltcp as net;
use embedded_hal::{
blocking::spi::Transfer,
digital::v2::OutputPin
};
pub use embedded_time as time;
use time::duration::*;
#[derive(Debug)]
pub enum NetworkError {
NoSocket,
ConnectionFailure,
ReadFailure,
WriteFailure,
Unsupported,
TimeFault,
}
pub type NetworkInterface<SPI, NSS> = net::iface::EthernetInterface<
'static,
crate::smoltcp_phy::SmoltcpDevice<
crate::Enc424j600<SPI, NSS>
>,
>;
pub struct NetworkStack<'a, SPI, NSS, IntClock>
where
SPI: 'static + Transfer<u8>,
NSS: 'static + OutputPin,
IntClock: time::Clock<T = u32>,
{
network_interface: RefCell<NetworkInterface<SPI, NSS>>,
sockets: RefCell<net::socket::SocketSet<'a>>,
next_port: RefCell<u16>,
unused_handles: RefCell<Vec<net::socket::SocketHandle, consts::U16>>,
time_ms: RefCell<u32>,
last_update_instant: RefCell<Option<time::Instant<IntClock>>>,
clock: IntClock,
connection_timeout_ms: u32,
}
impl<'a, SPI, NSS, IntClock> NetworkStack<'a, SPI, NSS, IntClock>
where
SPI: Transfer<u8>,
NSS: OutputPin,
IntClock: time::Clock<T = u32>,
{
pub fn new(
interface: NetworkInterface<SPI, NSS>,
sockets: net::socket::SocketSet<'a>,
clock: IntClock,
connection_timeout_ms: u32,
) -> Self {
let mut unused_handles: Vec<net::socket::SocketHandle, consts::U16> = Vec::new();
for socket in sockets.iter() {
unused_handles.push(socket.handle()).unwrap();
}
NetworkStack {
network_interface: RefCell::new(interface),
sockets: RefCell::new(sockets),
next_port: RefCell::new(49152),
unused_handles: RefCell::new(unused_handles),
time_ms: RefCell::new(0),
last_update_instant: RefCell::new(None),
clock,
connection_timeout_ms,
}
}
// Initiate or advance the timer, and return the duration in ms as u32.
fn update(&self) -> Result<u32, NetworkError> {
let mut duration_ms: u32 = 0;
// Check if it is the first time the stack has updated the time itself
let now = match *self.last_update_instant.borrow() {
// If it is the first time, do not advance time
// Simply store the current instant to initiate time updating
None => self.clock.try_now().map_err(|_| NetworkError::TimeFault)?,
// If it was updated before, advance time and update last_update_instant
Some(instant) => {
// Calculate elapsed time
let now = self.clock.try_now().map_err(|_| NetworkError::TimeFault)?;
let mut duration = now.checked_duration_since(&instant);
// Normally, the wrapping clock should produce a valid duration.
// However, if `now` is earlier than `instant` (e.g. because the main
// application cannot get a valid epoch time during initialisation,
// we should still produce a duration that is just 1ms.
if duration.is_none() {
self.time_ms.replace(0);
duration = Some(Milliseconds(1_u32)
.to_generic::<u32>(IntClock::SCALING_FACTOR)
.map_err(|_| NetworkError::TimeFault)?);
}
let duration_ms_time: Milliseconds<u32> = duration.unwrap().try_into()
.map_err(|_| NetworkError::TimeFault)?;
duration_ms = *duration_ms_time.integer();
// Adjust duration into ms (note: decimal point truncated)
self.advance_time(duration_ms);
now
}
};
self.last_update_instant.replace(Some(now));
Ok(duration_ms)
}
fn advance_time(&self, duration_ms: u32) {
let time = self.time_ms.borrow().wrapping_add(duration_ms);
self.time_ms.replace(time);
}
// Poll on the smoltcp interface
fn poll(&self) -> Result<bool, NetworkError> {
match self.network_interface.borrow_mut().poll(
&mut self.sockets.borrow_mut(),
net::time::Instant::from_millis(*self.time_ms.borrow() as u32),
) {
Ok(changed) => Ok(!changed),
Err(_e) => {
Ok(true)
}
}
}
fn get_ephemeral_port(&self) -> u16 {
// Get the next ephemeral port
let current_port = self.next_port.borrow().clone();
let (next, wrap) = self.next_port.borrow().overflowing_add(1);
*self.next_port.borrow_mut() = if wrap { 49152 } else { next };
return current_port;
}
}
impl<'a, SPI, NSS, IntClock> nal::TcpStack for NetworkStack<'a, SPI, NSS, IntClock>
where
SPI: Transfer<u8>,
NSS: OutputPin,
IntClock: time::Clock<T = u32>,
{
type TcpSocket = net::socket::SocketHandle;
type Error = NetworkError;
fn open(&self, _mode: nal::Mode) -> Result<Self::TcpSocket, Self::Error> {
match self.unused_handles.borrow_mut().pop() {
Some(handle) => {
// Abort any active connections on the handle.
let mut sockets = self.sockets.borrow_mut();
let socket: &mut net::socket::TcpSocket = &mut *sockets.get(handle);
socket.abort();
Ok(handle)
}
None => Err(NetworkError::NoSocket),
}
}
fn connect(
&self,
handle: Self::TcpSocket,
remote: nal::SocketAddr,
) -> Result<Self::TcpSocket, Self::Error> {
{
// If the socket has already been connected, ignore the connection
// request silently.
let mut sockets = self.sockets.borrow_mut();
let socket: &mut net::socket::TcpSocket = &mut *sockets.get(handle);
if socket.state() == net::socket::TcpState::Established {
return Ok(handle)
}
}
{
let mut sockets = self.sockets.borrow_mut();
let socket: &mut net::socket::TcpSocket = &mut *sockets.get(handle);
// abort() instead of close() prevents TcpSocket::connect() from
// raising an error
socket.abort();
match remote.ip() {
nal::IpAddr::V4(addr) => {
let address =
net::wire::Ipv4Address::from_bytes(&addr.octets()[..]);
socket
.connect((address, remote.port()), self.get_ephemeral_port())
.map_err(|_| NetworkError::ConnectionFailure)?;
net::wire::IpAddress::Ipv4(address)
},
nal::IpAddr::V6(addr) => {
let address =
net::wire::Ipv6Address::from_parts(&addr.segments()[..]);
socket
.connect((address, remote.port()), self.get_ephemeral_port())
.map_err(|_| NetworkError::ConnectionFailure)?;
net::wire::IpAddress::Ipv6(address)
}
}
};
// Blocking connect
// Loop to wait until the socket is staying established or closed,
// or the connection attempt has timed out.
let mut timeout_ms: u32 = 0;
loop {
{
let mut sockets = self.sockets.borrow_mut();
let socket: &mut net::socket::TcpSocket = &mut *sockets.get(handle);
// TCP state at ESTABLISHED means there is connection, so
// simply return the socket.
if socket.state() == net::socket::TcpState::Established {
return Ok(handle)
}
// TCP state at CLOSED implies that the remote rejected connection;
// In this case, abort the connection, and then return the socket
// for re-connection in the future.
if socket.state() == net::socket::TcpState::Closed {
socket.abort();
// TODO: Return Err(), but would require changes in quartiq/minimq
return Ok(handle)
}
}
// Any TCP states other than CLOSED and ESTABLISHED are considered
// "transient", so this function should keep waiting and let smoltcp poll
// (e.g. for handling echo reqeust/reply) at the same time.
timeout_ms += self.update()?;
self.poll()?;
// Time out, and return the socket for re-connection in the future.
if timeout_ms > self.connection_timeout_ms {
// TODO: Return Err(), but would require changes in quartiq/minimq
return Ok(handle)
}
}
}
fn is_connected(&self, handle: &Self::TcpSocket) -> Result<bool, Self::Error> {
let mut sockets = self.sockets.borrow_mut();
let socket: &mut net::socket::TcpSocket = &mut *sockets.get(*handle);
Ok(socket.state() == net::socket::TcpState::Established)
}
fn write(&self, handle: &mut Self::TcpSocket, buffer: &[u8]) -> nb::Result<usize, Self::Error> {
let mut write_error = false;
let mut non_queued_bytes = &buffer[..];
while non_queued_bytes.len() != 0 {
let result = {
let mut sockets = self.sockets.borrow_mut();
let socket: &mut net::socket::TcpSocket = &mut *sockets.get(*handle);
let result = socket.send_slice(non_queued_bytes);
result
};
match result {
Ok(num_bytes) => {
// If the buffer is completely filled, close the socket and
// return an error
if num_bytes == 0 {
write_error = true;
break;
}
// In case the buffer is filled up, push bytes into ethernet driver
if num_bytes != non_queued_bytes.len() {
self.update()?;
self.poll()?;
}
// Process the unwritten bytes again, if any
non_queued_bytes = &non_queued_bytes[num_bytes..]
}
Err(_) => {
write_error = true;
break;
}
}
}
if write_error {
// Close the socket to push it back to the array, for
// re-opening the socket in the future
self.close(*handle)?;
return Err(nb::Error::Other(NetworkError::WriteFailure))
}
Ok(buffer.len())
}
fn read(
&self,
handle: &mut Self::TcpSocket,
buffer: &mut [u8],
) -> nb::Result<usize, Self::Error> {
// Enqueue received bytes into the TCP socket buffer
self.update()?;
self.poll()?;
{
let mut sockets = self.sockets.borrow_mut();
let socket: &mut net::socket::TcpSocket = &mut *sockets.get(*handle);
let result = socket.recv_slice(buffer);
match result {
Ok(num_bytes) => { return Ok(num_bytes) },
Err(_) => {},
}
}
// Close the socket to push it back to the array, for
// re-opening the socket in the future
self.close(*handle)?;
Err(nb::Error::Other(NetworkError::ReadFailure))
}
fn close(&self, handle: Self::TcpSocket) -> Result<(), Self::Error> {
let mut sockets = self.sockets.borrow_mut();
let socket: &mut net::socket::TcpSocket = &mut *sockets.get(handle);
socket.close();
let mut unused_handles = self.unused_handles.borrow_mut();
if unused_handles.iter().find(|&x| *x == handle).is_none() {
unused_handles.push(handle).unwrap();
}
Ok(())
}
}

View File

@ -13,7 +13,7 @@ pub const RSV_LENGTH: usize = 6;
pub struct RxBuffer {
start_addr: u16,
next_addr: u16,
tail_addr: u16
tail_addr: u16,
}
impl RxBuffer {
@ -21,28 +21,28 @@ impl RxBuffer {
RxBuffer {
start_addr: ERXST_DEFAULT,
next_addr: ERXST_DEFAULT,
tail_addr: ERXTAIL_DEFAULT
tail_addr: ERXTAIL_DEFAULT,
}
}
pub fn set_start_addr(&mut self, addr: u16) {
self.start_addr = addr;
}
pub fn get_start_addr(& self) -> u16{
pub fn get_start_addr(&self) -> u16 {
self.start_addr
}
pub fn set_next_addr(&mut self, addr: u16) {
self.next_addr = addr;
}
pub fn get_next_addr(& self) -> u16 {
pub fn get_next_addr(&self) -> u16 {
self.next_addr
}
pub fn set_tail_addr(&mut self, addr: u16) {
self.tail_addr = addr;
}
pub fn get_tail_addr(& self) -> u16{
pub fn get_tail_addr(&self) -> u16 {
self.tail_addr
}
}
@ -52,7 +52,7 @@ impl RxBuffer {
pub struct RxPacket {
rsv: Rsv,
frame: [u8; RAW_FRAME_LENGTH_MAX],
frame_length: usize
frame_length: usize,
}
impl RxPacket {
@ -60,7 +60,7 @@ impl RxPacket {
RxPacket {
rsv: Rsv::new(),
frame: [0; RAW_FRAME_LENGTH_MAX],
frame_length: 0
frame_length: 0,
}
}
@ -106,14 +106,14 @@ impl RxPacket {
struct Rsv {
raw_rsv: [u8; RSV_LENGTH],
// TODO: Add more definitions
frame_length: u16
frame_length: u16,
}
impl Rsv {
fn new() -> Self {
Rsv {
raw_rsv: [0; RSV_LENGTH],
frame_length: 0_u16
frame_length: 0_u16,
}
}
@ -132,4 +132,4 @@ impl Rsv {
fn get_frame_length(&self) -> u16 {
self.frame_length
}
}
}

View File

@ -1,17 +1,15 @@
use crate::{
EthPhy, tx, RAW_FRAME_LENGTH_MAX
};
use crate::{tx, EthPhy, RAW_FRAME_LENGTH_MAX};
use core::cell;
use smoltcp::{
phy::{Device, DeviceCapabilities, RxToken, TxToken},
time::Instant,
Error
Error,
};
pub struct SmoltcpDevice<E: EthPhy> {
pub eth_phy: cell::RefCell<E>,
rx_packet_buf: [u8; RAW_FRAME_LENGTH_MAX],
tx_packet_buf: [u8; RAW_FRAME_LENGTH_MAX]
tx_packet_buf: [u8; RAW_FRAME_LENGTH_MAX],
}
impl<E: EthPhy> SmoltcpDevice<E> {
@ -19,7 +17,7 @@ impl<E: EthPhy> SmoltcpDevice<E> {
SmoltcpDevice {
eth_phy: cell::RefCell::new(eth_phy),
rx_packet_buf: [0; RAW_FRAME_LENGTH_MAX],
tx_packet_buf: [0; RAW_FRAME_LENGTH_MAX]
tx_packet_buf: [0; RAW_FRAME_LENGTH_MAX],
}
}
}
@ -43,16 +41,16 @@ impl<'a, E: 'a + EthPhy> Device<'a> for SmoltcpDevice<E> {
// Construct a RxToken
let rx_token = EthRxToken {
buf: &mut self.rx_packet_buf,
len: rx_packet.get_frame_length()
len: rx_packet.get_frame_length(),
};
// Construct a blank TxToken
let tx_token = EthTxToken {
buf: &mut self.tx_packet_buf,
dev: self_p
dev: self_p,
};
Some((rx_token, tx_token))
},
Err(_) => None
}
Err(_) => None,
}
}
@ -61,7 +59,7 @@ impl<'a, E: 'a + EthPhy> Device<'a> for SmoltcpDevice<E> {
// Construct a blank TxToken
let tx_token = EthTxToken {
buf: &mut self.tx_packet_buf,
dev: self_p
dev: self_p,
};
Some(tx_token)
}
@ -69,7 +67,7 @@ impl<'a, E: 'a + EthPhy> Device<'a> for SmoltcpDevice<E> {
pub struct EthRxToken<'a> {
buf: &'a mut [u8],
len: usize
len: usize,
}
impl<'a> RxToken for EthRxToken<'a> {
@ -83,7 +81,7 @@ impl<'a> RxToken for EthRxToken<'a> {
pub struct EthTxToken<'a, E: EthPhy> {
buf: &'a mut [u8],
dev: *mut SmoltcpDevice<E>
dev: *mut SmoltcpDevice<E>,
}
impl<'a, E: 'a + EthPhy> TxToken for EthTxToken<'a, E> {
@ -97,12 +95,10 @@ impl<'a, E: 'a + EthPhy> TxToken for EthTxToken<'a, E> {
// Update frame length and write frame bytes
tx_packet.update_frame(&mut self.buf[..len], len);
// Send the packet as raw
let eth_phy = unsafe {
&mut (*self.dev).eth_phy
};
let eth_phy = unsafe { &mut (*self.dev).eth_phy };
match eth_phy.borrow_mut().send_packet(&tx_packet) {
Ok(_) => { result },
Err(_) => Err(Error::Exhausted)
Ok(_) => result,
Err(_) => Err(Error::Exhausted),
}
}
}

View File

@ -1,7 +1,4 @@
use embedded_hal::{
blocking::spi::Transfer,
digital::v2::OutputPin,
};
use embedded_hal::{blocking::spi::Transfer, digital::v2::OutputPin};
pub mod interfaces {
use embedded_hal::spi;
@ -21,48 +18,47 @@ pub mod opcodes {
pub const SETTXRTS: u8 = 0b1101_0100;
pub const ENABLERX: u8 = 0b1110_1000;
/// 3-byte Instructions
pub const WRXRDPT: u8 = 0b0110_0100; // 8-bit opcode followed by data
pub const RRXRDPT: u8 = 0b0110_0110; // 8-bit opcode followed by data
pub const WGPWRPT: u8 = 0b0110_1100; // 8-bit opcode followed by data
pub const RGPWRPT: u8 = 0b0110_1110; // 8-bit opcode followed by data
pub const WRXRDPT: u8 = 0b0110_0100; // 8-bit opcode followed by data
pub const RRXRDPT: u8 = 0b0110_0110; // 8-bit opcode followed by data
pub const WGPWRPT: u8 = 0b0110_1100; // 8-bit opcode followed by data
pub const RGPWRPT: u8 = 0b0110_1110; // 8-bit opcode followed by data
/// N-byte Instructions
pub const RCRU: u8 = 0b0010_0000;
pub const WCRU: u8 = 0b0010_0010;
pub const RRXDATA: u8 = 0b0010_1100; // 8-bit opcode followed by data
pub const WGPDATA: u8 = 0b0010_1010; // 8-bit opcode followed by data
pub const RRXDATA: u8 = 0b0010_1100; // 8-bit opcode followed by data
pub const WGPDATA: u8 = 0b0010_1010; // 8-bit opcode followed by data
}
pub mod addrs {
/// SPI Register Mapping
/// Note: PSP interface use different address mapping
// SPI Init Reset Registers
pub const EUDAST: u8 = 0x16; // 16-bit data
pub const ESTAT: u8 = 0x1a; // 16-bit data
pub const ECON2: u8 = 0x6e; // 16-bit data
//
pub const ERXFCON: u8 = 0x34; // 16-bit data
//
pub const MAADR3: u8 = 0x60; // 16-bit data
pub const MAADR2: u8 = 0x62; // 16-bit data
pub const MAADR1: u8 = 0x64; // 16-bit data
// RX Registers
pub const ERXRDPT: u8 = 0x8a; // 16-bit data
pub const ERXST: u8 = 0x04; // 16-bit data
pub const ERXTAIL: u8 = 0x06; // 16-bit data
pub const EIR: u8 = 0x1c; // 16-bit data
pub const ECON1: u8 = 0x1e; // 16-bit data
pub const MAMXFL: u8 = 0x4a; // 16-bit data
// TX Registers
pub const EGPWRPT: u8 = 0x88; // 16-bit data
pub const ETXST: u8 = 0x00; // 16-bit data
pub const ETXSTAT: u8 = 0x12; // 16-bit data
pub const ETXLEN: u8 = 0x02; // 16-bit data
pub const EUDAST: u8 = 0x16; // 16-bit data
pub const ESTAT: u8 = 0x1a; // 16-bit data
pub const ECON2: u8 = 0x6e; // 16-bit data
//
pub const ERXFCON: u8 = 0x34; // 16-bit data
//
pub const MAADR3: u8 = 0x60; // 16-bit data
pub const MAADR2: u8 = 0x62; // 16-bit data
pub const MAADR1: u8 = 0x64; // 16-bit data
// RX Registers
pub const ERXRDPT: u8 = 0x8a; // 16-bit data
pub const ERXST: u8 = 0x04; // 16-bit data
pub const ERXTAIL: u8 = 0x06; // 16-bit data
pub const EIR: u8 = 0x1c; // 16-bit data
pub const ECON1: u8 = 0x1e; // 16-bit data
pub const MAMXFL: u8 = 0x4a; // 16-bit data
// TX Registers
pub const EGPWRPT: u8 = 0x88; // 16-bit data
pub const ETXST: u8 = 0x00; // 16-bit data
pub const ETXSTAT: u8 = 0x12; // 16-bit data
pub const ETXLEN: u8 = 0x02; // 16-bit data
}
/// Struct for SPI I/O interface on ENC424J600
/// Note: stm32f4xx_hal::spi's pins include: SCK, MISO, MOSI
pub struct SpiPort<SPI: Transfer<u8>,
NSS: OutputPin> {
pub struct SpiPort<SPI: Transfer<u8>, NSS: OutputPin> {
spi: SPI,
nss: NSS,
#[cfg(feature = "cortex-m-cpu")]
@ -71,12 +67,11 @@ pub struct SpiPort<SPI: Transfer<u8>,
pub enum Error {
OpcodeError,
TransferError
TransferError,
}
#[allow(unused_must_use)]
impl <SPI: Transfer<u8>,
NSS: OutputPin> SpiPort<SPI, NSS> {
impl<SPI: Transfer<u8>, NSS: OutputPin> SpiPort<SPI, NSS> {
// TODO: return as Result()
pub fn new(spi: SPI, mut nss: NSS) -> Self {
nss.set_high();
@ -107,10 +102,10 @@ impl <SPI: Transfer<u8>,
// Unless the register can be written with specific opcode,
// use WCRU instruction to write using unbanked (full) address
let mut buf: [u8; 4] = [0; 4];
let mut data_offset = 0; // number of bytes separating
// actual data from opcode
let mut data_offset = 0; // number of bytes separating
// actual data from opcode
match lo_addr {
addrs::ERXRDPT | addrs::EGPWRPT => { }
addrs::ERXRDPT | addrs::EGPWRPT => {}
_ => {
buf[1] = lo_addr;
data_offset = 1;
@ -121,23 +116,21 @@ impl <SPI: Transfer<u8>,
match lo_addr {
addrs::ERXRDPT => opcodes::RRXRDPT,
addrs::EGPWRPT => opcodes::RGPWRPT,
_ => opcodes::RCRU
_ => opcodes::RCRU,
},
2 + data_offset // extra 8-bit lo_addr before data
2 + data_offset, // extra 8-bit lo_addr before data
)?;
Ok(buf[data_offset+1] as u16 | (buf[data_offset+2] as u16) << 8)
Ok(buf[data_offset + 1] as u16 | (buf[data_offset + 2] as u16) << 8)
}
// Currently requires manual slicing (buf[1..]) for the data read back
pub fn read_rxdat<'a>(&mut self, buf: &'a mut [u8], data_length: usize)
-> Result<(), Error> {
pub fn read_rxdat<'a>(&mut self, buf: &'a mut [u8], data_length: usize) -> Result<(), Error> {
self.rw_n(buf, opcodes::RRXDATA, data_length)
}
// Currently requires actual data to be stored in buf[1..] instead of buf[0..]
// TODO: Maybe better naming?
pub fn write_txdat<'a>(&mut self, buf: &'a mut [u8], data_length: usize)
-> Result<(), Error> {
pub fn write_txdat<'a>(&mut self, buf: &'a mut [u8], data_length: usize) -> Result<(), Error> {
self.rw_n(buf, opcodes::WGPDATA, data_length)
}
@ -153,36 +146,35 @@ impl <SPI: Transfer<u8>,
// Unless the register can be written with specific opcode,
// use WCRU instruction to write using unbanked (full) address
let mut buf: [u8; 4] = [0; 4];
let mut data_offset = 0; // number of bytes separating
// actual data from opcode
let mut data_offset = 0; // number of bytes separating
// actual data from opcode
match lo_addr {
addrs::ERXRDPT | addrs::EGPWRPT => { }
addrs::ERXRDPT | addrs::EGPWRPT => {}
_ => {
buf[1] = lo_addr;
data_offset = 1;
}
}
buf[1+data_offset] = data as u8;
buf[2+data_offset] = (data >> 8) as u8;
buf[1 + data_offset] = data as u8;
buf[2 + data_offset] = (data >> 8) as u8;
self.rw_n(
&mut buf,
match lo_addr {
addrs::ERXRDPT => opcodes::WRXRDPT,
addrs::EGPWRPT => opcodes::WGPWRPT,
_ => opcodes::WCRU
_ => opcodes::WCRU,
},
2 + data_offset // extra 8-bit lo_addr before data
2 + data_offset, // extra 8-bit lo_addr before data
)
}
pub fn send_opcode(&mut self, opcode: u8) -> Result<(), Error> {
match opcode {
opcodes::SETETHRST | opcodes::SETPKTDEC |
opcodes::SETTXRTS | opcodes::ENABLERX => {
opcodes::SETETHRST | opcodes::SETPKTDEC | opcodes::SETTXRTS | opcodes::ENABLERX => {
let mut buf: [u8; 1] = [0];
self.rw_n(&mut buf, opcode, 0)
}
_ => Err(Error::OpcodeError)
_ => Err(Error::OpcodeError),
}
}
@ -199,23 +191,22 @@ impl <SPI: Transfer<u8>,
self.nss.set_low();
// >=50ns min. CS_n setup time
#[cfg(feature = "cortex-m-cpu")]
cortex_m::asm::delay((0.05*(self.cpu_freq_mhz+1.)) as u32);
cortex_m::asm::delay((0.05 * (self.cpu_freq_mhz + 1.)) as u32);
// Start writing to SLAVE
buf[0] = opcode;
let result = self.spi.transfer(&mut buf[..data_length+1]);
let result = self.spi.transfer(&mut buf[..data_length + 1]);
match opcode {
opcodes::RCRU | opcodes::WCRU |
opcodes::RRXDATA | opcodes::WGPDATA => {
opcodes::RCRU | opcodes::WCRU | opcodes::RRXDATA | opcodes::WGPDATA => {
// Disable chip select
// >=50ns min. CS_n hold time
#[cfg(feature = "cortex-m-cpu")]
cortex_m::asm::delay((0.05*(self.cpu_freq_mhz+1.)) as u32);
cortex_m::asm::delay((0.05 * (self.cpu_freq_mhz + 1.)) as u32);
self.nss.set_high();
// >=20ns min. CS_n disable time
#[cfg(feature = "cortex-m-cpu")]
cortex_m::asm::delay((0.02*(self.cpu_freq_mhz+1.)) as u32);
cortex_m::asm::delay((0.02 * (self.cpu_freq_mhz + 1.)) as u32);
}
_ => { }
_ => {}
}
match result {
Ok(_) => Ok(()),

View File

@ -6,7 +6,7 @@ pub struct TxBuffer {
start_addr: u16,
// The following two fields are controlled by firmware
next_addr: u16,
tail_addr: u16
tail_addr: u16,
}
impl TxBuffer {
@ -14,28 +14,28 @@ impl TxBuffer {
TxBuffer {
start_addr: 0x0000,
next_addr: 0x0001,
tail_addr: 0x0000
tail_addr: 0x0000,
}
}
pub fn set_start_addr(&mut self, addr: u16) {
self.start_addr = addr;
}
pub fn get_start_addr(& self) -> u16{
pub fn get_start_addr(&self) -> u16 {
self.start_addr
}
pub fn set_next_addr(&mut self, addr: u16) {
self.next_addr = addr;
}
pub fn get_next_addr(& self) -> u16 {
pub fn get_next_addr(&self) -> u16 {
self.next_addr
}
pub fn set_tail_addr(&mut self, addr: u16) {
self.tail_addr = addr;
}
pub fn get_tail_addr(& self) -> u16{
pub fn get_tail_addr(&self) -> u16 {
self.tail_addr
}
}
@ -44,14 +44,14 @@ impl TxBuffer {
/// TODO: Generalise MAC addresses
pub struct TxPacket {
frame: [u8; RAW_FRAME_LENGTH_MAX],
frame_length: usize
frame_length: usize,
}
impl TxPacket {
pub fn new() -> Self {
TxPacket {
frame: [0; RAW_FRAME_LENGTH_MAX],
frame_length: 0
frame_length: 0,
}
}