stm32: add WindowWatchdog timer
WindowWatchdog configures a timer channel to capture both edges of an input and compare the time between edges to a duration range. In other words, it expects a ~50% duty cycle square wave on its input. One possible use for this is to monitor whether another device is functional. If VALID_THRESHOLD (currently 2) edge intervals are within range, ok() returns true, otherwise, if an edge is outside of the valid range ok() returns false. state_change() runs the calling task whenever the ok() state changes.
This commit is contained in:
parent
4ac257adb9
commit
4c089134ef
@ -1,7 +1,7 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![cfg_attr(
|
#![cfg_attr(
|
||||||
feature = "nightly",
|
feature = "nightly",
|
||||||
feature(type_alias_impl_trait, async_fn_in_trait, impl_trait_projections)
|
feature(type_alias_impl_trait, async_fn_in_trait, impl_trait_projections, int_roundings)
|
||||||
)]
|
)]
|
||||||
#![cfg_attr(feature = "nightly", allow(incomplete_features))]
|
#![cfg_attr(feature = "nightly", allow(incomplete_features))]
|
||||||
|
|
||||||
|
@ -10,6 +10,9 @@ pub mod low_level {
|
|||||||
pub use super::sealed::*;
|
pub use super::sealed::*;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "nightly")]
|
||||||
|
pub mod window_watchdog;
|
||||||
|
|
||||||
pub(crate) mod sealed {
|
pub(crate) mod sealed {
|
||||||
use super::*;
|
use super::*;
|
||||||
pub trait Basic16bitInstance: RccPeripheral {
|
pub trait Basic16bitInstance: RccPeripheral {
|
||||||
|
150
embassy-stm32/src/timer/window_watchdog.rs
Normal file
150
embassy-stm32/src/timer/window_watchdog.rs
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
use core::cell::RefCell;
|
||||||
|
use core::future::poll_fn;
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
use core::task::Poll;
|
||||||
|
|
||||||
|
use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage};
|
||||||
|
use embassy_sync::waitqueue::WakerRegistration;
|
||||||
|
use embassy_time::Duration;
|
||||||
|
use stm32_metapac::timer::vals;
|
||||||
|
|
||||||
|
use crate::gpio::sealed::AFType;
|
||||||
|
use crate::pwm::*;
|
||||||
|
use crate::Peripheral;
|
||||||
|
|
||||||
|
/* requires VALID_THRESHOLD edges within range before reporting ok */
|
||||||
|
const VALID_THRESHOLD: u8 = 2;
|
||||||
|
|
||||||
|
pub struct State<'a, T: CaptureCompare16bitInstance>(StateStorage<StateInner<'a, T>>);
|
||||||
|
impl<'a, T: CaptureCompare16bitInstance> State<'a, T> {
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self(StateStorage::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct StateInner<'a, T: CaptureCompare16bitInstance> {
|
||||||
|
phantom: PhantomData<&'a mut T>,
|
||||||
|
waker: WakerRegistration,
|
||||||
|
min: u16,
|
||||||
|
count: u8,
|
||||||
|
}
|
||||||
|
unsafe impl<'a, T: CaptureCompare16bitInstance> Send for StateInner<'a, T> {}
|
||||||
|
|
||||||
|
pub struct WindowWatchdog<'a, T: CaptureCompare16bitInstance> {
|
||||||
|
inner: RefCell<PeripheralMutex<'a, StateInner<'a, T>>>,
|
||||||
|
}
|
||||||
|
unsafe impl<'a, T: CaptureCompare16bitInstance> Send for WindowWatchdog<'a, T> {}
|
||||||
|
|
||||||
|
impl<'a, T: CaptureCompare16bitInstance> WindowWatchdog<'a, T> {
|
||||||
|
pub fn new(
|
||||||
|
state: &'a mut State<'a, T>,
|
||||||
|
tim: impl Peripheral<P = T> + 'a,
|
||||||
|
input: impl Peripheral<P = impl Channel1Pin<T>> + 'a,
|
||||||
|
irq: impl Peripheral<P = T::Interrupt> + 'a,
|
||||||
|
min_interval: Duration,
|
||||||
|
max_interval: Duration,
|
||||||
|
) -> Self {
|
||||||
|
assert!(min_interval < max_interval);
|
||||||
|
|
||||||
|
let mut tim = tim.into_ref();
|
||||||
|
let input = input.into_ref();
|
||||||
|
|
||||||
|
T::enable();
|
||||||
|
<T as crate::rcc::sealed::RccPeripheral>::reset();
|
||||||
|
|
||||||
|
let clk = T::frequency().0 as u64;
|
||||||
|
let psc: u16 = unwrap!(((max_interval.as_micros() * clk).div_ceil((1 << 16) * 1000000) - 1).try_into());
|
||||||
|
let arr: u16 = unwrap!((max_interval.as_micros() * clk / ((psc as u64 + 1) * 1000000) - 1).try_into());
|
||||||
|
let min: u16 = unwrap!(((min_interval.as_micros() * clk).div_ceil((psc as u64 + 1) * 1000000) - 1).try_into());
|
||||||
|
|
||||||
|
let r = T::regs_gp16();
|
||||||
|
unsafe {
|
||||||
|
input.set_as_af(input.af_num(), AFType::Input);
|
||||||
|
r.psc().write(|v| v.set_psc(psc));
|
||||||
|
r.arr().write(|v| v.set_arr(arr));
|
||||||
|
r.ccmr_input(0).write(|v| v.set_ccs(0, vals::CcmrInputCcs(1)));
|
||||||
|
r.cr1().write(|v| v.set_urs(vals::Urs::COUNTERONLY));
|
||||||
|
r.ccer().write(|v| {
|
||||||
|
v.set_ccnp(0, true);
|
||||||
|
v.set_ccp(0, true);
|
||||||
|
v.set_cce(0, true);
|
||||||
|
});
|
||||||
|
r.dier().write(|v| {
|
||||||
|
v.set_ccie(0, true);
|
||||||
|
v.set_uie(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
tim.start();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
inner: RefCell::new(PeripheralMutex::new(irq, &mut state.0, move || StateInner {
|
||||||
|
phantom: PhantomData,
|
||||||
|
waker: WakerRegistration::new(),
|
||||||
|
min,
|
||||||
|
count: 0,
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ok(&self) -> bool {
|
||||||
|
self.inner.borrow_mut().with(|state| state.ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn state_change(&self, old_state: bool) {
|
||||||
|
poll_fn(|cx| {
|
||||||
|
let mut inner = self.inner.borrow_mut();
|
||||||
|
inner.with(|state| {
|
||||||
|
if state.ok() != old_state {
|
||||||
|
return Poll::Ready(());
|
||||||
|
}
|
||||||
|
state.waker.register(cx.waker());
|
||||||
|
Poll::Pending
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: CaptureCompare16bitInstance> StateInner<'a, T> {
|
||||||
|
fn isr(&mut self) {
|
||||||
|
let r = T::regs_gp16();
|
||||||
|
let sr = unsafe { r.sr().read() };
|
||||||
|
|
||||||
|
if sr.0 == 0 {
|
||||||
|
/* spurious interrupt */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let was_ok = self.ok();
|
||||||
|
|
||||||
|
if sr.ccif(0) && unsafe { r.ccr(0).read().ccr() } >= self.min {
|
||||||
|
/* got a valid pulse */
|
||||||
|
self.count = self.count.saturating_add(1);
|
||||||
|
|
||||||
|
/* reset timer */
|
||||||
|
unsafe { r.egr().write(|r| r.set_ug(true)) };
|
||||||
|
} else {
|
||||||
|
self.count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ack interrupt */
|
||||||
|
unsafe { r.sr().write(|w| w.0) };
|
||||||
|
|
||||||
|
/* wakeup watchers on state change */
|
||||||
|
if was_ok != self.ok() {
|
||||||
|
self.waker.wake();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ok(&self) -> bool {
|
||||||
|
self.count >= VALID_THRESHOLD
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: CaptureCompare16bitInstance> PeripheralState for StateInner<'a, T> {
|
||||||
|
type Interrupt = T::Interrupt;
|
||||||
|
fn on_interrupt(&mut self) {
|
||||||
|
self.isr();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user