RT-Thread 的时钟框架为嵌入式系统中的硬件时钟管理提供了全面的基础设施。它实现了层次化时钟树模型,支持时钟门控、频率调节、父时钟切换和相位调整——这些都是电源管理和系统性能调优的关键。
时钟管理对嵌入式系统至关重要:
常见时钟类型包括:
RT-Thread 时钟框架位于 components/drivers/clk/,提供:
menuconfig RT_USING_CLK
bool "Using Common Clock Framework (CLK)"
depends on RT_USING_DM
select RT_USING_ADT_REF
default y
在 menuconfig 中的位置:
RT-Thread Components → Device Drivers → Using Common Clock Framework (CLK)
依赖项:
RT_USING_DM:必须首先启用RT_USING_ADT_REF:引用计数支持(自动)默认值:启用 DM 时默认启用
config RT_CLK_SCMI
bool "Clock driver controlled via SCMI interface"
depends on RT_USING_CLK
depends on RT_FIRMWARE_ARM_SCMI
default n
支持通过 ARM 系统控制和管理接口(SCMI)控制的时钟。
时钟提供者使用这些属性导出时钟:
#clock-cells = <n>; /* 时钟说明符中的单元数 */
clock-output-names = "name1", "name2"; /* 输出时钟名称 */
设备使用以下方式引用时钟:
clocks = <&clk_provider idx>; /* 时钟 phandle 和索引 */
clock-names = "name"; /* 时钟连接名称 */
clocks {
/* 简单固定频率振荡器 */
osc24M: oscillator-24M {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <24000000>;
clock-output-names = "osc24M";
};
/* 带精度规范的固定频率 */
osc32k: oscillator-32k {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <32768>;
clock-accuracy = <50>; /* ±50 PPM */
clock-output-names = "osc32k";
};
};
ccu: clock-controller@1c20000 {
compatible = "vendor,clock-controller";
reg = <0x1c20000 0x400>;
#clock-cells = <1>;
/* 父时钟 */
clocks = <&osc24M>, <&osc32k>;
clock-names = "hosc", "losc";
/* 输出时钟名称(可选)*/
clock-output-names = "pll-cpu", "pll-ddr", "ahb1", "apb1";
};
/* 单时钟消费者 */
uart0: serial@1c28000 {
compatible = "vendor,uart";
reg = <0x1c28000 0x400>;
interrupts = <0 0 4>;
clocks = <&ccu 64>; /* 时钟索引 64 */
clock-names = "baudclk";
status = "okay";
};
/* 多时钟消费者 */
mmc0: mmc@1c0f000 {
compatible = "vendor,mmc";
reg = <0x1c0f000 0x1000>;
clocks = <&ccu 8>, <&ccu 9>;
clock-names = "ahb", "mmc";
status = "okay";
};
消费者 API 为设备驱动程序提供管理时钟的函数。框架使用类似 Linux 的两级 prepare/enable 模型:
struct rt_clk *rt_clk_get_by_name(struct rt_device *dev, const char *name);
通过连接名称获取时钟。
参数:
dev:设备结构指针name:时钟连接名称(匹配设备树中的 clock-names)返回值:
示例:
struct rt_clk *clk = rt_clk_get_by_name(dev, "baudclk");
if (!clk) {
LOG_E("获取 baudclk 失败");
return -RT_ERROR;
}
struct rt_clk *rt_clk_get_by_index(struct rt_device *dev, int index);
通过 clocks 属性中的索引获取时钟。
参数:
dev:设备结构指针index:时钟索引(从 0 开始)struct rt_clk_array *rt_clk_get_array(struct rt_device *dev);
获取设备的所有时钟作为数组。
参数:
dev:设备结构指针返回值:
rt_is_err() 检查)void rt_clk_put(struct rt_clk *clk);
释放时钟引用。
rt_err_t rt_clk_prepare(struct rt_clk *clk);
为启用准备时钟。这可能会休眠,可以配置 PLL 或执行其他不能原子完成的操作。
参数:
clk:时钟指针返回值:
RT_EOK注意:
rt_clk_enable() 之前调用void rt_clk_unprepare(struct rt_clk *clk);
取消准备先前已准备的时钟。
rt_err_t rt_clk_enable(struct rt_clk *clk);
启用时钟。这是一个不能休眠的原子操作。
参数:
clk:时钟指针返回值:
RT_EOK注意:
rt_clk_prepare() 之后void rt_clk_disable(struct rt_clk *clk);
禁用先前启用的时钟。
rt_err_t rt_clk_prepare_enable(struct rt_clk *clk);
便捷函数,准备并启用时钟。
示例:
/* 典型用法 */
ret = rt_clk_prepare_enable(clk);
if (ret != RT_EOK) {
LOG_E("启用时钟失败: %d", ret);
return ret;
}
void rt_clk_disable_unprepare(struct rt_clk *clk);
便捷函数,禁用并取消准备时钟。
rt_err_t rt_clk_set_rate(struct rt_clk *clk, rt_ubase_t rate);
设置时钟频率。
参数:
clk:时钟指针rate:期望频率(Hz)返回值:
RT_EOK注意:
rt_clk_get_rate() 验证实际频率示例:
/* 将 UART 时钟设置为 48MHz */
ret = rt_clk_set_rate(uart_clk, 48000000);
if (ret != RT_EOK) {
LOG_E("设置时钟频率失败: %d", ret);
}
/* 验证实际频率 */
rt_ubase_t actual_rate = rt_clk_get_rate(uart_clk);
LOG_I("时钟频率: %u Hz", actual_rate);
rt_ubase_t rt_clk_get_rate(struct rt_clk *clk);
获取当前时钟频率。
参数:
clk:时钟指针返回值:
rt_base_t rt_clk_round_rate(struct rt_clk *clk, rt_ubase_t rate);
获取最接近的支持频率,但不更改时钟。
参数:
clk:时钟指针rate:期望频率(Hz)返回值:
rt_err_t rt_clk_set_rate_range(struct rt_clk *clk, rt_ubase_t min, rt_ubase_t max);
为此消费者设置可接受的频率范围。
参数:
clk:时钟指针min:最小可接受频率(Hz)max:最大可接受频率(Hz)示例:
/* UART 需要 48MHz ±2% */
rt_clk_set_rate_range(uart_clk, 47040000, 48960000);
rt_err_t rt_clk_set_parent(struct rt_clk *clk, struct rt_clk *parent);
更改时钟父时钟(用于多路复用器时钟)。
参数:
clk:时钟指针parent:新的父时钟struct rt_clk *rt_clk_get_parent(struct rt_clk *clk);
获取当前父时钟。
rt_err_t rt_clk_set_phase(struct rt_clk *clk, int degrees);
以度为单位设置时钟相位。
参数:
clk:时钟指针degrees:相位(度,0-359)返回值:
RT_EOK-RT_ENOSYSrt_base_t rt_clk_get_phase(struct rt_clk *clk);
获取当前时钟相位。
rt_err_t rt_clk_notifier_register(struct rt_clk *clk,
struct rt_clk_notifier *notifier);
为时钟事件注册通知器。
通知器结构:
struct rt_clk_notifier {
rt_list_t list;
struct rt_clk *clk;
rt_clk_notifier_callback callback;
void *priv;
};
typedef rt_err_t (*rt_clk_notifier_callback)(
struct rt_clk_notifier *notifier,
rt_ubase_t msg,
rt_ubase_t old_rate,
rt_ubase_t new_rate);
事件消息:
RT_CLK_MSG_PRE_RATE_CHANGE:频率变化前RT_CLK_MSG_POST_RATE_CHANGE:频率成功变化后RT_CLK_MSG_ABORT_RATE_CHANGE:频率变化中止示例:
static rt_err_t clk_notifier_cb(struct rt_clk_notifier *notifier,
rt_ubase_t msg,
rt_ubase_t old_rate,
rt_ubase_t new_rate)
{
if (msg == RT_CLK_MSG_PRE_RATE_CHANGE) {
LOG_I("时钟频率变化: %u -> %u Hz", old_rate, new_rate);
/* 为频率变化做准备 */
}
return RT_EOK;
}
struct rt_clk_notifier my_notifier = {
.callback = clk_notifier_cb,
};
rt_clk_notifier_register(clk, &my_notifier);
#include <rtthread.h>
#include <drivers/platform.h>
#include <drivers/clk.h>
struct uart_device {
void *base;
int irq;
struct rt_clk *clk;
struct rt_serial_device serial;
};
static rt_err_t uart_configure(struct rt_serial_device *serial,
struct serial_configure *cfg)
{
struct uart_device *uart = rt_container_of(serial,
struct uart_device, serial);
rt_ubase_t clk_rate;
rt_uint32_t divisor;
/* 获取时钟频率 */
clk_rate = rt_clk_get_rate(uart->clk);
if (clk_rate == 0) {
LOG_E("无效的时钟频率");
return -RT_ERROR;
}
/* 计算波特率分频器 */
divisor = clk_rate / (16 * cfg->baud_rate);
/* 配置硬件 */
writel(divisor & 0xFF, uart->base + UART_DLL);
writel((divisor >> 8) & 0xFF, uart->base + UART_DLH);
return RT_EOK;
}
static rt_err_t uart_probe(struct rt_platform_device *pdev)
{
rt_err_t ret;
struct rt_device *dev = &pdev->parent;
struct uart_device *uart;
/* 分配设备结构 */
uart = rt_calloc(1, sizeof(*uart));
if (!uart)
return -RT_ENOMEM;
/* 映射 MMIO 区域 */
uart->base = rt_dm_dev_iomap(dev, 0);
if (!uart->base) {
ret = -RT_ERROR;
goto err_free;
}
/* 获取 IRQ */
uart->irq = rt_dm_dev_get_irq(dev, 0);
/* 获取时钟 */
uart->clk = rt_clk_get_by_name(dev, "baudclk");
if (!uart->clk) {
LOG_E("获取 baudclk 失败");
ret = -RT_ERROR;
goto err_unmap;
}
/* 准备并启用时钟 */
ret = rt_clk_prepare_enable(uart->clk);
if (ret != RT_EOK) {
LOG_E("启用时钟失败: %d", ret);
goto err_put_clk;
}
/* 注册串口设备 */
ret = rt_hw_serial_register(&uart->serial,
rt_dm_dev_get_name(dev),
RT_DEVICE_FLAG_RDWR,
uart);
if (ret != RT_EOK) {
goto err_disable_clk;
}
pdev->priv = uart;
return RT_EOK;
err_disable_clk:
rt_clk_disable_unprepare(uart->clk);
err_put_clk:
rt_clk_put(uart->clk);
err_unmap:
rt_iounmap(uart->base);
err_free:
rt_free(uart);
return ret;
}
static rt_err_t uart_remove(struct rt_platform_device *pdev)
{
struct uart_device *uart = pdev->priv;
/* 取消注册串口设备 */
rt_device_unregister(&uart->serial.parent);
/* 禁用并释放时钟 */
rt_clk_disable_unprepare(uart->clk);
rt_clk_put(uart->clk);
/* 释放资源 */
rt_iounmap(uart->base);
rt_free(uart);
return RT_EOK;
}
static const struct rt_ofw_node_id uart_ofw_ids[] = {
{ .compatible = "vendor,uart" },
{ /* sentinel */ }
};
static struct rt_platform_driver uart_driver = {
.name = "uart",
.ids = uart_ofw_ids,
.probe = uart_probe,
.remove = uart_remove,
};
RT_PLATFORM_DRIVER_EXPORT(uart_driver);
struct rt_clk_ops {
/* Prepare/unprepare (可能休眠) */
rt_err_t (*prepare)(struct rt_clk_cell *cell);
void (*unprepare)(struct rt_clk_cell *cell);
rt_bool_t (*is_prepared)(struct rt_clk_cell *cell);
/* Enable/disable (原子操作) */
rt_err_t (*enable)(struct rt_clk_cell *cell);
void (*disable)(struct rt_clk_cell *cell);
rt_bool_t (*is_enabled)(struct rt_clk_cell *cell);
/* 频率控制 */
rt_ubase_t (*recalc_rate)(struct rt_clk_cell *cell, rt_ubase_t parent_rate);
rt_base_t (*round_rate)(struct rt_clk_cell *cell, rt_ubase_t drate, rt_ubase_t *prate);
rt_err_t (*set_rate)(struct rt_clk_cell *cell, rt_ubase_t rate, rt_ubase_t parent_rate);
/* 父时钟控制 */
rt_err_t (*set_parent)(struct rt_clk_cell *cell, rt_uint8_t idx);
rt_uint8_t (*get_parent)(struct rt_clk_cell *cell);
};
#include <rtthread.h>
#include <drivers/platform.h>
#include <drivers/clk.h>
struct clk_fixed {
struct rt_clk_node parent;
struct rt_clk_fixed_rate fcell;
struct rt_clk_cell *cells[1];
};
static rt_ubase_t fixed_clk_recalc_rate(struct rt_clk_cell *cell,
rt_ubase_t parent_rate)
{
struct rt_clk_fixed_rate *fr = rt_container_of(cell,
struct rt_clk_fixed_rate,
cell);
return fr->fixed_rate;
}
static struct rt_clk_ops fixed_clk_ops = {
.recalc_rate = fixed_clk_recalc_rate,
};
static rt_err_t fixed_clk_probe(struct rt_platform_device *pdev)
{
rt_err_t err;
rt_uint32_t val;
struct rt_device *dev = &pdev->parent;
struct clk_fixed *cf;
/* 分配驱动结构 */
cf = rt_calloc(1, sizeof(*cf));
if (!cf)
return -RT_ENOMEM;
/* 从设备树读取时钟频率 */
if ((err = rt_dm_dev_prop_read_u32(dev, "clock-frequency", &val))) {
LOG_E("缺少 clock-frequency 属性");
goto _fail;
}
cf->fcell.fixed_rate = val;
/* 读取可选的精度 */
val = 0;
rt_dm_dev_prop_read_u32(dev, "clock-accuracy", &val);
cf->fcell.fixed_accuracy = val;
/* 读取可选的时钟名称 */
rt_dm_dev_prop_read_string(dev, "clock-output-names",
&cf->fcell.cell.name);
/* 初始化时钟节点 */
cf->parent.dev = dev;
cf->parent.cells_nr = 1;
cf->parent.cells = cf->cells;
cf->cells[0] = &cf->fcell.cell;
cf->fcell.cell.ops = &fixed_clk_ops;
/* 向框架注册 */
if ((err = rt_clk_register(&cf->parent))) {
LOG_E("注册时钟失败: %d", err);
goto _fail;
}
LOG_I("固定时钟 '%s' 已注册: %u Hz",
cf->fcell.cell.name, cf->fcell.fixed_rate);
return RT_EOK;
_fail:
rt_free(cf);
return err;
}
static const struct rt_ofw_node_id fixed_clk_ofw_ids[] = {
{ .compatible = "fixed-clock" },
{ /* sentinel */ }
};
static struct rt_platform_driver fixed_clk_driver = {
.name = "clk-fixed-rate",
.ids = fixed_clk_ofw_ids,
.probe = fixed_clk_probe,
};
static int fixed_clk_drv_register(void)
{
rt_platform_driver_register(&fixed_clk_driver);
return 0;
}
INIT_SUBSYS_EXPORT(fixed_clk_drv_register);
rt_clk_set_rate_range()get_rate() 中访问硬件/* 获取并启用时钟 */
struct rt_clk *clk = rt_clk_get_by_name(dev, "clk");
if (!clk)
return -RT_ERROR;
ret = rt_clk_prepare_enable(clk);
if (ret != RT_EOK) {
rt_clk_put(clk);
return ret;
}
/* 使用硬件 */
/* 清理 */
rt_clk_disable_unprepare(clk);
rt_clk_put(clk);
/* 根据工作负载更改频率 */
switch (perf_level) {
case PERF_HIGH:
rt_clk_set_rate(cpu_clk, 1000000000); /* 1GHz */
break;
case PERF_NORMAL:
rt_clk_set_rate(cpu_clk, 800000000); /* 800MHz */
break;
case PERF_LOW:
rt_clk_set_rate(cpu_clk, 400000000); /* 400MHz */
break;
}
找不到时钟
clocks 和 clock-names 属性存在启用失败
频率错误
rt_clk_round_rate():验证支持的频率components/drivers/clk/components/drivers/include/drivers/clk.h