Zephyr RTOS 中timing 相关函数功能介绍

目录

概述

[1 核心时间函数列表](#1 核心时间函数列表)

[2 函数使用方法](#2 函数使用方法)

[2.1 时间基准与获取函数](#2.1 时间基准与获取函数)

[2.2 线程睡眠函数](#2.2 线程睡眠函数)

[3. 超时(Timeout)机制](#3. 超时(Timeout)机制)

[3.1 超时参数创建与使用](#3.1 超时参数创建与使用)

[3.2 超时参数转换](#3.2 超时参数转换)

[4. 时间转换函数](#4. 时间转换函数)

[4.1 Tick与毫秒转换](#4.1 Tick与毫秒转换)

[4.2 CPU周期与时间转换](#4.2 CPU周期与时间转换)

[5. 忙等待(Busy Wait)](#5. 忙等待(Busy Wait))

[5.1 高精度延迟](#5.1 高精度延迟)

[6. 完整应用示例](#6. 完整应用示例)

[6.1 精确周期性任务调度](#6.1 精确周期性任务调度)

[6.2 超时管理系统](#6.2 超时管理系统)

[6.3 性能优化与最佳实践](#6.3 性能优化与最佳实践)

[7 应用总结](#7 应用总结)

[7.1 Kconfig配置优化](#7.1 Kconfig配置优化)

[7.2 常见问题与解决方案](#7.2 常见问题与解决方案)

[7.3 核心要点](#7.3 核心要点)


概述

在 Zephyr RTOS 中,Timing(时间管理)相关函数是构建实时、可靠嵌入式系统的基石。它们提供了从高精度延迟测量到线程睡眠调度的完整时间控制能力。下面我将系统性地介绍所有核心的时间管理函数及其应用。通过合理使用Zephyr的时间函数,可以构建出精确、可靠的实时嵌入式应用,满足从毫秒级任务调度到微秒级硬件时序控制的各类需求。

1 核心时间函数列表

类别 主要函数 核心用途
时间基准 k_cycle_get_32/64(), k_uptime_get/ticks() 获取高精度计数值和系统运行时间
线程睡眠 k_sleep(), k_msleep(), k_usleep() 让当前线程暂停执行指定时间
超时机制 K_TIMEOUT(), k_timeout_to_ms/ticks/cyc() 超时参数包装与转换
时间转换 k_ticks_to_ms_near64(), k_cyc_to_ms_floor32() 时间单位间相互转换
忙等待 k_busy_wait() 高精度微秒级延迟(不释放CPU)

2 函数使用方法

2.1 时间基准与获取函数

1) 高精度循环计数器(CPU Cycles)

cpp 复制代码
/* 获取32位循环计数器(最快、最精确,但可能溢出)*/
uint32_t start_cycles = k_cycle_get_32();
uint32_t end_cycles = k_cycle_get_32();
uint32_t elapsed_cycles = end_cycles - start_cycles;

/* 获取64位循环计数器(无溢出问题)*/
uint64_t start_cycles_64 = k_cycle_get_64();
/* 执行操作... */
uint64_t end_cycles_64 = k_cycle_get_64();
uint64_t elapsed_cycles_64 = end_cycles_64 - start_cycles_64;

/* 实际应用:测量代码执行时间(微秒级)*/
void measure_execution_time(void) {
    uint32_t start = k_cycle_get_32();
    
    /* 需要测量的代码段 */
    critical_function();
    
    uint32_t end = k_cycle_get_32();
    uint32_t cycles = end - start;
    
    /* 转换为微秒 */
    uint32_t us = k_cyc_to_us_near32(cycles);
    printk("执行耗时: %u 周期 (%u us)\n", cycles, us);
    
    /* 注意:k_cyc_to_us_near32() 的精度取决于 CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC */
}

2) 系统运行时间(Uptime)

cpp 复制代码
/* 获取系统启动后的毫秒数(最常用)*/
int64_t uptime_ms = k_uptime_get();  /* 返回int64_t,单位:毫秒 */
printk("系统已运行: %lld 毫秒\n", uptime_ms);

/* 获取系统启动后的tick数 */
int64_t uptime_ticks = k_uptime_ticks();  /* 返回int64_t,单位:系统tick */
printk("系统已运行: %lld 个tick\n", uptime_ticks);

/* 实际应用:计算时间间隔 */
void check_interval(void) {
    static int64_t last_time = 0;
    int64_t now = k_uptime_get();
    
    if (now - last_time >= 1000) {  /* 至少1秒间隔 */
        printk("每秒执行一次的任务\n");
        last_time = now;
    }
}

2.2 线程睡眠函数

1) 基本睡眠函数

cpp 复制代码
/* 毫秒级睡眠(最常用)*/
k_msleep(100);  /* 睡眠100毫秒 */

/* 等价于(但更推荐k_msleep,更直观)*/
k_sleep(K_MSEC(100));

/* 秒级睡眠 */
k_sleep(K_SECONDS(2));  /* 睡眠2秒 */

/* 微秒级睡眠(注意:精度受系统tick限制)*/
k_usleep(500);  /* 睡眠500微秒 */

/* 实际应用:周期性任务 */
void periodic_task(void) {
    while (1) {
        /* 执行任务 */
        read_sensor_data();
        process_data();
        
        /* 固定周期:每秒执行一次 */
        k_msleep(1000);  /* 1000ms = 1秒 */
        
        /* 注意:k_msleep()可能因系统调度有微小误差 */
    }
}

2) 高级睡眠模式(结合条件变量)

cpp 复制代码
/* 带条件检查的限时等待 */
K_MUTEX_DEFINE(data_mutex);
K_CONDVAR_DEFINE(data_cond);

void wait_for_data_with_timeout(void) {
    bool data_ready = false;
    int ret;
    
    k_mutex_lock(&data_mutex, K_FOREVER);
    
    /* 等待数据就绪,最多100ms */
    while (!data_ready) {
        ret = k_condvar_wait(&data_cond, &data_mutex, K_MSEC(100));
        
        if (ret == -EAGAIN) {  /* 超时 */
            printk("等待数据超时\n");
            break;
        }
    }
    
    if (data_ready) {
        process_data();
    }
    
    k_mutex_unlock(&data_mutex);
}

3. 超时(Timeout)机制

3.1 超时参数创建与使用

cpp 复制代码
/* 创建超时参数的几种方式 */
k_timeout_t timeout;

timeout = K_NO_WAIT;      /* 非阻塞,立即返回 */
timeout = K_FOREVER;      /* 永久阻塞,无限等待 */
timeout = K_MSEC(100);    /* 阻塞100毫秒 */
timeout = K_SECONDS(5);   /* 阻塞5秒 */
timeout = K_TICKS(10);    /* 阻塞10个系统tick */

/* 实际应用:带超时的消息接收 */
void receive_with_timeout(void) {
    char message[64];
    int ret;
    
    /* 尝试在500ms内接收消息 */
    ret = k_msgq_get(&my_msgq, message, sizeof(message), K_MSEC(500));
    
    switch (ret) {
        case 0:
            printk("收到消息: %s\n", message);
            break;
        case -EAGAIN:
            printk("接收超时,无消息\n");
            break;
        case -ENOMSG:
            printk("消息队列空(非阻塞模式)\n");
            break;
        default:
            printk("接收错误: %d\n", ret);
    }
}

3.2 超时参数转换

cpp 复制代码
/* 超时值转换为其他单位 */
k_timeout_t timeout = K_MSEC(1500);

/* 转换为毫秒 */
int64_t ms = k_timeout_to_ms(timeout);  /* 1500 */

/* 转换为tick数 */
int64_t ticks = k_timeout_to_ticks(timeout);

/* 转换为周期数 */
uint64_t cycles = k_timeout_to_cyc(timeout);

/* 实际应用:计算剩余时间 */
int64_t calculate_remaining_time(int64_t start_time, k_timeout_t timeout) {
    int64_t timeout_ms = k_timeout_to_ms(timeout);
    int64_t current_time = k_uptime_get();
    int64_t elapsed = current_time - start_time;
    int64_t remaining = timeout_ms - elapsed;
    
    return (remaining > 0) ? remaining : 0;
}

4. 时间转换函数

4.1 Tick与毫秒转换

cpp 复制代码
/* 常用转换函数 */
uint64_t ticks, ms;

/* Tick → 毫秒(四舍五入) */
ms = k_ticks_to_ms_near64(ticks);

/* Tick → 毫秒(向下取整) */
ms = k_ticks_to_ms_floor64(ticks);

/* 毫秒 → Tick */
ticks = k_ms_to_ticks_near64(ms);

/* 毫秒 → Tick(向上取整) */
ticks = k_ms_to_ticks_ceil64(ms);

/* 32位版本(注意溢出) */
uint32_t ms32 = k_ticks_to_ms_near32(ticks32);

/* 实际应用:配置定时器 */
void configure_timer_interval(void) {
    /* 需要100ms的定时器间隔 */
    uint32_t interval_ms = 100;
    
    /* 转换为tick数(向上取整,确保至少100ms) */
    uint32_t interval_ticks = k_ms_to_ticks_ceil32(interval_ms);
    
    printk("定时器间隔: %u ms ≈ %u ticks\n", interval_ms, interval_ticks);
    
    /* 设置硬件定时器 */
    // timer_set_interval(interval_ticks);
}

4.2 CPU周期与时间转换

cpp 复制代码
/* 周期 ↔ 时间转换(依赖CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC) */
uint32_t cycles, us, ns;

/* 周期 → 微秒(四舍五入) */
us = k_cyc_to_us_near32(cycles);

/* 周期 → 纳秒(四舍五入) */
ns = k_cyc_to_ns_near32(cycles);

/* 微秒 → 周期 */
cycles = k_us_to_cyc_near32(us);

/* 纳秒 → 周期 */
cycles = k_ns_to_cyc_near32(ns);

/* 实际应用:测量短时间事件 */
void measure_short_event(void) {
    uint32_t start = k_cycle_get_32();
    
    /* 非常短的操作,可能只有几微秒 */
    gpio_pin_toggle(led_pin);
    
    uint32_t end = k_cycle_get_32();
    uint32_t elapsed_cycles = (end >= start) ? (end - start) : (UINT32_MAX - start + end + 1);
    
    uint32_t elapsed_ns = k_cyc_to_ns_near32(elapsed_cycles);
    printk("GPIO切换耗时: %u ns\n", elapsed_ns);
}

5. 忙等待(Busy Wait)

5.1 高精度延迟

cpp 复制代码
/* 忙等待:不释放CPU,用于极短延迟 */
void precise_delay_us(uint32_t us) {
    /* 注意:k_busy_wait() 会独占CPU,慎用! */
    k_busy_wait(us);  /* 延迟微秒数 */
}

/* 实际应用:精确时序控制(如I2C、SPI) */
void generate_i2c_start_condition(void) {
    /* SDA拉低 */
    gpio_pin_set(sda_pin, 0);
    
    /* 满足I2C时序要求:SDA低电平保持至少4.7μs */
    k_busy_wait(5);  /* 5μs,略大于最小要求 */
    
    /* SCL拉低 */
    gpio_pin_set(scl_pin, 0);
}

/* 正确使用模式:仅用于极短延迟 */
void mixed_delay_example(void) {
    /* 长延迟用睡眠(释放CPU) */
    k_msleep(10);
    
    /* 极短延迟用忙等待(保持CPU) */
    k_busy_wait(50);  /* 50μs */
    
    /* 中等延迟可以组合使用 */
    k_msleep(1);      /* 1ms */
    k_busy_wait(500); /* 再加500μs = 总共1.5ms */
}

6. 完整应用示例

6.1 精确周期性任务调度

cpp 复制代码
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>

/* 精确的10Hz任务(每100ms执行一次) */
void precise_10hz_task(void) {
    /* 方法1:简单但可能有累积误差 */
    // while (1) {
    //     do_work();
    //     k_msleep(100);  /* 可能因执行时间产生误差 */
    // }
    
    /* 方法2:基于绝对时间的精确调度 */
    int64_t next_wakeup = k_uptime_get();
    
    while (1) {
        /* 执行任务 */
        do_work();
        
        /* 计算下一次唤醒时间 */
        next_wakeup += 100;  /* 100ms后 */
        
        /* 计算需要睡眠的时间 */
        int64_t now = k_uptime_get();
        int64_t sleep_time = next_wakeup - now;
        
        if (sleep_time > 0) {
            k_msleep(sleep_time);
        } else {
            /* 任务执行超时,追赶进度 */
            printk("警告: 任务执行超时 %lld ms\n", -sleep_time);
            next_wakeup = now;  /* 重新同步 */
        }
    }
}

/* 带统计的任务执行器 */
struct task_timing_stats {
    uint32_t executions;
    uint32_t total_cycles;
    uint32_t max_cycles;
    uint32_t min_cycles;
    int64_t last_report;
};

void monitored_periodic_task(struct task_timing_stats *stats) {
    uint32_t start_cycles;
    
    while (1) {
        start_cycles = k_cycle_get_32();
        
        /* 执行实际工作 */
        perform_task();
        
        /* 计算执行时间 */
        uint32_t end_cycles = k_cycle_get_32();
        uint32_t elapsed_cycles = (end_cycles >= start_cycles) 
            ? (end_cycles - start_cycles) 
            : (UINT32_MAX - start_cycles + end_cycles + 1);
        
        /* 更新统计 */
        stats->executions++;
        stats->total_cycles += elapsed_cycles;
        
        if (elapsed_cycles > stats->max_cycles) {
            stats->max_cycles = elapsed_cycles;
        }
        
        if (stats->min_cycles == 0 || elapsed_cycles < stats->min_cycles) {
            stats->min_cycles = elapsed_cycles;
        }
        
        /* 定期报告统计 */
        int64_t now = k_uptime_get();
        if (now - stats->last_report > 5000) {  /* 每5秒报告一次 */
            uint32_t avg_cycles = stats->total_cycles / stats->executions;
            uint32_t avg_us = k_cyc_to_us_near32(avg_cycles);
            
            printk("任务统计[%u次]: 平均%u us, 最小%u us, 最大%u us\n",
                   stats->executions,
                   avg_us,
                   k_cyc_to_us_near32(stats->min_cycles),
                   k_cyc_to_us_near32(stats->max_cycles));
            
            /* 重置统计 */
            stats->executions = 0;
            stats->total_cycles = 0;
            stats->max_cycles = 0;
            stats->min_cycles = 0;
            stats->last_report = now;
        }
        
        /* 固定周期睡眠 */
        k_msleep(100);
    }
}

6.2 超时管理系统

cpp 复制代码
#include <zephyr/kernel.h>

/* 超时管理器:跟踪多个操作的超时 */
struct timeout_manager {
    int64_t start_time;
    k_timeout_t timeout;
    const char *operation_name;
};

/* 初始化超时管理器 */
void timeout_mgr_init(struct timeout_manager *mgr,
                     k_timeout_t timeout,
                     const char *name) {
    mgr->start_time = k_uptime_get();
    mgr->timeout = timeout;
    mgr->operation_name = name;
}

/* 检查是否超时 */
int timeout_mgr_check(struct timeout_manager *mgr) {
    int64_t now = k_uptime_get();
    int64_t elapsed = now - mgr->start_time;
    int64_t timeout_ms = k_timeout_to_ms(mgr->timeout);
    
    if (elapsed >= timeout_ms) {
        printk("操作 '%s' 超时: %lld ms > %lld ms\n",
               mgr->operation_name, elapsed, timeout_ms);
        return -ETIMEDOUT;
    }
    
    /* 计算剩余时间百分比 */
    int percent_used = (elapsed * 100) / timeout_ms;
    if (percent_used > 80) {
        printk("警告: 操作 '%s' 已用 %d%% 时间\n",
               mgr->operation_name, percent_used);
    }
    
    return 0;  /* 未超时 */
}

/* 实际应用:带超时的多步骤操作 */
void multi_step_operation_with_timeout(void) {
    struct timeout_manager global_timeout;
    
    /* 总超时:2秒 */
    timeout_mgr_init(&global_timeout, K_SECONDS(2), "多步骤操作");
    
    /* 步骤1:最多500ms */
    struct timeout_manager step1;
    timeout_mgr_init(&step1, K_MSEC(500), "步骤1");
    
    while (!step1_complete()) {
        if (timeout_mgr_check(&step1) != 0) {
            printk("步骤1超时,中止操作\n");
            return;
        }
        if (timeout_mgr_check(&global_timeout) != 0) {
            printk("总时间超时,中止操作\n");
            return;
        }
        
        perform_step1();
        k_msleep(10);  /* 短暂休息,防止忙循环 */
    }
    
    printk("步骤1完成,耗时 %lld ms\n",
           k_uptime_get() - step1.start_time);
    
    /* 步骤2:使用剩余时间 */
    int64_t remaining_ms = k_timeout_to_ms(global_timeout.timeout) -
                          (k_uptime_get() - global_timeout.start_time);
    
    if (remaining_ms > 0) {
        struct timeout_manager step2;
        timeout_mgr_init(&step2, K_MSEC(remaining_ms), "步骤2");
        
        while (!step2_complete()) {
            if (timeout_mgr_check(&step2) != 0 ||
                timeout_mgr_check(&global_timeout) != 0) {
                printk("步骤2超时\n");
                break;
            }
            
            perform_step2();
            k_msleep(10);
        }
    }
}

6.3 性能优化与最佳实践

1) 选择合适的精度级别

cpp 复制代码
/* 精度选择指南 */
void select_timing_method(void) {
    /* 1. 长延迟(>10ms):使用k_msleep() */
    k_msleep(100);  /* 100ms延迟 */
    
    /* 2. 中等延迟(1ms~10ms):k_msleep() 或 k_usleep() */
    k_usleep(5000);  /* 5ms延迟 */
    
    /* 3. 短延迟(<1ms):谨慎使用k_busy_wait() */
    k_busy_wait(100);  /* 100μs延迟 */
    
    /* 4. 极短延迟/时序测量:使用CPU周期 */
    uint32_t start = k_cycle_get_32();
    critical_operation();
    uint32_t end = k_cycle_get_32();
    
    /* 5. 周期性任务:基于绝对时间避免漂移 */
    static int64_t next = 0;
    if (k_uptime_get() >= next) {
        next += PERIOD_MS;  /* 固定周期 */
        periodic_task();
    }
}

7 应用总结

7.1 Kconfig配置优化

cpp 复制代码
# 时间相关配置
CONFIG_SYS_CLOCK_TICKS_PER_SEC=1000  # 系统tick频率,默认100Hz,这里设为1000Hz(1ms/tick)

# 高精度计时
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=64000000  # CPU时钟频率,用于cycle转换

# 性能监控
CONFIG_THREAD_RUNTIME_STATS=y      # 线程运行时间统计
CONFIG_KERNEL_COUNTERS=y           # 内核计数器

# 调试支持
CONFIG_TIMING_FUNCTIONS=y          # 启用时间函数
CONFIG_ASSERT=y                    # 启用断言

7.2 常见问题与解决方案

问题 现象 解决方案
睡眠时间不准确 k_msleep(100) 实际睡眠102ms 1. 提高CONFIG_SYS_CLOCK_TICKS_PER_SEC 2. 使用基于绝对时间的调度
忙等待导致系统无响应 高优先级任务被阻塞 1. 限制忙等待时间(<100μs) 2. 在低优先级线程中使用 3. 使用k_yield()让出CPU
32位cycle计数器溢出 时间计算错误(特别是长时间测量) 1. 使用k_cycle_get_64() 2. 处理回绕:(end >= start) ? (end-start) : (UINT32_MAX-start+end+1)
tick与ms转换误差 k_ms_to_ticks_ceil32(1)可能返回0 1. 使用向上取整函数 2. 确保最小时间单位≥1 tick
多线程时间竞争 时间检查与操作非原子 1. 使用互斥锁保护时间操作 2. 在中断禁用区进行关键时间测量

7.3 核心要点

Zephyr RTOS 提供了一套完整的时间管理API,从高精度的CPU周期计数到便捷的线程睡眠函数。关键要点如下:

1) 时间函数选择指南:

  1. 获取时间戳k_uptime_get()(毫秒级)或 k_cycle_get_32/64()(纳秒级)

  2. 线程延迟k_msleep()(通常足够)或 k_usleep()(微秒级)

  3. 极短延迟 :谨慎使用 k_busy_wait()(<100μs)

  4. 超时处理K_MSEC() + 相应API的超时参数

  5. 时间转换 :使用k_ticks_to_ms_*()系列函数

2) 最佳实践:

  • 避免长时间忙等待:会阻塞整个系统

  • 使用绝对时间调度:防止周期性任务的累积误差

  • 处理计数器溢出:特别是32位cycle计数器

  • 合理配置tick频率:平衡精度和系统开销

相关推荐
mftang12 天前
Zephyr RTOS 中用于同步多路复用 I/O 的核心机制k_poll 相关函数用法
zephyr rtos·k_poll
mftang12 天前
Zephyr RTOS 中k_cpu_idle 函数功能介绍
zephyr rtos·k_cpu_idle
mftang21 天前
Zephyr RTOS 下 bt_le_scan_start 函数详解
被动扫描·zephyr rtos·主动扫描
xianzi20202 个月前
提升财务工作效率:工资条生成器的应用价值分析
时间管理·流程优化·价值分析
IT小哥哥呀2 个月前
基于windows的个人/团队的时间管理工具
windows·c#·wpf·时间管理
jianghao20252 个月前
四种模式,无限可能:迷你关机工具的实用场景深度解析
效率工具·工作流·时间管理·自动关机·实用场景
玉梅小洋4 个月前
四象限法则:从理论到落地的高效时间管理指南
效率·技能·时间管理
余生H4 个月前
2026 年时间记录软件对比研究:时间线与「时光流」设计的产品分化
前端·软件工程·时间管理·时间记录
李长太4 个月前
四象限与DDL、Flag
知识管理·时间管理·个人管理