Use RTIC framework on the examples

* tcp_stm32f407 no longer obtains IP address from environment variables.
pull/3/head
Harry Ho 2020-12-29 11:47:02 +08:00
parent 25e682763c
commit 362cf3c411
6 changed files with 451 additions and 321 deletions

93
Cargo.lock generated
View File

@ -26,6 +26,12 @@ dependencies = [
"stable_deref_trait",
]
[[package]]
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "bare-metal"
version = "0.2.5"
@ -106,6 +112,32 @@ dependencies = [
"syn",
]
[[package]]
name = "cortex-m-rtic"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b30efcb6b7920d9016182c485687f0012487032a14c415d2fce6e9862ef8260e"
dependencies = [
"cortex-m 0.6.2",
"cortex-m-rt",
"cortex-m-rtic-macros",
"heapless",
"rtic-core",
"version_check",
]
[[package]]
name = "cortex-m-rtic-macros"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a1a6a4c9550373038c0e21a78d44d529bd697c25bbf6b8004bddc6e63b119c7"
dependencies = [
"proc-macro2",
"quote",
"rtic-syntax",
"syn",
]
[[package]]
name = "embedded-hal"
version = "0.2.4"
@ -123,6 +155,7 @@ dependencies = [
"aligned 0.3.2",
"cortex-m 0.5.10",
"cortex-m-rt",
"cortex-m-rtic",
"embedded-hal",
"log",
"panic-itm",
@ -149,6 +182,43 @@ dependencies = [
"typenum",
]
[[package]]
name = "hash32"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4041af86e63ac4298ce40e5cca669066e75b6f1aa3390fe2561ffa5e1d9f4cc"
dependencies = [
"byteorder",
]
[[package]]
name = "hashbrown"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
[[package]]
name = "heapless"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74911a68a1658cfcfb61bc0ccfbd536e3b6e906f8c2f7883ee50157e3e2184f1"
dependencies = [
"as-slice",
"generic-array 0.13.2",
"hash32",
"stable_deref_trait",
]
[[package]]
name = "indexmap"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "log"
version = "0.4.8"
@ -209,6 +279,23 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
[[package]]
name = "rtic-core"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bd58a6949de8ff797a346a28d9f13f7b8f54fa61bb5e3cb0985a4efb497a5ef"
[[package]]
name = "rtic-syntax"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8152fcaa845720d61e6cc570548b89144c2c307f18a480bbd97e55e9f6eeff04"
dependencies = [
"indexmap",
"proc-macro2",
"syn",
]
[[package]]
name = "rustc_version"
version = "0.2.3"
@ -308,6 +395,12 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "876e32dcadfe563a4289e994f7cb391197f362b6315dc45e8ba4aa6f564a4b3c"
[[package]]
name = "version_check"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
[[package]]
name = "void"
version = "1.0.2"

View File

@ -15,6 +15,7 @@ embedded-hal = "0.2"
stm32f4xx-hal = { version = "0.8" , optional = true }
smoltcp = { version = "0.6.0", default-features = false, features = ["proto-ipv4", "proto-ipv6", "socket-icmp", "socket-udp", "socket-tcp", "log", "verbose", "ethernet"], optional = true }
log = "0.4"
cortex-m-rtic = "0.5.3"
[features]
smoltcp-phy = ["smoltcp"]

View File

@ -82,9 +82,9 @@ This program demonstrates the TCP connectivity using **smoltcp** on an STM32F407
[nix-shell]$ run-tmux-env
```
3. When the `tmux` session is ready, on the top-right pane, compile and run the example program. Choose your own IPv4 address and prefix length:
3. When the `tmux` session is ready, on the top-right pane, compile and run the example program. The default IP address is 192.168.1.77, which can be edited in the source file `examples/tcp_stm32f407.rs`.
```sh
[nix-shell]$ tcp_stm32f407 <ip> <prefix>
[nix-shell]$ tcp_stm32f407
```
4. To test the TCP ports, switch to the bottom-right pane (with <kbd>Ctrl</kbd>+<kbd>B</kbd>, followed by an arrow key) and use utilities like NetCat (`nc`):
@ -97,7 +97,7 @@ This program demonstrates the TCP connectivity using **smoltcp** on an STM32F407
#### Expected Output
(Note: the IP address, MAC address and timestamps shown below are examples only.)
(Note: the MAC address and timestamps shown below are examples only.)
ITM output at the initial state:
```

View File

@ -1,125 +1,99 @@
#![no_std]
#![no_main]
use core::env;
extern crate panic_itm;
use cortex_m::{iprintln, iprint};
use cortex_m_rt::entry;
use embedded_hal::digital::v2::OutputPin;
use embedded_hal::blocking::delay::DelayMs;
use embedded_hal::{
digital::v2::OutputPin,
blocking::delay::DelayMs
};
use stm32f4xx_hal::{
rcc::RccExt,
gpio::GpioExt,
time::U32Ext,
stm32::{CorePeripherals, Peripherals},
stm32::ITM,
delay::Delay,
spi::Spi,
time::Hertz
};
use enc424j600;
use enc424j600::EthController;
use enc424j600::smoltcp_phy;
use enc424j600::{smoltcp_phy, EthController};
use smoltcp::wire::{
EthernetAddress, IpAddress, IpCidr
EthernetAddress, IpAddress, IpCidr, Ipv6Cidr
};
use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder};
use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder, EthernetInterface};
use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer};
use smoltcp::time::{Instant, Duration};
use core::str;
use core::fmt::Write;
use core::cell::RefCell;
use cortex_m::interrupt::Mutex;
use cortex_m_rt::exception;
use rtic::cyccnt::Instant;
///
use stm32f4xx_hal::{
rcc::Clocks,
time::MilliSeconds,
timer::{Timer, Event as TimerEvent},
stm32::SYST
stm32::SPI1,
gpio::{
gpioa::{PA5, PA6, PA7, PA4},
Alternate, AF5, Output, PushPull
}
};
/// Rate in Hz
const TIMER_RATE: u32 = 20;
/// Interval duration in milliseconds
const TIMER_DELTA: u32 = 1000 / TIMER_RATE;
/// Elapsed time in milliseconds
static TIMER_MS: Mutex<RefCell<u32>> = Mutex::new(RefCell::new(0));
type BoosterSpiEth = enc424j600::SpiEth<
Spi<SPI1, (PA5<Alternate<AF5>>, PA6<Alternate<AF5>>, PA7<Alternate<AF5>>)>,
PA4<Output<PushPull>>>;
/// Setup SysTick exception
fn timer_setup(syst: SYST, clocks: Clocks) {
let mut timer = Timer::syst(syst, TIMER_RATE.hz(), clocks);
timer.listen(TimerEvent::TimeOut);
pub struct NetStorage {
ip_addrs: [IpCidr; 1],
neighbor_cache: [Option<(IpAddress, smoltcp::iface::Neighbor)>; 8],
}
/// SysTick exception (Timer)
#[exception]
fn SysTick() {
cortex_m::interrupt::free(|cs| {
*TIMER_MS.borrow(cs)
.borrow_mut() += TIMER_DELTA;
});
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,
)],
neighbor_cache: [None; 8],
};
#[rtic::app(device = stm32f4xx_hal::stm32, peripherals = true, monotonic = rtic::cyccnt::CYCCNT)]
const APP: () = {
struct Resources {
eth_iface: EthernetInterface<
'static,
'static,
'static,
smoltcp_phy::SmoltcpDevice<BoosterSpiEth>>,
itm: ITM
}
/// Obtain current time in milliseconds
pub fn timer_now() -> MilliSeconds {
let ms = cortex_m::interrupt::free(|cs| {
*TIMER_MS.borrow(cs)
.borrow()
});
ms.ms()
}
#[init()]
fn init(mut c: init::Context) -> init::LateResources {
c.core.SCB.enable_icache();
c.core.SCB.enable_dcache(&mut c.core.CPUID);
#[entry]
fn main() -> ! {
let mut cp = CorePeripherals::take().unwrap();
cp.SCB.enable_icache();
cp.SCB.enable_dcache(&mut cp.CPUID);
// Enable monotonic timer CYCCNT
c.core.DWT.enable_cycle_counter();
c.core.DCB.enable_trace();
let dp = Peripherals::take().unwrap();
let clocks = dp.RCC.constrain()
let clocks = c.device.RCC.constrain()
.cfgr
.sysclk(168.mhz())
.hclk(168.mhz())
.pclk1(32.mhz())
.pclk2(64.mhz())
.pclk1(42.mhz())
.require_pll48clk()
.freeze();
let mut delay = Delay::new(c.core.SYST, clocks);
// Init ITM & use Stimulus Port 0
let mut itm = cp.ITM;
// Init ITM
let mut itm = c.core.ITM;
let stim0 = &mut itm.stim[0];
iprintln!(stim0,
"Eth TCP Server on STM32-F407 via NIC100/ENC424J600");
// Get IP address from args
let arg_ip_raw = env!("ENC424J600_TCP_IP");
let mut arg_ip_str = arg_ip_raw.split('.');
let mut arg_ip: [u8; 4] = [0; 4];
for i in 0..4 {
match arg_ip_str.next() {
Some(x) => {
match x.parse() {
Ok(x_) => { arg_ip[i] = x_ },
Err(_) => { panic!("IPv4 address invalid!") }
}
},
None => { panic!("IPv4 address invalid!") }
}
}
// Get IP prefix length from args
let arg_ip_pref_raw = env!("ENC424J600_TCP_PREF");
let mut arg_ip_pref: u8 = 0;
match arg_ip_pref_raw.parse() {
Ok(x) => { arg_ip_pref = x },
Err(_) => { panic!("IP prefix length invalid!") }
}
// NIC100 / ENC424J600 Set-up
let spi1 = dp.SPI1;
let gpioa = dp.GPIOA.split();
let mut delay = Delay::new(cp.SYST, clocks);
let spi1 = c.device.SPI1;
let gpioa = c.device.GPIOA.split();
// Mapping: see Table 9, STM32F407ZG Manual
let spi1_sck = gpioa.pa5.into_alternate_af5();
let spi1_miso = gpioa.pa6.into_alternate_af5();
@ -130,28 +104,28 @@ fn main() -> ! {
spisel.set_high().unwrap();
delay.delay_ms(1_u32);
spisel.set_low().unwrap();
// Create SPI1 for HAL
let eth_iface = {
let mut spi_eth = {
let spi_eth_port = Spi::spi1(
spi1, (spi1_sck, spi1_miso, spi1_mosi),
enc424j600::spi::interfaces::SPI_MODE,
Hertz(enc424j600::spi::interfaces::SPI_CLOCK_FREQ),
clocks);
let mut spi_eth = enc424j600::SpiEth::new(spi_eth_port, spi1_nss);
// Init
enc424j600::SpiEth::new(spi_eth_port, spi1_nss)
};
// Init controller
match spi_eth.init_dev(&mut delay) {
Ok(_) => {
iprintln!(stim0, "Ethernet initialized")
iprintln!(stim0, "Initializing Ethernet...")
}
Err(_) => {
panic!("Ethernet initialization failed!")
}
}
// Setup SysTick
// Reference to stm32-eth:examples/ip.rs
timer_setup(delay.free(), clocks);
iprintln!(stim0, "Timer initialized");
// Read MAC
let mut eth_mac_addr: [u8; 6] = [0; 6];
spi_eth.read_from_mac(&mut eth_mac_addr);
@ -168,20 +142,37 @@ fn main() -> ! {
// Init Rx/Tx buffers
spi_eth.init_rxbuf();
spi_eth.init_txbuf();
iprintln!(stim0, "Ethernet controller initialized");
// Copied / modified from smoltcp:
// examples/loopback.rs, examples/multicast.rs
// Init smoltcp interface
let eth_iface = {
let device = smoltcp_phy::SmoltcpDevice::new(spi_eth);
let mut neighbor_cache_entries = [None; 16];
let neighbor_cache = NeighborCache::new(&mut neighbor_cache_entries[..]);
let ip_addr = IpCidr::new(IpAddress::v4(
arg_ip[0], arg_ip[1], arg_ip[2], arg_ip[3]), arg_ip_pref);
let mut ip_addrs = [ip_addr];
let mut iface = EthernetInterfaceBuilder::new(device)
let store = unsafe { &mut NET_STORE };
store.ip_addrs[0] = IpCidr::new(IpAddress::v4(192, 168, 1, 77), 24);
let neighbor_cache = NeighborCache::new(&mut store.neighbor_cache[..]);
EthernetInterfaceBuilder::new(device)
.ethernet_addr(EthernetAddress(eth_mac_addr))
.neighbor_cache(neighbor_cache)
.ip_addrs(&mut ip_addrs[..])
.finalize();
.ip_addrs(&mut store.ip_addrs[..])
.finalize()
};
iprintln!(stim0, "Ethernet interface initialized");
eth_iface
};
init::LateResources {
eth_iface,
itm
}
}
#[idle(resources=[eth_iface, itm])]
fn idle(c: idle::Context) -> ! {
let stim0 = &mut c.resources.itm.stim[0];
let iface = c.resources.eth_iface;
// Copied / modified from smoltcp:
// examples/loopback.rs
@ -203,15 +194,28 @@ fn main() -> ! {
let mut socket_set = SocketSet::new(&mut socket_set_entries[..]);
let echo_handle = socket_set.add(echo_socket);
let greet_handle = socket_set.add(greet_socket);
iprintln!(stim0, "TCP sockets will listen at {}", ip_addr);
{
let store = unsafe { &mut NET_STORE };
iprintln!(stim0,
"TCP sockets will listen at {}", store.ip_addrs[0].address());
}
// Copied / modified from:
// smoltcp:examples/loopback.rs, examples/server.rs;
// stm32-eth:examples/ip.rs,
// git.m-labs.hk/M-Labs/tnetplug
let mut time = 0u32;
let mut next_ms = Instant::now();
use rtic::cyccnt::U32Ext;
next_ms += 168_000_u32.cycles();
loop {
let now = timer_now().0;
let instant = Instant::from_millis(now as i64);
// Poll
let tick = Instant::now() > next_ms;
if tick {
next_ms += 168_000_u32.cycles();
time += 1;
}
let instant = smoltcp::time::Instant::from_millis(time as i64);
match iface.poll(&mut socket_set, instant) {
Ok(_) => {
},
@ -226,7 +230,7 @@ fn main() -> ! {
iprintln!(stim0,
"[{}] Listening to port 1234 for echoing, time-out in 10s", instant);
socket.listen(1234).unwrap();
socket.set_timeout(Some(Duration::from_millis(10000)));
socket.set_timeout(Some(smoltcp::time::Duration::from_millis(10000)));
}
if socket.can_recv() {
iprintln!(stim0,
@ -255,3 +259,8 @@ fn main() -> ! {
}
}
}
extern "C" {
fn EXTI0();
}
};

View File

@ -4,14 +4,15 @@
extern crate panic_itm;
use cortex_m::{iprintln, iprint};
use cortex_m_rt::entry;
use embedded_hal::digital::v2::OutputPin;
use embedded_hal::blocking::delay::DelayMs;
use embedded_hal::{
digital::v2::OutputPin,
blocking::delay::DelayMs
};
use stm32f4xx_hal::{
rcc::RccExt,
gpio::GpioExt,
time::U32Ext,
stm32::{CorePeripherals, Peripherals},
stm32::ITM,
delay::Delay,
spi::Spi,
time::Hertz
@ -19,32 +20,51 @@ use stm32f4xx_hal::{
use enc424j600;
use enc424j600::EthController;
#[entry]
fn main() -> ! {
let mut cp = CorePeripherals::take().unwrap();
cp.SCB.enable_icache();
cp.SCB.enable_dcache(&mut cp.CPUID);
///
use stm32f4xx_hal::{
stm32::SPI1,
gpio::{
gpioa::{PA5, PA6, PA7, PA4},
Alternate, AF5, Output, PushPull
},
};
type BoosterSpiEth = enc424j600::SpiEth<
Spi<SPI1, (PA5<Alternate<AF5>>, PA6<Alternate<AF5>>, PA7<Alternate<AF5>>)>,
PA4<Output<PushPull>>>;
let dp = Peripherals::take().unwrap();
let clocks = dp.RCC.constrain()
#[rtic::app(device = stm32f4xx_hal::stm32, peripherals = true, monotonic = rtic::cyccnt::CYCCNT)]
const APP: () = {
struct Resources {
spi_eth: BoosterSpiEth,
delay: Delay,
itm: ITM,
}
#[init()]
fn init(mut c: init::Context) -> init::LateResources {
c.core.SCB.enable_icache();
c.core.SCB.enable_dcache(&mut c.core.CPUID);
let clocks = c.device.RCC.constrain()
.cfgr
.sysclk(168.mhz())
.hclk(168.mhz())
.pclk1(32.mhz())
.pclk2(64.mhz())
//.pclk1(32.mhz())
.pclk1(42.mhz())
//.pclk2(64.mhz())
.require_pll48clk()
.freeze();
let mut delay = Delay::new(cp.SYST, clocks);
let mut delay = Delay::new(c.core.SYST, clocks);
// Init ITM & use Stimulus Port 0
let mut itm = cp.ITM;
// 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");
// NIC100 / ENC424J600 Set-up
let spi1 = dp.SPI1;
let gpioa = dp.GPIOA.split();
let spi1 = c.device.SPI1;
let gpioa = c.device.GPIOA.split();
// Mapping: see Table 9, STM32F407ZG Manual
let spi1_sck = gpioa.pa5.into_alternate_af5();
let spi1_miso = gpioa.pa6.into_alternate_af5();
@ -56,16 +76,19 @@ fn main() -> ! {
delay.delay_ms(1_u32);
spisel.set_low().unwrap();
// Create SPI1 for HAL
let mut spi_eth = {
let spi_eth_port = Spi::spi1(
spi1, (spi1_sck, spi1_miso, spi1_mosi),
enc424j600::spi::interfaces::SPI_MODE,
Hertz(enc424j600::spi::interfaces::SPI_CLOCK_FREQ),
clocks);
let mut spi_eth = enc424j600::SpiEth::new(spi_eth_port, spi1_nss);
enc424j600::SpiEth::new(spi_eth_port, spi1_nss)
};
// Init
match spi_eth.init_dev(&mut delay) {
Ok(_) => {
iprintln!(stim0, "Ethernet initialized")
iprintln!(stim0, "Initializing Ethernet...")
}
Err(_) => {
panic!("Ethernet initialization failed!")
@ -88,6 +111,18 @@ fn main() -> ! {
// Init Rx/Tx buffers
spi_eth.init_rxbuf();
spi_eth.init_txbuf();
iprintln!(stim0, "Ethernet controller initialized");
init::LateResources {
spi_eth,
delay,
itm,
}
}
#[idle(resources=[spi_eth, delay, itm])]
fn idle(c: idle::Context) -> ! {
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,
@ -117,8 +152,9 @@ fn main() -> ! {
_ => ()
};
}
spi_eth.send_raw_packet(&eth_tx_packet);
c.resources.spi_eth.send_raw_packet(&eth_tx_packet);
iprintln!(stim0, "Packet sent");
delay.delay_ms(100_u32);
c.resources.delay.delay_ms(100_u32);
}
}
};

View File

@ -22,9 +22,8 @@ let
echo "[Examples]"
echo " tx_stm32f407"
echo " - Run tx_stm32f407 example."
echo " tcp_stm32f407 <ip> <pref>"
echo " - Run tcp_stm32f407 example with the IPv4"
echo " address <ip> (dot-separated) and prefix length <pref>."
echo " tcp_stm32f407"
echo " - Run tcp_stm32f407 example."
echo ""
echo "[Workspace]"
echo " run-tmux-env"
@ -98,14 +97,6 @@ let
cargo run --release --example=tx_stm32f407 --features=stm32f407
'';
exTcpStm32f407 = writeShellScriptBin "tcp_stm32f407" ''
if [[ $1 = "" ]] || [[ $2 = "" ]]
then
echo "Arguments <ip> or <pref> are missing."
exit
fi
touch ./examples/tcp_stm32f407.rs
export ENC424J600_TCP_IP=$1
export ENC424J600_TCP_PREF=$2
cargo run --release --example=tcp_stm32f407 --features=stm32f407,smoltcp-phy-all
'';
in