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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
|
//! CLKOUT pseudo-peripheral
//!
//! CLKOUT is a part of the clock generation subsystem, and can be used
//! either to generate arbitrary waveforms, or to debug the state of
//! internal oscillators.
use core::marker::PhantomData;
use embassy_hal_internal::Peri;
pub use crate::clocks::periph_helpers::Div4;
use crate::clocks::{ClockError, PoweredClock, with_clocks};
use crate::pac::mrcc0::mrcc_clkout_clksel::Mux;
use crate::peripherals::CLKOUT;
/// A peripheral representing the CLKOUT pseudo-peripheral
pub struct ClockOut<'a> {
_p: PhantomData<&'a mut CLKOUT>,
freq: u32,
}
/// Selected clock source to output
pub enum ClockOutSel {
/// 12MHz Internal Oscillator
Fro12M,
/// FRO180M Internal Oscillator, via divisor
FroHfDiv,
/// External Oscillator
ClkIn,
/// 16KHz oscillator
Clk16K,
/// Output of PLL1
Pll1Clk,
/// Main System CPU clock, divided by 6
SlowClk,
}
/// Configuration for the ClockOut
pub struct Config {
/// Selected Source Clock
pub sel: ClockOutSel,
/// Selected division level
pub div: Div4,
/// Selected power level
pub level: PoweredClock,
}
impl<'a> ClockOut<'a> {
/// Create a new ClockOut pin. On success, the clock signal will begin immediately
/// on the given pin.
pub fn new(
_peri: Peri<'a, CLKOUT>,
pin: Peri<'a, impl sealed::ClockOutPin>,
cfg: Config,
) -> Result<Self, ClockError> {
// There's no MRCC enable bit, so we check the validity of the clocks here
//
// TODO: Should we check that the frequency is suitably low?
let (freq, mux) = check_sel(cfg.sel, cfg.level)?;
// All good! Apply requested config, starting with the pin.
pin.mux();
setup_clkout(mux, cfg.div);
Ok(Self {
_p: PhantomData,
freq: freq / cfg.div.into_divisor(),
})
}
/// Frequency of the clkout pin
#[inline]
pub fn frequency(&self) -> u32 {
self.freq
}
}
impl Drop for ClockOut<'_> {
fn drop(&mut self) {
disable_clkout();
}
}
/// Check whether the given clock selection is valid
fn check_sel(sel: ClockOutSel, level: PoweredClock) -> Result<(u32, Mux), ClockError> {
let res = with_clocks(|c| {
Ok(match sel {
ClockOutSel::Fro12M => (c.ensure_fro_hf_active(&level)?, Mux::Clkroot12m),
ClockOutSel::FroHfDiv => (c.ensure_fro_hf_div_active(&level)?, Mux::ClkrootFircDiv),
ClockOutSel::ClkIn => (c.ensure_clk_in_active(&level)?, Mux::ClkrootSosc),
ClockOutSel::Clk16K => (c.ensure_clk_16k_vdd_core_active(&level)?, Mux::Clkroot16k),
ClockOutSel::Pll1Clk => (c.ensure_pll1_clk_active(&level)?, Mux::ClkrootSpll),
ClockOutSel::SlowClk => (c.ensure_slow_clk_active(&level)?, Mux::ClkrootSlow),
})
});
let Some(res) = res else {
return Err(ClockError::NeverInitialized);
};
res
}
/// Set up the clkout pin using the given mux and div settings
fn setup_clkout(mux: Mux, div: Div4) {
let mrcc = unsafe { crate::pac::Mrcc0::steal() };
mrcc.mrcc_clkout_clksel().write(|w| w.mux().variant(mux));
// Set up clkdiv
mrcc.mrcc_clkout_clkdiv().write(|w| {
w.halt().set_bit();
w.reset().set_bit();
unsafe { w.div().bits(div.into_bits()) };
w
});
mrcc.mrcc_clkout_clkdiv().write(|w| {
w.halt().clear_bit();
w.reset().clear_bit();
unsafe { w.div().bits(div.into_bits()) };
w
});
while mrcc.mrcc_clkout_clkdiv().read().unstab().bit_is_set() {}
}
/// Stop the clkout
fn disable_clkout() {
// Stop the output by selecting the "none" clock
//
// TODO: restore the pin to hi-z or something?
let mrcc = unsafe { crate::pac::Mrcc0::steal() };
mrcc.mrcc_clkout_clkdiv().write(|w| {
w.reset().set_bit();
w.halt().set_bit();
unsafe {
w.div().bits(0);
}
w
});
mrcc.mrcc_clkout_clksel().write(|w| unsafe { w.bits(0b111) });
}
mod sealed {
use embassy_hal_internal::PeripheralType;
use crate::gpio::{Pull, SealedPin};
/// Sealed marker trait for clockout pins
pub trait ClockOutPin: PeripheralType {
/// Set the given pin to the correct muxing state
fn mux(&self);
}
macro_rules! impl_pin {
($pin:ident, $func:ident) => {
impl ClockOutPin for crate::peripherals::$pin {
fn mux(&self) {
self.set_function(crate::pac::port0::pcr0::Mux::$func);
self.set_pull(Pull::Disabled);
}
}
};
}
impl_pin!(P0_6, Mux12);
impl_pin!(P3_6, Mux1);
impl_pin!(P3_8, Mux12);
impl_pin!(P4_2, Mux1);
}
|