Skip to content
This repository was archived by the owner on Feb 13, 2019. It is now read-only.

Independent Watchdog peripheral #118

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions src/iwdg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//! Independent Watchdog

use stm32f103xx::IWDG;
use hal::watchdog::{Watchdog, WatchdogEnable};

use rcc::CSR;


pub struct IndependentWatchdog {
iwdg: IWDG,
}

enum Key {
Feed = 0xAAAA,
Unlock = 0x5555,
Lock = 0x0,
Start = 0xCCCC,
}

impl IndependentWatchdog {
/// Wrap the watchdog peripheral into a struct that implements the
/// embedded_hal `Watchdog` and `WatchdogEnable` traits.
///
/// Pass a `rcc.csr` to initialize the LSI clock.
pub fn new(iwdg: IWDG, csr: &mut CSR) -> Self {
csr.enable_lsi();

IndependentWatchdog { iwdg }
}

fn write_key(&self, key: Key) {
self.iwdg.kr.write(|w| unsafe { w.key().bits(key as u16) });
}

fn unlocked<F, R>(&mut self, f: F) -> R
where
F: FnOnce(&mut IWDG) -> R,
{
self.write_key(Key::Unlock);
let r = f(&mut self.iwdg);
self.write_key(Key::Lock);
r
}

fn configure(&mut self, prescaler: u8, reload: u16) {
self.unlocked(|iwdg| {
iwdg.pr.write(|w| unsafe {
w.pr().bits(prescaler)
});
iwdg.rlr.write(|w| unsafe {
w.rl().bits(reload)
});
});
}

/// Maximum watchdog timeout is 26.214 seconds
pub const MAX_TIMEOUT_MS: u32 = 26214;

const MAX_RELOAD: u16 = 0xFFF;
const MAX_PRESCALE: u8 = 6;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to add some comment on what this prescale value corresponds to, if that makes sense.

}

impl WatchdogEnable for IndependentWatchdog {
type Time = u32;

fn start<T>(&mut self, period: T) where T: Into<Self::Time> {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be good to add some information about the requirements of period and state what happens when these checks fail. (e.g. max timeout check)

let timeout_ms = period.into();

let mut max_timeout_ms = Self::MAX_TIMEOUT_MS;
assert!(timeout_ms <= max_timeout_ms);

let mut prescaler = Self::MAX_PRESCALE;
while prescaler > 0 && timeout_ms <= max_timeout_ms / 2 {
prescaler -= 1;
max_timeout_ms /= 2;
}

let reload = ((Self::MAX_RELOAD as u32) * timeout_ms / max_timeout_ms) as u16;
self.configure(prescaler, reload);
self.write_key(Key::Start);
}
}

impl Watchdog for IndependentWatchdog {
fn feed(&mut self) {
self.write_key(Key::Feed);
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,4 @@ pub mod serial;
pub mod spi;
pub mod time;
pub mod timer;
pub mod iwdg;
61 changes: 61 additions & 0 deletions src/rcc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ impl RccExt for RCC {
pclk2: None,
sysclk: None,
},
csr: CSR(()),
}
}
}
Expand All @@ -37,6 +38,7 @@ pub struct Rcc {
/// Advanced Peripheral Bus 2 (APB2) registers
pub apb2: APB2,
pub cfgr: CFGR,
pub csr: CSR,
}

/// AMBA High-performance Bus (AHB) registers
Expand Down Expand Up @@ -252,6 +254,65 @@ impl CFGR {
}
}

/// Control/Status Register
pub struct CSR(());

impl CSR {
fn csr(&mut self) -> &rcc::CSR {
// NOTE(unsafe) this proxy grants exclusive access to this register
unsafe { &(*RCC::ptr()).csr }
}

/// Enable the LSI clock around 40 kHz (30..60 kHz)
///
/// Needed for the Independent Watchdog (IWDG) periphal and the
/// Auto-Wakeup Unit (AWU)
pub fn enable_lsi(&mut self) {
// Enable
self.csr().modify(|_, w| w.lsion().set_bit());

// Wait for clock to become stable
while self.csr().read().lsirdy().bit_is_set() {}
}

/// Read why a reset occured
pub fn reset_reason(&mut self) -> ResetReason {
let r = self.csr().read();
if r.lpwrrstf().bit_is_set() {
ResetReason::LowPower
} else if r.wwdgrstf().bit_is_set() {
ResetReason::WWDG
} else if r.iwdgrstf().bit_is_set() {
ResetReason::IWDG
} else if r.sftrstf().bit_is_set() {
ResetReason::Software
} else if r.porrstf().bit_is_set() {
ResetReason::PorPdr
} else if r.pinrstf().bit_is_set() {
ResetReason::NrstPin
} else {
ResetReason::Unknown
}
}

/// Clear the reason for the last reset
pub fn clear_reset_reason(&mut self) {
self.csr().write(|w| w.rmvf().set_bit());
}
}

/// Reason for controller reset
#[derive(Clone, Copy, Debug)]
pub enum ResetReason {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about adding documentation on the variants of this enum? I would say some are not immediately obvious.

LowPower,
WWDG,
IWDG,
Software,
PorPdr,
NrstPin,
Unknown,
}

/// Frozen clock frequencies
///
/// The existence of this value indicates that the clock configuration can no longer be changed
Expand Down
28 changes: 28 additions & 0 deletions src/time.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Time units

use core::ops::Div;
use cortex_m::peripheral::DWT;

use rcc::Clocks;
Expand All @@ -20,6 +21,10 @@ pub struct KiloHertz(pub u32);
#[derive(Clone, Copy)]
pub struct MegaHertz(pub u32);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we make the other structs also PartialOrd and PartialEq?


/// 1/1000th of a second
#[derive(Clone, Copy, PartialOrd, PartialEq)]
pub struct MilliSeconds(pub u32);

/// Extension trait that adds convenience methods to the `u32` type
pub trait U32Ext {
/// Wrap in `Bps`
Expand All @@ -33,6 +38,9 @@ pub trait U32Ext {

/// Wrap in `MegaHertz`
fn mhz(self) -> MegaHertz;

/// Wrap in `MilliSeconds`
fn ms(self) -> MilliSeconds;
}

impl U32Ext for u32 {
Expand All @@ -51,6 +59,10 @@ impl U32Ext for u32 {
fn mhz(self) -> MegaHertz {
MegaHertz(self)
}

fn ms(self) -> MilliSeconds {
MilliSeconds(self)
}
}

impl Into<Hertz> for KiloHertz {
Expand All @@ -71,6 +83,22 @@ impl Into<KiloHertz> for MegaHertz {
}
}

impl Div<u32> for MilliSeconds {
type Output = MilliSeconds;

fn div(self, rhs: u32) -> Self::Output {
MilliSeconds(self.0 / rhs)
}
}

impl Div<MilliSeconds> for MilliSeconds {
type Output = u32;

fn div(self, rhs: MilliSeconds) -> Self::Output {
self.0 / rhs.0
}
}

/// A monotonic nondecreasing timer
#[derive(Clone, Copy)]
pub struct MonoTimer {
Expand Down