软定时器的原理与创建

1 软定时器的原理与创建

1.1 问题概述

在实际应用中,常常需要周期性或者在指定时间做一件事情:

  • 周期性:在指定的延时开始做某件事情,然后周期性重复执行。
  • 一次性:在指定的延时开始后做某件事情,执行一次后立即结束。

在这里插入图片描述
显然,硬件定时器有限,而这种需求可能有很多个。单纯依赖硬件定时器不能满足需求。也许,可以使用任务+tTaskDelay()。但是,如果每种需求都使用一个任务的话,导致任务很多,资源开销太大。

在这里插入图片描述
能否只用一个任务满足所有需求?

1.2 设计原理

每种需求的所有信息,保存到软定时器结构。
在这里插入图片描述
这时又出现了一个问题:如果定时器任务可能被更高优先级的任务抢占CPU运行,导致刷新定时器列表的时间滞后。

为了保证时间确定,我们可以增加一条在时钟节拍中断处理的定时器列表。

总体结构如下:
在这里插入图片描述

1.3 设计实现

定义

<think> 首先,用户的问题是:“我想了解实现定时器的方法 请问定时器如何实现定时器”,基于引用内容。引用提供了关于定时器的信息。 从引用中: - 引用[1]:区分了定时器和硬定时器,硬定时器基于硬件,定时器件的。 - 引用[2]:描述了如何使用硬件定时器和变量来实现定时功能。 - 引用[3]:解释了定时器是借助硬定时器产生累积计数值,可能被中断打断,因此不精确。 - 引用[4]:提供了定时器的实现细节,包括结构体和函数定义。 用户想了解实现定时器的方法,所以我需要基于引用内容,提供一个清晰、结构化的回答。 回答结构: - 根据系统指令,回答要结构清晰,帮助用户逐步解决问题。 - 使用中文回答。 - 所有数学表达式使用$...$格式,但由于主题是定时器,可能没有太多数学内容,但如果有变量或公式,就用$...$。 - 独立公式用$$...$$,但这里可能不需要。 - 引用的段落末尾添加引用标识,如[^1]。 - 生成相关问题。 关键点来自引用: - 定时器是基于硬定时器实现的,使用一个累积计数值。 - 硬定时器(如系统时钟节拍)产生中断,定期更新一个计数器(如系统滴答数)。 - 定时器维护自己的状态和计数器,比如delayTicks,在计数器减到0时触发回调函数。 - 定时器可能被中断打断,导致不精确,但对于非实时应用可接受。 实现步骤: 1. **依赖硬定时器**:定时器需要一个硬定时器作为基础,这个硬定时器定期中断(如每秒1000次)。 2. **系统滴答计数器**:在硬定时器的中断服务程序(ISR)中,更新一个全局计数器(如sysTickCount)。 3. **定时器结构**:定义一个定时器结构体,包含状态、初始延迟、周期、当前延迟、回调函数等。 4. **管理定时器**:使用一个列表或队列来管理所有定时器。 5. **更新定时器**:在系统滴答中断或一个低优先级任务中,遍历所有定时器,减少它们的delayTicks。当delayTicks为0时,触发回调函数;如果是周期定时器,重置delayTicks到周期值。 6. **处理不精确性**:由于定时器可能在中断上下文中处理,也可能在任务上下文中处理,以避免中断延迟问题。引用[4]提到了TIMER_CONFIG_TYPE_HARD和TIMER_CONFIG_TYPE_SOFT,分别表示在中断中处理或在任务中处理。 来自引用[4]的代码片段: - tTimer 结构体包括:startDelayTicks, durationTicks, delayTicks, timerFunc, arg, config, state. - 函数 tTimerInit 用于初始化定时器。 回答结构: 1. **介绍定时器**:解释什么是定时器及其特性。 2. **实现原理**:逐步描述如何实现定时器。 3. **代码示例**:提供一个简单的代码示例,基于引用内容。 4. **注意事项**:讨论不精确性和应用场景。 5. **生成相关问题**:在最后添加相关问题。 确保引用: - 在回答中引用相关段落时添加标识,如[^1]。 相关问题: - 基于用户问题,生成2-3个相关问题。 - 例如:如何优化定时器定时器和硬定时器的区别?在实时系统中如何使用定时器? 最终回答: - 用中文。 - 结构清晰。 </think> 基于您的问题和提供的引用内容,我将详细解释定时器的实现方法。定时器是一种件层面的定时机制,它依赖于硬定时器(如硬件中断)来维护一个全局计数器,并基于此生成多个可自定义的定时器实例。定时器通常用于对时间精度要求不高的场景(如用户界面刷新、后台任务调度),因为其执行可能被中断或高优先级任务打断,导致轻微延迟[^3][^1]。下面我将逐步说明实现方法,确保结构清晰易懂。 ### 定时器的实现原理 定时器的核心是借助硬定时器(如系统时钟节拍中断)产生一个全局累积计数值(如 `sysTickCount`)。每个定时器实例维护自己的状态和计数器,当计数器减至0时触发回调函数。实现过程分为以下步骤: 1. **依赖硬定时器基础** 定时器需要一个硬定时器作为驱动源。硬定时器通常基于硬件(如CPU的TSC或APIC),在固定时间间隔产生中断(例如每1ms中断一次)。在中断服务程序(ISR)中,更新一个全局计数器: ```c volatile uint32_t sysTickCount = 0; // 全局系统滴答计数器 // 硬定时器中断服务程序(简化示例) void SysTick_ISR(void) { sysTickCount++; // 每次中断递增计数器 } ``` 这一步确保有一个稳定的时间基准。硬定时器提供精确的计时,但定时器通过件逻辑扩展其功能[^1][^2]。 2. **定义定时器结构体** 每个定时器需要存储其配置和状态。基于引用[^4],定时器结构体通常包括以下字段: - `state`: 定时器状态(如已启动、已停止)。 - `startDelayTicks`: 初始延时计数值(单位:滴答数)。 - `durationTicks`: 周期计数值(用于重复定时器)。 - `delayTicks`: 当前剩余计数值(从 `startDelayTicks` 开始递减)。 - `timerFunc`: 回调函数指针,定时结束时执行。 - `arg`: 传递给回调函数的参数。 - `config`: 配置标志(如指定在中断或任务上下文中处理)。 示例结构体定义(基于引用[^4]): ```c typedef enum { tTimerCreated, tTimerStarted, tTimerRunning, tTimerStopped, tTimerDestroyed } tTimerState; typedef struct _tTimer { uint32_t startDelayTicks; uint32_t durationTicks; uint32_t delayTicks; void (*timerFunc)(void *arg); void *arg; uint32_t config; // 例如 TIMER_CONFIG_TYPE_SOFT 表示任务中处理 tTimerState state; } tTimer; ``` 3. **初始化定时器** 创建一个初始化函数来设置定时器参数。例如: ```c void tTimerInit(tTimer *timer, uint32_t delayTicks, uint32_t durationTicks, void (*timerFunc)(void *arg), void *arg, uint32_t config) { timer->startDelayTicks = delayTicks; timer->durationTicks = durationTicks; timer->delayTicks = delayTicks; // 当前计数初始化为初始延时 timer->timerFunc = timerFunc; timer->arg = arg; timer->config = config; timer->state = tTimerCreated; // 状态设为已创建 } ``` 这里,`delayTicks` 是第一次触发的延时(单位:滴答数),`durationTicks` 是周期触发的间隔(若为0则表示单次定时)。`config` 可指定处理方式:`TIMER_CONFIG_TYPE_HARD`(在中断中处理)或 `TIMER_CONFIG_TYPE_SOFT`(在低优先级任务中处理以减少中断延迟影响)[^4]。 4. **管理定时器列表** 所有定时器实例应存储在一个链表或队列中,便于批量更新。在系统初始化时创建此列表: ```c #define MAX_TIMERS 10 tTimer timerList[MAX_TIMERS]; // 简化示例,使用数组存储 uint8_t timerCount = 0; ``` 5. **更新定时器计数器** 在硬定时器的ISR或一个专用任务中,遍历所有定时器并更新计数器。推荐在任务上下文中处理(`TIMER_CONFIG_TYPE_SOFT`)以避免中断嵌套问题: ```c void UpdateSoftTimers(void) { for (int i = 0; i < timerCount; i++) { tTimer *timer = &timerList[i]; if (timer->state == tTimerStarted) { if (timer->delayTicks > 0) { timer->delayTicks--; // 递减当前计数 } if (timer->delayTicks == 0) { timer->state = tTimerRunning; timer->timerFunc(timer->arg); // 触发回调函数 if (timer->durationTicks > 0) { timer->delayTicks = timer->durationTicks; // 重置为周期值,用于重复定时 } else { timer->state = tTimerStopped; // 单次定时后停止 } } } } } ``` 此函数应由系统滴答计数器驱动(例如在 `sysTickCount` 更新后调用)。回调函数执行实际任务,如点亮LED或发送数据[^2][^3]。 6. **处理不精确性和优化** 定时器的误差主要源于中断延迟或被高优先级任务抢占。为减少影响: - 使用 `TIMER_CONFIG_TYPE_SOFT` 在任务中处理回调,避免在ISR中长时间执行。 - 添加误差补偿机制,如在回调中检查实际延迟并调整后续计数。 例如,回调函数可记录实际执行时间预期时间的差值:$ \Delta t = t_{\text{实际}} - t_{\text{预期}} $,并应用于下一次定时[^3]。 ### 完整示例代码 以下是一个简化实现,基于引用[^4]和上述原理: ```c #include <stdint.h> // 全局系统滴答计数器(由硬定时器更新) volatile uint32_t sysTickCount = 0; // 定时器结构体和初始化 typedef enum { tTimerCreated, tTimerStarted, tTimerStopped } tTimerState; typedef struct { uint32_t delayTicks; uint32_t durationTicks; void (*callback)(void*); void* arg; tTimerState state; } SoftTimer; void initSoftTimer(SoftTimer *timer, uint32_t delay, uint32_t duration, void (*cb)(void*), void *arg) { timer->delayTicks = delay; timer->durationTicks = duration; timer->callback = cb; timer->arg = arg; timer->state = tTimerCreated; } void startTimer(SoftTimer *timer) { timer->state = tTimerStarted; } // 在系统滴答任务中调用此函数 void processTimers() { for (int i = 0; i < MAX_TIMERS; i++) { SoftTimer *timer = &timerList[i]; if (timer->state == tTimerStarted && timer->delayTicks > 0) { timer->delayTicks--; if (timer->delayTicks == 0) { timer->callback(timer->arg); // 执行回调 if (timer->durationTicks > 0) { timer->delayTicks = timer->durationTicks; // 重置为周期值 } else { timer->state = tTimerStopped; } } } } } // 示例回调函数 void blinkLED(void *arg) { // 控制LED闪烁 } ``` ### 注意事项 - **精度问题**:定时器非实时,适用于允许$\pm 1$–$10\text{ms}$误差的场景(如网络协议超时)。高精度需求时使用硬定时器[^1][^3]。 - **资源开销**:多个定时器会增加CPU负载,需限制数量和更新频率。 - **线程安全**:在多任务系统中,使用互斥锁保护定时器列表。 - **应用场景**:常见于嵌入式系统、RTOS(如FreeRTOS的定时器功能)或用户态应用[^2][^4]。 通过以上方法,您可以灵活实现定时器定时器的核心优势是节省硬件资源,但需权衡精度[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值