API GUIDE / AON / HAL
AON HAL 接口
1. HAL 接口
1.1 初始化
error_code_t hal_cpm_init(void);
功能:
- 清零 HAL clock enable 引用计数
- 从当前硬件寄存器刷新 clock parent 和 rate 缓存
返回值:
| 返回值 | 说明 |
|---|---|
IM_OK |
初始化成功。 |
IM_ERR_INVALID_PARAM |
内部 clock 表或硬件读取结果异常。 |
| 其它错误码 | 刷新某个 clock parent/rate 时返回的底层错误。 |
1.2 Enable / Disable / Is Enabled
error_code_t hal_cpm_clk_enable(hal_cpm_id_t id);
error_code_t hal_cpm_clk_disable(hal_cpm_id_t id);
error_code_t hal_cpm_clk_is_enabled(hal_cpm_id_t id, bool *is_enabled_out);
clock id 语义:
hal_cpm_clk_enable()会维护 clock enable 引用计数。- 引用计数从 0 变成 1 时,才调用对应 clock 类型的硬件 enable。
- 多次 enable 需要对应多次 disable;引用计数大于 1 时 disable 只递减计数,不关闭硬件。
- 对当前无硬件 disable 控制的 base/PLL clock,最后一次 disable 只清软件引用计数。
HAL_CPM_ID_CLK_CGU_UART与HAL_CPM_ID_CLK_CGU_LP_UART共用一个物理 STOP 位和引用计数;对任一 ID 调用enable、disable或is_enabled都按该物理时钟处理。
gate id 语义:
- 不维护引用计数。
- enable 直接打开 gate,disable 直接关闭 gate。
返回值:
| 返回值 | 说明 |
|---|---|
IM_OK |
操作成功。 |
IM_ERR_INVALID_PARAM |
id 非法或输出指针为空。 |
IM_ERR_BUSY |
clock enable 引用计数达到上限。 |
IM_ERR_TIMEOUT |
等待 PLL on 状态或 CDR busy 清除超时。 |
IM_ERR_UNSUPPORTED |
指定 clock 类型不支持该操作。 |
1.3 Rate
error_code_t hal_cpm_clk_get_rate_hz(hal_cpm_id_t id, uint32_t *rate_hz_out);
error_code_t hal_cpm_clk_set_rate_hz(hal_cpm_id_t id, uint32_t rate_hz);
说明:
- rate 单位固定为 Hz。
get_rate_hz读取 HAL 缓存值;hal_cpm_clk_get_info()每次调用会先全量刷新 parent/rate 缓存。set_rate_hz、set_parent成功后刷新被操作 clock 自身的 parent/rate 缓存; UART 与 LP_UART 共享uartcdr,会同时刷新两者。其它依赖该 clock 的子节点不会递归刷新。set_rate_hz计算出的 CDR divider 字段与当前硬件值相同时不写寄存器、不触发 CE,因此重复设置同一频率不会产生额外的共享时钟重配窗口。- gate id 不支持 rate 接口。
返回值:
| 返回值 | 说明 |
|---|---|
IM_OK |
操作成功。 |
IM_ERR_INVALID_PARAM |
id 非法、输出指针为空或 rate 参数非法。 |
IM_ERR_NOT_READY |
当前 parent 频率为 0,无法计算目标分频。 |
IM_ERR_UNSUPPORTED |
资源不支持设置频率,或目标模式与该 clock 的当前配置冲突。 |
IM_ERR_TIMEOUT |
等待 CDR busy 清除或 PLL on 状态超时。 |
1.4 Parent
error_code_t hal_cpm_clk_get_parent(hal_cpm_id_t id,
hal_cpm_id_t *parent_id_out);
error_code_t hal_cpm_clk_set_parent(hal_cpm_id_t id,
hal_cpm_id_t parent_id);
说明:
- parent 参数必须是可用作 parent 的 clock id 或
HAL_CPM_ID_CLK_NONE;具体目标 clock 还须支持该 parent,否则返回IM_ERR_UNSUPPORTED。 - gate id 不支持 parent 接口。
- root clock 没有 parent,调用
get_parent返回IM_ERR_UNSUPPORTED。 get_parent读到硬件无时钟输出时返回HAL_CPM_ID_CLK_NONE;此时对应 clock rate 为 0。set_parent在有效 parent 及所需 EXCLK 使能状态均未变化时不写 CDR、不触发 CE;LP clock 已经选择LPCLK时也不会重复切换 AONLPCLKSEL。set_parent成功后刷新当前节点自身的 parent/rate 缓存;UART 与 LP_UART 会同时刷新。其它子节点缓存不会自动重算,依赖该节点的子 clock 需通过hal_cpm_clk_get_info()或hal_cpm_init()重新同步。
1.5 ROSC2 校准
error_code_t hal_cpm_rosc2_trim(void);
功能:
- 把 ROSC2 切到 24 MHz 档位(
AON_ROSC2_FSEL_24M),然后固定延时 100 us;该流程没有可读取的档位稳定状态位 - 清计数,保持
TRIM_COUNT_EN为低电平 200 us,再拉高TRIM_COUNT_EN;固定延时 100 us 后轮询TRIMS.TRIM_FINISH,置位后读取一次TRIMS.TRIM_COUNT_NUM - 在 64 项升序 trim 表中取第一个不小于计数值的表项索引,作为
ROSCC.ROSC2_TRIM字段值并写入 - 测量只执行一次;写入 trim 后不会再次测量,也不会反复测量直到表值与应用值一致
说明:
- 阻塞实现:档位切换后固定延时 100 us,计数前保持
TRIM_COUNT_EN低电平 200 us,拉高后再等待 100 us,然后轮询TRIM_FINISH(最多 100000 次)。按约 1 ms的计数窗口估算,单次调用为 1.4 ms 量级;忙等期间不让出 CPU。 - 必须在任务上下文调用,不要在中断上下文、关中断上下文或其它对延迟敏感的路径调用。
- 调用结束后
ROSC2_FSEL保持为 24 MHz,不会恢复调用前的档位;调用方需评估LPCLK/RTCLK 等下游时钟对频率变化的容忍度。 - 计数值高于表项上限时饱和到
ROSC2_TRIM=63,不会返回“未校准”错误。 - 计数握手失败时关闭
TRIM_COUNT_EN并返回IM_ERR_TIMEOUT;成功路径读完计数后同样关闭。TRIMS中的计数保持到下一次测量开始(TRIM_COUNT_CLR)。 - 超时返回时
ROSC2_FSEL仍为 24 MHz,ROSCC.ROSC2_TRIM保持原值不变。 - 该接口直接操作 LL 位域,不刷新 HAL clock cache。若调用前
ROSC2_FSEL不是24 MHz,hal_cpm_clk_get_rate_hz()在刷新前仍可能返回旧档位的标称频率;调用后可执行hal_cpm_clk_get_info()或hal_cpm_init()刷新。 hal_cpm_clk_get_info()/hal_cpm_init()只会按ROSC2_FSEL刷新 HAL使用的标称频率,不返回 trim 后的实测频率;需要实测值时必须通过ll_aon_clear_trim_count()/ll_aon_enable_trim_count()/ll_aon_is_trim_finished()/ll_aon_get_trim_count_number()重新执行一次 测量。
返回值:
| 返回值 | 说明 |
|---|---|
IM_OK |
测量完成,trim 已写入。 |
IM_ERR_TIMEOUT |
TRIM_FINISH 在轮询上限内未置位。 |
1.6 Clock information
typedef struct {
uint32_t id;
const char *name;
const char *parent;
bool enabled;
bool gate_present;
bool gate_enabled;
bool busy;
uint32_t enable_count;
uint32_t rate_hz;
uint32_t div_num;
uint32_t div_den;
} hal_cpm_clk_info_t;
error_code_t hal_cpm_clk_get_count(uint32_t *count_out);
error_code_t hal_cpm_clk_get_info(uint32_t id,
hal_cpm_clk_info_t *info_out);
typedef struct {
const char *name;
uintptr_t address;
uint32_t value;
} hal_cpm_gate_reg_info_t;
error_code_t hal_cpm_clk_get_gate_reg_count(uint32_t *count_out);
error_code_t hal_cpm_clk_get_gate_reg_info(uint32_t index,
hal_cpm_gate_reg_info_t *info_out);
功能:
- 由具体芯片 HAL 提供当前芯片的 clock 名称、parent 关系、gate、DIV 和 BUSY 语义
hal_cpm_clk_get_count()返回当前芯片 clock 数量hal_cpm_clk_get_info()查询一个 clock 的完整状态enabled表示 clock output 状态;gate_present/gate_enabled表示关联的模块 gate 状态busy表示当前是否正在进行分频或时钟切换div_num/div_den表示分频值;普通分频使用N/1,I2S 使用M/N,没有分频器时为0/0- 通用
clkmiddleware 负责遍历 HAL 信息并提供im_clk_summary();shell 的cat_clk只依赖 middleware hal_cpm_clk_get_gate_reg_count()/hal_cpm_clk_get_gate_reg_info()提供诊断用的 clock gate 寄存器枚举接口- gate 寄存器集合是芯片相关的:数量、名称和地址都由具体芯片 HAL 决定,调用方按
[0, count)遍历,不要假定固定是LPCLKGR/HPCLKGR0/HPCLKGR1三个;因此im_clk_get_gate_reg_count()/im_clk_get_gate_reg_info()也一样返回描述符而不写死寄存器 cat_clk misc直接打印 HAL 返回的名称和地址;数量为 0 时提示该芯片不暴露 gate 寄存器
返回值:
| 返回值 | 说明 |
|---|---|
IM_OK |
查询成功,info_out 已填写。 |
IM_ERR_INVALID_PARAM |
id 越界或 info_out 为空。 |
IM_ERR_NOT_READY |
全量刷新 clock cache 失败,无法保证信息一致。 |
CPM stub 的 clock information 和 gate register 查询接口返回 IM_ERR_UNSUPPORTED。
2. 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 对频率变化的容忍度。
3. 典型使用
3.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;
}
3.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;
}
3.3 打开模块 gate
ret = hal_cpm_clk_enable(HAL_CPM_ID_GATE_UART1);
if (ret != IM_OK) {
return ret;
}
3.4 查询 gate 状态
bool open;
ret = hal_cpm_clk_is_enabled(HAL_CPM_ID_GATE_UART1, &open);
if (ret != IM_OK) {
return ret;
}