Merge pull request #2 from quartiq/feature/ci

Adding CI, format
This commit is contained in:
Ryan Summers 2023-07-03 13:37:12 +02:00 committed by GitHub
commit 21ab601b0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 389 additions and 274 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

@ -38,12 +38,12 @@ smoltcp-examples = [
"smoltcp-phy", "smoltcp/socket-tcp", "smoltcp/ethernet"
]
tx_stm32f407 = [
"stm32f4xx-hal/stm32f407", "cortex-m", "cortex-m-rtic", "cortex-m-cpu",
"stm32f4xx-hal/stm32f407", "stm32f4xx-hal/rt", "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"]
"stm32f4xx-hal/stm32f407", "stm32f4xx-hal/rt", "cortex-m", "cortex-m-rt", "cortex-m-rtic",
"cortex-m-cpu", "smoltcp-examples", "panic-itm", "log"]
default = []
[[example]]

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,10 +2,7 @@
pub mod spi;
use embedded_hal::{
blocking::{
spi::Transfer,
delay::DelayUs,
},
blocking::{delay::DelayUs, spi::Transfer},
digital::v2::OutputPin,
};
@ -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>
@ -91,11 +86,15 @@ where
// we should still produce a duration that is just 1ms.
if duration.is_none() {
self.time_ms.replace(0);
duration = Some(Milliseconds(1_u32)
duration = Some(
Milliseconds(1_u32)
.to_generic::<u32>(IntClock::SCALING_FACTOR)
.map_err(|_| NetworkError::TimeFault)?);
.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,7 +208,7 @@ 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);
}
}
@ -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,7 +268,7 @@ 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

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,7 +21,7 @@ impl RxBuffer {
RxBuffer {
start_addr: ERXST_DEFAULT,
next_addr: ERXST_DEFAULT,
tail_addr: ERXTAIL_DEFAULT
tail_addr: ERXTAIL_DEFAULT,
}
}
@ -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,
}
}

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;
@ -61,8 +58,7 @@ pub mod addrs {
/// 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();
@ -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)
}
// 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)
}
@ -169,20 +162,19 @@ impl <SPI: Transfer<u8>,
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),
}
}
@ -204,8 +196,7 @@ impl <SPI: Transfer<u8>,
buf[0] = opcode;
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")]

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,7 +14,7 @@ impl TxBuffer {
TxBuffer {
start_addr: 0x0000,
next_addr: 0x0001,
tail_addr: 0x0000
tail_addr: 0x0000,
}
}
@ -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,
}
}