RP2040的时钟结构相当的简单:
ROSC(环形振荡器),频率精度不到1000ppm,用来做PLL有点困难.
- PLL_SYS = 125MHz (默认) 133MHz (最高)
- PLL_USB = 48MHz (固定)
- CLK_SYS = PLL_SYS/CLK_REF (系统时钟,除非DORMANT模式,否则在这两个时钟中选择)
- CLK_PERI = CLK_SYS (外设时钟)
- CLK_USB = PLL_USB (固定)
- CLK_ADC = PLL_USB (固定)
- CLK_RTC = 46875Hz (用于RTC计数,12MHz / 256 = 46875Hz,CLKDIV_M1 = 46874时,频率1Hz)
- GPCLK INPUT = 从IO输入时钟 (可选)
官方例子Hello 48MHz是从USB取时钟,直接导出到系统时钟.由于系统内目前最快的时钟给是125MHz,而时钟只有除法,所以需要先考虑调高PLL_SYS.
PLL SYS = 12MHz(REF) * 125(FBDIV) = 1500MHZ(VCO) / 6(POSTDIV1) / 2(POSTDIV2) = 125MHz
想提高频率,就得提高VCO的倍率,即调整FBDIV.VCO = 1596MHz,调整PLL_SYS时候记得先让系统切换到PLL_USB,不然系统就进入RESUS了,我修改Hello 48MHz文件得到的结果.
int main() {
stdio_init_all();
printf("Hello, world!\n");
measure_freqs();
// Change clk_sys to be 48MHz. The simplest way is to take this from PLL_USB
// which has a source frequency of 48MHz
clock_configure(clk_sys,
CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX,
CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
48 * MHZ,
48 * MHZ);
// Turn off PLL sys for good measure
pll_deinit(pll_sys);
pll_init(pll_sys, 1, 1596 * MHZ, 6, 2);
clock_configure(clk_sys,
CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX,
CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS,
133 * MHZ,
133 * MHZ);
clock_configure(clk_peri,
0,
CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS,
125 * MHZ,
125 * MHZ);
// Re init uart now that clk_peri has changed
stdio_init_all();
measure_freqs();
printf("Hello, 48MHz");
return 0;
}
另外两个例子,一个是输出时钟,一个是发生RESUS时候紧急重新初始化逻辑,由于代码很简单,这里就不多废话了.
还有XOSC Countdown计数就是用作延迟用途的,官方库的sleep_ms就自带低功耗功能的并且用上了XOSC Countdown.
最后一个知识点DORMANT模式,这是一个低功耗模式,只要一个函数就可以进入,这个函数就是xosc_dormant().
在DORMANT模式下,所有的片上时钟都暂停,降低功耗.和其他传统单片机差不多,可以通过RTC和引脚INT唤醒,如果是RTC唤醒,必须有外部时钟源,进入之前内部时钟必须切换到XOSC(唤醒1ms)或者ROSC启动(唤醒1us),唤醒后需要重新配置PLL.(真TM不智能,很多国产芯片都能自动恢复了,更不说主流老牌的.)
代码参考,记得如果要用做DORMANT模式,不能开启PLL,即输入频率等于输出频率.
ROSC => CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_ROSC_CLKSRC
XOSC => CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_XOSC_CLKSRC
那么,时钟方面就差不多了.