Set New RP2040 System Clock Rate for PIO
With Internal PLL
Set New RP2040 System Clock Rate for PIO
With Internal PLL
Many RP2040 breakout boards come with a 12MHz external crystal. Inside RP2040, there are two PLLs, one sets a system clock rate at 125MHz, the other is at 48MHz for USB signals.
In my recent application, I need to use PIO to generate a 6.78MHz carrier frequency signal. The PIO instruction speed is one clock cycle. That clock can be exactly the system clock or a divide-down of the system clock. However it is hard to get exactly 6.78MHz from a 125MHz system clock.
To give a concrete example, 125MHz clock will run each PIO instructions at 8ns, so 18 * 8 = 144ns, which is closest to 1/6.78MHz = 147.5ns. But that’s not exact. BTW, the number 18 is easy to achieve with PIO “delays”, for example
".wrap_target",
"set pins, 1 [8]", // total 18 cycles for 6.78MHz (147ns) carrier: 8ns * 18 = 144 ns
"set pins, 0 [8]",
".wrap"
Fortunately, one can change RP2040 system clock frequency by configuring the internal PLL with different dividers.
There is a Rust program (link) to do the PLL divider calculation for you, as long as you change this line to the desired PIO frequency. In my case, 67.8MHz:
let desired_pio_frequency_hz = 67_800_000u64; //6_144_000u64;
Here is the print-out of the calculator program: vco freq = 1356 MHz, post divider 1 = 5; post divider 2 = 4; pio divider = 1

The next step is to set the RP2040 clock using the corresponding divider numbers from the calculator program. Note the PLL_SYS_67P8MHZ struct
// External high-speed crystal on the pico board is 12Mhz
let external_xtal_freq_hz = 12_000_000u32;
let xosc = setup_xosc_blocking(pac.XOSC, external_xtal_freq_hz.Hz())
.map_err(InitError::XoscErr)
.unwrap();
watchdog.enable_tick_generation((external_xtal_freq_hz / 1_000_000) as u8);
let mut clocks = ClocksManager::new(pac.CLOCKS);
pub const PLL_SYS_67P8MHZ: PLLConfig = PLLConfig {
vco_freq: HertzU32::MHz(1356),
refdiv: 1,
post_div1: 5,
post_div2: 4,
};
let pll_sys = setup_pll_blocking(
pac.PLL_SYS,
xosc.operating_frequency().into(),
PLL_SYS_67P8MHZ,
&mut clocks,
&mut pac.RESETS,
)
.unwrap();
let pll_usb = setup_pll_blocking(
pac.PLL_USB,
xosc.operating_frequency().into(),
PLL_USB_48MHZ,
&mut clocks,
&mut pac.RESETS,
)
.unwrap();
clocks.init_default(&xosc, &pll_sys, &pll_usb).unwrap();
Similarly the PIO code of outputting a 6.78MHz carrier signal (divide down a 67.8MHz system clock)
".wrap_target",
"set pins, 1 [4]", // total 10 cycles for 6.78MHz carrier
"set pins, 0 [4]",
".wrap"
On common question, why not set system clock at exactly 6.78MHz?
Because a 10x faster system clock allows 10 PIO instructions to be executed in that time slot, which allows signal demodulation running in a second PIO.
If you need more PIO instructions to run, you can bump system clock speed to 135.6MHz for example.
메타데이터
- post_id
- c4ebf48a9eff
- slug
- set-new-rp2040-system-clock-rate-for-pio-c4ebf48a9eff
- url
- https://medium.com/@circuit4us/set-new-rp2040-system-clock-rate-for-pio-c4ebf48a9eff
- canonical_url
- https://medium.com/@circuit4us/set-new-rp2040-system-clock-rate-for-pio-c4ebf48a9eff
- author_url
- https://medium.com/@circuit4us
- status
- ok
- fetched_at
- 2026-07-21 07:28:30