aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-nrf/CHANGELOG.md4
-rw-r--r--embassy-nrf/src/chips/nrf54l15_app.rs2
-rw-r--r--embassy-nrf/src/gpiote.rs68
-rw-r--r--embassy-nxp/CHANGELOG.md1
-rw-r--r--embassy-nxp/Cargo.toml4
-rw-r--r--embassy-nxp/build.rs353
-rw-r--r--embassy-nxp/src/chips/lpc55.rs127
-rw-r--r--embassy-nxp/src/chips/mimxrt1011.rs104
-rw-r--r--embassy-nxp/src/chips/mimxrt1062.rs273
-rw-r--r--embassy-nxp/src/dma.rs1
-rw-r--r--embassy-nxp/src/dma/lpc55.rs47
-rw-r--r--embassy-nxp/src/gpio/lpc55.rs93
-rw-r--r--embassy-nxp/src/gpio/rt1xxx.rs50
-rw-r--r--embassy-nxp/src/iomuxc.rs29
-rw-r--r--embassy-nxp/src/lib.rs16
-rw-r--r--embassy-nxp/src/pwm.rs2
-rw-r--r--embassy-nxp/src/pwm/lpc55.rs113
-rw-r--r--embassy-nxp/src/sct.rs56
-rw-r--r--embassy-nxp/src/usart.rs2
-rw-r--r--embassy-nxp/src/usart/lpc55.rs115
-rw-r--r--embassy-stm32-wpan/CHANGELOG.md1
-rw-r--r--embassy-stm32-wpan/Cargo.toml4
-rw-r--r--embassy-stm32-wpan/src/mac/commands.rs26
-rw-r--r--embassy-stm32-wpan/src/mac/control.rs185
-rw-r--r--embassy-stm32-wpan/src/mac/driver.rs143
-rw-r--r--embassy-stm32-wpan/src/mac/indications.rs35
-rw-r--r--embassy-stm32-wpan/src/mac/mod.rs6
-rw-r--r--embassy-stm32-wpan/src/mac/runner.rs158
-rw-r--r--embassy-stm32-wpan/src/mac/typedefs.rs55
-rw-r--r--embassy-stm32-wpan/src/sub/mac.rs83
-rw-r--r--embassy-stm32-wpan/src/sub/mm.rs2
-rw-r--r--embassy-stm32/CHANGELOG.md8
-rw-r--r--embassy-stm32/src/adc/adc4.rs2
-rw-r--r--embassy-stm32/src/adc/c0.rs124
-rw-r--r--embassy-stm32/src/adc/g4.rs65
-rw-r--r--embassy-stm32/src/adc/mod.rs54
-rw-r--r--embassy-stm32/src/adc/v2.rs2
-rw-r--r--embassy-stm32/src/adc/v3.rs85
-rw-r--r--embassy-stm32/src/adc/v4.rs2
-rw-r--r--embassy-stm32/src/lib.rs5
-rw-r--r--embassy-stm32/src/low_power.rs118
-rw-r--r--embassy-stm32/src/rcc/l.rs43
-rw-r--r--embassy-stm32/src/rcc/mod.rs34
-rw-r--r--embassy-stm32/src/rtc/low_power.rs69
-rw-r--r--embassy-stm32/src/rtc/mod.rs7
-rw-r--r--embassy-stm32/src/sai/mod.rs4
-rw-r--r--embassy-stm32/src/time_driver.rs109
-rw-r--r--embassy-stm32/src/timer/complementary_pwm.rs109
-rw-r--r--embassy-stm32/src/timer/input_capture.rs1
-rw-r--r--embassy-stm32/src/timer/low_level.rs234
-rw-r--r--embassy-stm32/src/timer/pwm_input.rs1
-rw-r--r--embassy-stm32/src/timer/simple_pwm.rs241
-rw-r--r--examples/lpc55s69/src/bin/pwm.rs2
-rw-r--r--examples/lpc55s69/src/bin/usart_async.rs4
-rw-r--r--examples/nrf52840/src/bin/gpiote_channel.rs8
-rw-r--r--examples/nrf5340/src/bin/gpiote_channel.rs8
-rw-r--r--examples/nrf54l15/memory.x2
-rw-r--r--examples/nrf54l15/src/bin/gpiote_channel.rs8
-rw-r--r--examples/rp/src/bin/wifi_webrequest.rs90
-rw-r--r--examples/stm32h5/src/bin/adc_dma.rs9
-rw-r--r--examples/stm32h5/src/bin/stop.rs12
-rw-r--r--examples/stm32h723/src/bin/spdifrx.rs2
-rw-r--r--examples/stm32l5/src/bin/stop.rs12
-rw-r--r--examples/stm32wb/src/bin/mac_ffd_net.rs185
-rw-r--r--examples/stm32wle5/src/bin/adc.rs12
-rw-r--r--examples/stm32wle5/src/bin/blinky.rs12
-rw-r--r--examples/stm32wle5/src/bin/button_exti.rs12
-rw-r--r--examples/stm32wle5/src/bin/i2c.rs13
-rw-r--r--tests/stm32/src/bin/stop.rs14
69 files changed, 1979 insertions, 1901 deletions
diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md
index 72ecb116a..f6fe1e14f 100644
--- a/embassy-nrf/CHANGELOG.md
+++ b/embassy-nrf/CHANGELOG.md
@@ -23,6 +23,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
23- bugfix: Do not write to UICR from non-secure code on nrf53 23- bugfix: Do not write to UICR from non-secure code on nrf53
24- bugfix: Add delay to uart init anomaly fix 24- bugfix: Add delay to uart init anomaly fix
25- changed: `BufferedUarte::read_ready` now uses the same definition for 'empty' so following read calls will not block when true is returned 25- changed: `BufferedUarte::read_ready` now uses the same definition for 'empty' so following read calls will not block when true is returned
26- added: add `gpiote::InputChannel::wait_for_high()` and `wait_for_low()` to wait for specific signal level
27- changed: `gpiote::InputChannel::wait()` now takes a mutable reference to `self` to avoid interference from concurrent calls
28- changed: `gpiote::InputChannel::wait()` now ensures events are seen as soon as the function is called, even if the future is not polled
29- bugfix: use correct flash size for nRF54l
26 30
27## 0.8.0 - 2025-09-30 31## 0.8.0 - 2025-09-30
28 32
diff --git a/embassy-nrf/src/chips/nrf54l15_app.rs b/embassy-nrf/src/chips/nrf54l15_app.rs
index 0724f2ff6..8846717db 100644
--- a/embassy-nrf/src/chips/nrf54l15_app.rs
+++ b/embassy-nrf/src/chips/nrf54l15_app.rs
@@ -204,7 +204,7 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 1024;
204 204
205// 1.5 MB NVM 205// 1.5 MB NVM
206#[allow(unused)] 206#[allow(unused)]
207pub const FLASH_SIZE: usize = 1536 * 1024; 207pub const FLASH_SIZE: usize = 1524 * 1024;
208 208
209embassy_hal_internal::peripherals! { 209embassy_hal_internal::peripherals! {
210 // PPI 210 // PPI
diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs
index 91944d8cd..d4f6668f3 100644
--- a/embassy-nrf/src/gpiote.rs
+++ b/embassy-nrf/src/gpiote.rs
@@ -349,16 +349,73 @@ impl<'d> InputChannel<'d> {
349 } 349 }
350 350
351 /// Asynchronously wait for an event in this channel. 351 /// Asynchronously wait for an event in this channel.
352 pub async fn wait(&self) { 352 ///
353 let g = self.ch.regs(); 353 /// It is possible to call this function and await the returned future later.
354 let num = self.ch.number(); 354 /// If an even occurs in the mean time, the future will immediately report ready.
355 let waker = self.ch.waker(); 355 pub fn wait(&mut self) -> impl Future<Output = ()> {
356 // NOTE: This is `-> impl Future` and not an `async fn` on purpose.
357 // Otherwise, events will only be detected starting at the first poll of the returned future.
358 Self::wait_internal(&mut self.ch)
359 }
360
361 /// Asynchronously wait for the pin to become high.
362 ///
363 /// The channel must be configured with [`InputChannelPolarity::LoToHi`] or [`InputChannelPolarity::Toggle`].
364 /// If the channel is not configured to detect rising edges, it is unspecified when the returned future completes.
365 ///
366 /// It is possible to call this function and await the returned future later.
367 /// If an even occurs in the mean time, the future will immediately report ready.
368 pub fn wait_for_high(&mut self) -> impl Future<Output = ()> {
369 // NOTE: This is `-> impl Future` and not an `async fn` on purpose.
370 // Otherwise, events will only be detected starting at the first poll of the returned future.
371
372 // Subscribe to the event before checking the pin level.
373 let wait = Self::wait_internal(&mut self.ch);
374 let pin = &self.pin;
375 async move {
376 if pin.is_high() {
377 return;
378 }
379 wait.await;
380 }
381 }
382
383 /// Asynchronously wait for the pin to become low.
384 ///
385 /// The channel must be configured with [`InputChannelPolarity::HiToLo`] or [`InputChannelPolarity::Toggle`].
386 /// If the channel is not configured to detect falling edges, it is unspecified when the returned future completes.
387 ///
388 /// It is possible to call this function and await the returned future later.
389 /// If an even occurs in the mean time, the future will immediately report ready.
390 pub fn wait_for_low(&mut self) -> impl Future<Output = ()> {
391 // NOTE: This is `-> impl Future` and not an `async fn` on purpose.
392 // Otherwise, events will only be detected starting at the first poll of the returned future.
393
394 // Subscribe to the event before checking the pin level.
395 let wait = Self::wait_internal(&mut self.ch);
396 let pin = &self.pin;
397 async move {
398 if pin.is_low() {
399 return;
400 }
401 wait.await;
402 }
403 }
404
405 /// Internal implementation for `wait()` and friends.
406 fn wait_internal(channel: &mut Peri<'_, AnyChannel>) -> impl Future<Output = ()> {
407 // NOTE: This is `-> impl Future` and not an `async fn` on purpose.
408 // Otherwise, events will only be detected starting at the first poll of the returned future.
409
410 let g = channel.regs();
411 let num = channel.number();
412 let waker = channel.waker();
356 413
357 // Enable interrupt 414 // Enable interrupt
358 g.events_in(num).write_value(0); 415 g.events_in(num).write_value(0);
359 g.intenset(INTNUM).write(|w| w.0 = 1 << num); 416 g.intenset(INTNUM).write(|w| w.0 = 1 << num);
360 417
361 poll_fn(|cx| { 418 poll_fn(move |cx| {
362 CHANNEL_WAKERS[waker].register(cx.waker()); 419 CHANNEL_WAKERS[waker].register(cx.waker());
363 420
364 if g.events_in(num).read() != 0 { 421 if g.events_in(num).read() != 0 {
@@ -367,7 +424,6 @@ impl<'d> InputChannel<'d> {
367 Poll::Pending 424 Poll::Pending
368 } 425 }
369 }) 426 })
370 .await;
371 } 427 }
372 428
373 /// Get the associated input pin. 429 /// Get the associated input pin.
diff --git a/embassy-nxp/CHANGELOG.md b/embassy-nxp/CHANGELOG.md
index 39f5c75bd..e6f117da4 100644
--- a/embassy-nxp/CHANGELOG.md
+++ b/embassy-nxp/CHANGELOG.md
@@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7 7
8<!-- next-header --> 8<!-- next-header -->
9## Unreleased - ReleaseDate 9## Unreleased - ReleaseDate
10- Codegen using `nxp-pac` metadata
10- LPC55: PWM simple 11- LPC55: PWM simple
11- LPC55: Move ALT definitions for USART to TX/RX pin impls. 12- LPC55: Move ALT definitions for USART to TX/RX pin impls.
12- LPC55: Remove internal match_iocon macro 13- LPC55: Remove internal match_iocon macro
diff --git a/embassy-nxp/Cargo.toml b/embassy-nxp/Cargo.toml
index f8c63ba29..b78c26c77 100644
--- a/embassy-nxp/Cargo.toml
+++ b/embassy-nxp/Cargo.toml
@@ -38,13 +38,13 @@ embassy-time-queue-utils = { version = "0.3.0", path = "../embassy-time-queue-ut
38embedded-io = "0.6.1" 38embedded-io = "0.6.1"
39embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } 39embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
40## Chip dependencies 40## Chip dependencies
41nxp-pac = { version = "0.1.0", optional = true, git = "https://github.com/i509VCB/nxp-pac", rev = "477dfdbfd5e6c75c0730c56494b601c1b2257263"} 41nxp-pac = { version = "0.1.0", optional = true, git = "https://github.com/i509VCB/nxp-pac", rev = "af5122e1cbe1483833c5d2e5af96b26a34ed5d62"}
42 42
43imxrt-rt = { version = "0.1.7", optional = true, features = ["device"] } 43imxrt-rt = { version = "0.1.7", optional = true, features = ["device"] }
44 44
45[build-dependencies] 45[build-dependencies]
46cfg_aliases = "0.2.1" 46cfg_aliases = "0.2.1"
47nxp-pac = { version = "0.1.0", git = "https://github.com/i509VCB/nxp-pac", rev = "477dfdbfd5e6c75c0730c56494b601c1b2257263", features = ["metadata"], optional = true } 47nxp-pac = { version = "0.1.0", git = "https://github.com/i509VCB/nxp-pac", rev = "af5122e1cbe1483833c5d2e5af96b26a34ed5d62", features = ["metadata"], optional = true }
48proc-macro2 = "1.0.95" 48proc-macro2 = "1.0.95"
49quote = "1.0.15" 49quote = "1.0.15"
50 50
diff --git a/embassy-nxp/build.rs b/embassy-nxp/build.rs
index f3c062c87..f53c29161 100644
--- a/embassy-nxp/build.rs
+++ b/embassy-nxp/build.rs
@@ -4,10 +4,12 @@ use std::process::Command;
4use std::{env, fs}; 4use std::{env, fs};
5 5
6use cfg_aliases::cfg_aliases; 6use cfg_aliases::cfg_aliases;
7#[cfg(feature = "_rt1xxx")]
8use nxp_pac::metadata; 7use nxp_pac::metadata;
8use nxp_pac::metadata::{METADATA, Peripheral};
9#[allow(unused)] 9#[allow(unused)]
10use proc_macro2::TokenStream; 10use proc_macro2::TokenStream;
11use proc_macro2::{Ident, Literal, Span};
12use quote::format_ident;
11#[allow(unused)] 13#[allow(unused)]
12use quote::quote; 14use quote::quote;
13 15
@@ -31,56 +33,188 @@ fn main() {
31 .unwrap() 33 .unwrap()
32 .to_ascii_lowercase(); 34 .to_ascii_lowercase();
33 35
36 let singletons = singletons(&mut cfgs);
37
34 cfg_aliases! { 38 cfg_aliases! {
35 rt1xxx: { any(feature = "mimxrt1011", feature = "mimxrt1062") }, 39 rt1xxx: { any(feature = "mimxrt1011", feature = "mimxrt1062") },
36 gpio1: { any(feature = "mimxrt1011", feature = "mimxrt1062") },
37 gpio2: { any(feature = "mimxrt1011", feature = "mimxrt1062") },
38 gpio3: { feature = "mimxrt1062" },
39 gpio4: { feature = "mimxrt1062" },
40 gpio5: { any(feature = "mimxrt1011", feature = "mimxrt1062") },
41 } 40 }
42 41
43 eprintln!("chip: {chip_name}"); 42 eprintln!("chip: {chip_name}");
44 43
45 generate_code(); 44 generate_code(&mut cfgs, &singletons);
46} 45}
47 46
48#[cfg(feature = "_rt1xxx")] 47/// A peripheral singleton returned by `embassy_nxp::init`.
49fn generate_iomuxc() -> TokenStream { 48struct Singleton {
50 use proc_macro2::{Ident, Span}; 49 name: String,
51 50
52 let pads = metadata::iomuxc::IOMUXC_REGISTERS.iter().map(|registers| { 51 /// A cfg guard which indicates whether the `Peripherals` struct will give the user this singleton.
53 let name = Ident::new(&registers.name, Span::call_site()); 52 cfg: Option<TokenStream>,
54 let address = registers.pad_ctl; 53}
55 54
56 quote! { 55fn singletons(cfgs: &mut common::CfgSet) -> Vec<Singleton> {
57 pub const #name: u32 = #address; 56 let mut singletons = Vec::new();
57
58 for peripheral in METADATA.peripherals {
59 // GPIO and DMA are generated in a 2nd pass.
60 let skip_singleton = if peripheral.name.starts_with("GPIO") || peripheral.name.starts_with("DMA") {
61 true
62 } else {
63 false
64 };
65
66 if !skip_singleton {
67 singletons.push(Singleton {
68 name: peripheral.name.into(),
69 cfg: None,
70 });
58 } 71 }
59 }); 72 }
60 73
61 let muxes = metadata::iomuxc::IOMUXC_REGISTERS.iter().map(|registers| { 74 cfgs.declare_all(&[
62 let name = Ident::new(&registers.name, Span::call_site()); 75 "gpio1",
63 let address = registers.mux_ctl; 76 "gpio1_hi",
77 "gpio2",
78 "gpio2_hi",
79 "gpio3",
80 "gpio3_hi",
81 "gpio4",
82 "gpio4_hi",
83 "gpio5",
84 "gpio5_hi",
85 "gpio10",
86 "gpio10_hi",
87 ]);
64 88
65 quote! { 89 for peripheral in METADATA.peripherals.iter().filter(|p| p.name.starts_with("GPIO")) {
66 pub const #name: u32 = #address; 90 let number = peripheral.name.strip_prefix("GPIO").unwrap();
91 assert!(number.parse::<u8>().is_ok());
92 cfgs.enable(format!("gpio{}", number));
93
94 for signal in peripheral.signals.iter() {
95 let pin_number = signal.name.parse::<u8>().unwrap();
96
97 if pin_number > 15 {
98 cfgs.enable(format!("gpio{}_hi", number));
99 }
100
101 // GPIO signals only defined a single signal, on a single pin.
102 assert_eq!(signal.pins.len(), 1);
103
104 singletons.push(Singleton {
105 name: signal.pins[0].pin.into(),
106 cfg: None,
107 });
108 }
109 }
110
111 for peripheral in METADATA.peripherals.iter().filter(|p| p.name.starts_with("DMA")) {
112 let instance = peripheral.name.strip_prefix("DMA").unwrap();
113 assert!(instance.parse::<u8>().is_ok());
114
115 for signal in peripheral.signals.iter() {
116 let channel_number = signal.name.parse::<u8>().unwrap();
117 let name = format!("DMA{instance}_CH{channel_number}");
118
119 // DMA has no pins.
120 assert!(signal.pins.is_empty());
121
122 singletons.push(Singleton { name, cfg: None });
123 }
124 }
125
126 for peripheral in METADATA.peripherals.iter().filter(|p| p.name.starts_with("SCT")) {
127 let instance = peripheral.name.strip_prefix("SCT").unwrap();
128 assert!(instance.parse::<u8>().is_ok());
129
130 for signal in peripheral.signals.iter() {
131 if !signal.name.starts_with("OUT") {
132 continue;
133 }
134
135 let channel_number = signal.name.strip_prefix("OUT").unwrap().parse::<u8>().unwrap();
136 let name = format!("SCT{instance}_OUT{channel_number}");
137
138 singletons.push(Singleton { name, cfg: None });
67 } 139 }
140 }
141
142 singletons
143}
144
145#[cfg(feature = "_rt1xxx")]
146fn generate_iomuxc() -> TokenStream {
147 let iomuxc_pad_impls = metadata::METADATA
148 .pins
149 .iter()
150 .filter(|p| p.iomuxc.as_ref().filter(|i| i.mux.is_some()).is_some())
151 .map(|pin| {
152 let Some(ref iomuxc) = pin.iomuxc else {
153 panic!("Pin {} has no IOMUXC definitions", pin.name);
154 };
155
156 let name = Ident::new(pin.name, Span::call_site());
157 let mux = iomuxc.mux.unwrap();
158 let pad = iomuxc.pad;
159
160 quote! {
161 impl_iomuxc_pad!(#name, #pad, #mux);
162 }
163 });
164
165 let base_match_arms = metadata::METADATA
166 .peripherals
167 .iter()
168 .filter(|p| p.name.starts_with("GPIO"))
169 .map(|peripheral| {
170 peripheral.signals.iter().map(|signal| {
171 // All GPIO signals have a single pin.
172 let pin = &signal.pins[0];
173 let instance = peripheral.name.strip_prefix("GPIO").unwrap();
174 let bank_match = format_ident!("Gpio{}", instance);
175 let pin_number = signal.name.parse::<u8>().unwrap();
176 let pin_ident = Ident::new(pin.pin, Span::call_site());
177
178 quote! {
179 (Bank::#bank_match, #pin_number) => <crate::peripherals::#pin_ident as crate::iomuxc::SealedPad>
180 }
181 })
182 })
183 .flatten()
184 .collect::<Vec<_>>();
185
186 let pad_match_arms = base_match_arms.iter().map(|arm| {
187 quote! { #arm::PAD }
188 });
189
190 let mux_match_arms = base_match_arms.iter().map(|arm| {
191 quote! { #arm::MUX }
68 }); 192 });
69 193
70 quote! { 194 quote! {
71 pub mod iomuxc { 195 #(#iomuxc_pad_impls)*
72 pub mod pads { 196
73 #(#pads)* 197 pub(crate) fn iomuxc_pad(bank: crate::gpio::Bank, pin: u8) -> *mut () {
198 use crate::gpio::Bank;
199
200 match (bank, pin) {
201 #(#pad_match_arms),*,
202 _ => unreachable!()
74 } 203 }
204 }
205
206 pub(crate) fn iomuxc_mux(bank: crate::gpio::Bank, pin: u8) -> Option<*mut ()> {
207 use crate::gpio::Bank;
75 208
76 pub mod muxes { 209 match (bank, pin) {
77 #(#muxes)* 210 #(#mux_match_arms),*,
211 _ => unreachable!()
78 } 212 }
79 } 213 }
80 } 214 }
81} 215}
82 216
83fn generate_code() { 217fn generate_code(cfgs: &mut common::CfgSet, singletons: &[Singleton]) {
84 #[allow(unused)] 218 #[allow(unused)]
85 use std::fmt::Write; 219 use std::fmt::Write;
86 220
@@ -88,14 +222,179 @@ fn generate_code() {
88 #[allow(unused_mut)] 222 #[allow(unused_mut)]
89 let mut output = String::new(); 223 let mut output = String::new();
90 224
225 writeln!(&mut output, "{}", peripherals(singletons)).unwrap();
226
91 #[cfg(feature = "_rt1xxx")] 227 #[cfg(feature = "_rt1xxx")]
92 writeln!(&mut output, "{}", generate_iomuxc()).unwrap(); 228 writeln!(&mut output, "{}", generate_iomuxc()).unwrap();
93 229
230 writeln!(&mut output, "{}", interrupts()).unwrap();
231 writeln!(&mut output, "{}", impl_peripherals(cfgs, singletons)).unwrap();
232
94 let out_file = out_dir.join("_generated.rs").to_string_lossy().to_string(); 233 let out_file = out_dir.join("_generated.rs").to_string_lossy().to_string();
95 fs::write(&out_file, output).unwrap(); 234 fs::write(&out_file, output).unwrap();
96 rustfmt(&out_file); 235 rustfmt(&out_file);
97} 236}
98 237
238fn interrupts() -> TokenStream {
239 let interrupts = METADATA.interrupts.iter().map(|interrupt| format_ident!("{interrupt}"));
240
241 quote! {
242 embassy_hal_internal::interrupt_mod!(#(#interrupts),*);
243 }
244}
245
246fn peripherals(singletons: &[Singleton]) -> TokenStream {
247 let defs = singletons.iter().map(|s| {
248 let ident = Ident::new(&s.name, Span::call_site());
249 quote! { #ident }
250 });
251
252 let peripherals = singletons.iter().map(|s| {
253 let ident = Ident::new(&s.name, Span::call_site());
254 let cfg = s.cfg.clone().unwrap_or_else(|| quote! {});
255 quote! {
256 #cfg
257 #ident
258 }
259 });
260
261 quote! {
262 embassy_hal_internal::peripherals_definition!(#(#defs),*);
263 embassy_hal_internal::peripherals_struct!(#(#peripherals),*);
264 }
265}
266
267fn impl_gpio_pin(impls: &mut Vec<TokenStream>, peripheral: &Peripheral) {
268 let instance = peripheral.name.strip_prefix("GPIO").unwrap();
269 let bank = format_ident!("Gpio{}", instance);
270 // let pin =
271
272 for signal in peripheral.signals.iter() {
273 let pin_number = signal.name.parse::<u8>().unwrap();
274 let pin = Ident::new(signal.pins[0].pin, Span::call_site());
275
276 impls.push(quote! {
277 impl_pin!(#pin, #bank, #pin_number);
278 });
279 }
280}
281
282fn impl_dma_channel(impls: &mut Vec<TokenStream>, peripheral: &Peripheral) {
283 let instance = Ident::new(peripheral.name, Span::call_site());
284
285 for signal in peripheral.signals.iter() {
286 let channel_number = signal.name.parse::<u8>().unwrap();
287 let channel_name = format_ident!("{instance}_CH{channel_number}");
288
289 impls.push(quote! {
290 impl_dma_channel!(#instance, #channel_name, #channel_number);
291 });
292 }
293}
294
295fn impl_usart(impls: &mut Vec<TokenStream>, peripheral: &Peripheral) {
296 let instance = Ident::new(peripheral.name, Span::call_site());
297 let flexcomm = Ident::new(
298 peripheral.flexcomm.expect("LPC55 must specify FLEXCOMM instance"),
299 Span::call_site(),
300 );
301 let number = Literal::u8_unsuffixed(peripheral.name.strip_prefix("USART").unwrap().parse::<u8>().unwrap());
302
303 impls.push(quote! {
304 impl_usart_instance!(#instance, #flexcomm, #number);
305 });
306
307 for signal in peripheral.signals {
308 let r#macro = match signal.name {
309 "TXD" => format_ident!("impl_usart_txd_pin"),
310 "RXD" => format_ident!("impl_usart_rxd_pin"),
311 _ => unreachable!(),
312 };
313
314 for pin in signal.pins {
315 let alt = format_ident!("ALT{}", pin.alt);
316 let pin = format_ident!("{}", pin.pin);
317
318 impls.push(quote! {
319 #r#macro!(#pin, #instance, #alt);
320 });
321 }
322 }
323
324 for dma_mux in peripheral.dma_muxing {
325 assert_eq!(dma_mux.mux, "DMA0", "TODO: USART for more than LPC55");
326
327 let r#macro = match dma_mux.signal {
328 "TX" => format_ident!("impl_usart_tx_channel"),
329 "RX" => format_ident!("impl_usart_rx_channel"),
330 _ => unreachable!(),
331 };
332
333 let channel = format_ident!("DMA0_CH{}", dma_mux.request);
334
335 impls.push(quote! {
336 #r#macro!(#instance, #channel);
337 });
338 }
339}
340
341fn impl_sct(impls: &mut Vec<TokenStream>, peripheral: &Peripheral) {
342 let instance = Ident::new(peripheral.name, Span::call_site());
343
344 impls.push(quote! {
345 impl_sct_instance!(#instance);
346 });
347
348 for signal in peripheral.signals.iter() {
349 if signal.name.starts_with("OUT") {
350 let channel_number = signal.name.strip_prefix("OUT").unwrap().parse::<u8>().unwrap();
351
352 let channel_name = format_ident!("{instance}_OUT{channel_number}");
353
354 impls.push(quote! {
355 impl_sct_output_instance!(#instance, #channel_name, #channel_number);
356 });
357
358 if signal.name.starts_with("OUT") {
359 for pin in signal.pins {
360 let pin_name = format_ident!("{}", pin.pin);
361 let alt = format_ident!("ALT{}", pin.alt);
362
363 impls.push(quote! {
364 impl_sct_output_pin!(#instance, #channel_name, #pin_name, #alt);
365 });
366 }
367 }
368 }
369 }
370}
371
372fn impl_peripherals(_cfgs: &mut common::CfgSet, _singletons: &[Singleton]) -> TokenStream {
373 let mut impls = Vec::new();
374
375 for peripheral in metadata::METADATA.peripherals.iter() {
376 if peripheral.name.starts_with("GPIO") {
377 impl_gpio_pin(&mut impls, peripheral);
378 }
379
380 if peripheral.name.starts_with("DMA") {
381 impl_dma_channel(&mut impls, peripheral);
382 }
383
384 if peripheral.name.starts_with("USART") {
385 impl_usart(&mut impls, peripheral);
386 }
387
388 if peripheral.name.starts_with("SCT") {
389 impl_sct(&mut impls, peripheral);
390 }
391 }
392
393 quote! {
394 #(#impls)*
395 }
396}
397
99/// rustfmt a given path. 398/// rustfmt a given path.
100/// Failures are logged to stderr and ignored. 399/// Failures are logged to stderr and ignored.
101fn rustfmt(path: impl AsRef<Path>) { 400fn rustfmt(path: impl AsRef<Path>) {
diff --git a/embassy-nxp/src/chips/lpc55.rs b/embassy-nxp/src/chips/lpc55.rs
index e9addddb6..7967e07d1 100644
--- a/embassy-nxp/src/chips/lpc55.rs
+++ b/embassy-nxp/src/chips/lpc55.rs
@@ -1,121 +1,10 @@
1pub use nxp_pac as pac; 1pub(crate) mod _generated {
2 #![allow(dead_code)]
3 #![allow(unused_imports)]
4 #![allow(non_snake_case)]
5 #![allow(missing_docs)]
2 6
3embassy_hal_internal::interrupt_mod!( 7 include!(concat!(env!("OUT_DIR"), "/_generated.rs"));
4 FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7
5);
6
7embassy_hal_internal::peripherals! {
8 // External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other
9 // peripheral types (e.g. I2C).
10 PIO0_0,
11 PIO0_1,
12 PIO0_2,
13 PIO0_3,
14 PIO0_4,
15 PIO0_5,
16 PIO0_6,
17 PIO0_7,
18 PIO0_8,
19 PIO0_9,
20 PIO0_10,
21 PIO0_11,
22 PIO0_12,
23 PIO0_13,
24 PIO0_14,
25 PIO0_15,
26 PIO0_16,
27 PIO0_17,
28 PIO0_18,
29 PIO0_19,
30 PIO0_20,
31 PIO0_21,
32 PIO0_22,
33 PIO0_23,
34 PIO0_24,
35 PIO0_25,
36 PIO0_26,
37 PIO0_27,
38 PIO0_28,
39 PIO0_29,
40 PIO0_30,
41 PIO0_31,
42 PIO1_0,
43 PIO1_1,
44 PIO1_2,
45 PIO1_3,
46 PIO1_4,
47 PIO1_5,
48 PIO1_6,
49 PIO1_7,
50 PIO1_8,
51 PIO1_9,
52 PIO1_10,
53 PIO1_11,
54 PIO1_12,
55 PIO1_13,
56 PIO1_14,
57 PIO1_15,
58 PIO1_16,
59 PIO1_17,
60 PIO1_18,
61 PIO1_19,
62 PIO1_20,
63 PIO1_21,
64 PIO1_22,
65 PIO1_23,
66 PIO1_24,
67 PIO1_25,
68 PIO1_26,
69 PIO1_27,
70 PIO1_28,
71 PIO1_29,
72 PIO1_30,
73 PIO1_31,
74
75 // Direct Memory Access (DMA) channels. They are used for asynchronous modes of peripherals.
76 DMA_CH0,
77 DMA_CH1,
78 DMA_CH2,
79 DMA_CH3,
80 DMA_CH4,
81 DMA_CH5,
82 DMA_CH6,
83 DMA_CH7,
84 DMA_CH8,
85 DMA_CH9,
86 DMA_CH10,
87 DMA_CH11,
88 DMA_CH12,
89 DMA_CH13,
90 DMA_CH14,
91 DMA_CH15,
92 DMA_CH16,
93 DMA_CH17,
94 DMA_CH18,
95 DMA_CH19,
96 DMA_CH20,
97 DMA_CH21,
98 DMA_CH22,
99
100 // Pulse-Width Modulation Outputs.
101 PWM_OUTPUT0,
102 PWM_OUTPUT1,
103 PWM_OUTPUT2,
104 PWM_OUTPUT3,
105 PWM_OUTPUT4,
106 PWM_OUTPUT5,
107 PWM_OUTPUT6,
108 PWM_OUTPUT7,
109 PWM_OUTPUT8,
110 PWM_OUTPUT9,
111
112 // Universal Synchronous/Asynchronous Receiver/Transmitter (USART) instances.
113 USART0,
114 USART1,
115 USART2,
116 USART3,
117 USART4,
118 USART5,
119 USART6,
120 USART7
121} 8}
9
10pub use _generated::*;
diff --git a/embassy-nxp/src/chips/mimxrt1011.rs b/embassy-nxp/src/chips/mimxrt1011.rs
index a74d953fc..d5969a24b 100644
--- a/embassy-nxp/src/chips/mimxrt1011.rs
+++ b/embassy-nxp/src/chips/mimxrt1011.rs
@@ -1,107 +1,5 @@
1// This must be imported so that __preinit is defined. 1// This must be imported so that __preinit is defined.
2use imxrt_rt as _; 2use imxrt_rt as _;
3pub use nxp_pac as pac;
4
5embassy_hal_internal::peripherals! {
6 // External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other
7 // peripheral types (e.g. I2C).
8 GPIO_00,
9 GPIO_01,
10 GPIO_02,
11 GPIO_03,
12 GPIO_04,
13 GPIO_05,
14 GPIO_06,
15 GPIO_07,
16 GPIO_08,
17 GPIO_09,
18 GPIO_10,
19 GPIO_11,
20 GPIO_12,
21 GPIO_13,
22 GPIO_AD_00,
23 GPIO_AD_01,
24 GPIO_AD_02,
25 GPIO_AD_03,
26 GPIO_AD_04,
27 GPIO_AD_05,
28 GPIO_AD_06,
29 GPIO_AD_07,
30 GPIO_AD_08,
31 GPIO_AD_09,
32 GPIO_AD_10,
33 GPIO_AD_11,
34 GPIO_AD_12,
35 GPIO_AD_13,
36 GPIO_AD_14,
37 GPIO_SD_00,
38 GPIO_SD_01,
39 GPIO_SD_02,
40 GPIO_SD_03,
41 GPIO_SD_04,
42 GPIO_SD_05,
43 GPIO_SD_06,
44 GPIO_SD_07,
45 GPIO_SD_08,
46 GPIO_SD_09,
47 GPIO_SD_10,
48 GPIO_SD_11,
49 GPIO_SD_12,
50 GPIO_SD_13,
51 PMIC_ON_REQ,
52}
53
54impl_gpio! {
55 // GPIO Bank 1
56 GPIO_00(Gpio1, 0);
57 GPIO_01(Gpio1, 1);
58 GPIO_02(Gpio1, 2);
59 GPIO_03(Gpio1, 3);
60 GPIO_04(Gpio1, 4);
61 GPIO_05(Gpio1, 5);
62 GPIO_06(Gpio1, 6);
63 GPIO_07(Gpio1, 7);
64 GPIO_08(Gpio1, 8);
65 GPIO_09(Gpio1, 9);
66 GPIO_10(Gpio1, 10);
67 GPIO_11(Gpio1, 11);
68 GPIO_12(Gpio1, 12);
69 GPIO_13(Gpio1, 13);
70 GPIO_AD_00(Gpio1, 14);
71 GPIO_AD_01(Gpio1, 15);
72 GPIO_AD_02(Gpio1, 16);
73 GPIO_AD_03(Gpio1, 17);
74 GPIO_AD_04(Gpio1, 18);
75 GPIO_AD_05(Gpio1, 19);
76 GPIO_AD_06(Gpio1, 20);
77 GPIO_AD_07(Gpio1, 21);
78 GPIO_AD_08(Gpio1, 22);
79 GPIO_AD_09(Gpio1, 23);
80 GPIO_AD_10(Gpio1, 24);
81 GPIO_AD_11(Gpio1, 25);
82 GPIO_AD_12(Gpio1, 26);
83 GPIO_AD_13(Gpio1, 27);
84 GPIO_AD_14(Gpio1, 28);
85
86 // GPIO Bank 2
87 GPIO_SD_00(Gpio2, 0);
88 GPIO_SD_01(Gpio2, 1);
89 GPIO_SD_02(Gpio2, 2);
90 GPIO_SD_03(Gpio2, 3);
91 GPIO_SD_04(Gpio2, 4);
92 GPIO_SD_05(Gpio2, 5);
93 GPIO_SD_06(Gpio2, 6);
94 GPIO_SD_07(Gpio2, 7);
95 GPIO_SD_08(Gpio2, 8);
96 GPIO_SD_09(Gpio2, 9);
97 GPIO_SD_10(Gpio2, 10);
98 GPIO_SD_11(Gpio2, 11);
99 GPIO_SD_12(Gpio2, 12);
100 GPIO_SD_13(Gpio2, 13);
101
102 // GPIO Bank 5
103 PMIC_ON_REQ(Gpio5, 0);
104}
105 3
106pub(crate) mod _generated { 4pub(crate) mod _generated {
107 #![allow(dead_code)] 5 #![allow(dead_code)]
@@ -111,3 +9,5 @@ pub(crate) mod _generated {
111 9
112 include!(concat!(env!("OUT_DIR"), "/_generated.rs")); 10 include!(concat!(env!("OUT_DIR"), "/_generated.rs"));
113} 11}
12
13pub use _generated::*;
diff --git a/embassy-nxp/src/chips/mimxrt1062.rs b/embassy-nxp/src/chips/mimxrt1062.rs
index ef153bd66..d5969a24b 100644
--- a/embassy-nxp/src/chips/mimxrt1062.rs
+++ b/embassy-nxp/src/chips/mimxrt1062.rs
@@ -1,276 +1,5 @@
1// This must be imported so that __preinit is defined. 1// This must be imported so that __preinit is defined.
2use imxrt_rt as _; 2use imxrt_rt as _;
3pub use nxp_pac as pac;
4
5embassy_hal_internal::peripherals! {
6 // External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other
7 // peripheral types (e.g. I2C).
8 GPIO_AD_B0_00,
9 GPIO_AD_B0_01,
10 GPIO_AD_B0_02,
11 GPIO_AD_B0_03,
12 GPIO_AD_B0_04,
13 GPIO_AD_B0_05,
14 GPIO_AD_B0_06,
15 GPIO_AD_B0_07,
16 GPIO_AD_B0_08,
17 GPIO_AD_B0_09,
18 GPIO_AD_B0_10,
19 GPIO_AD_B0_11,
20 GPIO_AD_B0_12,
21 GPIO_AD_B0_13,
22 GPIO_AD_B0_14,
23 GPIO_AD_B0_15,
24 GPIO_AD_B1_00,
25 GPIO_AD_B1_01,
26 GPIO_AD_B1_02,
27 GPIO_AD_B1_03,
28 GPIO_AD_B1_04,
29 GPIO_AD_B1_05,
30 GPIO_AD_B1_06,
31 GPIO_AD_B1_07,
32 GPIO_AD_B1_08,
33 GPIO_AD_B1_09,
34 GPIO_AD_B1_10,
35 GPIO_AD_B1_11,
36 GPIO_AD_B1_12,
37 GPIO_AD_B1_13,
38 GPIO_AD_B1_14,
39 GPIO_AD_B1_15,
40 GPIO_B0_00,
41 GPIO_B0_01,
42 GPIO_B0_02,
43 GPIO_B0_03,
44 GPIO_B0_04,
45 GPIO_B0_05,
46 GPIO_B0_06,
47 GPIO_B0_07,
48 GPIO_B0_08,
49 GPIO_B0_09,
50 GPIO_B0_10,
51 GPIO_B0_11,
52 GPIO_B0_12,
53 GPIO_B0_13,
54 GPIO_B0_14,
55 GPIO_B0_15,
56 GPIO_B1_00,
57 GPIO_B1_01,
58 GPIO_B1_02,
59 GPIO_B1_03,
60 GPIO_B1_04,
61 GPIO_B1_05,
62 GPIO_B1_06,
63 GPIO_B1_07,
64 GPIO_B1_08,
65 GPIO_B1_09,
66 GPIO_B1_10,
67 GPIO_B1_11,
68 GPIO_B1_12,
69 GPIO_B1_13,
70 GPIO_B1_14,
71 GPIO_B1_15,
72 GPIO_EMC_00,
73 GPIO_EMC_01,
74 GPIO_EMC_02,
75 GPIO_EMC_03,
76 GPIO_EMC_04,
77 GPIO_EMC_05,
78 GPIO_EMC_06,
79 GPIO_EMC_07,
80 GPIO_EMC_08,
81 GPIO_EMC_09,
82 GPIO_EMC_10,
83 GPIO_EMC_11,
84 GPIO_EMC_12,
85 GPIO_EMC_13,
86 GPIO_EMC_14,
87 GPIO_EMC_15,
88 GPIO_EMC_16,
89 GPIO_EMC_17,
90 GPIO_EMC_18,
91 GPIO_EMC_19,
92 GPIO_EMC_20,
93 GPIO_EMC_21,
94 GPIO_EMC_22,
95 GPIO_EMC_23,
96 GPIO_EMC_24,
97 GPIO_EMC_25,
98 GPIO_EMC_26,
99 GPIO_EMC_27,
100 GPIO_EMC_28,
101 GPIO_EMC_29,
102 GPIO_EMC_30,
103 GPIO_EMC_31,
104 GPIO_EMC_32,
105 GPIO_EMC_33,
106 GPIO_EMC_34,
107 GPIO_EMC_35,
108 GPIO_EMC_36,
109 GPIO_EMC_37,
110 GPIO_EMC_38,
111 GPIO_EMC_39,
112 GPIO_EMC_40,
113 GPIO_EMC_41,
114 GPIO_SD_B0_00,
115 GPIO_SD_B0_01,
116 GPIO_SD_B0_02,
117 GPIO_SD_B0_03,
118 GPIO_SD_B0_04,
119 GPIO_SD_B0_05,
120 GPIO_SD_B1_00,
121 GPIO_SD_B1_01,
122 GPIO_SD_B1_02,
123 GPIO_SD_B1_03,
124 GPIO_SD_B1_04,
125 GPIO_SD_B1_05,
126 GPIO_SD_B1_06,
127 GPIO_SD_B1_07,
128 GPIO_SD_B1_08,
129 GPIO_SD_B1_09,
130 GPIO_SD_B1_10,
131 GPIO_SD_B1_11,
132 WAKEUP,
133 PMIC_ON_REQ,
134 PMIC_STBY_REQ,
135}
136
137impl_gpio! {
138 // GPIO Bank 1
139 GPIO_AD_B0_00(Gpio1, 0);
140 GPIO_AD_B0_01(Gpio1, 1);
141 GPIO_AD_B0_02(Gpio1, 2);
142 GPIO_AD_B0_03(Gpio1, 3);
143 GPIO_AD_B0_04(Gpio1, 4);
144 GPIO_AD_B0_05(Gpio1, 5);
145 GPIO_AD_B0_06(Gpio1, 6);
146 GPIO_AD_B0_07(Gpio1, 7);
147 GPIO_AD_B0_08(Gpio1, 8);
148 GPIO_AD_B0_09(Gpio1, 9);
149 GPIO_AD_B0_10(Gpio1, 10);
150 GPIO_AD_B0_11(Gpio1, 11);
151 GPIO_AD_B0_12(Gpio1, 12);
152 GPIO_AD_B0_13(Gpio1, 13);
153 GPIO_AD_B0_14(Gpio1, 14);
154 GPIO_AD_B0_15(Gpio1, 15);
155 GPIO_AD_B1_00(Gpio1, 16);
156 GPIO_AD_B1_01(Gpio1, 17);
157 GPIO_AD_B1_02(Gpio1, 18);
158 GPIO_AD_B1_03(Gpio1, 19);
159 GPIO_AD_B1_04(Gpio1, 20);
160 GPIO_AD_B1_05(Gpio1, 21);
161 GPIO_AD_B1_06(Gpio1, 22);
162 GPIO_AD_B1_07(Gpio1, 23);
163 GPIO_AD_B1_08(Gpio1, 24);
164 GPIO_AD_B1_09(Gpio1, 25);
165 GPIO_AD_B1_10(Gpio1, 26);
166 GPIO_AD_B1_11(Gpio1, 27);
167 GPIO_AD_B1_12(Gpio1, 28);
168 GPIO_AD_B1_13(Gpio1, 29);
169 GPIO_AD_B1_14(Gpio1, 30);
170 GPIO_AD_B1_15(Gpio1, 31);
171
172 // GPIO Bank 2
173 GPIO_B0_00(Gpio2, 0);
174 GPIO_B0_01(Gpio2, 1);
175 GPIO_B0_02(Gpio2, 2);
176 GPIO_B0_03(Gpio2, 3);
177 GPIO_B0_04(Gpio2, 4);
178 GPIO_B0_05(Gpio2, 5);
179 GPIO_B0_06(Gpio2, 6);
180 GPIO_B0_07(Gpio2, 7);
181 GPIO_B0_08(Gpio2, 8);
182 GPIO_B0_09(Gpio2, 9);
183 GPIO_B0_10(Gpio2, 10);
184 GPIO_B0_11(Gpio2, 11);
185 GPIO_B0_12(Gpio2, 12);
186 GPIO_B0_13(Gpio2, 13);
187 GPIO_B0_14(Gpio2, 14);
188 GPIO_B0_15(Gpio2, 15);
189 GPIO_B1_00(Gpio2, 16);
190 GPIO_B1_01(Gpio2, 17);
191 GPIO_B1_02(Gpio2, 18);
192 GPIO_B1_03(Gpio2, 19);
193 GPIO_B1_04(Gpio2, 20);
194 GPIO_B1_05(Gpio2, 21);
195 GPIO_B1_06(Gpio2, 22);
196 GPIO_B1_07(Gpio2, 23);
197 GPIO_B1_08(Gpio2, 24);
198 GPIO_B1_09(Gpio2, 25);
199 GPIO_B1_10(Gpio2, 26);
200 GPIO_B1_11(Gpio2, 27);
201 GPIO_B1_12(Gpio2, 28);
202 GPIO_B1_13(Gpio2, 29);
203 GPIO_B1_14(Gpio2, 30);
204 GPIO_B1_15(Gpio2, 31);
205
206 // GPIO Bank 4 (EMC is 4, then 3)
207 GPIO_EMC_00(Gpio4, 0);
208 GPIO_EMC_01(Gpio4, 1);
209 GPIO_EMC_02(Gpio4, 2);
210 GPIO_EMC_03(Gpio4, 3);
211 GPIO_EMC_04(Gpio4, 4);
212 GPIO_EMC_05(Gpio4, 5);
213 GPIO_EMC_06(Gpio4, 6);
214 GPIO_EMC_07(Gpio4, 7);
215 GPIO_EMC_08(Gpio4, 8);
216 GPIO_EMC_09(Gpio4, 9);
217 GPIO_EMC_10(Gpio4, 10);
218 GPIO_EMC_11(Gpio4, 11);
219 GPIO_EMC_12(Gpio4, 12);
220 GPIO_EMC_13(Gpio4, 13);
221 GPIO_EMC_14(Gpio4, 14);
222 GPIO_EMC_15(Gpio4, 15);
223 GPIO_EMC_16(Gpio4, 16);
224 GPIO_EMC_17(Gpio4, 17);
225 GPIO_EMC_18(Gpio4, 18);
226 GPIO_EMC_19(Gpio4, 19);
227 GPIO_EMC_20(Gpio4, 20);
228 GPIO_EMC_21(Gpio4, 21);
229 GPIO_EMC_22(Gpio4, 22);
230 GPIO_EMC_23(Gpio4, 23);
231 GPIO_EMC_24(Gpio4, 24);
232 GPIO_EMC_25(Gpio4, 25);
233 GPIO_EMC_26(Gpio4, 26);
234 GPIO_EMC_27(Gpio4, 27);
235 GPIO_EMC_28(Gpio4, 28);
236 GPIO_EMC_29(Gpio4, 29);
237 GPIO_EMC_30(Gpio4, 30);
238 GPIO_EMC_31(Gpio4, 31);
239
240 // GPIO Bank 3
241 GPIO_EMC_32(Gpio3, 18);
242 GPIO_EMC_33(Gpio3, 19);
243 GPIO_EMC_34(Gpio3, 20);
244 GPIO_EMC_35(Gpio3, 21);
245 GPIO_EMC_36(Gpio3, 22);
246 GPIO_EMC_37(Gpio3, 23);
247 GPIO_EMC_38(Gpio3, 24);
248 GPIO_EMC_39(Gpio3, 25);
249 GPIO_EMC_40(Gpio3, 26);
250 GPIO_EMC_41(Gpio3, 27);
251 GPIO_SD_B0_00(Gpio3, 12);
252 GPIO_SD_B0_01(Gpio3, 13);
253 GPIO_SD_B0_02(Gpio3, 14);
254 GPIO_SD_B0_03(Gpio3, 15);
255 GPIO_SD_B0_04(Gpio3, 16);
256 GPIO_SD_B0_05(Gpio3, 17);
257 GPIO_SD_B1_00(Gpio3, 0);
258 GPIO_SD_B1_01(Gpio3, 1);
259 GPIO_SD_B1_02(Gpio3, 2);
260 GPIO_SD_B1_03(Gpio3, 3);
261 GPIO_SD_B1_04(Gpio3, 4);
262 GPIO_SD_B1_05(Gpio3, 5);
263 GPIO_SD_B1_06(Gpio3, 6);
264 GPIO_SD_B1_07(Gpio3, 7);
265 GPIO_SD_B1_08(Gpio3, 8);
266 GPIO_SD_B1_09(Gpio3, 9);
267 GPIO_SD_B1_10(Gpio3, 10);
268 GPIO_SD_B1_11(Gpio3, 11);
269
270 WAKEUP(Gpio5, 0);
271 PMIC_ON_REQ(Gpio5, 1);
272 PMIC_STBY_REQ(Gpio5, 2);
273}
274 3
275pub(crate) mod _generated { 4pub(crate) mod _generated {
276 #![allow(dead_code)] 5 #![allow(dead_code)]
@@ -280,3 +9,5 @@ pub(crate) mod _generated {
280 9
281 include!(concat!(env!("OUT_DIR"), "/_generated.rs")); 10 include!(concat!(env!("OUT_DIR"), "/_generated.rs"));
282} 11}
12
13pub use _generated::*;
diff --git a/embassy-nxp/src/dma.rs b/embassy-nxp/src/dma.rs
index e2df65fc9..1f479122d 100644
--- a/embassy-nxp/src/dma.rs
+++ b/embassy-nxp/src/dma.rs
@@ -1,3 +1,4 @@
1#![macro_use]
1//! Direct Memory Access (DMA) driver. 2//! Direct Memory Access (DMA) driver.
2 3
3#[cfg_attr(feature = "lpc55-core0", path = "./dma/lpc55.rs")] 4#[cfg_attr(feature = "lpc55-core0", path = "./dma/lpc55.rs")]
diff --git a/embassy-nxp/src/dma/lpc55.rs b/embassy-nxp/src/dma/lpc55.rs
index 5bd763f03..623644bf1 100644
--- a/embassy-nxp/src/dma/lpc55.rs
+++ b/embassy-nxp/src/dma/lpc55.rs
@@ -1,3 +1,5 @@
1#![macro_use]
2
1use core::cell::RefCell; 3use core::cell::RefCell;
2use core::future::Future; 4use core::future::Future;
3use core::pin::Pin; 5use core::pin::Pin;
@@ -9,9 +11,12 @@ use embassy_hal_internal::interrupt::InterruptExt;
9use embassy_hal_internal::{PeripheralType, impl_peripheral}; 11use embassy_hal_internal::{PeripheralType, impl_peripheral};
10use embassy_sync::waitqueue::AtomicWaker; 12use embassy_sync::waitqueue::AtomicWaker;
11 13
12use crate::pac::{DMA0, SYSCON, *}; 14use crate::Peri;
13use crate::{Peri, peripherals}; 15#[cfg(feature = "rt")]
16use crate::pac::interrupt;
17use crate::pac::{SYSCON, *};
14 18
19#[cfg(feature = "rt")]
15#[interrupt] 20#[interrupt]
16fn DMA0() { 21fn DMA0() {
17 let inta = DMA0.inta0().read().ia(); 22 let inta = DMA0.inta0().read().ia();
@@ -278,7 +283,7 @@ static DMA_DESCRIPTORS: Mutex<RefCell<DmaDescriptorTable>> = Mutex::new(RefCell:
278 }; CHANNEL_COUNT], 283 }; CHANNEL_COUNT],
279})); 284}));
280 285
281trait SealedChannel {} 286pub(crate) trait SealedChannel {}
282trait SealedWord {} 287trait SealedWord {}
283 288
284/// DMA channel interface. 289/// DMA channel interface.
@@ -323,7 +328,7 @@ impl Word for u32 {
323 328
324/// Type erased DMA channel. 329/// Type erased DMA channel.
325pub struct AnyChannel { 330pub struct AnyChannel {
326 number: u8, 331 pub(crate) number: u8,
327} 332}
328 333
329impl_peripheral!(AnyChannel); 334impl_peripheral!(AnyChannel);
@@ -335,10 +340,10 @@ impl Channel for AnyChannel {
335 } 340 }
336} 341}
337 342
338macro_rules! channel { 343macro_rules! impl_dma_channel {
339 ($name:ident, $num:expr) => { 344 ($instance:ident, $name:ident, $num:expr) => {
340 impl SealedChannel for peripherals::$name {} 345 impl crate::dma::SealedChannel for crate::peripherals::$name {}
341 impl Channel for peripherals::$name { 346 impl crate::dma::Channel for crate::peripherals::$name {
342 fn number(&self) -> u8 { 347 fn number(&self) -> u8 {
343 $num 348 $num
344 } 349 }
@@ -346,32 +351,10 @@ macro_rules! channel {
346 351
347 impl From<peripherals::$name> for crate::dma::AnyChannel { 352 impl From<peripherals::$name> for crate::dma::AnyChannel {
348 fn from(val: peripherals::$name) -> Self { 353 fn from(val: peripherals::$name) -> Self {
354 use crate::dma::Channel;
355
349 Self { number: val.number() } 356 Self { number: val.number() }
350 } 357 }
351 } 358 }
352 }; 359 };
353} 360}
354
355channel!(DMA_CH0, 0);
356channel!(DMA_CH1, 1);
357channel!(DMA_CH2, 2);
358channel!(DMA_CH3, 3);
359channel!(DMA_CH4, 4);
360channel!(DMA_CH5, 5);
361channel!(DMA_CH6, 6);
362channel!(DMA_CH7, 7);
363channel!(DMA_CH8, 8);
364channel!(DMA_CH9, 9);
365channel!(DMA_CH10, 10);
366channel!(DMA_CH11, 11);
367channel!(DMA_CH12, 12);
368channel!(DMA_CH13, 13);
369channel!(DMA_CH14, 14);
370channel!(DMA_CH15, 15);
371channel!(DMA_CH16, 16);
372channel!(DMA_CH17, 17);
373channel!(DMA_CH18, 18);
374channel!(DMA_CH19, 19);
375channel!(DMA_CH20, 20);
376channel!(DMA_CH21, 21);
377channel!(DMA_CH22, 22);
diff --git a/embassy-nxp/src/gpio/lpc55.rs b/embassy-nxp/src/gpio/lpc55.rs
index 6039d8ca8..6be405463 100644
--- a/embassy-nxp/src/gpio/lpc55.rs
+++ b/embassy-nxp/src/gpio/lpc55.rs
@@ -1,9 +1,11 @@
1#![macro_use]
2
1use embassy_hal_internal::{PeripheralType, impl_peripheral}; 3use embassy_hal_internal::{PeripheralType, impl_peripheral};
2 4
5use crate::Peri;
3use crate::pac::common::{RW, Reg}; 6use crate::pac::common::{RW, Reg};
4use crate::pac::iocon::vals::{PioDigimode, PioMode}; 7use crate::pac::iocon::vals::{PioDigimode, PioMode};
5use crate::pac::{GPIO, IOCON, SYSCON, iocon}; 8use crate::pac::{GPIO, IOCON, SYSCON, iocon};
6use crate::{Peri, peripherals};
7 9
8pub(crate) fn init() { 10pub(crate) fn init() {
9 // Enable clocks for GPIO, PINT, and IOCON 11 // Enable clocks for GPIO, PINT, and IOCON
@@ -39,8 +41,8 @@ pub enum Pull {
39/// The LPC55 boards have two GPIO banks, each with 32 pins. This enum represents the two banks. 41/// The LPC55 boards have two GPIO banks, each with 32 pins. This enum represents the two banks.
40#[derive(Debug, Eq, PartialEq, Clone, Copy)] 42#[derive(Debug, Eq, PartialEq, Clone, Copy)]
41pub enum Bank { 43pub enum Bank {
42 Bank0 = 0, 44 Gpio0 = 0,
43 Bank1 = 1, 45 Gpio1 = 1,
44} 46}
45 47
46/// GPIO output driver. Internally, this is a specialized [Flex] pin. 48/// GPIO output driver. Internally, this is a specialized [Flex] pin.
@@ -228,8 +230,8 @@ pub(crate) trait SealedPin: Sized {
228 #[inline] 230 #[inline]
229 fn pio(&self) -> Reg<iocon::regs::Pio, RW> { 231 fn pio(&self) -> Reg<iocon::regs::Pio, RW> {
230 match self.pin_bank() { 232 match self.pin_bank() {
231 Bank::Bank0 => IOCON.pio0(self.pin_number() as usize), 233 Bank::Gpio0 => IOCON.pio0(self.pin_number() as usize),
232 Bank::Bank1 => IOCON.pio1(self.pin_number() as usize), 234 Bank::Gpio1 => IOCON.pio1(self.pin_number() as usize),
233 } 235 }
234 } 236 }
235} 237}
@@ -254,8 +256,8 @@ pub trait Pin: PeripheralType + Into<AnyPin> + SealedPin + Sized + 'static {
254 256
255/// Type-erased GPIO pin. 257/// Type-erased GPIO pin.
256pub struct AnyPin { 258pub struct AnyPin {
257 pin_bank: Bank, 259 pub(crate) pin_bank: Bank,
258 pin_number: u8, 260 pub(crate) pin_number: u8,
259} 261}
260 262
261impl AnyPin { 263impl AnyPin {
@@ -285,12 +287,12 @@ impl SealedPin for AnyPin {
285} 287}
286 288
287macro_rules! impl_pin { 289macro_rules! impl_pin {
288 ($name:ident, $bank:expr, $pin_num:expr) => { 290 ($name:ident, $bank:ident, $pin_num:expr) => {
289 impl Pin for peripherals::$name {} 291 impl crate::gpio::Pin for peripherals::$name {}
290 impl SealedPin for peripherals::$name { 292 impl crate::gpio::SealedPin for peripherals::$name {
291 #[inline] 293 #[inline]
292 fn pin_bank(&self) -> Bank { 294 fn pin_bank(&self) -> crate::gpio::Bank {
293 $bank 295 crate::gpio::Bank::$bank
294 } 296 }
295 297
296 #[inline] 298 #[inline]
@@ -301,6 +303,8 @@ macro_rules! impl_pin {
301 303
302 impl From<peripherals::$name> for crate::gpio::AnyPin { 304 impl From<peripherals::$name> for crate::gpio::AnyPin {
303 fn from(val: peripherals::$name) -> Self { 305 fn from(val: peripherals::$name) -> Self {
306 use crate::gpio::SealedPin;
307
304 Self { 308 Self {
305 pin_bank: val.pin_bank(), 309 pin_bank: val.pin_bank(),
306 pin_number: val.pin_number(), 310 pin_number: val.pin_number(),
@@ -309,68 +313,3 @@ macro_rules! impl_pin {
309 } 313 }
310 }; 314 };
311} 315}
312
313impl_pin!(PIO0_0, Bank::Bank0, 0);
314impl_pin!(PIO0_1, Bank::Bank0, 1);
315impl_pin!(PIO0_2, Bank::Bank0, 2);
316impl_pin!(PIO0_3, Bank::Bank0, 3);
317impl_pin!(PIO0_4, Bank::Bank0, 4);
318impl_pin!(PIO0_5, Bank::Bank0, 5);
319impl_pin!(PIO0_6, Bank::Bank0, 6);
320impl_pin!(PIO0_7, Bank::Bank0, 7);
321impl_pin!(PIO0_8, Bank::Bank0, 8);
322impl_pin!(PIO0_9, Bank::Bank0, 9);
323impl_pin!(PIO0_10, Bank::Bank0, 10);
324impl_pin!(PIO0_11, Bank::Bank0, 11);
325impl_pin!(PIO0_12, Bank::Bank0, 12);
326impl_pin!(PIO0_13, Bank::Bank0, 13);
327impl_pin!(PIO0_14, Bank::Bank0, 14);
328impl_pin!(PIO0_15, Bank::Bank0, 15);
329impl_pin!(PIO0_16, Bank::Bank0, 16);
330impl_pin!(PIO0_17, Bank::Bank0, 17);
331impl_pin!(PIO0_18, Bank::Bank0, 18);
332impl_pin!(PIO0_19, Bank::Bank0, 19);
333impl_pin!(PIO0_20, Bank::Bank0, 20);
334impl_pin!(PIO0_21, Bank::Bank0, 21);
335impl_pin!(PIO0_22, Bank::Bank0, 22);
336impl_pin!(PIO0_23, Bank::Bank0, 23);
337impl_pin!(PIO0_24, Bank::Bank0, 24);
338impl_pin!(PIO0_25, Bank::Bank0, 25);
339impl_pin!(PIO0_26, Bank::Bank0, 26);
340impl_pin!(PIO0_27, Bank::Bank0, 27);
341impl_pin!(PIO0_28, Bank::Bank0, 28);
342impl_pin!(PIO0_29, Bank::Bank0, 29);
343impl_pin!(PIO0_30, Bank::Bank0, 30);
344impl_pin!(PIO0_31, Bank::Bank0, 31);
345impl_pin!(PIO1_0, Bank::Bank1, 0);
346impl_pin!(PIO1_1, Bank::Bank1, 1);
347impl_pin!(PIO1_2, Bank::Bank1, 2);
348impl_pin!(PIO1_3, Bank::Bank1, 3);
349impl_pin!(PIO1_4, Bank::Bank1, 4);
350impl_pin!(PIO1_5, Bank::Bank1, 5);
351impl_pin!(PIO1_6, Bank::Bank1, 6);
352impl_pin!(PIO1_7, Bank::Bank1, 7);
353impl_pin!(PIO1_8, Bank::Bank1, 8);
354impl_pin!(PIO1_9, Bank::Bank1, 9);
355impl_pin!(PIO1_10, Bank::Bank1, 10);
356impl_pin!(PIO1_11, Bank::Bank1, 11);
357impl_pin!(PIO1_12, Bank::Bank1, 12);
358impl_pin!(PIO1_13, Bank::Bank1, 13);
359impl_pin!(PIO1_14, Bank::Bank1, 14);
360impl_pin!(PIO1_15, Bank::Bank1, 15);
361impl_pin!(PIO1_16, Bank::Bank1, 16);
362impl_pin!(PIO1_17, Bank::Bank1, 17);
363impl_pin!(PIO1_18, Bank::Bank1, 18);
364impl_pin!(PIO1_19, Bank::Bank1, 19);
365impl_pin!(PIO1_20, Bank::Bank1, 20);
366impl_pin!(PIO1_21, Bank::Bank1, 21);
367impl_pin!(PIO1_22, Bank::Bank1, 22);
368impl_pin!(PIO1_23, Bank::Bank1, 23);
369impl_pin!(PIO1_24, Bank::Bank1, 24);
370impl_pin!(PIO1_25, Bank::Bank1, 25);
371impl_pin!(PIO1_26, Bank::Bank1, 26);
372impl_pin!(PIO1_27, Bank::Bank1, 27);
373impl_pin!(PIO1_28, Bank::Bank1, 28);
374impl_pin!(PIO1_29, Bank::Bank1, 29);
375impl_pin!(PIO1_30, Bank::Bank1, 30);
376impl_pin!(PIO1_31, Bank::Bank1, 31);
diff --git a/embassy-nxp/src/gpio/rt1xxx.rs b/embassy-nxp/src/gpio/rt1xxx.rs
index c4dc110ff..8a560310c 100644
--- a/embassy-nxp/src/gpio/rt1xxx.rs
+++ b/embassy-nxp/src/gpio/rt1xxx.rs
@@ -10,7 +10,7 @@ use embassy_sync::waitqueue::AtomicWaker;
10use nxp_pac::gpio::vals::Icr; 10use nxp_pac::gpio::vals::Icr;
11use nxp_pac::iomuxc::vals::Pus; 11use nxp_pac::iomuxc::vals::Pus;
12 12
13use crate::chip::{mux_address, pad_address}; 13use crate::chip::{iomuxc_mux, iomuxc_pad};
14use crate::pac::common::{RW, Reg}; 14use crate::pac::common::{RW, Reg};
15use crate::pac::gpio::Gpio; 15use crate::pac::gpio::Gpio;
16#[cfg(feature = "rt")] 16#[cfg(feature = "rt")]
@@ -121,6 +121,10 @@ pub enum Bank {
121 /// Bank 5 121 /// Bank 5
122 #[cfg(gpio5)] 122 #[cfg(gpio5)]
123 Gpio5, 123 Gpio5,
124
125 #[cfg(gpio10)]
126 /// Bank 10
127 Gpio10,
124} 128}
125 129
126/// GPIO flexible pin. 130/// GPIO flexible pin.
@@ -656,6 +660,8 @@ static GPIO3_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];
656static GPIO4_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; 660static GPIO4_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];
657#[cfg(gpio5)] 661#[cfg(gpio5)]
658static GPIO5_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; 662static GPIO5_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];
663#[cfg(gpio10)]
664static GPIO10_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];
659 665
660/// Sealed trait for pins. This trait is sealed and cannot be implemented outside of this crate. 666/// Sealed trait for pins. This trait is sealed and cannot be implemented outside of this crate.
661pub(crate) trait SealedPin: Sized { 667pub(crate) trait SealedPin: Sized {
@@ -676,13 +682,15 @@ pub(crate) trait SealedPin: Sized {
676 Bank::Gpio4 => pac::GPIO4, 682 Bank::Gpio4 => pac::GPIO4,
677 #[cfg(gpio5)] 683 #[cfg(gpio5)]
678 Bank::Gpio5 => pac::GPIO5, 684 Bank::Gpio5 => pac::GPIO5,
685 #[cfg(gpio10)]
686 Bank::Gpio10 => pac::GPIO10,
679 } 687 }
680 } 688 }
681 689
682 #[inline] 690 #[inline]
683 fn mux(&self) -> Reg<MuxCtl, RW> { 691 fn mux(&self) -> Reg<MuxCtl, RW> {
684 // SAFETY: The generated mux address table is valid since it is generated from the SVD files. 692 // SAFETY: The generated mux address table is valid since it is generated from the SVD files.
685 let address = unsafe { mux_address(self._bank(), self.pin_number()).unwrap_unchecked() }; 693 let address = unsafe { iomuxc_mux(self._bank(), self.pin_number()).unwrap_unchecked() };
686 694
687 // SAFETY: The register at the address is an instance of MuxCtl. 695 // SAFETY: The register at the address is an instance of MuxCtl.
688 unsafe { Reg::from_ptr(address as *mut _) } 696 unsafe { Reg::from_ptr(address as *mut _) }
@@ -690,8 +698,7 @@ pub(crate) trait SealedPin: Sized {
690 698
691 #[inline] 699 #[inline]
692 fn pad(&self) -> Reg<Ctl, RW> { 700 fn pad(&self) -> Reg<Ctl, RW> {
693 // SAFETY: The generated pad address table is valid since it is generated from the SVD files. 701 let address = iomuxc_pad(self._bank(), self.pin_number());
694 let address = unsafe { pad_address(self._bank(), self.pin_number()).unwrap_unchecked() };
695 702
696 // SAFETY: The register at the address is an instance of Ctl. 703 // SAFETY: The register at the address is an instance of Ctl.
697 unsafe { Reg::from_ptr(address as *mut _) } 704 unsafe { Reg::from_ptr(address as *mut _) }
@@ -709,6 +716,8 @@ pub(crate) trait SealedPin: Sized {
709 Bank::Gpio4 => &GPIO4_WAKERS[self.pin_number() as usize], 716 Bank::Gpio4 => &GPIO4_WAKERS[self.pin_number() as usize],
710 #[cfg(gpio5)] 717 #[cfg(gpio5)]
711 Bank::Gpio5 => &GPIO5_WAKERS[self.pin_number() as usize], 718 Bank::Gpio5 => &GPIO5_WAKERS[self.pin_number() as usize],
719 #[cfg(gpio10)]
720 Bank::Gpio10 => &GPIO10_WAKERS[self.pin_number() as usize],
712 } 721 }
713 } 722 }
714} 723}
@@ -793,39 +802,6 @@ impl<'d> Future for InputFuture<'d> {
793 } 802 }
794} 803}
795 804
796/// A macro to generate all GPIO pins.
797///
798/// This generates a lookup table for IOMUX register addresses.
799macro_rules! impl_gpio {
800 (
801 $($name: ident($bank: ident, $pin_number: expr);)*
802 ) => {
803 #[inline]
804 pub(crate) const fn pad_address(bank: crate::gpio::Bank, pin: u8) -> Option<u32> {
805 match (bank, pin) {
806 $(
807 (crate::gpio::Bank::$bank, $pin_number) => Some(crate::chip::_generated::iomuxc::pads::$name),
808 )*
809 _ => None
810 }
811 }
812
813 #[inline]
814 pub(crate) const fn mux_address(bank: crate::gpio::Bank, pin: u8) -> Option<u32> {
815 match (bank, pin) {
816 $(
817 (crate::gpio::Bank::$bank, $pin_number) => Some(crate::chip::_generated::iomuxc::muxes::$name),
818 )*
819 _ => None
820 }
821 }
822
823 $(
824 impl_pin!($name, $bank, $pin_number);
825 )*
826 };
827}
828
829macro_rules! impl_pin { 805macro_rules! impl_pin {
830 ($name: ident, $bank: ident, $pin_num: expr) => { 806 ($name: ident, $bank: ident, $pin_num: expr) => {
831 impl crate::gpio::Pin for crate::peripherals::$name {} 807 impl crate::gpio::Pin for crate::peripherals::$name {}
diff --git a/embassy-nxp/src/iomuxc.rs b/embassy-nxp/src/iomuxc.rs
new file mode 100644
index 000000000..c015ecbc2
--- /dev/null
+++ b/embassy-nxp/src/iomuxc.rs
@@ -0,0 +1,29 @@
1#![macro_use]
2
3/// An IOMUXC pad.
4///
5/// This trait does not imply that GPIO can be used with this pad. [`Pin`](crate::gpio::Pin) must
6/// also be implemented for GPIO.
7#[allow(private_bounds)]
8pub trait Pad: SealedPad {}
9
10pub(crate) trait SealedPad {
11 /// Address of the pad register for this pad.
12 const PAD: *mut ();
13
14 /// Address of the mux register for this pad.
15 ///
16 /// Some pads do not allow muxing (e.g. ONOFF).
17 const MUX: Option<*mut ()>;
18}
19
20macro_rules! impl_iomuxc_pad {
21 ($name: ident, $pad: expr, $mux: expr) => {
22 impl crate::iomuxc::SealedPad for crate::peripherals::$name {
23 const PAD: *mut () = $pad as *mut ();
24 const MUX: Option<*mut ()> = Some($mux as *mut ());
25 }
26
27 impl crate::iomuxc::Pad for crate::peripherals::$name {}
28 };
29}
diff --git a/embassy-nxp/src/lib.rs b/embassy-nxp/src/lib.rs
index 4058881a5..4c3dbebb9 100644
--- a/embassy-nxp/src/lib.rs
+++ b/embassy-nxp/src/lib.rs
@@ -12,8 +12,13 @@ pub mod pint;
12#[cfg(feature = "lpc55-core0")] 12#[cfg(feature = "lpc55-core0")]
13pub mod pwm; 13pub mod pwm;
14#[cfg(feature = "lpc55-core0")] 14#[cfg(feature = "lpc55-core0")]
15pub mod sct;
16#[cfg(feature = "lpc55-core0")]
15pub mod usart; 17pub mod usart;
16 18
19#[cfg(rt1xxx)]
20mod iomuxc;
21
17#[cfg(feature = "_time_driver")] 22#[cfg(feature = "_time_driver")]
18#[cfg_attr(feature = "time-driver-pit", path = "time_driver/pit.rs")] 23#[cfg_attr(feature = "time-driver-pit", path = "time_driver/pit.rs")]
19#[cfg_attr(feature = "time-driver-rtc", path = "time_driver/rtc.rs")] 24#[cfg_attr(feature = "time-driver-rtc", path = "time_driver/rtc.rs")]
@@ -25,15 +30,12 @@ mod time_driver;
25#[cfg_attr(feature = "mimxrt1062", path = "chips/mimxrt1062.rs")] 30#[cfg_attr(feature = "mimxrt1062", path = "chips/mimxrt1062.rs")]
26mod chip; 31mod chip;
27 32
28// TODO: Remove when this module is implemented for other chips 33pub use chip::{Peripherals, interrupt, peripherals};
29#[cfg(feature = "lpc55-core0")] 34pub use embassy_hal_internal::{Peri, PeripheralType};
30pub use chip::interrupt;
31#[cfg(feature = "unstable-pac")] 35#[cfg(feature = "unstable-pac")]
32pub use chip::pac; 36pub use nxp_pac as pac;
33#[cfg(not(feature = "unstable-pac"))] 37#[cfg(not(feature = "unstable-pac"))]
34pub(crate) use chip::pac; 38pub(crate) use nxp_pac as pac;
35pub use chip::{Peripherals, peripherals};
36pub use embassy_hal_internal::{Peri, PeripheralType};
37 39
38/// Macro to bind interrupts to handlers. 40/// Macro to bind interrupts to handlers.
39/// (Copied from `embassy-rp`) 41/// (Copied from `embassy-rp`)
diff --git a/embassy-nxp/src/pwm.rs b/embassy-nxp/src/pwm.rs
index 68980924a..c87a39c34 100644
--- a/embassy-nxp/src/pwm.rs
+++ b/embassy-nxp/src/pwm.rs
@@ -1,3 +1,5 @@
1#![macro_use]
2
1//! Pulse-Width Modulation (PWM) driver. 3//! Pulse-Width Modulation (PWM) driver.
2 4
3#[cfg_attr(feature = "lpc55-core0", path = "./pwm/lpc55.rs")] 5#[cfg_attr(feature = "lpc55-core0", path = "./pwm/lpc55.rs")]
diff --git a/embassy-nxp/src/pwm/lpc55.rs b/embassy-nxp/src/pwm/lpc55.rs
index 197184ad6..4cdbd8526 100644
--- a/embassy-nxp/src/pwm/lpc55.rs
+++ b/embassy-nxp/src/pwm/lpc55.rs
@@ -1,12 +1,15 @@
1#![macro_use]
2
1use core::sync::atomic::{AtomicU8, AtomicU32, Ordering}; 3use core::sync::atomic::{AtomicU8, AtomicU32, Ordering};
2 4
3use embassy_hal_internal::{Peri, PeripheralType}; 5use embassy_hal_internal::Peri;
4 6
5use crate::gpio::AnyPin; 7use crate::gpio::AnyPin;
6use crate::pac::iocon::vals::{PioDigimode, PioFunc, PioMode, PioOd, PioSlew}; 8use crate::pac::iocon::vals::{PioDigimode, PioMode, PioOd, PioSlew};
7use crate::pac::sct0::vals; 9use crate::pac::sct0::vals;
8use crate::pac::syscon::vals::{SctRst, SctclkselSel}; 10use crate::pac::syscon::vals::{SctRst, SctclkselSel};
9use crate::pac::{SCT0, SYSCON}; 11use crate::pac::{SCT0, SYSCON};
12use crate::sct;
10 13
11// Since for now the counter is shared, the TOP value has to be kept. 14// Since for now the counter is shared, the TOP value has to be kept.
12static TOP_VALUE: AtomicU32 = AtomicU32::new(0); 15static TOP_VALUE: AtomicU32 = AtomicU32::new(0);
@@ -75,7 +78,11 @@ impl<'d> Pwm<'d> {
75 SYSCON.presetctrl1().modify(|w| w.set_sct_rst(SctRst::ASSERTED)); 78 SYSCON.presetctrl1().modify(|w| w.set_sct_rst(SctRst::ASSERTED));
76 SYSCON.presetctrl1().modify(|w| w.set_sct_rst(SctRst::RELEASED)); 79 SYSCON.presetctrl1().modify(|w| w.set_sct_rst(SctRst::RELEASED));
77 } 80 }
78 fn new_inner<T: Output>(output: usize, channel: Peri<'d, impl OutputChannelPin<T>>, config: Config) -> Self { 81 fn new_inner<T: sct::Instance, O: sct::Output<T>>(
82 output: usize,
83 channel: Peri<'d, impl sct::OutputPin<T, O>>,
84 config: Config,
85 ) -> Self {
79 // Enable clocks (Syscon is enabled by default) 86 // Enable clocks (Syscon is enabled by default)
80 critical_section::with(|_cs| { 87 critical_section::with(|_cs| {
81 if !SYSCON.ahbclkctrl0().read().iocon() { 88 if !SYSCON.ahbclkctrl0().read().iocon() {
@@ -109,12 +116,12 @@ impl<'d> Pwm<'d> {
109 116
110 /// Create PWM driver with a single 'a' pin as output. 117 /// Create PWM driver with a single 'a' pin as output.
111 #[inline] 118 #[inline]
112 pub fn new_output<T: Output>( 119 pub fn new_output<T: sct::Instance, O: sct::Output<T>>(
113 output: Peri<'d, T>, 120 output: Peri<'d, O>,
114 channel: Peri<'d, impl OutputChannelPin<T>>, 121 channel: Peri<'d, impl sct::OutputPin<T, O>>,
115 config: Config, 122 config: Config,
116 ) -> Self { 123 ) -> Self {
117 Self::new_inner(output.number(), channel, config) 124 Self::new_inner::<T, O>(output.number(), channel, config)
118 } 125 }
119 126
120 /// Set the PWM config. 127 /// Set the PWM config.
@@ -196,18 +203,18 @@ impl<'d> Pwm<'d> {
196 // TODO(frihetselsker): optimize nxp-pac so that `set_clr` and `set_set` are turned into a bit array. 203 // TODO(frihetselsker): optimize nxp-pac so that `set_clr` and `set_set` are turned into a bit array.
197 if config.invert { 204 if config.invert {
198 // Low when event 0 is active 205 // Low when event 0 is active
199 SCT0.out(output_number).out_clr().modify(|w| w.set_clr(1 << 0)); 206 SCT0.out(output_number).out_clr().modify(|w| w.set_clr(0, true));
200 // High when event `output_number + 1` is active 207 // High when event `output_number + 1` is active
201 SCT0.out(output_number) 208 SCT0.out(output_number)
202 .out_set() 209 .out_set()
203 .modify(|w| w.set_set(1 << (output_number + 1))); 210 .modify(|w| w.set_set(output_number, true));
204 } else { 211 } else {
205 // High when event 0 is active 212 // High when event 0 is active
206 SCT0.out(output_number).out_set().modify(|w| w.set_set(1 << 0)); 213 SCT0.out(output_number).out_set().modify(|w| w.set_set(0, true));
207 // Low when event `output_number + 1` is active 214 // Low when event `output_number + 1` is active
208 SCT0.out(output_number) 215 SCT0.out(output_number)
209 .out_clr() 216 .out_clr()
210 .modify(|w| w.set_clr(1 << (output_number + 1))); 217 .modify(|w| w.set_clr(output_number, true));
211 } 218 }
212 219
213 if config.phase_correct { 220 if config.phase_correct {
@@ -239,87 +246,3 @@ impl<'d> Drop for Pwm<'d> {
239 } 246 }
240 } 247 }
241} 248}
242
243trait SealedOutput {
244 /// Output number.
245 fn number(&self) -> usize;
246}
247
248/// PWM Output.
249#[allow(private_bounds)]
250pub trait Output: PeripheralType + SealedOutput {}
251
252macro_rules! output {
253 ($name:ident, $num:expr) => {
254 impl SealedOutput for crate::peripherals::$name {
255 fn number(&self) -> usize {
256 $num
257 }
258 }
259 impl Output for crate::peripherals::$name {}
260 };
261}
262
263output!(PWM_OUTPUT0, 0);
264output!(PWM_OUTPUT1, 1);
265output!(PWM_OUTPUT2, 2);
266output!(PWM_OUTPUT3, 3);
267output!(PWM_OUTPUT4, 4);
268output!(PWM_OUTPUT5, 5);
269output!(PWM_OUTPUT6, 6);
270output!(PWM_OUTPUT7, 7);
271output!(PWM_OUTPUT8, 8);
272output!(PWM_OUTPUT9, 9);
273
274/// PWM Output Channel.
275pub trait OutputChannelPin<T: Output>: crate::gpio::Pin {
276 fn pin_func(&self) -> PioFunc;
277}
278
279macro_rules! impl_pin {
280 ($pin:ident, $output:ident, $func:ident) => {
281 impl crate::pwm::inner::OutputChannelPin<crate::peripherals::$output> for crate::peripherals::$pin {
282 fn pin_func(&self) -> PioFunc {
283 PioFunc::$func
284 }
285 }
286 };
287}
288
289impl_pin!(PIO0_2, PWM_OUTPUT0, ALT3);
290impl_pin!(PIO0_17, PWM_OUTPUT0, ALT4);
291impl_pin!(PIO1_4, PWM_OUTPUT0, ALT4);
292impl_pin!(PIO1_23, PWM_OUTPUT0, ALT2);
293
294impl_pin!(PIO0_3, PWM_OUTPUT1, ALT3);
295impl_pin!(PIO0_18, PWM_OUTPUT1, ALT4);
296impl_pin!(PIO1_8, PWM_OUTPUT1, ALT4);
297impl_pin!(PIO1_24, PWM_OUTPUT1, ALT2);
298
299impl_pin!(PIO0_10, PWM_OUTPUT2, ALT5);
300impl_pin!(PIO0_15, PWM_OUTPUT2, ALT4);
301impl_pin!(PIO0_19, PWM_OUTPUT2, ALT4);
302impl_pin!(PIO1_9, PWM_OUTPUT2, ALT4);
303impl_pin!(PIO1_25, PWM_OUTPUT2, ALT2);
304
305impl_pin!(PIO0_22, PWM_OUTPUT3, ALT4);
306impl_pin!(PIO0_31, PWM_OUTPUT3, ALT4);
307impl_pin!(PIO1_10, PWM_OUTPUT3, ALT4);
308impl_pin!(PIO1_26, PWM_OUTPUT3, ALT2);
309
310impl_pin!(PIO0_23, PWM_OUTPUT4, ALT4);
311impl_pin!(PIO1_3, PWM_OUTPUT4, ALT4);
312impl_pin!(PIO1_17, PWM_OUTPUT4, ALT4);
313
314impl_pin!(PIO0_26, PWM_OUTPUT5, ALT4);
315impl_pin!(PIO1_18, PWM_OUTPUT5, ALT4);
316
317impl_pin!(PIO0_27, PWM_OUTPUT6, ALT4);
318impl_pin!(PIO1_31, PWM_OUTPUT6, ALT4);
319
320impl_pin!(PIO0_28, PWM_OUTPUT7, ALT4);
321impl_pin!(PIO1_19, PWM_OUTPUT7, ALT2);
322
323impl_pin!(PIO0_29, PWM_OUTPUT8, ALT4);
324
325impl_pin!(PIO0_30, PWM_OUTPUT9, ALT4);
diff --git a/embassy-nxp/src/sct.rs b/embassy-nxp/src/sct.rs
new file mode 100644
index 000000000..b6b0e35a9
--- /dev/null
+++ b/embassy-nxp/src/sct.rs
@@ -0,0 +1,56 @@
1#![macro_use]
2
3use embassy_hal_internal::PeripheralType;
4use nxp_pac::iocon::vals::PioFunc;
5
6use crate::gpio;
7
8/// SCT instance.
9#[allow(private_bounds)]
10pub trait Instance: SealedInstance + PeripheralType {}
11
12pub(crate) trait SealedInstance {}
13
14/// An SCT output.
15#[allow(private_bounds)]
16pub trait Output<T: Instance>: SealedOutput + PeripheralType {}
17
18pub(crate) trait SealedOutput {
19 /// Output number.
20 fn number(&self) -> usize;
21}
22
23/// An SCT output capable pin.
24pub trait OutputPin<T: Instance, O: Output<T>>: gpio::Pin {
25 fn pin_func(&self) -> PioFunc;
26}
27
28macro_rules! impl_sct_instance {
29 ($instance: ident) => {
30 impl crate::sct::SealedInstance for crate::peripherals::$instance {}
31 impl crate::sct::Instance for crate::peripherals::$instance {}
32 };
33}
34
35macro_rules! impl_sct_output_instance {
36 ($instance: ident, $name: ident, $num: expr) => {
37 impl crate::sct::SealedOutput for crate::peripherals::$name {
38 fn number(&self) -> usize {
39 $num as usize
40 }
41 }
42 impl crate::sct::Output<crate::peripherals::$instance> for crate::peripherals::$name {}
43 };
44}
45
46macro_rules! impl_sct_output_pin {
47 ($instance: ident, $output_instance: ident, $pin: ident, $alt: ident) => {
48 impl crate::sct::OutputPin<crate::peripherals::$instance, crate::peripherals::$output_instance>
49 for crate::peripherals::$pin
50 {
51 fn pin_func(&self) -> crate::pac::iocon::vals::PioFunc {
52 crate::pac::iocon::vals::PioFunc::$alt
53 }
54 }
55 };
56}
diff --git a/embassy-nxp/src/usart.rs b/embassy-nxp/src/usart.rs
index 1d8886f24..af039dee4 100644
--- a/embassy-nxp/src/usart.rs
+++ b/embassy-nxp/src/usart.rs
@@ -1,3 +1,5 @@
1#![macro_use]
2
1//! Universal Synchronous/Asynchronous Receiver/Transmitter (USART) driver. 3//! Universal Synchronous/Asynchronous Receiver/Transmitter (USART) driver.
2 4
3#[cfg_attr(feature = "lpc55-core0", path = "./usart/lpc55.rs")] 5#[cfg_attr(feature = "lpc55-core0", path = "./usart/lpc55.rs")]
diff --git a/embassy-nxp/src/usart/lpc55.rs b/embassy-nxp/src/usart/lpc55.rs
index d54927b25..d77f08fd8 100644
--- a/embassy-nxp/src/usart/lpc55.rs
+++ b/embassy-nxp/src/usart/lpc55.rs
@@ -1,3 +1,5 @@
1#![macro_use]
2
1use core::fmt::Debug; 3use core::fmt::Debug;
2use core::future::poll_fn; 4use core::future::poll_fn;
3use core::marker::PhantomData; 5use core::marker::PhantomData;
@@ -13,7 +15,7 @@ use embedded_io::{self, ErrorKind};
13use crate::dma::{AnyChannel, Channel}; 15use crate::dma::{AnyChannel, Channel};
14use crate::gpio::{AnyPin, SealedPin}; 16use crate::gpio::{AnyPin, SealedPin};
15use crate::interrupt::Interrupt; 17use crate::interrupt::Interrupt;
16use crate::interrupt::typelevel::{Binding, Interrupt as _}; 18use crate::interrupt::typelevel::Binding;
17use crate::pac::flexcomm::Flexcomm as FlexcommReg; 19use crate::pac::flexcomm::Flexcomm as FlexcommReg;
18use crate::pac::iocon::vals::PioFunc; 20use crate::pac::iocon::vals::PioFunc;
19use crate::pac::usart::Usart as UsartReg; 21use crate::pac::usart::Usart as UsartReg;
@@ -113,8 +115,8 @@ impl Default for Config {
113 115
114/// Internal DMA state of UART RX. 116/// Internal DMA state of UART RX.
115pub struct DmaState { 117pub struct DmaState {
116 rx_err_waker: AtomicWaker, 118 pub(crate) rx_err_waker: AtomicWaker,
117 rx_err: AtomicBool, 119 pub(crate) rx_err: AtomicBool,
118} 120}
119 121
120/// # Type parameters 122/// # Type parameters
@@ -818,13 +820,13 @@ impl<'d> embedded_io::Read for Usart<'d, Blocking> {
818 } 820 }
819} 821}
820 822
821struct Info { 823pub(crate) struct Info {
822 usart_reg: UsartReg, 824 pub(crate) usart_reg: UsartReg,
823 fc_reg: FlexcommReg, 825 pub(crate) fc_reg: FlexcommReg,
824 interrupt: Interrupt, 826 pub(crate) interrupt: Interrupt,
825} 827}
826 828
827trait SealedInstance { 829pub(crate) trait SealedInstance {
828 fn info() -> &'static Info; 830 fn info() -> &'static Info;
829 fn dma_state() -> &'static DmaState; 831 fn dma_state() -> &'static DmaState;
830 fn instance_number() -> usize; 832 fn instance_number() -> usize;
@@ -837,10 +839,13 @@ pub trait Instance: SealedInstance + PeripheralType {
837 type Interrupt: crate::interrupt::typelevel::Interrupt; 839 type Interrupt: crate::interrupt::typelevel::Interrupt;
838} 840}
839 841
840macro_rules! impl_instance { 842macro_rules! impl_usart_instance {
841 ($inst:ident, $fc:ident, $fc_num:expr) => { 843 ($inst:ident, $fc:ident, $fc_num:expr) => {
842 impl $crate::usart::inner::SealedInstance for $crate::peripherals::$inst { 844 impl crate::usart::SealedInstance for $crate::peripherals::$inst {
843 fn info() -> &'static Info { 845 fn info() -> &'static crate::usart::Info {
846 use crate::interrupt::typelevel::Interrupt;
847 use crate::usart::Info;
848
844 static INFO: Info = Info { 849 static INFO: Info = Info {
845 usart_reg: crate::pac::$inst, 850 usart_reg: crate::pac::$inst,
846 fc_reg: crate::pac::$fc, 851 fc_reg: crate::pac::$fc,
@@ -849,7 +854,13 @@ macro_rules! impl_instance {
849 &INFO 854 &INFO
850 } 855 }
851 856
852 fn dma_state() -> &'static DmaState { 857 fn dma_state() -> &'static crate::usart::DmaState {
858 use core::sync::atomic::AtomicBool;
859
860 use embassy_sync::waitqueue::AtomicWaker;
861
862 use crate::usart::DmaState;
863
853 static STATE: DmaState = DmaState { 864 static STATE: DmaState = DmaState {
854 rx_err_waker: AtomicWaker::new(), 865 rx_err_waker: AtomicWaker::new(),
855 rx_err: AtomicBool::new(false), 866 rx_err: AtomicBool::new(false),
@@ -867,15 +878,6 @@ macro_rules! impl_instance {
867 }; 878 };
868} 879}
869 880
870impl_instance!(USART0, FLEXCOMM0, 0);
871impl_instance!(USART1, FLEXCOMM1, 1);
872impl_instance!(USART2, FLEXCOMM2, 2);
873impl_instance!(USART3, FLEXCOMM3, 3);
874impl_instance!(USART4, FLEXCOMM4, 4);
875impl_instance!(USART5, FLEXCOMM5, 5);
876impl_instance!(USART6, FLEXCOMM6, 6);
877impl_instance!(USART7, FLEXCOMM7, 7);
878
879pub(crate) trait SealedTxPin<T: Instance>: crate::gpio::Pin { 881pub(crate) trait SealedTxPin<T: Instance>: crate::gpio::Pin {
880 fn pin_func(&self) -> PioFunc; 882 fn pin_func(&self) -> PioFunc;
881} 883}
@@ -892,75 +894,46 @@ pub trait TxPin<T: Instance>: SealedTxPin<T> + crate::gpio::Pin {}
892#[allow(private_bounds)] 894#[allow(private_bounds)]
893pub trait RxPin<T: Instance>: SealedRxPin<T> + crate::gpio::Pin {} 895pub trait RxPin<T: Instance>: SealedRxPin<T> + crate::gpio::Pin {}
894 896
895macro_rules! impl_tx_pin { 897macro_rules! impl_usart_txd_pin {
896 ($pin:ident, $instance:ident, $func: ident) => { 898 ($pin:ident, $instance:ident, $func: ident) => {
897 impl SealedTxPin<crate::peripherals::$instance> for crate::peripherals::$pin { 899 impl crate::usart::SealedTxPin<crate::peripherals::$instance> for crate::peripherals::$pin {
898 fn pin_func(&self) -> PioFunc { 900 fn pin_func(&self) -> crate::pac::iocon::vals::PioFunc {
901 use crate::pac::iocon::vals::PioFunc;
899 PioFunc::$func 902 PioFunc::$func
900 } 903 }
901 } 904 }
902 905
903 impl TxPin<crate::peripherals::$instance> for crate::peripherals::$pin {} 906 impl crate::usart::TxPin<crate::peripherals::$instance> for crate::peripherals::$pin {}
904 }; 907 };
905} 908}
906 909
907macro_rules! impl_rx_pin { 910macro_rules! impl_usart_rxd_pin {
908 ($pin:ident, $instance:ident, $func: ident) => { 911 ($pin:ident, $instance:ident, $func: ident) => {
909 impl SealedRxPin<crate::peripherals::$instance> for crate::peripherals::$pin { 912 impl crate::usart::SealedRxPin<crate::peripherals::$instance> for crate::peripherals::$pin {
910 fn pin_func(&self) -> PioFunc { 913 fn pin_func(&self) -> crate::pac::iocon::vals::PioFunc {
914 use crate::pac::iocon::vals::PioFunc;
911 PioFunc::$func 915 PioFunc::$func
912 } 916 }
913 } 917 }
914 918
915 impl RxPin<crate::peripherals::$instance> for crate::peripherals::$pin {} 919 impl crate::usart::RxPin<crate::peripherals::$instance> for crate::peripherals::$pin {}
916 }; 920 };
917} 921}
918 922
919impl_tx_pin!(PIO1_6, USART0, ALT1); 923/// Marker trait indicating a DMA channel may be used for USART transmit.
920impl_tx_pin!(PIO1_11, USART1, ALT2);
921impl_tx_pin!(PIO0_27, USART2, ALT1);
922impl_tx_pin!(PIO0_2, USART3, ALT1);
923impl_tx_pin!(PIO0_16, USART4, ALT1);
924impl_tx_pin!(PIO0_9, USART5, ALT3);
925impl_tx_pin!(PIO1_16, USART6, ALT2);
926impl_tx_pin!(PIO0_19, USART7, ALT7);
927
928impl_rx_pin!(PIO1_5, USART0, ALT1);
929impl_rx_pin!(PIO1_10, USART1, ALT2);
930impl_rx_pin!(PIO1_24, USART2, ALT1);
931impl_rx_pin!(PIO0_3, USART3, ALT1);
932impl_rx_pin!(PIO0_5, USART4, ALT2);
933impl_rx_pin!(PIO0_8, USART5, ALT3);
934impl_rx_pin!(PIO1_13, USART6, ALT2);
935impl_rx_pin!(PIO0_20, USART7, ALT7);
936
937/// Trait for TX DMA channels.
938pub trait TxChannel<T: Instance>: crate::dma::Channel {} 924pub trait TxChannel<T: Instance>: crate::dma::Channel {}
939/// Trait for RX DMA channels. 925
926/// Marker trait indicating a DMA channel may be used for USART recieve.
940pub trait RxChannel<T: Instance>: crate::dma::Channel {} 927pub trait RxChannel<T: Instance>: crate::dma::Channel {}
941 928
942macro_rules! impl_channel { 929macro_rules! impl_usart_tx_channel {
943 ($dma:ident, $instance:ident, Tx) => { 930 ($instance: ident, $channel: ident) => {
944 impl TxChannel<crate::peripherals::$instance> for crate::peripherals::$dma {} 931 impl crate::usart::TxChannel<crate::peripherals::$instance> for crate::peripherals::$channel {}
945 };
946 ($dma:ident, $instance:ident, Rx) => {
947 impl RxChannel<crate::peripherals::$instance> for crate::peripherals::$dma {}
948 }; 932 };
949} 933}
950 934
951impl_channel!(DMA_CH4, USART0, Rx); 935macro_rules! impl_usart_rx_channel {
952impl_channel!(DMA_CH5, USART0, Tx); 936 ($instance: ident, $channel: ident) => {
953impl_channel!(DMA_CH6, USART1, Rx); 937 impl crate::usart::RxChannel<crate::peripherals::$instance> for crate::peripherals::$channel {}
954impl_channel!(DMA_CH7, USART1, Tx); 938 };
955impl_channel!(DMA_CH10, USART2, Rx); 939}
956impl_channel!(DMA_CH11, USART2, Tx);
957impl_channel!(DMA_CH8, USART3, Rx);
958impl_channel!(DMA_CH9, USART3, Tx);
959impl_channel!(DMA_CH12, USART4, Rx);
960impl_channel!(DMA_CH13, USART4, Tx);
961impl_channel!(DMA_CH14, USART5, Rx);
962impl_channel!(DMA_CH15, USART5, Tx);
963impl_channel!(DMA_CH16, USART6, Rx);
964impl_channel!(DMA_CH17, USART6, Tx);
965impl_channel!(DMA_CH18, USART7, Rx);
966impl_channel!(DMA_CH19, USART7, Tx);
diff --git a/embassy-stm32-wpan/CHANGELOG.md b/embassy-stm32-wpan/CHANGELOG.md
index 7042ad14c..c567fe1de 100644
--- a/embassy-stm32-wpan/CHANGELOG.md
+++ b/embassy-stm32-wpan/CHANGELOG.md
@@ -8,4 +8,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8<!-- next-header --> 8<!-- next-header -->
9## Unreleased - ReleaseDate 9## Unreleased - ReleaseDate
10 10
11- restructure to allow embassy net driver to work.
11- First release with changelog. 12- First release with changelog.
diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml
index 0802b7328..05d76f4a6 100644
--- a/embassy-stm32-wpan/Cargo.toml
+++ b/embassy-stm32-wpan/Cargo.toml
@@ -33,6 +33,7 @@ embassy-futures = { version = "0.1.2", path = "../embassy-futures" }
33embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal" } 33embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal" }
34embassy-embedded-hal = { version = "0.5.0", path = "../embassy-embedded-hal" } 34embassy-embedded-hal = { version = "0.5.0", path = "../embassy-embedded-hal" }
35embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver", optional=true } 35embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver", optional=true }
36smoltcp = { version = "0.12.0", optional=true, default-features = false }
36 37
37defmt = { version = "1.0.1", optional = true } 38defmt = { version = "1.0.1", optional = true }
38log = { version = "0.4.17", optional = true } 39log = { version = "0.4.17", optional = true }
@@ -40,6 +41,7 @@ log = { version = "0.4.17", optional = true }
40cortex-m = "0.7.6" 41cortex-m = "0.7.6"
41heapless = "0.8" 42heapless = "0.8"
42aligned = "0.4.1" 43aligned = "0.4.1"
44critical-section = "1.1"
43 45
44bit_field = "0.10.2" 46bit_field = "0.10.2"
45stm32-device-signature = { version = "0.3.3", features = ["stm32wb5x"] } 47stm32-device-signature = { version = "0.3.3", features = ["stm32wb5x"] }
@@ -51,7 +53,7 @@ bitflags = { version = "2.3.3", optional = true }
51defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "stm32wb-hci?/defmt"] 53defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "stm32wb-hci?/defmt"]
52 54
53ble = ["dep:stm32wb-hci"] 55ble = ["dep:stm32wb-hci"]
54mac = ["dep:bitflags", "dep:embassy-net-driver" ] 56mac = ["dep:bitflags", "dep:embassy-net-driver", "dep:smoltcp", "smoltcp/medium-ieee802154"]
55 57
56extended = [] 58extended = []
57 59
diff --git a/embassy-stm32-wpan/src/mac/commands.rs b/embassy-stm32-wpan/src/mac/commands.rs
index 82b9d2772..a3a40f377 100644
--- a/embassy-stm32-wpan/src/mac/commands.rs
+++ b/embassy-stm32-wpan/src/mac/commands.rs
@@ -2,6 +2,8 @@
2 2
3use core::{mem, slice}; 3use core::{mem, slice};
4 4
5use smoltcp::wire::ieee802154::Frame;
6
5use super::opcodes::OpcodeM4ToM0; 7use super::opcodes::OpcodeM4ToM0;
6use super::typedefs::{ 8use super::typedefs::{
7 AddressMode, Capabilities, DisassociationReason, GtsCharacteristics, KeyIdMode, MacAddress, MacChannel, MacStatus, 9 AddressMode, Capabilities, DisassociationReason, GtsCharacteristics, KeyIdMode, MacAddress, MacChannel, MacStatus,
@@ -379,6 +381,30 @@ impl DataRequest {
379 } 381 }
380} 382}
381 383
384impl<'a, T: AsRef<[u8]>> TryFrom<Frame<&'a T>> for DataRequest {
385 type Error = ();
386
387 fn try_from(frame: Frame<&'a T>) -> Result<Self, Self::Error> {
388 // TODO: map the rest of these
389
390 let mut request = DataRequest {
391 src_addr_mode: frame.src_addressing_mode().try_into()?,
392 dst_addr_mode: frame.dst_addressing_mode().try_into()?,
393 dst_pan_id: frame.dst_pan_id().ok_or(())?.into(),
394 dst_address: frame.dst_addr().ok_or(())?.into(),
395 msdu_handle: frame.sequence_number().ok_or(())?,
396 ack_tx: 0x00,
397 gts_tx: false,
398 security_level: SecurityLevel::Unsecure,
399 ..Default::default()
400 };
401
402 request.set_buffer(frame.payload().ok_or(())?);
403
404 Ok(request)
405 }
406}
407
382impl Default for DataRequest { 408impl Default for DataRequest {
383 fn default() -> Self { 409 fn default() -> Self {
384 Self { 410 Self {
diff --git a/embassy-stm32-wpan/src/mac/control.rs b/embassy-stm32-wpan/src/mac/control.rs
index e8d2f9f7b..d2a7b65ee 100644
--- a/embassy-stm32-wpan/src/mac/control.rs
+++ b/embassy-stm32-wpan/src/mac/control.rs
@@ -1,65 +1,186 @@
1use core::cell::RefCell;
1use core::future::Future; 2use core::future::Future;
3use core::sync::atomic::{Ordering, compiler_fence};
2use core::task; 4use core::task;
3use core::task::Poll; 5use core::task::Poll;
4 6
7use embassy_net_driver::LinkState;
8use embassy_sync::blocking_mutex;
5use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; 9use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
6use embassy_sync::mutex::MutexGuard; 10use embassy_sync::mutex::Mutex;
7use embassy_sync::signal::Signal; 11use embassy_sync::signal::Signal;
8use futures_util::FutureExt; 12use futures_util::FutureExt;
9 13
10use super::commands::MacCommand; 14use crate::mac::commands::*;
11use super::event::MacEvent; 15use crate::mac::driver::NetworkState;
12use super::typedefs::MacError; 16use crate::mac::event::MacEvent;
13use crate::mac::runner::Runner; 17use crate::mac::runner::ZeroCopyPubSub;
18use crate::mac::typedefs::*;
19use crate::sub::mac::MacTx;
14 20
15pub struct Control<'a> { 21pub struct Control<'a> {
16 runner: &'a Runner<'a>, 22 rx_event_channel: &'a ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>,
23 mac_tx: &'a Mutex<CriticalSectionRawMutex, MacTx>,
24 #[allow(unused)]
25 network_state: &'a blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<NetworkState>>,
17} 26}
18 27
19impl<'a> Control<'a> { 28impl<'a> Control<'a> {
20 pub(crate) fn new(runner: &'a Runner<'a>) -> Self { 29 pub(crate) fn new(
21 Self { runner: runner } 30 rx_event_channel: &'a ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>,
31 mac_tx: &'a Mutex<CriticalSectionRawMutex, MacTx>,
32 network_state: &'a blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<NetworkState>>,
33 ) -> Self {
34 Self {
35 rx_event_channel,
36 mac_tx,
37 network_state,
38 }
39 }
40
41 pub async fn init_link(&mut self, pan_id: [u8; 2]) {
42 debug!("resetting");
43
44 debug!(
45 "{:#x}",
46 self.send_command_and_get_response(&ResetRequest {
47 set_default_pib: true,
48 ..Default::default()
49 })
50 .await
51 .unwrap()
52 .await
53 );
54
55 let (short_address, mac_address) = critical_section::with(|cs| {
56 let mut network_state = self.network_state.borrow(cs).borrow_mut();
57
58 network_state.pan_id = pan_id;
59
60 (network_state.short_addr, network_state.mac_addr)
61 });
62
63 debug!("setting extended address");
64 debug!(
65 "{:#x}",
66 self.send_command_and_get_response(&SetRequest {
67 pib_attribute_ptr: &u64::from_be_bytes(mac_address) as *const _ as *const u8,
68 pib_attribute: PibId::ExtendedAddress,
69 })
70 .await
71 .unwrap()
72 .await
73 );
74
75 debug!("setting short address");
76 debug!(
77 "{:#x}",
78 self.send_command_and_get_response(&SetRequest {
79 pib_attribute_ptr: &u16::from_be_bytes(short_address) as *const _ as *const u8,
80 pib_attribute: PibId::ShortAddress,
81 })
82 .await
83 .unwrap()
84 .await
85 );
86
87 debug!("setting association permit");
88 let association_permit: bool = true;
89 debug!(
90 "{:#x}",
91 self.send_command_and_get_response(&SetRequest {
92 pib_attribute_ptr: &association_permit as *const _ as *const u8,
93 pib_attribute: PibId::AssociationPermit,
94 })
95 .await
96 .unwrap()
97 .await
98 );
99
100 debug!("setting TX power");
101 let transmit_power: i8 = 2;
102 debug!(
103 "{:#x}",
104 self.send_command_and_get_response(&SetRequest {
105 pib_attribute_ptr: &transmit_power as *const _ as *const u8,
106 pib_attribute: PibId::TransmitPower,
107 })
108 .await
109 .unwrap()
110 .await
111 );
112
113 debug!("starting FFD device");
114 debug!(
115 "{:#x}",
116 self.send_command_and_get_response(&StartRequest {
117 pan_id: PanId(pan_id),
118 channel_number: MacChannel::Channel16,
119 beacon_order: 0x0F,
120 superframe_order: 0x0F,
121 pan_coordinator: true,
122 battery_life_extension: false,
123 ..Default::default()
124 })
125 .await
126 .unwrap()
127 .await
128 );
129
130 debug!("setting RX on when idle");
131 let rx_on_while_idle: bool = true;
132 debug!(
133 "{:#x}",
134 self.send_command_and_get_response(&SetRequest {
135 pib_attribute_ptr: &rx_on_while_idle as *const _ as *const u8,
136 pib_attribute: PibId::RxOnWhenIdle,
137 })
138 .await
139 .unwrap()
140 .await
141 );
142
143 critical_section::with(|cs| {
144 let mut network_state = self.network_state.borrow(cs).borrow_mut();
145
146 network_state.link_state = LinkState::Up;
147 network_state.link_waker.wake();
148 });
22 } 149 }
23 150
24 pub async fn send_command<T>(&self, cmd: &T) -> Result<(), MacError> 151 pub async fn send_command<T>(&self, cmd: &T) -> Result<(), MacError>
25 where 152 where
26 T: MacCommand, 153 T: MacCommand,
27 { 154 {
28 let _wm = self.runner.write_mutex.lock().await; 155 self.mac_tx.lock().await.send_command(cmd).await
29
30 self.runner.mac_subsystem.send_command(cmd).await
31 } 156 }
32 157
33 pub async fn send_command_and_get_response<T>(&self, cmd: &T) -> Result<EventToken<'a>, MacError> 158 pub async fn send_command_and_get_response<T>(&self, cmd: &T) -> Result<EventToken<'a>, MacError>
34 where 159 where
35 T: MacCommand, 160 T: MacCommand,
36 { 161 {
37 let rm = self.runner.read_mutex.lock().await; 162 let token = EventToken::new(self.rx_event_channel);
38 let _wm = self.runner.write_mutex.lock().await;
39 let token = EventToken::new(self.runner, rm);
40 163
41 self.runner.mac_subsystem.send_command(cmd).await?; 164 compiler_fence(Ordering::Release);
165
166 self.mac_tx.lock().await.send_command(cmd).await?;
42 167
43 Ok(token) 168 Ok(token)
44 } 169 }
45} 170}
46 171
47pub struct EventToken<'a> { 172pub struct EventToken<'a> {
48 runner: &'a Runner<'a>, 173 rx_event_channel: &'a ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>,
49 _mutex_guard: MutexGuard<'a, CriticalSectionRawMutex, ()>,
50} 174}
51 175
52impl<'a> EventToken<'a> { 176impl<'a> EventToken<'a> {
53 pub(crate) fn new(runner: &'a Runner<'a>, mutex_guard: MutexGuard<'a, CriticalSectionRawMutex, ()>) -> Self { 177 pub(crate) fn new(rx_event_channel: &'a ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>) -> Self {
54 // Enable event receiving 178 // Enable event receiving
55 runner.rx_event_channel.lock(|s| { 179 rx_event_channel.lock(|s| {
56 *s.borrow_mut() = Some(Signal::new()); 180 *s.borrow_mut() = Some(Signal::new());
57 }); 181 });
58 182
59 Self { 183 Self { rx_event_channel }
60 runner: runner,
61 _mutex_guard: mutex_guard,
62 }
63 } 184 }
64} 185}
65 186
@@ -67,20 +188,8 @@ impl<'a> Future for EventToken<'a> {
67 type Output = MacEvent<'a>; 188 type Output = MacEvent<'a>;
68 189
69 fn poll(self: core::pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> { 190 fn poll(self: core::pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
70 self.get_mut().runner.rx_event_channel.lock(|s| { 191 self.rx_event_channel
71 let signal = s.borrow_mut(); 192 .lock(|s| s.borrow_mut().as_mut().unwrap().wait().poll_unpin(cx))
72 let signal = match &*signal {
73 Some(s) => s,
74 _ => unreachable!(),
75 };
76
77 let result = match signal.wait().poll_unpin(cx) {
78 Poll::Ready(mac_event) => Poll::Ready(mac_event),
79 Poll::Pending => Poll::Pending,
80 };
81
82 result
83 })
84 } 193 }
85} 194}
86 195
@@ -88,7 +197,7 @@ impl<'a> Drop for EventToken<'a> {
88 fn drop(&mut self) { 197 fn drop(&mut self) {
89 // Disable event receiving 198 // Disable event receiving
90 // This will also drop the contained event, if it exists, and will free up receiving the next event 199 // This will also drop the contained event, if it exists, and will free up receiving the next event
91 self.runner.rx_event_channel.lock(|s| { 200 self.rx_event_channel.lock(|s| {
92 *s.borrow_mut() = None; 201 *s.borrow_mut() = None;
93 }); 202 });
94 } 203 }
diff --git a/embassy-stm32-wpan/src/mac/driver.rs b/embassy-stm32-wpan/src/mac/driver.rs
index 480ac3790..c43d595b7 100644
--- a/embassy-stm32-wpan/src/mac/driver.rs
+++ b/embassy-stm32-wpan/src/mac/driver.rs
@@ -1,22 +1,108 @@
1#![deny(unused_must_use)] 1#![deny(unused_must_use)]
2 2
3use core::cell::RefCell;
3use core::task::Context; 4use core::task::Context;
4 5
5use embassy_net_driver::{Capabilities, HardwareAddress, LinkState}; 6use embassy_net_driver::{Capabilities, HardwareAddress, LinkState};
7use embassy_sync::blocking_mutex;
6use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; 8use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
7use embassy_sync::channel::Channel; 9use embassy_sync::channel::Channel;
10use embassy_sync::mutex::Mutex;
11use embassy_sync::waitqueue::AtomicWaker;
8 12
9use crate::mac::MTU;
10use crate::mac::event::MacEvent; 13use crate::mac::event::MacEvent;
11use crate::mac::runner::Runner; 14use crate::mac::indications::{write_frame_from_beacon_indication, write_frame_from_data_indication};
15use crate::mac::runner::{BUF_SIZE, ZeroCopyPubSub};
16use crate::mac::{Control, MTU, Runner};
17use crate::sub::mac::{Mac, MacRx, MacTx};
18
19pub struct NetworkState {
20 pub mac_addr: [u8; 8],
21 pub short_addr: [u8; 2],
22 pub pan_id: [u8; 2],
23 pub link_state: LinkState,
24 pub link_waker: AtomicWaker,
25}
26
27impl NetworkState {
28 pub const fn new() -> Self {
29 Self {
30 mac_addr: [0u8; 8],
31 short_addr: [0u8; 2],
32 pan_id: [0u8; 2],
33 link_state: LinkState::Down,
34 link_waker: AtomicWaker::new(),
35 }
36 }
37}
38
39pub struct DriverState<'d> {
40 pub mac_tx: Mutex<CriticalSectionRawMutex, MacTx>,
41 pub mac_rx: MacRx,
42 pub rx_event_channel: ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'d>>,
43 pub rx_data_channel: Channel<CriticalSectionRawMutex, MacEvent<'d>, 1>,
44 pub tx_data_channel: Channel<CriticalSectionRawMutex, (&'d mut [u8; MTU], usize), BUF_SIZE>,
45 pub tx_buf_channel: Channel<CriticalSectionRawMutex, &'d mut [u8; MTU], BUF_SIZE>,
46 pub tx_buf_queue: [[u8; MTU]; BUF_SIZE],
47 pub network_state: blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<NetworkState>>,
48}
49
50impl<'d> DriverState<'d> {
51 pub const fn new(mac: Mac) -> Self {
52 let (mac_rx, mac_tx) = mac.split();
53 let mac_tx = Mutex::new(mac_tx);
54
55 Self {
56 mac_tx,
57 mac_rx,
58 rx_event_channel: ZeroCopyPubSub::new(RefCell::new(None)),
59 rx_data_channel: Channel::new(),
60 tx_data_channel: Channel::new(),
61 tx_buf_channel: Channel::new(),
62 tx_buf_queue: [[0u8; MTU]; BUF_SIZE],
63 network_state: blocking_mutex::Mutex::new(RefCell::new(NetworkState::new())),
64 }
65 }
66}
12 67
13pub struct Driver<'d> { 68pub struct Driver<'d> {
14 runner: &'d Runner<'d>, 69 tx_data_channel: &'d Channel<CriticalSectionRawMutex, (&'d mut [u8; MTU], usize), BUF_SIZE>,
70 tx_buf_channel: &'d Channel<CriticalSectionRawMutex, &'d mut [u8; MTU], BUF_SIZE>,
71 rx_data_channel: &'d Channel<CriticalSectionRawMutex, MacEvent<'d>, 1>,
72 network_state: &'d blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<NetworkState>>,
15} 73}
16 74
17impl<'d> Driver<'d> { 75impl<'d> Driver<'d> {
18 pub(crate) fn new(runner: &'d Runner<'d>) -> Self { 76 pub fn new(
19 Self { runner: runner } 77 driver_state: &'d mut DriverState<'d>,
78 short_address: [u8; 2],
79 mac_address: [u8; 8],
80 ) -> (Self, Runner<'d>, Control<'d>) {
81 (
82 Self {
83 tx_data_channel: &driver_state.tx_data_channel,
84 tx_buf_channel: &driver_state.tx_buf_channel,
85 rx_data_channel: &driver_state.rx_data_channel,
86 network_state: &driver_state.network_state,
87 },
88 Runner::new(
89 &driver_state.rx_event_channel,
90 &driver_state.rx_data_channel,
91 &mut driver_state.mac_rx,
92 &driver_state.tx_data_channel,
93 &driver_state.tx_buf_channel,
94 &driver_state.mac_tx,
95 &mut driver_state.tx_buf_queue,
96 &driver_state.network_state,
97 short_address,
98 mac_address,
99 ),
100 Control::new(
101 &driver_state.rx_event_channel,
102 &driver_state.mac_tx,
103 &driver_state.network_state,
104 ),
105 )
20 } 106 }
21} 107}
22 108
@@ -33,16 +119,16 @@ impl<'d> embassy_net_driver::Driver for Driver<'d> {
33 Self: 'a; 119 Self: 'a;
34 120
35 fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { 121 fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
36 if self.runner.rx_channel.poll_ready_to_receive(cx).is_ready() 122 if self.rx_data_channel.poll_ready_to_receive(cx).is_ready()
37 && self.runner.tx_buf_channel.poll_ready_to_receive(cx).is_ready() 123 && self.tx_buf_channel.poll_ready_to_receive(cx).is_ready()
38 { 124 {
39 Some(( 125 Some((
40 RxToken { 126 RxToken {
41 rx: &self.runner.rx_channel, 127 rx: self.rx_data_channel,
42 }, 128 },
43 TxToken { 129 TxToken {
44 tx: &self.runner.tx_channel, 130 tx: self.tx_data_channel,
45 tx_buf: &self.runner.tx_buf_channel, 131 tx_buf: self.tx_buf_channel,
46 }, 132 },
47 )) 133 ))
48 } else { 134 } else {
@@ -51,10 +137,10 @@ impl<'d> embassy_net_driver::Driver for Driver<'d> {
51 } 137 }
52 138
53 fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> { 139 fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> {
54 if self.runner.tx_buf_channel.poll_ready_to_receive(cx).is_ready() { 140 if self.tx_buf_channel.poll_ready_to_receive(cx).is_ready() {
55 Some(TxToken { 141 Some(TxToken {
56 tx: &self.runner.tx_channel, 142 tx: self.tx_data_channel,
57 tx_buf: &self.runner.tx_buf_channel, 143 tx_buf: self.tx_buf_channel,
58 }) 144 })
59 } else { 145 } else {
60 None 146 None
@@ -68,13 +154,20 @@ impl<'d> embassy_net_driver::Driver for Driver<'d> {
68 caps 154 caps
69 } 155 }
70 156
71 fn link_state(&mut self, _cx: &mut Context) -> LinkState { 157 fn link_state(&mut self, cx: &mut Context) -> LinkState {
72 LinkState::Down 158 critical_section::with(|cs| {
159 let network_state = self.network_state.borrow(cs).borrow_mut();
160
161 // Unconditionally register the waker to avoid a race
162 network_state.link_waker.register(cx.waker());
163 network_state.link_state
164 })
73 } 165 }
74 166
75 fn hardware_address(&self) -> HardwareAddress { 167 fn hardware_address(&self) -> HardwareAddress {
76 // self.mac_addr 168 HardwareAddress::Ieee802154(critical_section::with(|cs| {
77 HardwareAddress::Ieee802154([0; 8]) 169 self.network_state.borrow(cs).borrow().mac_addr
170 }))
78 } 171 }
79} 172}
80 173
@@ -87,20 +180,20 @@ impl<'d> embassy_net_driver::RxToken for RxToken<'d> {
87 where 180 where
88 F: FnOnce(&mut [u8]) -> R, 181 F: FnOnce(&mut [u8]) -> R,
89 { 182 {
90 // Only valid data events should be put into the queue 183 let mut buffer = [0u8; MTU];
91 184 match self.rx.try_receive().unwrap() {
92 let data_event = match self.rx.try_receive().unwrap() { 185 MacEvent::McpsDataInd(data_event) => write_frame_from_data_indication(data_event, &mut buffer),
93 MacEvent::McpsDataInd(data_event) => data_event, 186 MacEvent::MlmeBeaconNotifyInd(data_event) => write_frame_from_beacon_indication(data_event, &mut buffer),
94 _ => unreachable!(), 187 _ => {}
95 }; 188 };
96 189
97 f(&mut data_event.payload()) 190 f(&mut buffer[..])
98 } 191 }
99} 192}
100 193
101pub struct TxToken<'d> { 194pub struct TxToken<'d> {
102 tx: &'d Channel<CriticalSectionRawMutex, (&'d mut [u8; MTU], usize), 5>, 195 tx: &'d Channel<CriticalSectionRawMutex, (&'d mut [u8; MTU], usize), BUF_SIZE>,
103 tx_buf: &'d Channel<CriticalSectionRawMutex, &'d mut [u8; MTU], 5>, 196 tx_buf: &'d Channel<CriticalSectionRawMutex, &'d mut [u8; MTU], BUF_SIZE>,
104} 197}
105 198
106impl<'d> embassy_net_driver::TxToken for TxToken<'d> { 199impl<'d> embassy_net_driver::TxToken for TxToken<'d> {
diff --git a/embassy-stm32-wpan/src/mac/indications.rs b/embassy-stm32-wpan/src/mac/indications.rs
index c0b86d745..5673514c9 100644
--- a/embassy-stm32-wpan/src/mac/indications.rs
+++ b/embassy-stm32-wpan/src/mac/indications.rs
@@ -1,11 +1,15 @@
1use core::slice; 1use core::slice;
2 2
3use smoltcp::wire::Ieee802154FrameType;
4use smoltcp::wire::ieee802154::Frame;
5
3use super::consts::MAX_PENDING_ADDRESS; 6use super::consts::MAX_PENDING_ADDRESS;
4use super::event::ParseableMacEvent; 7use super::event::ParseableMacEvent;
5use super::typedefs::{ 8use super::typedefs::{
6 AddressMode, Capabilities, DisassociationReason, KeyIdMode, MacAddress, MacChannel, MacStatus, PanDescriptor, 9 AddressMode, Capabilities, DisassociationReason, KeyIdMode, MacAddress, MacChannel, MacStatus, PanDescriptor,
7 PanId, SecurityLevel, 10 PanId, SecurityLevel,
8}; 11};
12use crate::mac::typedefs::MacAddressAndMode;
9 13
10/// MLME ASSOCIATE Indication which will be used by the MAC 14/// MLME ASSOCIATE Indication which will be used by the MAC
11/// to indicate the reception of an association request command 15/// to indicate the reception of an association request command
@@ -74,6 +78,22 @@ pub struct BeaconNotifyIndication {
74 78
75impl ParseableMacEvent for BeaconNotifyIndication {} 79impl ParseableMacEvent for BeaconNotifyIndication {}
76 80
81impl BeaconNotifyIndication {
82 pub fn payload<'a>(&'a self) -> &'a mut [u8] {
83 unsafe { slice::from_raw_parts_mut(self.sdu_ptr as *mut _, self.sdu_length as usize) }
84 }
85}
86
87pub fn write_frame_from_beacon_indication<'a, T: AsRef<[u8]> + AsMut<[u8]>>(
88 data: &'a BeaconNotifyIndication,
89 buffer: &'a mut T,
90) {
91 let mut frame = Frame::new_unchecked(buffer);
92
93 frame.set_frame_type(Ieee802154FrameType::Beacon);
94 frame.set_sequence_number(data.bsn);
95}
96
77/// MLME COMM STATUS Indication which is used by the MAC to indicate a communications status 97/// MLME COMM STATUS Indication which is used by the MAC to indicate a communications status
78#[repr(C)] 98#[repr(C)]
79#[derive(Debug)] 99#[derive(Debug)]
@@ -250,6 +270,21 @@ impl DataIndication {
250 } 270 }
251} 271}
252 272
273pub fn write_frame_from_data_indication<'a, T: AsRef<[u8]> + AsMut<[u8]>>(data: &'a DataIndication, buffer: &'a mut T) {
274 let mut frame = Frame::new_unchecked(buffer);
275
276 frame.set_frame_type(Ieee802154FrameType::Data);
277 frame.set_src_addr(MacAddressAndMode(data.src_address, data.src_addr_mode).into());
278 frame.set_dst_addr(MacAddressAndMode(data.dst_address, data.dst_addr_mode).into());
279 frame.set_dst_pan_id(data.dst_pan_id.into());
280 frame.set_src_pan_id(data.src_pan_id.into());
281 frame.set_sequence_number(data.dsn);
282 frame.set_security_enabled(data.security_level == SecurityLevel::Secured);
283
284 // No way around the copy with the current API
285 frame.payload_mut().unwrap().copy_from_slice(data.payload());
286}
287
253/// MLME POLL Indication which will be used for indicating the Data Request 288/// MLME POLL Indication which will be used for indicating the Data Request
254/// reception to upper layer as defined in Zigbee r22 - D.8.2 289/// reception to upper layer as defined in Zigbee r22 - D.8.2
255#[repr(C)] 290#[repr(C)]
diff --git a/embassy-stm32-wpan/src/mac/mod.rs b/embassy-stm32-wpan/src/mac/mod.rs
index c847a5cca..ac50a6b29 100644
--- a/embassy-stm32-wpan/src/mac/mod.rs
+++ b/embassy-stm32-wpan/src/mac/mod.rs
@@ -11,11 +11,7 @@ pub mod runner;
11pub mod typedefs; 11pub mod typedefs;
12 12
13pub use crate::mac::control::Control; 13pub use crate::mac::control::Control;
14use crate::mac::driver::Driver; 14pub use crate::mac::driver::{Driver, DriverState};
15pub use crate::mac::runner::Runner; 15pub use crate::mac::runner::Runner;
16 16
17const MTU: usize = 127; 17const MTU: usize = 127;
18
19pub async fn new<'a>(runner: &'a Runner<'a>) -> (Control<'a>, Driver<'a>) {
20 (Control::new(runner), Driver::new(runner))
21}
diff --git a/embassy-stm32-wpan/src/mac/runner.rs b/embassy-stm32-wpan/src/mac/runner.rs
index 2409f994d..acca70019 100644
--- a/embassy-stm32-wpan/src/mac/runner.rs
+++ b/embassy-stm32-wpan/src/mac/runner.rs
@@ -6,96 +6,138 @@ use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex};
6use embassy_sync::channel::Channel; 6use embassy_sync::channel::Channel;
7use embassy_sync::mutex::Mutex; 7use embassy_sync::mutex::Mutex;
8use embassy_sync::signal::Signal; 8use embassy_sync::signal::Signal;
9use smoltcp::wire::Ieee802154FrameType;
10use smoltcp::wire::ieee802154::Frame;
9 11
10use crate::mac::MTU; 12use crate::mac::MTU;
11use crate::mac::commands::DataRequest; 13use crate::mac::commands::*;
14use crate::mac::driver::NetworkState;
12use crate::mac::event::MacEvent; 15use crate::mac::event::MacEvent;
13use crate::mac::typedefs::{AddressMode, MacAddress, PanId, SecurityLevel}; 16use crate::sub::mac::{MacRx, MacTx};
14use crate::sub::mac::Mac;
15 17
16type ZeroCopyPubSub<M, T> = blocking_mutex::Mutex<M, RefCell<Option<Signal<NoopRawMutex, T>>>>; 18pub type ZeroCopyPubSub<M, T> = blocking_mutex::Mutex<M, RefCell<Option<Signal<NoopRawMutex, T>>>>;
19
20pub const BUF_SIZE: usize = 3;
17 21
18pub struct Runner<'a> { 22pub struct Runner<'a> {
19 pub(crate) mac_subsystem: Mac,
20 // rx event backpressure is already provided through the MacEvent drop mechanism 23 // rx event backpressure is already provided through the MacEvent drop mechanism
21 // therefore, we don't need to worry about overwriting events 24 // therefore, we don't need to worry about overwriting events
22 pub(crate) rx_event_channel: ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>, 25 rx_event_channel: &'a ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>,
23 pub(crate) read_mutex: Mutex<CriticalSectionRawMutex, ()>, 26 rx_data_channel: &'a Channel<CriticalSectionRawMutex, MacEvent<'a>, 1>,
24 pub(crate) write_mutex: Mutex<CriticalSectionRawMutex, ()>, 27 mac_rx: &'a mut MacRx,
25 pub(crate) rx_channel: Channel<CriticalSectionRawMutex, MacEvent<'a>, 1>, 28
26 pub(crate) tx_channel: Channel<CriticalSectionRawMutex, (&'a mut [u8; MTU], usize), 5>, 29 tx_data_channel: &'a Channel<CriticalSectionRawMutex, (&'a mut [u8; MTU], usize), BUF_SIZE>,
27 pub(crate) tx_buf_channel: Channel<CriticalSectionRawMutex, &'a mut [u8; MTU], 5>, 30 tx_buf_channel: &'a Channel<CriticalSectionRawMutex, &'a mut [u8; MTU], BUF_SIZE>,
31 mac_tx: &'a Mutex<CriticalSectionRawMutex, MacTx>,
32
33 #[allow(unused)]
34 network_state: &'a blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<NetworkState>>,
28} 35}
29 36
30impl<'a> Runner<'a> { 37impl<'a> Runner<'a> {
31 pub fn new(mac: Mac, tx_buf_queue: [&'a mut [u8; MTU]; 5]) -> Self { 38 pub(crate) fn new(
32 let this = Self { 39 rx_event_channel: &'a ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>,
33 mac_subsystem: mac, 40 rx_data_channel: &'a Channel<CriticalSectionRawMutex, MacEvent<'a>, 1>,
34 rx_event_channel: blocking_mutex::Mutex::new(RefCell::new(None)), 41 mac_rx: &'a mut MacRx,
35 read_mutex: Mutex::new(()), 42 tx_data_channel: &'a Channel<CriticalSectionRawMutex, (&'a mut [u8; MTU], usize), BUF_SIZE>,
36 write_mutex: Mutex::new(()), 43 tx_buf_channel: &'a Channel<CriticalSectionRawMutex, &'a mut [u8; MTU], BUF_SIZE>,
37 rx_channel: Channel::new(), 44 mac_tx: &'a Mutex<CriticalSectionRawMutex, MacTx>,
38 tx_channel: Channel::new(), 45 tx_buf_queue: &'a mut [[u8; MTU]; BUF_SIZE],
39 tx_buf_channel: Channel::new(), 46 network_state: &'a blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<NetworkState>>,
40 }; 47 short_address: [u8; 2],
41 48 mac_address: [u8; 8],
49 ) -> Self {
42 for buf in tx_buf_queue { 50 for buf in tx_buf_queue {
43 this.tx_buf_channel.try_send(buf).unwrap(); 51 tx_buf_channel.try_send(buf).unwrap();
52 }
53
54 critical_section::with(|cs| {
55 let mut network_state = network_state.borrow(cs).borrow_mut();
56
57 network_state.mac_addr = mac_address;
58 network_state.short_addr = short_address;
59 });
60
61 Self {
62 rx_event_channel,
63 rx_data_channel,
64 mac_rx,
65 tx_data_channel,
66 tx_buf_channel,
67 mac_tx,
68 network_state,
44 } 69 }
70 }
71
72 async fn send_request<T: MacCommand, U: TryInto<T>>(&self, frame: U) -> Result<(), ()>
73 where
74 (): From<<U as TryInto<T>>::Error>,
75 {
76 let request: T = frame.try_into()?;
77 self.mac_tx.lock().await.send_command(&request).await.map_err(|_| ())?;
45 78
46 this 79 Ok(())
47 } 80 }
48 81
49 pub async fn run(&'a self) -> ! { 82 pub async fn run(&'a self) -> ! {
50 join::join( 83 join::join(
51 async { 84 async {
52 loop { 85 loop {
53 if let Ok(mac_event) = self.mac_subsystem.read().await { 86 if let Ok(mac_event) = self.mac_rx.read().await {
54 match mac_event { 87 match mac_event {
88 MacEvent::MlmeAssociateCnf(_)
89 | MacEvent::MlmeDisassociateCnf(_)
90 | MacEvent::MlmeGetCnf(_)
91 | MacEvent::MlmeGtsCnf(_)
92 | MacEvent::MlmeResetCnf(_)
93 | MacEvent::MlmeRxEnableCnf(_)
94 | MacEvent::MlmeScanCnf(_)
95 | MacEvent::MlmeSetCnf(_)
96 | MacEvent::MlmeStartCnf(_)
97 | MacEvent::MlmePollCnf(_)
98 | MacEvent::MlmeDpsCnf(_)
99 | MacEvent::MlmeSoundingCnf(_)
100 | MacEvent::MlmeCalibrateCnf(_)
101 | MacEvent::McpsDataCnf(_)
102 | MacEvent::McpsPurgeCnf(_) => {
103 self.rx_event_channel.lock(|s| {
104 s.borrow().as_ref().map(|signal| signal.signal(mac_event));
105 });
106 }
55 MacEvent::McpsDataInd(_) => { 107 MacEvent::McpsDataInd(_) => {
56 self.rx_channel.send(mac_event).await; 108 // Pattern should match driver
109 self.rx_data_channel.send(mac_event).await;
57 } 110 }
58 _ => { 111 _ => {
59 self.rx_event_channel.lock(|s| { 112 debug!("unhandled mac event: {:#x}", mac_event);
60 match &*s.borrow() {
61 Some(signal) => {
62 signal.signal(mac_event);
63 }
64 None => {}
65 };
66 });
67 } 113 }
68 } 114 }
69 } 115 }
70 } 116 }
71 }, 117 },
72 async { 118 async {
73 let mut msdu_handle = 0x02;
74
75 loop { 119 loop {
76 let (buf, len) = self.tx_channel.receive().await; 120 let (buf, _) = self.tx_data_channel.receive().await;
77 let _wm = self.write_mutex.lock().await; 121
78 122 // Smoltcp has created this frame, so there's no need to reparse it.
79 // The mutex should be dropped on the next loop iteration 123 let frame = Frame::new_unchecked(&buf);
80 self.mac_subsystem
81 .send_command(
82 DataRequest {
83 src_addr_mode: AddressMode::Short,
84 dst_addr_mode: AddressMode::Short,
85 dst_pan_id: PanId([0x1A, 0xAA]),
86 dst_address: MacAddress::BROADCAST,
87 msdu_handle: msdu_handle,
88 ack_tx: 0x00,
89 gts_tx: false,
90 security_level: SecurityLevel::Unsecure,
91 ..Default::default()
92 }
93 .set_buffer(&buf[..len]),
94 )
95 .await
96 .unwrap();
97 124
98 msdu_handle = msdu_handle.wrapping_add(1); 125 let result: Result<(), ()> = match frame.frame_type() {
126 Ieee802154FrameType::Beacon => Err(()),
127 Ieee802154FrameType::Data => self.send_request::<DataRequest, _>(frame).await,
128 Ieee802154FrameType::Acknowledgement => Err(()),
129 Ieee802154FrameType::MacCommand => Err(()),
130 Ieee802154FrameType::Multipurpose => Err(()),
131 Ieee802154FrameType::FragmentOrFrak => Err(()),
132 Ieee802154FrameType::Extended => Err(()),
133 _ => Err(()),
134 };
135
136 if result.is_err() {
137 debug!("failed to parse mac frame");
138 } else {
139 trace!("data frame sent!");
140 }
99 141
100 // The tx channel should always be of equal capacity to the tx_buf channel 142 // The tx channel should always be of equal capacity to the tx_buf channel
101 self.tx_buf_channel.try_send(buf).unwrap(); 143 self.tx_buf_channel.try_send(buf).unwrap();
diff --git a/embassy-stm32-wpan/src/mac/typedefs.rs b/embassy-stm32-wpan/src/mac/typedefs.rs
index 0552b8ea1..175d4a37d 100644
--- a/embassy-stm32-wpan/src/mac/typedefs.rs
+++ b/embassy-stm32-wpan/src/mac/typedefs.rs
@@ -1,5 +1,7 @@
1use core::fmt::Debug; 1use core::fmt::Debug;
2 2
3use smoltcp::wire::ieee802154::{Address, AddressingMode, Pan};
4
3use crate::numeric_enum; 5use crate::numeric_enum;
4 6
5#[derive(Debug)] 7#[derive(Debug)]
@@ -109,12 +111,51 @@ numeric_enum! {
109} 111}
110} 112}
111 113
114impl TryFrom<AddressingMode> for AddressMode {
115 type Error = ();
116
117 fn try_from(value: AddressingMode) -> Result<Self, Self::Error> {
118 match value {
119 AddressingMode::Absent => Ok(Self::NoAddress),
120 AddressingMode::Extended => Ok(Self::Extended),
121 AddressingMode::Short => Ok(Self::Short),
122 AddressingMode::Unknown(_) => Err(()),
123 }
124 }
125}
126
112#[derive(Clone, Copy)] 127#[derive(Clone, Copy)]
113pub union MacAddress { 128pub union MacAddress {
114 pub short: [u8; 2], 129 pub short: [u8; 2],
115 pub extended: [u8; 8], 130 pub extended: [u8; 8],
116} 131}
117 132
133impl From<Address> for MacAddress {
134 fn from(value: Address) -> Self {
135 match value {
136 Address::Short(addr) => Self { short: addr },
137 Address::Extended(addr) => Self { extended: addr },
138 Address::Absent => Self { short: [0u8; 2] },
139 }
140 }
141}
142
143pub struct MacAddressAndMode(pub MacAddress, pub AddressMode);
144
145impl From<MacAddressAndMode> for Address {
146 fn from(mac_address_and_mode: MacAddressAndMode) -> Self {
147 let address = mac_address_and_mode.0;
148 let mode = mac_address_and_mode.1;
149
150 match mode {
151 AddressMode::Short => Address::Short(unsafe { address.short }),
152 AddressMode::Extended => Address::Extended(unsafe { address.extended }),
153 AddressMode::NoAddress => Address::Absent,
154 AddressMode::Reserved => Address::Absent,
155 }
156 }
157}
158
118impl Debug for MacAddress { 159impl Debug for MacAddress {
119 fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 160 fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
120 unsafe { 161 unsafe {
@@ -346,7 +387,7 @@ numeric_enum! {
346 387
347numeric_enum! { 388numeric_enum! {
348 #[repr(u8)] 389 #[repr(u8)]
349 #[derive(Default, Clone, Copy, Debug)] 390 #[derive(Default, Clone, Copy, Debug, PartialEq)]
350 #[cfg_attr(feature = "defmt", derive(defmt::Format))] 391 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
351 pub enum SecurityLevel { 392 pub enum SecurityLevel {
352 /// MAC Unsecured Mode Security 393 /// MAC Unsecured Mode Security
@@ -379,3 +420,15 @@ pub struct PanId(pub [u8; 2]);
379impl PanId { 420impl PanId {
380 pub const BROADCAST: Self = Self([0xFF, 0xFF]); 421 pub const BROADCAST: Self = Self([0xFF, 0xFF]);
381} 422}
423
424impl From<Pan> for PanId {
425 fn from(value: Pan) -> Self {
426 Self(value.0.to_be_bytes())
427 }
428}
429
430impl From<PanId> for Pan {
431 fn from(value: PanId) -> Self {
432 Self(u16::from_be_bytes(value.0))
433 }
434}
diff --git a/embassy-stm32-wpan/src/sub/mac.rs b/embassy-stm32-wpan/src/sub/mac.rs
index baf4da979..93cafbc72 100644
--- a/embassy-stm32-wpan/src/sub/mac.rs
+++ b/embassy-stm32-wpan/src/sub/mac.rs
@@ -28,32 +28,39 @@ impl Mac {
28 Self { _private: () } 28 Self { _private: () }
29 } 29 }
30 30
31 /// `HW_IPCC_MAC_802_15_4_EvtNot` 31 pub const fn split(self) -> (MacRx, MacTx) {
32 /// 32 (MacRx { _private: () }, MacTx { _private: () })
33 /// This function will stall if the previous `EvtBox` has not been dropped 33 }
34 pub async fn tl_read(&self) -> EvtBox<Self> {
35 // Wait for the last event box to be dropped
36 poll_fn(|cx| {
37 MAC_WAKER.register(cx.waker());
38 if MAC_EVT_OUT.load(Ordering::SeqCst) {
39 Poll::Pending
40 } else {
41 Poll::Ready(())
42 }
43 })
44 .await;
45 34
46 // Return a new event box 35 pub async fn tl_write_and_get_response(&self, opcode: u16, payload: &[u8]) -> u8 {
47 Ipcc::receive(channels::cpu2::IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL, || unsafe { 36 MacTx { _private: () }.tl_write_and_get_response(opcode, payload).await
48 // The closure is not async, therefore the closure must execute to completion (cannot be dropped) 37 }
49 // Therefore, the event box is guaranteed to be cleaned up if it's not leaked
50 MAC_EVT_OUT.store(true, Ordering::SeqCst);
51 38
52 Some(EvtBox::new(MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr() as *mut _)) 39 pub async fn tl_write(&self, opcode: u16, payload: &[u8]) {
53 }) 40 MacTx { _private: () }.tl_write(opcode, payload).await
54 .await
55 } 41 }
56 42
43 pub async fn send_command<T>(&self, cmd: &T) -> Result<(), MacError>
44 where
45 T: MacCommand,
46 {
47 MacTx { _private: () }.send_command(cmd).await
48 }
49
50 pub async fn tl_read(&self) -> EvtBox<Mac> {
51 MacRx { _private: () }.tl_read().await
52 }
53
54 pub async fn read(&self) -> Result<MacEvent<'_>, ()> {
55 MacRx { _private: () }.read().await
56 }
57}
58
59pub struct MacTx {
60 _private: (),
61}
62
63impl MacTx {
57 /// `HW_IPCC_MAC_802_15_4_CmdEvtNot` 64 /// `HW_IPCC_MAC_802_15_4_CmdEvtNot`
58 pub async fn tl_write_and_get_response(&self, opcode: u16, payload: &[u8]) -> u8 { 65 pub async fn tl_write_and_get_response(&self, opcode: u16, payload: &[u8]) -> u8 {
59 self.tl_write(opcode, payload).await; 66 self.tl_write(opcode, payload).await;
@@ -92,6 +99,38 @@ impl Mac {
92 Err(MacError::from(response)) 99 Err(MacError::from(response))
93 } 100 }
94 } 101 }
102}
103
104pub struct MacRx {
105 _private: (),
106}
107
108impl MacRx {
109 /// `HW_IPCC_MAC_802_15_4_EvtNot`
110 ///
111 /// This function will stall if the previous `EvtBox` has not been dropped
112 pub async fn tl_read(&self) -> EvtBox<Mac> {
113 // Wait for the last event box to be dropped
114 poll_fn(|cx| {
115 MAC_WAKER.register(cx.waker());
116 if MAC_EVT_OUT.load(Ordering::SeqCst) {
117 Poll::Pending
118 } else {
119 Poll::Ready(())
120 }
121 })
122 .await;
123
124 // Return a new event box
125 Ipcc::receive(channels::cpu2::IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL, || unsafe {
126 // The closure is not async, therefore the closure must execute to completion (cannot be dropped)
127 // Therefore, the event box is guaranteed to be cleaned up if it's not leaked
128 MAC_EVT_OUT.store(true, Ordering::SeqCst);
129
130 Some(EvtBox::new(MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr() as *mut _))
131 })
132 .await
133 }
95 134
96 pub async fn read(&self) -> Result<MacEvent<'_>, ()> { 135 pub async fn read(&self) -> Result<MacEvent<'_>, ()> {
97 MacEvent::new(self.tl_read().await) 136 MacEvent::new(self.tl_read().await)
diff --git a/embassy-stm32-wpan/src/sub/mm.rs b/embassy-stm32-wpan/src/sub/mm.rs
index 62d0de8bd..a90c6ee55 100644
--- a/embassy-stm32-wpan/src/sub/mm.rs
+++ b/embassy-stm32-wpan/src/sub/mm.rs
@@ -46,7 +46,7 @@ impl MemoryManager {
46 Self { _private: () } 46 Self { _private: () }
47 } 47 }
48 48
49 pub async fn run_queue(&self) { 49 pub async fn run_queue(&self) -> ! {
50 loop { 50 loop {
51 poll_fn(|cx| unsafe { 51 poll_fn(|cx| unsafe {
52 MM_WAKER.register(cx.waker()); 52 MM_WAKER.register(cx.waker());
diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md
index d17663a61..b6caf8f65 100644
--- a/embassy-stm32/CHANGELOG.md
+++ b/embassy-stm32/CHANGELOG.md
@@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8## Unreleased - ReleaseDate 8## Unreleased - ReleaseDate
9 9
10- change: stm32/eth: ethernet no longer has a hard dependency on station management, and station management can be used independently ([#4871](https://github.com/embassy-rs/embassy/pull/4871)) 10- change: stm32/eth: ethernet no longer has a hard dependency on station management, and station management can be used independently ([#4871](https://github.com/embassy-rs/embassy/pull/4871))
11- feat: allow embassy_executor::main for low power
12- feat: Add waveform methods to ComplementaryPwm
13- fix: Avoid generating timer update events when updating the frequency ([#4890](https://github.com/embassy-rs/embassy/pull/4890))
14- chore: cleanup low-power add time
15- fix: Allow setting SAI peripheral `frame_length` to `256`
11- fix: flash erase on dual-bank STM32Gxxx 16- fix: flash erase on dual-bank STM32Gxxx
12- feat: Add support for STM32N657X0 17- feat: Add support for STM32N657X0
13- feat: timer: Add 32-bit timer support to SimplePwm waveform_up method following waveform pattern ([#4717](https://github.com/embassy-rs/embassy/pull/4717)) 18- feat: timer: Add 32-bit timer support to SimplePwm waveform_up method following waveform pattern ([#4717](https://github.com/embassy-rs/embassy/pull/4717))
@@ -39,6 +44,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
39- feat: stm32/usart: add `eager_reads` option to control if buffered readers return as soon as possible or after more data is available ([#4668](https://github.com/embassy-rs/embassy/pull/4668)) 44- feat: stm32/usart: add `eager_reads` option to control if buffered readers return as soon as possible or after more data is available ([#4668](https://github.com/embassy-rs/embassy/pull/4668))
40- feat: stm32/usart: add `de_assertion_time` and `de_deassertion_time` config options 45- feat: stm32/usart: add `de_assertion_time` and `de_deassertion_time` config options
41- change: stm32/uart: BufferedUartRx now returns all available bytes from the internal buffer 46- change: stm32/uart: BufferedUartRx now returns all available bytes from the internal buffer
47- fix: stm32/adc: Calculate the ADC prescaler in a way that it allows for the max frequency to be reached
42- fix: Prevent a HardFault crash on STM32H5 devices by changing `uid()` to return `[u8; 12]` by value instead of a reference. (Fixes #2696) 48- fix: Prevent a HardFault crash on STM32H5 devices by changing `uid()` to return `[u8; 12]` by value instead of a reference. (Fixes #2696)
43- change: timer: added output compare values 49- change: timer: added output compare values
44- feat: timer: add ability to set master mode 50- feat: timer: add ability to set master mode
@@ -59,6 +65,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
59- adc: reogranize and cleanup somewhat. require sample_time to be passed on conversion 65- adc: reogranize and cleanup somewhat. require sample_time to be passed on conversion
60- fix: stm32/i2c v2 slave: prevent misaligned reads, error false positives, and incorrect counts of bytes read/written 66- fix: stm32/i2c v2 slave: prevent misaligned reads, error false positives, and incorrect counts of bytes read/written
61- feat: add flash support for c0 family ([#4874](https://github.com/embassy-rs/embassy/pull/4874)) 67- feat: add flash support for c0 family ([#4874](https://github.com/embassy-rs/embassy/pull/4874))
68- fix: fixing channel numbers on vbat and vddcore for adc on adc
69- adc: adding disable to vbat
62 70
63## 0.4.0 - 2025-08-26 71## 0.4.0 - 2025-08-26
64 72
diff --git a/embassy-stm32/src/adc/adc4.rs b/embassy-stm32/src/adc/adc4.rs
index babdebfdb..499fc2093 100644
--- a/embassy-stm32/src/adc/adc4.rs
+++ b/embassy-stm32/src/adc/adc4.rs
@@ -77,7 +77,7 @@ pub const fn resolution_to_max_count(res: Resolution) -> u32 {
77} 77}
78 78
79fn from_ker_ck(frequency: Hertz) -> Presc { 79fn from_ker_ck(frequency: Hertz) -> Presc {
80 let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; 80 let raw_prescaler = rcc::raw_prescaler(frequency.0, MAX_ADC_CLK_FREQ.0);
81 match raw_prescaler { 81 match raw_prescaler {
82 0 => Presc::DIV1, 82 0 => Presc::DIV1,
83 1 => Presc::DIV2, 83 1 => Presc::DIV2,
diff --git a/embassy-stm32/src/adc/c0.rs b/embassy-stm32/src/adc/c0.rs
index 3bdca7edb..3e109e429 100644
--- a/embassy-stm32/src/adc/c0.rs
+++ b/embassy-stm32/src/adc/c0.rs
@@ -1,7 +1,7 @@
1#[allow(unused)] 1#[allow(unused)]
2use pac::adc::vals::{Adstp, Align, Ckmode, Dmacfg, Exten, Ovrmod, Ovsr}; 2use pac::adc::vals::{Adstp, Align, Ckmode, Dmacfg, Exten, Ovrmod, Ovsr};
3use pac::adccommon::vals::Presc; 3use pac::adccommon::vals::Presc;
4use stm32_metapac::adc::vals::Scandir; 4use stm32_metapac::adc::vals::{SampleTime, Scandir};
5 5
6use super::{Adc, Instance, Resolution, blocking_delay_us}; 6use super::{Adc, Instance, Resolution, blocking_delay_us};
7use crate::adc::{AnyInstance, ConversionMode}; 7use crate::adc::{AnyInstance, ConversionMode};
@@ -17,7 +17,6 @@ const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(25);
17 17
18const TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US: u32 = 20; 18const TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US: u32 = 20;
19 19
20const NUM_HW_CHANNELS: u8 = 22;
21const CHSELR_SQ_SIZE: usize = 8; 20const CHSELR_SQ_SIZE: usize = 8;
22const CHSELR_SQ_MAX_CHANNEL: u8 = 14; 21const CHSELR_SQ_MAX_CHANNEL: u8 = 14;
23const CHSELR_SQ_SEQUENCE_END_MARKER: u8 = 0b1111; 22const CHSELR_SQ_SEQUENCE_END_MARKER: u8 = 0b1111;
@@ -31,7 +30,7 @@ impl<T: Instance> super::SealedSpecialConverter<super::Temperature> for T {
31} 30}
32 31
33fn from_ker_ck(frequency: Hertz) -> Presc { 32fn from_ker_ck(frequency: Hertz) -> Presc {
34 let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; 33 let raw_prescaler = rcc::raw_prescaler(frequency.0, MAX_ADC_CLK_FREQ.0);
35 match raw_prescaler { 34 match raw_prescaler {
36 0 => Presc::DIV1, 35 0 => Presc::DIV1,
37 1 => Presc::DIV2, 36 1 => Presc::DIV2,
@@ -100,82 +99,67 @@ impl<T: Instance> super::SealedAnyInstance for T {
100 } 99 }
101 } 100 }
102 101
103 fn configure_sequence(mut sequence: impl ExactSizeIterator<Item = ((u8, bool), Self::SampleTime)>, blocking: bool) { 102 fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), Self::SampleTime)>) {
104 T::regs().cfgr1().modify(|reg| { 103 let mut needs_hw = sequence.len() == 1 || sequence.len() > CHSELR_SQ_SIZE;
105 reg.set_chselrmod(!blocking); 104 let mut is_ordered_up = true;
106 reg.set_align(Align::RIGHT); 105 let mut is_ordered_down = true;
107 });
108 106
109 assert!(!blocking || sequence.len() == 1, "Sequence len must be 1 for blocking."); 107 let sequence_len = sequence.len();
110 if blocking { 108 let mut hw_channel_selection: u32 = 0;
111 let ((ch, _), sample_time) = sequence.next().unwrap(); 109 let mut last_channel: u8 = 0;
112 // Set all channels to use SMP1 field as source. 110 let mut sample_time: Self::SampleTime = SampleTime::CYCLES2_5;
113 T::regs().smpr().modify(|w| { 111
114 w.smpsel(0); 112 T::regs().chselr_sq().write(|w| {
115 w.set_smp1(sample_time); 113 for (i, ((channel, _), _sample_time)) in sequence.enumerate() {
116 }); 114 assert!(
115 sample_time == _sample_time || i == 0,
116 "C0 only supports one sample time for the sequence."
117 );
117 118
118 // write() because we want all other bits to be set to 0. 119 sample_time = _sample_time;
119 T::regs().chselr().write(|w| w.set_chsel(ch.into(), true)); 120 needs_hw = needs_hw || channel > CHSELR_SQ_MAX_CHANNEL;
120 } else { 121 is_ordered_up = is_ordered_up && (channel > last_channel || i == 0);
121 let mut hw_channel_selection: u32 = 0; 122 is_ordered_down = is_ordered_down && (channel < last_channel || i == 0);
122 let mut is_ordered_up = true; 123 hw_channel_selection += 1 << channel;
123 let mut is_ordered_down = true; 124 last_channel = channel;
124 let mut needs_hw = false;
125 125
126 if !needs_hw {
127 w.set_sq(i, channel);
128 }
129 }
130
131 for i in sequence_len..CHSELR_SQ_SIZE {
132 w.set_sq(i, CHSELR_SQ_SEQUENCE_END_MARKER);
133 }
134 });
135
136 if needs_hw {
126 assert!( 137 assert!(
127 sequence.len() <= CHSELR_SQ_SIZE, 138 sequence_len <= CHSELR_SQ_SIZE || is_ordered_up || is_ordered_down,
128 "Sequence read set cannot be more than {} in size.", 139 "Sequencer is required because of unordered channels, but read set cannot be more than {} in size.",
129 CHSELR_SQ_SIZE 140 CHSELR_SQ_SIZE
130 ); 141 );
131 let mut last_sq_set: usize = 0; 142 assert!(
132 let mut last_channel: u8 = 0; 143 sequence_len > CHSELR_SQ_SIZE || is_ordered_up || is_ordered_down,
133 T::regs().chselr_sq().write(|w| { 144 "Sequencer is required because of unordered channels, but only support HW channels smaller than {}.",
134 for (i, ((channel, _), _sample_time)) in sequence.enumerate() { 145 CHSELR_SQ_MAX_CHANNEL
135 needs_hw = needs_hw || channel > CHSELR_SQ_MAX_CHANNEL; 146 );
136 last_sq_set = i;
137 is_ordered_up = is_ordered_up && channel > last_channel;
138 is_ordered_down = is_ordered_down && channel < last_channel;
139 hw_channel_selection += 1 << channel;
140 last_channel = channel;
141
142 if !needs_hw {
143 w.set_sq(i, channel);
144 }
145 }
146
147 assert!(
148 !needs_hw || is_ordered_up || is_ordered_down,
149 "Sequencer is required because of unordered channels, but only support HW channels smaller than {}.",
150 CHSELR_SQ_MAX_CHANNEL
151 );
152 147
153 if needs_hw { 148 // Set required channels for multi-convert.
154 assert!( 149 unsafe { (T::regs().chselr().as_ptr() as *mut u32).write_volatile(hw_channel_selection) }
155 hw_channel_selection != 0,
156 "Some bits in `hw_channel_selection` shall be set."
157 );
158 assert!(
159 (hw_channel_selection >> NUM_HW_CHANNELS) == 0,
160 "STM32C0 only have {} ADC channels. `hw_channel_selection` cannot have bits higher than this number set.",
161 NUM_HW_CHANNELS
162 );
163
164 T::regs().cfgr1().modify(|reg| {
165 reg.set_chselrmod(false);
166 reg.set_scandir(if is_ordered_up { Scandir::UP} else { Scandir::BACK });
167 });
168
169 // Set required channels for multi-convert.
170 unsafe { (T::regs().chselr().as_ptr() as *mut u32).write_volatile(hw_channel_selection) }
171 } else {
172 for i in (last_sq_set + 1)..CHSELR_SQ_SIZE {
173 w.set_sq(i, CHSELR_SQ_SEQUENCE_END_MARKER);
174 }
175 }
176 });
177 } 150 }
178 151
152 T::regs().smpr().modify(|w| {
153 w.smpsel(0);
154 w.set_smp1(sample_time);
155 });
156
157 T::regs().cfgr1().modify(|reg| {
158 reg.set_chselrmod(!needs_hw);
159 reg.set_align(Align::RIGHT);
160 reg.set_scandir(if is_ordered_up { Scandir::UP } else { Scandir::BACK });
161 });
162
179 // Trigger and wait for the channel selection procedure to complete. 163 // Trigger and wait for the channel selection procedure to complete.
180 T::regs().isr().modify(|w| w.set_ccrdy(false)); 164 T::regs().isr().modify(|w| w.set_ccrdy(false));
181 while !T::regs().isr().read().ccrdy() {} 165 while !T::regs().isr().read().ccrdy() {}
diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs
index 514734017..1767a3bb3 100644
--- a/embassy-stm32/src/adc/g4.rs
+++ b/embassy-stm32/src/adc/g4.rs
@@ -1,12 +1,12 @@
1#[cfg(stm32g4)]
2use pac::adc::regs::Difsel as DifselReg;
3#[allow(unused)]
4#[cfg(stm32g4)]
5pub use pac::adc::vals::{Adcaldif, Adstp, Difsel, Dmacfg, Dmaen, Exten, Rovsm, Trovs};
1#[allow(unused)] 6#[allow(unused)]
2#[cfg(stm32h7)] 7#[cfg(stm32h7)]
3use pac::adc::vals::{Adcaldif, Difsel, Exten}; 8use pac::adc::vals::{Adcaldif, Difsel, Exten};
4#[allow(unused)] 9pub use pac::adccommon::vals::{Dual, Presc};
5#[cfg(stm32g4)]
6pub use pac::adc::vals::{Adcaldif, Difsel, Exten, Rovsm, Trovs};
7pub use pac::adccommon::vals::Presc;
8pub use stm32_metapac::adc::vals::{Adstp, Dmacfg, Dmaen};
9pub use stm32_metapac::adccommon::vals::Dual;
10 10
11use super::{ 11use super::{
12 Adc, AnyAdcChannel, ConversionMode, Instance, RegularConversionMode, Resolution, RxDma, SampleTime, 12 Adc, AnyAdcChannel, ConversionMode, Instance, RegularConversionMode, Resolution, RxDma, SampleTime,
@@ -33,7 +33,7 @@ const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60);
33const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50); 33const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50);
34 34
35fn from_ker_ck(frequency: Hertz) -> Presc { 35fn from_ker_ck(frequency: Hertz) -> Presc {
36 let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; 36 let raw_prescaler = rcc::raw_prescaler(frequency.0, MAX_ADC_CLK_FREQ.0);
37 match raw_prescaler { 37 match raw_prescaler {
38 0 => Presc::DIV1, 38 0 => Presc::DIV1,
39 1 => Presc::DIV2, 39 1 => Presc::DIV2,
@@ -174,50 +174,51 @@ impl<T: Instance> super::SealedAnyInstance for T {
174 } 174 }
175 175
176 fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { 176 fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) {
177 T::regs().cr().modify(|w| w.set_aden(false));
178
177 // Set sequence length 179 // Set sequence length
178 T::regs().sqr1().modify(|w| { 180 T::regs().sqr1().modify(|w| {
179 w.set_l(sequence.len() as u8 - 1); 181 w.set_l(sequence.len() as u8 - 1);
180 }); 182 });
181 183
184 #[cfg(stm32g4)]
185 let mut difsel = DifselReg::default();
186 let mut smpr = T::regs().smpr().read();
187 let mut smpr2 = T::regs().smpr2().read();
188 let mut sqr1 = T::regs().sqr1().read();
189 let mut sqr2 = T::regs().sqr2().read();
190 let mut sqr3 = T::regs().sqr3().read();
191 let mut sqr4 = T::regs().sqr4().read();
192
182 // Configure channels and ranks 193 // Configure channels and ranks
183 for (_i, ((ch, is_differential), sample_time)) in sequence.enumerate() { 194 for (_i, ((ch, is_differential), sample_time)) in sequence.enumerate() {
184 let sample_time = sample_time.into(); 195 let sample_time = sample_time.into();
185 if ch <= 9 { 196 if ch <= 9 {
186 T::regs().smpr().modify(|reg| reg.set_smp(ch as _, sample_time)); 197 smpr.set_smp(ch as _, sample_time);
187 } else { 198 } else {
188 T::regs().smpr2().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); 199 smpr2.set_smp((ch - 10) as _, sample_time);
189 } 200 }
190 201
191 match _i { 202 match _i {
192 0..=3 => { 203 0..=3 => {
193 T::regs().sqr1().modify(|w| { 204 sqr1.set_sq(_i, ch);
194 w.set_sq(_i, ch);
195 });
196 } 205 }
197 4..=8 => { 206 4..=8 => {
198 T::regs().sqr2().modify(|w| { 207 sqr2.set_sq(_i - 4, ch);
199 w.set_sq(_i - 4, ch);
200 });
201 } 208 }
202 9..=13 => { 209 9..=13 => {
203 T::regs().sqr3().modify(|w| { 210 sqr3.set_sq(_i - 9, ch);
204 w.set_sq(_i - 9, ch);
205 });
206 } 211 }
207 14..=15 => { 212 14..=15 => {
208 T::regs().sqr4().modify(|w| { 213 sqr4.set_sq(_i - 14, ch);
209 w.set_sq(_i - 14, ch);
210 });
211 } 214 }
212 _ => unreachable!(), 215 _ => unreachable!(),
213 } 216 }
214 217
215 #[cfg(stm32g4)] 218 #[cfg(stm32g4)]
216 { 219 {
217 T::regs().cr().modify(|w| w.set_aden(false)); // disable adc 220 if ch < 18 {
218 221 difsel.set_difsel(
219 T::regs().difsel().modify(|w| {
220 w.set_difsel(
221 ch.into(), 222 ch.into(),
222 if is_differential { 223 if is_differential {
223 Difsel::DIFFERENTIAL 224 Difsel::DIFFERENTIAL
@@ -225,11 +226,18 @@ impl<T: Instance> super::SealedAnyInstance for T {
225 Difsel::SINGLE_ENDED 226 Difsel::SINGLE_ENDED
226 }, 227 },
227 ); 228 );
228 }); 229 }
229
230 T::regs().cr().modify(|w| w.set_aden(true)); // enable adc
231 } 230 }
232 } 231 }
232
233 T::regs().smpr().write_value(smpr);
234 T::regs().smpr2().write_value(smpr2);
235 T::regs().sqr1().write_value(sqr1);
236 T::regs().sqr2().write_value(sqr2);
237 T::regs().sqr3().write_value(sqr3);
238 T::regs().sqr4().write_value(sqr4);
239 #[cfg(stm32g4)]
240 T::regs().difsel().write_value(difsel);
233 } 241 }
234} 242}
235 243
@@ -412,7 +420,6 @@ impl<'d, T: Instance + AnyInstance> Adc<'d, T> {
412 NR_INJECTED_RANKS 420 NR_INJECTED_RANKS
413 ); 421 );
414 422
415 T::stop();
416 T::enable(); 423 T::enable();
417 424
418 T::regs().jsqr().modify(|w| w.set_jl(N as u8 - 1)); 425 T::regs().jsqr().modify(|w| w.set_jl(N as u8 - 1));
diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs
index 5ec08a22d..74648cc21 100644
--- a/embassy-stm32/src/adc/mod.rs
+++ b/embassy-stm32/src/adc/mod.rs
@@ -111,10 +111,7 @@ pub(self) trait SealedAnyInstance: BasicAnyInstance {
111 fn stop(); 111 fn stop();
112 fn convert() -> u16; 112 fn convert() -> u16;
113 fn configure_dma(conversion_mode: ConversionMode); 113 fn configure_dma(conversion_mode: ConversionMode);
114 #[cfg(not(adc_c0))]
115 fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), Self::SampleTime)>); 114 fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), Self::SampleTime)>);
116 #[cfg(adc_c0)]
117 fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), Self::SampleTime)>, blocking: bool);
118 #[allow(dead_code)] 115 #[allow(dead_code)]
119 fn dr() -> *mut u16; 116 fn dr() -> *mut u16;
120} 117}
@@ -195,15 +192,15 @@ impl<'d, T: AnyInstance> Adc<'d, T> {
195 #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] 192 #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))]
196 channel.setup(); 193 channel.setup();
197 194
198 #[cfg(not(adc_v4))] 195 #[cfg(any(adc_v2, adc_v3, adc_g0, adc_h7rs, adc_u0, adc_u5, adc_wba, adc_c0))]
199 T::enable(); 196 T::enable();
200 #[cfg(not(adc_c0))]
201 T::configure_sequence([((channel.channel(), channel.is_differential()), sample_time)].into_iter()); 197 T::configure_sequence([((channel.channel(), channel.is_differential()), sample_time)].into_iter());
202 #[cfg(adc_c0)] 198
203 T::configure_sequence( 199 // On chips with differential channels, enable after configure_sequence to allow setting differential channels
204 [((channel.channel(), channel.is_differential()), sample_time)].into_iter(), 200 //
205 true, 201 // TODO: If hardware allows, enable after configure_sequence on all chips
206 ); 202 #[cfg(any(adc_g4, adc_h5))]
203 T::enable();
207 204
208 T::convert() 205 T::convert()
209 } 206 }
@@ -238,10 +235,10 @@ impl<'d, T: AnyInstance> Adc<'d, T> {
238 /// Note: This is not very efficient as the ADC needs to be reconfigured for each read. Use 235 /// Note: This is not very efficient as the ADC needs to be reconfigured for each read. Use
239 /// `into_ring_buffered`, `into_ring_buffered_and_injected` 236 /// `into_ring_buffered`, `into_ring_buffered_and_injected`
240 /// 237 ///
241 /// In STM32C0, channels bigger than 14 cannot be read using sequencer, so you have to use 238 /// Note: Depending on hardware limitations, this method may require channels to be passed
242 /// either blocking reads or use the mechanism to read in HW order (CHSELRMOD=0). 239 /// in order or require the sequence to have the same sample time for all channnels, depending
243 /// 240 /// on the number and properties of the channels in the sequence. This method will panic if
244 /// In addtion, on STM320, this method will panic if the channels are not passed in order 241 /// the hardware cannot deliver the requested configuration.
245 pub async fn read( 242 pub async fn read(
246 &mut self, 243 &mut self,
247 rx_dma: embassy_hal_internal::Peri<'_, impl RxDma<T>>, 244 rx_dma: embassy_hal_internal::Peri<'_, impl RxDma<T>>,
@@ -258,21 +255,20 @@ impl<'d, T: AnyInstance> Adc<'d, T> {
258 "Asynchronous read sequence cannot be more than 16 in length" 255 "Asynchronous read sequence cannot be more than 16 in length"
259 ); 256 );
260 257
261 // Ensure no conversions are ongoing and ADC is enabled. 258 // Ensure no conversions are ongoing
262 T::stop(); 259 T::stop();
260 #[cfg(any(adc_g0, adc_v3, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))]
263 T::enable(); 261 T::enable();
264 262
265 #[cfg(not(adc_c0))]
266 T::configure_sequence(
267 sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)),
268 );
269
270 #[cfg(adc_c0)]
271 T::configure_sequence( 263 T::configure_sequence(
272 sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), 264 sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)),
273 false,
274 ); 265 );
275 266
267 // On chips with differential channels, enable after configure_sequence to allow setting differential channels
268 //
269 // TODO: If hardware allows, enable after configure_sequence on all chips
270 #[cfg(any(adc_g4, adc_h5))]
271 T::enable();
276 T::configure_dma(ConversionMode::Singular); 272 T::configure_dma(ConversionMode::Singular);
277 273
278 let request = rx_dma.request(); 274 let request = rx_dma.request();
@@ -310,6 +306,11 @@ impl<'d, T: AnyInstance> Adc<'d, T> {
310 /// 306 ///
311 /// # Returns 307 /// # Returns
312 /// A `RingBufferedAdc<'a, T>` instance configured for continuous DMA-based sampling. 308 /// A `RingBufferedAdc<'a, T>` instance configured for continuous DMA-based sampling.
309 ///
310 /// Note: Depending on hardware limitations, this method may require channels to be passed
311 /// in order or require the sequence to have the same sample time for all channnels, depending
312 /// on the number and properties of the channels in the sequence. This method will panic if
313 /// the hardware cannot deliver the requested configuration.
313 pub fn into_ring_buffered<'a>( 314 pub fn into_ring_buffered<'a>(
314 self, 315 self,
315 dma: embassy_hal_internal::Peri<'a, impl RxDma<T>>, 316 dma: embassy_hal_internal::Peri<'a, impl RxDma<T>>,
@@ -323,15 +324,20 @@ impl<'d, T: AnyInstance> Adc<'d, T> {
323 sequence.len() <= 16, 324 sequence.len() <= 16,
324 "Asynchronous read sequence cannot be more than 16 in length" 325 "Asynchronous read sequence cannot be more than 16 in length"
325 ); 326 );
326 // reset conversions and enable the adc 327 // Ensure no conversions are ongoing
327 T::stop(); 328 T::stop();
329 #[cfg(any(adc_g0, adc_v3, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))]
328 T::enable(); 330 T::enable();
329 331
330 //adc side setup
331 T::configure_sequence( 332 T::configure_sequence(
332 sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), 333 sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)),
333 ); 334 );
334 335
336 // On chips with differential channels, enable after configure_sequence to allow setting differential channels
337 //
338 // TODO: If hardware allows, enable after configure_sequence on all chips
339 #[cfg(any(adc_g4, adc_h5))]
340 T::enable();
335 T::configure_dma(ConversionMode::Repeated(mode)); 341 T::configure_dma(ConversionMode::Repeated(mode));
336 342
337 core::mem::forget(self); 343 core::mem::forget(self);
diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs
index 07eaebf7c..341b15674 100644
--- a/embassy-stm32/src/adc/v2.rs
+++ b/embassy-stm32/src/adc/v2.rs
@@ -58,7 +58,7 @@ fn from_pclk2(freq: Hertz) -> Adcpre {
58 // Datasheet for both F4 and F7 specifies min frequency 0.6 MHz, typ freq. 30 MHz and max 36 MHz. 58 // Datasheet for both F4 and F7 specifies min frequency 0.6 MHz, typ freq. 30 MHz and max 36 MHz.
59 #[cfg(not(stm32f2))] 59 #[cfg(not(stm32f2))]
60 const MAX_FREQUENCY: Hertz = Hertz(36_000_000); 60 const MAX_FREQUENCY: Hertz = Hertz(36_000_000);
61 let raw_div = freq.0 / MAX_FREQUENCY.0; 61 let raw_div = rcc::raw_prescaler(freq.0, MAX_FREQUENCY.0);
62 match raw_div { 62 match raw_div {
63 0..=1 => Adcpre::DIV2, 63 0..=1 => Adcpre::DIV2,
64 2..=3 => Adcpre::DIV4, 64 2..=3 => Adcpre::DIV4,
diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs
index 78b497727..b270588c4 100644
--- a/embassy-stm32/src/adc/v3.rs
+++ b/embassy-stm32/src/adc/v3.rs
@@ -65,7 +65,7 @@ impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T {
65} 65}
66#[cfg(any(adc_h5, adc_h7rs))] 66#[cfg(any(adc_h5, adc_h7rs))]
67impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { 67impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T {
68 const CHANNEL: u8 = 2; 68 const CHANNEL: u8 = 16;
69} 69}
70#[cfg(adc_u0)] 70#[cfg(adc_u0)]
71impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T { 71impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T {
@@ -82,7 +82,7 @@ cfg_if! {
82 impl<T: Instance> super::AdcChannel<T> for VddCore {} 82 impl<T: Instance> super::AdcChannel<T> for VddCore {}
83 impl<T: Instance> super::SealedAdcChannel<T> for VddCore { 83 impl<T: Instance> super::SealedAdcChannel<T> for VddCore {
84 fn channel(&self) -> u8 { 84 fn channel(&self) -> u8 {
85 6 85 17
86 } 86 }
87 } 87 }
88 } 88 }
@@ -174,38 +174,31 @@ impl<T: Instance> super::SealedAnyInstance for T {
174 } 174 }
175 175
176 fn start() { 176 fn start() {
177 #[cfg(any(adc_v3, adc_g0, adc_u0))] 177 T::regs().cr().modify(|reg| {
178 { 178 reg.set_adstart(true);
179 // Start adc conversion 179 });
180 T::regs().cr().modify(|reg| {
181 reg.set_adstart(true);
182 });
183 }
184 } 180 }
185 181
186 fn stop() { 182 fn stop() {
187 #[cfg(any(adc_v3, adc_g0, adc_u0))] 183 // Ensure conversions are finished.
188 { 184 if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() {
189 // Ensure conversions are finished. 185 T::regs().cr().modify(|reg| {
190 if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { 186 reg.set_adstp(true);
191 T::regs().cr().modify(|reg| {
192 reg.set_adstp(true);
193 });
194 while T::regs().cr().read().adstart() {}
195 }
196
197 // Reset configuration.
198 #[cfg(not(any(adc_g0, adc_u0)))]
199 T::regs().cfgr().modify(|reg| {
200 reg.set_cont(false);
201 reg.set_dmaen(false);
202 });
203 #[cfg(any(adc_g0, adc_u0))]
204 T::regs().cfgr1().modify(|reg| {
205 reg.set_cont(false);
206 reg.set_dmaen(false);
207 }); 187 });
188 while T::regs().cr().read().adstart() {}
208 } 189 }
190
191 // Reset configuration.
192 #[cfg(not(any(adc_g0, adc_u0)))]
193 T::regs().cfgr().modify(|reg| {
194 reg.set_cont(false);
195 reg.set_dmaen(false);
196 });
197 #[cfg(any(adc_g0, adc_u0))]
198 T::regs().cfgr1().modify(|reg| {
199 reg.set_cont(false);
200 reg.set_dmaen(false);
201 });
209 } 202 }
210 203
211 /// Perform a single conversion. 204 /// Perform a single conversion.
@@ -267,6 +260,9 @@ impl<T: Instance> super::SealedAnyInstance for T {
267 } 260 }
268 261
269 fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) { 262 fn configure_sequence(sequence: impl ExactSizeIterator<Item = ((u8, bool), SampleTime)>) {
263 #[cfg(adc_h5)]
264 T::regs().cr().modify(|w| w.set_aden(false));
265
270 // Set sequence length 266 // Set sequence length
271 #[cfg(not(any(adc_g0, adc_u0)))] 267 #[cfg(not(any(adc_g0, adc_u0)))]
272 T::regs().sqr1().modify(|w| { 268 T::regs().sqr1().modify(|w| {
@@ -301,8 +297,11 @@ impl<T: Instance> super::SealedAnyInstance for T {
301 #[cfg(adc_u0)] 297 #[cfg(adc_u0)]
302 let mut channel_mask = 0; 298 let mut channel_mask = 0;
303 299
300 #[cfg(adc_h5)]
301 let mut difsel = 0u32;
302
304 // Configure channels and ranks 303 // Configure channels and ranks
305 for (_i, ((channel, _), sample_time)) in sequence.enumerate() { 304 for (_i, ((channel, _is_differential), sample_time)) in sequence.enumerate() {
306 // RM0492, RM0481, etc. 305 // RM0492, RM0481, etc.
307 // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." 306 // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected."
308 #[cfg(any(adc_h5, adc_h7rs))] 307 #[cfg(any(adc_h5, adc_h7rs))]
@@ -364,12 +363,20 @@ impl<T: Instance> super::SealedAnyInstance for T {
364 _ => unreachable!(), 363 _ => unreachable!(),
365 } 364 }
366 365
366 #[cfg(adc_h5)]
367 {
368 difsel |= (_is_differential as u32) << channel;
369 }
370
367 #[cfg(adc_u0)] 371 #[cfg(adc_u0)]
368 { 372 {
369 channel_mask |= 1 << channel; 373 channel_mask |= 1 << channel;
370 } 374 }
371 } 375 }
372 376
377 #[cfg(adc_h5)]
378 T::regs().difsel().write(|w| w.set_difsel(difsel));
379
373 // On G0 and U0 enabled channels are sampled from 0 to last channel. 380 // On G0 and U0 enabled channels are sampled from 0 to last channel.
374 // It is possible to add up to 8 sequences if CHSELRMOD = 1. 381 // It is possible to add up to 8 sequences if CHSELRMOD = 1.
375 // However for supporting more than 8 channels alternative CHSELRMOD = 0 approach is used. 382 // However for supporting more than 8 channels alternative CHSELRMOD = 0 approach is used.
@@ -582,6 +589,24 @@ impl<'d, T: Instance> Adc<'d, T> {
582 Vbat {} 589 Vbat {}
583 } 590 }
584 591
592 pub fn disable_vbat(&self) {
593 cfg_if! {
594 if #[cfg(any(adc_g0, adc_u0))] {
595 T::regs().ccr().modify(|reg| {
596 reg.set_vbaten(false);
597 });
598 } else if #[cfg(any(adc_h5, adc_h7rs))] {
599 T::common_regs().ccr().modify(|reg| {
600 reg.set_vbaten(false);
601 });
602 } else {
603 T::common_regs().ccr().modify(|reg| {
604 reg.set_ch18sel(false);
605 });
606 }
607 }
608 }
609
585 /* 610 /*
586 /// Convert a raw sample from the `Temperature` to deg C 611 /// Convert a raw sample from the `Temperature` to deg C
587 pub fn to_degrees_centigrade(sample: u16) -> f32 { 612 pub fn to_degrees_centigrade(sample: u16) -> f32 {
diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs
index 804e63db6..a3d9e6176 100644
--- a/embassy-stm32/src/adc/v4.rs
+++ b/embassy-stm32/src/adc/v4.rs
@@ -60,7 +60,7 @@ impl<T: Instance> super::SealedSpecialConverter<super::Vbat> for T {
60} 60}
61 61
62fn from_ker_ck(frequency: Hertz) -> Presc { 62fn from_ker_ck(frequency: Hertz) -> Presc {
63 let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; 63 let raw_prescaler = rcc::raw_prescaler(frequency.0, MAX_ADC_CLK_FREQ.0);
64 match raw_prescaler { 64 match raw_prescaler {
65 0 => Presc::DIV1, 65 0 => Presc::DIV1,
66 1 => Presc::DIV2, 66 1 => Presc::DIV2,
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index 6e492946a..7c3770643 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -649,10 +649,7 @@ fn init_hw(config: Config) -> Peripherals {
649 rcc::init_rcc(cs, config.rcc); 649 rcc::init_rcc(cs, config.rcc);
650 650
651 #[cfg(feature = "low-power")] 651 #[cfg(feature = "low-power")]
652 crate::rtc::init_rtc(cs, config.rtc); 652 rtc::init_rtc(cs, config.rtc, config.min_stop_pause);
653
654 #[cfg(feature = "low-power")]
655 crate::time_driver::get_driver().set_min_stop_pause(cs, config.min_stop_pause);
656 } 653 }
657 654
658 p 655 p
diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs
index 696dfe83f..cf8f2b393 100644
--- a/embassy-stm32/src/low_power.rs
+++ b/embassy-stm32/src/low_power.rs
@@ -14,7 +14,7 @@
14//! 14//!
15//! Since entering and leaving low-power modes typically incurs a significant latency, the 15//! Since entering and leaving low-power modes typically incurs a significant latency, the
16//! low-power executor will only attempt to enter when the next timer event is at least 16//! low-power executor will only attempt to enter when the next timer event is at least
17//! [`time_driver::MIN_STOP_PAUSE`] in the future. 17//! [`time_driver::min_stop_pause`] in the future.
18//! 18//!
19//! Currently there is no macro analogous to `embassy_executor::main` for this executor; 19//! Currently there is no macro analogous to `embassy_executor::main` for this executor;
20//! consequently one must define their entrypoint manually. Moreover, you must relinquish control 20//! consequently one must define their entrypoint manually. Moreover, you must relinquish control
@@ -22,21 +22,16 @@
22//! 22//!
23//! ```rust,no_run 23//! ```rust,no_run
24//! use embassy_executor::Spawner; 24//! use embassy_executor::Spawner;
25//! use embassy_stm32::low_power::Executor; 25//! use embassy_stm32::low_power;
26//! use embassy_stm32::rtc::{Rtc, RtcConfig}; 26//! use embassy_stm32::rtc::{Rtc, RtcConfig};
27//! use static_cell::StaticCell; 27//! use embassy_time::Duration;
28//! 28//!
29//! #[cortex_m_rt::entry] 29//! #[embassy_executor::main(executor = "low_power::Executor")]
30//! fn main() -> ! {
31//! Executor::take().run(|spawner| {
32//! spawner.spawn(unwrap!(async_main(spawner)));
33//! });
34//! }
35//!
36//! #[embassy_executor::task]
37//! async fn async_main(spawner: Spawner) { 30//! async fn async_main(spawner: Spawner) {
38//! // initialize the platform... 31//! // initialize the platform...
39//! let mut config = embassy_stm32::Config::default(); 32//! let mut config = embassy_stm32::Config::default();
33//! // the default value, but can be adjusted
34//! config.min_stop_pause = Duration::from_millis(250);
40//! // when enabled the power-consumption is much higher during stop, but debugging and RTT is working 35//! // when enabled the power-consumption is much higher during stop, but debugging and RTT is working
41//! config.enable_debug_during_sleep = false; 36//! config.enable_debug_during_sleep = false;
42//! let p = embassy_stm32::init(config); 37//! let p = embassy_stm32::init(config);
@@ -45,11 +40,9 @@
45//! } 40//! }
46//! ``` 41//! ```
47 42
48// TODO: Usage of `static mut` here is unsound. Fix then remove this `allow`.`
49#![allow(static_mut_refs)]
50
51use core::arch::asm; 43use core::arch::asm;
52use core::marker::PhantomData; 44use core::marker::PhantomData;
45use core::mem;
53use core::sync::atomic::{Ordering, compiler_fence}; 46use core::sync::atomic::{Ordering, compiler_fence};
54 47
55use cortex_m::peripheral::SCB; 48use cortex_m::peripheral::SCB;
@@ -57,11 +50,12 @@ use critical_section::CriticalSection;
57use embassy_executor::*; 50use embassy_executor::*;
58 51
59use crate::interrupt; 52use crate::interrupt;
53use crate::rcc::{REFCOUNT_STOP1, REFCOUNT_STOP2};
60use crate::time_driver::get_driver; 54use crate::time_driver::get_driver;
61 55
62const THREAD_PENDER: usize = usize::MAX; 56const THREAD_PENDER: usize = usize::MAX;
63 57
64static mut EXECUTOR: Option<Executor> = None; 58static mut EXECUTOR_TAKEN: bool = false;
65 59
66/// Prevent the device from going into the stop mode if held 60/// Prevent the device from going into the stop mode if held
67pub struct DeviceBusy(StopMode); 61pub struct DeviceBusy(StopMode);
@@ -182,42 +176,47 @@ impl Into<Lpms> for StopMode {
182pub struct Executor { 176pub struct Executor {
183 inner: raw::Executor, 177 inner: raw::Executor,
184 not_send: PhantomData<*mut ()>, 178 not_send: PhantomData<*mut ()>,
185 scb: SCB,
186} 179}
187 180
188impl Executor { 181impl Executor {
189 /// Create a new Executor. 182 /// Create a new Executor.
190 pub fn take() -> &'static mut Self { 183 pub fn new() -> Self {
191 critical_section::with(|_| unsafe { 184 unsafe {
192 assert!(EXECUTOR.is_none()); 185 if EXECUTOR_TAKEN {
193 186 panic!("Low power executor can only be taken once.");
194 EXECUTOR = Some(Self { 187 } else {
195 inner: raw::Executor::new(THREAD_PENDER as *mut ()), 188 EXECUTOR_TAKEN = true;
196 not_send: PhantomData, 189 }
197 scb: cortex_m::Peripherals::steal().SCB, 190 }
198 });
199
200 let executor = EXECUTOR.as_mut().unwrap();
201 191
202 executor 192 Self {
203 }) 193 inner: raw::Executor::new(THREAD_PENDER as *mut ()),
194 not_send: PhantomData,
195 }
204 } 196 }
205 197
206 pub(crate) unsafe fn on_wakeup_irq() { 198 pub(crate) unsafe fn on_wakeup_irq() {
207 critical_section::with(|cs| { 199 critical_section::with(|cs| {
208 #[cfg(stm32wlex)] 200 #[cfg(stm32wlex)]
209 { 201 {
210 let extscr = crate::pac::PWR.extscr().read(); 202 use crate::pac::rcc::vals::Sw;
203 use crate::pac::{PWR, RCC};
204 use crate::rcc::{RCC_CONFIG, init as init_rcc};
205
206 let extscr = PWR.extscr().read();
211 if extscr.c1stop2f() || extscr.c1stopf() { 207 if extscr.c1stop2f() || extscr.c1stopf() {
212 // when we wake from any stop mode we need to re-initialize the rcc 208 // when we wake from any stop mode we need to re-initialize the rcc
213 crate::rcc::apply_resume_config(); 209 while RCC.cfgr().read().sws() != Sw::MSI {}
210
211 init_rcc(RCC_CONFIG.unwrap());
212
214 if extscr.c1stop2f() { 213 if extscr.c1stop2f() {
215 // when we wake from STOP2, we need to re-initialize the time driver 214 // when we wake from STOP2, we need to re-initialize the time driver
216 crate::time_driver::init_timer(cs); 215 get_driver().init_timer(cs);
217 // reset the refcounts for STOP2 and STOP1 (initializing the time driver will increment one of them for the timer) 216 // reset the refcounts for STOP2 and STOP1 (initializing the time driver will increment one of them for the timer)
218 // and given that we just woke from STOP2, we can reset them 217 // and given that we just woke from STOP2, we can reset them
219 crate::rcc::REFCOUNT_STOP2 = 0; 218 REFCOUNT_STOP2 = 0;
220 crate::rcc::REFCOUNT_STOP1 = 0; 219 REFCOUNT_STOP1 = 0;
221 } 220 }
222 } 221 }
223 } 222 }
@@ -226,18 +225,25 @@ impl Executor {
226 }); 225 });
227 } 226 }
228 227
228 const fn get_scb() -> SCB {
229 unsafe { mem::transmute(()) }
230 }
231
229 fn stop_mode(_cs: CriticalSection) -> Option<StopMode> { 232 fn stop_mode(_cs: CriticalSection) -> Option<StopMode> {
230 if unsafe { crate::rcc::REFCOUNT_STOP2 == 0 && crate::rcc::REFCOUNT_STOP1 == 0 } { 233 if unsafe { REFCOUNT_STOP2 == 0 && REFCOUNT_STOP1 == 0 } {
234 trace!("low power: stop 2");
231 Some(StopMode::Stop2) 235 Some(StopMode::Stop2)
232 } else if unsafe { crate::rcc::REFCOUNT_STOP1 == 0 } { 236 } else if unsafe { REFCOUNT_STOP1 == 0 } {
237 trace!("low power: stop 1");
233 Some(StopMode::Stop1) 238 Some(StopMode::Stop1)
234 } else { 239 } else {
240 trace!("low power: not ready to stop");
235 None 241 None
236 } 242 }
237 } 243 }
238 244
239 #[allow(unused_variables)] 245 #[allow(unused_variables)]
240 fn configure_stop(&mut self, stop_mode: StopMode) { 246 fn configure_stop(&self, stop_mode: StopMode) {
241 #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wba, stm32wlex))] 247 #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wba, stm32wlex))]
242 crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into())); 248 crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into()));
243 #[cfg(stm32h5)] 249 #[cfg(stm32h5)]
@@ -248,8 +254,8 @@ impl Executor {
248 }); 254 });
249 } 255 }
250 256
251 fn configure_pwr(&mut self) { 257 fn configure_pwr(&self) {
252 self.scb.clear_sleepdeep(); 258 Self::get_scb().clear_sleepdeep();
253 // Clear any previous stop flags 259 // Clear any previous stop flags
254 #[cfg(stm32wlex)] 260 #[cfg(stm32wlex)]
255 crate::pac::PWR.extscr().modify(|w| { 261 crate::pac::PWR.extscr().modify(|w| {
@@ -258,27 +264,18 @@ impl Executor {
258 264
259 compiler_fence(Ordering::SeqCst); 265 compiler_fence(Ordering::SeqCst);
260 266
261 let stop_mode = critical_section::with(|cs| Self::stop_mode(cs)); 267 critical_section::with(|cs| {
262 268 let stop_mode = Self::stop_mode(cs)?;
263 if stop_mode.is_none() { 269 let _ = get_driver().pause_time(cs).ok()?;
264 trace!("low power: not ready to stop");
265 return;
266 }
267
268 if get_driver().pause_time().is_err() {
269 trace!("low power: failed to pause time");
270 return;
271 }
272 270
273 let stop_mode = stop_mode.unwrap(); 271 Some(stop_mode)
274 match stop_mode { 272 })
275 StopMode::Stop1 => trace!("low power: stop 1"), 273 .map(|stop_mode| {
276 StopMode::Stop2 => trace!("low power: stop 2"), 274 self.configure_stop(stop_mode);
277 }
278 self.configure_stop(stop_mode);
279 275
280 #[cfg(not(feature = "low-power-debug-with-sleep"))] 276 #[cfg(not(feature = "low-power-debug-with-sleep"))]
281 self.scb.set_sleepdeep(); 277 Self::get_scb().set_sleepdeep();
278 });
282 } 279 }
283 280
284 /// Run the executor. 281 /// Run the executor.
@@ -300,12 +297,11 @@ impl Executor {
300 /// 297 ///
301 /// This function never returns. 298 /// This function never returns.
302 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { 299 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
303 let executor = unsafe { EXECUTOR.as_mut().unwrap() }; 300 init(self.inner.spawner());
304 init(executor.inner.spawner());
305 301
306 loop { 302 loop {
307 unsafe { 303 unsafe {
308 executor.inner.poll(); 304 self.inner.poll();
309 self.configure_pwr(); 305 self.configure_pwr();
310 asm!("wfe"); 306 asm!("wfe");
311 #[cfg(stm32wlex)] 307 #[cfg(stm32wlex)]
diff --git a/embassy-stm32/src/rcc/l.rs b/embassy-stm32/src/rcc/l.rs
index 584957c6d..2e1cbd702 100644
--- a/embassy-stm32/src/rcc/l.rs
+++ b/embassy-stm32/src/rcc/l.rs
@@ -1,6 +1,3 @@
1#[cfg(all(feature = "low-power", stm32wlex))]
2use core::mem::MaybeUninit;
3
4#[cfg(any(stm32l0, stm32l1))] 1#[cfg(any(stm32l0, stm32l1))]
5pub use crate::pac::pwr::vals::Vos as VoltageScale; 2pub use crate::pac::pwr::vals::Vos as VoltageScale;
6use crate::pac::rcc::regs::Cfgr; 3use crate::pac::rcc::regs::Cfgr;
@@ -14,42 +11,6 @@ use crate::time::Hertz;
14/// HSI speed 11/// HSI speed
15pub const HSI_FREQ: Hertz = Hertz(16_000_000); 12pub const HSI_FREQ: Hertz = Hertz(16_000_000);
16 13
17/// Saved RCC Config
18///
19/// Used when exiting STOP2 to re-enable clocks to their last configured state
20/// for chips that need it.
21#[cfg(all(feature = "low-power", stm32wlex))]
22static mut RESUME_RCC_CONFIG: MaybeUninit<Config> = MaybeUninit::uninit();
23
24/// Set the rcc config to be restored when exiting STOP2
25///
26/// Safety: Sets a mutable global.
27#[cfg(all(feature = "low-power", stm32wlex))]
28pub(crate) unsafe fn set_resume_config(config: Config) {
29 trace!("rcc set_resume_config()");
30 RESUME_RCC_CONFIG = MaybeUninit::new(config);
31}
32
33/// Get the rcc config to be restored when exiting STOP2
34///
35/// Safety: Reads a mutable global.
36#[cfg(all(feature = "low-power", stm32wlex))]
37pub(crate) unsafe fn get_resume_config() -> Config {
38 *(*core::ptr::addr_of_mut!(RESUME_RCC_CONFIG)).assume_init_ref()
39}
40
41#[cfg(all(feature = "low-power", stm32wlex))]
42/// Safety: should only be called from low power executable just after resuming from STOP2
43pub(crate) unsafe fn apply_resume_config() {
44 trace!("rcc apply_resume_config()");
45
46 while RCC.cfgr().read().sws() != Sysclk::MSI {}
47
48 let config = get_resume_config();
49
50 init(config);
51}
52
53#[derive(Clone, Copy, Eq, PartialEq)] 14#[derive(Clone, Copy, Eq, PartialEq)]
54pub enum HseMode { 15pub enum HseMode {
55 /// crystal/ceramic oscillator (HSEBYP=0) 16 /// crystal/ceramic oscillator (HSEBYP=0)
@@ -193,10 +154,6 @@ fn msi_enable(range: MSIRange) {
193} 154}
194 155
195pub(crate) unsafe fn init(config: Config) { 156pub(crate) unsafe fn init(config: Config) {
196 // save the rcc config because if we enter stop 2 we need to re-apply it on wakeup
197 #[cfg(all(feature = "low-power", stm32wlex))]
198 set_resume_config(config);
199
200 // Switch to MSI to prevent problems with PLL configuration. 157 // Switch to MSI to prevent problems with PLL configuration.
201 if !RCC.cr().read().msion() { 158 if !RCC.cr().read().msion() {
202 // Turn on MSI and configure it to 4MHz. 159 // Turn on MSI and configure it to 4MHz.
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs
index 592890777..66ee06e17 100644
--- a/embassy-stm32/src/rcc/mod.rs
+++ b/embassy-stm32/src/rcc/mod.rs
@@ -49,6 +49,9 @@ pub(crate) static mut REFCOUNT_STOP1: u32 = 0;
49/// May be read without a critical section 49/// May be read without a critical section
50pub(crate) static mut REFCOUNT_STOP2: u32 = 0; 50pub(crate) static mut REFCOUNT_STOP2: u32 = 0;
51 51
52#[cfg(feature = "low-power")]
53pub(crate) static mut RCC_CONFIG: Option<Config> = None;
54
52#[cfg(backup_sram)] 55#[cfg(backup_sram)]
53pub(crate) static mut BKSRAM_RETAINED: bool = false; 56pub(crate) static mut BKSRAM_RETAINED: bool = false;
54 57
@@ -408,8 +411,39 @@ pub(crate) fn init_rcc(_cs: CriticalSection, config: Config) {
408 411
409 #[cfg(feature = "low-power")] 412 #[cfg(feature = "low-power")]
410 { 413 {
414 RCC_CONFIG = Some(config);
411 REFCOUNT_STOP2 = 0; 415 REFCOUNT_STOP2 = 0;
412 REFCOUNT_STOP1 = 0; 416 REFCOUNT_STOP1 = 0;
413 } 417 }
414 } 418 }
415} 419}
420
421/// Calculate intermediate prescaler number used to calculate peripheral prescalers
422///
423/// This function is intended to calculate a number indicating a minimum division
424/// necessary to result in a frequency lower than the provided `freq_max`.
425///
426/// The returned value indicates the `val + 1` divider is necessary to result in
427/// the output frequency that is below the maximum provided.
428///
429/// For example:
430/// 0 = divider of 1 => no division necessary as the input frequency is below max
431/// 1 = divider of 2 => division by 2 necessary
432/// ...
433///
434/// The provided max frequency is inclusive. So if `freq_in == freq_max` the result
435/// will be 0, indicating that no division is necessary. To accomplish that we subtract
436/// 1 from the input frequency so that the integer rounding plays in our favor.
437///
438/// For example:
439/// Let the input frequency be 110 and the max frequency be 55.
440/// If we naiively do `110/55 = 2` the renult will indicate that we need a divider by 3
441/// which in reality will be rounded up to 4 as usually a 3 division is not available.
442/// In either case the resulting frequency will be either 36 or 27 which is lower than
443/// what we would want. The result should be 1.
444/// If we do the following instead `109/55 = 1` indicating that we need a divide by 2
445/// which will result in the correct 55.
446#[allow(unused)]
447pub(crate) fn raw_prescaler(freq_in: u32, freq_max: u32) -> u32 {
448 freq_in.saturating_sub(1) / freq_max
449}
diff --git a/embassy-stm32/src/rtc/low_power.rs b/embassy-stm32/src/rtc/low_power.rs
index e5bf30927..f049d6b12 100644
--- a/embassy-stm32/src/rtc/low_power.rs
+++ b/embassy-stm32/src/rtc/low_power.rs
@@ -3,6 +3,7 @@ use embassy_time::{Duration, TICK_HZ};
3 3
4use super::{DateTimeError, Rtc, RtcError, bcd2_to_byte}; 4use super::{DateTimeError, Rtc, RtcError, bcd2_to_byte};
5use crate::interrupt::typelevel::Interrupt; 5use crate::interrupt::typelevel::Interrupt;
6use crate::pac::rtc::vals::Wucksel;
6use crate::peripherals::RTC; 7use crate::peripherals::RTC;
7use crate::rtc::{RtcTimeProvider, SealedInstance}; 8use crate::rtc::{RtcTimeProvider, SealedInstance};
8 9
@@ -58,60 +59,16 @@ impl core::ops::Sub for RtcInstant {
58 } 59 }
59} 60}
60 61
61#[repr(u8)] 62fn wucksel_compute_min(val: u32) -> (Wucksel, u32) {
62#[derive(Clone, Copy, Debug)] 63 *[
63pub(crate) enum WakeupPrescaler { 64 (Wucksel::DIV2, 2),
64 Div2 = 2, 65 (Wucksel::DIV4, 4),
65 Div4 = 4, 66 (Wucksel::DIV8, 8),
66 Div8 = 8, 67 (Wucksel::DIV16, 16),
67 Div16 = 16, 68 ]
68} 69 .iter()
69 70 .find(|(_, psc)| *psc as u32 > val)
70#[cfg(any( 71 .unwrap_or(&(Wucksel::DIV16, 16))
71 stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0, stm32wba, stm32wlex
72))]
73impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel {
74 fn from(val: WakeupPrescaler) -> Self {
75 use crate::pac::rtc::vals::Wucksel;
76
77 match val {
78 WakeupPrescaler::Div2 => Wucksel::DIV2,
79 WakeupPrescaler::Div4 => Wucksel::DIV4,
80 WakeupPrescaler::Div8 => Wucksel::DIV8,
81 WakeupPrescaler::Div16 => Wucksel::DIV16,
82 }
83 }
84}
85
86#[cfg(any(
87 stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0, stm32wba, stm32wlex
88))]
89impl From<crate::pac::rtc::vals::Wucksel> for WakeupPrescaler {
90 fn from(val: crate::pac::rtc::vals::Wucksel) -> Self {
91 use crate::pac::rtc::vals::Wucksel;
92
93 match val {
94 Wucksel::DIV2 => WakeupPrescaler::Div2,
95 Wucksel::DIV4 => WakeupPrescaler::Div4,
96 Wucksel::DIV8 => WakeupPrescaler::Div8,
97 Wucksel::DIV16 => WakeupPrescaler::Div16,
98 _ => unreachable!(),
99 }
100 }
101}
102
103impl WakeupPrescaler {
104 pub fn compute_min(val: u32) -> Self {
105 *[
106 WakeupPrescaler::Div2,
107 WakeupPrescaler::Div4,
108 WakeupPrescaler::Div8,
109 WakeupPrescaler::Div16,
110 ]
111 .iter()
112 .find(|psc| **psc as u32 > val)
113 .unwrap_or(&WakeupPrescaler::Div16)
114 }
115} 72}
116 73
117impl Rtc { 74impl Rtc {
@@ -138,7 +95,7 @@ impl Rtc {
138 let requested_duration = requested_duration.as_ticks().clamp(0, u32::MAX as u64); 95 let requested_duration = requested_duration.as_ticks().clamp(0, u32::MAX as u64);
139 let rtc_hz = Self::frequency().0 as u64; 96 let rtc_hz = Self::frequency().0 as u64;
140 let rtc_ticks = requested_duration * rtc_hz / TICK_HZ; 97 let rtc_ticks = requested_duration * rtc_hz / TICK_HZ;
141 let prescaler = WakeupPrescaler::compute_min((rtc_ticks / u16::MAX as u64) as u32); 98 let (wucksel, prescaler) = wucksel_compute_min((rtc_ticks / u16::MAX as u64) as u32);
142 99
143 // adjust the rtc ticks to the prescaler and subtract one rtc tick 100 // adjust the rtc ticks to the prescaler and subtract one rtc tick
144 let rtc_ticks = rtc_ticks / prescaler as u64; 101 let rtc_ticks = rtc_ticks / prescaler as u64;
@@ -159,7 +116,7 @@ impl Rtc {
159 while !regs.icsr().read().wutwf() {} 116 while !regs.icsr().read().wutwf() {}
160 } 117 }
161 118
162 regs.cr().modify(|w| w.set_wucksel(prescaler.into())); 119 regs.cr().modify(|w| w.set_wucksel(wucksel));
163 regs.wutr().write(|w| w.set_wut(rtc_ticks)); 120 regs.wutr().write(|w| w.set_wut(rtc_ticks));
164 regs.cr().modify(|w| w.set_wute(true)); 121 regs.cr().modify(|w| w.set_wute(true));
165 regs.cr().modify(|w| w.set_wutie(true)); 122 regs.cr().modify(|w| w.set_wutie(true));
diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs
index 116b3c7ed..e88bd7ab2 100644
--- a/embassy-stm32/src/rtc/mod.rs
+++ b/embassy-stm32/src/rtc/mod.rs
@@ -379,13 +379,16 @@ trait SealedInstance {
379} 379}
380 380
381#[cfg(feature = "low-power")] 381#[cfg(feature = "low-power")]
382pub(crate) fn init_rtc(cs: CriticalSection, config: RtcConfig) { 382pub(crate) fn init_rtc(cs: CriticalSection, config: RtcConfig, min_stop_pause: embassy_time::Duration) {
383 use crate::time_driver::get_driver;
384
383 #[cfg(feature = "_allow-disable-rtc")] 385 #[cfg(feature = "_allow-disable-rtc")]
384 if config._disable_rtc { 386 if config._disable_rtc {
385 return; 387 return;
386 } 388 }
387 389
388 crate::time_driver::get_driver().set_rtc(cs, Rtc::new_inner(config)); 390 get_driver().set_rtc(cs, Rtc::new_inner(config));
391 get_driver().set_min_stop_pause(cs, min_stop_pause);
389 392
390 trace!("low power: stop with rtc configured"); 393 trace!("low power: stop with rtc configured");
391} 394}
diff --git a/embassy-stm32/src/sai/mod.rs b/embassy-stm32/src/sai/mod.rs
index 726d1729a..ce4bc43c3 100644
--- a/embassy-stm32/src/sai/mod.rs
+++ b/embassy-stm32/src/sai/mod.rs
@@ -391,7 +391,7 @@ pub struct Config {
391 pub frame_sync_polarity: FrameSyncPolarity, 391 pub frame_sync_polarity: FrameSyncPolarity,
392 pub frame_sync_active_level_length: word::U7, 392 pub frame_sync_active_level_length: word::U7,
393 pub frame_sync_definition: FrameSyncDefinition, 393 pub frame_sync_definition: FrameSyncDefinition,
394 pub frame_length: u8, 394 pub frame_length: u16,
395 pub clock_strobe: ClockStrobe, 395 pub clock_strobe: ClockStrobe,
396 pub output_drive: OutputDrive, 396 pub output_drive: OutputDrive,
397 pub master_clock_divider: Option<MasterClockDivider>, 397 pub master_clock_divider: Option<MasterClockDivider>,
@@ -696,7 +696,7 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> {
696 w.set_fspol(config.frame_sync_polarity.fspol()); 696 w.set_fspol(config.frame_sync_polarity.fspol());
697 w.set_fsdef(config.frame_sync_definition.fsdef()); 697 w.set_fsdef(config.frame_sync_definition.fsdef());
698 w.set_fsall(config.frame_sync_active_level_length.0 as u8 - 1); 698 w.set_fsall(config.frame_sync_active_level_length.0 as u8 - 1);
699 w.set_frl(config.frame_length - 1); 699 w.set_frl((config.frame_length - 1).try_into().unwrap());
700 }); 700 });
701 701
702 ch.slotr().modify(|w| { 702 ch.slotr().modify(|w| {
diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs
index 7db51d72e..0b75aef92 100644
--- a/embassy-stm32/src/time_driver.rs
+++ b/embassy-stm32/src/time_driver.rs
@@ -196,6 +196,11 @@ fn calc_now(period: u32, counter: u16) -> u64 {
196 ((period as u64) << 15) + ((counter as u32 ^ ((period & 1) << 15)) as u64) 196 ((period as u64) << 15) + ((counter as u32 ^ ((period & 1) << 15)) as u64)
197} 197}
198 198
199#[cfg(feature = "low-power")]
200fn calc_period_counter(ticks: u64) -> (u32, u16) {
201 (2 * (ticks >> 16) as u32 + (ticks as u16 >= 0x8000) as u32, ticks as u16)
202}
203
199struct AlarmState { 204struct AlarmState {
200 timestamp: Cell<u64>, 205 timestamp: Cell<u64>,
201} 206}
@@ -240,7 +245,7 @@ embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
240impl RtcDriver { 245impl RtcDriver {
241 /// initialize the timer, but don't start it. Used for chips like stm32wle5 246 /// initialize the timer, but don't start it. Used for chips like stm32wle5
242 /// for low power where the timer config is lost in STOP2. 247 /// for low power where the timer config is lost in STOP2.
243 fn init_timer(&'static self, cs: critical_section::CriticalSection) { 248 pub(crate) fn init_timer(&'static self, cs: critical_section::CriticalSection) {
244 let r = regs_gp16(); 249 let r = regs_gp16();
245 250
246 rcc::enable_and_reset_with_cs::<T>(cs); 251 rcc::enable_and_reset_with_cs::<T>(cs);
@@ -358,34 +363,10 @@ impl RtcDriver {
358 #[cfg(feature = "low-power")] 363 #[cfg(feature = "low-power")]
359 /// Add the given offset to the current time 364 /// Add the given offset to the current time
360 fn add_time(&self, offset: embassy_time::Duration, cs: CriticalSection) { 365 fn add_time(&self, offset: embassy_time::Duration, cs: CriticalSection) {
361 let offset = offset.as_ticks(); 366 let (period, counter) = calc_period_counter(self.now() + offset.as_ticks());
362 let cnt = regs_gp16().cnt().read().cnt() as u32;
363 let period = self.period.load(Ordering::SeqCst);
364
365 // Correct the race, if it exists
366 let period = if period & 1 == 1 && cnt < u16::MAX as u32 / 2 {
367 period + 1
368 } else {
369 period
370 };
371
372 // Normalize to the full overflow
373 let period = (period / 2) * 2;
374
375 // Add the offset
376 let period = period + 2 * (offset / u16::MAX as u64) as u32;
377 let cnt = cnt + (offset % u16::MAX as u64) as u32;
378
379 let (cnt, period) = if cnt > u16::MAX as u32 {
380 (cnt - u16::MAX as u32, period + 2)
381 } else {
382 (cnt, period)
383 };
384
385 let period = if cnt > u16::MAX as u32 / 2 { period + 1 } else { period };
386 367
387 self.period.store(period, Ordering::SeqCst); 368 self.period.store(period, Ordering::SeqCst);
388 regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16)); 369 regs_gp16().cnt().write(|w| w.set_cnt(counter));
389 370
390 // Now, recompute alarm 371 // Now, recompute alarm
391 let alarm = self.alarm.borrow(cs); 372 let alarm = self.alarm.borrow(cs);
@@ -399,13 +380,15 @@ impl RtcDriver {
399 #[cfg(feature = "low-power")] 380 #[cfg(feature = "low-power")]
400 /// Stop the wakeup alarm, if enabled, and add the appropriate offset 381 /// Stop the wakeup alarm, if enabled, and add the appropriate offset
401 fn stop_wakeup_alarm(&self, cs: CriticalSection) { 382 fn stop_wakeup_alarm(&self, cs: CriticalSection) {
402 if let Some(offset) = self.rtc.borrow(cs).borrow_mut().as_mut().unwrap().stop_wakeup_alarm(cs) { 383 if !regs_gp16().cr1().read().cen()
384 && let Some(offset) = self.rtc.borrow(cs).borrow_mut().as_mut().unwrap().stop_wakeup_alarm(cs)
385 {
403 self.add_time(offset, cs); 386 self.add_time(offset, cs);
404 } 387 }
405 } 388 }
406 389
407 /* 390 /*
408 Low-power public functions: all create or require a critical section 391 Low-power public functions: all require a critical section
409 */ 392 */
410 #[cfg(feature = "low-power")] 393 #[cfg(feature = "low-power")]
411 pub(crate) fn set_min_stop_pause(&self, cs: CriticalSection, min_stop_pause: embassy_time::Duration) { 394 pub(crate) fn set_min_stop_pause(&self, cs: CriticalSection, min_stop_pause: embassy_time::Duration) {
@@ -422,49 +405,36 @@ impl RtcDriver {
422 405
423 #[cfg(feature = "low-power")] 406 #[cfg(feature = "low-power")]
424 /// Pause the timer if ready; return err if not 407 /// Pause the timer if ready; return err if not
425 pub(crate) fn pause_time(&self) -> Result<(), ()> { 408 pub(crate) fn pause_time(&self, cs: CriticalSection) -> Result<(), ()> {
426 critical_section::with(|cs| { 409 self.stop_wakeup_alarm(cs);
427 /* 410
428 If the wakeup timer is currently running, then we need to stop it and 411 let time_until_next_alarm = self.time_until_next_alarm(cs);
429 add the elapsed time to the current time, as this will impact the result 412 if time_until_next_alarm < self.min_stop_pause.borrow(cs).get() {
430 of `time_until_next_alarm`. 413 trace!(
431 */ 414 "time_until_next_alarm < self.min_stop_pause ({})",
432 self.stop_wakeup_alarm(cs); 415 time_until_next_alarm
433 416 );
434 let time_until_next_alarm = self.time_until_next_alarm(cs); 417 Err(())
435 if time_until_next_alarm < self.min_stop_pause.borrow(cs).get() { 418 } else {
436 trace!( 419 self.rtc
437 "time_until_next_alarm < self.min_stop_pause ({})", 420 .borrow(cs)
438 time_until_next_alarm 421 .borrow_mut()
439 ); 422 .as_mut()
440 Err(()) 423 .unwrap()
441 } else { 424 .start_wakeup_alarm(time_until_next_alarm, cs);
442 self.rtc 425
443 .borrow(cs) 426 regs_gp16().cr1().modify(|w| w.set_cen(false));
444 .borrow_mut() 427 // save the count for the timer as its lost in STOP2 for stm32wlex
445 .as_mut() 428 #[cfg(stm32wlex)]
446 .unwrap() 429 self.saved_count
447 .start_wakeup_alarm(time_until_next_alarm, cs); 430 .store(regs_gp16().cnt().read().cnt() as u16, Ordering::SeqCst);
448 431 Ok(())
449 regs_gp16().cr1().modify(|w| w.set_cen(false)); 432 }
450 // save the count for the timer as its lost in STOP2 for stm32wlex
451 #[cfg(stm32wlex)]
452 self.saved_count
453 .store(regs_gp16().cnt().read().cnt() as u16, Ordering::SeqCst);
454 Ok(())
455 }
456 })
457 } 433 }
458 434
459 #[cfg(feature = "low-power")] 435 #[cfg(feature = "low-power")]
460 /// Resume the timer with the given offset 436 /// Resume the timer with the given offset
461 pub(crate) fn resume_time(&self, cs: CriticalSection) { 437 pub(crate) fn resume_time(&self, cs: CriticalSection) {
462 if regs_gp16().cr1().read().cen() {
463 // Time isn't currently stopped
464
465 return;
466 }
467
468 self.stop_wakeup_alarm(cs); 438 self.stop_wakeup_alarm(cs);
469 439
470 regs_gp16().cr1().modify(|w| w.set_cen(true)); 440 regs_gp16().cr1().modify(|w| w.set_cen(true));
@@ -546,8 +516,3 @@ pub(crate) const fn get_driver() -> &'static RtcDriver {
546pub(crate) fn init(cs: CriticalSection) { 516pub(crate) fn init(cs: CriticalSection) {
547 DRIVER.init(cs) 517 DRIVER.init(cs)
548} 518}
549
550#[cfg(all(feature = "low-power", stm32wlex))]
551pub(crate) fn init_timer(cs: CriticalSection) {
552 DRIVER.init_timer(cs)
553}
diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs
index 9a56a41fb..6d4c70dff 100644
--- a/embassy-stm32/src/timer/complementary_pwm.rs
+++ b/embassy-stm32/src/timer/complementary_pwm.rs
@@ -77,8 +77,6 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
77 77
78 this.inner.set_counting_mode(counting_mode); 78 this.inner.set_counting_mode(counting_mode);
79 this.set_frequency(freq); 79 this.set_frequency(freq);
80 this.inner.start();
81
82 this.inner.enable_outputs(); 80 this.inner.enable_outputs();
83 81
84 [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] 82 [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4]
@@ -89,6 +87,10 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
89 }); 87 });
90 this.inner.set_autoreload_preload(true); 88 this.inner.set_autoreload_preload(true);
91 89
90 // Generate update event so pre-load registers are written to the shadow registers
91 this.inner.generate_update_event();
92 this.inner.start();
93
92 this 94 this
93 } 95 }
94 96
@@ -160,8 +162,8 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
160 162
161 /// Set PWM frequency. 163 /// Set PWM frequency.
162 /// 164 ///
163 /// Note: when you call this, the max duty value changes, so you will have to 165 /// Note: that the frequency will not be applied in the timer until an update event
164 /// call `set_duty` on all channels with the duty calculated based on the new max duty. 166 /// occurs.
165 pub fn set_frequency(&mut self, freq: Hertz) { 167 pub fn set_frequency(&mut self, freq: Hertz) {
166 let multiplier = if self.inner.get_counting_mode().is_center_aligned() { 168 let multiplier = if self.inner.get_counting_mode().is_center_aligned() {
167 2u8 169 2u8
@@ -218,60 +220,57 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
218 /// 220 ///
219 /// Note: 221 /// Note:
220 /// you will need to provide corresponding TIMx_UP DMA channel to use this method. 222 /// you will need to provide corresponding TIMx_UP DMA channel to use this method.
223 #[inline(always)]
221 pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) { 224 pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) {
222 #[allow(clippy::let_unit_value)] // eg. stm32f334 225 self.inner.waveform_up(dma, channel, duty).await
223 let req = dma.request(); 226 }
224
225 let original_duty_state = self.inner.get_compare_value(channel);
226 let original_enable_state = self.inner.get_channel_enable_state(channel);
227 let original_update_dma_state = self.inner.get_update_dma_state();
228
229 if !original_update_dma_state {
230 self.inner.enable_update_dma(true);
231 }
232
233 if !original_enable_state {
234 self.inner.enable_channel(channel, true);
235 }
236
237 unsafe {
238 #[cfg(not(any(bdma, gpdma)))]
239 use crate::dma::{Burst, FifoThreshold};
240 use crate::dma::{Transfer, TransferOptions};
241
242 let dma_transfer_option = TransferOptions {
243 #[cfg(not(any(bdma, gpdma)))]
244 fifo_threshold: Some(FifoThreshold::Full),
245 #[cfg(not(any(bdma, gpdma)))]
246 mburst: Burst::Incr8,
247 ..Default::default()
248 };
249
250 Transfer::new_write(
251 dma,
252 req,
253 duty,
254 self.inner.regs_gp16().ccr(channel.index()).as_ptr() as *mut u16,
255 dma_transfer_option,
256 )
257 .await
258 };
259
260 // restore output compare state
261 if !original_enable_state {
262 self.inner.enable_channel(channel, false);
263 }
264 227
265 self.inner.set_compare_value(channel, original_duty_state); 228 /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events.
229 ///
230 /// This method utilizes the timer's DMA burst transfer capability to update multiple CCRx registers
231 /// in sequence on each update event (UEV). The data is written via the DMAR register using the
232 /// DMA base address (DBA) and burst length (DBL) configured in the DCR register.
233 ///
234 /// The `duty` buffer must be structured as a flattened 2D array in row-major order, where each row
235 /// represents a single update event and each column corresponds to a specific timer channel (starting
236 /// from `starting_channel` up to and including `ending_channel`).
237 ///
238 /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like:
239 ///
240 /// ```rust,ignore
241 /// let dma_buf: [u16; 16] = [
242 /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1
243 /// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2
244 /// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3
245 /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4
246 /// ];
247 /// ```
248 ///
249 /// Each group of `N` values (where `N` is number of channels) is transferred on one update event,
250 /// updating the duty cycles of all selected channels simultaneously.
251 ///
252 /// Note:
253 /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method.
254 /// Also be aware that embassy timers use one of timers internally. It is possible to
255 /// switch this timer by using `time-driver-timX` feature.
256 ///
257 #[inline(always)]
258 pub async fn waveform_up_multi_channel(
259 &mut self,
260 dma: Peri<'_, impl super::UpDma<T>>,
261 starting_channel: Channel,
262 ending_channel: Channel,
263 duty: &[u16],
264 ) {
265 self.inner
266 .waveform_up_multi_channel(dma, starting_channel, ending_channel, duty)
267 .await;
268 }
266 269
267 // Since DMA is closed before timer update event trigger DMA is turn off, 270 /// Generate a sequence of PWM waveform
268 // this can almost always trigger a DMA FIFO error. 271 #[inline(always)]
269 // 272 pub async fn waveform<C: TimerChannel>(&mut self, dma: Peri<'_, impl super::Dma<T, C>>, duty: &[u16]) {
270 // optional TODO: 273 self.inner.waveform(dma, duty).await;
271 // clean FEIF after disable UDE
272 if !original_update_dma_state {
273 self.inner.enable_update_dma(false);
274 }
275 } 274 }
276} 275}
277 276
diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs
index 2a4ec2db0..9cf0f8c34 100644
--- a/embassy-stm32/src/timer/input_capture.rs
+++ b/embassy-stm32/src/timer/input_capture.rs
@@ -60,6 +60,7 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> {
60 this.inner.set_counting_mode(counting_mode); 60 this.inner.set_counting_mode(counting_mode);
61 this.inner.set_tick_freq(freq); 61 this.inner.set_tick_freq(freq);
62 this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details 62 this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details
63 this.inner.generate_update_event();
63 this.inner.start(); 64 this.inner.start();
64 65
65 // enable NVIC interrupt 66 // enable NVIC interrupt
diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs
index 0122fe4f7..f0105ece8 100644
--- a/embassy-stm32/src/timer/low_level.rs
+++ b/embassy-stm32/src/timer/low_level.rs
@@ -14,8 +14,8 @@ pub use stm32_metapac::timer::vals::{FilterValue, Mms as MasterMode, Sms as Slav
14 14
15use super::*; 15use super::*;
16use crate::pac::timer::vals; 16use crate::pac::timer::vals;
17use crate::rcc;
18use crate::time::Hertz; 17use crate::time::Hertz;
18use crate::{dma, rcc};
19 19
20/// Input capture mode. 20/// Input capture mode.
21#[derive(Clone, Copy)] 21#[derive(Clone, Copy)]
@@ -272,6 +272,17 @@ impl<'d, T: CoreInstance> Timer<'d, T> {
272 self.regs_core().cr1().modify(|r| r.set_cen(true)); 272 self.regs_core().cr1().modify(|r| r.set_cen(true));
273 } 273 }
274 274
275 /// Generate timer update event from software.
276 ///
277 /// Set URS to avoid generating interrupt or DMA request. This update event is only
278 /// used to load value from pre-load registers. If called when the timer is running,
279 /// it may disrupt the output waveform.
280 pub fn generate_update_event(&self) {
281 self.regs_core().cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY));
282 self.regs_core().egr().write(|r| r.set_ug(true));
283 self.regs_core().cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT));
284 }
285
275 /// Stop the timer. 286 /// Stop the timer.
276 pub fn stop(&self) { 287 pub fn stop(&self) {
277 self.regs_core().cr1().modify(|r| r.set_cen(false)); 288 self.regs_core().cr1().modify(|r| r.set_cen(false));
@@ -322,10 +333,6 @@ impl<'d, T: CoreInstance> Timer<'d, T> {
322 let regs = self.regs_core(); 333 let regs = self.regs_core();
323 regs.psc().write_value(psc); 334 regs.psc().write_value(psc);
324 regs.arr().write(|r| r.set_arr(arr)); 335 regs.arr().write(|r| r.set_arr(arr));
325
326 regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY));
327 regs.egr().write(|r| r.set_ug(true));
328 regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT));
329 } 336 }
330 #[cfg(not(stm32l0))] 337 #[cfg(not(stm32l0))]
331 TimerBits::Bits32 => { 338 TimerBits::Bits32 => {
@@ -335,10 +342,6 @@ impl<'d, T: CoreInstance> Timer<'d, T> {
335 let regs = self.regs_gp32_unchecked(); 342 let regs = self.regs_gp32_unchecked();
336 regs.psc().write_value(psc); 343 regs.psc().write_value(psc);
337 regs.arr().write_value(arr); 344 regs.arr().write_value(arr);
338
339 regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY));
340 regs.egr().write(|r| r.set_ug(true));
341 regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT));
342 } 345 }
343 } 346 }
344 } 347 }
@@ -656,6 +659,219 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> {
656 } 659 }
657 } 660 }
658 661
662 /// Generate a sequence of PWM waveform
663 ///
664 /// Note:
665 /// you will need to provide corresponding TIMx_UP DMA channel to use this method.
666 pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) {
667 #[allow(clippy::let_unit_value)] // eg. stm32f334
668 let req = dma.request();
669
670 let original_update_dma_state = self.get_update_dma_state();
671
672 if !original_update_dma_state {
673 self.enable_update_dma(true);
674 }
675
676 self.waveform_helper(dma, req, channel, duty).await;
677
678 // Since DMA is closed before timer update event trigger DMA is turn off,
679 // this can almost always trigger a DMA FIFO error.
680 //
681 // optional TODO:
682 // clean FEIF after disable UDE
683 if !original_update_dma_state {
684 self.enable_update_dma(false);
685 }
686 }
687
688 /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events.
689 ///
690 /// This method utilizes the timer's DMA burst transfer capability to update multiple CCRx registers
691 /// in sequence on each update event (UEV). The data is written via the DMAR register using the
692 /// DMA base address (DBA) and burst length (DBL) configured in the DCR register.
693 ///
694 /// The `duty` buffer must be structured as a flattened 2D array in row-major order, where each row
695 /// represents a single update event and each column corresponds to a specific timer channel (starting
696 /// from `starting_channel` up to and including `ending_channel`).
697 ///
698 /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like:
699 ///
700 /// ```rust,ignore
701 /// let dma_buf: [u16; 16] = [
702 /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1
703 /// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2
704 /// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3
705 /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4
706 /// ];
707 /// ```
708 ///
709 /// Each group of `N` values (where `N` is number of channels) is transferred on one update event,
710 /// updating the duty cycles of all selected channels simultaneously.
711 ///
712 /// Note:
713 /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method.
714 /// Also be aware that embassy timers use one of timers internally. It is possible to
715 /// switch this timer by using `time-driver-timX` feature.
716 ///
717 pub async fn waveform_up_multi_channel(
718 &mut self,
719 dma: Peri<'_, impl super::UpDma<T>>,
720 starting_channel: Channel,
721 ending_channel: Channel,
722 duty: &[u16],
723 ) {
724 let cr1_addr = self.regs_gp16().cr1().as_ptr() as u32;
725 let start_ch_index = starting_channel.index();
726 let end_ch_index = ending_channel.index();
727
728 assert!(start_ch_index <= end_ch_index);
729
730 let ccrx_addr = self.regs_gp16().ccr(start_ch_index).as_ptr() as u32;
731 self.regs_gp16()
732 .dcr()
733 .modify(|w| w.set_dba(((ccrx_addr - cr1_addr) / 4) as u8));
734 self.regs_gp16()
735 .dcr()
736 .modify(|w| w.set_dbl((end_ch_index - start_ch_index) as u8));
737
738 #[allow(clippy::let_unit_value)] // eg. stm32f334
739 let req = dma.request();
740
741 let original_update_dma_state = self.get_update_dma_state();
742 if !original_update_dma_state {
743 self.enable_update_dma(true);
744 }
745
746 unsafe {
747 #[cfg(not(any(bdma, gpdma)))]
748 use crate::dma::{Burst, FifoThreshold};
749 use crate::dma::{Transfer, TransferOptions};
750
751 let dma_transfer_option = TransferOptions {
752 #[cfg(not(any(bdma, gpdma)))]
753 fifo_threshold: Some(FifoThreshold::Full),
754 #[cfg(not(any(bdma, gpdma)))]
755 mburst: Burst::Incr4,
756 ..Default::default()
757 };
758
759 Transfer::new_write(
760 dma,
761 req,
762 duty,
763 self.regs_gp16().dmar().as_ptr() as *mut u16,
764 dma_transfer_option,
765 )
766 .await
767 };
768
769 if !original_update_dma_state {
770 self.enable_update_dma(false);
771 }
772 }
773
774 /// Generate a sequence of PWM waveform
775 pub async fn waveform<C: TimerChannel>(&mut self, dma: Peri<'_, impl super::Dma<T, C>>, duty: &[u16]) {
776 use crate::pac::timer::vals::Ccds;
777
778 #[allow(clippy::let_unit_value)] // eg. stm32f334
779 let req = dma.request();
780
781 let cc_channel = C::CHANNEL;
782
783 let original_cc_dma_on_update = self.get_cc_dma_selection() == Ccds::ON_UPDATE;
784 let original_cc_dma_enabled = self.get_cc_dma_enable_state(cc_channel);
785
786 // redirect CC DMA request onto Update Event
787 if !original_cc_dma_on_update {
788 self.set_cc_dma_selection(Ccds::ON_UPDATE)
789 }
790
791 if !original_cc_dma_enabled {
792 self.set_cc_dma_enable_state(cc_channel, true);
793 }
794
795 self.waveform_helper(dma, req, cc_channel, duty).await;
796
797 // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off,
798 // this can almost always trigger a DMA FIFO error.
799 //
800 // optional TODO:
801 // clean FEIF after disable UDE
802 if !original_cc_dma_enabled {
803 self.set_cc_dma_enable_state(cc_channel, false);
804 }
805
806 if !original_cc_dma_on_update {
807 self.set_cc_dma_selection(Ccds::ON_COMPARE)
808 }
809 }
810
811 async fn waveform_helper(
812 &mut self,
813 dma: Peri<'_, impl dma::Channel>,
814 req: dma::Request,
815 channel: Channel,
816 duty: &[u16],
817 ) {
818 let original_duty_state = self.get_compare_value(channel);
819 let original_enable_state = self.get_channel_enable_state(channel);
820
821 if !original_enable_state {
822 self.enable_channel(channel, true);
823 }
824
825 unsafe {
826 #[cfg(not(any(bdma, gpdma)))]
827 use crate::dma::{Burst, FifoThreshold};
828 use crate::dma::{Transfer, TransferOptions};
829
830 let dma_transfer_option = TransferOptions {
831 #[cfg(not(any(bdma, gpdma)))]
832 fifo_threshold: Some(FifoThreshold::Full),
833 #[cfg(not(any(bdma, gpdma)))]
834 mburst: Burst::Incr8,
835 ..Default::default()
836 };
837
838 match self.bits() {
839 TimerBits::Bits16 => {
840 Transfer::new_write(
841 dma,
842 req,
843 duty,
844 self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16,
845 dma_transfer_option,
846 )
847 .await
848 }
849 #[cfg(not(any(stm32l0)))]
850 TimerBits::Bits32 => {
851 #[cfg(not(any(bdma, gpdma)))]
852 panic!("unsupported timer bits");
853
854 #[cfg(any(bdma, gpdma))]
855 Transfer::new_write(
856 dma,
857 req,
858 duty,
859 self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u32,
860 dma_transfer_option,
861 )
862 .await
863 }
864 };
865 };
866
867 // restore output compare state
868 if !original_enable_state {
869 self.enable_channel(channel, false);
870 }
871
872 self.set_compare_value(channel, original_duty_state);
873 }
874
659 /// Get capture value for a channel. 875 /// Get capture value for a channel.
660 pub fn get_capture_value(&self, channel: Channel) -> u32 { 876 pub fn get_capture_value(&self, channel: Channel) -> u32 {
661 self.get_compare_value(channel) 877 self.get_compare_value(channel)
diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs
index da8a79b09..057ab011a 100644
--- a/embassy-stm32/src/timer/pwm_input.rs
+++ b/embassy-stm32/src/timer/pwm_input.rs
@@ -47,6 +47,7 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> {
47 inner.set_counting_mode(CountingMode::EdgeAlignedUp); 47 inner.set_counting_mode(CountingMode::EdgeAlignedUp);
48 inner.set_tick_freq(freq); 48 inner.set_tick_freq(freq);
49 inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details 49 inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details
50 inner.generate_update_event();
50 inner.start(); 51 inner.start();
51 52
52 // Configuration steps from ST RM0390 (STM32F446) chapter 17.3.6 53 // Configuration steps from ST RM0390 (STM32F446) chapter 17.3.6
diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs
index 36303aeb4..6c9ef17e0 100644
--- a/embassy-stm32/src/timer/simple_pwm.rs
+++ b/embassy-stm32/src/timer/simple_pwm.rs
@@ -4,7 +4,7 @@ use core::marker::PhantomData;
4use core::mem::ManuallyDrop; 4use core::mem::ManuallyDrop;
5 5
6use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; 6use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer};
7use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerBits, TimerChannel, TimerPin}; 7use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerChannel, TimerPin};
8use crate::Peri; 8use crate::Peri;
9#[cfg(gpio_v2)] 9#[cfg(gpio_v2)]
10use crate::gpio::Pull; 10use crate::gpio::Pull;
@@ -198,7 +198,6 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
198 this.inner.set_counting_mode(counting_mode); 198 this.inner.set_counting_mode(counting_mode);
199 this.set_frequency(freq); 199 this.set_frequency(freq);
200 this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details 200 this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details
201 this.inner.start();
202 201
203 [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] 202 [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4]
204 .iter() 203 .iter()
@@ -207,6 +206,11 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
207 206
208 this.inner.set_output_compare_preload(channel, true); 207 this.inner.set_output_compare_preload(channel, true);
209 }); 208 });
209 this.inner.set_autoreload_preload(true);
210
211 // Generate update event so pre-load registers are written to the shadow registers
212 this.inner.generate_update_event();
213 this.inner.start();
210 214
211 this 215 this
212 } 216 }
@@ -285,8 +289,8 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
285 289
286 /// Set PWM frequency. 290 /// Set PWM frequency.
287 /// 291 ///
288 /// Note: when you call this, the max duty value changes, so you will have to 292 /// Note: that the frequency will not be applied in the timer until an update event
289 /// call `set_duty` on all channels with the duty calculated based on the new max duty. 293 /// occurs.
290 pub fn set_frequency(&mut self, freq: Hertz) { 294 pub fn set_frequency(&mut self, freq: Hertz) {
291 // TODO: prevent ARR = u16::MAX? 295 // TODO: prevent ARR = u16::MAX?
292 let multiplier = if self.inner.get_counting_mode().is_center_aligned() { 296 let multiplier = if self.inner.get_counting_mode().is_center_aligned() {
@@ -309,80 +313,12 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
309 /// Generate a sequence of PWM waveform 313 /// Generate a sequence of PWM waveform
310 /// 314 ///
311 /// Note: 315 /// Note:
312 /// you will need to provide corresponding TIMx_UP DMA channel to use this method. 316 /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method.
317 /// Also be aware that embassy timers use one of timers internally. It is possible to
318 /// switch this timer by using `time-driver-timX` feature.
319 #[inline(always)]
313 pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) { 320 pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) {
314 #[allow(clippy::let_unit_value)] // eg. stm32f334 321 self.inner.waveform_up(dma, channel, duty).await;
315 let req = dma.request();
316
317 let original_duty_state = self.channel(channel).current_duty_cycle();
318 let original_enable_state = self.channel(channel).is_enabled();
319 let original_update_dma_state = self.inner.get_update_dma_state();
320
321 if !original_update_dma_state {
322 self.inner.enable_update_dma(true);
323 }
324
325 if !original_enable_state {
326 self.channel(channel).enable();
327 }
328
329 unsafe {
330 #[cfg(not(any(bdma, gpdma)))]
331 use crate::dma::{Burst, FifoThreshold};
332 use crate::dma::{Transfer, TransferOptions};
333
334 let dma_transfer_option = TransferOptions {
335 #[cfg(not(any(bdma, gpdma)))]
336 fifo_threshold: Some(FifoThreshold::Full),
337 #[cfg(not(any(bdma, gpdma)))]
338 mburst: Burst::Incr8,
339 ..Default::default()
340 };
341
342 match self.inner.bits() {
343 TimerBits::Bits16 => {
344 Transfer::new_write(
345 dma,
346 req,
347 duty,
348 self.inner.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16,
349 dma_transfer_option,
350 )
351 .await
352 }
353 #[cfg(not(any(stm32l0)))]
354 TimerBits::Bits32 => {
355 #[cfg(not(any(bdma, gpdma)))]
356 panic!("unsupported timer bits");
357
358 #[cfg(any(bdma, gpdma))]
359 Transfer::new_write(
360 dma,
361 req,
362 duty,
363 self.inner.regs_1ch().ccr(channel.index()).as_ptr() as *mut u32,
364 dma_transfer_option,
365 )
366 .await
367 }
368 };
369 };
370
371 // restore output compare state
372 if !original_enable_state {
373 self.channel(channel).disable();
374 }
375
376 self.channel(channel).set_duty_cycle(original_duty_state);
377
378 // Since DMA is closed before timer update event trigger DMA is turn off,
379 // this can almost always trigger a DMA FIFO error.
380 //
381 // optional TODO:
382 // clean FEIF after disable UDE
383 if !original_update_dma_state {
384 self.inner.enable_update_dma(false);
385 }
386 } 322 }
387 323
388 /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events. 324 /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events.
@@ -397,18 +333,24 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
397 /// 333 ///
398 /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like: 334 /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like:
399 /// 335 ///
336 /// ```rust,ignore
400 /// let dma_buf: [u16; 16] = [ 337 /// let dma_buf: [u16; 16] = [
401 /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1 338 /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1
402 /// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2 339 /// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2
403 /// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3 340 /// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3
404 /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4 341 /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4
405 /// ]; 342 /// ];
343 /// ```
406 /// 344 ///
407 /// Each group of N values (where N = number of channels) is transferred on one update event, 345 /// Each group of `N` values (where `N` is number of channels) is transferred on one update event,
408 /// updating the duty cycles of all selected channels simultaneously. 346 /// updating the duty cycles of all selected channels simultaneously.
409 /// 347 ///
410 /// Note: 348 /// Note:
411 /// you will need to provide corresponding TIMx_UP DMA channel to use this method. 349 /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method.
350 /// Also be aware that embassy timers use one of timers internally. It is possible to
351 /// switch this timer by using `time-driver-timX` feature.
352 ///
353 #[inline(always)]
412 pub async fn waveform_up_multi_channel( 354 pub async fn waveform_up_multi_channel(
413 &mut self, 355 &mut self,
414 dma: Peri<'_, impl super::UpDma<T>>, 356 dma: Peri<'_, impl super::UpDma<T>>,
@@ -416,148 +358,15 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
416 ending_channel: Channel, 358 ending_channel: Channel,
417 duty: &[u16], 359 duty: &[u16],
418 ) { 360 ) {
419 let cr1_addr = self.inner.regs_gp16().cr1().as_ptr() as u32;
420 let start_ch_index = starting_channel.index();
421 let end_ch_index = ending_channel.index();
422
423 assert!(start_ch_index <= end_ch_index);
424
425 let ccrx_addr = self.inner.regs_gp16().ccr(start_ch_index).as_ptr() as u32;
426 self.inner 361 self.inner
427 .regs_gp16() 362 .waveform_up_multi_channel(dma, starting_channel, ending_channel, duty)
428 .dcr() 363 .await;
429 .modify(|w| w.set_dba(((ccrx_addr - cr1_addr) / 4) as u8));
430 self.inner
431 .regs_gp16()
432 .dcr()
433 .modify(|w| w.set_dbl((end_ch_index - start_ch_index) as u8));
434
435 #[allow(clippy::let_unit_value)] // eg. stm32f334
436 let req = dma.request();
437
438 let original_update_dma_state = self.inner.get_update_dma_state();
439 if !original_update_dma_state {
440 self.inner.enable_update_dma(true);
441 }
442
443 unsafe {
444 #[cfg(not(any(bdma, gpdma)))]
445 use crate::dma::{Burst, FifoThreshold};
446 use crate::dma::{Transfer, TransferOptions};
447
448 let dma_transfer_option = TransferOptions {
449 #[cfg(not(any(bdma, gpdma)))]
450 fifo_threshold: Some(FifoThreshold::Full),
451 #[cfg(not(any(bdma, gpdma)))]
452 mburst: Burst::Incr4,
453 ..Default::default()
454 };
455
456 Transfer::new_write(
457 dma,
458 req,
459 duty,
460 self.inner.regs_gp16().dmar().as_ptr() as *mut u16,
461 dma_transfer_option,
462 )
463 .await
464 };
465
466 if !original_update_dma_state {
467 self.inner.enable_update_dma(false);
468 }
469 } 364 }
470}
471 365
472impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
473 /// Generate a sequence of PWM waveform 366 /// Generate a sequence of PWM waveform
367 #[inline(always)]
474 pub async fn waveform<C: TimerChannel>(&mut self, dma: Peri<'_, impl super::Dma<T, C>>, duty: &[u16]) { 368 pub async fn waveform<C: TimerChannel>(&mut self, dma: Peri<'_, impl super::Dma<T, C>>, duty: &[u16]) {
475 use crate::pac::timer::vals::Ccds; 369 self.inner.waveform(dma, duty).await;
476
477 #[allow(clippy::let_unit_value)] // eg. stm32f334
478 let req = dma.request();
479
480 let cc_channel = C::CHANNEL;
481
482 let original_duty_state = self.channel(cc_channel).current_duty_cycle();
483 let original_enable_state = self.channel(cc_channel).is_enabled();
484 let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ON_UPDATE;
485 let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel);
486
487 // redirect CC DMA request onto Update Event
488 if !original_cc_dma_on_update {
489 self.inner.set_cc_dma_selection(Ccds::ON_UPDATE)
490 }
491
492 if !original_cc_dma_enabled {
493 self.inner.set_cc_dma_enable_state(cc_channel, true);
494 }
495
496 if !original_enable_state {
497 self.channel(cc_channel).enable();
498 }
499
500 unsafe {
501 #[cfg(not(any(bdma, gpdma)))]
502 use crate::dma::{Burst, FifoThreshold};
503 use crate::dma::{Transfer, TransferOptions};
504
505 let dma_transfer_option = TransferOptions {
506 #[cfg(not(any(bdma, gpdma)))]
507 fifo_threshold: Some(FifoThreshold::Full),
508 #[cfg(not(any(bdma, gpdma)))]
509 mburst: Burst::Incr8,
510 ..Default::default()
511 };
512
513 match self.inner.bits() {
514 TimerBits::Bits16 => {
515 Transfer::new_write(
516 dma,
517 req,
518 duty,
519 self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u16,
520 dma_transfer_option,
521 )
522 .await
523 }
524 #[cfg(not(any(stm32l0)))]
525 TimerBits::Bits32 => {
526 #[cfg(not(any(bdma, gpdma)))]
527 panic!("unsupported timer bits");
528
529 #[cfg(any(bdma, gpdma))]
530 Transfer::new_write(
531 dma,
532 req,
533 duty,
534 self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u32,
535 dma_transfer_option,
536 )
537 .await
538 }
539 };
540 };
541
542 // restore output compare state
543 if !original_enable_state {
544 self.channel(cc_channel).disable();
545 }
546
547 self.channel(cc_channel).set_duty_cycle(original_duty_state);
548
549 // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off,
550 // this can almost always trigger a DMA FIFO error.
551 //
552 // optional TODO:
553 // clean FEIF after disable UDE
554 if !original_cc_dma_enabled {
555 self.inner.set_cc_dma_enable_state(cc_channel, false);
556 }
557
558 if !original_cc_dma_on_update {
559 self.inner.set_cc_dma_selection(Ccds::ON_COMPARE)
560 }
561 } 370 }
562} 371}
563 372
diff --git a/examples/lpc55s69/src/bin/pwm.rs b/examples/lpc55s69/src/bin/pwm.rs
index 93b898b9d..8a9894b94 100644
--- a/examples/lpc55s69/src/bin/pwm.rs
+++ b/examples/lpc55s69/src/bin/pwm.rs
@@ -10,7 +10,7 @@ use {defmt_rtt as _, panic_halt as _};
10#[embassy_executor::main] 10#[embassy_executor::main]
11async fn main(_spawner: Spawner) { 11async fn main(_spawner: Spawner) {
12 let p = embassy_nxp::init(Default::default()); 12 let p = embassy_nxp::init(Default::default());
13 let pwm = Pwm::new_output(p.PWM_OUTPUT1, p.PIO0_18, Config::new(1_000_000_000, 2_000_000_000)); 13 let pwm = Pwm::new_output(p.SCT0_OUT1, p.PIO0_18, Config::new(1_000_000_000, 2_000_000_000));
14 loop { 14 loop {
15 info!("Counter: {}", pwm.counter()); 15 info!("Counter: {}", pwm.counter());
16 Timer::after_millis(50).await; 16 Timer::after_millis(50).await;
diff --git a/examples/lpc55s69/src/bin/usart_async.rs b/examples/lpc55s69/src/bin/usart_async.rs
index b06abd477..a9815b920 100644
--- a/examples/lpc55s69/src/bin/usart_async.rs
+++ b/examples/lpc55s69/src/bin/usart_async.rs
@@ -38,8 +38,8 @@ async fn main(spawner: Spawner) {
38 p.PIO0_27, 38 p.PIO0_27,
39 p.PIO1_24, 39 p.PIO1_24,
40 Irqs, 40 Irqs,
41 p.DMA_CH11, 41 p.DMA0_CH11,
42 p.DMA_CH10, 42 p.DMA0_CH10,
43 Config::default(), 43 Config::default(),
44 ); 44 );
45 let led = Output::new(p.PIO1_6, Level::Low); 45 let led = Output::new(p.PIO1_6, Level::Low);
diff --git a/examples/nrf52840/src/bin/gpiote_channel.rs b/examples/nrf52840/src/bin/gpiote_channel.rs
index c7ddc1d8d..e358779b2 100644
--- a/examples/nrf52840/src/bin/gpiote_channel.rs
+++ b/examples/nrf52840/src/bin/gpiote_channel.rs
@@ -12,10 +12,10 @@ async fn main(_spawner: Spawner) {
12 let p = embassy_nrf::init(Default::default()); 12 let p = embassy_nrf::init(Default::default());
13 info!("Starting!"); 13 info!("Starting!");
14 14
15 let ch1 = InputChannel::new(p.GPIOTE_CH0, p.P0_11, Pull::Up, InputChannelPolarity::HiToLo); 15 let mut ch1 = InputChannel::new(p.GPIOTE_CH0, p.P0_11, Pull::Up, InputChannelPolarity::HiToLo);
16 let ch2 = InputChannel::new(p.GPIOTE_CH1, p.P0_12, Pull::Up, InputChannelPolarity::LoToHi); 16 let mut ch2 = InputChannel::new(p.GPIOTE_CH1, p.P0_12, Pull::Up, InputChannelPolarity::LoToHi);
17 let ch3 = InputChannel::new(p.GPIOTE_CH2, p.P0_24, Pull::Up, InputChannelPolarity::Toggle); 17 let mut ch3 = InputChannel::new(p.GPIOTE_CH2, p.P0_24, Pull::Up, InputChannelPolarity::Toggle);
18 let ch4 = InputChannel::new(p.GPIOTE_CH3, p.P0_25, Pull::Up, InputChannelPolarity::Toggle); 18 let mut ch4 = InputChannel::new(p.GPIOTE_CH3, p.P0_25, Pull::Up, InputChannelPolarity::Toggle);
19 19
20 let button1 = async { 20 let button1 = async {
21 loop { 21 loop {
diff --git a/examples/nrf5340/src/bin/gpiote_channel.rs b/examples/nrf5340/src/bin/gpiote_channel.rs
index a085310ce..41ee732c3 100644
--- a/examples/nrf5340/src/bin/gpiote_channel.rs
+++ b/examples/nrf5340/src/bin/gpiote_channel.rs
@@ -12,10 +12,10 @@ async fn main(_spawner: Spawner) {
12 let p = embassy_nrf::init(Default::default()); 12 let p = embassy_nrf::init(Default::default());
13 info!("Starting!"); 13 info!("Starting!");
14 14
15 let ch1 = InputChannel::new(p.GPIOTE_CH0, p.P0_23, Pull::Up, InputChannelPolarity::HiToLo); 15 let mut ch1 = InputChannel::new(p.GPIOTE_CH0, p.P0_23, Pull::Up, InputChannelPolarity::HiToLo);
16 let ch2 = InputChannel::new(p.GPIOTE_CH1, p.P0_24, Pull::Up, InputChannelPolarity::LoToHi); 16 let mut ch2 = InputChannel::new(p.GPIOTE_CH1, p.P0_24, Pull::Up, InputChannelPolarity::LoToHi);
17 let ch3 = InputChannel::new(p.GPIOTE_CH2, p.P0_08, Pull::Up, InputChannelPolarity::Toggle); 17 let mut ch3 = InputChannel::new(p.GPIOTE_CH2, p.P0_08, Pull::Up, InputChannelPolarity::Toggle);
18 let ch4 = InputChannel::new(p.GPIOTE_CH3, p.P0_09, Pull::Up, InputChannelPolarity::Toggle); 18 let mut ch4 = InputChannel::new(p.GPIOTE_CH3, p.P0_09, Pull::Up, InputChannelPolarity::Toggle);
19 19
20 let button1 = async { 20 let button1 = async {
21 loop { 21 loop {
diff --git a/examples/nrf54l15/memory.x b/examples/nrf54l15/memory.x
index 1064c8a5c..332200828 100644
--- a/examples/nrf54l15/memory.x
+++ b/examples/nrf54l15/memory.x
@@ -1,5 +1,5 @@
1MEMORY 1MEMORY
2{ 2{
3 FLASH : ORIGIN = 0x00000000, LENGTH = 1536K 3 FLASH : ORIGIN = 0x00000000, LENGTH = 1524K
4 RAM : ORIGIN = 0x20000000, LENGTH = 256K 4 RAM : ORIGIN = 0x20000000, LENGTH = 256K
5} 5}
diff --git a/examples/nrf54l15/src/bin/gpiote_channel.rs b/examples/nrf54l15/src/bin/gpiote_channel.rs
index 6333250ba..cac8823f8 100644
--- a/examples/nrf54l15/src/bin/gpiote_channel.rs
+++ b/examples/nrf54l15/src/bin/gpiote_channel.rs
@@ -12,10 +12,10 @@ async fn main(_spawner: Spawner) {
12 let p = embassy_nrf::init(Default::default()); 12 let p = embassy_nrf::init(Default::default());
13 info!("Starting!"); 13 info!("Starting!");
14 14
15 let ch1 = InputChannel::new(p.GPIOTE20_CH0, p.P1_13, Pull::Up, InputChannelPolarity::HiToLo); 15 let mut ch1 = InputChannel::new(p.GPIOTE20_CH0, p.P1_13, Pull::Up, InputChannelPolarity::HiToLo);
16 let ch2 = InputChannel::new(p.GPIOTE20_CH1, p.P1_09, Pull::Up, InputChannelPolarity::LoToHi); 16 let mut ch2 = InputChannel::new(p.GPIOTE20_CH1, p.P1_09, Pull::Up, InputChannelPolarity::LoToHi);
17 let ch3 = InputChannel::new(p.GPIOTE20_CH2, p.P1_08, Pull::Up, InputChannelPolarity::Toggle); 17 let mut ch3 = InputChannel::new(p.GPIOTE20_CH2, p.P1_08, Pull::Up, InputChannelPolarity::Toggle);
18 let ch4 = InputChannel::new(p.GPIOTE30_CH0, p.P0_04, Pull::Up, InputChannelPolarity::Toggle); 18 let mut ch4 = InputChannel::new(p.GPIOTE30_CH0, p.P0_04, Pull::Up, InputChannelPolarity::Toggle);
19 19
20 let button1 = async { 20 let button1 = async {
21 loop { 21 loop {
diff --git a/examples/rp/src/bin/wifi_webrequest.rs b/examples/rp/src/bin/wifi_webrequest.rs
index b618d2b38..ce85f4b9a 100644
--- a/examples/rp/src/bin/wifi_webrequest.rs
+++ b/examples/rp/src/bin/wifi_webrequest.rs
@@ -1,9 +1,8 @@
1//! This example uses the RP Pico W board Wifi chip (cyw43). 1//! This example uses the RP Pico W board Wifi chip (cyw43).
2//! Connects to Wifi network and makes a web request to get the current time. 2//! Connects to Wifi network and makes a web request to httpbin.org.
3 3
4#![no_std] 4#![no_std]
5#![no_main] 5#![no_main]
6#![allow(async_fn_in_trait)]
7 6
8use core::str::from_utf8; 7use core::str::from_utf8;
9 8
@@ -20,11 +19,14 @@ use embassy_rp::gpio::{Level, Output};
20use embassy_rp::peripherals::{DMA_CH0, PIO0}; 19use embassy_rp::peripherals::{DMA_CH0, PIO0};
21use embassy_rp::pio::{InterruptHandler, Pio}; 20use embassy_rp::pio::{InterruptHandler, Pio};
22use embassy_time::{Duration, Timer}; 21use embassy_time::{Duration, Timer};
23use reqwless::client::{HttpClient, TlsConfig, TlsVerify}; 22use reqwless::client::HttpClient;
23// Uncomment these for TLS requests:
24// use reqwless::client::{HttpClient, TlsConfig, TlsVerify};
24use reqwless::request::Method; 25use reqwless::request::Method;
25use serde::Deserialize; 26use serde::Deserialize;
27use serde_json_core::from_slice;
26use static_cell::StaticCell; 28use static_cell::StaticCell;
27use {defmt_rtt as _, panic_probe as _, serde_json_core}; 29use {defmt_rtt as _, panic_probe as _};
28 30
29bind_interrupts!(struct Irqs { 31bind_interrupts!(struct Irqs {
30 PIO0_IRQ_0 => InterruptHandler<PIO0>; 32 PIO0_IRQ_0 => InterruptHandler<PIO0>;
@@ -119,64 +121,90 @@ async fn main(spawner: Spawner) {
119 // And now we can use it! 121 // And now we can use it!
120 122
121 loop { 123 loop {
122 let mut rx_buffer = [0; 8192]; 124 let mut rx_buffer = [0; 4096];
123 let mut tls_read_buffer = [0; 16640]; 125 // Uncomment these for TLS requests:
124 let mut tls_write_buffer = [0; 16640]; 126 // let mut tls_read_buffer = [0; 16640];
127 // let mut tls_write_buffer = [0; 16640];
125 128
126 let client_state = TcpClientState::<1, 1024, 1024>::new(); 129 let client_state = TcpClientState::<1, 4096, 4096>::new();
127 let tcp_client = TcpClient::new(stack, &client_state); 130 let tcp_client = TcpClient::new(stack, &client_state);
128 let dns_client = DnsSocket::new(stack); 131 let dns_client = DnsSocket::new(stack);
129 let tls_config = TlsConfig::new(seed, &mut tls_read_buffer, &mut tls_write_buffer, TlsVerify::None); 132 // Uncomment these for TLS requests:
133 // let tls_config = TlsConfig::new(seed, &mut tls_read_buffer, &mut tls_write_buffer, TlsVerify::None);
130 134
131 let mut http_client = HttpClient::new_with_tls(&tcp_client, &dns_client, tls_config); 135 // Using non-TLS HTTP for this example
132 let url = "https://worldtimeapi.org/api/timezone/Europe/Berlin"; 136 let mut http_client = HttpClient::new(&tcp_client, &dns_client);
133 // for non-TLS requests, use this instead: 137 let url = "http://httpbin.org/json";
134 // let mut http_client = HttpClient::new(&tcp_client, &dns_client); 138 // For TLS requests, use this instead:
135 // let url = "http://worldtimeapi.org/api/timezone/Europe/Berlin"; 139 // let mut http_client = HttpClient::new_with_tls(&tcp_client, &dns_client, tls_config);
140 // let url = "https://httpbin.org/json";
136 141
137 info!("connecting to {}", &url); 142 info!("connecting to {}", &url);
138 143
139 let mut request = match http_client.request(Method::GET, &url).await { 144 let mut request = match http_client.request(Method::GET, url).await {
140 Ok(req) => req, 145 Ok(req) => req,
141 Err(e) => { 146 Err(e) => {
142 error!("Failed to make HTTP request: {:?}", e); 147 error!("Failed to make HTTP request: {:?}", e);
143 return; // handle the error 148 Timer::after(Duration::from_secs(5)).await;
149 continue;
144 } 150 }
145 }; 151 };
146 152
147 let response = match request.send(&mut rx_buffer).await { 153 let response = match request.send(&mut rx_buffer).await {
148 Ok(resp) => resp, 154 Ok(resp) => resp,
149 Err(_e) => { 155 Err(e) => {
150 error!("Failed to send HTTP request"); 156 error!("Failed to send HTTP request: {:?}", e);
151 return; // handle the error; 157 Timer::after(Duration::from_secs(5)).await;
158 continue;
152 } 159 }
153 }; 160 };
154 161
155 let body = match from_utf8(response.body().read_to_end().await.unwrap()) { 162 info!("Response status: {}", response.status.0);
163
164 let body_bytes = match response.body().read_to_end().await {
156 Ok(b) => b, 165 Ok(b) => b,
157 Err(_e) => { 166 Err(_e) => {
158 error!("Failed to read response body"); 167 error!("Failed to read response body");
159 return; // handle the error 168 Timer::after(Duration::from_secs(5)).await;
169 continue;
170 }
171 };
172
173 let body = match from_utf8(body_bytes) {
174 Ok(b) => b,
175 Err(_e) => {
176 error!("Failed to parse response body as UTF-8");
177 Timer::after(Duration::from_secs(5)).await;
178 continue;
160 } 179 }
161 }; 180 };
162 info!("Response body: {:?}", &body); 181 info!("Response body length: {} bytes", body.len());
163 182
164 // parse the response body and update the RTC 183 // Parse the JSON response from httpbin.org/json
184 #[derive(Deserialize)]
185 struct SlideShow<'a> {
186 author: &'a str,
187 title: &'a str,
188 }
165 189
166 #[derive(Deserialize)] 190 #[derive(Deserialize)]
167 struct ApiResponse<'a> { 191 struct HttpBinResponse<'a> {
168 datetime: &'a str, 192 #[serde(borrow)]
169 // other fields as needed 193 slideshow: SlideShow<'a>,
170 } 194 }
171 195
172 let bytes = body.as_bytes(); 196 let bytes = body.as_bytes();
173 match serde_json_core::de::from_slice::<ApiResponse>(bytes) { 197 match from_slice::<HttpBinResponse>(bytes) {
174 Ok((output, _used)) => { 198 Ok((output, _used)) => {
175 info!("Datetime: {:?}", output.datetime); 199 info!("Successfully parsed JSON response!");
200 info!("Slideshow title: {:?}", output.slideshow.title);
201 info!("Slideshow author: {:?}", output.slideshow.author);
176 } 202 }
177 Err(_e) => { 203 Err(e) => {
178 error!("Failed to parse response body"); 204 error!("Failed to parse JSON response: {}", Debug2Format(&e));
179 return; // handle the error 205 // Log preview of response for debugging
206 let preview = if body.len() > 200 { &body[..200] } else { body };
207 info!("Response preview: {:?}", preview);
180 } 208 }
181 } 209 }
182 210
diff --git a/examples/stm32h5/src/bin/adc_dma.rs b/examples/stm32h5/src/bin/adc_dma.rs
index fb9fcbc5c..2138257f7 100644
--- a/examples/stm32h5/src/bin/adc_dma.rs
+++ b/examples/stm32h5/src/bin/adc_dma.rs
@@ -6,7 +6,7 @@ use embassy_executor::Spawner;
6use embassy_stm32::adc::{self, Adc, AdcChannel, RxDma, SampleTime}; 6use embassy_stm32::adc::{self, Adc, AdcChannel, RxDma, SampleTime};
7use embassy_stm32::peripherals::{ADC1, ADC2, GPDMA1_CH0, GPDMA1_CH1, PA0, PA1, PA2, PA3}; 7use embassy_stm32::peripherals::{ADC1, ADC2, GPDMA1_CH0, GPDMA1_CH1, PA0, PA1, PA2, PA3};
8use embassy_stm32::{Config, Peri}; 8use embassy_stm32::{Config, Peri};
9use embassy_time::Instant; 9use embassy_time::{Duration, Instant, Ticker};
10use {defmt_rtt as _, panic_probe as _}; 10use {defmt_rtt as _, panic_probe as _};
11 11
12#[embassy_executor::main] 12#[embassy_executor::main]
@@ -76,6 +76,9 @@ async fn adc_task<'a, T: adc::Instance>(
76 let mut pin1 = pin1.degrade_adc(); 76 let mut pin1 = pin1.degrade_adc();
77 let mut pin2 = pin2.degrade_adc(); 77 let mut pin2 = pin2.degrade_adc();
78 78
79 info!("adc init");
80
81 let mut ticker = Ticker::every(Duration::from_millis(500));
79 let mut tic = Instant::now(); 82 let mut tic = Instant::now();
80 let mut buffer = [0u16; 512]; 83 let mut buffer = [0u16; 512];
81 loop { 84 loop {
@@ -84,11 +87,13 @@ async fn adc_task<'a, T: adc::Instance>(
84 adc.read( 87 adc.read(
85 dma.reborrow(), 88 dma.reborrow(),
86 [(&mut pin1, SampleTime::CYCLES2_5), (&mut pin2, SampleTime::CYCLES2_5)].into_iter(), 89 [(&mut pin1, SampleTime::CYCLES2_5), (&mut pin2, SampleTime::CYCLES2_5)].into_iter(),
87 &mut buffer, 90 &mut buffer[0..2],
88 ) 91 )
89 .await; 92 .await;
90 let toc = Instant::now(); 93 let toc = Instant::now();
91 info!("\n adc1: {} dt = {}", buffer[0..16], (toc - tic).as_micros()); 94 info!("\n adc1: {} dt = {}", buffer[0..16], (toc - tic).as_micros());
92 tic = toc; 95 tic = toc;
96
97 ticker.next().await;
93 } 98 }
94} 99}
diff --git a/examples/stm32h5/src/bin/stop.rs b/examples/stm32h5/src/bin/stop.rs
index caebc9daf..8d5456b80 100644
--- a/examples/stm32h5/src/bin/stop.rs
+++ b/examples/stm32h5/src/bin/stop.rs
@@ -7,20 +7,12 @@
7use defmt::*; 7use defmt::*;
8use embassy_executor::Spawner; 8use embassy_executor::Spawner;
9use embassy_stm32::gpio::{AnyPin, Level, Output, Speed}; 9use embassy_stm32::gpio::{AnyPin, Level, Output, Speed};
10use embassy_stm32::low_power::Executor;
11use embassy_stm32::rcc::{HSIPrescaler, LsConfig}; 10use embassy_stm32::rcc::{HSIPrescaler, LsConfig};
12use embassy_stm32::{Config, Peri}; 11use embassy_stm32::{Config, Peri, low_power};
13use embassy_time::Timer; 12use embassy_time::Timer;
14use {defmt_rtt as _, panic_probe as _}; 13use {defmt_rtt as _, panic_probe as _};
15 14
16#[cortex_m_rt::entry] 15#[embassy_executor::main(executor = "low_power::Executor")]
17fn main() -> ! {
18 Executor::take().run(|spawner| {
19 spawner.spawn(unwrap!(async_main(spawner)));
20 })
21}
22
23#[embassy_executor::task]
24async fn async_main(spawner: Spawner) { 16async fn async_main(spawner: Spawner) {
25 defmt::info!("Program Start"); 17 defmt::info!("Program Start");
26 18
diff --git a/examples/stm32h723/src/bin/spdifrx.rs b/examples/stm32h723/src/bin/spdifrx.rs
index cdbd69b89..5c29602c6 100644
--- a/examples/stm32h723/src/bin/spdifrx.rs
+++ b/examples/stm32h723/src/bin/spdifrx.rs
@@ -167,7 +167,7 @@ fn new_sai_transmitter<'d>(
167 sai_config.slot_count = hal::sai::word::U4(CHANNEL_COUNT as u8); 167 sai_config.slot_count = hal::sai::word::U4(CHANNEL_COUNT as u8);
168 sai_config.slot_enable = 0xFFFF; // All slots 168 sai_config.slot_enable = 0xFFFF; // All slots
169 sai_config.data_size = sai::DataSize::Data32; 169 sai_config.data_size = sai::DataSize::Data32;
170 sai_config.frame_length = (CHANNEL_COUNT * 32) as u8; 170 sai_config.frame_length = (CHANNEL_COUNT * 32) as u16;
171 sai_config.master_clock_divider = None; 171 sai_config.master_clock_divider = None;
172 172
173 let (sub_block_tx, _) = hal::sai::split_subblocks(sai); 173 let (sub_block_tx, _) = hal::sai::split_subblocks(sai);
diff --git a/examples/stm32l5/src/bin/stop.rs b/examples/stm32l5/src/bin/stop.rs
index 3d119f90f..fde804fb7 100644
--- a/examples/stm32l5/src/bin/stop.rs
+++ b/examples/stm32l5/src/bin/stop.rs
@@ -4,20 +4,12 @@
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::gpio::{AnyPin, Level, Output, Speed}; 6use embassy_stm32::gpio::{AnyPin, Level, Output, Speed};
7use embassy_stm32::low_power::Executor;
8use embassy_stm32::rcc::LsConfig; 7use embassy_stm32::rcc::LsConfig;
9use embassy_stm32::{Config, Peri}; 8use embassy_stm32::{Config, Peri, low_power};
10use embassy_time::Timer; 9use embassy_time::Timer;
11use {defmt_rtt as _, panic_probe as _}; 10use {defmt_rtt as _, panic_probe as _};
12 11
13#[cortex_m_rt::entry] 12#[embassy_executor::main(executor = "low_power::Executor")]
14fn main() -> ! {
15 Executor::take().run(|spawner| {
16 spawner.spawn(unwrap!(async_main(spawner)));
17 })
18}
19
20#[embassy_executor::task]
21async fn async_main(spawner: Spawner) { 13async fn async_main(spawner: Spawner) {
22 let mut config = Config::default(); 14 let mut config = Config::default();
23 config.rcc.ls = LsConfig::default_lsi(); 15 config.rcc.ls = LsConfig::default_lsi();
diff --git a/examples/stm32wb/src/bin/mac_ffd_net.rs b/examples/stm32wb/src/bin/mac_ffd_net.rs
index 5296943a1..5d946b35b 100644
--- a/examples/stm32wb/src/bin/mac_ffd_net.rs
+++ b/examples/stm32wb/src/bin/mac_ffd_net.rs
@@ -1,32 +1,44 @@
1#![no_std] 1#![no_std]
2#![no_main] 2#![no_main]
3 3
4use core::net::Ipv6Addr;
5
4use defmt::*; 6use defmt::*;
5use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_net::udp::{PacketMetadata, UdpSocket};
9use embassy_net::{Ipv6Cidr, StackResources, StaticConfigV6};
6use embassy_stm32::bind_interrupts; 10use embassy_stm32::bind_interrupts;
7use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; 11use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler};
12use embassy_stm32::peripherals::RNG;
8use embassy_stm32::rcc::WPAN_DEFAULT; 13use embassy_stm32::rcc::WPAN_DEFAULT;
14use embassy_stm32::rng::InterruptHandler as RngInterruptHandler;
9use embassy_stm32_wpan::TlMbox; 15use embassy_stm32_wpan::TlMbox;
10use embassy_stm32_wpan::mac::commands::{ResetRequest, SetRequest, StartRequest}; 16use embassy_stm32_wpan::mac::{Driver, DriverState, Runner};
11use embassy_stm32_wpan::mac::typedefs::{MacChannel, PanId, PibId};
12use embassy_stm32_wpan::mac::{self, Runner};
13use embassy_stm32_wpan::sub::mm; 17use embassy_stm32_wpan::sub::mm;
18use embassy_time::{Duration, Timer};
19use heapless::Vec;
14use static_cell::StaticCell; 20use static_cell::StaticCell;
15use {defmt_rtt as _, panic_probe as _}; 21use {defmt_rtt as _, panic_probe as _};
16 22
17bind_interrupts!(struct Irqs{ 23bind_interrupts!(struct Irqs{
18 IPCC_C1_RX => ReceiveInterruptHandler; 24 IPCC_C1_RX => ReceiveInterruptHandler;
19 IPCC_C1_TX => TransmitInterruptHandler; 25 IPCC_C1_TX => TransmitInterruptHandler;
26 RNG => RngInterruptHandler<RNG>;
20}); 27});
21 28
22#[embassy_executor::task] 29#[embassy_executor::task]
23async fn run_mm_queue(memory_manager: mm::MemoryManager) { 30async fn run_mm_queue(memory_manager: mm::MemoryManager) -> ! {
24 memory_manager.run_queue().await; 31 memory_manager.run_queue().await
32}
33
34#[embassy_executor::task]
35async fn run_mac(runner: &'static Runner<'static>) -> ! {
36 runner.run().await
25} 37}
26 38
27#[embassy_executor::task] 39#[embassy_executor::task]
28async fn run_mac(runner: &'static Runner<'static>) { 40async fn run_net(mut runner: embassy_net::Runner<'static, Driver<'static>>) -> ! {
29 runner.run().await; 41 runner.run().await
30} 42}
31 43
32#[embassy_executor::main] 44#[embassy_executor::main]
@@ -72,106 +84,69 @@ async fn main(spawner: Spawner) {
72 let result = mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await; 84 let result = mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await;
73 info!("initialized mac: {}", result); 85 info!("initialized mac: {}", result);
74 86
75 info!("resetting"); 87 static DRIVER_STATE: StaticCell<DriverState> = StaticCell::new();
76 mbox.mac_subsystem
77 .send_command(&ResetRequest {
78 set_default_pib: true,
79 ..Default::default()
80 })
81 .await
82 .unwrap();
83 defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap());
84
85 info!("setting extended address");
86 let extended_address: u64 = 0xACDE480000000001;
87 mbox.mac_subsystem
88 .send_command(&SetRequest {
89 pib_attribute_ptr: &extended_address as *const _ as *const u8,
90 pib_attribute: PibId::ExtendedAddress,
91 })
92 .await
93 .unwrap();
94 defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap());
95
96 info!("setting short address");
97 let short_address: u16 = 0x1122;
98 mbox.mac_subsystem
99 .send_command(&SetRequest {
100 pib_attribute_ptr: &short_address as *const _ as *const u8,
101 pib_attribute: PibId::ShortAddress,
102 })
103 .await
104 .unwrap();
105 defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap());
106
107 info!("setting association permit");
108 let association_permit: bool = true;
109 mbox.mac_subsystem
110 .send_command(&SetRequest {
111 pib_attribute_ptr: &association_permit as *const _ as *const u8,
112 pib_attribute: PibId::AssociationPermit,
113 })
114 .await
115 .unwrap();
116 defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap());
117
118 info!("setting TX power");
119 let transmit_power: i8 = 2;
120 mbox.mac_subsystem
121 .send_command(&SetRequest {
122 pib_attribute_ptr: &transmit_power as *const _ as *const u8,
123 pib_attribute: PibId::TransmitPower,
124 })
125 .await
126 .unwrap();
127 defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap());
128
129 info!("starting FFD device");
130 mbox.mac_subsystem
131 .send_command(&StartRequest {
132 pan_id: PanId([0x1A, 0xAA]),
133 channel_number: MacChannel::Channel16,
134 beacon_order: 0x0F,
135 superframe_order: 0x0F,
136 pan_coordinator: true,
137 battery_life_extension: false,
138 ..Default::default()
139 })
140 .await
141 .unwrap();
142 defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap());
143
144 info!("setting RX on when idle");
145 let rx_on_while_idle: bool = true;
146 mbox.mac_subsystem
147 .send_command(&SetRequest {
148 pib_attribute_ptr: &rx_on_while_idle as *const _ as *const u8,
149 pib_attribute: PibId::RxOnWhenIdle,
150 })
151 .await
152 .unwrap();
153 defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap());
154
155 static TX1: StaticCell<[u8; 127]> = StaticCell::new();
156 static TX2: StaticCell<[u8; 127]> = StaticCell::new();
157 static TX3: StaticCell<[u8; 127]> = StaticCell::new();
158 static TX4: StaticCell<[u8; 127]> = StaticCell::new();
159 static TX5: StaticCell<[u8; 127]> = StaticCell::new();
160 let tx_queue = [
161 TX1.init([0u8; 127]),
162 TX2.init([0u8; 127]),
163 TX3.init([0u8; 127]),
164 TX4.init([0u8; 127]),
165 TX5.init([0u8; 127]),
166 ];
167
168 static RUNNER: StaticCell<Runner> = StaticCell::new(); 88 static RUNNER: StaticCell<Runner> = StaticCell::new();
169 let runner = RUNNER.init(Runner::new(mbox.mac_subsystem, tx_queue)); 89 static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();
90
91 let driver_state = DRIVER_STATE.init(DriverState::new(mbox.mac_subsystem));
92
93 let (driver, mac_runner, mut control) = Driver::new(
94 driver_state,
95 0x1122u16.to_be_bytes().try_into().unwrap(),
96 0xACDE480000000001u64.to_be_bytes().try_into().unwrap(),
97 );
98
99 // TODO: rng does not work for some reason
100 // Generate random seed.
101 // let mut rng = Rng::new(p.RNG, Irqs);
102 let seed = [0; 8];
103 // let _ = rng.async_fill_bytes(&mut seed).await;
104 let seed = u64::from_le_bytes(seed);
105
106 info!("seed generated");
107
108 // Init network stack
109 let ipv6_addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff);
110
111 let config = embassy_net::Config::ipv6_static(StaticConfigV6 {
112 address: Ipv6Cidr::new(ipv6_addr, 104),
113 gateway: None,
114 dns_servers: Vec::new(),
115 });
116
117 let (stack, eth_runner) = embassy_net::new(driver, config, RESOURCES.init(StackResources::new()), seed);
118
119 // wpan runner
120 spawner.spawn(run_mac(RUNNER.init(mac_runner)).unwrap());
121
122 // Launch network task
123 spawner.spawn(unwrap!(run_net(eth_runner)));
124
125 info!("Network task initialized");
126
127 control.init_link([0x1A, 0xAA]).await;
128
129 // Ensure DHCP configuration is up before trying connect
130 stack.wait_config_up().await;
131
132 info!("Network up");
133
134 // Then we can use it!
135 let mut rx_meta = [PacketMetadata::EMPTY];
136 let mut rx_buffer = [0; 4096];
137 let mut tx_meta = [PacketMetadata::EMPTY];
138 let mut tx_buffer = [0; 4096];
139
140 let mut socket = UdpSocket::new(stack, &mut rx_meta, &mut rx_buffer, &mut tx_meta, &mut tx_buffer);
141
142 let remote_endpoint = (Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2fb), 8000);
143
144 let send_buf = [0u8; 20];
170 145
171 spawner.spawn(run_mac(runner).unwrap()); 146 socket.bind((ipv6_addr, 8000)).unwrap();
147 socket.send_to(&send_buf, remote_endpoint).await.unwrap();
172 148
173 let (driver, control) = mac::new(runner).await; 149 Timer::after(Duration::from_secs(2)).await;
174 150
175 let _ = driver; 151 cortex_m::asm::bkpt();
176 let _ = control;
177} 152}
diff --git a/examples/stm32wle5/src/bin/adc.rs b/examples/stm32wle5/src/bin/adc.rs
index 4e0574d97..ea91fb063 100644
--- a/examples/stm32wle5/src/bin/adc.rs
+++ b/examples/stm32wle5/src/bin/adc.rs
@@ -6,20 +6,12 @@ use defmt::*;
6use defmt_rtt as _; 6use defmt_rtt as _;
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_stm32::adc::{Adc, SampleTime}; 8use embassy_stm32::adc::{Adc, SampleTime};
9use embassy_stm32::low_power::Executor; 9use embassy_stm32::low_power;
10use embassy_time::Timer; 10use embassy_time::Timer;
11use panic_probe as _; 11use panic_probe as _;
12use static_cell::StaticCell; 12use static_cell::StaticCell;
13 13
14#[cortex_m_rt::entry] 14#[embassy_executor::main(executor = "low_power::Executor")]
15fn main() -> ! {
16 info!("main: Starting!");
17 Executor::take().run(|spawner| {
18 spawner.spawn(unwrap!(async_main(spawner)));
19 });
20}
21
22#[embassy_executor::task]
23async fn async_main(_spawner: Spawner) { 15async fn async_main(_spawner: Spawner) {
24 let mut config = embassy_stm32::Config::default(); 16 let mut config = embassy_stm32::Config::default();
25 // enable HSI clock 17 // enable HSI clock
diff --git a/examples/stm32wle5/src/bin/blinky.rs b/examples/stm32wle5/src/bin/blinky.rs
index b2745fdaf..9f0c04672 100644
--- a/examples/stm32wle5/src/bin/blinky.rs
+++ b/examples/stm32wle5/src/bin/blinky.rs
@@ -6,20 +6,12 @@ use defmt::*;
6use defmt_rtt as _; 6use defmt_rtt as _;
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_stm32::gpio::{Level, Output, Speed}; 8use embassy_stm32::gpio::{Level, Output, Speed};
9use embassy_stm32::low_power::Executor; 9use embassy_stm32::low_power;
10use embassy_time::Timer; 10use embassy_time::Timer;
11use panic_probe as _; 11use panic_probe as _;
12use static_cell::StaticCell; 12use static_cell::StaticCell;
13 13
14#[cortex_m_rt::entry] 14#[embassy_executor::main(executor = "low_power::Executor")]
15fn main() -> ! {
16 info!("main: Starting!");
17 Executor::take().run(|spawner| {
18 spawner.spawn(unwrap!(async_main(spawner)));
19 });
20}
21
22#[embassy_executor::task]
23async fn async_main(_spawner: Spawner) { 15async fn async_main(_spawner: Spawner) {
24 let mut config = embassy_stm32::Config::default(); 16 let mut config = embassy_stm32::Config::default();
25 // enable HSI clock 17 // enable HSI clock
diff --git a/examples/stm32wle5/src/bin/button_exti.rs b/examples/stm32wle5/src/bin/button_exti.rs
index db1bff0be..878eca7d0 100644
--- a/examples/stm32wle5/src/bin/button_exti.rs
+++ b/examples/stm32wle5/src/bin/button_exti.rs
@@ -7,19 +7,11 @@ use defmt_rtt as _;
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_stm32::exti::ExtiInput; 8use embassy_stm32::exti::ExtiInput;
9use embassy_stm32::gpio::Pull; 9use embassy_stm32::gpio::Pull;
10use embassy_stm32::low_power::Executor; 10use embassy_stm32::low_power;
11use panic_probe as _; 11use panic_probe as _;
12use static_cell::StaticCell; 12use static_cell::StaticCell;
13 13
14#[cortex_m_rt::entry] 14#[embassy_executor::main(executor = "low_power::Executor")]
15fn main() -> ! {
16 info!("main: Starting!");
17 Executor::take().run(|spawner| {
18 spawner.spawn(unwrap!(async_main(spawner)));
19 });
20}
21
22#[embassy_executor::task]
23async fn async_main(_spawner: Spawner) { 15async fn async_main(_spawner: Spawner) {
24 let mut config = embassy_stm32::Config::default(); 16 let mut config = embassy_stm32::Config::default();
25 // enable HSI clock 17 // enable HSI clock
diff --git a/examples/stm32wle5/src/bin/i2c.rs b/examples/stm32wle5/src/bin/i2c.rs
index c31c673c9..68c17a672 100644
--- a/examples/stm32wle5/src/bin/i2c.rs
+++ b/examples/stm32wle5/src/bin/i2c.rs
@@ -6,9 +6,8 @@ use defmt::*;
6use defmt_rtt as _; 6use defmt_rtt as _;
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_stm32::i2c::I2c; 8use embassy_stm32::i2c::I2c;
9use embassy_stm32::low_power::Executor;
10use embassy_stm32::time::Hertz; 9use embassy_stm32::time::Hertz;
11use embassy_stm32::{bind_interrupts, i2c, peripherals}; 10use embassy_stm32::{bind_interrupts, i2c, low_power, peripherals};
12use embassy_time::{Duration, Timer}; 11use embassy_time::{Duration, Timer};
13use panic_probe as _; 12use panic_probe as _;
14use static_cell::StaticCell; 13use static_cell::StaticCell;
@@ -18,15 +17,7 @@ bind_interrupts!(struct IrqsI2C{
18 I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>; 17 I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
19}); 18});
20 19
21#[cortex_m_rt::entry] 20#[embassy_executor::main(executor = "low_power::Executor")]
22fn main() -> ! {
23 info!("main: Starting!");
24 Executor::take().run(|spawner| {
25 spawner.spawn(unwrap!(async_main(spawner)));
26 });
27}
28
29#[embassy_executor::task]
30async fn async_main(_spawner: Spawner) { 21async fn async_main(_spawner: Spawner) {
31 let mut config = embassy_stm32::Config::default(); 22 let mut config = embassy_stm32::Config::default();
32 // enable HSI clock 23 // enable HSI clock
diff --git a/tests/stm32/src/bin/stop.rs b/tests/stm32/src/bin/stop.rs
index 1fe65d867..83c375bc5 100644
--- a/tests/stm32/src/bin/stop.rs
+++ b/tests/stm32/src/bin/stop.rs
@@ -7,21 +7,13 @@ mod common;
7 7
8use chrono::NaiveDate; 8use chrono::NaiveDate;
9use common::*; 9use common::*;
10use cortex_m_rt::entry;
11use embassy_executor::Spawner; 10use embassy_executor::Spawner;
12use embassy_stm32::Config; 11use embassy_stm32::low_power::{StopMode, stop_ready};
13use embassy_stm32::low_power::{Executor, StopMode, stop_ready};
14use embassy_stm32::rcc::LsConfig; 12use embassy_stm32::rcc::LsConfig;
15use embassy_stm32::rtc::Rtc; 13use embassy_stm32::rtc::Rtc;
14use embassy_stm32::{Config, low_power};
16use embassy_time::Timer; 15use embassy_time::Timer;
17 16
18#[entry]
19fn main() -> ! {
20 Executor::take().run(|spawner| {
21 spawner.spawn(unwrap!(async_main(spawner)));
22 });
23}
24
25#[embassy_executor::task] 17#[embassy_executor::task]
26async fn task_1() { 18async fn task_1() {
27 for _ in 0..9 { 19 for _ in 0..9 {
@@ -43,7 +35,7 @@ async fn task_2() {
43 cortex_m::asm::bkpt(); 35 cortex_m::asm::bkpt();
44} 36}
45 37
46#[embassy_executor::task] 38#[embassy_executor::main(executor = "low_power::Executor")]
47async fn async_main(spawner: Spawner) { 39async fn async_main(spawner: Spawner) {
48 let _ = config(); 40 let _ = config();
49 41