LOADING
1603 字
8 分钟
ChibiOS/RT 时间与定时器

ChibiOS/RT 提供了一套完整的时间管理机制,包括系统时钟、虚拟定时器、阶梯定时器和时间测量工具,是实现周期任务、超时检测和性能分析的基础。

系统时间#

系统时间是 ChibiOS/RT 中所有时间相关操作的基础。内核维护一个全局系统计数器,在每次系统滴答(tick)时递增。

核心类型#

/* 系统时间戳,64位无符号整数,单位为 tick */
typedef uint64_t systime_t;
/* 系统时间间隔,32位无符号整数,用于表示相对时间 */
typedef uint32_t sysinterval_t;

systime_t 是绝对时间戳,从系统启动开始单调递增;sysinterval_t 表示相对时间间隔,两者用途不同,不可混用。

获取系统时间#

/* 获取当前系统时间(32位截断,适合短间隔测量) */
systime_t chSysGetRealtimeCounterX(void);
/* 获取系统滴答计数 */
systime_t chVTGetSystemTimeX(void);
/* 获取系统频率(ticks/秒) */
sysinterval_t chVTGetSystemFrequencyX(void);

线程休眠#

/* 休眠指定 tick 数 */
void chThdSleep(sysinterval_t interval);
/* 休眠到指定系统时间点 */
void chThdSleepUntil(systime_t time);
/* 示例:每 100ms 周期执行 */
static THD_WORKING_AREA(waWorker, 128);
static THD_FUNCTION(WorkerThread, arg) {
(void)arg;
systime_t prev = chVTGetSystemTimeX();
while (!chThdShouldTerminateX()) {
/* 执行周期任务 */
do_periodic_work();
/* 精确周期休眠,补偿任务执行耗时 */
chThdSleepUntil(chTimeAddX(prev, TIME_MS2I(100)));
prev = chVTGetSystemTimeX();
}
}

chThdSleepUntil 相比 chThdSleep 的优势在于:它基于绝对时间点休眠,不会因为任务执行耗时而产生累积漂移,适合对时序精度要求高的场景。

虚拟定时器#

虚拟定时器是 ChibiOS/RT 最常用的时间管理机制,用于在指定时间后执行回调函数。

数据结构#

typedef struct {
virtual_timer_t * volatile next; /* 链表指针 */
systime_t deadline; /* 触发时间点 */
vtfunc_t callback; /* 回调函数 */
void * par; /* 回调参数 */
} virtual_timer_t;

基本操作#

/* 启动定时器,interval 后触发回调 */
void chVTSet(virtual_timer_t *vtp, sysinterval_t interval,
vtfunc_t fn, void *par);
/* 重置定时器(取消 + 重新启动) */
void chVTReset(virtual_timer_t *vtp);
/* 检查定时器是否已启动(中断上下文) */
bool chVTIsArmedX(const virtual_timer_t *vtp);

单次定时器示例#

static virtual_timer_t led_timer;
/* LED 闪烁回调 */
static void led_blink_cb(void *par) {
(void)par;
palTogglePad(GPIOC, GPIOC_LED);
/* 重新启动定时器,实现周期效果 */
chVTSet(&led_timer, TIME_MS2I(500), led_blink_cb, NULL);
}
/* 启动 LED 闪烁 */
static void start_led_blink(void) {
chVTSet(&led_timer, TIME_MS2I(500), led_blink_cb, NULL);
}

周期定时器#

ChibiOS/RT 的虚拟定时器本质上是单次触发的。要实现周期定时器,在回调中重新设置即可:

static virtual_timer_t periodic_timer;
static void periodic_cb(void *par) {
uint32_t *count = (uint32_t *)par;
(*count)++;
do_periodic_task(*count);
/* 重新启动定时器 */
chVTSet(&periodic_timer, TIME_S2I(1), periodic_cb, par);
}
static uint32_t tick_count = 0;
static void start_periodic_timer(void) {
chVTSet(&periodic_timer, TIME_S2I(1), periodic_cb, &tick_count);
}

注意事项#

  • 虚拟定时器回调在中断上下文(系统 tick 中断)中执行,回调函数应尽量简短,避免阻塞操作。
  • 定时器回调中不能调用任何会导致线程切换的 API。
  • 定时器精度受限于系统 tick 频率,对于亚毫秒级精度需求应使用硬件定时器。

时间窗口#

时间窗口机制用于验证某个时间点是否落在指定的时间间隔内,常用于通信超时检测和周期任务调度。

时间间隔表示#

/* 时间间隔结构 */
typedef struct {
sysinterval_t start; /* 窗口起始点 */
sysinterval_t end; /* 窗口结束点 */
} time_window_t;
/* 无效时间窗口(已过期) */
#define TIME_WINDOW_NONE ((sysinterval_t)0)

窗口检查#

/* 检查时间是否在窗口内 */
static inline bool chTimeIsInWindowX(sysinterval_t time,
sysinterval_t start,
sysinterval_t end) {
return (time >= start) && (time < end);
}
/* 示例:超时检测 */
#define TIMEOUT_MS TIME_MS2I(5000) /* 5秒超时 */
static bool wait_with_timeout(virtual_timer_t *vt) {
systime_t start = chVTGetSystemTimeX();
systime_t deadline = chTimeAddX(start, TIMEOUT_MS);
chVTSet(vt, TIMEOUT_MS, timeout_cb, NULL);
/* 等待事件 */
eventmask_t evt = chEvtWaitAnyTimeout(ALL_EVENTS, TIMEOUT_MS);
if (evt == 0) {
return false; /* 超时 */
}
return true; /* 收到事件 */
}

阶梯定时器#

阶梯定时器(Step Timer)提供一种轻量级的时间步进机制,适用于需要在固定时间间隔执行多个任务的场景,比虚拟定时器更节省资源。

数据结构与操作#

typedef struct {
systime_t next; /* 下一步时间点 */
sysinterval_t step; /* 步进间隔 */
stfunc_t callback; /* 回调函数 */
void * par; /* 回调参数 */
} step_timer_t;
/* 初始化阶梯定时器 */
void chSTimerSetup(step_timer_t *stp, sysinterval_t step,
stfunc_t fn, void *par);
/* 驱动阶梯定时器(在 tick 中断或主循环中调用) */
void chSTimerTick(step_timer_t *stp);

阶梯定时器示例#

static step_timer_t heartbeat_timer;
/* 心跳回调 */
static void heartbeat_cb(void *par) {
(void)par;
palTogglePad(GPIOB, GPIOB_HEARTBEAT);
}
/* 初始化并启动 */
static void heartbeat_init(void) {
chSTimerSetup(&heartbeat_timer, TIME_MS2I(250),
heartbeat_cb, NULL);
}
/* 在主循环或周期任务中驱动 */
static THD_FUNCTION(HeartbeatThread, arg) {
(void)arg;
heartbeat_init();
while (!chThdShouldTerminateX()) {
chSTimerTick(&heartbeat_timer);
chThdSleep(TIME_MS2I(50));
}
}

阶梯定时器与虚拟定时器的区别:阶梯定时器需要显式调用 chSTimerTick 来驱动,适合在已有的周期任务中复用;虚拟定时器由内核自动驱动,适合独立的定时需求。

时间测量#

ChibiOS/RT 提供时间测量工具,用于精确测量代码段的执行时间,是性能分析和调试的重要手段。

数据结构#

typedef struct {
systime_t start; /* 测量起始时间 */
systime_t end; /* 测量结束时间 */
systime_t best; /* 最短执行时间 */
systime_t worst; /* 最长执行时间 */
uint32_t n; /* 测量次数 */
} time_measurement_t;

测量操作#

/* 初始化测量对象 */
void chTMInitTimeMeasurementX(time_measurement_t *tmp);
/* 开始测量 */
void chTMStart(time_measurement_t *tmp);
/* 停止测量并更新统计 */
void chTMStop(time_measurement_t *tmp);
/* 获取测量结果(tick 数) */
systime_t chTMGetElapsedX(const time_measurement_t *tmp);

性能分析示例#

static time_measurement_t meas;
/* 测量中断服务程序执行时间 */
void irq_handler(void) {
chTMStart(&meas);
/* 执行 ISR 逻辑 */
process_interrupt();
chTMStop(&meas);
}
/* 定期打印测量结果 */
static THD_FUNCTION(MonitorThread, arg) {
(void)arg;
chTMInitTimeMeasurementX(&meas);
while (!chThdShouldTerminateX()) {
chThdSleep(TIME_S2I(5));
/* 打印统计信息 */
uart_printf("n=%lu, best=%lu, worst=%lu\r\n",
meas.n,
meas.best,
meas.worst);
}
}

时间测量会自动记录最小值和最大值,便于发现最坏情况下的执行时间,对实时系统的响应时间分析非常有价值。

总结#

ChibiOS/RT 的时间管理机制层次分明:

机制用途精度适用场景
系统时间获取当前时间tick 级基础时间参考
虚拟定时器延时回调tick 级超时、周期任务
阶梯定时器步进式定时调用精度复用周期任务
时间窗口间隔验证tick 级通信检测
时间测量性能分析tick 级调试优化

合理选择时间管理机制,是编写高效、精确嵌入式程序的关键。在实际项目中,通常需要组合使用多种机制来满足不同的时间管理需求。

ChibiOS/RT 时间与定时器
/posts/chibios-rt-time/
作者
JJZBQA
发布于
2024-12-19
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时