diff options
| author | xoviat <[email protected]> | 2023-08-22 21:50:53 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-08-22 21:50:53 +0000 |
| commit | 7bff2ebab3b36cc922505e9db961840109c509ed (patch) | |
| tree | d80a7b263fa8e500dec602d8be6d4224414b4c5a | |
| parent | b3212ae383e2cb4f167cbe9361b64bf83bf89049 (diff) | |
| parent | 048bdf6968773a642c60ddcac46958d3a54af7c3 (diff) | |
Merge pull request #1766 from xoviat/rtc-w
stm32/rtc: add start/stop wakeup
| -rw-r--r-- | embassy-stm32/src/rcc/f4.rs | 7 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/mod.rs | 2 | ||||
| -rw-r--r-- | embassy-stm32/src/rtc/mod.rs | 1 | ||||
| -rw-r--r-- | embassy-stm32/src/rtc/v2.rs | 218 |
4 files changed, 227 insertions, 1 deletions
diff --git a/embassy-stm32/src/rcc/f4.rs b/embassy-stm32/src/rcc/f4.rs index 2ae0d15cb..ee9cb2897 100644 --- a/embassy-stm32/src/rcc/f4.rs +++ b/embassy-stm32/src/rcc/f4.rs | |||
| @@ -473,6 +473,11 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 473 | Rtc::set_clock_source(clock_source); | 473 | Rtc::set_clock_source(clock_source); |
| 474 | }); | 474 | }); |
| 475 | 475 | ||
| 476 | let rtc = match config.rtc { | ||
| 477 | Some(RtcClockSource::LSI) => Some(LSI_FREQ), | ||
| 478 | _ => None, | ||
| 479 | }; | ||
| 480 | |||
| 476 | set_freqs(Clocks { | 481 | set_freqs(Clocks { |
| 477 | sys: Hertz(sysclk), | 482 | sys: Hertz(sysclk), |
| 478 | apb1: Hertz(pclk1), | 483 | apb1: Hertz(pclk1), |
| @@ -492,6 +497,8 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 492 | 497 | ||
| 493 | #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))] | 498 | #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))] |
| 494 | pllsai: None, | 499 | pllsai: None, |
| 500 | |||
| 501 | rtc: rtc, | ||
| 495 | }); | 502 | }); |
| 496 | } | 503 | } |
| 497 | 504 | ||
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index e5dd0019e..ac9ae9c6a 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs | |||
| @@ -74,7 +74,7 @@ pub struct Clocks { | |||
| 74 | #[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab))] | 74 | #[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab))] |
| 75 | pub adc: Option<Hertz>, | 75 | pub adc: Option<Hertz>, |
| 76 | 76 | ||
| 77 | #[cfg(rcc_wb)] | 77 | #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] |
| 78 | /// Set only if the lsi or lse is configured | 78 | /// Set only if the lsi or lse is configured |
| 79 | pub rtc: Option<Hertz>, | 79 | pub rtc: Option<Hertz>, |
| 80 | } | 80 | } |
diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index 945bfafd6..a6102077a 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs | |||
| @@ -13,6 +13,7 @@ pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; | |||
| 13 | )] | 13 | )] |
| 14 | #[cfg_attr(any(rtc_v3, rtc_v3u5), path = "v3.rs")] | 14 | #[cfg_attr(any(rtc_v3, rtc_v3u5), path = "v3.rs")] |
| 15 | mod _version; | 15 | mod _version; |
| 16 | #[allow(unused_imports)] | ||
| 16 | pub use _version::*; | 17 | pub use _version::*; |
| 17 | use embassy_hal_internal::Peripheral; | 18 | use embassy_hal_internal::Peripheral; |
| 18 | 19 | ||
diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index 5b8960696..53d0161d6 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs | |||
| @@ -5,6 +5,138 @@ use crate::pac::rtc::Rtc; | |||
| 5 | use crate::peripherals::RTC; | 5 | use crate::peripherals::RTC; |
| 6 | use crate::rtc::sealed::Instance; | 6 | use crate::rtc::sealed::Instance; |
| 7 | 7 | ||
| 8 | #[cfg(all(feature = "time", any(stm32wb, stm32f4)))] | ||
| 9 | pub struct RtcInstant { | ||
| 10 | ssr: u16, | ||
| 11 | st: u8, | ||
| 12 | } | ||
| 13 | |||
| 14 | #[cfg(all(feature = "time", any(stm32wb, stm32f4)))] | ||
| 15 | impl RtcInstant { | ||
| 16 | pub fn now() -> Self { | ||
| 17 | // TODO: read value twice | ||
| 18 | use crate::rtc::bcd2_to_byte; | ||
| 19 | |||
| 20 | let tr = RTC::regs().tr().read(); | ||
| 21 | let tr2 = RTC::regs().tr().read(); | ||
| 22 | let ssr = RTC::regs().ssr().read().ss(); | ||
| 23 | let ssr2 = RTC::regs().ssr().read().ss(); | ||
| 24 | |||
| 25 | let st = bcd2_to_byte((tr.st(), tr.su())); | ||
| 26 | let st2 = bcd2_to_byte((tr2.st(), tr2.su())); | ||
| 27 | |||
| 28 | assert!(st == st2); | ||
| 29 | assert!(ssr == ssr2); | ||
| 30 | |||
| 31 | let _ = RTC::regs().dr().read(); | ||
| 32 | |||
| 33 | trace!("ssr: {}", ssr); | ||
| 34 | trace!("st: {}", st); | ||
| 35 | |||
| 36 | Self { ssr, st } | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | #[cfg(all(feature = "time", any(stm32wb, stm32f4)))] | ||
| 41 | impl core::ops::Sub for RtcInstant { | ||
| 42 | type Output = embassy_time::Duration; | ||
| 43 | |||
| 44 | fn sub(self, rhs: Self) -> Self::Output { | ||
| 45 | use embassy_time::{Duration, TICK_HZ}; | ||
| 46 | |||
| 47 | trace!("self st: {}", self.st); | ||
| 48 | trace!("other st: {}", rhs.st); | ||
| 49 | |||
| 50 | trace!("self ssr: {}", self.ssr); | ||
| 51 | trace!("other ssr: {}", rhs.ssr); | ||
| 52 | |||
| 53 | let st = if self.st < rhs.st { self.st + 60 } else { self.st }; | ||
| 54 | |||
| 55 | trace!("self st: {}", st); | ||
| 56 | |||
| 57 | let self_ticks = st as u32 * 256 + (255 - self.ssr as u32); | ||
| 58 | let other_ticks = rhs.st as u32 * 256 + (255 - rhs.ssr as u32); | ||
| 59 | let rtc_ticks = self_ticks - other_ticks; | ||
| 60 | |||
| 61 | trace!("self ticks: {}", self_ticks); | ||
| 62 | trace!("other ticks: {}", other_ticks); | ||
| 63 | trace!("rtc ticks: {}", rtc_ticks); | ||
| 64 | |||
| 65 | // TODO: read prescaler | ||
| 66 | |||
| 67 | Duration::from_ticks( | ||
| 68 | ((((st as u32 * 256 + (255u32 - self.ssr as u32)) - (rhs.st as u32 * 256 + (255u32 - rhs.ssr as u32))) | ||
| 69 | * TICK_HZ as u32) as u32 | ||
| 70 | / 256u32) as u64, | ||
| 71 | ) | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | #[allow(dead_code)] | ||
| 76 | #[derive(Clone, Copy)] | ||
| 77 | pub(crate) enum WakeupPrescaler { | ||
| 78 | Div2, | ||
| 79 | Div4, | ||
| 80 | Div8, | ||
| 81 | Div16, | ||
| 82 | } | ||
| 83 | |||
| 84 | #[cfg(any(stm32wb, stm32f4))] | ||
| 85 | impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel { | ||
| 86 | fn from(val: WakeupPrescaler) -> Self { | ||
| 87 | use crate::pac::rtc::vals::Wucksel; | ||
| 88 | |||
| 89 | match val { | ||
| 90 | WakeupPrescaler::Div2 => Wucksel::DIV2, | ||
| 91 | WakeupPrescaler::Div4 => Wucksel::DIV4, | ||
| 92 | WakeupPrescaler::Div8 => Wucksel::DIV8, | ||
| 93 | WakeupPrescaler::Div16 => Wucksel::DIV16, | ||
| 94 | } | ||
| 95 | } | ||
| 96 | } | ||
| 97 | |||
| 98 | #[cfg(any(stm32wb, stm32f4))] | ||
| 99 | impl From<crate::pac::rtc::vals::Wucksel> for WakeupPrescaler { | ||
| 100 | fn from(val: crate::pac::rtc::vals::Wucksel) -> Self { | ||
| 101 | use crate::pac::rtc::vals::Wucksel; | ||
| 102 | |||
| 103 | match val { | ||
| 104 | Wucksel::DIV2 => WakeupPrescaler::Div2, | ||
| 105 | Wucksel::DIV4 => WakeupPrescaler::Div4, | ||
| 106 | Wucksel::DIV8 => WakeupPrescaler::Div8, | ||
| 107 | Wucksel::DIV16 => WakeupPrescaler::Div16, | ||
| 108 | _ => unreachable!(), | ||
| 109 | } | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | impl From<WakeupPrescaler> for u32 { | ||
| 114 | fn from(val: WakeupPrescaler) -> Self { | ||
| 115 | match val { | ||
| 116 | WakeupPrescaler::Div2 => 2, | ||
| 117 | WakeupPrescaler::Div4 => 4, | ||
| 118 | WakeupPrescaler::Div8 => 8, | ||
| 119 | WakeupPrescaler::Div16 => 16, | ||
| 120 | } | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 124 | #[allow(dead_code)] | ||
| 125 | impl WakeupPrescaler { | ||
| 126 | pub fn compute_min(val: u32) -> Self { | ||
| 127 | *[ | ||
| 128 | WakeupPrescaler::Div2, | ||
| 129 | WakeupPrescaler::Div4, | ||
| 130 | WakeupPrescaler::Div8, | ||
| 131 | WakeupPrescaler::Div16, | ||
| 132 | ] | ||
| 133 | .iter() | ||
| 134 | .skip_while(|psc| <WakeupPrescaler as Into<u32>>::into(**psc) <= val) | ||
| 135 | .next() | ||
| 136 | .unwrap_or(&WakeupPrescaler::Div16) | ||
| 137 | } | ||
| 138 | } | ||
| 139 | |||
| 8 | impl super::Rtc { | 140 | impl super::Rtc { |
| 9 | fn unlock_registers() { | 141 | fn unlock_registers() { |
| 10 | #[cfg(any(rtc_v2f2, rtc_v2f3, rtc_v2l1))] | 142 | #[cfg(any(rtc_v2f2, rtc_v2f3, rtc_v2l1))] |
| @@ -23,6 +155,92 @@ impl super::Rtc { | |||
| 23 | } | 155 | } |
| 24 | 156 | ||
| 25 | #[allow(dead_code)] | 157 | #[allow(dead_code)] |
| 158 | #[cfg(all(feature = "time", any(stm32wb, stm32f4)))] | ||
| 159 | /// start the wakeup alarm and return the actual duration of the alarm | ||
| 160 | /// the actual duration will be the closest value possible that is less | ||
| 161 | /// than the requested duration. | ||
| 162 | /// | ||
| 163 | /// note: this api is exposed for testing purposes until low power is implemented. | ||
| 164 | /// it is not intended to be public | ||
| 165 | pub fn start_wakeup_alarm(requested_duration: embassy_time::Duration) -> RtcInstant { | ||
| 166 | use embassy_time::{Duration, TICK_HZ}; | ||
| 167 | |||
| 168 | use crate::interrupt::typelevel::Interrupt; | ||
| 169 | use crate::rcc::get_freqs; | ||
| 170 | |||
| 171 | let rtc_hz = unsafe { get_freqs() }.rtc.unwrap().0 as u64; | ||
| 172 | |||
| 173 | let rtc_ticks = requested_duration.as_ticks() * rtc_hz / TICK_HZ; | ||
| 174 | let prescaler = WakeupPrescaler::compute_min((rtc_ticks / u16::MAX as u64) as u32); | ||
| 175 | |||
| 176 | // adjust the rtc ticks to the prescaler | ||
| 177 | let rtc_ticks = rtc_ticks / (<WakeupPrescaler as Into<u32>>::into(prescaler) as u64); | ||
| 178 | let rtc_ticks = if rtc_ticks >= u16::MAX as u64 { | ||
| 179 | u16::MAX - 1 | ||
| 180 | } else { | ||
| 181 | rtc_ticks as u16 | ||
| 182 | }; | ||
| 183 | |||
| 184 | let duration = Duration::from_ticks( | ||
| 185 | rtc_ticks as u64 * TICK_HZ * (<WakeupPrescaler as Into<u32>>::into(prescaler) as u64) / rtc_hz, | ||
| 186 | ); | ||
| 187 | |||
| 188 | trace!("set wakeup timer for {} ms", duration.as_millis()); | ||
| 189 | |||
| 190 | RTC::regs().wpr().write(|w| w.set_key(0xca)); | ||
| 191 | RTC::regs().wpr().write(|w| w.set_key(0x53)); | ||
| 192 | |||
| 193 | RTC::regs().wutr().modify(|w| w.set_wut(rtc_ticks)); | ||
| 194 | |||
| 195 | RTC::regs().cr().modify(|w| { | ||
| 196 | w.set_wucksel(prescaler.into()); | ||
| 197 | |||
| 198 | w.set_wutie(true); | ||
| 199 | w.set_wute(true); | ||
| 200 | }); | ||
| 201 | |||
| 202 | if !RTC::regs().cr().read().wute() { | ||
| 203 | trace!("wakeup timer not enabled"); | ||
| 204 | } else { | ||
| 205 | trace!("wakeup timer enabled"); | ||
| 206 | } | ||
| 207 | |||
| 208 | crate::interrupt::typelevel::RTC_WKUP::unpend(); | ||
| 209 | unsafe { crate::interrupt::typelevel::RTC_WKUP::enable() }; | ||
| 210 | |||
| 211 | RtcInstant::now() | ||
| 212 | } | ||
| 213 | |||
| 214 | #[allow(dead_code)] | ||
| 215 | #[cfg(all(feature = "time", any(stm32wb, stm32f4)))] | ||
| 216 | /// stop the wakeup alarm and return the time remaining | ||
| 217 | /// | ||
| 218 | /// note: this api is exposed for testing purposes until low power is implemented. | ||
| 219 | /// it is not intended to be public | ||
| 220 | pub fn stop_wakeup_alarm() -> RtcInstant { | ||
| 221 | use crate::interrupt::typelevel::Interrupt; | ||
| 222 | |||
| 223 | crate::interrupt::typelevel::RTC_WKUP::disable(); | ||
| 224 | |||
| 225 | trace!("disable wakeup timer..."); | ||
| 226 | |||
| 227 | RTC::regs().cr().modify(|w| { | ||
| 228 | w.set_wute(false); | ||
| 229 | }); | ||
| 230 | |||
| 231 | trace!("wait for wakeup timer stop..."); | ||
| 232 | |||
| 233 | // Wait for the wakeup timer to stop | ||
| 234 | // while !RTC::regs().isr().read().wutf() {} | ||
| 235 | // | ||
| 236 | // RTC::regs().isr().modify(|w| w.set_wutf(false)); | ||
| 237 | |||
| 238 | trace!("wait for wakeup timer stop...done"); | ||
| 239 | |||
| 240 | RtcInstant::now() | ||
| 241 | } | ||
| 242 | |||
| 243 | #[allow(dead_code)] | ||
| 26 | pub(crate) fn set_clock_source(clock_source: RtcClockSource) { | 244 | pub(crate) fn set_clock_source(clock_source: RtcClockSource) { |
| 27 | #[cfg(not(rtc_v2wb))] | 245 | #[cfg(not(rtc_v2wb))] |
| 28 | use stm32_metapac::rcc::vals::Rtcsel; | 246 | use stm32_metapac::rcc::vals::Rtcsel; |
