diff options
| author | chemicstry <[email protected]> | 2022-07-10 20:38:30 +0300 |
|---|---|---|
| committer | chemicstry <[email protected]> | 2022-07-10 20:38:30 +0300 |
| commit | bd01e90bfa4895b45e41ad538cb24a959b0b58ab (patch) | |
| tree | ec86a61d00a087e1faba730d922e53c8f3086f2f | |
| parent | 5f43c1d37e9db847c7861fe0bd821db62edba9f6 (diff) | |
Implement IWDG timeout calculation
| -rw-r--r-- | embassy-stm32/src/wdg/mod.rs | 50 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/wdt.rs | 44 |
2 files changed, 90 insertions, 4 deletions
diff --git a/embassy-stm32/src/wdg/mod.rs b/embassy-stm32/src/wdg/mod.rs index da25692ab..b4d59ff2d 100644 --- a/embassy-stm32/src/wdg/mod.rs +++ b/embassy-stm32/src/wdg/mod.rs | |||
| @@ -1,21 +1,63 @@ | |||
| 1 | use core::marker::PhantomData; | 1 | use core::marker::PhantomData; |
| 2 | 2 | ||
| 3 | use embassy::time::Duration; | ||
| 3 | use embassy_hal_common::{unborrow, Unborrow}; | 4 | use embassy_hal_common::{unborrow, Unborrow}; |
| 4 | use stm32_metapac::iwdg::vals::Key; | 5 | use stm32_metapac::iwdg::vals::{Key, Pr}; |
| 5 | pub use stm32_metapac::iwdg::vals::Pr as Prescaler; | 6 | |
| 7 | use crate::time::Hertz; | ||
| 6 | 8 | ||
| 7 | pub struct IndependentWatchdog<'d, T: Instance> { | 9 | pub struct IndependentWatchdog<'d, T: Instance> { |
| 8 | wdg: PhantomData<&'d mut T>, | 10 | wdg: PhantomData<&'d mut T>, |
| 9 | } | 11 | } |
| 10 | 12 | ||
| 13 | // Some STM32 families have 40 kHz LSI clock instead of 32 kHz | ||
| 14 | cfg_if::cfg_if! { | ||
| 15 | if #[cfg(any(stm32f0, stm32f1))] { | ||
| 16 | pub const IWDG_FREQ: Hertz = Hertz(40_000); | ||
| 17 | } else { | ||
| 18 | pub const IWDG_FREQ: Hertz = Hertz(32_000); | ||
| 19 | } | ||
| 20 | } | ||
| 21 | |||
| 22 | // 12-bit counter | ||
| 23 | const MAX_RL: u16 = 0xFFF; | ||
| 24 | |||
| 25 | /// Calculates maximum watchdog timeout (RL = 0xFFF) for a given prescaler | ||
| 26 | const fn max_timeout(prescaler: u8) -> Duration { | ||
| 27 | Duration::from_micros(1_000_000 / (IWDG_FREQ.0 / prescaler as u32) as u64 * MAX_RL as u64) | ||
| 28 | } | ||
| 29 | |||
| 30 | /// Calculates watchdog reload value for the given prescaler and desired timeout | ||
| 31 | const fn reload_value(prescaler: u8, timeout: Duration) -> u16 { | ||
| 32 | ((IWDG_FREQ.0 / prescaler as u32) as u64 * timeout.as_micros() / 1_000_000) as u16 | ||
| 33 | } | ||
| 34 | |||
| 11 | impl<'d, T: Instance> IndependentWatchdog<'d, T> { | 35 | impl<'d, T: Instance> IndependentWatchdog<'d, T> { |
| 12 | pub fn new(_instance: impl Unborrow<Target = T> + 'd, presc: Prescaler) -> Self { | 36 | pub fn new(_instance: impl Unborrow<Target = T> + 'd, timeout: Duration) -> Self { |
| 13 | unborrow!(_instance); | 37 | unborrow!(_instance); |
| 14 | 38 | ||
| 39 | // Find lowest prescaler value, which makes watchdog period longer or equal to timeout. | ||
| 40 | // This iterates from 4 (2^2) to 256 (2^8). | ||
| 41 | let psc_power = unwrap!((2..=8).find(|psc_power| { | ||
| 42 | let psc = 2u8.pow(*psc_power); | ||
| 43 | timeout <= max_timeout(psc) | ||
| 44 | })); | ||
| 45 | |||
| 46 | // Prescaler value | ||
| 47 | let psc = 2u8.pow(psc_power); | ||
| 48 | |||
| 49 | // Convert prescaler power to PR register value | ||
| 50 | let pr = psc_power as u8 - 2; | ||
| 51 | assert!(pr <= 0b110); | ||
| 52 | |||
| 53 | // Reload value | ||
| 54 | let rl = reload_value(psc, timeout); | ||
| 55 | |||
| 15 | let wdg = T::regs(); | 56 | let wdg = T::regs(); |
| 16 | unsafe { | 57 | unsafe { |
| 17 | wdg.kr().write(|w| w.set_key(Key::ENABLE)); | 58 | wdg.kr().write(|w| w.set_key(Key::ENABLE)); |
| 18 | wdg.pr().write(|w| w.set_pr(presc)); | 59 | wdg.pr().write(|w| w.set_pr(Pr(pr))); |
| 60 | wdg.rlr().write(|w| w.set_rl(rl)); | ||
| 19 | } | 61 | } |
| 20 | 62 | ||
| 21 | IndependentWatchdog { | 63 | IndependentWatchdog { |
diff --git a/examples/stm32f4/src/bin/wdt.rs b/examples/stm32f4/src/bin/wdt.rs new file mode 100644 index 000000000..41e1f4c7b --- /dev/null +++ b/examples/stm32f4/src/bin/wdt.rs | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy::executor::Spawner; | ||
| 7 | use embassy::time::{Duration, Timer}; | ||
| 8 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 9 | use embassy_stm32::wdg::IndependentWatchdog; | ||
| 10 | use embassy_stm32::Peripherals; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | #[embassy::main] | ||
| 14 | async fn main(_spawner: Spawner, p: Peripherals) { | ||
| 15 | info!("Hello World!"); | ||
| 16 | |||
| 17 | let mut led = Output::new(p.PB7, Level::High, Speed::Low); | ||
| 18 | |||
| 19 | let mut wdt = IndependentWatchdog::new(p.IWDG, Duration::from_secs(1)); | ||
| 20 | unsafe { | ||
| 21 | wdt.unleash(); | ||
| 22 | } | ||
| 23 | |||
| 24 | let mut i = 0; | ||
| 25 | |||
| 26 | loop { | ||
| 27 | info!("high"); | ||
| 28 | led.set_high(); | ||
| 29 | Timer::after(Duration::from_millis(300)).await; | ||
| 30 | |||
| 31 | info!("low"); | ||
| 32 | led.set_low(); | ||
| 33 | Timer::after(Duration::from_millis(300)).await; | ||
| 34 | |||
| 35 | if i < 5 { | ||
| 36 | info!("Petting watchdog"); | ||
| 37 | unsafe { | ||
| 38 | wdt.pet(); | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | i += 1; | ||
| 43 | } | ||
| 44 | } | ||
