2024-07-03 - custom quartz for rp2040

rp2040 chip - image from Wikipedia

i'm slowly working on yals – a small project to create digital, linear servo for use with RC model boat. when designing PCB i wanted to try out rp2040 µC (spoiler – quite challenging to hand solder at home), but for most other components i was aiming at things i already had. that included 8MHz quarts, since i have plenty of these since my “AVR times”.

all the pre-made rp2040 boards out there use 12MHz quartz and PLL to bump system clock to 120MHz and USB to 48MHz. in this project i did not even care for the USB, and µC itself is not expecting heavy load, so i decided to use 8MHz and assumed i'll likely anyway turn down a dial on PLL for even less speed.

when i received my boards from a factory and soldered them, i've pushed a classic LED-blinker to see if it works as expected. my heart rose when i saw it blinking… albeit the frequency was off – by 1/3 to be precise. ;) it's expected, as the quarts did not match. so how do you change it?

oh boy – it turned out to be a bit of a journey! after reading a tons of posts and comments, i finally came across a PR that added this functionality to SDK. turned out that until ~1 year ago it wasn't even supported! :/

after digging though details, i've managed to isolate a set of variables that are need to be set and a script: '/opt/pico/pico-sdk/src/rp2_common/hardware_clocks/scripts/vcocalc.py' that calculates these parameters (dividers) for your custom quartz and required frequency:

# with 8MHz quartz, 80MHz clock plz (system clock):
$ /opt/pico/pico-sdk/src/rp2_common/hardware_clocks/scripts/vcocalc.py --input 8 80
Requested: 80.0 MHz
Achieved: 80.0 MHz
REFDIV: 1
FBDIV: 200 (VCO = 1600.0 MHz)
PD1: 5
PD2: 4
# with 8MHz quartz, 48MHz clock plz (USB):
$ /opt/pico/pico-sdk/src/rp2_common/hardware_clocks/scripts/vcocalc.py --input 8 48
Requested: 48.0 MHz
Achieved: 48.0 MHz
REFDIV: 1
FBDIV: 180 (VCO = 1440.0 MHz)
PD1: 6
PD2: 5

now some guess work to how to map these values into the defines from the PR… and then, to set it in the code, just put in proper defines in your CMake files:

# common:
add_definitions(-DSYS_CLK_KHZ=80000)
# clock:
add_definitions(-DPLL_SYS_VCO_FREQ_KHZ=1600000)
add_definitions(-DPLL_SYS_POSTDIV1=5)
add_definitions(-DPLL_SYS_POSTDIV2=4)
# USB:
add_definitions(-DPLL_USB_VCO_FREQ_KHZ=1440000)
add_definitions(-DPLL_USB_POSTDIV1=6)
add_definitions(-DPLL_USB_POSTDIV2=5)

now you might be asking – why do i show clock settings for USB, that i already mentioned i do not care about in this project? here we come to the second point, that caused me some wasted hours… you not only need to setup these values correctly (ensure they are not out of range, that PPL can handle, µC can process at that speeds, etc…) but they need to be set both. always.

if things are off, µC will just not start. dead silence on LED-blinker. nothing. while i'm happy that the mister is now solved, i honestly was expecting a bit more when it comes to user-friendliness here. i.e. i know that 99.9% of boards out there have 12MHz clock, yet still some basic documentation, and a couple of compile-time diagnostics would be helpful and would have saved me many hours of guess work and greping though the SDK sources.

now that it's solved, it's here for you to enjoy ready solution. ;) have fun!

blog/2024/07/03/2024-07-03_-_custom_quartz_for_rp2040.txt · Last modified: 2024/07/03 17:34 by basz
Back to top
Valid CSS Driven by DokuWiki Recent changes RSS feed Valid XHTML 1.0