Skip to content

Commit 4ae71ea

Browse files
committed
Add RTICv2 example skeleton
1 parent fc3ceea commit 4ae71ea

File tree

6 files changed

+589
-0
lines changed

6 files changed

+589
-0
lines changed

examples/rtic-stm32/.cargo/config

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2+
runner = "probe-rs run --chip STM32F429ZITx"
3+
rustflags = [
4+
"-C", "link-arg=-Tlink.x",
5+
"-C", "link-arg=-Tdefmt.x",
6+
]
7+
8+
9+
[build]
10+
target = "thumbv7em-none-eabi"

examples/rtic-stm32/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
target/
2+
Cargo.lock

examples/rtic-stm32/Cargo.toml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
[package]
2+
version = "0.1.0"
3+
name = "stm32-rtic"
4+
edition = "2021"
5+
resolver = "2"
6+
7+
[dependencies]
8+
volatile-register = "0.2"
9+
aligned = "0.4"
10+
stm32f4xx-hal = { version = "0.14", features = ["stm32f429"] }
11+
stm32f4 = { version = "0.15" }
12+
ieee802_3_miim = "0.8"
13+
defmt = { version = "0.3" }
14+
futures = { version = "0.3", default-features = false, features = [
15+
"async-await",
16+
] }
17+
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
18+
cortex-m-rt = "0.7"
19+
fugit = "0.3"
20+
defmt-rtt = "0.4"
21+
panic-probe = { version = "0.3", features = ["print-defmt"] }
22+
systick-monotonic = "1.0"
23+
rtic = { version = "2.0.1", features = ["thumbv7-backend"] }
24+
stm32-eth = { version = "0.5.2", default-features = false, features = ["defmt", "stm32f429", "async-await"] }
25+
26+
[profile.release]
27+
debug = 2
28+
lto = true
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[toolchain]
2+
channel = "nightly"
3+
components = ["llvm-tools"]
4+
targets = ["thumbv7em-none-eabi"]

examples/rtic-stm32/src/common.rs

Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
//! Common features used in examples.
2+
//!
3+
//! Note that this module isn't an example by itself.
4+
5+
use defmt_rtt as _;
6+
use panic_probe as _;
7+
8+
use stm32_eth::{
9+
hal::{
10+
gpio::{GpioExt, *},
11+
rcc::Clocks,
12+
},
13+
EthPins, PartsIn,
14+
};
15+
16+
use fugit::RateExtU32;
17+
use stm32_eth::hal::rcc::RccExt;
18+
19+
/// Setup the clocks and return clocks and a GPIO struct that
20+
/// can be used to set up all of the pins.
21+
///
22+
/// This configures HCLK to be at least 25 MHz, which is the minimum required
23+
/// for ethernet operation to be valid.
24+
pub fn setup_peripherals(p: stm32_eth::stm32::Peripherals) -> (Clocks, Gpio, PartsIn) {
25+
let ethernet = PartsIn {
26+
dma: p.ETHERNET_DMA,
27+
mac: p.ETHERNET_MAC,
28+
mmc: p.ETHERNET_MMC,
29+
#[cfg(feature = "ptp")]
30+
ptp: p.ETHERNET_PTP,
31+
};
32+
33+
let rcc = p.RCC.constrain();
34+
35+
let clocks = rcc.cfgr.sysclk(96.MHz()).hclk(96.MHz());
36+
37+
#[cfg(feature = "stm32f4xx-hal")]
38+
let clocks = {
39+
if cfg!(hse = "bypass") {
40+
clocks.use_hse(8.MHz()).bypass_hse_oscillator()
41+
} else if cfg!(hse = "oscillator") {
42+
clocks.use_hse(8.MHz())
43+
} else {
44+
clocks
45+
}
46+
};
47+
48+
#[cfg(feature = "stm32f7xx-hal")]
49+
let clocks = {
50+
if cfg!(hse = "bypass") {
51+
clocks.hse(stm32_eth::hal::rcc::HSEClock::new(
52+
8.MHz(),
53+
stm32_eth::hal::rcc::HSEClockMode::Bypass,
54+
))
55+
} else if cfg!(hse = "oscillator") {
56+
clocks.hse(stm32_eth::hal::rcc::HSEClock::new(
57+
8.MHz(),
58+
stm32_eth::hal::rcc::HSEClockMode::Oscillator,
59+
))
60+
} else {
61+
clocks
62+
}
63+
};
64+
65+
let clocks = clocks.freeze();
66+
67+
let gpio = Gpio {
68+
gpioa: p.GPIOA.split(),
69+
gpiob: p.GPIOB.split(),
70+
gpioc: p.GPIOC.split(),
71+
gpiog: p.GPIOG.split(),
72+
};
73+
74+
(clocks, gpio, ethernet)
75+
}
76+
77+
pub struct Gpio {
78+
pub gpioa: gpioa::Parts,
79+
pub gpiob: gpiob::Parts,
80+
pub gpioc: gpioc::Parts,
81+
pub gpiog: gpiog::Parts,
82+
}
83+
84+
pub type RefClk = PA1<Input>;
85+
pub type Crs = PA7<Input>;
86+
pub type TxD1 = PB13<Input>;
87+
pub type RxD0 = PC4<Input>;
88+
pub type RxD1 = PC5<Input>;
89+
90+
#[cfg(not(pins = "nucleo"))]
91+
pub type TxEn = PB11<Input>;
92+
#[cfg(not(pins = "nucleo"))]
93+
pub type TxD0 = PB12<Input>;
94+
95+
#[cfg(pins = "nucleo")]
96+
pub type TxEn = PG11<Input>;
97+
#[cfg(pins = "nucleo")]
98+
pub type TxD0 = PG13<Input>;
99+
100+
pub type Mdio = PA2<Alternate<11>>;
101+
pub type Mdc = PC1<Alternate<11>>;
102+
103+
#[cfg(not(pps = "alternate"))]
104+
pub type Pps = PB5<Output<PushPull>>;
105+
106+
#[cfg(pps = "alternate")]
107+
pub type Pps = PG8<Output<PushPull>>;
108+
109+
pub fn setup_pins(
110+
gpio: Gpio,
111+
) -> (
112+
EthPins<RefClk, Crs, TxEn, TxD0, TxD1, RxD0, RxD1>,
113+
Mdio,
114+
Mdc,
115+
Pps,
116+
) {
117+
#[allow(unused_variables)]
118+
let Gpio {
119+
gpioa,
120+
gpiob,
121+
gpioc,
122+
gpiog,
123+
} = gpio;
124+
125+
let ref_clk = gpioa.pa1.into_floating_input();
126+
let crs = gpioa.pa7.into_floating_input();
127+
let tx_d1 = gpiob.pb13.into_floating_input();
128+
let rx_d0 = gpioc.pc4.into_floating_input();
129+
let rx_d1 = gpioc.pc5.into_floating_input();
130+
131+
#[cfg(not(pins = "nucleo"))]
132+
let (tx_en, tx_d0) = (
133+
gpiob.pb11.into_floating_input(),
134+
gpiob.pb12.into_floating_input(),
135+
);
136+
137+
#[cfg(pins = "nucleo")]
138+
let (tx_en, tx_d0) = {
139+
(
140+
gpiog.pg11.into_floating_input(),
141+
gpiog.pg13.into_floating_input(),
142+
)
143+
};
144+
145+
#[cfg(feature = "stm32f4xx-hal")]
146+
let (mdio, mdc) = {
147+
let mut mdio = gpioa.pa2.into_alternate();
148+
mdio.set_speed(Speed::VeryHigh);
149+
let mut mdc = gpioc.pc1.into_alternate();
150+
mdc.set_speed(Speed::VeryHigh);
151+
(mdio, mdc)
152+
};
153+
154+
#[cfg(any(feature = "stm32f7xx-hal"))]
155+
let (mdio, mdc) = (
156+
gpioa.pa2.into_alternate().set_speed(Speed::VeryHigh),
157+
gpioc.pc1.into_alternate().set_speed(Speed::VeryHigh),
158+
);
159+
160+
#[cfg(not(pps = "alternate"))]
161+
let pps = gpiob.pb5.into_push_pull_output();
162+
#[cfg(pps = "alternate")]
163+
let pps = gpiog.pg8.into_push_pull_output();
164+
165+
(
166+
EthPins {
167+
ref_clk,
168+
crs,
169+
tx_en,
170+
tx_d0,
171+
tx_d1,
172+
rx_d0,
173+
rx_d1,
174+
},
175+
mdio,
176+
mdc,
177+
pps,
178+
)
179+
}
180+
181+
use ieee802_3_miim::{
182+
phy::{
183+
lan87xxa::{LAN8720A, LAN8742A},
184+
BarePhy, KSZ8081R,
185+
},
186+
Miim, Pause, Phy,
187+
};
188+
189+
/// An ethernet PHY
190+
pub enum EthernetPhy<M: Miim> {
191+
/// LAN8720A
192+
LAN8720A(LAN8720A<M>),
193+
/// LAN8742A
194+
LAN8742A(LAN8742A<M>),
195+
/// KSZ8081R
196+
KSZ8081R(KSZ8081R<M>),
197+
}
198+
199+
impl<M: Miim> Phy<M> for EthernetPhy<M> {
200+
fn best_supported_advertisement(&self) -> ieee802_3_miim::AutoNegotiationAdvertisement {
201+
match self {
202+
EthernetPhy::LAN8720A(phy) => phy.best_supported_advertisement(),
203+
EthernetPhy::LAN8742A(phy) => phy.best_supported_advertisement(),
204+
EthernetPhy::KSZ8081R(phy) => phy.best_supported_advertisement(),
205+
}
206+
}
207+
208+
fn get_miim(&mut self) -> &mut M {
209+
match self {
210+
EthernetPhy::LAN8720A(phy) => phy.get_miim(),
211+
EthernetPhy::LAN8742A(phy) => phy.get_miim(),
212+
EthernetPhy::KSZ8081R(phy) => phy.get_miim(),
213+
}
214+
}
215+
216+
fn get_phy_addr(&self) -> u8 {
217+
match self {
218+
EthernetPhy::LAN8720A(phy) => phy.get_phy_addr(),
219+
EthernetPhy::LAN8742A(phy) => phy.get_phy_addr(),
220+
EthernetPhy::KSZ8081R(phy) => phy.get_phy_addr(),
221+
}
222+
}
223+
}
224+
225+
impl<M: Miim> EthernetPhy<M> {
226+
/// Attempt to create one of the known PHYs from the given
227+
/// MIIM.
228+
///
229+
/// Returns an error if the PHY does not support the extended register
230+
/// set, or if the PHY's identifier does not correspond to a known PHY.
231+
pub fn from_miim(miim: M, phy_addr: u8) -> Result<Self, M> {
232+
let mut bare = BarePhy::new(miim, phy_addr, Pause::NoPause);
233+
let phy_ident = if let Some(id) = bare.phy_ident() {
234+
id.raw_u32()
235+
} else {
236+
return Err(bare.release());
237+
};
238+
let miim = bare.release();
239+
match phy_ident & 0xFFFFFFF0 {
240+
0x0007C0F0 => Ok(Self::LAN8720A(LAN8720A::new(miim, phy_addr))),
241+
0x0007C130 => Ok(Self::LAN8742A(LAN8742A::new(miim, phy_addr))),
242+
0x00221560 => Ok(Self::KSZ8081R(KSZ8081R::new(miim, phy_addr))),
243+
_ => Err(miim),
244+
}
245+
}
246+
247+
/// Get a string describing the type of PHY
248+
pub const fn ident_string(&self) -> &'static str {
249+
match self {
250+
EthernetPhy::LAN8720A(_) => "LAN8720A",
251+
EthernetPhy::LAN8742A(_) => "LAN8742A",
252+
EthernetPhy::KSZ8081R(_) => "KSZ8081R",
253+
}
254+
}
255+
256+
/// Initialize the PHY
257+
pub fn phy_init(&mut self) {
258+
match self {
259+
EthernetPhy::LAN8720A(phy) => phy.phy_init(),
260+
EthernetPhy::LAN8742A(phy) => phy.phy_init(),
261+
EthernetPhy::KSZ8081R(phy) => {
262+
phy.set_autonegotiation_advertisement(phy.best_supported_advertisement());
263+
}
264+
}
265+
}
266+
267+
#[allow(dead_code)]
268+
pub fn speed(&mut self) -> Option<ieee802_3_miim::phy::PhySpeed> {
269+
match self {
270+
EthernetPhy::LAN8720A(phy) => phy.link_speed(),
271+
EthernetPhy::LAN8742A(phy) => phy.link_speed(),
272+
EthernetPhy::KSZ8081R(phy) => phy.link_speed(),
273+
}
274+
}
275+
276+
#[allow(dead_code)]
277+
pub fn release(self) -> M {
278+
match self {
279+
EthernetPhy::LAN8720A(phy) => phy.release(),
280+
EthernetPhy::LAN8742A(phy) => phy.release(),
281+
EthernetPhy::KSZ8081R(phy) => phy.release(),
282+
}
283+
}
284+
}

0 commit comments

Comments
 (0)