Skip to content

Commit 6b21ffc

Browse files
committed
add example that brings up the ethernet link on the nucleo-h723zg
1 parent c221c51 commit 6b21ffc

File tree

1 file changed

+236
-0
lines changed

1 file changed

+236
-0
lines changed
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
//! Demo for STM32H735G-DK eval board using the Real Time for the Masses
2+
//! (RTIC) framework.
3+
//!
4+
//! This demo responds to pings on 192.168.1.99 (IP address hardcoded below)
5+
//!
6+
//! We use the SysTick timer to create a 1ms timebase for use with smoltcp.
7+
//!
8+
//! The ethernet ring buffers are placed in AXI SRAM, where they can be
9+
//! accessed by both the core and the Ethernet DMA.
10+
//!
11+
//! Run like
12+
//!
13+
//! `cargo flash --example ethernet-rtic-nucleo-h723zg --features=ethernet,stm32h735 --chip=STM32H723ZGTx`
14+
#![deny(warnings)]
15+
#![no_main]
16+
#![no_std]
17+
18+
#[macro_use]
19+
#[allow(unused)]
20+
mod utilities;
21+
22+
use core::sync::atomic::AtomicU32;
23+
24+
use smoltcp::iface::{Config, Interface, SocketSet, SocketStorage};
25+
use smoltcp::time::Instant;
26+
use smoltcp::wire::{HardwareAddress, IpAddress, IpCidr};
27+
28+
use stm32h7xx_hal::{ethernet, rcc::CoreClocks, stm32};
29+
30+
/// Configure SYSTICK for 1ms timebase
31+
fn systick_init(mut syst: stm32::SYST, clocks: CoreClocks) {
32+
let c_ck_mhz = clocks.c_ck().to_MHz();
33+
34+
let syst_calib = 0x3E8;
35+
36+
syst.set_clock_source(cortex_m::peripheral::syst::SystClkSource::Core);
37+
syst.set_reload((syst_calib * c_ck_mhz) - 1);
38+
syst.enable_interrupt();
39+
syst.enable_counter();
40+
}
41+
42+
/// TIME is an atomic u32 that counts milliseconds.
43+
static TIME: AtomicU32 = AtomicU32::new(0);
44+
45+
/// Locally administered MAC address
46+
const MAC_ADDRESS: [u8; 6] = [0x02, 0x00, 0x11, 0x22, 0x33, 0x44];
47+
48+
/// Ethernet descriptor rings are a global singleton
49+
#[link_section = ".axisram.eth"]
50+
static mut DES_RING: ethernet::DesRing<4, 4> = ethernet::DesRing::new();
51+
52+
/// Net storage with static initialisation - another global singleton
53+
pub struct NetStorageStatic<'a> {
54+
socket_storage: [SocketStorage<'a>; 8],
55+
}
56+
static mut STORE: NetStorageStatic = NetStorageStatic {
57+
// Garbage
58+
socket_storage: [SocketStorage::EMPTY; 8],
59+
};
60+
61+
pub struct Net<'a> {
62+
iface: Interface,
63+
ethdev: ethernet::EthernetDMA<4, 4>,
64+
sockets: SocketSet<'a>,
65+
}
66+
impl<'a> Net<'a> {
67+
pub fn new(
68+
store: &'a mut NetStorageStatic<'a>,
69+
mut ethdev: ethernet::EthernetDMA<4, 4>,
70+
ethernet_addr: HardwareAddress,
71+
) -> Self {
72+
let mut config = Config::new();
73+
config.hardware_addr = Some(ethernet_addr);
74+
75+
let mut iface = Interface::new(config, &mut ethdev);
76+
// Set IP address
77+
iface.update_ip_addrs(|addrs| {
78+
let _ = addrs.push(IpCidr::new(IpAddress::v4(192, 168, 1, 99), 0));
79+
});
80+
81+
let sockets = SocketSet::new(&mut store.socket_storage[..]);
82+
83+
Net::<'a> {
84+
iface,
85+
ethdev,
86+
sockets,
87+
}
88+
}
89+
90+
/// Polls on the ethernet interface. You should refer to the smoltcp
91+
/// documentation for poll() to understand how to call poll efficiently
92+
pub fn poll(&mut self, now: i64) {
93+
let timestamp = Instant::from_millis(now);
94+
95+
self.iface
96+
.poll(timestamp, &mut self.ethdev, &mut self.sockets);
97+
}
98+
}
99+
100+
#[rtic::app(device = stm32h7xx_hal::stm32, peripherals = true)]
101+
mod app {
102+
use stm32h7xx_hal::{ethernet, ethernet::PHY, gpio, prelude::*};
103+
104+
use super::*;
105+
use core::sync::atomic::Ordering;
106+
107+
#[shared]
108+
struct SharedResources {}
109+
#[local]
110+
struct LocalResources {
111+
net: Net<'static>,
112+
lan8742a: ethernet::phy::LAN8742A<ethernet::EthernetMAC>,
113+
link_led: gpio::gpioe::PE1<gpio::Output<gpio::PushPull>>,
114+
}
115+
116+
#[init]
117+
fn init(
118+
mut ctx: init::Context,
119+
) -> (SharedResources, LocalResources, init::Monotonics) {
120+
utilities::logger::init();
121+
// Initialise power...
122+
let pwr = ctx.device.PWR.constrain();
123+
let pwrcfg = pwr.ldo().freeze(); // nucleo-h723zg board doesn't have SMPS
124+
125+
// Initialise clocks...
126+
let rcc = ctx.device.RCC.constrain();
127+
let ccdr = rcc
128+
.sys_ck(200.MHz())
129+
.hclk(200.MHz())
130+
.freeze(pwrcfg, &ctx.device.SYSCFG);
131+
132+
// Initialise system...
133+
ctx.core.SCB.invalidate_icache();
134+
ctx.core.SCB.enable_icache();
135+
// TODO: ETH DMA coherence issues
136+
// ctx.core.SCB.enable_dcache(&mut ctx.core.CPUID);
137+
ctx.core.DWT.enable_cycle_counter();
138+
139+
// Initialise IO...
140+
let gpioa = ctx.device.GPIOA.split(ccdr.peripheral.GPIOA);
141+
let gpioc = ctx.device.GPIOC.split(ccdr.peripheral.GPIOC);
142+
let gpioe = ctx.device.GPIOE.split(ccdr.peripheral.GPIOE);
143+
let gpiob = ctx.device.GPIOB.split(ccdr.peripheral.GPIOB);
144+
let mut link_led = gpioe.pe1.into_push_pull_output(); // USR LED1
145+
link_led.set_high();
146+
147+
let rmii_ref_clk = gpioa.pa1.into_alternate();
148+
let rmii_mdio = gpioa.pa2.into_alternate();
149+
let rmii_mdc = gpioc.pc1.into_alternate();
150+
let rmii_crs_dv = gpioa.pa7.into_alternate();
151+
let rmii_rxd0 = gpioc.pc4.into_alternate();
152+
let rmii_rxd1 = gpioc.pc5.into_alternate();
153+
let rmii_tx_en = gpiob.pb11.into_alternate();
154+
let rmii_txd0 = gpiob.pb12.into_alternate();
155+
let rmii_txd1 = gpiob.pb13.into_alternate();
156+
157+
// Initialise ethernet...
158+
assert_eq!(ccdr.clocks.hclk().raw(), 200_000_000); // HCLK 200MHz
159+
assert_eq!(ccdr.clocks.pclk1().raw(), 100_000_000); // PCLK 100MHz
160+
assert_eq!(ccdr.clocks.pclk2().raw(), 100_000_000); // PCLK 100MHz
161+
assert_eq!(ccdr.clocks.pclk4().raw(), 100_000_000); // PCLK 100MHz
162+
163+
let mac_addr = smoltcp::wire::EthernetAddress::from_bytes(&MAC_ADDRESS);
164+
let (eth_dma, eth_mac) = unsafe {
165+
ethernet::new(
166+
ctx.device.ETHERNET_MAC,
167+
ctx.device.ETHERNET_MTL,
168+
ctx.device.ETHERNET_DMA,
169+
(
170+
rmii_ref_clk,
171+
rmii_mdio,
172+
rmii_mdc,
173+
rmii_crs_dv,
174+
rmii_rxd0,
175+
rmii_rxd1,
176+
rmii_tx_en,
177+
rmii_txd0,
178+
rmii_txd1,
179+
),
180+
&mut DES_RING,
181+
mac_addr,
182+
ccdr.peripheral.ETH1MAC,
183+
&ccdr.clocks,
184+
)
185+
};
186+
187+
// Initialise ethernet PHY...
188+
let mut lan8742a = ethernet::phy::LAN8742A::new(eth_mac);
189+
lan8742a.phy_reset();
190+
lan8742a.phy_init();
191+
// The eth_dma should not be used until the PHY reports the link is up
192+
193+
unsafe { ethernet::enable_interrupt() };
194+
195+
// unsafe: mutable reference to static storage, we only do this once
196+
let store = unsafe { &mut STORE };
197+
let net = Net::new(store, eth_dma, mac_addr.into());
198+
199+
// 1ms tick
200+
systick_init(ctx.core.SYST, ccdr.clocks);
201+
202+
(
203+
SharedResources {},
204+
LocalResources {
205+
net,
206+
lan8742a,
207+
link_led,
208+
},
209+
init::Monotonics(),
210+
)
211+
}
212+
213+
#[idle(local = [lan8742a, link_led])]
214+
fn idle(ctx: idle::Context) -> ! {
215+
loop {
216+
// Ethernet
217+
match ctx.local.lan8742a.poll_link() {
218+
true => ctx.local.link_led.set_low(),
219+
_ => ctx.local.link_led.set_high(),
220+
}
221+
}
222+
}
223+
224+
#[task(binds = ETH, local = [net])]
225+
fn ethernet_event(ctx: ethernet_event::Context) {
226+
unsafe { ethernet::interrupt_handler() }
227+
228+
let time = TIME.load(Ordering::Relaxed);
229+
ctx.local.net.poll(time as i64);
230+
}
231+
232+
#[task(binds = SysTick, priority=15)]
233+
fn systick_tick(_: systick_tick::Context) {
234+
TIME.fetch_add(1, Ordering::Relaxed);
235+
}
236+
}

0 commit comments

Comments
 (0)