一、简介
软件定时器,是基于系统Tick时钟中断且由软件来模拟的定时器。当经过设定的Tick时钟计数值后,会触发用户定义的回调函数。定时精度与系统Tick时钟周期有关。
硬件定时器受硬件的限制,数量上不足以满足用户的实际需求。因此,为了满足用户需求,提供更多的定时器,LiteOS提供软件定时器功能。
软件定时器扩展了定时器的数量,允许创建更多的定时业务。
软件定时器功能上支持:
- 静态裁剪:能通过宏关闭软件定时器功能。
- 软件定时器创建。
- 软件定时器启动。
- 软件定时器停止。
- 软件定时器删除。
- 软件定时器剩余Tick数获取。
更多概念可以参考:FreeRTOS学习六(软件定时器)_freertos 执行定时器回调函数的内存消耗将是在定时器任务堆栈上动态分配_t_guest的博客-CSDN博客
Timer Management
二、运作机制
软件定时器使用了系统的一个队列和一个任务资源,软件定时器的触发遵循队列规则,先进先出。定时时间短的定时器总是比定时时间长的靠近队列头,满足优先被触发的准则。
软件定时器以Tick为基本计时单位,当用户创建并启动一个软件定时器时,LiteOS会根据当前系统Tick时间寄用户设置的定时间隔确定该定时器的到期Tick时间,并将该定时器控制结构挂入计时全局链表。
当Tick中断到来时,在Tick中断处理函数中扫描软件定时器的计时全局链表,看是否有定时器超时,若有则将超时的定时器记录下来。
Tick中断处理函数结束后,软件定时器任务(优先级最高)被唤醒,在该任务中调佣之前记录下来的定时器的超时回调函数。
三、API介绍
osTimerNew
函数功能:
创建一个软件定时器
函数原型:
osTimerId_t osTimerNew(osTimerFunc_t func, osTimerType_t type, void *argument, const osTimerAttr_t *attr)
参数:
func:超时回调函数
type:运行模式
osTimerOnce | 0,单次 |
osTimerPeriodic | 1,周期 |
argument:传给定时器的参数。没有填NULL
attr:定时器相关属性。自定义地址的时候会用到。大部分情况用不到,填NULL。
返回值:
NULL:失败
其他值:osTimerId_t类型的定时器ID。该ID给其他函数使用
实例:
osTimerPeriodic
char timer1_param[] = "timer1 param";
g_timer1_id = osTimerNew(Timer1_Callback, osTimerPeriodic, timer1_param, NULL);
osTimerStart
函数功能:
软件定时器启动
函数原型:
osStatus_t osTimerStart(osTimerId_t timer_id, uint32_t ticks)
参数:
timer_id:软件定时器ID,创建时osTimerNew获得。
ticks:软件定时器的定时周期。对于Hi3861,定时器单位为10ms。
返回值:
osOK:成功
其他值:失败
typedef enum {/** Operation completed successfully */osOK = 0,/** Unspecified error */osError = -1,/** Timeout */osErrorTimeout = -2,/** Resource error */osErrorResource = -3,/** Incorrect parameter */osErrorParameter = -4,/** Insufficient memory */osErrorNoMemory = -5,/** Service interruption */osErrorISR = -6,/** Reserved. It is used to prevent the compiler from optimizing enumerations. */osStatusReserved = 0x7FFFFFFF
} osStatus_t;
实例:
osTimerId_t g_timer1_id;
timerDelay = 100U;
status = osTimerStart(g_timer1_id, timerDelay);
osTimerStop
函数功能:
软件定时器停止
函数原型:
osStatus_t osTimerStop(osTimerId_t timer_id)
参数:
timer_id 定时器ID
返回值
osOK:成功
其他值:失败
实例:
osTimerId_t g_timer1_id;
osTimerStop(g_timer1_id);
osTimerDelete
函数功能:
软件定时器删除
函数原型:
osStatus_t osTimerDelete(osTimerId_t timer_id)
参数:
timer_id 定时器ID
返回值:
osOK:成功
其他值:失败
实例:
osTimerId_t g_timer1_id;
osTimerDelete(g_timer1_id);
四、代码实例
此代码创建两个软件定时器,定时器1为循环定时器,定时器2为单次定时器。
#define LOG_I(fmt, args...) printf("<%8ld> - [TIMER]:"fmt"\r\n",osKernelGetTickCount(),##args);
#define LOG_E(fmt, args...) printf("<%8ld>-[TIMER_ERR]>>>>>>>>>>>>:"fmt"\r\n",osKernelGetTickCount(), ##args);osTimerId_t g_timer1_id;
osTimerId_t g_timer2_id;/***** 定时器1 回调函数 *****/
void Timer1_Callback(void *arg)
{static uint8_t cnt = 0;LOG_I("timer1 callback,cnt:%d,param:%s",cnt,arg);if(cnt++ > 10){osTimerDelete(g_timer1_id);LOG_I("timer1 delete");}else if(cnt == 3){osTimerStop(g_timer1_id);LOG_I("timer1 stop and restart timer2");osTimerStart(g_timer2_id, 500);}
}/***** 定时器2 回调函数 *****/
void Timer2_Callback(void *arg)
{LOG_I("timer2 callback,param:%d",*(uint32_t *)arg);osTimerStart(g_timer1_id, 100);LOG_I("start timer1");
}char timer1_param[] = "timer1 param";
uint32_t timer2_param = 1024;void Hello_World(void)
{LOG_I("Test software Timer");uint32_t timerDelay;osStatus_t status;/*timer 1*/g_timer1_id = osTimerNew(Timer1_Callback, osTimerPeriodic, timer1_param, NULL);if (g_timer1_id != NULL){// Hi3861 1U=10ms,100U=1StimerDelay = 100U;status = osTimerStart(g_timer1_id, timerDelay);if (status != osOK){LOG_E("timer1 start error"); }else{LOG_I("timer1 start success,cycle:%dms",timerDelay * 10);}}else{LOG_E("timer1 create fail!!!");}/*timer 2*/g_timer2_id = osTimerNew(Timer2_Callback, osTimerOnce, (void *)&timer2_param, NULL);if (g_timer2_id != NULL){// Hi3861 1U=10ms,100U=1StimerDelay = 500U;status = osTimerStart(g_timer2_id, timerDelay);if (status != osOK){LOG_E("timer2 start error");}else{LOG_I("timer2 start success,cycle:%dms",timerDelay * 10);}}else{LOG_E("timer2 create fail!!!");}
}
定时器1为循环定时器,循环周期为1秒,定时器2为单次定时器,超时时间为5秒。两个定时器同时启动。在定时器1第三秒的时候,会停止自己,并且重新启动定时器2。定时器2超时后会重新启动定时器1。定时器1在第10次时会删除自己。
看运行结果:
可以看到,虽然定时器2在运行,但是如果此时再次调用osTimerStart来启动定时器2,会刷新定时器的超时时间。
这里我们用软件打印当前的时间戳,来看一下1秒的定时周期是否准确。
可以看到1秒的定时还是很准的。