Driver: Clock Control¶
1 功能概述¶
clock_control sample
演示了Zephyr Clock Control Driver在PAN1080 SoC上的使用方法,主要包括:
获取某个硬件模块的时钟开启状态
获取AHB/APB1/APB2等总线的时钟频率
另外,此Sample还演示了通过dts overlay修改系统的时钟源、时钟频率、AHB/APB1/APB2分频因子等配置的方法。
2 环境准备¶
PAN1080 EVB 一块
USB-TypeC 线一条(用于供电和查看串口打印 Log)
硬件接线:
使用USB线,将 PC USB 与 EVB USB-TypeC(USB->UART)相连
使用杜邦线将 EVB 上的:
TX0 与 P00 相连
RX0 与 P01 相连
PC 软件: 串口调试助手(UartAssist)或终端工具(SecureCRT),波特率921600
3 编译和烧录¶
例程位置:zephyr\samples_panchip\drivers\clock_control
使用 ZAL 工具可以对其进行编译、烧录、打开 VS Code 调试等操作。关于 ZAL 工具的详细介绍请参考:Zephyr APP Launcher 工具介绍。
4 演示说明¶
本例程中:
代码包含2个子流程:
get_clock_status_of_peripherals()
:演示如何获取硬件模块的时钟是否开启,其涉及如下Clock Control API:clock_control_get_status()
get_clock_rate_of_buses()
:演示如何获取AHB/APB1/APB2等总线的时钟频率,其涉及如下Clock Control API:clock_control_get_rate()
工程中包含2个修改了Clock参数的
DTS Overlay
配置文件:clock-dts-conf-1.overlay
:将DPLL输出由默认的48MHz改为64MHz,系统高速时钟源保持为DPLLclock-dts-conf-2.overlay
:关闭DPLL输出使能,系统高速时钟源(clk_system)由48MHz DPLL(dpll)切换为32MHz晶振(clk_xth),系统低速时钟源(clk_slow)由32768Hz晶振(clk_xtl)切换为32000Hz RC(clk_rcl)
4.1 示例1:获取硬件模块的时钟开关状态¶
PAN1080 SoC硬件上有1个AHB总线和2个APB总线,当程序执行get_clock_status_of_peripherals()
函数时,会获取当前App中近乎所有硬件模块的时钟开启状态,并将它们打印出来:
Start to get clock status of peripherals...
PAN_CLK_AHBPeriph_DMAC:
-> OFF
PAN_CLK_AHBPeriph_GPIO:
-> ON
PAN_CLK_AHBPeriph_SYSTICK:
-> ON
PAN_CLK_AHBPeriph_APB1:
-> ON
PAN_CLK_AHBPeriph_APB2:
-> ON
PAN_CLK_AHBPeriph_AHB:
-> ON
PAN_CLK_AHBPeriph_BLE_32M:
-> OFF
PAN_CLK_AHBPeriph_BLE_32K:
-> OFF
PAN_CLK_AHBPeriph_ROM:
-> OFF
PAN_CLK_AHBPeriph_EFUSE:
-> OFF
PAN_CLK_AHBPeriph_ACC:
-> OFF
PAN_CLK_AHBPeriph_USB_AHB:
-> OFF
PAN_CLK_AHBPeriph_USB_48M:
-> OFF
PAN_CLK_APB1Periph_I2C0:
-> OFF
PAN_CLK_APB1Periph_SPI0:
-> OFF
PAN_CLK_APB1Periph_UART0:
-> ON
PAN_CLK_APB1Periph_PWM0_EN:
-> OFF
PAN_CLK_APB1Periph_ADC:
-> OFF
PAN_CLK_APB1Periph_WDT:
-> OFF
PAN_CLK_APB1Periph_WWDT:
-> OFF
PAN_CLK_APB1Periph_TMR0:
-> OFF
PAN_CLK_APB1Periph_I2SS:
-> OFF
PAN_CLK_APB1Periph_I2SM:
-> OFF
PAN_CLK_APB1Periph_PWM1_EN:
-> OFF
PAN_CLK_APB1Periph_PWM2_EN:
-> OFF
PAN_CLK_APB2Periph_SPI1:
-> OFF
PAN_CLK_APB2Periph_UART1:
-> ON
PAN_CLK_APB2Periph_TMR1:
-> OFF
PAN_CLK_APB2Periph_TMR2:
-> OFF
PAN_CLK_APB2Periph_KSCAN:
-> OFF
PAN_CLK_APB2Periph_QDEC:
-> OFF
Done.
由上可知,AHB总线上,时钟开启的模块有:
GPIO:GPIO模块时钟
SYSTICK:Cortex-M Systick时钟
RCC_APB1:APB1 Bridge的总时钟源
RCC_APB2:APB2 Bridge的总时钟源
RCC_AHB:AHB自身的时钟源
以上这些时钟是系统正常工作必须开启的模块,SoC上电后由硬件默认开启,且一般不应关闭。
APB总线上,时钟开启的模块仅有2个:
APB1总线上的UART0模块
此模块时钟虽然开启了,但多数例程中并未使用到,实际上可以通过在DTS中将其
status
属性配置为disabled
状态,或在程序中调用clock_control_off()
接口将其关闭APB2总线上的UART1模块
PAN1080 EVB默认使用此UART打印Log
4.2 示例2:获取AHB/APB1/APB2等总线的时钟频率¶
当程序执行get_clock_rate_of_buses()
函数时,其会获取当前SoC的AHB、APB1、APB2等三个总线的时钟频率:
Start to get clock rate of buses...
AHB clock rate: 48000000 Hz
APB1 clock rate: 48000000 Hz
APB2 clock rate: 48000000 Hz
Done.
由上可知,当前系统中,AHB、APB1、APB2等三个总线的时钟频率均为48MHz,这是因为在PAN1080 EVB的board级目录下的dts文件中,我们默认将系统高速时钟配置为DPLL 48MHz,且AHB/APB1/APB2总线的分频因子均为1(表示不分频):
/* file: zephyr\boards\arm\pan108xxb5_evb\pan108xxb5_evb.dts */
&clk_xth {
clock-frequency = <DT_FREQ_M(32)>;
/* Enable the 32 MHz high speed crystal oscillator */
status = "okay";
};
&clk_xtl {
clock-frequency = <32768>;
/* Enable the 32 KHz low speed crystal oscillator */
status = "okay";
};
&dpll {
/* f(dpll_output) = f(dpll_input) / clock_div * clock_mult */
clocks = <&clk_xth>;
clock-div = <2>; /* Fixed to 2 */
clock-mult = <3>; /* Can be 3 (48MHz) or 4 (64MHz) */
status = "okay";
};
&rcc {
clock-names = "clk_system", "clk_slow";
clocks = <&dpll &clk_xtl>;
clock-frequency-system = <DT_FREQ_M(48)>;
clock-frequency-slow = <32768>;
ahb-prescaler = <1>;
apb1-prescaler = <1>;
apb2-prescaler = <1>;
};
4.3 示例3:修改时钟配置1¶
这里我们通过使能 App 目录下 DTS Overlay Config File 的方式,演示如何将系统默认的高速时钟由 DPLL 48MHz 修改为 DPLL 64MHz。
打开App目录下的
clock-dts-conf-1.overlay
文件:&dpll { clock-mult = <4>; /* <4> means output 64MHz */ }; &rcc { clock-frequency-system = <DT_FREQ_M(64)>; ahb-prescaler = <1>; apb1-prescaler = <2>; apb2-prescaler = <4>; };
由上可以看到:
dpll
节点下的clock-mult
属性被由<3>
修改成了<4>
,表示DPLL输出由48MHz修改为64MHzrcc
节点下的:clock-frequency-system
属性被由<DT_FREQ_M(48)>
修改成了<DT_FREQ_M(64)>
,与DPLL的输出频率保持一致,表示系统高速时钟源的频率ahb-prescaler
属性未修改,保持为<1>
,表示AHB总线时钟分频因子为1,其频率保持与系统高速时钟源一致(即64MHz)apb1-prescaler
属性被由<1>
修改成了<2>
,表示APB1总线时钟分频因子为2,其频率是AHB时钟的1/2(即32MHz)apb1-prescaler
属性被由<1>
修改成了<4>
,表示APB2总线时钟分频因子为4,其频率是AHB时钟的1/4(即16MHz)
打开 App 目录下的
CMakeLists.txt
文件,取消第5行set()
语句的注释,以令clock-dts-conf-1.overlay
文件作为当前 App 的 DTS Overlay Config 传入 Build System:# SPDX-License-Identifier: Apache-2.0 cmake_minimum_required(VERSION 3.20.0) set(DTC_OVERLAY_FILE "${CMAKE_CURRENT_SOURCE_DIR}/clock-dts-conf-1.overlay") # set(DTC_OVERLAY_FILE "${CMAKE_CURRENT_SOURCE_DIR}/clock-dts-conf-2.overlay") find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(clock_control) target_sources(app PRIVATE src/main.c)
重新编译并烧录修改后的 App 工程,可以看到 AHB 时钟被修改为 64MHz,APB1 时钟被修改为 32MHz,APB2 时钟被修改为 16MHz:
Start to get clock rate of buses... AHB clock rate: 64000000 Hz APB1 clock rate: 32000000 Hz APB2 clock rate: 16000000 Hz Done.
4.4 示例4:修改时钟配置2¶
这里我们通过使能 App 目录下 DTS Overlay Config File 的方式,演示如何将系统默认的高速时钟由 DPLL 48MHz 修改为 XTH 32MHz,并关闭 DPLL 使能,同时将系统默认的低速时钟由 XTL 32768Hz 修改为 RCL 32000Hz。
打开App目录下的
clock-dts-conf-2.overlay
文件:&dpll { status = "disabled"; }; &rcc { clock-names = "clk_system", "clk_slow"; clocks = <&clk_xth &clk_rcl>; clock-frequency-system = <DT_FREQ_M(32)>; clock-frequency-slow = <DT_FREQ_K(32)>; };
由上可以看到:
dpll
节点下的status
属性被由okay
修改成了disabled
,表示系统初始化时不使能 DPLL 时钟模块rcc
节点下的:clock-names
属性未修改,保持为"clk_system", "clk_slow"
,实际上,此属性对应下面clocks
属性中两个时钟的名称,即:clocks
属性数组中的第一个时钟名为 “clk_system”,表示系统使用的高速时钟源;第二个时钟名为 “clk_slow”,表示系统的低速时钟源clocks
属性被由<&dpll &clk_xtl>
修改成了<&clk_xth &clk_rcl>
,表示系统的高速时钟由 dpll 切换成了 clk_xth,低速时钟由 clk_xtl 切换成了 clk_rclclock-frequency-system
属性被由<DT_FREQ_M(48)>
修改成了<DT_FREQ_M(32)>
,与当前的系统高速时钟源 clk_system 对应的 clk_xth 的输出频率保持一致clock-frequency-slow
属性被由<32768>
修改成了<DT_FREQ_K(32)>
,与当前的系统低速时钟源 clk_slow 对应的 clk_rcl 的输出频率保持一致
打开 App 目录下的
CMakeLists.txt
文件,重新注释掉第5行的set()
语句,并取消第6行set()
语句的注释,以令clock-dts-conf-2.overlay
文件作为当前 App 的 DTS Overlay Config 传入 Build System:# SPDX-License-Identifier: Apache-2.0 cmake_minimum_required(VERSION 3.20.0) # set(DTC_OVERLAY_FILE "${CMAKE_CURRENT_SOURCE_DIR}/clock-dts-conf-1.overlay") set(DTC_OVERLAY_FILE "${CMAKE_CURRENT_SOURCE_DIR}/clock-dts-conf-2.overlay") find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(clock_control) target_sources(app PRIVATE src/main.c)
重新编译并烧录修改后的App工程,可以看到AHB/APB1/APB2的时钟均被修改为32MHz:
Start to get clock rate of buses... AHB clock rate: 32000000 Hz APB1 clock rate: 32000000 Hz APB2 clock rate: 32000000 Hz Done.
5 开发者说明¶
Clock Control Driver中还有2个API用于手动开关某个硬件模块的Clock:
clock_control_on()
和clock_control_off()
,其中:clock_control_on()
接口会在各个外设Driver的Init函数中被自动调用,也就是说,我们在App中使用某个外设硬件模块的时候,无需手动开启此模块的时钟;以SPI为例,如果App中使能了SPI Driver(CONFIG_SPI=y
),并且DTS中将SPI的status
属性配置为okay
,则系统在初始化SPI模块的时候,会自动将其时钟打开clock_control_off()
接口不会被外设Driver自动调用,这意味着,如果我们在系统初始化的时候开启了某个外设模块,使用一段时间之后想将其关闭,则可以手动在App中调用clock_control_off()
接口将其关闭
Devicetree 的
rcc
节点中,AHB/APB1/APB2可供配置的分频因数,可以在以下文件中找到:zephyr\dts\bindings\clock\panchip,pan-rcc.yaml
例如,APB1的分频因数只能从其 enum 键值列表中选择,否则将会产生 Build 错误:
apb1-prescaler: type: int required: true description: | APB1 prescaler. Make the actual input clock frequency of peripherals on APB1 bus lower than AHB bus clock frequency, and the actual APB1 bus clock frequency can be computed with the following formula: f(clk_apb1) = f(clk_ahb) / apb1-prescaler. enum: - 1 - 2 - 4 - 6 - 8 - 10 - 12 - 14 - 16 - 18 - 20 - 22 - 24 - 26 - 28 - 30
另外需要注意的是,分频因子应根据实际情况谨慎调整,配置过大(即AHB/APB总线频率过慢)可能导致某些功能不正常;例如,PAN1080 EVB Board中默认使用APB2总线上的UART1模块打印Log,其波特率被配置为921600,如果我们将APB2的分频因子配置过大,则可能导致UART1在初始化的时候无法配置成92100如此高的波特率,从而导致UART1初始化失败。
6 RAM/Flash资源使用情况¶
Memory region Used Size Region Size %age Used
FLASH: 18520 B 1020 KB 1.77%
SRAM: 3888 B 64 KB 5.93%
IDT_LIST: 0 GB 2 KB 0.00%