6
6
//!
7
7
//! [examples/can.rs]: https://github.com/stm32-rs/stm32f3xx-hal/blob/v0.6.1/examples/can.rs
8
8
9
+ use embedded_time:: fixed_point:: FixedPoint ;
10
+
9
11
use crate :: hal:: watchdog:: { Watchdog , WatchdogEnable } ;
10
12
11
- use crate :: pac:: { DBGMCU , IWDG } ;
12
- use crate :: time:: duration:: * ;
13
+ use crate :: pac:: { iwdg:: pr:: PR_A , DBGMCU , IWDG } ;
14
+ use crate :: time:: duration:: Milliseconds ;
15
+ use crate :: time:: rate:: Kilohertz ;
13
16
14
- const LSI_KHZ : u32 = 40 ;
15
- const MAX_PR : u8 = 8 ;
16
- const MAX_RL : u16 = 0x1000 ;
17
+ /// Frequency of the watchdog peripheral clock
18
+ const LSI : Kilohertz = Kilohertz ( 40 ) ;
19
+ // const MAX_PRESCALER: u8 = 0b0111;
20
+ const MAX_PRESCALER : PR_A = PR_A :: DIVIDEBY256 ;
21
+ const MAX_RELOAD : u32 = 0x0FFF ;
17
22
18
23
/// Independent Watchdog Peripheral
19
24
pub struct IndependentWatchDog {
20
25
iwdg : IWDG ,
21
26
}
22
27
28
+ fn into_division_value ( psc : PR_A ) -> u32 {
29
+ match psc {
30
+ PR_A :: DIVIDEBY4 => 4 ,
31
+ PR_A :: DIVIDEBY8 => 8 ,
32
+ PR_A :: DIVIDEBY16 => 16 ,
33
+ PR_A :: DIVIDEBY32 => 32 ,
34
+ PR_A :: DIVIDEBY64 => 64 ,
35
+ PR_A :: DIVIDEBY128 => 128 ,
36
+ PR_A :: DIVIDEBY256 | PR_A :: DIVIDEBY256BIS => 256 ,
37
+ }
38
+ }
39
+
23
40
impl IndependentWatchDog {
24
41
/// Creates a new [`IndependentWatchDog`] without starting it.
25
42
///
@@ -35,52 +52,53 @@ impl IndependentWatchDog {
35
52
dbg. apb1_fz . modify ( |_, w| w. dbg_iwdg_stop ( ) . bit ( stop) ) ;
36
53
}
37
54
38
- fn setup ( & self , timeout_ms : u32 ) {
39
- let mut pr = 0 ;
40
- while pr < MAX_PR && Self :: timeout_period ( pr, MAX_RL ) < timeout_ms {
41
- pr += 1 ;
55
+ /// Find and setup the next best prescaler and reload value for the selected timeout
56
+ fn setup ( & self , timeout : Milliseconds ) {
57
+ let mut reload: u32 =
58
+ timeout. integer ( ) * LSI . integer ( ) / into_division_value ( PR_A :: DIVIDEBY4 ) ;
59
+
60
+ // Reload is potentially to high to be stored in the register.
61
+ // The goal of this loop is to find the maximum possible reload value,
62
+ // which can be stored in the register, while still guaranteeing the wanted timeout.
63
+ //
64
+ // This is achived by increasing the prescaler value.
65
+ let mut psc = 0 ;
66
+ loop {
67
+ if psc >= MAX_PRESCALER as u8 {
68
+ // ceil the value to the maximum allow reload value
69
+ if reload > MAX_RELOAD {
70
+ reload = MAX_RELOAD ;
71
+ }
72
+ break ;
73
+ }
74
+ if reload <= MAX_RELOAD {
75
+ break ;
76
+ }
77
+ psc += 1 ;
78
+ // When the precaler value incresed, the reload value has to be halfed
79
+ // so that the timeout stays the same.
80
+ reload /= 2 ;
42
81
}
43
82
44
- let max_period = Self :: timeout_period ( pr, MAX_RL ) ;
45
- let max_rl = u32:: from ( MAX_RL ) ;
46
- let rl = ( timeout_ms * max_rl / max_period) . min ( max_rl) as u16 ;
47
-
48
83
self . access_registers ( |iwdg| {
49
- iwdg. pr . modify ( |_, w| w. pr ( ) . bits ( pr ) ) ;
50
- iwdg. rlr . modify ( |_, w| w. rl ( ) . bits ( rl ) ) ;
84
+ iwdg. pr . modify ( |_, w| w. pr ( ) . bits ( psc ) ) ;
85
+ iwdg. rlr . modify ( |_, w| w. rl ( ) . bits ( reload as u16 ) ) ;
51
86
} ) ;
52
- }
53
87
54
- fn is_pr_updating ( & self ) -> bool {
55
- self . iwdg . sr . read ( ) . pvu ( ) . bit ( )
88
+ // NOTE: As the watchdog can not be stopped once started,
89
+ // a free method is not provided.
90
+ // pub fn free(self) -> IWDG {}
56
91
}
57
92
58
- /// Returns the interval in ms
93
+ /// Returns the currently set interval
59
94
pub fn interval ( & self ) -> Milliseconds {
60
- while self . is_pr_updating ( ) { }
95
+ // If the prescaler was changed wait until the change procedure is finished.
96
+ while self . iwdg . sr . read ( ) . pvu ( ) . bit ( ) { }
61
97
62
- let pr = self . iwdg . pr . read ( ) . pr ( ) . bits ( ) ;
63
- let rl = self . iwdg . rlr . read ( ) . rl ( ) . bits ( ) ;
64
- let ms = Self :: timeout_period ( pr, rl) ;
65
- Milliseconds ( ms)
66
- }
98
+ let psc = self . iwdg . pr . read ( ) . pr ( ) . variant ( ) ;
99
+ let reload = self . iwdg . rlr . read ( ) . rl ( ) . bits ( ) ;
67
100
68
- /// pr: Prescaler divider bits, rl: reload value
69
- ///
70
- /// Returns ms
71
- fn timeout_period ( pr : u8 , rl : u16 ) -> u32 {
72
- let divider: u32 = match pr {
73
- 0b000 => 4 ,
74
- 0b001 => 8 ,
75
- 0b010 => 16 ,
76
- 0b011 => 32 ,
77
- 0b100 => 64 ,
78
- 0b101 => 128 ,
79
- 0b110 => 256 ,
80
- 0b111 => 256 ,
81
- _ => crate :: panic!( "Invalid IWDG prescaler divider" ) ,
82
- } ;
83
- ( u32:: from ( rl) + 1 ) * divider / LSI_KHZ
101
+ Milliseconds ( ( into_division_value ( psc) * u32:: from ( reload) ) / LSI . integer ( ) )
84
102
}
85
103
86
104
fn access_registers < A , F : FnMut ( & IWDG ) -> A > ( & self , mut f : F ) -> A {
@@ -92,29 +110,13 @@ impl IndependentWatchDog {
92
110
self . iwdg . kr . write ( |w| w. key ( ) . reset ( ) ) ;
93
111
a
94
112
}
95
-
96
- /// Stop the watchdog timer
97
- pub fn stop ( & mut self ) -> Self {
98
- self . access_registers ( |iwdg| {
99
- iwdg. pr . reset ( ) ;
100
- iwdg. rlr . reset ( ) ;
101
- } ) ;
102
- }
103
-
104
- /// Release the independent watchdog peripheral.
105
- ///
106
- /// Disables the watchdog before releasing it.
107
- pub fn free ( mut self ) -> IWDG {
108
- self . stop ( ) ;
109
- self . iwdg
110
- }
111
113
}
112
114
113
115
impl WatchdogEnable for IndependentWatchDog {
114
116
type Time = Milliseconds ;
115
117
116
118
fn start < T : Into < Self :: Time > > ( & mut self , period : T ) {
117
- self . setup ( period. into ( ) . integer ( ) ) ;
119
+ self . setup ( period. into ( ) ) ;
118
120
119
121
self . iwdg . kr . write ( |w| w. key ( ) . start ( ) ) ;
120
122
}
0 commit comments