跳转至

PM Clock

PM Clock 驱动 | 无 sample

学习目标

  • 理解 PM Clock 两大核心功能——时钟门控(Clock Gating)和 CRG 时钟源/分频配置
  • 掌握 uapi_clock_clken_config 开关外设时钟以节省功耗
  • 掌握 uapi_clock_crg_config 配置时钟源和分频系数
  • 理解时钟分频器、选择器、CRG 三种配置路径的区别

基本概念

时钟树架构

WS63 的时钟系统分层管理:时钟源(PLL/XTAL/RC)→ CRG(时钟源选择 + 分频)→ 时钟门控(开/关)→ 外设模块。

flowchart TB
    PLL[PLL 锁相环] -->|高频| CRG
    XTAL[XTAL 晶振] -->|基准| CRG
    RC[RC 振荡器] -->|低频| CRG
    CRG[CRG 时钟控制] -->|源选择 + 分频| DIV[分频器]
    DIV -->|具体频率| CLKEN[时钟门控]
    CLKEN -->|clk_en = true| UART[UART 模块]
    CLKEN -->|clk_en = false| OFF[关闭 省电]
    DDS[时钟选择器] -->|clk_sel| DIV2[独立分频]
    DIV2 -->|独立频率| SPI[SPI 模块]

三项配置路径

路径 API 作用 粒度
时钟门控 uapi_clock_clken_config 开/关外设时钟 模块级
CRG 配置 uapi_clock_crg_config 时钟源选择 + 分频 模块级
选择器+分频器 uapi_clock_clksel_config + uapi_clock_clkdiv_config 独立配置选择和分频 信号级

时钟门控——省电的核心

CPU 空闲时可休眠,但外设模块只要有时钟就会消耗动态功耗。Clock Gating 思路简单:不用某外设时关其时钟。例如:

  • UART 不在通信时 → 关 UART 时钟
  • SPI 传输完成后 → 关 SPI 时钟
  • GPIO 模块永远需要 → 始终开启

CRG 频率计算

f_out = f_source / clk_div

例如:PLL 输出 480MHz,clk_div = 4 → SPI 时钟 = 120MHz。通过 uapi_clock_crg_get_freq 查询当前实际频率。

涉及 API

API 用途 头文件
uapi_clock_clken_config(clock_clken_id_t id, bool clk_en) 外设时钟门控开关 pm_clock.h
uapi_clock_crg_config(clock_crg_id_t id, clock_clk_src_t clk_src, uint8_t clk_div) 配置 CRG 时钟源和分频(需 CONFIG_PM_SUPPORT_CLOCK_CRG_API pm_clock.h
uapi_clock_crg_get_freq(clock_crg_id_t id) 获取 CRG 模块当前频率 pm_clock.h
uapi_clock_clksel_config(clock_clksel_id_t id, uint8_t clk_sel) 配置时钟选择器 pm_clock.h
uapi_clock_clkdiv_config(clock_clkdiv_id_t id, uint8_t clk_div) 配置时钟分频系数 pm_clock.h
uapi_clock_get_clkdiv(clock_clkdiv_id_t id) 查询分频系数 pm_clock.h
uapi_clock_control(clock_control_type_t type, uint8_t param) 通用时钟控制接口 pm_clock.h

clock_clken_id_tclock_crg_id_t 均为芯片 porting 层定义的枚举——具体 ID 值参考 pm_clock_porting.h

案例说明

做什么

  • 关闭空闲 UART 模块的时钟以节省功耗
  • 配置 SPI 模块 CRG:时钟源 PLL,分频系数 4
  • 查询并打印 SPI 实际工作频率

规格与功能

规格项 说明
UART 处理 关闭 CLOCK_CLKEN_UART2 时钟
SPI CRG CLOCK_CLK_SRC_PLL,分频 /4
验证方法 crg_get_freq 查询频率并打印

程序运行流程:clken_config 关 UART2 → crg_config 设 SPI PLL/4 → crg_get_freq 查 SPI 频率 → 打印。

案例流程

sequenceDiagram
    participant A as 应用
    participant C as 时钟控制器
    participant U as UART2模块
    participant S as SPI0模块

    A->>C: clken_config UART2=false
    C->>U: 关闭时钟
    Note over U: UART2 停止功耗

    A->>C: crg_config SPI0 src=PLL div=4
    C->>S: 源PLL 分频4
    Note over S: SPI0 频率 = PLL/4

    A->>C: crg_get_freq SPI0
    C->>A: freq = 120000000
    A->>A: print SPI clock=120MHz

案例操作指导

第一步:编译

fbb build ws63-liteos-app

第二步:烧录和验证

烧录后串口输出 SPI 配置前后的频率变化。可用功耗仪对比:UART2 时钟开启和关闭时的电流差异。

第三步:预期输出

PM Clock: disable UART2 clock
PM Clock: config SPI0 CRG: src=PLL, div=4
PM Clock: SPI0 freq = 120000000 Hz
PM Clock: verify: div=4

关键配置

参数 说明
时钟源选择 PLL / XTAL / RC PLL 高频高性能,XTAL 稳定,RC 低功耗
分频系数 1~255 过大 → 频率过低外设不工作;过小 → 超频
门控开关 true/false 关错时钟导致外设不响应——确认模块空闲再关
CRG 频率查询 返回 0 表示失败 先确认 CRG 配置成功再查询

时钟源选择

时钟源 频率 精度 功耗 适用
PLL 数百 MHz 中等(锁相环锁定时间) 高速外设(SPI/SDIO)
XTAL 稳定(如 40MHz) 高(晶振精度) 低抖动需求(UART 精确波特率)
RC 数 MHz 低(温漂) 极低 休眠时维持计时

功耗优化策略

策略 实现 省电效果
关空闲模块时钟 clken_config(id, false) 显著
降低非活跃模块频率 crg_config 增大分频 中等
休眠时切 RC 源 crg_config 切换源 显著
动态频率调节 任务负载低时降频 中等

代码详解

概念性代码,基于 SDK 头文件 pm_clock.h 中的 API 签名。

#include "pm_clock.h"
#include "errcode.h"
#include "osal_printk.h"
#include "app_init.h"

static void pm_clock_entry(void)
{
    errcode_t ret;
    uint32_t freq;
    uint8_t div;

    /* ---- 1. 关闭空闲 UART 时钟——省电 ---- */
    uapi_clock_clken_config(CLOCK_CLKEN_UART2, false);
    osal_printk("PM Clock: disable UART2 clock\n");

    /* ---- 2. 配置 SPI0 CRG:PLL 源,4 分频 ---- */
    ret = uapi_clock_crg_config(CLOCK_CRG_SPI0,
                                 CLOCK_CLK_SRC_PLL,
                                 4);
    if (ret != ERRCODE_SUCC) {
        osal_printk("PM Clock: SPI0 CRG config failed: %d\n", ret);
        return;
    }
    osal_printk("PM Clock: config SPI0 CRG: src=PLL, div=4\n");

    /* ---- 3. 查询 SPI0 实际频率 ---- */
    freq = uapi_clock_crg_get_freq(CLOCK_CRG_SPI0);
    if (freq > 0) {
        osal_printk("PM Clock: SPI0 freq = %u Hz\n", freq);
    } else {
        osal_printk("PM Clock: SPI0 freq query failed\n");
    }

    /* ---- 4. 验证分频系数 ---- */
    div = uapi_clock_get_clkdiv(CLOCK_CLKDIV_SPI0);
    osal_printk("PM Clock: verify: div=%u\n", div);

    /* ---- 5. SPI 使用完毕后关闭时钟 ---- */
    uapi_clock_clken_config(CLOCK_CLKEN_SPI0, false);
    osal_printk("PM Clock: disable SPI0 clock (idle)\n");
}

app_run(pm_clock_entry);

时钟门控和 CRG 配置互不影响——可先配 CRG 决定频率,再通过门控开关控制启停。关外设时钟前需确认该外设没有正在进行的传输——否则数据丢失。CRG 配置受 CONFIG_PM_SUPPORT_CLOCK_CRG_API 宏控制——若不启用,仅门控和选择器/分频器路径可用。