一、简介
软件定时器,是基于系统Tick时钟中断且由软件来模拟的定时器。当经过设定的Tick时钟计数值后,会触发用户定义的回调函数。定时精度与系统Tick时钟周期有关。
硬件定时器受硬件的限制,数量上不足以满足用户的实际需求。因此,为了满足用户需求,提供更多的定时器,LiteOS提供软件定时器功能。
软件定时器扩展了定时器的数量,允许创建更多的定时业务。
软件定时器功能上支持:
- 静态裁剪:能通过宏关闭软件定时器功能。
- 软件定时器创建。
- 软件定时器启动。
- 软件定时器停止。
- 软件定时器删除。
- 软件定时器剩余Tick数获取。
更多概念可以参考:FreeRTOS学习六(软件定时器)_freertos 执行定时器回调函数的内存消耗将是在定时器任务堆栈上动态分配_t_guest的博客-CSDN博客
二、运作机制
软件定时器使用了系统的一个队列和一个任务资源,软件定时器的触发遵循队列规则,先进先出。定时时间短的定时器总是比定时时间长的靠近队列头,满足优先被触发的准则。
软件定时器以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=1S
timerDelay = 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=1S
timerDelay = 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秒的定时还是很准的。