对于单片机来说,定时器主要用于单次或周期性的执行任务或者触发通知。从实现来说,分为硬件定时器和软件定时器类型。
硬件定时器:单片机内部存在的定时器,如系统滴答时钟SysTick,通用和高级定时器Timer等。 软件定时器:软件定时器一般是建立在系统的时钟节拍上,通过事件触发或hook函数,单次或者周期性执行的软件函数。
软件定时器是建立在系统软件实现上,不受硬件数目限制,可以在资源允许的情况下,创建多个软件定时器。不过同样,软件定时器也受限于系统的时钟精度,抖动较大,适用于执行时间较长,对于实时性要求不高的场景。
软件定时器说明和接口
在Threadx中,软件定时器的接口如下所示。
对于上述接口,比较关键的接口如下所示。
// 获取当前时间(对应函数tx_time_get)// @return: 当前Tick时钟ULONG _tx_time_get(VOID)// 设置当前时间(对应函数tx_time_set)// @param new_time: 新的Tick时钟VOID _tx_time_set(ULONG new_time)// 激活定时器(对应函数tx_timer_active)// @timer_ptr: 处理的定时器// @return: TX_SUCCESS表示成功,其它表示失败UINT _txe_timer_activate(TX_TIMER *timer_ptr)// 停止定时器(对应函数tx_timer_deactivate)// @timer_ptr: 处理的定时器// @return: TX_SUCCESS表示成功,其它表示失败UINT _tx_timer_deactivate(TX_TIMER *timer_ptr)// 创建定时器(对应函数tx_timer_create)// @timer_ptr: 创建的定时器// @name_ptr: 定时器的名称// @expiration_function: 定时器到期时调用的函数// @expiration_input: 定时器到期时调用的函数的参数// @initial_ticks: 定时器的初始值// @reschedule_ticks: 定时器重新启动的间隔// @auto_activate: 定时器是否自动启动// @timer_control_block_size: 定时器控制块长度(强制为sizeof(TX_TIMER))// @return: TX_SUCCESS: 定时器创建成功, 其他: 定时器创建失败UINT _txe_timer_create(TX_TIMER *timer_ptr, CHAR *name_ptr, VOID (*expiration_function)(ULONG id), ULONG expiration_input, ULONG initial_ticks, ULONG reschedule_ticks, UINT auto_activate, UINT timer_control_block_size)// 删除定时器// @timer_ptr: 定时器控制块指针// @return: TX_SUCCESS: 定时器删除成功, 其他: 定时器删除失败UINT _txe_timer_delete(TX_TIMER *timer_ptr)对于上述接口,比较常用的就是tx_timer_create,用于创建软件定时器的接口。在此函数的参数接口中,比较重要的如下所示。
expiration_input: expiration_function执行时指定的id值。 reschedule_ticks: 为0表示单次模式,非0表示自动重载模式。 initial_ticks: 定时器的初始值,表示第一 auto_activate: 定时器是否自动启动,支持配置为TX_AUTO_ACTIVATE和TX_NO_ACTIVATE,则创建后定时器立刻启动。
软件定时器使用实例
软件定时器的使用实例如下所示。
#include"app_timer.h"#include"elog.h"static TX_TIMER one_shot_timer; /* 一次性定时器 */static TX_TIMER periodic_timer; /* 周期性定时器 */static TX_EVENT_FLAGS_GROUP timer_event_flag = {0};static TX_THREAD tx_app_timer_get_event_thread = {0};staticvolatile ULONG one_shot_count = 0;staticvolatile ULONG periodic_count = 0;#define APP_STACK_SIZE 512#define APP_THREAD_PRIO 9#define TIME_EVENT_FLAG_0 (0x01)#define TIME_EVENT_FLAG_1 (0x02)// one-shot timer callbackvoidone_shot_timer_callback(ULONG timer_input){ tx_event_flags_set(&timer_event_flag, TIME_EVENT_FLAG_0, TX_OR);}// periodic timer callbackvoidperiodic_timer_callback(ULONG timer_input){ tx_event_flags_set(&timer_event_flag, TIME_EVENT_FLAG_1, TX_OR);}voidtimer_event_flag_get_thread_entry(ULONG thread_input){ UINT status; ULONG actual_flags;while(1) { status = tx_event_flags_get(&timer_event_flag, TIME_EVENT_FLAG_0 | TIME_EVENT_FLAG_1, TX_OR_CLEAR, &actual_flags, TX_WAIT_FOREVER);if(status == TX_SUCCESS) { if(actual_flags & TIME_EVENT_FLAG_0) { one_shot_count++; log_i("One-shot timer triggered! Count: %lu", one_shot_count); }if(actual_flags & TIME_EVENT_FLAG_1) { periodic_count++; log_i("Periodic timer triggered! Count: %lu", periodic_count); } }else { log_i("Failed to get event flags, status: %d", status); } }}GlobalType_t app_timer_init(VOID *memory_ptr){ UINT status; CHAR *pointer; TX_BYTE_POOL *byte_pool = (TX_BYTE_POOL*)memory_ptr;/* create one-shot timer */// @timer_ptr: Timer pointer.// @name_ptr: Timer name.// @expiration_function: Timer expiration function.// @expiration_input: Timer expiration input.// @initial_ticks: Timer initial ticks.// @reschedule_ticks: Timer reschedule ticks, zero means no reschedule.// @auto_activate: Auto activate. status = tx_timer_create(&one_shot_timer, "one_shot_timer", one_shot_timer_callback, 0, 5000, 0, TX_AUTO_ACTIVATE);if (status != TX_SUCCESS) {return RT_FAIL; }/* create periodic timer */ status = tx_timer_create(&periodic_timer, "periodic_timer", periodic_timer_callback, 0, 2000, 2000, TX_AUTO_ACTIVATE);if (status != TX_SUCCESS) {return RT_FAIL; }if (tx_event_flags_create(&timer_event_flag, "timer_event_flag") != TX_SUCCESS) {return RT_FAIL; }/* Allocate the stack for get event thread. */if (tx_byte_allocate(byte_pool, (VOID **) &pointer, APP_STACK_SIZE, TX_NO_WAIT) != TX_SUCCESS) {return RT_FAIL; }/* Create get event thread. */if (tx_thread_create(&tx_app_timer_get_event_thread, "tx_app_timer_get_event_thread", timer_event_flag_get_thread_entry, 0, pointer, APP_STACK_SIZE, APP_THREAD_PRIO, APP_THREAD_PRIO, TX_NO_TIME_SLICE, TX_AUTO_START) != TX_SUCCESS) {return RT_FAIL; }return RT_OK;}对于上述代码,执行结果如下所示。

软件定时器总结说明
ThreadX的软件定时器是由系统内核提供的,允许开发者在指定的时钟节拍执行指定任务的机制。
对于软件定时器,使用时需要注意以下几点。
避免阻塞:对于Threadx来说,软件定时器共用一个系统线程,堵塞会导致所有定时任务挂起,影响执行时间。快速返回:软件定时器的处理要尽可能简单,可以是触发单个事件,或者执行单个处理,从而避免阻塞其他定时器的执行。
注意: ThreadX的软件定时器通过宏TX_TIMER_PROCESS_IN_ISR定义是否在中断中执行,定义时拥有更高的时间精度,不过同样要符合所有在中断中执行的要求。默认则由软件定时器线程进行处理,此时需要设置如下宏来设置定时器线程信息。
TX_TIMER_THREAD_STACK_SIZE: 定时器线程栈大小。TX_TIMER_THREAD_PRIO: 定时器线程优先级。
基于这些特点,软件定时器可以满足通讯超时检测、低频数据处理(如读取传感器数据)等场景的需求,在实践中可以按需使用。
夜雨聆风