Adding format

master
Ryan Summers 2023-07-03 12:25:46 +02:00
parent 512fd154bd
commit 253a1f6f1d
9 changed files with 385 additions and 271 deletions

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

@ -0,0 +1,116 @@
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

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

@ -2,20 +2,17 @@
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")]
#[cfg(feature = "nal")]
pub mod nal;
/// Max raw frame array size
@ -33,7 +30,7 @@ pub enum Error {
SpiPortError,
RegisterError,
// TODO: Better name?
NoRxPacketError
NoRxPacketError,
}
impl From<spi::Error> for Error {
@ -43,15 +40,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 +74,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 +89,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 +97,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 +122,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 +151,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 +159,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 +185,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 +208,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,14 +1,11 @@
use core::cell::RefCell;
use core::convert::TryInto;
use heapless::{consts, Vec};
use embedded_hal::{blocking::spi::Transfer, digital::v2::OutputPin};
use embedded_nal as nal;
pub use embedded_time as time;
use heapless::{consts, Vec};
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)]
@ -23,9 +20,7 @@ pub enum NetworkError {
pub type NetworkInterface<SPI, NSS> = net::iface::EthernetInterface<
'static,
crate::smoltcp_phy::SmoltcpDevice<
crate::Enc424j600<SPI, NSS>
>,
crate::smoltcp_phy::SmoltcpDevice<crate::Enc424j600<SPI, NSS>>,
>;
pub struct NetworkStack<'a, SPI, NSS, IntClock>
@ -86,16 +81,20 @@ where
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,
// 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)?);
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()
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)
@ -119,9 +118,7 @@ where
net::time::Instant::from_millis(*self.time_ms.borrow() as u32),
) {
Ok(changed) => Ok(!changed),
Err(_e) => {
Ok(true)
}
Err(_e) => Ok(true),
}
}
@ -165,7 +162,7 @@ where
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)
return Ok(handle);
}
}
@ -177,16 +174,14 @@ where
socket.abort();
match remote.ip() {
nal::IpAddr::V4(addr) => {
let address =
net::wire::Ipv4Address::from_bytes(&addr.octets()[..]);
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()[..]);
let address = net::wire::Ipv6Address::from_parts(&addr.segments()[..]);
socket
.connect((address, remote.port()), self.get_ephemeral_port())
.map_err(|_| NetworkError::ConnectionFailure)?;
@ -205,7 +200,7 @@ where
// TCP state at ESTABLISHED means there is connection, so
// simply return the socket.
if socket.state() == net::socket::TcpState::Established {
return Ok(handle)
return Ok(handle);
}
// TCP state at CLOSED implies that the remote rejected connection;
// In this case, abort the connection, and then return the socket
@ -213,10 +208,10 @@ where
if socket.state() == net::socket::TcpState::Closed {
socket.abort();
// TODO: Return Err(), but would require changes in quartiq/minimq
return Ok(handle)
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.
@ -226,7 +221,7 @@ where
// 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)
return Ok(handle);
}
}
}
@ -273,8 +268,8 @@ where
// 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))
}
return Err(nb::Error::Other(NetworkError::WriteFailure));
}
Ok(buffer.len())
}
@ -291,8 +286,8 @@ where
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(_) => {},
Ok(num_bytes) => return Ok(num_bytes),
Err(_) => {}
}
}
// Close the socket to push it back to the array, for
@ -311,4 +306,4 @@ where
}
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,
}
}