3. 配置系统时钟节拍

任务目标

在Zephyr项目中,通过设备树和Kconfig将系统时钟节拍(SYSTEM_CLOCK_TICK_FREQ)从默认值修改为更高频率,并编写一个简单的延时测试任务,观察节拍频率变化对k_sleep()实际延时精度的影响。

环境与依赖

  • Zephyr RTOS 3.x(以native_sim或qemu_x86为目标板验证)
  • 已安装Zephyr SDK并初始化west工作区
  • 本案例仅依赖Zephyr内核自带API,无额外第三方库

实现原理

Zephyr的系统节拍频率由CONFIG_SYS_CLOCK_TICKS_PER_SEC控制,默认值为100(即每 tick 10ms)。通过Kconfig将其改为1000后,每个tick变为1ms,k_sleep(100)的延时精度将从“至少100ms”提升为“至少100ms且误差不超过1ms”。设备树中chosen节点的zephyr,sys-clock属性通常指向系统定时器,但节拍频率主要由Kconfig决定,设备树仅需保持默认即可。本案例同时展示如何确认当前生效的节拍频率。

关键完整代码

// prj.conf
CONFIG_SYS_CLOCK_TICKS_PER_SEC=1000
CONFIG_PRINTK=y

// src/main.c
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>

/* 延时误差统计结构 */
struct delay_stat {
    uint32_t min_delta_us;
    uint32_t max_delta_us;
    uint32_t total_delta_us;
    uint32_t samples;
};

static void measure_delay(struct delay_stat *stat, int sleep_ms)
{
    uint32_t start = k_cycle_get_32();
    k_sleep(K_MSEC(sleep_ms));
    uint32_t end = k_cycle_get_32();
    uint32_t delta_us = (end - start) / (sys_clock_hw_cycles_per_sec() / 1000000);

    if (stat->samples == 0) {
        stat->min_delta_us = delta_us;
        stat->max_delta_us = delta_us;
    } else {
        if (delta_us < stat->min_delta_us)
            stat->min_delta_us = delta_us;
        if (delta_us > stat->max_delta_us)
            stat->max_delta_us = delta_us;
    }
    stat->total_delta_us += delta_us;
    stat->samples++;
}

void main(void)
{
    struct delay_stat stat = {0};
    const int sleep_ms = 100;
    const int iterations = 10;

    printk("System tick frequency: %d Hz\n",
           CONFIG_SYS_CLOCK_TICKS_PER_SEC);
    printk("HW cycles per sec: %d\n",
           sys_clock_hw_cycles_per_sec());

    for (int i = 0; i < iterations; i++) {
        measure_delay(&stat, sleep_ms);
    }

    uint32_t avg_us = stat.total_delta_us / stat.samples;
    printk("Sleep %d ms over %d iterations:\n", sleep_ms, iterations);
    printk("  avg: %d us, min: %d us, max: %d us\n",
           avg_us, stat.min_delta_us, stat.max_delta_us);
    printk("  expected: %d us (ideal)\n", sleep_ms * 1000);
}

构建与运行

# 在west工作区中创建项目后执行
west build -b native_sim -d build . 
west build -d build -t run

若使用qemu_x86,将-b native_sim替换为-b qemu_x86,运行命令改为west build -d build -t qemu。

验证方法与判定标准

  1. 运行后首先观察打印的System tick frequency是否为1000,确认Kconfig生效。
  2. 观察avg值:当节拍频率为1000Hz时,平均延时应在100000us(100ms)附近,且max - min的抖动通常不超过1个tick周期(即1000us)。
  3. 将prj.conf中的值改回100并重新构建运行,对比两次的max值:100Hz时最大误差可达10000us(10ms),而1000Hz时最大误差应明显减小。这一对比直接证明节拍频率对延时精度的影响。

通过条件:两次运行结果中,1000Hz配置下的最大延时误差显著小于100Hz配置(至少减小5倍以上),且打印的tick频率与配置一致。

常见问题

  • 修改后无变化:确认prj.conf位于项目根目录且拼写正确;若使用设备树覆盖,需检查chosen节点是否被意外修改。
  • native_sim时间不精确:native_sim的计时基于主机系统,误差可能受宿主机调度影响,建议用qemu_x86获得更稳定的结果。
  • 周期计数溢出:本测试延时短、循环少,不会触发32位周期计数回绕;若增加循环次数需处理回绕。