乐于分享
好东西不私藏

ThreadX应用实践:软件定时器

ThreadX应用实践:软件定时器

对于单片机来说,定时器主要用于单次或周期性的执行任务或者触发通知。从实现来说,分为硬件定时器和软件定时器类型。

  • 硬件定时器:单片机内部存在的定时器,如系统滴答时钟SysTick,通用和高级定时器Timer等。
  • 软件定时器:软件定时器一般是建立在系统的时钟节拍上,通过事件触发或hook函数,单次或者周期性执行的软件函数。

软件定时器是建立在系统软件实现上,不受硬件数目限制,可以在资源允许的情况下,创建多个软件定时器。不过同样,软件定时器也受限于系统的时钟精度,抖动较大,适用于执行时间较长,对于实时性要求不高的场景。

软件定时器说明和接口

在Threadx中,软件定时器的接口如下所示。

函数
说明
tx_time_get
获取当前系统Tick时钟
tx_time_set
设置系统Tick时钟
tx_timer_activate
激活一个软件定时器
tx_timer_deactivate
停止一个软件定时器
tx_timer_change
修改一个软件定时器的时间
tx_timer_create
创建一个软件定时器
tx_timer_delete
删除一个软件定时器
tx_timer_info_get
获取一个软件定时器的信息
tx_timer_performance_info_get
获取软件定时器的性能信息
tx_timer_performance_system_info_get
获取软件定时器的系统性能信息

对于上述接口,比较关键的接口如下所示。

// 获取当前时间(对应函数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, 050000, TX_AUTO_ACTIVATE);if (status != TX_SUCCESS)    {return RT_FAIL;    }/* create periodic timer */    status = tx_timer_create(&periodic_timer, "periodic_timer"                            periodic_timer_callback, 020002000, 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: 定时器线程优先级。

基于这些特点,软件定时器可以满足通讯超时检测、低频数据处理(如读取传感器数据)等场景的需求,在实践中可以按需使用。