Add tcp_stm32f407 example

This commit is contained in:
Harry Ho 2020-06-24 11:17:12 +08:00
parent dd062723a3
commit e1c0b0dd23
2 changed files with 277 additions and 0 deletions

View File

@ -2,6 +2,12 @@
## Examples
<!---
TODO: Remove the installation/env setup steps in the following how-to;
only keep example-specific steps.
-->
### Endless Pinging - `tx_stm32f407`
This program demonstrates the Ethernet TX capability on an STM32F407 board connected to an ENC424J600 module via SPI. Once loaded and initialised, a specific ping packet is sent (broadcasted) every 100ms. Such a packet has the following properties:
@ -38,3 +44,48 @@ Note that this program uses ITM for logging output.
```
itmdump -f itm.log -F
```
### TCP Echoing & Greeting - `tcp_stm32f407`
This program demonstrates the TCP connectivity using **smoltcp** on an STM32F407 board connected to an ENC424J600 module via SPI. Once loaded and initialised, two TCP sockets will be opened on the IP address 192.168.1.75/24. These sockets are:
1. **Echoing port - 1234**
* This socket receives raw data on all incoming TCP packets on the said port and prints them back on the output.
* Note that this socket has a time-out of 10s.
2. **Greeting port - 4321**
* This socket waits for a single incoming TCP packet on the said port, and sends a TCP packet holding a text of greeting on the port.
* Note that once a greeting is sent, the port is closed immediately. Further packets received by the controller are dropped until the initiator closes the port.
Note that this program uses ITM for logging output.
#### How-to
1. Prepare the Rust and Cargo environment for building and running the program on STM32F4xx Cortex-M platforms.
2. Install [`itm`](https://docs.rs/itm/) on Cargo:
```
cargo install itm
```
If necessary, add your installation location (`~/.cargo/bin` by default) to `$PATH`.
3. Connect your STM32F407 device to the computer. Without changing any code, you may use an STLink V2 debugger.
4. Run OpenOCD with the appropriate configuration files (e.g. `interface/stlink-v2.cfg`, `target/stm32f4x.cfg`).
5. With OpenOCD running, build and run this program:
```
cargo run --release --example=tcp_stm32f407 --features=stm32f407,smoltcp-phy-all
```
Use appropriate GDB commands such as `c` for continuing.
6. On a separate console window, monitor and observe the ITM output, which is located at the same directory as you started OpenOCD:
```
itmdump -f itm.log -F
```
7. To test the TCP ports, you may use the Netcat utility (`nc`):
```
nc 192.168.1.75 <port-number>
```
Multiple instances of Netcat can run to use all the ports simultaneously. Use Ctrl+C to close the port manually.

226
examples/tcp_stm32f407.rs Normal file
View File

@ -0,0 +1,226 @@
#![no_std]
#![no_main]
extern crate panic_itm;
use cortex_m::iprintln;
use cortex_m_rt::entry;
use embedded_hal::digital::v2::OutputPin;
use stm32f4xx_hal::{
rcc::RccExt,
gpio::GpioExt,
time::U32Ext,
stm32::{CorePeripherals, Peripherals},
spi::Spi
};
use enc424j600;
use enc424j600::EthController;
use enc424j600::smoltcp_phy;
use smoltcp::wire::{
EthernetAddress, IpAddress, IpCidr
};
use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder};
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 stm32f4xx_hal::{
rcc::Clocks,
time::MilliSeconds,
timer::{Timer, Event as TimerEvent},
stm32::SYST,
};
/// 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));
/// 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()
}
#[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-H407 via NIC100/ENC424J600");
// 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();
spisel.set_low().unwrap();
// Create SPI1 for HAL
let spi_eth_port = Spi::spi1(
spi1, (spi1_sck, spi1_miso, spi1_mosi),
enc424j600::spi::SPI_MODE, enc424j600::spi::SPI_CLOCK.into(),
clocks);
let mut spi_eth = enc424j600::SpiEth::new(spi_eth_port, spi1_nss);
// Init
match spi_eth.init_dev() {
Ok(_) => {
iprintln!(stim0, "Ethernet initialised.")
}
Err(_) => {
panic!("Ethernet initialisation Failed!")
}
}
// Setup SysTick
// Reference to stm32-eth:examples/ip.rs
timer_setup(cp.SYST, clocks);
iprintln!(stim0, "Timer initialised.");
// Read MAC
let mut eth_mac_addr: [u8; 6] = [0; 6];
spi_eth.read_from_mac(&mut eth_mac_addr);
iprintln!(stim0,
"MAC Address = {:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}",
eth_mac_addr[0], eth_mac_addr[1],
eth_mac_addr[2], eth_mac_addr[3],
eth_mac_addr[4], eth_mac_addr[5]);
// 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 mut neighbor_cache = NeighborCache::new(&mut neighbor_cache_entries[..]);
let ip_addr = IpCidr::new(IpAddress::v4(192, 168, 1, 75), 24);
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::<TcpSocket>(echo_handle);
if !socket.is_open() {
iprintln!(stim0,
"[{}] Listening to port 1234 for echoing, auto-closing in 10s", instant);
socket.listen(1234).unwrap();
//socket.set_keep_alive(Some(Duration::from_millis(1000)));
socket.set_timeout(Some(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::<TcpSocket>(greet_handle);
if !socket.is_open() {
iprintln!(stim0,
"[{}] Listening to port 4321 for greeting, \
please open 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();
}
}
}
unreachable!()
}