From f03bd8f4657ca7ed8e02c2d6c2c4e341d2aea14e Mon Sep 17 00:00:00 2001 From: Pieter Staal Date: Fri, 27 Dec 2024 12:44:08 +0100 Subject: [PATCH 1/3] Add support for external clocks on gpin GPIO pins --- rp2040-hal-examples/src/bin/gpin.rs | 83 ++++++++++++++++++++++++++ rp2040-hal/src/clocks/clock_sources.rs | 18 +++--- rp2040-hal/src/clocks/mod.rs | 22 +++---- rp2040-hal/src/gpin.rs | 59 ++++++++++++++++++ rp2040-hal/src/lib.rs | 1 + 5 files changed, 162 insertions(+), 21 deletions(-) create mode 100644 rp2040-hal-examples/src/bin/gpin.rs create mode 100644 rp2040-hal/src/gpin.rs diff --git a/rp2040-hal-examples/src/bin/gpin.rs b/rp2040-hal-examples/src/bin/gpin.rs new file mode 100644 index 000000000..e66b34070 --- /dev/null +++ b/rp2040-hal-examples/src/bin/gpin.rs @@ -0,0 +1,83 @@ +//! # 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, xosc::setup_xosc_blocking, 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; +// Frequency of the external crystal on the board. This value works for an RPi Pico. +const EXTERNAL_XTAL_FREQ_HZ: u32 = 12_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 _xosc = setup_xosc_blocking(pac.XOSC, EXTERNAL_XTAL_FREQ_HZ.Hz()).unwrap(); + + 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).set_frequency(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..fe38af8be --- /dev/null +++ b/rp2040-hal/src/gpin.rs @@ -0,0 +1,59 @@ +//! 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>) -> Self { + Self { + pin, + frequency: HertzU32::from_raw(0), + } + } + + /// 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; From eae0599eb474fa14801497bf3194325352eeaf9e Mon Sep 17 00:00:00 2001 From: Pieter Staal Date: Fri, 27 Dec 2024 20:55:34 +0100 Subject: [PATCH 2/3] Add frequency as argument to new of GpIn --- rp2040-hal-examples/src/bin/gpin.rs | 2 +- rp2040-hal/src/gpin.rs | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/rp2040-hal-examples/src/bin/gpin.rs b/rp2040-hal-examples/src/bin/gpin.rs index e66b34070..d6499104a 100644 --- a/rp2040-hal-examples/src/bin/gpin.rs +++ b/rp2040-hal-examples/src/bin/gpin.rs @@ -64,7 +64,7 @@ fn main() -> ! { ); let gpin0_pin = pins.gpio20.reconfigure(); - let gpin0: GpIn0 = GpIn0::new(gpin0_pin).set_frequency(GPIN_EXTERNAL_CLOCK_FREQ_HZ.Hz()); + let gpin0: GpIn0 = GpIn0::new(gpin0_pin, GPIN_EXTERNAL_CLOCK_FREQ_HZ.Hz()); let mut clocks = ClocksManager::new(pac.CLOCKS); diff --git a/rp2040-hal/src/gpin.rs b/rp2040-hal/src/gpin.rs index fe38af8be..1038734ec 100644 --- a/rp2040-hal/src/gpin.rs +++ b/rp2040-hal/src/gpin.rs @@ -26,11 +26,8 @@ macro_rules! gpin { impl $id { #[doc = concat!("Creates a new ", stringify!($id), " given the input pin.")] - pub fn new(pin: Pin<$pin, FunctionClock, M>) -> Self { - Self { - pin, - frequency: HertzU32::from_raw(0), - } + pub fn new(pin: Pin<$pin, FunctionClock, M>, frequency: HertzU32) -> Self { + Self { pin, frequency } } /// Set the frequency of the externally applied clock signal. From a8c902f9e979b0ff1cf8c65aaeae06d8ac83c25e Mon Sep 17 00:00:00 2001 From: Pieter Staal Date: Tue, 31 Dec 2024 10:48:10 +0100 Subject: [PATCH 3/3] Remove XOSC setup from example --- rp2040-hal-examples/src/bin/gpin.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/rp2040-hal-examples/src/bin/gpin.rs b/rp2040-hal-examples/src/bin/gpin.rs index d6499104a..9d37174c9 100644 --- a/rp2040-hal-examples/src/bin/gpin.rs +++ b/rp2040-hal-examples/src/bin/gpin.rs @@ -22,7 +22,7 @@ use rp2040_hal::clocks::ClockSource; use rp2040_hal as hal; // Necessary HAL types -use hal::{clocks::ClocksManager, gpin::GpIn0, gpio, xosc::setup_xosc_blocking, Clock, Sio}; +use hal::{clocks::ClocksManager, gpin::GpIn0, gpio, Clock, Sio}; // A shorter alias for the Peripheral Access Crate, which provides low-level // register access @@ -38,8 +38,6 @@ 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; -// Frequency of the external crystal on the board. This value works for an RPi Pico. -const EXTERNAL_XTAL_FREQ_HZ: u32 = 12_000_000u32; /// Entry point to our bare-metal application. /// @@ -54,8 +52,6 @@ fn main() -> ! { let sio = Sio::new(pac.SIO); - let _xosc = setup_xosc_blocking(pac.XOSC, EXTERNAL_XTAL_FREQ_HZ.Hz()).unwrap(); - let pins = gpio::Pins::new( pac.IO_BANK0, pac.PADS_BANK0,