Skip to content

Commit f03bd8f

Browse files
committed
Add support for external clocks on gpin GPIO pins
1 parent 2fb6306 commit f03bd8f

File tree

5 files changed

+162
-21
lines changed

5 files changed

+162
-21
lines changed
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
//! # gpin External Clocks example
2+
//!
3+
//! This application demonstrates how to clock the processor using an external clock on GPIO20
4+
//!
5+
//! It may need to be adapted to your particular board layout and/or pin assignment.
6+
//!
7+
//! See the top-level `README.md` file for Copyright and license details.
8+
9+
#![no_std]
10+
#![no_main]
11+
12+
use embedded_hal_0_2::digital::v2::ToggleableOutputPin;
13+
// Ensure we halt the program on panic (if we don't mention this crate it won't
14+
// be linked)
15+
use panic_halt as _;
16+
17+
// To use the .MHz() function
18+
use fugit::RateExtU32;
19+
20+
use rp2040_hal::clocks::ClockSource;
21+
// Alias for our HAL crate
22+
use rp2040_hal as hal;
23+
24+
// Necessary HAL types
25+
use hal::{clocks::ClocksManager, gpin::GpIn0, gpio, xosc::setup_xosc_blocking, Clock, Sio};
26+
27+
// A shorter alias for the Peripheral Access Crate, which provides low-level
28+
// register access
29+
use hal::pac;
30+
31+
/// The linker will place this boot block at the start of our program image. We
32+
/// need this to help the ROM bootloader get our code up and running.
33+
/// Note: This boot block is not necessary when using a rp-hal based BSP
34+
/// as the BSPs already perform this step.
35+
#[link_section = ".boot2"]
36+
#[used]
37+
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H;
38+
39+
// The external clock provided to GPIO pin 20.
40+
const GPIN_EXTERNAL_CLOCK_FREQ_HZ: u32 = 1_000_000u32;
41+
// Frequency of the external crystal on the board. This value works for an RPi Pico.
42+
const EXTERNAL_XTAL_FREQ_HZ: u32 = 12_000_000u32;
43+
44+
/// Entry point to our bare-metal application.
45+
///
46+
/// The `#[rp2040_hal::entry]` macro ensures the Cortex-M start-up code calls this function
47+
/// as soon as all global variables and the spinlock are initialised.
48+
///
49+
/// The function configures the RP2040 to accept an external clock on Gpio20,
50+
/// then configures the system clock to run off this clock.
51+
#[rp2040_hal::entry]
52+
fn main() -> ! {
53+
let mut pac = pac::Peripherals::take().unwrap();
54+
55+
let sio = Sio::new(pac.SIO);
56+
57+
let _xosc = setup_xosc_blocking(pac.XOSC, EXTERNAL_XTAL_FREQ_HZ.Hz()).unwrap();
58+
59+
let pins = gpio::Pins::new(
60+
pac.IO_BANK0,
61+
pac.PADS_BANK0,
62+
sio.gpio_bank0,
63+
&mut pac.RESETS,
64+
);
65+
66+
let gpin0_pin = pins.gpio20.reconfigure();
67+
let gpin0: GpIn0 = GpIn0::new(gpin0_pin).set_frequency(GPIN_EXTERNAL_CLOCK_FREQ_HZ.Hz());
68+
69+
let mut clocks = ClocksManager::new(pac.CLOCKS);
70+
71+
clocks
72+
.system_clock
73+
.configure_clock(&gpin0, gpin0.get_freq())
74+
.unwrap();
75+
76+
let mut test_pin = pins.gpio0.into_push_pull_output();
77+
78+
loop {
79+
// Continuously toggle a pin so it's possible to observe on a scope that the pico runs on
80+
// the externally provided frequency, and is synchronized to it.
81+
test_pin.toggle().unwrap();
82+
}
83+
}

rp2040-hal/src/clocks/clock_sources.rs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@
22
33
use super::*;
44
use crate::{
5-
gpio::{
6-
bank0::{Gpio20, Gpio22},
7-
FunctionClock, Pin, PullNone, PullType,
8-
},
5+
gpin,
6+
gpio::{PullNone, PullType},
97
rosc::{Enabled, RingOscillator},
108
};
119

@@ -73,17 +71,17 @@ impl ClockSource for RingOscillator<Enabled> {
7371
}
7472

7573
// GPIN0
76-
pub(crate) type GPin0<M = PullNone> = Pin<Gpio20, FunctionClock, M>;
77-
impl<M: PullType> ClockSource for GPin0<M> {
74+
pub(crate) type GpIn0<M = PullNone> = gpin::GpIn0<M>;
75+
impl<M: PullType> ClockSource for GpIn0<M> {
7876
fn get_freq(&self) -> HertzU32 {
79-
todo!()
77+
self.frequency()
8078
}
8179
}
8280

8381
// GPIN1
84-
pub(crate) type GPin1<M = PullNone> = Pin<Gpio22, FunctionClock, M>;
85-
impl<M: PullType> ClockSource for Pin<Gpio22, FunctionClock, M> {
82+
pub(crate) type GpIn1<M = PullNone> = gpin::GpIn1<M>;
83+
impl<M: PullType> ClockSource for GpIn1<M> {
8684
fn get_freq(&self) -> HertzU32 {
87-
todo!()
85+
self.frequency()
8886
}
8987
}

rp2040-hal/src/clocks/mod.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ mod clock_sources;
8181

8282
use clock_sources::PllSys;
8383

84-
use self::clock_sources::{GPin0, GPin1, PllUsb, Rosc, Xosc};
84+
use self::clock_sources::{GpIn0, GpIn1, PllUsb, Rosc, Xosc};
8585

8686
bitfield::bitfield! {
8787
/// Bit field mapping clock enable bits.
@@ -341,64 +341,64 @@ clocks! {
341341
struct GpioOutput0Clock {
342342
init_freq: 0,
343343
reg: clk_gpout0,
344-
auxsrc: {PllSys:CLKSRC_PLL_SYS, GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1, PllUsb:CLKSRC_PLL_USB, Rosc: ROSC_CLKSRC, Xosc: XOSC_CLKSRC, SystemClock: CLK_SYS, UsbClock: CLK_USB, AdcClock: CLK_ADC, RtcClock: CLK_RTC, ReferenceClock:CLK_REF}
344+
auxsrc: {PllSys:CLKSRC_PLL_SYS, GpIn0:CLKSRC_GPIN0, GpIn1:CLKSRC_GPIN1, PllUsb:CLKSRC_PLL_USB, Rosc: ROSC_CLKSRC, Xosc: XOSC_CLKSRC, SystemClock: CLK_SYS, UsbClock: CLK_USB, AdcClock: CLK_ADC, RtcClock: CLK_RTC, ReferenceClock:CLK_REF}
345345
}
346346
/// GPIO Output 1 Clock
347347
struct GpioOutput1Clock {
348348
init_freq: 0,
349349
reg: clk_gpout1,
350-
auxsrc: {PllSys:CLKSRC_PLL_SYS, GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1, PllUsb:CLKSRC_PLL_USB, Rosc: ROSC_CLKSRC, Xosc: XOSC_CLKSRC, SystemClock: CLK_SYS, UsbClock: CLK_USB, AdcClock: CLK_ADC, RtcClock: CLK_RTC, ReferenceClock:CLK_REF}
350+
auxsrc: {PllSys:CLKSRC_PLL_SYS, GpIn0:CLKSRC_GPIN0, GpIn1:CLKSRC_GPIN1, PllUsb:CLKSRC_PLL_USB, Rosc: ROSC_CLKSRC, Xosc: XOSC_CLKSRC, SystemClock: CLK_SYS, UsbClock: CLK_USB, AdcClock: CLK_ADC, RtcClock: CLK_RTC, ReferenceClock:CLK_REF}
351351
}
352352
/// GPIO Output 2 Clock
353353
struct GpioOutput2Clock {
354354
init_freq: 0,
355355
reg: clk_gpout2,
356-
auxsrc: {PllSys:CLKSRC_PLL_SYS, GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1, PllUsb:CLKSRC_PLL_USB, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC, SystemClock: CLK_SYS, UsbClock: CLK_USB, AdcClock: CLK_ADC, RtcClock: CLK_RTC, ReferenceClock:CLK_REF}
356+
auxsrc: {PllSys:CLKSRC_PLL_SYS, GpIn0:CLKSRC_GPIN0, GpIn1:CLKSRC_GPIN1, PllUsb:CLKSRC_PLL_USB, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC, SystemClock: CLK_SYS, UsbClock: CLK_USB, AdcClock: CLK_ADC, RtcClock: CLK_RTC, ReferenceClock:CLK_REF}
357357
}
358358
/// GPIO Output 3 Clock
359359
struct GpioOutput3Clock {
360360
init_freq: 0,
361361
reg: clk_gpout3,
362-
auxsrc: {PllSys:CLKSRC_PLL_SYS, GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1, PllUsb:CLKSRC_PLL_USB, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC, SystemClock: CLK_SYS, UsbClock: CLK_USB, AdcClock: CLK_ADC, RtcClock: CLK_RTC, ReferenceClock:CLK_REF}
362+
auxsrc: {PllSys:CLKSRC_PLL_SYS, GpIn0:CLKSRC_GPIN0, GpIn1:CLKSRC_GPIN1, PllUsb:CLKSRC_PLL_USB, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC, SystemClock: CLK_SYS, UsbClock: CLK_USB, AdcClock: CLK_ADC, RtcClock: CLK_RTC, ReferenceClock:CLK_REF}
363363
}
364364
/// Reference Clock
365365
struct ReferenceClock {
366366
init_freq: 12_000_000, // Starts from ROSC which actually varies with input voltage etc, but 12 MHz seems to be a good value
367367
reg: clk_ref,
368368
src: {Rosc: ROSC_CLKSRC_PH, Xosc:XOSC_CLKSRC},
369-
auxsrc: {PllUsb:CLKSRC_PLL_USB, GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1}
369+
auxsrc: {PllUsb:CLKSRC_PLL_USB, GpIn0:CLKSRC_GPIN0, GpIn1:CLKSRC_GPIN1}
370370
}
371371
/// System Clock
372372
struct SystemClock {
373373
init_freq: 12_000_000, // ref_clk is 12 MHz
374374
reg: clk_sys,
375375
src: {ReferenceClock: CLK_REF},
376-
auxsrc: {PllSys: CLKSRC_PLL_SYS, PllUsb:CLKSRC_PLL_USB, Rosc: ROSC_CLKSRC, Xosc: XOSC_CLKSRC,GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1}
376+
auxsrc: {PllSys: CLKSRC_PLL_SYS, PllUsb:CLKSRC_PLL_USB, Rosc: ROSC_CLKSRC, Xosc: XOSC_CLKSRC,GpIn0:CLKSRC_GPIN0, GpIn1:CLKSRC_GPIN1}
377377
}
378378
/// Peripheral Clock
379379
struct PeripheralClock {
380380
init_freq: 12_000_000, // sys_clk is 12 MHz
381381
reg: clk_peri,
382-
auxsrc: {SystemClock: CLK_SYS, PllSys: CLKSRC_PLL_SYS, PllUsb:CLKSRC_PLL_USB, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC,GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1 },
382+
auxsrc: {SystemClock: CLK_SYS, PllSys: CLKSRC_PLL_SYS, PllUsb:CLKSRC_PLL_USB, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC,GpIn0:CLKSRC_GPIN0, GpIn1:CLKSRC_GPIN1 },
383383
div: false
384384
}
385385
/// USB Clock
386386
struct UsbClock {
387387
init_freq: 0,
388388
reg: clk_usb,
389-
auxsrc: {PllUsb:CLKSRC_PLL_USB,PllSys: CLKSRC_PLL_SYS, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC,GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1}
389+
auxsrc: {PllUsb:CLKSRC_PLL_USB,PllSys: CLKSRC_PLL_SYS, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC,GpIn0:CLKSRC_GPIN0, GpIn1:CLKSRC_GPIN1}
390390
}
391391
/// Adc Clock
392392
struct AdcClock {
393393
init_freq: 0,
394394
reg: clk_adc,
395-
auxsrc: {PllUsb:CLKSRC_PLL_USB,PllSys: CLKSRC_PLL_SYS, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC,GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1}
395+
auxsrc: {PllUsb:CLKSRC_PLL_USB,PllSys: CLKSRC_PLL_SYS, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC,GpIn0:CLKSRC_GPIN0, GpIn1:CLKSRC_GPIN1}
396396
}
397397
/// RTC Clock
398398
struct RtcClock {
399399
init_freq: 0,
400400
reg: clk_rtc,
401-
auxsrc: {PllUsb:CLKSRC_PLL_USB,PllSys: CLKSRC_PLL_SYS, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC,GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1}
401+
auxsrc: {PllUsb:CLKSRC_PLL_USB,PllSys: CLKSRC_PLL_SYS, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC,GpIn0:CLKSRC_GPIN0, GpIn1:CLKSRC_GPIN1}
402402
}
403403
}
404404

rp2040-hal/src/gpin.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
//! Defines a wrapper for the GPIO pins that can route external clocks into the RP2040.
2+
//!
3+
//! See [2.15.2.3. External Clocks](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf) for more details.
4+
//! Or see [examples/gpin.rs](https://github.com/rp-rs/rp-hal/tree/main/rp2040-hal-examples/src/bin/gpin.rs) for a practical example
5+
6+
use fugit::HertzU32;
7+
8+
use crate::{
9+
gpio::{
10+
bank0::{Gpio20, Gpio22},
11+
FunctionClock, Pin, PullNone, PullType,
12+
},
13+
typelevel::Sealed,
14+
};
15+
16+
macro_rules! gpin {
17+
($id:ident, $pin:ident) => {
18+
/// A gpin pin: a pin that can be used as a clock input.
19+
pub struct $id<M = PullNone>
20+
where
21+
M: PullType,
22+
{
23+
pin: Pin<$pin, FunctionClock, M>,
24+
frequency: HertzU32,
25+
}
26+
27+
impl<M: PullType> $id<M> {
28+
#[doc = concat!("Creates a new ", stringify!($id), " given the input pin.")]
29+
pub fn new(pin: Pin<$pin, FunctionClock, M>) -> Self {
30+
Self {
31+
pin,
32+
frequency: HertzU32::from_raw(0),
33+
}
34+
}
35+
36+
/// Set the frequency of the externally applied clock signal.
37+
/// This frequency is used when computing clock dividers.
38+
pub fn set_frequency(mut self, frequency: HertzU32) -> Self {
39+
self.frequency = frequency;
40+
self
41+
}
42+
43+
/// Retrieve frequency
44+
pub fn frequency(&self) -> HertzU32 {
45+
self.frequency
46+
}
47+
48+
#[doc = concat!("Release the underlying device and ", stringify!($pin), ".")]
49+
pub fn free(self) -> Pin<$pin, FunctionClock, M> {
50+
self.pin
51+
}
52+
}
53+
54+
impl<M: PullType> Sealed for $id<M> {}
55+
};
56+
}
57+
58+
gpin!(GpIn0, Gpio20);
59+
gpin!(GpIn1, Gpio22);

rp2040-hal/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ pub mod clocks;
6161
mod critical_section_impl;
6262
pub mod dma;
6363
mod float;
64+
pub mod gpin;
6465
pub mod gpio;
6566
pub mod i2c;
6667
pub mod multicore;

0 commit comments

Comments
 (0)