diff --git a/Cargo.lock b/Cargo.lock index 0b9d77d..8cb42d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index 293c3a5..754f7de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ smoltcp = { version = "0.6.0", default-features = false, features = ["proto-ipv4 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 } @@ -27,8 +28,8 @@ smoltcp-phy-all = [ "smoltcp/proto-ipv4", "smoltcp/proto-ipv6" ] # Example-based features -tx_stm32f407 = ["stm32f4xx-hal/stm32f407", "cortex-m", "cortex-m-rt", "panic-itm", "log"] -tcp_stm32f407 = ["stm32f4xx-hal/stm32f407", "cortex-m", "cortex-m-rt", "smoltcp-phy-all", "smoltcp/log", "panic-itm", "log"] +tx_stm32f407 = ["stm32f4xx-hal/stm32f407", "cortex-m", "cortex-m-rtic", "panic-itm", "log"] +tcp_stm32f407 = ["stm32f4xx-hal/stm32f407", "cortex-m", "cortex-m-rt", "cortex-m-rtic", "smoltcp-phy-all", "smoltcp/log", "panic-itm", "log"] default = [] [[example]] diff --git a/README.md b/README.md index 169fc3a..c5ae652 100644 --- a/README.md +++ b/README.md @@ -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 + [nix-shell]$ tcp_stm32f407 ``` 4. To test the TCP ports, switch to the bottom-right pane (with Ctrl+B, 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: ``` diff --git a/examples/tcp_stm32f407.rs b/examples/tcp_stm32f407.rs index a859149..b841517 100644 --- a/examples/tcp_stm32f407.rs +++ b/examples/tcp_stm32f407.rs @@ -1,36 +1,34 @@ #![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; +/// Timer use core::cell::RefCell; use cortex_m::interrupt::Mutex; use cortex_m_rt::exception; @@ -40,6 +38,7 @@ use stm32f4xx_hal::{ timer::{Timer, Event as TimerEvent}, stm32::SYST }; +use smoltcp::time::Instant; /// Rate in Hz const TIMER_RATE: u32 = 20; /// Interval duration in milliseconds @@ -71,187 +70,234 @@ pub fn timer_now() -> MilliSeconds { ms.ms() } -#[entry] -fn main() -> ! { - let mut cp = CorePeripherals::take().unwrap(); - cp.SCB.enable_icache(); - cp.SCB.enable_dcache(&mut cp.CPUID); - - let dp = Peripherals::take().unwrap(); - let clocks = dp.RCC.constrain() - .cfgr - .sysclk(168.mhz()) - .hclk(168.mhz()) - .pclk1(32.mhz()) - .pclk2(64.mhz()) - .freeze(); - - // Init ITM & use Stimulus Port 0 - let mut itm = cp.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!") } +/// +use stm32f4xx_hal::{ + stm32::SPI1, + gpio::{ + gpioa::{PA5, PA6, PA7, PA4}, + Alternate, AF5, Output, PushPull } +}; +type BoosterSpiEth = enc424j600::SpiEth< + Spi>, PA6>, PA7>)>, + PA4>>; - // NIC100 / ENC424J600 Set-up - let spi1 = dp.SPI1; - let gpioa = dp.GPIOA.split(); - let mut delay = Delay::new(cp.SYST, clocks); - // Mapping: see Table 9, STM32F407ZG Manual - let spi1_sck = gpioa.pa5.into_alternate_af5(); - let spi1_miso = gpioa.pa6.into_alternate_af5(); - let spi1_mosi = gpioa.pa7.into_alternate_af5(); - let spi1_nss = gpioa.pa4.into_push_pull_output(); - // Map SPISEL: see Table 1, NIC100 Manual - let mut spisel = gpioa.pa1.into_push_pull_output(); - spisel.set_high().unwrap(); - delay.delay_ms(1_u32); - spisel.set_low().unwrap(); - // Create SPI1 for HAL - 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 - match spi_eth.init_dev(&mut delay) { - Ok(_) => { - iprintln!(stim0, "Ethernet initialized") - } - Err(_) => { - panic!("Ethernet initialization failed!") - } - } +pub struct NetStorage { + ip_addrs: [IpCidr; 1], + neighbor_cache: [Option<(IpAddress, smoltcp::iface::Neighbor)>; 8], +} - // 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); - for i in 0..6 { - let byte = eth_mac_addr[i]; - match i { - 0 => iprint!(stim0, "MAC Address = {:02x}-", byte), - 1..=4 => iprint!(stim0, "{:02x}-", byte), - 5 => iprint!(stim0, "{:02x}\n", byte), - _ => () - }; +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>, + itm: ITM } - // Init Rx/Tx buffers - spi_eth.init_rxbuf(); - spi_eth.init_txbuf(); - - // Copied / modified from smoltcp: - // examples/loopback.rs, examples/multicast.rs - let device = smoltcp_phy::SmoltcpDevice::new(&mut 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) - .ethernet_addr(EthernetAddress(eth_mac_addr)) - .neighbor_cache(neighbor_cache) - .ip_addrs(&mut ip_addrs[..]) - .finalize(); - - // Copied / modified from smoltcp: - // examples/loopback.rs - let echo_socket = { - static mut TCP_SERVER_RX_DATA: [u8; 1024] = [0; 1024]; - static mut TCP_SERVER_TX_DATA: [u8; 1024] = [0; 1024]; - let tcp_rx_buffer = TcpSocketBuffer::new(unsafe { &mut TCP_SERVER_RX_DATA[..] }); - let tcp_tx_buffer = TcpSocketBuffer::new(unsafe { &mut TCP_SERVER_TX_DATA[..] }); - TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer) - }; - let greet_socket = { - static mut TCP_SERVER_RX_DATA: [u8; 256] = [0; 256]; - static mut TCP_SERVER_TX_DATA: [u8; 256] = [0; 256]; - let tcp_rx_buffer = TcpSocketBuffer::new(unsafe { &mut TCP_SERVER_RX_DATA[..] }); - let tcp_tx_buffer = TcpSocketBuffer::new(unsafe { &mut TCP_SERVER_TX_DATA[..] }); - TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer) - }; - let mut socket_set_entries = [None, None]; - 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); - - // Copied / modified from: - // smoltcp:examples/loopback.rs, examples/server.rs; - // stm32-eth:examples/ip.rs, - // git.m-labs.hk/M-Labs/tnetplug - loop { - let now = timer_now().0; - let instant = Instant::from_millis(now as i64); - match iface.poll(&mut socket_set, instant) { - Ok(_) => { - }, - Err(e) => { - iprintln!(stim0, "[{}] Poll error: {:?}", instant, e) - } - } - // Control the "echoing" socket (:1234) - { - let mut socket = socket_set.get::(echo_handle); - if !socket.is_open() { - 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))); + #[init()] + fn init(mut c: init::Context) -> init::LateResources { + c.core.SCB.enable_icache(); + c.core.SCB.enable_dcache(&mut c.core.CPUID); + + // Enable monotonic timer CYCCNT + c.core.DWT.enable_cycle_counter(); + c.core.DCB.enable_trace(); + + let clocks = c.device.RCC.constrain() + .cfgr + .sysclk(168.mhz()) + .hclk(168.mhz()) + .pclk1(42.mhz()) + .require_pll48clk() + .freeze(); + let mut delay = Delay::new(c.core.SYST, clocks); + + // 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"); + + // NIC100 / ENC424J600 Set-up + 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(); + let spi1_mosi = gpioa.pa7.into_alternate_af5(); + let spi1_nss = gpioa.pa4.into_push_pull_output(); + // Map SPISEL: see Table 1, NIC100 Manual + let mut spisel = gpioa.pa1.into_push_pull_output(); + 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); + enc424j600::SpiEth::new(spi_eth_port, spi1_nss) + }; + + // Init controller + match spi_eth.init_dev(&mut delay) { + Ok(_) => { + iprintln!(stim0, "Initializing Ethernet...") + } + Err(_) => { + panic!("Ethernet initialization failed!") + } } - if socket.can_recv() { - iprintln!(stim0, - "[{}] Received packet: {:?}", instant, socket.recv(|buffer| { - (buffer.len(), str::from_utf8(buffer).unwrap()) - })); + + // Read MAC + let mut eth_mac_addr: [u8; 6] = [0; 6]; + spi_eth.read_from_mac(&mut eth_mac_addr); + for i in 0..6 { + let byte = eth_mac_addr[i]; + match i { + 0 => iprint!(stim0, "MAC Address = {:02x}-", byte), + 1..=4 => iprint!(stim0, "{:02x}-", byte), + 5 => iprint!(stim0, "{:02x}\n", byte), + _ => () + }; } + + // Init Rx/Tx buffers + spi_eth.init_rxbuf(); + spi_eth.init_txbuf(); + iprintln!(stim0, "Ethernet controller initialized"); + + // Init smoltcp interface + let eth_iface = { + let device = smoltcp_phy::SmoltcpDevice::new(spi_eth); + + 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 store.ip_addrs[..]) + .finalize() + }; + iprintln!(stim0, "Ethernet interface initialized"); + + eth_iface + }; + + // Setup SysTick after releasing SYST from Delay + // Reference to stm32-eth:examples/ip.rs + timer_setup(delay.free(), clocks); + iprintln!(stim0, "Timer initialized"); + + init::LateResources { + eth_iface, + itm } - // Control the "greeting" socket (:4321) + } + + #[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 + let echo_socket = { + static mut TCP_SERVER_RX_DATA: [u8; 1024] = [0; 1024]; + static mut TCP_SERVER_TX_DATA: [u8; 1024] = [0; 1024]; + let tcp_rx_buffer = TcpSocketBuffer::new(unsafe { &mut TCP_SERVER_RX_DATA[..] }); + let tcp_tx_buffer = TcpSocketBuffer::new(unsafe { &mut TCP_SERVER_TX_DATA[..] }); + TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer) + }; + let greet_socket = { + static mut TCP_SERVER_RX_DATA: [u8; 256] = [0; 256]; + static mut TCP_SERVER_TX_DATA: [u8; 256] = [0; 256]; + let tcp_rx_buffer = TcpSocketBuffer::new(unsafe { &mut TCP_SERVER_RX_DATA[..] }); + let tcp_tx_buffer = TcpSocketBuffer::new(unsafe { &mut TCP_SERVER_TX_DATA[..] }); + TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer) + }; + let mut socket_set_entries = [None, None]; + 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); { - let mut socket = socket_set.get::(greet_handle); - if !socket.is_open() { - iprintln!(stim0, - "[{}] Listening to port 4321 for greeting, \ - please connect to the port", instant); - socket.listen(4321).unwrap(); + 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 + loop { + // Poll + let now = timer_now().0; + let instant = Instant::from_millis(now as i64); + match iface.poll(&mut socket_set, instant) { + Ok(_) => { + }, + Err(e) => { + iprintln!(stim0, "[{}] Poll error: {:?}", instant, e) + } + } + // Control the "echoing" socket (:1234) + { + let mut socket = socket_set.get::(echo_handle); + if !socket.is_open() { + 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()) + })); + } } + // Control the "greeting" socket (:4321) + { + let mut socket = socket_set.get::(greet_handle); + if !socket.is_open() { + iprintln!(stim0, + "[{}] Listening to port 4321 for greeting, \ + 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); - socket.close(); + 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); + socket.close(); + } } } } -} + + extern "C" { + fn EXTI0(); + } +}; diff --git a/examples/tx_stm32f407.rs b/examples/tx_stm32f407.rs index 84b5e84..4dd10ac 100644 --- a/examples/tx_stm32f407.rs +++ b/examples/tx_stm32f407.rs @@ -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,106 +20,141 @@ 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>, PA6>, PA7>)>, + PA4>>; - let dp = Peripherals::take().unwrap(); - let clocks = dp.RCC.constrain() - .cfgr - .sysclk(168.mhz()) - .hclk(168.mhz()) - .pclk1(32.mhz()) - .pclk2(64.mhz()) - .freeze(); - let mut delay = Delay::new(cp.SYST, clocks); +#[rtic::app(device = stm32f4xx_hal::stm32, peripherals = true, monotonic = rtic::cyccnt::CYCCNT)] +const APP: () = { + struct Resources { + spi_eth: BoosterSpiEth, + delay: Delay, + itm: ITM, + } - // Init ITM & use Stimulus Port 0 - let mut itm = cp.ITM; - let stim0 = &mut itm.stim[0]; + #[init()] + fn init(mut c: init::Context) -> init::LateResources { + c.core.SCB.enable_icache(); + c.core.SCB.enable_dcache(&mut c.core.CPUID); - iprintln!(stim0, - "Eth TX Pinging on STM32-F407 via NIC100/ENC424J600"); + let clocks = c.device.RCC.constrain() + .cfgr + .sysclk(168.mhz()) + .hclk(168.mhz()) + //.pclk1(32.mhz()) + .pclk1(42.mhz()) + //.pclk2(64.mhz()) + .require_pll48clk() + .freeze(); + let mut delay = Delay::new(c.core.SYST, clocks); - // NIC100 / ENC424J600 Set-up - let spi1 = dp.SPI1; - let gpioa = dp.GPIOA.split(); - // Mapping: see Table 9, STM32F407ZG Manual - let spi1_sck = gpioa.pa5.into_alternate_af5(); - let spi1_miso = gpioa.pa6.into_alternate_af5(); - let spi1_mosi = gpioa.pa7.into_alternate_af5(); - let spi1_nss = gpioa.pa4.into_push_pull_output(); - // Map SPISEL: see Table 1, NIC100 Manual - let mut spisel = gpioa.pa1.into_push_pull_output(); - spisel.set_high().unwrap(); - delay.delay_ms(1_u32); - spisel.set_low().unwrap(); - // Create SPI1 for HAL - 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 - match spi_eth.init_dev(&mut delay) { - Ok(_) => { - iprintln!(stim0, "Ethernet initialized") - } - Err(_) => { - panic!("Ethernet initialization failed!") - } - } + // 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"); - // Read MAC - let mut eth_mac_addr: [u8; 6] = [0; 6]; - spi_eth.read_from_mac(&mut eth_mac_addr); - for i in 0..6 { - let byte = eth_mac_addr[i]; - match i { - 0 => iprint!(stim0, "MAC Address = {:02x}-", byte), - 1..=4 => iprint!(stim0, "{:02x}-", byte), - 5 => iprint!(stim0, "{:02x}\n", byte), - _ => () + // NIC100 / ENC424J600 Set-up + 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(); + let spi1_mosi = gpioa.pa7.into_alternate_af5(); + let spi1_nss = gpioa.pa4.into_push_pull_output(); + // Map SPISEL: see Table 1, NIC100 Manual + let mut spisel = gpioa.pa1.into_push_pull_output(); + spisel.set_high().unwrap(); + 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); + enc424j600::SpiEth::new(spi_eth_port, spi1_nss) }; - } - // Init Rx/Tx buffers - spi_eth.init_rxbuf(); - spi_eth.init_txbuf(); - // 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 - ]; - loop { - let mut eth_tx_packet = enc424j600::tx::TxPacket::new(); - eth_tx_packet.update_frame(ð_tx_dat, 64); - 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); + // Init + match spi_eth.init_dev(&mut delay) { + Ok(_) => { + iprintln!(stim0, "Initializing Ethernet...") + } + Err(_) => { + panic!("Ethernet initialization failed!") + } + } + + // Read MAC + let mut eth_mac_addr: [u8; 6] = [0; 6]; + spi_eth.read_from_mac(&mut eth_mac_addr); + for i in 0..6 { + let byte = eth_mac_addr[i]; match i { - 0 => iprint!(stim0, "dest={:02x}-", byte), - 6 => iprint!(stim0, "src={:02x}-", byte), - 12 => iprint!(stim0, "data={:02x}", byte), - 1..=4 | 7..=10 => iprint!(stim0, "{:02x}-", byte), - 13..=14 | 16..=18 => iprint!(stim0, "{:02x}", byte), - 5 | 11 | 15 => iprint!(stim0, "{:02x} ", byte), - 19 => iprint!(stim0, "{:02x} ...\n", byte), + 0 => iprint!(stim0, "MAC Address = {:02x}-", byte), + 1..=4 => iprint!(stim0, "{:02x}-", byte), + 5 => iprint!(stim0, "{:02x}\n", byte), _ => () }; } - spi_eth.send_raw_packet(ð_tx_packet); - iprintln!(stim0, "Packet sent"); - delay.delay_ms(100_u32); + + // 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, + 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(ð_tx_dat, 64); + 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 { + 0 => iprint!(stim0, "dest={:02x}-", byte), + 6 => iprint!(stim0, "src={:02x}-", byte), + 12 => iprint!(stim0, "data={:02x}", byte), + 1..=4 | 7..=10 => iprint!(stim0, "{:02x}-", byte), + 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_raw_packet(ð_tx_packet); + iprintln!(stim0, "Packet sent"); + c.resources.delay.delay_ms(100_u32); + } + } +}; diff --git a/nix/itm-tools.nix b/nix/itm-tools.nix index 115cfb0..c92e7dc 100644 --- a/nix/itm-tools.nix +++ b/nix/itm-tools.nix @@ -1,21 +1,19 @@ { stdenv, fetchFromGitHub, rustPlatform, pkg-config }: rustPlatform.buildRustPackage rec { + version = "2019-11-15"; pname = "itm-tools"; - version = "unstable-2019-11-15"; src = fetchFromGitHub { owner = "japaric"; - repo = pname; + repo = "itm-tools"; rev = "e94155e44019d893ac8e6dab51cc282d344ab700"; sha256 = "19xkjym0i7y52cfhvis49c59nzvgw4906cd8bkz8ka38mbgfqgiy"; }; cargoPatches = [ ./itm-tools-cargo-lock.patch ]; - # This hash is computed under nixpkgs 20.09. - # Older versions nixpkgs would require a different value. - cargoSha256 = "0rl2ph5igwjl7rwpwcf6afnxly5av7cd6va6wn82lxm606giyq75"; + cargoSha256 = "0is702s14pgvd5i2m8aaw3zcsshqrwj97mjgg3wikbc627pagzg7"; nativeBuildInputs = [ pkg-config ]; diff --git a/shell.nix b/shell.nix index 4365a45..fb65df1 100644 --- a/shell.nix +++ b/shell.nix @@ -22,9 +22,8 @@ let echo "[Examples]" echo " tx_stm32f407" echo " - Run tx_stm32f407 example." - echo " tcp_stm32f407 " - echo " - Run tcp_stm32f407 example with the IPv4" - echo " address (dot-separated) and prefix length ." + 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=tx_stm32f407 ''; exTcpStm32f407 = writeShellScriptBin "tcp_stm32f407" '' - if [[ $1 = "" ]] || [[ $2 = "" ]] - then - echo "Arguments or 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=tcp_stm32f407 ''; in diff --git a/src/lib.rs b/src/lib.rs index 6efd701..7c964f9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,8 +18,8 @@ pub mod smoltcp_phy; /// Max raw frame array size pub const RAW_FRAME_LENGTH_MAX: usize = 0x1000; -pub trait EthController<'c> { - fn init_dev(&mut self, delay: &mut dyn DelayUs) -> Result<(), EthControllerError>; +pub trait EthController { + fn init_dev(&mut self, delay: &mut impl DelayUs) -> Result<(), EthControllerError>; fn init_rxbuf(&mut self) -> Result<(), EthControllerError>; fn init_txbuf(&mut self) -> Result<(), EthControllerError>; fn receive_next(&mut self, is_poll: bool) -> Result; @@ -61,9 +61,9 @@ impl , } } -impl <'c, SPI: Transfer, - NSS: OutputPin> EthController<'c> for SpiEth { - fn init_dev(&mut self, delay: &mut dyn DelayUs) -> Result<(), EthControllerError> { +impl , + NSS: OutputPin> EthController for SpiEth { + fn init_dev(&mut self, delay: &mut impl DelayUs) -> Result<(), EthControllerError> { // Write 0x1234 to EUDAST self.spi_port.write_reg_16b(spi::addrs::EUDAST, 0x1234)?; // Verify that EUDAST is 0x1234 diff --git a/src/smoltcp_phy.rs b/src/smoltcp_phy.rs index 88baf55..5c7e6ab 100644 --- a/src/smoltcp_phy.rs +++ b/src/smoltcp_phy.rs @@ -1,32 +1,32 @@ use crate::{ EthController, tx, RAW_FRAME_LENGTH_MAX }; -use core::intrinsics::transmute; +use core::cell; use smoltcp::{ phy::{Device, DeviceCapabilities, RxToken, TxToken}, time::Instant, Error }; -pub struct SmoltcpDevice<'c> { - eth_controller: &'c mut dyn EthController<'c>, +pub struct SmoltcpDevice { + pub eth_controller: cell::RefCell, rx_packet_buf: [u8; RAW_FRAME_LENGTH_MAX], tx_packet_buf: [u8; RAW_FRAME_LENGTH_MAX] } -impl<'c> SmoltcpDevice<'c> { - pub fn new(eth_controller: &'c mut dyn EthController<'c>) -> Self { +impl SmoltcpDevice { + pub fn new(eth_controller: EC) -> Self { SmoltcpDevice { - eth_controller, + eth_controller: cell::RefCell::new(eth_controller), rx_packet_buf: [0; RAW_FRAME_LENGTH_MAX], tx_packet_buf: [0; RAW_FRAME_LENGTH_MAX] } } } -impl<'a, 'c> Device<'a> for SmoltcpDevice<'c> { +impl<'a, EC: 'a + EthController> Device<'a> for SmoltcpDevice { type RxToken = EthRxToken<'a>; - type TxToken = EthTxToken<'a>; + type TxToken = EthTxToken<'a, EC>; fn capabilities(&self) -> DeviceCapabilities { let mut caps = DeviceCapabilities::default(); @@ -35,13 +35,8 @@ impl<'a, 'c> Device<'a> for SmoltcpDevice<'c> { } fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> { - // Extend self lifetime from 'c to 'a for tokens' access to EthController - let self_trans = unsafe { - transmute::<&mut SmoltcpDevice<'c>, &mut SmoltcpDevice<'a>>(&mut *self) - }; - // Make self_a point to *self that has a lifetime of 'a (extended) - let self_a = self_trans as *mut SmoltcpDevice<'a>; - match self_trans.eth_controller.receive_next(false) { + let self_p = (&mut *self) as *mut SmoltcpDevice; + match self.eth_controller.borrow_mut().receive_next(false) { Ok(rx_packet) => { // Write received packet to RX packet buffer rx_packet.write_frame_to(&mut self.rx_packet_buf); @@ -53,7 +48,7 @@ impl<'a, 'c> Device<'a> for SmoltcpDevice<'c> { // Construct a blank TxToken let tx_token = EthTxToken { buf: &mut self.tx_packet_buf, - dev: self_a + dev: self_p }; Some((rx_token, tx_token)) }, @@ -62,16 +57,11 @@ impl<'a, 'c> Device<'a> for SmoltcpDevice<'c> { } fn transmit(&'a mut self) -> Option { - // Extend self lifetime from 'c to 'a for TxToken's access to EthController - let self_trans = unsafe { - transmute::<&mut SmoltcpDevice<'c>, &mut SmoltcpDevice<'a>>(&mut *self) - }; - // Make self_a point to *self that has a lifetime of 'a (extended) - let self_a = self_trans as *mut SmoltcpDevice<'a>; + let self_p = (&mut *self) as *mut SmoltcpDevice; // Construct a blank TxToken let tx_token = EthTxToken { buf: &mut self.tx_packet_buf, - dev: self_a + dev: self_p }; Some(tx_token) } @@ -91,12 +81,12 @@ impl<'a> RxToken for EthRxToken<'a> { } } -pub struct EthTxToken<'a> { +pub struct EthTxToken<'a, EC: EthController> { buf: &'a mut [u8], - dev: *mut SmoltcpDevice<'a> + dev: *mut SmoltcpDevice } -impl<'a> TxToken for EthTxToken<'a> { +impl<'a, EC: 'a + EthController> TxToken for EthTxToken<'a, EC> { fn consume(self, _timestamp: Instant, len: usize, f: F) -> Result where F: FnOnce(&mut [u8]) -> Result, @@ -110,7 +100,7 @@ impl<'a> TxToken for EthTxToken<'a> { let eth_controller = unsafe { &mut (*self.dev).eth_controller }; - match eth_controller.send_raw_packet(&tx_packet) { + match eth_controller.borrow_mut().send_raw_packet(&tx_packet) { Ok(_) => { result }, Err(_) => Err(Error::Exhausted) }