1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
//! Blocking delays via a software cycle-counting busy-wait.
//!
//! Implements [`embedded_hal::delay::DelayNs`] for the MSP430FR5969 without
//! consuming a hardware timer. The implementation simply burns CPU cycles in a
//! tight loop whose per-iteration cost is known, so the only thing it needs to
//! turn a *time* into a *cycle count* is the core clock frequency (MCLK).
//!
//! # Why this needs the clock frequency
//!
//! A delay is fundamentally "spin for N clock cycles." Converting a requested
//! duration into N requires knowing how many cycles elapse per second, i.e.
//! MCLK in Hz. After reset this device runs MCLK ≈ 1 MHz (DCO), but once you
//! bring up the clock system (the next milestone in this HAL) MCLK changes, and
//! a [`Delay`] built with the old frequency would be wrong. Always construct
//! [`Delay`] with the *current* MCLK frequency.
//!
//! # Accuracy
//!
//! This is a software delay, so it is **approximate** and biased slightly long:
//!
//! - The inner loop costs `CYCLES_PER_ITER` cycles per iteration; the
//! requested cycle count is divided by that, so sub-`CYCLES_PER_ITER`
//! remainders are rounded down.
//! - The per-call function/loop-setup overhead (a handful of cycles) is not
//! subtracted, so very short delays overshoot proportionally.
//! - Interrupts, if enabled, extend the delay by their service time.
//!
//! For the intended use — an ad-hoc watchdog polling on the order of
//! milliseconds-to-seconds — this is well within tolerance. If you need precise
//! or interrupt-tolerant timing, drive a Timer_A/Timer_B channel instead.
//!
//! # Example: watchdog poll loop
//!
//! ```ignore
//! use hal::delay::Delay;
//! use embedded_hal::delay::DelayNs;
//! use embedded_hal::digital::{InputPin, OutputPin};
//!
//! let mut delay = Delay::new(1_000_000); // MCLK = 1 MHz after reset
//! loop {
//! if heartbeat.is_low().unwrap() { // external system stopped pinging
//! reset_line.set_high().unwrap();
//! delay.delay_ms(10); // hold the reset pulse
//! reset_line.set_low().unwrap();
//! }
//! delay.delay_ms(1000); // check once per second
//! }
//! ```
use DelayNs;
/// CPU cycles consumed by one iteration of the inner busy-loop.
///
/// The loop body is `sub #1, Rn` (1 cycle, `#1` comes from the constant
/// generator) followed by `jne` (2 cycles whether or not the branch is taken on
/// the MSP430), for 3 cycles per iteration.
const CYCLES_PER_ITER: u64 = 3;
/// A blocking delay source backed by a calibrated software busy-loop.
///
/// Construct it with the current MCLK frequency in Hz. Cheap to copy, so you can
/// hand a `Delay` to several drivers.
/// Spin for exactly `iters` iterations of a 3-cycle countdown loop.
///
/// Written in assembly so the optimizer cannot fold the loop away or alter its
/// cycle count. `sub #1, {i}` decrements (setting Z when it reaches 0) and
/// `jne` branches back while nonzero.