aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-mcxa/src/clkout.rs8
-rw-r--r--embassy-mcxa/src/clocks/config.rs23
-rw-r--r--embassy-mcxa/src/clocks/mod.rs128
-rw-r--r--embassy-mcxa/src/gpio.rs20
-rw-r--r--embassy-mcxa/src/i2c/mod.rs7
-rw-r--r--embassy-mcxa/src/lib.rs24
-rw-r--r--embassy-stm32/Cargo.toml2
-rw-r--r--embassy-stm32/src/rtc/low_power.rs94
-rw-r--r--embassy-stm32/src/rtc/mod.rs16
-rw-r--r--embassy-stm32/src/time_driver.rs23
-rw-r--r--examples/mcxa/src/bin/clkout.rs84
-rw-r--r--tests/stm32/src/bin/stop.rs2
12 files changed, 284 insertions, 147 deletions
diff --git a/embassy-mcxa/src/clkout.rs b/embassy-mcxa/src/clkout.rs
index 5b21f24b0..3495eb886 100644
--- a/embassy-mcxa/src/clkout.rs
+++ b/embassy-mcxa/src/clkout.rs
@@ -20,6 +20,7 @@ pub struct ClockOut<'a> {
20} 20}
21 21
22/// Selected clock source to output 22/// Selected clock source to output
23#[derive(Copy, Clone)]
23pub enum ClockOutSel { 24pub enum ClockOutSel {
24 /// 12MHz Internal Oscillator 25 /// 12MHz Internal Oscillator
25 Fro12M, 26 Fro12M,
@@ -36,6 +37,7 @@ pub enum ClockOutSel {
36} 37}
37 38
38/// Configuration for the ClockOut 39/// Configuration for the ClockOut
40#[derive(Copy, Clone)]
39pub struct Config { 41pub struct Config {
40 /// Selected Source Clock 42 /// Selected Source Clock
41 pub sel: ClockOutSel, 43 pub sel: ClockOutSel,
@@ -157,6 +159,12 @@ mod sealed {
157 fn mux(&self) { 159 fn mux(&self) {
158 self.set_function(crate::pac::port0::pcr0::Mux::$func); 160 self.set_function(crate::pac::port0::pcr0::Mux::$func);
159 self.set_pull(Pull::Disabled); 161 self.set_pull(Pull::Disabled);
162
163 // TODO: we may want to expose these as options to allow the slew rate
164 // and drive strength for clocks if they are particularly high speed.
165 //
166 // self.set_drive_strength(crate::pac::port0::pcr0::Dse::Dse1);
167 // self.set_slew_rate(crate::pac::port0::pcr0::Sre::Sre0);
160 } 168 }
161 } 169 }
162 }; 170 };
diff --git a/embassy-mcxa/src/clocks/config.rs b/embassy-mcxa/src/clocks/config.rs
index 0563b8917..9f97160ff 100644
--- a/embassy-mcxa/src/clocks/config.rs
+++ b/embassy-mcxa/src/clocks/config.rs
@@ -119,6 +119,28 @@ pub struct ClocksConfig {
119 pub sirc: SircConfig, 119 pub sirc: SircConfig,
120 /// FRO16K clock source 120 /// FRO16K clock source
121 pub fro16k: Option<Fro16KConfig>, 121 pub fro16k: Option<Fro16KConfig>,
122 /// SOSC, clk_in clock source
123 pub sosc: Option<SoscConfig>,
124}
125
126/// The mode of the external reference clock
127#[derive(Copy, Clone)]
128pub enum SoscMode {
129 /// Passive crystal oscillators
130 CrystalOscillator,
131 /// Active external reference clock
132 ActiveClock,
133}
134
135// SOSC/clk_in configuration
136#[derive(Copy, Clone)]
137pub struct SoscConfig {
138 /// Mode of the external reference clock
139 pub mode: SoscMode,
140 /// Specific frequency of the external reference clock
141 pub frequency: u32,
142 /// Power state of the external reference clock
143 pub power: PoweredClock,
122} 144}
123 145
124// FIRC/FRO180M 146// FIRC/FRO180M
@@ -199,6 +221,7 @@ impl Default for ClocksConfig {
199 vsys_domain_active: true, 221 vsys_domain_active: true,
200 vdd_core_domain_active: true, 222 vdd_core_domain_active: true,
201 }), 223 }),
224 sosc: None,
202 } 225 }
203 } 226 }
204} 227}
diff --git a/embassy-mcxa/src/clocks/mod.rs b/embassy-mcxa/src/clocks/mod.rs
index 037f0a656..b41a1ba46 100644
--- a/embassy-mcxa/src/clocks/mod.rs
+++ b/embassy-mcxa/src/clocks/mod.rs
@@ -87,6 +87,7 @@ pub fn init(settings: ClocksConfig) -> Result<(), ClockError> {
87 operator.configure_firc_clocks()?; 87 operator.configure_firc_clocks()?;
88 operator.configure_sirc_clocks()?; 88 operator.configure_sirc_clocks()?;
89 operator.configure_fro16k_clocks()?; 89 operator.configure_fro16k_clocks()?;
90 operator.configure_sosc()?;
90 91
91 // For now, just use FIRC as the main/cpu clock, which should already be 92 // For now, just use FIRC as the main/cpu clock, which should already be
92 // the case on reset 93 // the case on reset
@@ -136,6 +137,7 @@ pub fn with_clocks<R: 'static, F: FnOnce(&Clocks) -> R>(f: F) -> Option<R> {
136#[non_exhaustive] 137#[non_exhaustive]
137pub struct Clocks { 138pub struct Clocks {
138 /// The `clk_in` is a clock provided by an external oscillator 139 /// The `clk_in` is a clock provided by an external oscillator
140 /// AKA SOSC
139 pub clk_in: Option<Clock>, 141 pub clk_in: Option<Clock>,
140 142
141 // FRO180M stuff 143 // FRO180M stuff
@@ -485,8 +487,20 @@ impl Clocks {
485 } 487 }
486 488
487 /// Ensure the `clk_in` clock is active and valid at the given power state. 489 /// Ensure the `clk_in` clock is active and valid at the given power state.
488 pub fn ensure_clk_in_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> { 490 pub fn ensure_clk_in_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
489 Err(ClockError::NotImplemented { clock: "clk_in" }) 491 let Some(clk) = self.clk_in.as_ref() else {
492 return Err(ClockError::BadConfig {
493 clock: "clk_in",
494 reason: "required but not active",
495 });
496 };
497 if !clk.power.meets_requirement_of(at_level) {
498 return Err(ClockError::BadConfig {
499 clock: "clk_in",
500 reason: "not low power active",
501 });
502 }
503 Ok(clk.frequency)
490 } 504 }
491 505
492 /// Ensure the `clk_16k_vsys` clock is active and valid at the given power state. 506 /// Ensure the `clk_16k_vsys` clock is active and valid at the given power state.
@@ -851,6 +865,116 @@ impl ClockOperator<'_> {
851 865
852 Ok(()) 866 Ok(())
853 } 867 }
868
869 /// Configure the SOSC/clk_in oscillator
870 fn configure_sosc(&mut self) -> Result<(), ClockError> {
871 let Some(parts) = self.config.sosc.as_ref() else {
872 return Ok(());
873 };
874
875 let scg0 = unsafe { pac::Scg0::steal() };
876
877 // TODO: Config for the LDO? For now, if we have Sosc, just enable
878 // using the default settings:
879 // LDOBYPASS: 0/not bypassed
880 // VOUT_SEL: 0b100: 1.1v
881 // LDOEN: 0/Disabled
882 scg0.ldocsr().modify(|_r, w| w.ldoen().enabled());
883 while scg0.ldocsr().read().vout_ok().is_disabled() {}
884
885 // TODO: something something pins? This seems to work when the pins are
886 // not enabled, even if GPIO hasn't been initialized at all yet.
887 let eref = match parts.mode {
888 config::SoscMode::CrystalOscillator => pac::scg0::sosccfg::Erefs::Internal,
889 config::SoscMode::ActiveClock => pac::scg0::sosccfg::Erefs::External,
890 };
891 let freq = parts.frequency;
892
893 // TODO: Fix PAC names here
894 //
895 // #[doc = "0: Frequency range select of 8-16 MHz."]
896 // Freq16to20mhz = 0,
897 // #[doc = "1: Frequency range select of 16-25 MHz."]
898 // LowFreq = 1,
899 // #[doc = "2: Frequency range select of 25-40 MHz."]
900 // MediumFreq = 2,
901 // #[doc = "3: Frequency range select of 40-50 MHz."]
902 // HighFreq = 3,
903 let range = match freq {
904 0..8_000_000 => {
905 return Err(ClockError::BadConfig {
906 clock: "clk_in",
907 reason: "freq too low",
908 });
909 }
910 8_000_000..16_000_000 => pac::scg0::sosccfg::Range::Freq16to20mhz,
911 16_000_000..25_000_000 => pac::scg0::sosccfg::Range::LowFreq,
912 25_000_000..40_000_000 => pac::scg0::sosccfg::Range::MediumFreq,
913 40_000_000..50_000_001 => pac::scg0::sosccfg::Range::HighFreq,
914 50_000_001.. => {
915 return Err(ClockError::BadConfig {
916 clock: "clk_in",
917 reason: "freq too high",
918 });
919 }
920 };
921
922 // Set source/erefs and range
923 scg0.sosccfg().modify(|_r, w| {
924 w.erefs().variant(eref);
925 w.range().variant(range);
926 w
927 });
928
929 // Disable lock
930 scg0.sosccsr().modify(|_r, w| w.lk().clear_bit());
931
932 // TODO: We could enable the SOSC clock monitor. There are some things to
933 // figure out first:
934 //
935 // * This requires SIRC to be enabled, not sure which branch. Maybe fro12m_root?
936 // * If SOSC needs to work in deep sleep, AND the monitor is enabled:
937 // * SIRC also need needs to be low power
938 // * We need to decide if we need an interrupt or a reset if the monitor trips
939
940 // Apply remaining config
941 scg0.sosccsr().modify(|_r, w| {
942 // For now, just disable the monitor. See above.
943 w.sosccm().disabled();
944
945 // Set deep sleep mode
946 match parts.power {
947 PoweredClock::NormalEnabledDeepSleepDisabled => {
948 w.soscsten().clear_bit();
949 }
950 PoweredClock::AlwaysEnabled => {
951 w.soscsten().set_bit();
952 }
953 }
954
955 // Enable SOSC
956 w.soscen().enabled()
957 });
958
959 // Wait for SOSC to be valid, check for errors
960 while !scg0.sosccsr().read().soscvld().bit_is_set() {}
961 if scg0.sosccsr().read().soscerr().is_enabled_and_error() {
962 return Err(ClockError::BadConfig {
963 clock: "clk_in",
964 reason: "soscerr is set",
965 });
966 }
967
968 // Re-lock the sosc
969 scg0.sosccsr().modify(|_r, w| w.lk().set_bit());
970
971 self.clocks.clk_in = Some(Clock {
972 frequency: freq,
973 power: parts.power,
974 });
975
976 Ok(())
977 }
854} 978}
855 979
856// 980//
diff --git a/embassy-mcxa/src/gpio.rs b/embassy-mcxa/src/gpio.rs
index 65f8df985..29d66656d 100644
--- a/embassy-mcxa/src/gpio.rs
+++ b/embassy-mcxa/src/gpio.rs
@@ -81,7 +81,7 @@ fn GPIO4() {
81 irq_handler(4, crate::pac::Gpio4::ptr()); 81 irq_handler(4, crate::pac::Gpio4::ptr());
82} 82}
83 83
84pub(crate) unsafe fn init() { 84pub(crate) unsafe fn interrupt_init() {
85 use embassy_hal_internal::interrupt::InterruptExt; 85 use embassy_hal_internal::interrupt::InterruptExt;
86 86
87 crate::pac::interrupt::GPIO0.enable(); 87 crate::pac::interrupt::GPIO0.enable();
@@ -320,8 +320,12 @@ impl GpioPin for AnyPin {}
320 320
321macro_rules! impl_pin { 321macro_rules! impl_pin {
322 ($peri:ident, $port:expr, $pin:expr, $block:ident) => { 322 ($peri:ident, $port:expr, $pin:expr, $block:ident) => {
323 impl_pin!(crate::peripherals, $peri, $port, $pin, $block);
324 };
325
326 ($perip:path, $peri:ident, $port:expr, $pin:expr, $block:ident) => {
323 paste! { 327 paste! {
324 impl SealedPin for crate::peripherals::$peri { 328 impl SealedPin for $perip::$peri {
325 fn pin_port(&self) -> usize { 329 fn pin_port(&self) -> usize {
326 $port * 32 + $pin 330 $port * 32 + $pin
327 } 331 }
@@ -372,15 +376,15 @@ macro_rules! impl_pin {
372 } 376 }
373 } 377 }
374 378
375 impl GpioPin for crate::peripherals::$peri {} 379 impl GpioPin for $perip::$peri {}
376 380
377 impl From<crate::peripherals::$peri> for AnyPin { 381 impl From<$perip::$peri> for AnyPin {
378 fn from(value: crate::peripherals::$peri) -> Self { 382 fn from(value: $perip::$peri) -> Self {
379 value.degrade() 383 value.degrade()
380 } 384 }
381 } 385 }
382 386
383 impl crate::peripherals::$peri { 387 impl $perip::$peri {
384 /// Convenience helper to obtain a type-erased handle to this pin. 388 /// Convenience helper to obtain a type-erased handle to this pin.
385 pub fn degrade(&self) -> AnyPin { 389 pub fn degrade(&self) -> AnyPin {
386 AnyPin::new(self.port(), self.pin(), self.gpio(), self.port_reg(), self.pcr_reg()) 390 AnyPin::new(self.port(), self.pin(), self.gpio(), self.port_reg(), self.pcr_reg())
@@ -453,8 +457,8 @@ impl_pin!(P1_26, 1, 26, Gpio1);
453impl_pin!(P1_27, 1, 27, Gpio1); 457impl_pin!(P1_27, 1, 27, Gpio1);
454impl_pin!(P1_28, 1, 28, Gpio1); 458impl_pin!(P1_28, 1, 28, Gpio1);
455impl_pin!(P1_29, 1, 29, Gpio1); 459impl_pin!(P1_29, 1, 29, Gpio1);
456impl_pin!(P1_30, 1, 30, Gpio1); 460impl_pin!(crate::internal_peripherals, P1_30, 1, 30, Gpio1);
457impl_pin!(P1_31, 1, 31, Gpio1); 461impl_pin!(crate::internal_peripherals, P1_31, 1, 31, Gpio1);
458 462
459impl_pin!(P2_0, 2, 0, Gpio2); 463impl_pin!(P2_0, 2, 0, Gpio2);
460impl_pin!(P2_1, 2, 1, Gpio2); 464impl_pin!(P2_1, 2, 1, Gpio2);
diff --git a/embassy-mcxa/src/i2c/mod.rs b/embassy-mcxa/src/i2c/mod.rs
index 9a014224a..55c933f71 100644
--- a/embassy-mcxa/src/i2c/mod.rs
+++ b/embassy-mcxa/src/i2c/mod.rs
@@ -180,8 +180,11 @@ impl_pin!(P1_12, LPI2C1, Mux2, SdaPin);
180impl_pin!(P1_13, LPI2C1, Mux2, SclPin); 180impl_pin!(P1_13, LPI2C1, Mux2, SclPin);
181impl_pin!(P1_14, LPI2C1, Mux2, SclPin); 181impl_pin!(P1_14, LPI2C1, Mux2, SclPin);
182impl_pin!(P1_15, LPI2C1, Mux2, SdaPin); 182impl_pin!(P1_15, LPI2C1, Mux2, SdaPin);
183impl_pin!(P1_30, LPI2C0, Mux3, SdaPin); 183// NOTE: P1_30 and P1_31 are typically used for the external oscillator
184impl_pin!(P1_31, LPI2C0, Mux3, SclPin); 184// For now, we just don't give users these pins.
185//
186// impl_pin!(P1_30, LPI2C0, Mux3, SdaPin);
187// impl_pin!(P1_31, LPI2C0, Mux3, SclPin);
185impl_pin!(P3_27, LPI2C3, Mux2, SclPin); 188impl_pin!(P3_27, LPI2C3, Mux2, SclPin);
186impl_pin!(P3_28, LPI2C3, Mux2, SdaPin); 189impl_pin!(P3_28, LPI2C3, Mux2, SdaPin);
187// impl_pin!(P3_29, LPI2C3, Mux2, HreqPin); What is this HREQ pin? 190// impl_pin!(P3_29, LPI2C3, Mux2, HreqPin); What is this HREQ pin?
diff --git a/embassy-mcxa/src/lib.rs b/embassy-mcxa/src/lib.rs
index 6383353db..76fd58210 100644
--- a/embassy-mcxa/src/lib.rs
+++ b/embassy-mcxa/src/lib.rs
@@ -175,8 +175,18 @@ embassy_hal_internal::peripherals!(
175 P1_27, 175 P1_27,
176 P1_28, 176 P1_28,
177 P1_29, 177 P1_29,
178 P1_30, 178 // TODO: These pins are optionally used as the clock sources for SOSC.
179 P1_31, 179 // Ideally, we'd want to have a custom version of the `peripheral!` macro
180 // that presented these as `Option<Peri<'_, P1_30>>` instead of `Peri<'_, P1_30>`
181 // when the user DOES enable the external SOSC. For now, I'm guessing MOST designs
182 // will have an external clock sitting on these pins anyway, so we just notch them
183 // out from the `Peripherals` struct given to users.
184 //
185 // If you find this and want your extra two pins to be available: please open an
186 // embassy issue to discuss how we could do this.
187 //
188 // P1_30,
189 // P1_31,
180 190
181 P2_0, 191 P2_0,
182 P2_1, 192 P2_1,
@@ -337,6 +347,14 @@ embassy_hal_internal::peripherals!(
337 WWDT0, 347 WWDT0,
338); 348);
339 349
350// See commented out items above to understand why we create the instances
351// here but don't give them to the user.
352pub(crate) mod internal_peripherals {
353 embassy_hal_internal::peripherals_definition!(P1_30, P1_31,);
354
355 pub(crate) use peripherals::*;
356}
357
340// Use cortex-m-rt's #[interrupt] attribute directly; PAC does not re-export it. 358// Use cortex-m-rt's #[interrupt] attribute directly; PAC does not re-export it.
341 359
342// Re-export interrupt traits and types 360// Re-export interrupt traits and types
@@ -369,7 +387,7 @@ pub fn init(cfg: crate::config::Config) -> Peripherals {
369 crate::clocks::init(cfg.clock_cfg).unwrap(); 387 crate::clocks::init(cfg.clock_cfg).unwrap();
370 388
371 unsafe { 389 unsafe {
372 crate::gpio::init(); 390 crate::gpio::interrupt_init();
373 } 391 }
374 392
375 // Initialize DMA controller (clock, reset, configuration) 393 // Initialize DMA controller (clock, reset, configuration)
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index 880df5f33..ce6fabc6b 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -241,7 +241,7 @@ log = ["dep:log"]
241chrono = ["dep:chrono"] 241chrono = ["dep:chrono"]
242 242
243exti = [] 243exti = []
244low-power = [ "dep:embassy-executor", "time" ] 244low-power = [ "dep:embassy-executor", "time", "chrono" ]
245low-power-pender = [ "low-power" ] 245low-power-pender = [ "low-power" ]
246low-power-debug-with-sleep = [ "low-power" ] 246low-power-debug-with-sleep = [ "low-power" ]
247 247
diff --git a/embassy-stm32/src/rtc/low_power.rs b/embassy-stm32/src/rtc/low_power.rs
index f049d6b12..cd5cea081 100644
--- a/embassy-stm32/src/rtc/low_power.rs
+++ b/embassy-stm32/src/rtc/low_power.rs
@@ -1,64 +1,12 @@
1#[cfg(feature = "time")] 1use chrono::{DateTime, NaiveDateTime, TimeDelta, Utc};
2use embassy_time::{Duration, TICK_HZ}; 2use embassy_time::{Duration, Instant, TICK_HZ};
3 3
4use super::{DateTimeError, Rtc, RtcError, bcd2_to_byte}; 4use super::Rtc;
5use crate::interrupt::typelevel::Interrupt; 5use crate::interrupt::typelevel::Interrupt;
6use crate::pac::rtc::vals::Wucksel; 6use crate::pac::rtc::vals::Wucksel;
7use crate::peripherals::RTC; 7use crate::peripherals::RTC;
8use crate::rtc::{RtcTimeProvider, SealedInstance}; 8use crate::rtc::{RtcTimeProvider, SealedInstance};
9 9
10/// Represents an instant in time that can be substracted to compute a duration
11pub(super) struct RtcInstant {
12 /// 0..59
13 second: u8,
14 /// 0..256
15 subsecond: u16,
16}
17
18impl RtcInstant {
19 #[cfg(not(rtc_v2_f2))]
20 const fn from(second: u8, subsecond: u16) -> Result<Self, DateTimeError> {
21 if second > 59 {
22 Err(DateTimeError::InvalidSecond)
23 } else {
24 Ok(Self { second, subsecond })
25 }
26 }
27}
28
29#[cfg(feature = "defmt")]
30impl defmt::Format for RtcInstant {
31 fn format(&self, fmt: defmt::Formatter) {
32 defmt::write!(
33 fmt,
34 "{}:{}",
35 self.second,
36 RTC::regs().prer().read().prediv_s() - self.subsecond,
37 )
38 }
39}
40
41#[cfg(feature = "time")]
42impl core::ops::Sub for RtcInstant {
43 type Output = embassy_time::Duration;
44
45 fn sub(self, rhs: Self) -> Self::Output {
46 let second = if self.second < rhs.second {
47 self.second + 60
48 } else {
49 self.second
50 };
51
52 let psc = RTC::regs().prer().read().prediv_s() as u32;
53
54 let self_ticks = second as u32 * (psc + 1) + (psc - self.subsecond as u32);
55 let other_ticks = rhs.second as u32 * (psc + 1) + (psc - rhs.subsecond as u32);
56 let rtc_ticks = self_ticks - other_ticks;
57
58 Duration::from_ticks(((rtc_ticks * TICK_HZ as u32) / (psc + 1)) as u64)
59 }
60}
61
62fn wucksel_compute_min(val: u32) -> (Wucksel, u32) { 10fn wucksel_compute_min(val: u32) -> (Wucksel, u32) {
63 *[ 11 *[
64 (Wucksel::DIV2, 2), 12 (Wucksel::DIV2, 2),
@@ -72,22 +20,15 @@ fn wucksel_compute_min(val: u32) -> (Wucksel, u32) {
72} 20}
73 21
74impl Rtc { 22impl Rtc {
75 /// Return the current instant. 23 pub(super) fn calc_epoch(&self) -> DateTime<Utc> {
76 fn instant(&self) -> Result<RtcInstant, RtcError> { 24 let now: NaiveDateTime = RtcTimeProvider::new().now().unwrap().into();
77 RtcTimeProvider::new().read(|_, tr, ss| {
78 let second = bcd2_to_byte((tr.st(), tr.su()));
79 25
80 RtcInstant::from(second, ss).map_err(RtcError::InvalidDateTime) 26 now.and_utc() - TimeDelta::microseconds(Instant::now().as_micros().try_into().unwrap())
81 })
82 } 27 }
83 28
84 /// start the wakeup alarm and with a duration that is as close to but less than 29 /// start the wakeup alarm and with a duration that is as close to but less than
85 /// the requested duration, and record the instant the wakeup alarm was started 30 /// the requested duration, and record the instant the wakeup alarm was started
86 pub(crate) fn start_wakeup_alarm( 31 pub(crate) fn start_wakeup_alarm(&mut self, requested_duration: embassy_time::Duration) {
87 &mut self,
88 requested_duration: embassy_time::Duration,
89 cs: critical_section::CriticalSection,
90 ) {
91 // Panic if the rcc mod knows we're not using low-power rtc 32 // Panic if the rcc mod knows we're not using low-power rtc
92 #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] 33 #[cfg(any(rcc_wb, rcc_f4, rcc_f410))]
93 unsafe { crate::rcc::get_freqs() }.rtc.to_hertz().unwrap(); 34 unsafe { crate::rcc::get_freqs() }.rtc.to_hertz().unwrap();
@@ -122,27 +63,19 @@ impl Rtc {
122 regs.cr().modify(|w| w.set_wutie(true)); 63 regs.cr().modify(|w| w.set_wutie(true));
123 }); 64 });
124 65
125 let instant = self.instant().unwrap();
126 trace!( 66 trace!(
127 "rtc: start wakeup alarm for {} ms (psc: {}, ticks: {}) at {}", 67 "rtc: start wakeup alarm for {} ms (psc: {}, ticks: {})",
128 Duration::from_ticks(rtc_ticks as u64 * TICK_HZ * prescaler as u64 / rtc_hz).as_millis(), 68 Duration::from_ticks(rtc_ticks as u64 * TICK_HZ * prescaler as u64 / rtc_hz).as_millis(),
129 prescaler as u32, 69 prescaler as u32,
130 rtc_ticks, 70 rtc_ticks,
131 instant,
132 ); 71 );
133
134 assert!(self.stop_time.borrow(cs).replace(Some(instant)).is_none())
135 } 72 }
136 73
137 /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm` 74 /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm`
138 /// was called, otherwise none 75 /// was called, otherwise none
139 pub(crate) fn stop_wakeup_alarm( 76 pub(crate) fn stop_wakeup_alarm(&mut self) -> embassy_time::Instant {
140 &mut self,
141 cs: critical_section::CriticalSection,
142 ) -> Option<embassy_time::Duration> {
143 let instant = self.instant().unwrap();
144 if RTC::regs().cr().read().wute() { 77 if RTC::regs().cr().read().wute() {
145 trace!("rtc: stop wakeup alarm at {}", instant); 78 trace!("rtc: stop wakeup alarm");
146 79
147 self.write(false, |regs| { 80 self.write(false, |regs| {
148 regs.cr().modify(|w| w.set_wutie(false)); 81 regs.cr().modify(|w| w.set_wutie(false));
@@ -166,10 +99,13 @@ impl Rtc {
166 }); 99 });
167 } 100 }
168 101
169 self.stop_time.borrow(cs).take().map(|stop_time| instant - stop_time) 102 let datetime: NaiveDateTime = RtcTimeProvider::new().now().expect("failed to read now").into();
103 let offset = datetime.and_utc() - self.epoch;
104
105 Instant::from_micros(offset.num_microseconds().unwrap().try_into().unwrap())
170 } 106 }
171 107
172 pub(crate) fn enable_wakeup_line(&self) { 108 pub(super) fn enable_wakeup_line(&mut self) {
173 <RTC as crate::rtc::SealedInstance>::WakeupInterrupt::unpend(); 109 <RTC as crate::rtc::SealedInstance>::WakeupInterrupt::unpend();
174 unsafe { <RTC as crate::rtc::SealedInstance>::WakeupInterrupt::enable() }; 110 unsafe { <RTC as crate::rtc::SealedInstance>::WakeupInterrupt::enable() };
175 111
diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs
index e88bd7ab2..94ba13ae1 100644
--- a/embassy-stm32/src/rtc/mod.rs
+++ b/embassy-stm32/src/rtc/mod.rs
@@ -5,7 +5,7 @@ mod datetime;
5mod low_power; 5mod low_power;
6 6
7#[cfg(feature = "low-power")] 7#[cfg(feature = "low-power")]
8use core::cell::{Cell, RefCell, RefMut}; 8use core::cell::{RefCell, RefMut};
9#[cfg(feature = "low-power")] 9#[cfg(feature = "low-power")]
10use core::ops; 10use core::ops;
11 11
@@ -163,7 +163,7 @@ impl<'a> ops::DerefMut for RtcBorrow<'a> {
163/// RTC driver. 163/// RTC driver.
164pub struct Rtc { 164pub struct Rtc {
165 #[cfg(feature = "low-power")] 165 #[cfg(feature = "low-power")]
166 stop_time: Mutex<CriticalSectionRawMutex, Cell<Option<low_power::RtcInstant>>>, 166 epoch: chrono::DateTime<chrono::Utc>,
167 _private: (), 167 _private: (),
168} 168}
169 169
@@ -225,7 +225,7 @@ impl Rtc {
225 225
226 let mut this = Self { 226 let mut this = Self {
227 #[cfg(feature = "low-power")] 227 #[cfg(feature = "low-power")]
228 stop_time: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), 228 epoch: chrono::DateTime::from_timestamp_secs(0).unwrap(),
229 _private: (), 229 _private: (),
230 }; 230 };
231 231
@@ -243,7 +243,10 @@ impl Rtc {
243 } 243 }
244 244
245 #[cfg(feature = "low-power")] 245 #[cfg(feature = "low-power")]
246 this.enable_wakeup_line(); 246 {
247 this.enable_wakeup_line();
248 this.epoch = this.calc_epoch();
249 }
247 250
248 this 251 this
249 } 252 }
@@ -293,6 +296,11 @@ impl Rtc {
293 }); 296 });
294 }); 297 });
295 298
299 #[cfg(feature = "low-power")]
300 {
301 self.epoch = self.calc_epoch();
302 }
303
296 Ok(()) 304 Ok(())
297 } 305 }
298 306
diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs
index ed5d902bd..59ec58575 100644
--- a/embassy-stm32/src/time_driver.rs
+++ b/embassy-stm32/src/time_driver.rs
@@ -251,8 +251,8 @@ impl RtcDriver {
251 251
252 #[cfg(feature = "low-power")] 252 #[cfg(feature = "low-power")]
253 /// Add the given offset to the current time 253 /// Add the given offset to the current time
254 fn add_time(&self, offset: embassy_time::Duration, cs: CriticalSection) { 254 fn set_time(&self, instant: u64, cs: CriticalSection) {
255 let (period, counter) = calc_period_counter(self.now() + offset.as_ticks()); 255 let (period, counter) = calc_period_counter(core::cmp::max(self.now(), instant));
256 256
257 self.period.store(period, Ordering::SeqCst); 257 self.period.store(period, Ordering::SeqCst);
258 regs_gp16().cnt().write(|w| w.set_cnt(counter)); 258 regs_gp16().cnt().write(|w| w.set_cnt(counter));
@@ -269,10 +269,17 @@ impl RtcDriver {
269 #[cfg(feature = "low-power")] 269 #[cfg(feature = "low-power")]
270 /// Stop the wakeup alarm, if enabled, and add the appropriate offset 270 /// Stop the wakeup alarm, if enabled, and add the appropriate offset
271 fn stop_wakeup_alarm(&self, cs: CriticalSection) { 271 fn stop_wakeup_alarm(&self, cs: CriticalSection) {
272 if !regs_gp16().cr1().read().cen() 272 if !regs_gp16().cr1().read().cen() {
273 && let Some(offset) = self.rtc.borrow(cs).borrow_mut().as_mut().unwrap().stop_wakeup_alarm(cs) 273 self.set_time(
274 { 274 self.rtc
275 self.add_time(offset, cs); 275 .borrow(cs)
276 .borrow_mut()
277 .as_mut()
278 .unwrap()
279 .stop_wakeup_alarm()
280 .as_ticks(),
281 cs,
282 );
276 } 283 }
277 } 284 }
278 285
@@ -287,7 +294,7 @@ impl RtcDriver {
287 #[cfg(feature = "low-power")] 294 #[cfg(feature = "low-power")]
288 /// Set the rtc but panic if it's already been set 295 /// Set the rtc but panic if it's already been set
289 pub(crate) fn set_rtc(&self, cs: CriticalSection, mut rtc: Rtc) { 296 pub(crate) fn set_rtc(&self, cs: CriticalSection, mut rtc: Rtc) {
290 rtc.stop_wakeup_alarm(cs); 297 rtc.stop_wakeup_alarm();
291 298
292 assert!(self.rtc.borrow(cs).replace(Some(rtc)).is_none()); 299 assert!(self.rtc.borrow(cs).replace(Some(rtc)).is_none());
293 } 300 }
@@ -310,7 +317,7 @@ impl RtcDriver {
310 .borrow_mut() 317 .borrow_mut()
311 .as_mut() 318 .as_mut()
312 .unwrap() 319 .unwrap()
313 .start_wakeup_alarm(time_until_next_alarm, cs); 320 .start_wakeup_alarm(time_until_next_alarm);
314 321
315 regs_gp16().cr1().modify(|w| w.set_cen(false)); 322 regs_gp16().cr1().modify(|w| w.set_cen(false));
316 // save the count for the timer as its lost in STOP2 for stm32wlex 323 // save the count for the timer as its lost in STOP2 for stm32wlex
diff --git a/examples/mcxa/src/bin/clkout.rs b/examples/mcxa/src/bin/clkout.rs
index 1e52912d3..e6e6a2d3d 100644
--- a/examples/mcxa/src/bin/clkout.rs
+++ b/examples/mcxa/src/bin/clkout.rs
@@ -4,6 +4,7 @@
4use embassy_executor::Spawner; 4use embassy_executor::Spawner;
5use embassy_mcxa::clkout::{ClockOut, ClockOutSel, Config, Div4}; 5use embassy_mcxa::clkout::{ClockOut, ClockOutSel, Config, Div4};
6use embassy_mcxa::clocks::PoweredClock; 6use embassy_mcxa::clocks::PoweredClock;
7use embassy_mcxa::clocks::config::{SoscConfig, SoscMode};
7use embassy_mcxa::gpio::{DriveStrength, Level, Output, SlewRate}; 8use embassy_mcxa::gpio::{DriveStrength, Level, Output, SlewRate};
8use embassy_time::Timer; 9use embassy_time::Timer;
9use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; 10use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
@@ -11,58 +12,61 @@ use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _};
11/// Demonstrate CLKOUT, using Pin P4.2 12/// Demonstrate CLKOUT, using Pin P4.2
12#[embassy_executor::main] 13#[embassy_executor::main]
13async fn main(_spawner: Spawner) { 14async fn main(_spawner: Spawner) {
14 let p = hal::init(hal::config::Config::default()); 15 let mut cfg = hal::config::Config::default();
16 cfg.clock_cfg.sosc = Some(SoscConfig {
17 mode: SoscMode::CrystalOscillator,
18 frequency: 8_000_000,
19 power: PoweredClock::NormalEnabledDeepSleepDisabled,
20 });
21
22 let p = hal::init(cfg);
23
15 let mut pin = p.P4_2; 24 let mut pin = p.P4_2;
16 let mut clkout = p.CLKOUT; 25 let mut clkout = p.CLKOUT;
17 26
18 loop { 27 const K16_CONFIG: Config = Config {
19 defmt::info!("Set Low..."); 28 sel: ClockOutSel::Clk16K,
20 let mut output = Output::new(pin.reborrow(), Level::Low, DriveStrength::Normal, SlewRate::Slow); 29 div: Div4::no_div(),
21 Timer::after_millis(500).await; 30 level: PoweredClock::NormalEnabledDeepSleepDisabled,
31 };
32 const M4_CONFIG: Config = Config {
33 sel: ClockOutSel::Fro12M,
34 div: const { Div4::from_divisor(3).unwrap() },
35 level: PoweredClock::NormalEnabledDeepSleepDisabled,
36 };
37 const K512_CONFIG: Config = Config {
38 sel: ClockOutSel::ClkIn,
39 div: const { Div4::from_divisor(16).unwrap() },
40 level: PoweredClock::NormalEnabledDeepSleepDisabled,
41 };
22 42
43 let configs = [
44 ("16K -> /1 = 16K", K16_CONFIG),
45 ("12M -> /3 = 4M", M4_CONFIG),
46 ("8M -> /16 = 512K", K512_CONFIG),
47 ];
48
49 loop {
23 defmt::info!("Set High..."); 50 defmt::info!("Set High...");
24 output.set_high(); 51 let mut output = Output::new(pin.reborrow(), Level::High, DriveStrength::Normal, SlewRate::Slow);
25 Timer::after_millis(400).await; 52 Timer::after_millis(250).await;
26 53
27 defmt::info!("Set Low..."); 54 defmt::info!("Set Low...");
28 output.set_low(); 55 output.set_low();
29 Timer::after_millis(500).await; 56 Timer::after_millis(750).await;
30 57
31 defmt::info!("16k..."); 58 for (name, conf) in configs.iter() {
32 // Run Clock Out with the 16K clock 59 defmt::info!("Running {=str}", name);
33 let _clock_out = ClockOut::new(
34 clkout.reborrow(),
35 pin.reborrow(),
36 Config {
37 sel: ClockOutSel::Clk16K,
38 div: Div4::no_div(),
39 level: PoweredClock::NormalEnabledDeepSleepDisabled,
40 },
41 )
42 .unwrap();
43 60
44 Timer::after_millis(3000).await; 61 let _clock_out = ClockOut::new(clkout.reborrow(), pin.reborrow(), *conf).unwrap();
45
46 defmt::info!("Set Low...");
47 drop(_clock_out);
48 62
49 let _output = Output::new(pin.reborrow(), Level::Low, DriveStrength::Normal, SlewRate::Slow); 63 Timer::after_millis(3000).await;
50 Timer::after_millis(500).await;
51 64
52 // Run Clock Out with the 12M clock, divided by 3 65 defmt::info!("Set Low...");
53 defmt::info!("4M..."); 66 drop(_clock_out);
54 let _clock_out = ClockOut::new(
55 clkout.reborrow(),
56 pin.reborrow(),
57 Config {
58 sel: ClockOutSel::Fro12M,
59 div: const { Div4::from_divisor(3).unwrap() },
60 level: PoweredClock::NormalEnabledDeepSleepDisabled,
61 },
62 )
63 .unwrap();
64 67
65 // Let it run for 3 seconds... 68 let _output = Output::new(pin.reborrow(), Level::Low, DriveStrength::Normal, SlewRate::Slow);
66 Timer::after_millis(3000).await; 69 Timer::after_millis(500).await;
70 }
67 } 71 }
68} 72}
diff --git a/tests/stm32/src/bin/stop.rs b/tests/stm32/src/bin/stop.rs
index 83c375bc5..75dacbe7e 100644
--- a/tests/stm32/src/bin/stop.rs
+++ b/tests/stm32/src/bin/stop.rs
@@ -60,10 +60,12 @@ async fn async_main(spawner: Spawner) {
60 60
61 let (rtc, _time_provider) = Rtc::new(p.RTC); 61 let (rtc, _time_provider) = Rtc::new(p.RTC);
62 62
63 info!("set datetime");
63 critical_section::with(|cs| { 64 critical_section::with(|cs| {
64 rtc.borrow_mut(cs).set_datetime(now.into()).expect("datetime not set"); 65 rtc.borrow_mut(cs).set_datetime(now.into()).expect("datetime not set");
65 }); 66 });
66 67
68 info!("spawn");
67 spawner.spawn(task_1().unwrap()); 69 spawner.spawn(task_1().unwrap());
68 spawner.spawn(task_2().unwrap()); 70 spawner.spawn(task_2().unwrap());
69} 71}