This repository was archived by the owner on Feb 13, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 39
Independent Watchdog peripheral #118
Open
astro
wants to merge
5
commits into
japaric:master
Choose a base branch
from
astro:iwdg
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
|
||
impl WatchdogEnable for IndependentWatchdog { | ||
type Time = u32; | ||
astro marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
fn start<T>(&mut self, period: T) where T: Into<Self::Time> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
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); | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -61,3 +61,4 @@ pub mod serial; | |
pub mod spi; | ||
pub mod time; | ||
pub mod timer; | ||
pub mod iwdg; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,6 +24,7 @@ impl RccExt for RCC { | |
pclk2: None, | ||
sysclk: None, | ||
}, | ||
csr: CSR(()), | ||
} | ||
} | ||
} | ||
|
@@ -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 | ||
|
@@ -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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
|
@@ -20,6 +21,10 @@ pub struct KiloHertz(pub u32); | |
#[derive(Clone, Copy)] | ||
pub struct MegaHertz(pub u32); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we make the other structs also |
||
|
||
/// 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` | ||
|
@@ -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 { | ||
|
@@ -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 { | ||
|
@@ -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 { | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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.