diff --git a/.gitignore b/.gitignore index 552dcf1..3ee4c12 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ -/target -/stm32f1xx-hal \ No newline at end of file +target/ +stm32f1xx-hal/ +*.jdebug* \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 37f5969..e0b45a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aligned" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d39da9b88ae1a81c03c9c082b8db83f1d0e93914126041962af61034ab44c4a5" + [[package]] name = "aligned" version = "0.3.5" @@ -23,6 +29,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" @@ -38,6 +50,18 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + [[package]] name = "cast" version = "0.2.7" @@ -47,13 +71,31 @@ dependencies = [ "rustc_version 0.4.0", ] +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cortex-m" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59971a5cf4dacacaf738dd9d8660875118fce790f800f661893eb20894c1d622" +dependencies = [ + "aligned 0.2.0", + "bare-metal", + "cortex-m 0.6.7", + "volatile-register", +] + [[package]] name = "cortex-m" version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9075300b07c6a56263b9b582c214d0ff037b00d45ec9fde1cc711490c56f1bb9" dependencies = [ - "aligned", + "aligned 0.3.5", "bare-metal", "bitfield", "cortex-m 0.7.3", @@ -93,6 +135,32 @@ dependencies = [ "syn", ] +[[package]] +name = "cortex-m-rtic" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9845c4c7f7af19e216a2d00345f7f1507b8907b85cd551e403d68baeec342bb3" +dependencies = [ + "cortex-m 0.6.7", + "cortex-m-rt", + "cortex-m-rtic-macros", + "heapless", + "rtic-core", + "version_check", +] + +[[package]] +name = "cortex-m-rtic-macros" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc874eda99515b15e67f03562726a530388f454431096d30131051b52b840559" +dependencies = [ + "proc-macro2", + "quote", + "rtic-syntax", + "syn", +] + [[package]] name = "embedded-hal" version = "0.2.5" @@ -103,6 +171,18 @@ dependencies = [ "void", ] +[[package]] +name = "enc424j600" +version = "0.2.0" +source = "git+https://git.m-labs.hk/M-Labs/ENC424J600.git#fbcc3778d27cfbeec7a1395c9b13a00c8a26af9a" +dependencies = [ + "aligned 0.3.5", + "cortex-m 0.5.11", + "embedded-hal", + "smoltcp", + "volatile-register", +] + [[package]] name = "generic-array" version = "0.12.4" @@ -131,6 +211,58 @@ dependencies = [ "version_check", ] +[[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.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "heapless" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634bd4d29cbf24424d0a4bfcbf80c6960129dc24424752a7d1d1390607023422" +dependencies = [ + "as-slice", + "generic-array 0.14.4", + "hash32", + "stable_deref_trait", +] + +[[package]] +name = "indexmap" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "managed" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75de51135344a4f8ed3cfe2720dc27736f7711989703a0b43aadf3753c55577" + [[package]] name = "nb" version = "0.1.3" @@ -152,6 +284,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812" +[[package]] +name = "panic-itm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d577d97d1b31268087b6dddf2470e6794ef5eee87d9dca7fcd0481695391a4c" +dependencies = [ + "cortex-m 0.7.3", +] + [[package]] name = "proc-macro2" version = "1.0.28" @@ -176,6 +317,23 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f" +[[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" @@ -215,6 +373,17 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +[[package]] +name = "smoltcp" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e4a069bef843d170df47e7c0a8bf8d037f217d9f5b325865acc3e466ffe40d3" +dependencies = [ + "bitflags", + "byteorder", + "managed", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -266,9 +435,14 @@ version = "0.1.0" dependencies = [ "cortex-m 0.6.7", "cortex-m-rt", + "cortex-m-rtic", "embedded-hal", + "enc424j600", + "log", "nb 0.1.3", "panic-halt", + "panic-itm", + "smoltcp", "stm32f1xx-hal", ] diff --git a/Cargo.toml b/Cargo.toml index f691913..4669915 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,11 +8,16 @@ edition = "2018" [dependencies] embedded-hal = "0.2.3" nb = "0.1.2" -cortex-m = "0.6.2" +cortex-m = {version = "0.6.2"} cortex-m-rt = "0.6.11" +cortex-m-rtic = { version = "0.5.3"} +panic-itm = { version = "0.4" } +log = { version = "0.4" } +smoltcp = { version = "0.7.0", default-features = false, features = [ "socket-raw", "proto-ipv4", "proto-ipv6" ]} + # Panic behaviour, see https://crates.io/keywords/panic-impl for alternatives panic-halt = "0.2.0" +stm32f1xx-hal = {version = "0.6.1", features = ["rt", "stm32f103", "high"]} + +enc424j600 = { git = "https://git.m-labs.hk/M-Labs/ENC424J600.git", features = ["smoltcp-examples", "cortex-m-cpu"]} -[dependencies.stm32f1xx-hal] -version = "0.6.1" -features = ["rt", "stm32f103", "high"] diff --git a/src/main.rs b/src/main.rs index 3e6755d..3d9920f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,52 +1,321 @@ -//! Blinks an LED -//! -//! This assumes that a LED is connected to pc0 as is the case on the blue pill board. -//! -//! Note: Without additional hardware, PC0 should not be used to drive an LED, see page 5.1.2 of -//! the reference manual for an explanation. This is not an issue on the blue pill. - -#![deny(unsafe_code)] #![no_std] #![no_main] -use panic_halt as _; +extern crate panic_itm; +use cortex_m::{iprintln, iprint}; -use nb::block; +use embedded_hal::{ + digital::v2::OutputPin, + // blocking::delay::DelayMs +}; +// use stm32f1xx_hal::spi::SpiRegisterBlock; +use stm32f1xx_hal::{ + afio::AfioExt, + rcc::RccExt, + flash::FlashExt, + gpio::GpioExt, + time::U32Ext, + stm32::ITM, + delay::Delay, + spi::Spi, + // time::Hertz +}; +use enc424j600::smoltcp_phy; -use cortex_m_rt::entry; -use embedded_hal::digital::v2::OutputPin; -use stm32f1xx_hal::{pac, prelude::*, timer::Timer}; +use smoltcp::wire::{ + EthernetAddress, IpAddress, IpCidr, Ipv6Cidr +}; +use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder, EthernetInterface}; +use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer}; +use core::str; +use core::fmt::Write; -#[entry] -fn main() -> ! { - // Get access to the core peripherals from the cortex-m crate - let cp = cortex_m::Peripherals::take().unwrap(); - // Get access to the device specific peripherals from the peripheral access crate - let dp = pac::Peripherals::take().unwrap(); +/// Timer +use core::cell::RefCell; +use cortex_m::interrupt::Mutex; +use cortex_m_rt::exception; +use stm32f1xx_hal::{ + rcc::Clocks, + time::MilliSeconds, + timer::{Timer, Event as TimerEvent}, + stm32::SYST +}; +use smoltcp::time::Instant; - // Take ownership over the raw flash and rcc devices and convert them into the corresponding - // HAL structs - let mut flash = dp.FLASH.constrain(); - let mut rcc = dp.RCC.constrain(); +/// 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> = Mutex::new(RefCell::new(0)); - // Freeze the configuration of all the clocks in the system and store the frozen frequencies in - // `clocks` - let clocks = rcc.cfgr.freeze(&mut flash.acr); - - // Acquire the GPIOC peripheral - let mut gpioc = dp.GPIOC.split(&mut rcc.apb2); - - // Configure gpio C pin 0 as a push-pull output. The `crl` register is passed to the function - // in order to configure the port. For pins 0-7, crl should be passed instead. - let mut led = gpioc.pc0.into_push_pull_output(&mut gpioc.crl); - // Configure the syst timer to trigger an update every second - let mut timer = Timer::syst(cp.SYST, &clocks).start_count_down(1.hz()); - - // Wait for the timer to trigger an update and change the state of the LED - loop { - block!(timer.wait()).unwrap(); - led.set_high().unwrap(); - block!(timer.wait()).unwrap(); - led.set_low().unwrap(); - } +/// Setup SysTick exception +fn timer_setup(syst: SYST, clocks: Clocks) { + let timer = Timer::syst(syst, &clocks); + timer.start_count_down(TIMER_RATE.hz()).listen(TimerEvent::Update); } + +/// SysTick exception (Timer) +#[exception] +fn SysTick() { + cortex_m::interrupt::free(|cs| { + *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() + }); + ms.ms() +} + +///spi +use stm32f1xx_hal::{ + stm32::SPI1, + spi::Spi1Remap, + gpio::{ + gpiob::{PB3, PB4, PB5}, + gpioc::PC13, + Alternate, Output, PushPull, Input, Floating + } +}; +type SpiEth = enc424j600::Enc424j600< + Spi>, PB4>, PB5>)>, + PC13> +>; + +pub struct NetStorage { + ip_addrs: [IpCidr; 1], + neighbor_cache: [Option<(IpAddress, smoltcp::iface::Neighbor)>; 8], +} + +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 = stm32f1xx_hal::stm32, peripherals = true, monotonic = rtic::cyccnt::CYCCNT)] +const APP: () = { + struct Resources { + eth_iface: EthernetInterface< + 'static, + smoltcp_phy::SmoltcpDevice>, + 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); + + // Enable monotonic timer CYCCNT + c.core.DWT.enable_cycle_counter(); + c.core.DCB.enable_trace(); + + let mut flash = c.device.FLASH.constrain(); + let mut rcc = c.device.RCC.constrain(); + + let clocks = rcc + .cfgr + .use_hse(8.mhz()) + .sysclk(72.mhz()) + .hclk(72.mhz()) + .pclk1(36.mhz()) + .pclk2(72.mhz()) + .freeze(&mut flash.acr); + + 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-F103 via NIC100/ENC424J600"); + + // NIC100 / ENC424J600 Set-up + let spi1 = c.device.SPI1; + let gpioa = c.device.GPIOA.split(&mut rcc.apb2); + let mut gpiob = c.device.GPIOB.split(&mut rcc.apb2); + let mut gpioc = c.device.GPIOC.split(&mut rcc.apb2); + let mut afio = c.device.AFIO.constrain(&mut rcc.apb2); + + let (_pa15, pb3, pb4) = afio.mapr.disable_jtag(gpioa.pa15, gpiob.pb3, gpiob.pb4); + let spi1_sck = pb3.into_alternate_push_pull(&mut gpiob.crl); + let spi1_miso = pb4;//.into_floating_input(&mut gpiob.crl); + let spi1_mosi = gpiob.pb5.into_alternate_push_pull(&mut gpiob.crl); + let spi1_nss = gpioc.pc13.into_push_pull_output(&mut gpioc.crh); + + // Create SPI1 for HAL + let eth_iface = { + let mut spi_eth = { + let spi_eth_port = Spi::spi1( + spi1, + (spi1_sck, spi1_miso, spi1_mosi), + &mut afio.mapr, + enc424j600::spi::interfaces::SPI_MODE, + // Hertz(enc424j600::spi::interfaces::SPI_CLOCK_FREQ), + 9.mhz(), + clocks, + &mut rcc.apb2,); + + SpiEth::new(spi_eth_port, spi1_nss) + .cpu_freq_mhz(72) + }; + + // Init controller + match spi_eth.reset(&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_mac_addr(&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, 88), 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"); + + let mut led = gpioc.pc0.into_push_pull_output(&mut gpioc.crl); + led.set_high().unwrap(); + + 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 + } + } + + #[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 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 STM32F103!"; + write!(socket, "{}\n", greeting).unwrap(); + iprintln!(stim0, + "[{}] Greeting sent, socket closed", instant); + socket.close(); + } + } + } + } + + extern "C" { + fn EXTI0(); + } +};