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