API GUIDE / CPM / OVERVIEW

G32S10 CPM API 概述

模块概述:本文档定义 G32S10 SDK 当前 CPM 模块的正式接口,覆盖以下两层:

1. 文档范围

本文档定义 G32S10 SDK 当前 CPM 模块的正式接口,覆盖以下两层:

  • HAL CPM:面向应用、BSP 和驱动上层的时钟、分频、父时钟和 gate 控制接口

  • LL CPM/AON:面向 CPM/AON 寄存器与位域操作的低层接口

本文档基于以下实现与头文件编写:

  • code/include/drivers/g32s10/cpm/hal_cpm.h

  • code/include/drivers/g32s10/cpm/cpm_regs.h

  • code/include/drivers/g32s10/cpm/ll_cpm.h

  • code/include/drivers/g32s10/cpm/ll_cdr.h

  • code/include/drivers/g32s10/cpm/ll_aon.h

  • code/include/drivers/g32s10/cpm/ll_pm.h

  • code/drivers/g32s10/cpm/hal_cpm_core.c

  • code/drivers/g32s10/cpm/hal_cpm_clk.c

  • code/drivers/g32s10/cpm/hal_cpm_pll.c

  • code/drivers/g32s10/cpm/hal_cpm_cgu.c

  • code/drivers/g32s10/cpm/hal_cpm_i2s_cgu.c

  • code/drivers/g32s10/cpm/hal_cpm_gate.c

  • code/drivers/g32s10/cpm/hal_cpm_summary.c

2. 分层关系

Application / BSP / Driver / Test
           |
           v
        HAL CPM
           |
           v
 LL CPM / LL CDR / LL AON / LL PM
           |
           v
     CPM and AON Registers

分层职责如下:

  • HAL CPM

    • 使用统一 hal_cpm_id_t 描述 clock 和 gate 资源

    • 封装 clock enable、disable、rate、parent 和 gate open/close 语义

    • 维护 clock 软件缓存,包括 parent、rate 和 clock enable 引用计数

    • 对参数进行 SDK 统一检查并返回 error_code_t

  • LL CPM/AON

    • 提供 static inline 寄存器和位域访问入口

    • 不维护软件状态,不做 clock tree 或引用计数语义

    • 不返回 SDK 错误码;读取接口返回寄存器值或 bool

  • cpm_regs.h

    • 定义 cpm_regs_t、aon_regs_t、寄存器实例宏、bit/field mask 和硬件枚举值

3. 构建配置

配置项默认值说明
CONFIG_DRIVER_CPMy编译 G32S10 CPM 驱动。
CONFIG_CPM_ENABLE_STUBn编译 CPM HAL 空实现。开启后 public HAL 符号保留,但不访问 CPM/AON 硬件。

CONFIG_CPM_ENABLE_STUB=y 时,code/drivers/g32s10/cpm/CMakeLists.txt 只编译 hal_cpm_stub.c。stub 行为如下:

接口stub 行为
hal_cpm_init()返回 IM_OK。
hal_cpm_clk_enable() / hal_cpm_clk_disable() / hal_cpm_clk_set_rate_hz() / hal_cpm_clk_set_parent()参数占位后返回 IM_OK。
hal_cpm_clk_is_enabled()输出 false,返回 IM_OK。
hal_cpm_clk_get_rate_hz()输出 0,返回 IM_OK。
hal_cpm_clk_get_parent()输出 HAL_CPM_ID_INVALID,返回 IM_OK。
hal_cpm_rosc2_trim()空操作,返回 IM_OK。

4. 资源模型与公开类型

4.1 统一资源 ID

CPM HAL 使用一个公开枚举类型描述 clock 和 gate:

typedef enum {
    HAL_CPM_ID_CLK_EXTAL = 0,
    ...
    HAL_CPM_ID_GATE_BASE = 0x1000U,
    ...
    HAL_CPM_ID_CLK_NONE = 0xFFFEU,
    HAL_CPM_ID_INVALID = 0xFFFFU,
} hal_cpm_id_t;

ID 空间按范围组织:

范围说明
HAL_CPM_ID_CLK_BASE_BEGIN ~ HAL_CPM_ID_CLK_BASE_END - 1根时钟、主时钟、LPCLK 和 RTCLK。
HAL_CPM_ID_CLK_PLL_BEGIN ~ HAL_CPM_ID_CLK_PLL_END - 1PLL 相关 clock。
HAL_CPM_ID_CLK_CGU_BEGIN ~ HAL_CPM_ID_CLK_CGU_END - 1普通 CGU CDR clock。
HAL_CPM_ID_CLK_I2S_CGU_BEGIN ~ HAL_CPM_ID_CLK_I2S_CGU_END - 1I2S TX/RX CGU clock。
HAL_CPM_ID_GATE_BASE ~ HAL_CPM_ID_GATE_END - 1AON LP/HP0/HP1 gate。
HAL_CPM_ID_CLK_NONE伪 parent id,表示支持无时钟输出的 mux 选择 none。

说明:

  • clock id 可用于 enable、disable、is_enabled、get_rate、set_rate、get_parent、set_parent。

  • HAL_CPM_ID_CLK_NONE 不属于普通 clock range,只能作为 parent 值或 get_parent 的返回值使用。

  • gate id 只支持 enable、disable、is_enabled。

  • 对 gate id 调用 rate 或 parent 接口返回 IM_ERR_UNSUPPORTED。

  • gate id 通过 HAL_CPM_ID_GATE_BASE 加硬件 stop bit offset 表示,中间保留位不是有效 gate。

4.2 PLL ID

ID说明Parent备注
HAL_CPM_ID_CLK_APLLAPLL 输出。HAL_CPM_ID_CLK_EXTAL支持 set_rate。
HAL_CPM_ID_CLK_MPLLMPLL 输出。HAL_CPM_ID_CLK_EXTAL支持 set_rate。
HAL_CPM_ID_CLK_APLL_DIV8APLL/8 派生输出。HAL_CPM_ID_CLK_APLL只读 rate,不支持直接 set_rate。

PLL rate 计算公式:

rate = parent_rate * M / N / OD1 / OD2

当前 HAL 对 APLL/MPLL set_rate 使用动态计算出的 M/N/OD1/OD2 配置,并轮询 PLL on 状态直至置位(最多 100000 次),超时返回 IM_ERR_TIMEOUT。HAL_CPM_ID_CLK_APLL_DIV8 的 rate 来自 APLL rate 除以 8。

4.3 I2S CGU ID

ID说明可选 parent
HAL_CPM_ID_CLK_CGU_I2S_TXI2S TX clock。SCLK_A / MPLL / ROSC2
HAL_CPM_ID_CLK_CGU_I2S_RXI2S RX clock。SCLK_A / MPLL / ROSC2

选择 SCLK_A 或 MPLL 时走 CPM I2S CDR 路径;选择 ROSC2 时走 AON I2S CDR 路径。TX/RX 共用 source mux,切换 source 会影响两个方向。

I2S CGU 使用 M/N 分频:

rate = parent_rate * M / N

当前实现对未使能的 I2S clock 支持保存待写入的 M/N 配置;后续 enable 时再写入硬件。切换 parent 时,如果旧 rate 非 0,HAL 会尝试按新 parent 重新计算 M/N 以保持原频率。

由于 source mux 由 TX/RX 共用,set_parent() 在切换前会检查 peer:如果 peer 已使能且其当前 source 与目标 source 不同,或 peer 未使能但保存的 M/N 是针对旧 source 计算的,则拒绝本次切换并返回 IM_ERR_UNSUPPORTED,避免把 peer 切到错误频率。

5. HAL 与 LL 使用约束

  • 普通业务和驱动上层应优先使用 hal_cpm_clk_* 接口,不直接拼 CDR、CPCCR 或 AON CLKGR 字段。

  • LL 接口适用于 HAL 内部、bring-up、调试和确实需要寄存器级控制的底层模块。

  • 修改 CPCCR/CPCCR1 或 CDR source/divider/STOP 字段后,需要按对应寄存器要求触发 CE/update 并等待 busy 清除;HAL 已封装这些流程。

  • gate close 会直接关闭目标模块时钟,关闭 bus、memory、CPU、PSRAM 等关键 gate 可能导致系统挂起。

  • hal_cpm_clk_get_rate_hz() 返回 HAL 缓存值,不直接读寄存器即时计算;外部绕过 HAL 修改寄存器后,应调用 hal_cpm_clk_get_info() 触发全量刷新,或调用 hal_cpm_init()(同时会清零 enable 引用计数)重新同步。

  • 普通 main clock 和 CGU 的 hal_cpm_clk_set_rate_hz() 向上取整分频,使输出频率不大于目标值;受字段上限限制时可能明显低于目标。I2S 使用 M/N 搜索最接近目标的组合,实际频率可能高于或低于目标。调用方应再读取实际 rate 确认结果。

  • hal_cpm_clk_set_rate_hz()/hal_cpm_clk_set_parent() 只刷新被操作 clock 自身,修改 SCLK_A、MPLL 等共享 parent 后,依赖它的子 clock 缓存可能仍是旧值。

  • PLL 动态改频会修改 PLL 控制寄存器并轮询 PLL on 状态,调用方应评估下游 clock 对频率变化的容忍度。

6. 典型使用

6.1 初始化并读取 clock rate

#include "hal_cpm.h"

error_code_t ret;
uint32_t rate_hz;

ret = hal_cpm_init();
if (ret != IM_OK) {
    return ret;
}

ret = hal_cpm_clk_get_rate_hz(HAL_CPM_ID_CLK_CGU_UART, &rate_hz);
if (ret != IM_OK) {
    return ret;
}

6.2 设置 CGU 频率并打开输出

ret = hal_cpm_clk_set_parent(HAL_CPM_ID_CLK_CGU_UART,
                             HAL_CPM_ID_CLK_EXTAL);
if (ret != IM_OK) {
    return ret;
}

ret = hal_cpm_clk_set_rate_hz(HAL_CPM_ID_CLK_CGU_UART, 24000000U);
if (ret != IM_OK) {
    return ret;
}

ret = hal_cpm_clk_enable(HAL_CPM_ID_CLK_CGU_UART);
if (ret != IM_OK) {
    return ret;
}

6.3 打开模块 gate

ret = hal_cpm_clk_enable(HAL_CPM_ID_GATE_UART1);
if (ret != IM_OK) {
    return ret;
}

6.4 查询 gate 状态

bool open;

ret = hal_cpm_clk_is_enabled(HAL_CPM_ID_GATE_UART1, &open);
if (ret != IM_OK) {
    return ret;
}