Add tcp_stm32f407 example
This commit is contained in:
parent
dd062723a3
commit
e1c0b0dd23
51
README.md
51
README.md
|
@ -2,6 +2,12 @@
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
|
<!---
|
||||||
|
TODO: Remove the installation/env setup steps in the following how-to;
|
||||||
|
only keep example-specific steps.
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
### Endless Pinging - `tx_stm32f407`
|
### 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:
|
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
|
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.
|
||||||
|
|
|
@ -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!()
|
||||||
|
}
|
Loading…
Reference in New Issue