diff --git a/rp2040-hal-examples/src/bin/gpin.rs b/rp2040-hal-examples/src/bin/gpin.rs new file mode 100644 index 000000000..9d37174c9 --- /dev/null +++ b/rp2040-hal-examples/src/bin/gpin.rs @@ -0,0 +1,79 @@ +//! # gpin External Clocks example +//! +//! This application demonstrates how to clock the processor using an external clock on GPIO20 +//! +//! It may need to be adapted to your particular board layout and/or pin assignment. +//! +//! See the top-level `README.md` file for Copyright and license details. + +#![no_std] +#![no_main] + +use embedded_hal_0_2::digital::v2::ToggleableOutputPin; +// Ensure we halt the program on panic (if we don't mention this crate it won't +// be linked) +use panic_halt as _; + +// To use the .MHz() function +use fugit::RateExtU32; + +use rp2040_hal::clocks::ClockSource; +// Alias for our HAL crate +use rp2040_hal as hal; + +// Necessary HAL types +use hal::{clocks::ClocksManager, gpin::GpIn0, gpio, Clock, Sio}; + +// A shorter alias for the Peripheral Access Crate, which provides low-level +// register access +use hal::pac; + +/// The linker will place this boot block at the start of our program image. We +/// need this to help the ROM bootloader get our code up and running. +/// Note: This boot block is not necessary when using a rp-hal based BSP +/// as the BSPs already perform this step. +#[link_section = ".boot2"] +#[used] +pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H; + +// The external clock provided to GPIO pin 20. +const GPIN_EXTERNAL_CLOCK_FREQ_HZ: u32 = 1_000_000u32; + +/// Entry point to our bare-metal application. +/// +/// The `#[rp2040_hal::entry]` macro ensures the Cortex-M start-up code calls this function +/// as soon as all global variables and the spinlock are initialised. +/// +/// The function configures the RP2040 to accept an external clock on Gpio20, +/// then configures the system clock to run off this clock. +#[rp2040_hal::entry] +fn main() -> ! { + let mut pac = pac::Peripherals::take().unwrap(); + + let sio = Sio::new(pac.SIO); + + let pins = gpio::Pins::new( + pac.IO_BANK0, + pac.PADS_BANK0, + sio.gpio_bank0, + &mut pac.RESETS, + ); + + let gpin0_pin = pins.gpio20.reconfigure(); + let gpin0: GpIn0 = GpIn0::new(gpin0_pin, GPIN_EXTERNAL_CLOCK_FREQ_HZ.Hz()); + + let mut clocks = ClocksManager::new(pac.CLOCKS); + + clocks + .system_clock + .configure_clock(&gpin0, gpin0.get_freq()) + .unwrap(); + + let mut test_pin = pins.gpio0.into_push_pull_output(); + + loop { + // Continuously toggle a pin so it's possible to observe on a scope that the pico runs on + // the externally provided frequency, and is synchronized to it. + test_pin.toggle().unwrap(); + } +} diff --git a/rp2040-hal/src/clocks/clock_sources.rs b/rp2040-hal/src/clocks/clock_sources.rs index 8189d757a..704f21598 100644 --- a/rp2040-hal/src/clocks/clock_sources.rs +++ b/rp2040-hal/src/clocks/clock_sources.rs @@ -2,10 +2,8 @@ use super::*; use crate::{ - gpio::{ - bank0::{Gpio20, Gpio22}, - FunctionClock, Pin, PullNone, PullType, - }, + gpin, + gpio::{PullNone, PullType}, rosc::{Enabled, RingOscillator}, }; @@ -73,17 +71,17 @@ impl ClockSource for RingOscillator { } // GPIN0 -pub(crate) type GPin0 = Pin; -impl ClockSource for GPin0 { +pub(crate) type GpIn0 = gpin::GpIn0; +impl ClockSource for GpIn0 { fn get_freq(&self) -> HertzU32 { - todo!() + self.frequency() } } // GPIN1 -pub(crate) type GPin1 = Pin; -impl ClockSource for Pin { +pub(crate) type GpIn1 = gpin::GpIn1; +impl ClockSource for GpIn1 { fn get_freq(&self) -> HertzU32 { - todo!() + self.frequency() } } diff --git a/rp2040-hal/src/clocks/mod.rs b/rp2040-hal/src/clocks/mod.rs index f2c4c88ef..c0c476c24 100644 --- a/rp2040-hal/src/clocks/mod.rs +++ b/rp2040-hal/src/clocks/mod.rs @@ -81,7 +81,7 @@ mod clock_sources; use clock_sources::PllSys; -use self::clock_sources::{GPin0, GPin1, PllUsb, Rosc, Xosc}; +use self::clock_sources::{GpIn0, GpIn1, PllUsb, Rosc, Xosc}; bitfield::bitfield! { /// Bit field mapping clock enable bits. @@ -341,64 +341,64 @@ clocks! { struct GpioOutput0Clock { init_freq: 0, reg: clk_gpout0, - 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} + 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} } /// GPIO Output 1 Clock struct GpioOutput1Clock { init_freq: 0, reg: clk_gpout1, - 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} + 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} } /// GPIO Output 2 Clock struct GpioOutput2Clock { init_freq: 0, reg: clk_gpout2, - 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} + 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} } /// GPIO Output 3 Clock struct GpioOutput3Clock { init_freq: 0, reg: clk_gpout3, - 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} + 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} } /// Reference Clock struct ReferenceClock { init_freq: 12_000_000, // Starts from ROSC which actually varies with input voltage etc, but 12 MHz seems to be a good value reg: clk_ref, src: {Rosc: ROSC_CLKSRC_PH, Xosc:XOSC_CLKSRC}, - auxsrc: {PllUsb:CLKSRC_PLL_USB, GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1} + auxsrc: {PllUsb:CLKSRC_PLL_USB, GpIn0:CLKSRC_GPIN0, GpIn1:CLKSRC_GPIN1} } /// System Clock struct SystemClock { init_freq: 12_000_000, // ref_clk is 12 MHz reg: clk_sys, src: {ReferenceClock: CLK_REF}, - auxsrc: {PllSys: CLKSRC_PLL_SYS, PllUsb:CLKSRC_PLL_USB, Rosc: ROSC_CLKSRC, Xosc: XOSC_CLKSRC,GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1} + auxsrc: {PllSys: CLKSRC_PLL_SYS, PllUsb:CLKSRC_PLL_USB, Rosc: ROSC_CLKSRC, Xosc: XOSC_CLKSRC,GpIn0:CLKSRC_GPIN0, GpIn1:CLKSRC_GPIN1} } /// Peripheral Clock struct PeripheralClock { init_freq: 12_000_000, // sys_clk is 12 MHz reg: clk_peri, - 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 }, + 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 }, div: false } /// USB Clock struct UsbClock { init_freq: 0, reg: clk_usb, - auxsrc: {PllUsb:CLKSRC_PLL_USB,PllSys: CLKSRC_PLL_SYS, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC,GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1} + auxsrc: {PllUsb:CLKSRC_PLL_USB,PllSys: CLKSRC_PLL_SYS, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC,GpIn0:CLKSRC_GPIN0, GpIn1:CLKSRC_GPIN1} } /// Adc Clock struct AdcClock { init_freq: 0, reg: clk_adc, - auxsrc: {PllUsb:CLKSRC_PLL_USB,PllSys: CLKSRC_PLL_SYS, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC,GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1} + auxsrc: {PllUsb:CLKSRC_PLL_USB,PllSys: CLKSRC_PLL_SYS, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC,GpIn0:CLKSRC_GPIN0, GpIn1:CLKSRC_GPIN1} } /// RTC Clock struct RtcClock { init_freq: 0, reg: clk_rtc, - auxsrc: {PllUsb:CLKSRC_PLL_USB,PllSys: CLKSRC_PLL_SYS, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC,GPin0:CLKSRC_GPIN0, GPin1:CLKSRC_GPIN1} + auxsrc: {PllUsb:CLKSRC_PLL_USB,PllSys: CLKSRC_PLL_SYS, Rosc: ROSC_CLKSRC_PH, Xosc: XOSC_CLKSRC,GpIn0:CLKSRC_GPIN0, GpIn1:CLKSRC_GPIN1} } } diff --git a/rp2040-hal/src/gpin.rs b/rp2040-hal/src/gpin.rs new file mode 100644 index 000000000..1038734ec --- /dev/null +++ b/rp2040-hal/src/gpin.rs @@ -0,0 +1,56 @@ +//! Defines a wrapper for the GPIO pins that can route external clocks into the RP2040. +//! +//! See [2.15.2.3. External Clocks](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf) for more details. +//! 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 + +use fugit::HertzU32; + +use crate::{ + gpio::{ + bank0::{Gpio20, Gpio22}, + FunctionClock, Pin, PullNone, PullType, + }, + typelevel::Sealed, +}; + +macro_rules! gpin { + ($id:ident, $pin:ident) => { + /// A gpin pin: a pin that can be used as a clock input. + pub struct $id + where + M: PullType, + { + pin: Pin<$pin, FunctionClock, M>, + frequency: HertzU32, + } + + impl $id { + #[doc = concat!("Creates a new ", stringify!($id), " given the input pin.")] + pub fn new(pin: Pin<$pin, FunctionClock, M>, frequency: HertzU32) -> Self { + Self { pin, frequency } + } + + /// Set the frequency of the externally applied clock signal. + /// This frequency is used when computing clock dividers. + pub fn set_frequency(mut self, frequency: HertzU32) -> Self { + self.frequency = frequency; + self + } + + /// Retrieve frequency + pub fn frequency(&self) -> HertzU32 { + self.frequency + } + + #[doc = concat!("Release the underlying device and ", stringify!($pin), ".")] + pub fn free(self) -> Pin<$pin, FunctionClock, M> { + self.pin + } + } + + impl Sealed for $id {} + }; +} + +gpin!(GpIn0, Gpio20); +gpin!(GpIn1, Gpio22); diff --git a/rp2040-hal/src/lib.rs b/rp2040-hal/src/lib.rs index 2c16312e8..3b3574866 100644 --- a/rp2040-hal/src/lib.rs +++ b/rp2040-hal/src/lib.rs @@ -61,6 +61,7 @@ pub mod clocks; mod critical_section_impl; pub mod dma; mod float; +pub mod gpin; pub mod gpio; pub mod i2c; pub mod multicore;