Zephyr RTOS 中timing 相关函数功能介绍
2026/5/23 3:06:45 网站建设 项目流程

目录

概述

1 核心时间函数列表

2 函数使用方法

2.1 时间基准与获取函数

2.2 线程睡眠函数

3. 超时(Timeout)机制

3.1 超时参数创建与使用

3.2 超时参数转换

4. 时间转换函数

4.1 Tick与毫秒转换

4.2 CPU周期与时间转换

5. 忙等待(Busy Wait)

5.1 高精度延迟

6. 完整应用示例

6.1 精确周期性任务调度

6.2 超时管理系统

6.3 性能优化与最佳实践

7 应用总结

7.1 Kconfig配置优化

7.2 常见问题与解决方案

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)

/* 获取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)

/* 获取系统启动后的毫秒数(最常用)*/ 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) 基本睡眠函数

/* 毫秒级睡眠(最常用)*/ 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) 高级睡眠模式(结合条件变量)

/* 带条件检查的限时等待 */ 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 超时参数创建与使用

/* 创建超时参数的几种方式 */ 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 超时参数转换

/* 超时值转换为其他单位 */ 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与毫秒转换

/* 常用转换函数 */ 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周期与时间转换

/* 周期 ↔ 时间转换(依赖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 高精度延迟

/* 忙等待:不释放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 精确周期性任务调度

#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 超时管理系统

#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) 选择合适的精度级别

/* 精度选择指南 */ 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配置优化

# 时间相关配置 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)实际睡眠102ms1. 提高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)可能返回01. 使用向上取整函数
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频率:平衡精度和系统开销

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询