aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2023-05-09 21:56:43 +0000
committerGitHub <[email protected]>2023-05-09 21:56:43 +0000
commite179e7cf85810f0aa7ef8027d8d48f6d21f64dac (patch)
tree153a70e9123bbbd876f3a4b08659181d83ccec89
parent856b944eaf20bbd5f1625226415af210a28af89a (diff)
parent9d971e5b150e2ebe51570040ea59e3ccdbef7b17 (diff)
Merge #1436
1436: rp: Clock configuration r=CBJamo a=CBJamo Draft of a more complete clock config for the 2040. I also extended and made public the clk_<name>_freq functions. I know at least the ws2812 pio example would like to get the sys clock at runtime rather than just using a constant. I suspect most pio-based peripherals will want access to the clocks. Open questions: 1. Best way to handle the 3 external clock frequencies. I think the XIN (aka crystal) freq should just be set by the init function then never changed, though if it's an external clock that could change? I'm not sure anyone would ever want to do that but maybe it should be handled just in case? The other two should probably be set by the application. 2. Better estimation of ROSC frequency. Right now it's really just a lookup table of the speed from the single sample I did this testing on, and only uses the frequency range and div, drive strength is ignored. 3. Probably some kind of warning should be generated if the random bit from the rosc won't be useful, not sure how to do that. 4. Should clocks only be allowed to be configured at init, or should they be modifiable at runtime? For example, switching the RTC to a clock in pin when a pps source is available. Bonus feature to support clock output. I only implemented the bare minimum, and only for gpout0. I'm sure there's a clean way with macros to impl all 4 without just copy/paste, but I haven't learned macros yet. Co-authored-by: Caleb Jamison <[email protected]>
-rw-r--r--embassy-rp/Cargo.toml2
-rw-r--r--embassy-rp/src/clocks.rs776
-rw-r--r--embassy-rp/src/lib.rs20
-rw-r--r--examples/rp/src/bin/gpout.rs34
4 files changed, 734 insertions, 98 deletions
diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml
index 836dca28e..e395a994f 100644
--- a/embassy-rp/Cargo.toml
+++ b/embassy-rp/Cargo.toml
@@ -70,7 +70,7 @@ embedded-storage = { version = "0.3" }
70rand_core = "0.6.4" 70rand_core = "0.6.4"
71fixed = "1.23.1" 71fixed = "1.23.1"
72 72
73rp-pac = { version = "3", features = ["rt"] } 73rp-pac = { version = "4", features = ["rt"] }
74 74
75embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } 75embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
76embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} 76embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true}
diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs
index 8a34b293d..1354ccd27 100644
--- a/embassy-rp/src/clocks.rs
+++ b/embassy-rp/src/clocks.rs
@@ -1,11 +1,183 @@
1use embassy_hal_common::{into_ref, PeripheralRef};
1use pac::clocks::vals::*; 2use pac::clocks::vals::*;
2 3
3use crate::{pac, reset}; 4use crate::{pac, reset, Peripheral};
5
6// TODO fix terrible use of global here
7static mut XIN_HZ: u32 = 0;
8
9pub use rp_pac::clocks::vals::{
10 ClkAdcCtrlAuxsrc as AdcAuxsrc, ClkGpoutCtrlAuxsrc as GpoutSrc, ClkPeriCtrlAuxsrc as PeriClkAuxsrc,
11 ClkRefCtrlAuxsrc as RefAuxsrc, ClkRtcCtrlAuxsrc as RtcAuxsrc, ClkSysCtrlAuxsrc as SysAuxsrc,
12 ClkUsbCtrlAuxsrc as UsbAuxsrc,
13};
14
15#[non_exhaustive]
16pub struct ClockConfig {
17 pub rosc: Option<RoscConfig>,
18 pub xosc: Option<XoscConfig>,
19 pub ref_clk: RefClkConfig,
20 pub sys_clk: SysClkConfig,
21 pub peri_clk_src: Option<ClkPeriCtrlAuxsrc>,
22 pub usb_clk: Option<UsbClkConfig>,
23 pub adc_clk: Option<AdcClkConfig>,
24 pub rtc_clk: Option<RtcClkConfig>,
25}
26
27impl ClockConfig {
28 pub fn crystal(crystal_hz: u32) -> Self {
29 Self {
30 rosc: Some(RoscConfig {
31 range: pac::rosc::vals::FreqRange::MEDIUM,
32 drive_strength: [0; 8],
33 div: 16,
34 }),
35 xosc: Some(XoscConfig {
36 hz: crystal_hz,
37 clock_type: ExternalClock::Crystal,
38 sys_pll: Some(PllConfig {
39 refdiv: 1,
40 vco_freq: 1500_000_000,
41 post_div1: 6,
42 post_div2: 2,
43 }),
44 usb_pll: Some(PllConfig {
45 refdiv: 1,
46 vco_freq: 480_000_000,
47 post_div1: 5,
48 post_div2: 2,
49 }),
50 }),
51 ref_clk: RefClkConfig {
52 src: RefClkSrc::Xosc,
53 div: 1,
54 },
55 sys_clk: SysClkConfig {
56 src: SysClkSrc::Aux(ClkSysCtrlAuxsrc::CLKSRC_PLL_SYS),
57 div_int: 1,
58 div_frac: 0,
59 },
60 peri_clk_src: Some(ClkPeriCtrlAuxsrc::CLK_SYS),
61 usb_clk: Some(UsbClkConfig {
62 src: ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB,
63 div: 1,
64 phase: 0,
65 }),
66 adc_clk: Some(AdcClkConfig {
67 src: ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB,
68 div: 1,
69 phase: 0,
70 }),
71 rtc_clk: Some(RtcClkConfig {
72 src: ClkRtcCtrlAuxsrc::CLKSRC_PLL_USB,
73 div_int: 1024,
74 div_frac: 0,
75 phase: 0,
76 }),
77 }
78 }
79
80 pub fn rosc() -> Self {
81 Self {
82 rosc: Some(RoscConfig {
83 range: pac::rosc::vals::FreqRange::HIGH,
84 drive_strength: [0; 8],
85 div: 1,
86 }),
87 xosc: None,
88 ref_clk: RefClkConfig {
89 src: RefClkSrc::Rosc,
90 div: 1,
91 },
92 sys_clk: SysClkConfig {
93 src: SysClkSrc::Aux(ClkSysCtrlAuxsrc::ROSC_CLKSRC),
94 div_int: 1,
95 div_frac: 0,
96 },
97 peri_clk_src: Some(ClkPeriCtrlAuxsrc::ROSC_CLKSRC_PH),
98 usb_clk: None,
99 adc_clk: Some(AdcClkConfig {
100 src: ClkAdcCtrlAuxsrc::ROSC_CLKSRC_PH,
101 div: 1,
102 phase: 0,
103 }),
104 rtc_clk: Some(RtcClkConfig {
105 src: ClkRtcCtrlAuxsrc::ROSC_CLKSRC_PH,
106 div_int: 1024,
107 div_frac: 0,
108 phase: 0,
109 }),
110 }
111 }
112}
113
114pub struct RoscConfig {
115 pub range: pac::rosc::vals::FreqRange,
116 pub drive_strength: [u8; 8],
117 pub div: u16,
118}
4 119
5const XOSC_MHZ: u32 = 12; 120pub struct XoscConfig {
121 pub hz: u32,
122 pub clock_type: ExternalClock,
123 pub sys_pll: Option<PllConfig>,
124 pub usb_pll: Option<PllConfig>,
125}
126
127pub struct PllConfig {
128 pub refdiv: u32,
129 pub vco_freq: u32,
130 pub post_div1: u8,
131 pub post_div2: u8,
132}
133
134pub enum ExternalClock {
135 Crystal,
136 Clock,
137}
138pub struct RefClkConfig {
139 pub src: RefClkSrc,
140 pub div: u8,
141}
142
143pub enum RefClkSrc {
144 Xosc,
145 Rosc,
146 Aux(ClkRefCtrlAuxsrc),
147}
148
149pub enum SysClkSrc {
150 Ref,
151 Aux(ClkSysCtrlAuxsrc),
152}
153
154pub struct SysClkConfig {
155 pub src: SysClkSrc,
156 pub div_int: u32,
157 pub div_frac: u8,
158}
159
160pub struct UsbClkConfig {
161 pub src: ClkUsbCtrlAuxsrc,
162 pub div: u8,
163 pub phase: u8,
164}
165
166pub struct AdcClkConfig {
167 pub src: ClkAdcCtrlAuxsrc,
168 pub div: u8,
169 pub phase: u8,
170}
171
172pub struct RtcClkConfig {
173 pub src: ClkRtcCtrlAuxsrc,
174 pub div_int: u32,
175 pub div_frac: u8,
176 pub phase: u8,
177}
6 178
7/// safety: must be called exactly once at bootup 179/// safety: must be called exactly once at bootup
8pub(crate) unsafe fn init() { 180pub(crate) unsafe fn init(config: ClockConfig) {
9 // Reset everything except: 181 // Reset everything except:
10 // - QSPI (we're using it to run this code!) 182 // - QSPI (we're using it to run this code!)
11 // - PLLs (it may be suicide if that's what's clocking us) 183 // - PLLs (it may be suicide if that's what's clocking us)
@@ -15,124 +187,387 @@ pub(crate) unsafe fn init() {
15 peris.set_pads_qspi(false); 187 peris.set_pads_qspi(false);
16 peris.set_pll_sys(false); 188 peris.set_pll_sys(false);
17 peris.set_pll_usb(false); 189 peris.set_pll_usb(false);
190 // TODO investigate if usb should be unreset here
18 peris.set_usbctrl(false); 191 peris.set_usbctrl(false);
19 peris.set_syscfg(false); 192 peris.set_syscfg(false);
20 reset::reset(peris); 193 reset::reset(peris);
21 194
22 // Remove reset from peripherals which are clocked only by clk_sys and
23 // clk_ref. Other peripherals stay in reset until we've configured clocks.
24 let mut peris = reset::ALL_PERIPHERALS;
25 peris.set_adc(false);
26 peris.set_rtc(false);
27 peris.set_spi0(false);
28 peris.set_spi1(false);
29 peris.set_uart0(false);
30 peris.set_uart1(false);
31 peris.set_usbctrl(false);
32 reset::unreset_wait(peris);
33
34 // Start tick in watchdog
35 // xosc 12 mhz
36 pac::WATCHDOG.tick().write(|w| {
37 w.set_cycles(XOSC_MHZ as u16);
38 w.set_enable(true);
39 });
40
41 // Disable resus that may be enabled from previous software 195 // Disable resus that may be enabled from previous software
42 let c = pac::CLOCKS; 196 let c = pac::CLOCKS;
43 c.clk_sys_resus_ctrl() 197 c.clk_sys_resus_ctrl()
44 .write_value(pac::clocks::regs::ClkSysResusCtrl(0)); 198 .write_value(pac::clocks::regs::ClkSysResusCtrl(0));
45 199
46 // start XOSC
47 start_xosc();
48
49 // Before we touch PLLs, switch sys and ref cleanly away from their aux sources. 200 // Before we touch PLLs, switch sys and ref cleanly away from their aux sources.
50 c.clk_sys_ctrl().modify(|w| w.set_src(ClkSysCtrlSrc::CLK_REF)); 201 c.clk_sys_ctrl().modify(|w| w.set_src(ClkSysCtrlSrc::CLK_REF));
51 while c.clk_sys_selected().read() != 1 {} 202 while c.clk_sys_selected().read() != 1 {}
52 c.clk_ref_ctrl().modify(|w| w.set_src(ClkRefCtrlSrc::ROSC_CLKSRC_PH)); 203 c.clk_ref_ctrl().modify(|w| w.set_src(ClkRefCtrlSrc::ROSC_CLKSRC_PH));
53 while c.clk_ref_selected().read() != 1 {} 204 while c.clk_ref_selected().read() != 1 {}
54 205
55 // Configure PLLs 206 if let Some(config) = config.rosc {
56 // REF FBDIV VCO POSTDIV 207 configure_rosc(config);
57 // PLL SYS: 12 / 1 = 12MHz * 125 = 1500MHZ / 6 / 2 = 125MHz 208 }
58 // PLL USB: 12 / 1 = 12MHz * 40 = 480 MHz / 5 / 2 = 48MHz
59 configure_pll(pac::PLL_SYS, 1, 1500_000_000, 6, 2);
60 configure_pll(pac::PLL_USB, 1, 480_000_000, 5, 2);
61 209
62 // CLK_REF = XOSC (12MHz) / 1 = 12MHz2Mhz 210 if let Some(config) = config.xosc {
63 c.clk_ref_ctrl().write(|w| { 211 XIN_HZ = config.hz;
64 w.set_src(ClkRefCtrlSrc::XOSC_CLKSRC);
65 });
66 while c.clk_ref_selected().read() != 1 << ClkRefCtrlSrc::XOSC_CLKSRC.0 {}
67 c.clk_ref_div().write(|w| w.set_int(1));
68 212
69 // CLK SYS = PLL SYS (125MHz) / 1 = 125MHz 213 pac::WATCHDOG.tick().write(|w| {
70 c.clk_sys_ctrl().write(|w| { 214 w.set_cycles((config.hz / 1_000_000) as u16);
71 w.set_src(ClkSysCtrlSrc::CLK_REF); 215 w.set_enable(true);
72 }); 216 });
73 while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLK_REF.0 {} 217
74 c.clk_sys_div().write(|w| w.set_int(1)); 218 // start XOSC
75 c.clk_sys_ctrl().write(|w| { 219 match config.clock_type {
76 w.set_auxsrc(ClkSysCtrlAuxsrc::CLKSRC_PLL_SYS); 220 ExternalClock::Crystal => start_xosc(config.hz),
77 w.set_src(ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX); 221 // TODO The datasheet says the xosc needs to be put into a bypass mode to use an
222 // external clock, but is mum about how to do that.
223 ExternalClock::Clock => todo!(),
224 }
225
226 if let Some(sys_pll_config) = config.sys_pll {
227 configure_pll(pac::PLL_SYS, config.hz, sys_pll_config);
228 }
229 if let Some(usb_pll_config) = config.usb_pll {
230 configure_pll(pac::PLL_USB, config.hz, usb_pll_config);
231 }
232 }
233
234 match config.ref_clk.src {
235 RefClkSrc::Xosc => {
236 c.clk_ref_ctrl().write(|w| {
237 w.set_src(ClkRefCtrlSrc::XOSC_CLKSRC);
238 });
239 while c.clk_ref_selected().read() != 1 << ClkRefCtrlSrc::XOSC_CLKSRC.0 {}
240 }
241 RefClkSrc::Rosc => {
242 c.clk_ref_ctrl().write(|w| {
243 w.set_src(ClkRefCtrlSrc::ROSC_CLKSRC_PH);
244 });
245 while c.clk_ref_selected().read() != 1 << ClkRefCtrlSrc::ROSC_CLKSRC_PH.0 {}
246 }
247 RefClkSrc::Aux(src) => {
248 c.clk_ref_ctrl().write(|w| {
249 w.set_auxsrc(src);
250 w.set_src(ClkRefCtrlSrc::CLKSRC_CLK_REF_AUX);
251 });
252 while c.clk_ref_selected().read() != 1 << ClkRefCtrlSrc::CLKSRC_CLK_REF_AUX.0 {}
253 }
254 }
255 c.clk_ref_div().write(|w| {
256 w.set_int(config.ref_clk.div);
78 }); 257 });
79 while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX.0 {}
80 258
81 // CLK USB = PLL USB (48MHz) / 1 = 48MHz 259 pac::WATCHDOG.tick().write(|w| {
82 c.clk_usb_div().write(|w| w.set_int(1)); 260 w.set_cycles((clk_ref_freq() / 1_000_000) as u16);
83 c.clk_usb_ctrl().write(|w| {
84 w.set_enable(true); 261 w.set_enable(true);
85 w.set_auxsrc(ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB);
86 }); 262 });
87 263
88 // CLK ADC = PLL USB (48MHZ) / 1 = 48MHz 264 match config.sys_clk.src {
89 c.clk_adc_div().write(|w| w.set_int(1)); 265 SysClkSrc::Ref => {
90 c.clk_adc_ctrl().write(|w| { 266 c.clk_sys_ctrl().write(|w| {
91 w.set_enable(true); 267 w.set_src(ClkSysCtrlSrc::CLK_REF);
92 w.set_auxsrc(ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB); 268 });
269 while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLK_REF.0 {}
270 }
271 SysClkSrc::Aux(src) => {
272 c.clk_sys_ctrl().write(|w| {
273 w.set_src(ClkSysCtrlSrc::CLK_REF);
274 });
275 while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLK_REF.0 {}
276
277 c.clk_sys_ctrl().write(|w| {
278 w.set_auxsrc(src);
279 w.set_src(ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX);
280 });
281 while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX.0 {}
282 }
283 }
284 c.clk_sys_div().write(|w| {
285 w.set_int(config.sys_clk.div_int);
286 w.set_frac(config.sys_clk.div_frac);
287 });
288
289 let mut peris = reset::ALL_PERIPHERALS;
290
291 if let Some(src) = config.peri_clk_src {
292 c.clk_peri_ctrl().write(|w| {
293 w.set_enable(true);
294 w.set_auxsrc(src);
295 });
296 } else {
297 peris.set_spi0(false);
298 peris.set_spi1(false);
299 peris.set_uart0(false);
300 peris.set_uart1(false);
301 }
302
303 if let Some(conf) = config.usb_clk {
304 // CLK USB = PLL USB (48MHz) / 1 = 48MHz
305 c.clk_usb_div().write(|w| w.set_int(conf.div));
306 c.clk_usb_ctrl().write(|w| {
307 w.set_phase(conf.phase);
308 w.set_enable(true);
309 w.set_auxsrc(conf.src);
310 });
311 } else {
312 peris.set_usbctrl(false);
313 }
314
315 if let Some(conf) = config.adc_clk {
316 // CLK ADC = PLL USB (48MHZ) / 1 = 48MHz
317 c.clk_adc_div().write(|w| w.set_int(conf.div));
318 c.clk_adc_ctrl().write(|w| {
319 w.set_phase(conf.phase);
320 w.set_enable(true);
321 w.set_auxsrc(conf.src);
322 });
323 } else {
324 peris.set_adc(false);
325 }
326
327 if let Some(conf) = config.rtc_clk {
328 // CLK RTC = PLL USB (48MHz) / 1024 = 46875Hz
329 c.clk_rtc_ctrl().modify(|w| {
330 w.set_enable(false);
331 });
332 c.clk_rtc_div().write(|w| {
333 w.set_int(conf.div_int);
334 w.set_frac(conf.div_frac);
335 });
336 c.clk_rtc_ctrl().write(|w| {
337 w.set_phase(conf.phase);
338 w.set_enable(true);
339 w.set_auxsrc(conf.src);
340 });
341 } else {
342 peris.set_rtc(false);
343 }
344
345 // Peripheral clocks should now all be running
346 reset::unreset_wait(peris);
347}
348
349unsafe fn configure_rosc(config: RoscConfig) {
350 let p = pac::ROSC;
351
352 p.freqa().write(|w| {
353 w.set_passwd(pac::rosc::vals::Passwd::PASS);
354 w.set_ds0(config.drive_strength[0]);
355 w.set_ds1(config.drive_strength[1]);
356 w.set_ds2(config.drive_strength[2]);
357 w.set_ds3(config.drive_strength[3]);
93 }); 358 });
94 359
95 // CLK RTC = PLL USB (48MHz) / 1024 = 46875Hz 360 p.freqb().write(|w| {
96 c.clk_rtc_ctrl().modify(|w| { 361 w.set_passwd(pac::rosc::vals::Passwd::PASS);
97 w.set_enable(false); 362 w.set_ds4(config.drive_strength[4]);
363 w.set_ds5(config.drive_strength[5]);
364 w.set_ds6(config.drive_strength[6]);
365 w.set_ds7(config.drive_strength[7]);
98 }); 366 });
99 c.clk_rtc_div().write(|w| w.set_int(1024)); 367
100 c.clk_rtc_ctrl().write(|w| { 368 p.div().write(|w| {
101 w.set_enable(true); 369 w.set_div(pac::rosc::vals::Div(config.div + pac::rosc::vals::Div::PASS.0));
102 w.set_auxsrc(ClkRtcCtrlAuxsrc::CLKSRC_PLL_USB);
103 }); 370 });
104 371
105 // CLK PERI = clk_sys. Used as reference clock for Peripherals. No dividers so just select and enable 372 p.ctrl().write(|w| {
106 // Normally choose clk_sys or clk_usb 373 w.set_enable(pac::rosc::vals::Enable::ENABLE);
107 c.clk_peri_ctrl().write(|w| { 374 w.set_freq_range(config.range);
108 w.set_enable(true);
109 w.set_auxsrc(ClkPeriCtrlAuxsrc::CLK_SYS);
110 }); 375 });
376}
111 377
112 // Peripheral clocks should now all be running 378pub fn estimate_rosc_freq() -> u32 {
113 let peris = reset::ALL_PERIPHERALS; 379 let p = pac::ROSC;
114 reset::unreset_wait(peris); 380
381 let base = match unsafe { p.ctrl().read().freq_range() } {
382 pac::rosc::vals::FreqRange::LOW => 84_000_000,
383 pac::rosc::vals::FreqRange::MEDIUM => 104_000_000,
384 pac::rosc::vals::FreqRange::HIGH => 140_000_000,
385 pac::rosc::vals::FreqRange::TOOHIGH => 208_000_000,
386 _ => unreachable!(),
387 };
388 let mut div = unsafe { p.div().read().0 - pac::rosc::vals::Div::PASS.0 as u32 };
389 if div == 0 {
390 div = 32
391 }
392
393 base / div
115} 394}
116 395
117pub(crate) fn _clk_sys_freq() -> u32 { 396pub fn xosc_freq() -> u32 {
118 125_000_000 397 unsafe { XIN_HZ }
119} 398}
120 399
121pub(crate) fn clk_peri_freq() -> u32 { 400pub fn gpin0_freq() -> u32 {
122 125_000_000 401 todo!()
402}
403pub fn gpin1_freq() -> u32 {
404 todo!()
123} 405}
124 406
125pub(crate) fn clk_rtc_freq() -> u32 { 407pub fn pll_sys_freq() -> u32 {
126 46875 408 let p = pac::PLL_SYS;
409
410 let input_freq = xosc_freq();
411 let cs = unsafe { p.cs().read() };
412
413 let refdiv = cs.refdiv() as u32;
414 let fbdiv = unsafe { p.fbdiv_int().read().fbdiv_int() } as u32;
415 let (postdiv1, postdiv2) = unsafe {
416 let prim = p.prim().read();
417 (prim.postdiv1() as u32, prim.postdiv2() as u32)
418 };
419
420 (((input_freq / refdiv) * fbdiv) / postdiv1) / postdiv2
127} 421}
128 422
129unsafe fn start_xosc() { 423pub fn pll_usb_freq() -> u32 {
130 const XOSC_MHZ: u32 = 12; 424 let p = pac::PLL_USB;
425
426 let input_freq = xosc_freq();
427 let cs = unsafe { p.cs().read() };
428
429 let refdiv = cs.refdiv() as u32;
430 let fbdiv = unsafe { p.fbdiv_int().read().fbdiv_int() } as u32;
431 let (postdiv1, postdiv2) = unsafe {
432 let prim = p.prim().read();
433 (prim.postdiv1() as u32, prim.postdiv2() as u32)
434 };
435
436 (((input_freq / refdiv) * fbdiv) / postdiv1) / postdiv2
437}
438
439pub fn clk_sys_freq() -> u32 {
440 let c = pac::CLOCKS;
441 let ctrl = unsafe { c.clk_sys_ctrl().read() };
442
443 let base = match ctrl.src() {
444 ClkSysCtrlSrc::CLK_REF => clk_ref_freq(),
445 ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX => match ctrl.auxsrc() {
446 ClkSysCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(),
447 ClkSysCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(),
448 ClkSysCtrlAuxsrc::ROSC_CLKSRC => estimate_rosc_freq(),
449 ClkSysCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(),
450 ClkSysCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(),
451 ClkSysCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(),
452 _ => unreachable!(),
453 },
454 _ => unreachable!(),
455 };
456
457 let div = unsafe { c.clk_sys_div().read() };
458 let int = if div.int() == 0 { 65536 } else { div.int() };
459 // TODO handle fractional clock div
460 let _frac = div.frac();
461
462 base / int
463}
464
465pub fn clk_ref_freq() -> u32 {
466 let c = pac::CLOCKS;
467 let ctrl = unsafe { c.clk_ref_ctrl().read() };
468
469 let base = match ctrl.src() {
470 ClkRefCtrlSrc::ROSC_CLKSRC_PH => estimate_rosc_freq(),
471 ClkRefCtrlSrc::XOSC_CLKSRC => xosc_freq(),
472 ClkRefCtrlSrc::CLKSRC_CLK_REF_AUX => match ctrl.auxsrc() {
473 ClkRefCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(),
474 ClkRefCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(),
475 ClkRefCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(),
476 _ => unreachable!(),
477 },
478 _ => unreachable!(),
479 };
480
481 let div = unsafe { c.clk_ref_div().read() };
482 let int = if div.int() == 0 { 4 } else { div.int() as u32 };
483
484 base / int
485}
486
487pub fn clk_peri_freq() -> u32 {
488 let c = pac::CLOCKS;
489 let src = unsafe { c.clk_peri_ctrl().read().auxsrc() };
490
491 match src {
492 ClkPeriCtrlAuxsrc::CLK_SYS => clk_sys_freq(),
493 ClkPeriCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(),
494 ClkPeriCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(),
495 ClkPeriCtrlAuxsrc::ROSC_CLKSRC_PH => estimate_rosc_freq(),
496 ClkPeriCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(),
497 ClkPeriCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(),
498 ClkPeriCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(),
499 _ => unreachable!(),
500 }
501}
502
503pub fn clk_usb_freq() -> u32 {
504 let c = pac::CLOCKS;
505 let ctrl = unsafe { c.clk_usb_ctrl().read() };
506
507 let base = match ctrl.auxsrc() {
508 ClkUsbCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(),
509 ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(),
510 ClkUsbCtrlAuxsrc::ROSC_CLKSRC_PH => estimate_rosc_freq(),
511 ClkUsbCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(),
512 ClkUsbCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(),
513 ClkUsbCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(),
514 _ => unreachable!(),
515 };
516
517 let div = unsafe { c.clk_ref_div().read() };
518 let int = if div.int() == 0 { 4 } else { div.int() as u32 };
519
520 base / int
521}
522
523pub fn clk_adc_freq() -> u32 {
524 let c = pac::CLOCKS;
525 let ctrl = unsafe { c.clk_adc_ctrl().read() };
526
527 let base = match ctrl.auxsrc() {
528 ClkAdcCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(),
529 ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(),
530 ClkAdcCtrlAuxsrc::ROSC_CLKSRC_PH => estimate_rosc_freq(),
531 ClkAdcCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(),
532 ClkAdcCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(),
533 ClkAdcCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(),
534 _ => unreachable!(),
535 };
536
537 let div = unsafe { c.clk_adc_div().read() };
538 let int = if div.int() == 0 { 4 } else { div.int() as u32 };
539
540 base / int
541}
542
543pub fn clk_rtc_freq() -> u32 {
544 let c = pac::CLOCKS;
545 let src = unsafe { c.clk_rtc_ctrl().read().auxsrc() };
546
547 let base = match src {
548 ClkRtcCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(),
549 ClkRtcCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(),
550 ClkRtcCtrlAuxsrc::ROSC_CLKSRC_PH => estimate_rosc_freq(),
551 ClkRtcCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(),
552 ClkRtcCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(),
553 ClkRtcCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(),
554 _ => unreachable!(),
555 };
556
557 let div = unsafe { c.clk_rtc_div().read() };
558 let int = if div.int() == 0 { 65536 } else { div.int() };
559 // TODO handle fractional clock div
560 let _frac = div.frac();
561
562 base / int
563}
564
565unsafe fn start_xosc(crystal_hz: u32) {
131 pac::XOSC 566 pac::XOSC
132 .ctrl() 567 .ctrl()
133 .write(|w| w.set_freq_range(pac::xosc::vals::CtrlFreqRange::_1_15MHZ)); 568 .write(|w| w.set_freq_range(pac::xosc::vals::CtrlFreqRange::_1_15MHZ));
134 569
135 let startup_delay = (((XOSC_MHZ * 1_000_000) / 1000) + 128) / 256; 570 let startup_delay = ((crystal_hz / 1000) + 128) / 256;
136 pac::XOSC.startup().write(|w| w.set_delay(startup_delay as u16)); 571 pac::XOSC.startup().write(|w| w.set_delay(startup_delay as u16));
137 pac::XOSC.ctrl().write(|w| { 572 pac::XOSC.ctrl().write(|w| {
138 w.set_freq_range(pac::xosc::vals::CtrlFreqRange::_1_15MHZ); 573 w.set_freq_range(pac::xosc::vals::CtrlFreqRange::_1_15MHZ);
@@ -141,24 +576,24 @@ unsafe fn start_xosc() {
141 while !pac::XOSC.status().read().stable() {} 576 while !pac::XOSC.status().read().stable() {}
142} 577}
143 578
144unsafe fn configure_pll(p: pac::pll::Pll, refdiv: u32, vco_freq: u32, post_div1: u8, post_div2: u8) { 579unsafe fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) {
145 let ref_freq = XOSC_MHZ * 1_000_000 / refdiv; 580 let ref_freq = input_freq / config.refdiv;
146 581
147 let fbdiv = vco_freq / ref_freq; 582 let fbdiv = config.vco_freq / ref_freq;
148 assert!(fbdiv >= 16 && fbdiv <= 320); 583 assert!(fbdiv >= 16 && fbdiv <= 320);
149 assert!(post_div1 >= 1 && post_div1 <= 7); 584 assert!(config.post_div1 >= 1 && config.post_div1 <= 7);
150 assert!(post_div2 >= 1 && post_div2 <= 7); 585 assert!(config.post_div2 >= 1 && config.post_div2 <= 7);
151 assert!(post_div2 <= post_div1); 586 assert!(config.post_div2 <= config.post_div1);
152 assert!(ref_freq <= (vco_freq / 16)); 587 assert!(ref_freq <= (config.vco_freq / 16));
153 588
154 // do not disrupt PLL that is already correctly configured and operating 589 // do not disrupt PLL that is already correctly configured and operating
155 let cs = p.cs().read(); 590 let cs = p.cs().read();
156 let prim = p.prim().read(); 591 let prim = p.prim().read();
157 if cs.lock() 592 if cs.lock()
158 && cs.refdiv() == refdiv as u8 593 && cs.refdiv() == config.refdiv as u8
159 && p.fbdiv_int().read().fbdiv_int() == fbdiv as u16 594 && p.fbdiv_int().read().fbdiv_int() == fbdiv as u16
160 && prim.postdiv1() == post_div1 595 && prim.postdiv1() == config.post_div1
161 && prim.postdiv2() == post_div2 596 && prim.postdiv2() == config.post_div2
162 { 597 {
163 return; 598 return;
164 } 599 }
@@ -174,7 +609,7 @@ unsafe fn configure_pll(p: pac::pll::Pll, refdiv: u32, vco_freq: u32, post_div1:
174 reset::unreset_wait(peris); 609 reset::unreset_wait(peris);
175 610
176 // Load VCO-related dividers before starting VCO 611 // Load VCO-related dividers before starting VCO
177 p.cs().write(|w| w.set_refdiv(refdiv as _)); 612 p.cs().write(|w| w.set_refdiv(config.refdiv as _));
178 p.fbdiv_int().write(|w| w.set_fbdiv_int(fbdiv as _)); 613 p.fbdiv_int().write(|w| w.set_fbdiv_int(fbdiv as _));
179 614
180 // Turn on PLL 615 // Turn on PLL
@@ -189,14 +624,169 @@ unsafe fn configure_pll(p: pac::pll::Pll, refdiv: u32, vco_freq: u32, post_div1:
189 624
190 // Wait for PLL to lock 625 // Wait for PLL to lock
191 p.prim().write(|w| { 626 p.prim().write(|w| {
192 w.set_postdiv1(post_div1); 627 w.set_postdiv1(config.post_div1);
193 w.set_postdiv2(post_div2); 628 w.set_postdiv2(config.post_div2);
194 }); 629 });
195 630
196 // Turn on post divider 631 // Turn on post divider
197 p.pwr().modify(|w| w.set_postdivpd(false)); 632 p.pwr().modify(|w| w.set_postdivpd(false));
198} 633}
199 634
635pub trait GpinPin: crate::gpio::Pin {
636 fn number(&self) -> usize;
637}
638
639macro_rules! impl_gpinpin {
640 ($name:ident, $pin_num:expr, $gpin_num:expr) => {
641 impl GpinPin for crate::peripherals::$name {
642 fn number(&self) -> usize {
643 $gpin_num
644 }
645 }
646 };
647}
648
649impl_gpinpin!(PIN_20, 20, 0);
650impl_gpinpin!(PIN_22, 22, 1);
651
652pub struct Gpin<'d, T: GpinPin> {
653 gpin: PeripheralRef<'d, T>,
654}
655
656impl<'d, T: GpinPin> Gpin<'d, T> {
657 pub fn new(gpin: impl Peripheral<P = T> + 'd) -> Self {
658 into_ref!(gpin);
659
660 unsafe {
661 gpin.io().ctrl().write(|w| w.set_funcsel(0x08));
662 }
663
664 Self { gpin }
665 }
666}
667
668impl<'d, T: GpinPin> Drop for Gpin<'d, T> {
669 fn drop(&mut self) {
670 unsafe {
671 self.gpin
672 .io()
673 .ctrl()
674 .write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL.0));
675 }
676 }
677}
678
679pub trait GpoutPin: crate::gpio::Pin {
680 fn number(&self) -> usize;
681}
682
683macro_rules! impl_gpoutpin {
684 ($name:ident, $gpout_num:expr) => {
685 impl GpoutPin for crate::peripherals::$name {
686 fn number(&self) -> usize {
687 $gpout_num
688 }
689 }
690 };
691}
692
693impl_gpoutpin!(PIN_21, 0);
694impl_gpoutpin!(PIN_23, 1);
695impl_gpoutpin!(PIN_24, 2);
696impl_gpoutpin!(PIN_25, 3);
697
698pub struct Gpout<'d, T: GpoutPin> {
699 gpout: PeripheralRef<'d, T>,
700}
701
702impl<'d, T: GpoutPin> Gpout<'d, T> {
703 pub fn new(gpout: impl Peripheral<P = T> + 'd) -> Self {
704 into_ref!(gpout);
705
706 unsafe {
707 gpout.io().ctrl().write(|w| w.set_funcsel(0x08));
708 }
709
710 Self { gpout }
711 }
712
713 pub fn set_div(&self, int: u32, frac: u8) {
714 unsafe {
715 let c = pac::CLOCKS;
716 c.clk_gpout_div(self.gpout.number()).write(|w| {
717 w.set_int(int);
718 w.set_frac(frac);
719 });
720 }
721 }
722
723 pub fn set_src(&self, src: ClkGpoutCtrlAuxsrc) {
724 unsafe {
725 let c = pac::CLOCKS;
726 c.clk_gpout_ctrl(self.gpout.number()).modify(|w| {
727 w.set_auxsrc(src);
728 });
729 }
730 }
731
732 pub fn enable(&self) {
733 unsafe {
734 let c = pac::CLOCKS;
735 c.clk_gpout_ctrl(self.gpout.number()).modify(|w| {
736 w.set_enable(true);
737 });
738 }
739 }
740
741 pub fn disable(&self) {
742 unsafe {
743 let c = pac::CLOCKS;
744 c.clk_gpout_ctrl(self.gpout.number()).modify(|w| {
745 w.set_enable(false);
746 });
747 }
748 }
749
750 pub fn get_freq(&self) -> u32 {
751 let c = pac::CLOCKS;
752 let src = unsafe { c.clk_gpout_ctrl(self.gpout.number()).read().auxsrc() };
753
754 let base = match src {
755 ClkGpoutCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(),
756 ClkGpoutCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(),
757 ClkGpoutCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(),
758 ClkGpoutCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(),
759 ClkGpoutCtrlAuxsrc::ROSC_CLKSRC => estimate_rosc_freq(),
760 ClkGpoutCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(),
761 ClkGpoutCtrlAuxsrc::CLK_SYS => clk_sys_freq(),
762 ClkGpoutCtrlAuxsrc::CLK_USB => clk_usb_freq(),
763 ClkGpoutCtrlAuxsrc::CLK_ADC => clk_adc_freq(),
764 ClkGpoutCtrlAuxsrc::CLK_RTC => clk_rtc_freq(),
765 ClkGpoutCtrlAuxsrc::CLK_REF => clk_ref_freq(),
766 _ => unreachable!(),
767 };
768
769 let div = unsafe { c.clk_gpout_div(self.gpout.number()).read() };
770 let int = if div.int() == 0 { 65536 } else { div.int() };
771 // TODO handle fractional clock div
772 let _frac = div.frac();
773
774 base / int
775 }
776}
777
778impl<'d, T: GpoutPin> Drop for Gpout<'d, T> {
779 fn drop(&mut self) {
780 self.disable();
781 unsafe {
782 self.gpout
783 .io()
784 .ctrl()
785 .write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL.0));
786 }
787 }
788}
789
200/// Random number generator based on the ROSC RANDOMBIT register. 790/// Random number generator based on the ROSC RANDOMBIT register.
201/// 791///
202/// This will not produce random values if the ROSC is stopped or run at some 792/// This will not produce random values if the ROSC is stopped or run at some
diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs
index 99f62738d..980ebe7f4 100644
--- a/embassy-rp/src/lib.rs
+++ b/embassy-rp/src/lib.rs
@@ -159,23 +159,35 @@ select_bootloader! {
159} 159}
160 160
161pub mod config { 161pub mod config {
162 use crate::clocks::ClockConfig;
163
162 #[non_exhaustive] 164 #[non_exhaustive]
163 pub struct Config {} 165 pub struct Config {
166 pub clocks: ClockConfig,
167 }
164 168
165 impl Default for Config { 169 impl Default for Config {
166 fn default() -> Self { 170 fn default() -> Self {
167 Self {} 171 Self {
172 clocks: ClockConfig::crystal(12_000_000),
173 }
174 }
175 }
176
177 impl Config {
178 pub fn new(clocks: ClockConfig) -> Self {
179 Self { clocks }
168 } 180 }
169 } 181 }
170} 182}
171 183
172pub fn init(_config: config::Config) -> Peripherals { 184pub fn init(config: config::Config) -> Peripherals {
173 // Do this first, so that it panics if user is calling `init` a second time 185 // Do this first, so that it panics if user is calling `init` a second time
174 // before doing anything important. 186 // before doing anything important.
175 let peripherals = Peripherals::take(); 187 let peripherals = Peripherals::take();
176 188
177 unsafe { 189 unsafe {
178 clocks::init(); 190 clocks::init(config.clocks);
179 #[cfg(feature = "time-driver")] 191 #[cfg(feature = "time-driver")]
180 timer::init(); 192 timer::init();
181 dma::init(); 193 dma::init();
diff --git a/examples/rp/src/bin/gpout.rs b/examples/rp/src/bin/gpout.rs
new file mode 100644
index 000000000..236a653ac
--- /dev/null
+++ b/examples/rp/src/bin/gpout.rs
@@ -0,0 +1,34 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_rp::clocks;
8use embassy_time::{Duration, Timer};
9use {defmt_rtt as _, panic_probe as _};
10
11#[embassy_executor::main]
12async fn main(_spawner: Spawner) {
13 let p = embassy_rp::init(Default::default());
14
15 let gpout3 = clocks::Gpout::new(p.PIN_25);
16 gpout3.set_div(1000, 0);
17 gpout3.enable();
18
19 loop {
20 gpout3.set_src(clocks::GpoutSrc::CLK_SYS);
21 info!(
22 "Pin 25 is now outputing CLK_SYS/1000, should be toggling at {}",
23 gpout3.get_freq()
24 );
25 Timer::after(Duration::from_secs(2)).await;
26
27 gpout3.set_src(clocks::GpoutSrc::CLK_REF);
28 info!(
29 "Pin 25 is now outputing CLK_REF/1000, should be toggling at {}",
30 gpout3.get_freq()
31 );
32 Timer::after(Duration::from_secs(2)).await;
33 }
34}