Skip to content

Commit a5b1b59

Browse files
authored
Merge pull request #445 from jlogan03/h723_ethernet
H723 ethernet
2 parents 5e3e87e + d2a215e commit a5b1b59

File tree

3 files changed

+245
-4
lines changed

3 files changed

+245
-4
lines changed

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,10 @@ required-features = ["rt", "stm32h747cm7", "ethernet"]
179179
name = "ethernet-rtic-stm32h735g-dk"
180180
required-features = ["rt", "stm32h735", "ethernet"]
181181

182+
[[example]]
183+
name = "ethernet-rtic-nucleo-h723zg"
184+
required-features = ["rt", "stm32h735", "ethernet"]
185+
182186
[[example]]
183187
name = "ethernet-nucleo-h743zi2"
184188
required-features = ["rt", "revision_v", "stm32h743v", "ethernet"]
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
//! Demo for Nucleo-H723ZG 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+
57+
static mut STORE: NetStorageStatic = NetStorageStatic {
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 config = Config::new(ethernet_addr);
73+
74+
let mut iface = Interface::new(config, &mut ethdev, Instant::ZERO);
75+
// Set IP address
76+
iface.update_ip_addrs(|addrs| {
77+
let _ = addrs.push(IpCidr::new(IpAddress::v4(192, 168, 1, 99), 0));
78+
});
79+
80+
let sockets = SocketSet::new(&mut store.socket_storage[..]);
81+
82+
Net::<'a> {
83+
iface,
84+
ethdev,
85+
sockets,
86+
}
87+
}
88+
89+
/// Polls on the ethernet interface. You should refer to the smoltcp
90+
/// documentation for poll() to understand how to call poll efficiently
91+
pub fn poll(&mut self, now: i64) {
92+
let timestamp = Instant::from_millis(now);
93+
94+
self.iface
95+
.poll(timestamp, &mut self.ethdev, &mut self.sockets);
96+
}
97+
}
98+
99+
#[rtic::app(device = stm32h7xx_hal::stm32, peripherals = true)]
100+
mod app {
101+
use stm32h7xx_hal::{ethernet, ethernet::PHY, gpio, prelude::*};
102+
103+
use super::*;
104+
use core::sync::atomic::Ordering;
105+
106+
#[shared]
107+
struct SharedResources {}
108+
#[local]
109+
struct LocalResources {
110+
net: Net<'static>,
111+
lan8742a: ethernet::phy::LAN8742A<ethernet::EthernetMAC>,
112+
link_led: gpio::gpioe::PE1<gpio::Output<gpio::PushPull>>,
113+
}
114+
115+
#[init]
116+
fn init(
117+
mut ctx: init::Context,
118+
) -> (SharedResources, LocalResources, init::Monotonics) {
119+
utilities::logger::init();
120+
// Initialise power...
121+
let pwr = ctx.device.PWR.constrain();
122+
let pwrcfg = pwr.ldo().freeze(); // nucleo-h723zg board doesn't have SMPS
123+
124+
// Initialise clocks...
125+
let rcc = ctx.device.RCC.constrain();
126+
let ccdr = rcc
127+
.sys_ck(200.MHz())
128+
.hclk(200.MHz())
129+
.freeze(pwrcfg, &ctx.device.SYSCFG);
130+
131+
// Initialise system...
132+
ctx.core.SCB.invalidate_icache();
133+
ctx.core.SCB.enable_icache();
134+
// TODO: ETH DMA coherence issues
135+
// ctx.core.SCB.enable_dcache(&mut ctx.core.CPUID);
136+
ctx.core.DWT.enable_cycle_counter();
137+
138+
// Initialise IO...
139+
let gpioa = ctx.device.GPIOA.split(ccdr.peripheral.GPIOA);
140+
let gpioc = ctx.device.GPIOC.split(ccdr.peripheral.GPIOC);
141+
let gpioe = ctx.device.GPIOE.split(ccdr.peripheral.GPIOE);
142+
let gpiob = ctx.device.GPIOB.split(ccdr.peripheral.GPIOB);
143+
let mut link_led = gpioe.pe1.into_push_pull_output(); // USR LED1
144+
link_led.set_high();
145+
146+
let rmii_ref_clk = gpioa.pa1.into_alternate();
147+
let rmii_mdio = gpioa.pa2.into_alternate();
148+
let rmii_mdc = gpioc.pc1.into_alternate();
149+
let rmii_crs_dv = gpioa.pa7.into_alternate();
150+
let rmii_rxd0 = gpioc.pc4.into_alternate();
151+
let rmii_rxd1 = gpioc.pc5.into_alternate();
152+
let rmii_tx_en = gpiob.pb11.into_alternate();
153+
let rmii_txd0 = gpiob.pb12.into_alternate();
154+
let rmii_txd1 = gpiob.pb13.into_alternate();
155+
156+
// Initialise ethernet...
157+
assert_eq!(ccdr.clocks.hclk().raw(), 200_000_000); // HCLK 200MHz
158+
assert_eq!(ccdr.clocks.pclk1().raw(), 100_000_000); // PCLK 100MHz
159+
assert_eq!(ccdr.clocks.pclk2().raw(), 100_000_000); // PCLK 100MHz
160+
assert_eq!(ccdr.clocks.pclk4().raw(), 100_000_000); // PCLK 100MHz
161+
162+
let mac_addr = smoltcp::wire::EthernetAddress::from_bytes(&MAC_ADDRESS);
163+
let (eth_dma, eth_mac) = unsafe {
164+
ethernet::new(
165+
ctx.device.ETHERNET_MAC,
166+
ctx.device.ETHERNET_MTL,
167+
ctx.device.ETHERNET_DMA,
168+
(
169+
rmii_ref_clk,
170+
rmii_mdio,
171+
rmii_mdc,
172+
rmii_crs_dv,
173+
rmii_rxd0,
174+
rmii_rxd1,
175+
rmii_tx_en,
176+
rmii_txd0,
177+
rmii_txd1,
178+
),
179+
&mut DES_RING,
180+
mac_addr,
181+
ccdr.peripheral.ETH1MAC,
182+
&ccdr.clocks,
183+
)
184+
};
185+
186+
// Initialise ethernet PHY...
187+
let mut lan8742a = ethernet::phy::LAN8742A::new(eth_mac);
188+
lan8742a.phy_reset();
189+
lan8742a.phy_init();
190+
// The eth_dma should not be used until the PHY reports the link is up
191+
192+
unsafe { ethernet::enable_interrupt() };
193+
194+
// unsafe: mutable reference to static storage, we only do this once
195+
let store = unsafe { &mut STORE };
196+
let net = Net::new(store, eth_dma, mac_addr.into());
197+
198+
// 1ms tick
199+
systick_init(ctx.core.SYST, ccdr.clocks);
200+
201+
(
202+
SharedResources {},
203+
LocalResources {
204+
net,
205+
lan8742a,
206+
link_led,
207+
},
208+
init::Monotonics(),
209+
)
210+
}
211+
212+
#[idle(local = [lan8742a, link_led])]
213+
fn idle(ctx: idle::Context) -> ! {
214+
loop {
215+
// Ethernet
216+
match ctx.local.lan8742a.poll_link() {
217+
true => ctx.local.link_led.set_low(),
218+
_ => ctx.local.link_led.set_high(),
219+
}
220+
}
221+
}
222+
223+
#[task(binds = ETH, local = [net])]
224+
fn ethernet_event(ctx: ethernet_event::Context) {
225+
unsafe { ethernet::interrupt_handler() }
226+
227+
let time = TIME.load(Ordering::Relaxed);
228+
ctx.local.net.poll(time as i64);
229+
}
230+
231+
#[task(binds = SysTick, priority=15)]
232+
fn systick_tick(_: systick_tick::Context) {
233+
TIME.fetch_add(1, Ordering::Relaxed);
234+
}
235+
}

src/ethernet/eth.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,12 @@ pub unsafe fn new_unchecked<const TD: usize, const RD: usize>(
455455
// Ensure syscfg is enabled (for PMCR)
456456
rcc.apb4enr.modify(|_, w| w.syscfgen().set_bit());
457457

458+
// Reset ETH_DMA - write 1 and wait for 0.
459+
// On the H723, we have to do this before prec.enable()
460+
// or the DMA will never come out of reset
461+
eth_dma.dmamr.modify(|_, w| w.swr().set_bit());
462+
while eth_dma.dmamr.read().swr().bit_is_set() {}
463+
458464
// AHB1 ETH1MACEN
459465
prec.enable();
460466

@@ -472,10 +478,6 @@ pub unsafe fn new_unchecked<const TD: usize, const RD: usize>(
472478
//rcc.ahb1rstr.modify(|_, w| w.eth1macrst().clear_bit());
473479

474480
cortex_m::interrupt::free(|_cs| {
475-
// reset ETH_DMA - write 1 and wait for 0
476-
eth_dma.dmamr.modify(|_, w| w.swr().set_bit());
477-
while eth_dma.dmamr.read().swr().bit_is_set() {}
478-
479481
// 200 MHz
480482
eth_mac
481483
.mac1ustcr

0 commit comments

Comments
 (0)