#![no_std] #![no_main] extern crate panic_itm; use cortex_m::{iprintln, iprint}; 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 enc424j600::smoltcp_phy; 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; /// Timer use core::cell::RefCell; use cortex_m::interrupt::Mutex; use cortex_m_rt::exception; use stm32f4xx_hal::{ rcc::Clocks, time::MilliSeconds, timer::{Timer, Event as TimerEvent}, stm32::SYST }; use smoltcp::time::Instant; /// 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)); /// Setup SysTick exception fn timer_setup(syst: SYST, clocks: Clocks) { let mut timer = Timer::syst(syst, TIMER_RATE.hz(), clocks); timer.listen(TimerEvent::TimeOut); } /// 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() } /// use stm32f4xx_hal::{ stm32::SPI1, gpio::{ gpioa::{PA5, PA6, PA7, PA4}, Alternate, AF5, Output, PushPull } }; type SpiEth = enc424j600::Enc424j600< Spi>, PA6>, PA7>)>, PA4> >; 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 = stm32f4xx_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 clocks = c.device.RCC.constrain() .cfgr .sysclk(168.mhz()) .hclk(168.mhz()) .pclk2(42.mhz()) // used by SPI1 .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, 10_500_000.hz(), // PCLK2 @ 42MHz, set SPI1 baud rate as f_PCLK2 divided by 4 clocks); SpiEth::new(spi_eth_port, spi1_nss) .cpu_freq_mhz(168) }; // 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, 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 } } #[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 STM32-F407!"; write!(socket, "{}\n", greeting).unwrap(); iprintln!(stim0, "[{}] Greeting sent, socket closed", instant); socket.close(); } } } } extern "C" { fn EXTI0(); } };