【FreeRTOS】详细讲解FreeRTOS的软件定时器及通过示例讲述其用法

6 篇文章 1 订阅

软件定时器

  所谓定时器,也就可以类比生活中人们常用的闹钟,可以单次响铃提醒,也可以间隔固定时间响铃提醒;与FreeRTOS定时器不同的是周期不同,FreeRTOS的周期更加短,一般使用毫秒(ms)、秒(s)。
  软件定时器,是指定时器的触发方式,软件定时器一旦到达定时时间就会触发回调函数。
  回调函数,遵循快进快出原则,因此,其中一定不能存在任何的阻塞,如vTaskDelay()、while(1)或者是其他能够产生阻塞的情况。
  FreeRTOS的软件定时器在功能上支持:

  • 软件定时器单次与周期执行;
  • 裁剪:能通过宏关闭软件定时器功能;
  • 软件定时器创建;
  • 软件定时器启动;
  • 软件定时器停止;
  • 软件定时器复位;
  • 软件定时器删除;

定时器周期性工作与单次工作时间轴简图

  在freeRTOS的配置上,如果要是用定时器就需要配置下面几个宏定义:

//打开定时器
#define configUSE_TIMERS                1
//定时器的优先级
#define configTIMER_TASK_PRIORITY       50
//定时器栈大小
#define configTIMER_TASK_STACK_DEPTH    50
//定时器队列大小
#define configTIMER_QUEUE_LENGTH        50

FreeRTOS定时器的控制块

    typedef struct tmrTimerControl                  /* The old naming convention is used to prevent breaking kernel aware debuggers. */
    {
        const char * pcTimerName;                   /*<< Text name.  This is not used by the kernel, it is included simply to make debugging easier. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
        ListItem_t xTimerListItem;                  /*<< Standard linked list item as used by all kernel features for event management. */
        TickType_t xTimerPeriodInTicks;             /*<< How quickly and often the timer expires. */
        void * pvTimerID;                           /*<< An ID to identify the timer.  This allows the timer to be identified when the same callback is used for multiple timers. */
        TimerCallbackFunction_t pxCallbackFunction; /*<< The function that will be called when the timer expires. */
        #if ( configUSE_TRACE_FACILITY == 1 )
            UBaseType_t uxTimerNumber;              /*<< An ID assigned by trace tools such as FreeRTOS+Trace */
        #endif
        uint8_t ucStatus;                           /*<< Holds bits to say if the timer was statically allocated or not, and if it is active or not. */
    } xTIMER;

  定时器控制块中一共含有7个变量,其作用解析如下:

  • const char * pcTimerName:记录定时器名字
  • ListItem_t xTimerListItem:定时器的列表项,用于插入定时器列表;
  • TickType_t xTimerPeriodInTicks:定时器的周期,单位为系统节拍周期,即tick;
  • void * pvTimerID:定时器的ID,整数形式。该ID是当一个回调函数分配给一个或多个定时器时,可以根据IP不同处理回调函数中不同程序;
  • TimerCallbackFunction_t pxCallbackFunction:定时器回调函数;
  • UBaseType_t uxTimerNumber:跟踪工具分配的ID,如FreeRTOS+Trace;
  • uint8_t ucStatus:保存计时器是否静态分配,以及它是否处于活动状态;

  在定义完成控制块后,代码中会使用typedef xTIMER Timer_t重定义控制块的变量别名,在后续代码及开发中可以使用别名完成程序开发。


相关函数解析

  在FreeRTOS的定时器相关函数中,相关的函数有很多,如:发送指定到定时器任务队列函数xTimerGenericCommand、更新定时器周期函数xTimerChangePeriod、重启定时器函数xTimerReset、创建定时器函数xTimerCreate、启动定时器函数xTimerStart等等,其中最为重要且常用的函数当属创建定时器xTimerCreate、启动定时器xTimerStart

动态创建定时器函数
函数原型:

TimerHandle_t xTimerCreate(  const char * const pcTimerName,
                             TickType_t xTimerPeriodInTicks,
                             BaseType_t xAutoReload,
                             void * pvTimerID,
                             TimerCallbackFunction_t pxCallbackFunction );

函数说明:

  • const char * const pcTimerName:设置定时器名字;
  • TickType_t xTimerPeriodInTicks:设置定时器执行周期;
  • BaseType_t xAutoReload:设置定时器是单次或周期执行;
  • void * pvTimerID:设置定时器ID;
  • TimerCallbackFunction_t pxCallbackFunction:设置定时器回调函数;
  • 返回值:pxNewTimer ,数据类型为TimerHandle_t。一旦创建成功,则返回值不为NULL;否则,就为NULL;

函数解析:
  通过该函数可以创建一个定时器,但是该函数仅仅只是申请了一块内存空间,真正进行定时器初始化的函数为prvInitialiseNewTimer()。但是需要注意的是创建完成后处于就绪状态,需要启动后才能正常使用该定时器。

静态创建定时器函数
函数原型:

 TimerHandle_t xTimerCreateStatic(const char * const pcTimerName,
                                  TickType_t xTimerPeriodInTicks,
                                  BaseType_t xAutoReload,
                                  void * pvTimerID,
                                  TimerCallbackFunction_t pxCallbackFunction,
                                  StaticTimer_t *pxTimerBuffer );

函数参数说明:

  • const char * const pcTimerName:设置定时器名字;
  • TickType_t xTimerPeriodInTicks:设置定时器执行周期;
  • BaseType_t xAutoReload:设置定时器是单次或周期执行;
  • void * pvTimerID:设置定时器ID;
  • TimerCallbackFunction_t pxCallbackFunction:设置定时器回调函数;
  • StaticTimer_t *pxTimerBuffer:指向StaticTimer_t类型的变量,用于保存计时器的状态,就不需要动态分配内存了;
  • 返回值:pxNewTimer ,数据类型为TimerHandle_t。一旦创建成功,则返回值不为NULL;否则,就为NULL;

函数解析:
  定时器创建函数,与上面的定时器创建函数功能上一样,参数与返回值大部分相同,唯一不同的是定时器内存的分配方式,该函数无需动态分配内存,因为其已经传递一个指针用于保存定时器的相关内容。

启动定时器函数
函数原型:

BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait );

函数参数说明:

  • TimerHandle_t xTimer:创建定时器成功后返回的控制权柄,也就是其地址
  • TickType_t xTicksToWait:用户指定的超时阻塞时间,单位为系统节拍tick。在系统开始调用vTaskStartScheduler()前调用该函数,那么函数形参无作用。在定时器开始指令成功送达定时器命令队列前,定时器还是处于阻塞状态的。
  • 返回值:返回值类型为BaseType_t,如果启动命令无法成功地发送到定时器命令队列则
    返回 pdFAILE,成功发送则返回pdPASS。

函数解析:
  该函数的作用为启动定时器,一旦创建定时器后,就必须使用该函数启动,否则定时器还是处于就绪状态,没有任何实际用处
  启动定时器的函数实际上还是调用xTimerGenericCommand()函数,该函数含有五个参数、一个返回值,功能是发送命令到定时器命令队列。

在中断中启动定时器函数
函数原型:

BaseType_t xTimerStartFromISR( TimerHandle_t xTimer, 
							   BaseType_t * pxHigherPriorityTaskWoken );

函数参数说明:

  • TimerHandle_t xTimer:定时器控制句柄。
  • BaseType_t * pxHigherPriorityTaskWoken:定时器守护任务的大部分时间都在阻塞态等
    待定时器命令队列的命令。调用函数 xTimerStartFromISR()将会往定时器的命令队列发送
    一个启动命令,这很有可能会将定时器任务从阻塞态 移 除 。如果调用函数
    xTimerStartFromISR()让定时器任务脱离阻塞态,且定时器守护任务的优先级大于或者等
    于当前被中断的任务的优先级,那么 pxHigherPriorityTaskWoken 的值会在函数
    xTimerStartFromISR()内部设置为 pdTRUE,然后在中断退出之前执行一次上下文切换。
  • 返回值:返回值类型为BaseType_t,如果启动命令无法成功地发送到定时器命令队列则
    返回 pdFAILE,成功发送则返回pdPASS。

函数解析:
xTimerStartFromISR()是函数 xTimerStart()的中断版本,用于在中断中启动一个先前由函数xTimerCreate()xTimerCreateStatic()创建的软件定时器。

终止定时器函数
函数原型:

BaseType_t xTimerStop( TimerHandle_t xTimer, TickType_t xTicksToWait );

函数参数说明:

  • TimerHandle_t xTimer:创建定时器成功后返回的控制权柄,也就是其地址;
  • TickType_t xTicksToWait:用户指定的超时阻塞时间,单位为系统节拍tick。在系统开始调用vTaskStartScheduler()前调用该函数,那么函数形参无作用。在定时器开始指令成功送达定时器命令队列前,定时器还是处于阻塞状态的。
  • 返回值:返回值类型为BaseType_t,如果启动命令无法成功地发送到定时器命令队列则
    返回 pdFAILE,成功发送则返回pdPASS。

函数解析:
  终止定时器函数,实际上调用xTimerGenericCommand()函数,该函数含有五个参数、一个返回值,功能是发送命令到定时器命令队列。
  终止定时器后,定时器会回归就绪态,等待下一次唤醒。

在中断中停止定时器函数
函数原型:

BaseType_t xTimerStopFromISR(    TimerHandle_t xTimer,
                                 BaseType_t *pxHigherPriorityTaskWoken );

函数参数说明:

  • TimerHandle_t xTimer:定时器控制句柄。
  • BaseType_t * pxHigherPriorityTaskWoken:定时器守护任务的大部分时间都在阻塞态等
    待定时器命令队列的命令。调用函数 xTimerStartFromISR()将会往定时器的命令队列发送
    一个启动命令,这很有可能会将定时器任务从阻塞态 移 除 。如果调用函数
    xTimerStartFromISR()让定时器任务脱离阻塞态,且定时器守护任务的优先级大于或者等
    于当前被中断的任务的优先级,那么 pxHigherPriorityTaskWoken 的值会在函数
    xTimerStartFromISR()内部设置为 pdTRUE,然后在中断退出之前执行一次上下文切换。
  • 返回值:返回值类型为BaseType_t,如果启动命令无法成功地发送到定时器命令队列则
    返回 pdFAILE,成功发送则返回pdPASS。

函数解析:
  该函数用于在中断停止定时器,使其进入就绪态。使用条件与xTimerStartFromISR()函数类似。

设置定时器执行周期函数
函数原型:

void vTimerSetReloadMode( TimerHandle_t xTimer,
                          const BaseType_t xAutoReload )

函数参数说明:

  • TimerHandle_t xTimer:定时器的控制权柄
  • const BaseType_t xAutoReload:定时器新的执行周期。若为pdFALSE则单次执行;否则为pdTRUE,就周期执行。

函数解析:
  使用该函数能够重新设置定时器的执行周期。
重启定时器函数
函数原型:

BaseType_t xTimerReset( TimerHandle_t xTimer, TickType_t xTicksToWait );

函数参数说明:

  • TimerHandle_t xTimer:定时器的控制权柄;
  • TickType_t xTicksToWait:用户指定的超时阻塞时间,单位为系统节拍tick。

函数解析:
  重新启动一个定时器。

删除定时器函数
函数原型:

BaseType_t xTimerDelete( TimerHandle_t xTimer, TickType_t xTicksToWait );

函数参数说明:

  • TimerHandle_t xTimer:定时器控制权柄;
  • TickType_t xTicksToWait:用户指定的超时阻塞时间,单位为系统节拍tick。

函数解析:
  删除定时器函数,调用该函数能够删除对应定时器工作。

示例

创建两个定时器任务
  通过创建链各个定时器任务,完成定时器任务1实现——LED1间隔循环1s闪烁与定时器任务2实现——LED3单次闪烁。

void timeCallBackTask(void);
void timeCallBackTask2(void);

int main(void)
{
	TimerHandle_t xTimeHandle[2];
	xTimeHandle[0] = xTimerCreate(
								(const char *)"task1",// 定时器名字
								(TickType_t)1000,// 定时器的周期
								pdTRUE,// 定时器是单次执行还是周期执行, pdTRUE为周期执行,pdFALSE为周期执行
								(void*)1,// 给定时器分配的唯一ID
								(TimerCallbackFunction_t)timeCallBackTask// 定时器的回调函数
								);
	xTimeHandle[1] = xTimerCreate(
								(const char *)"task2",// 定时器名字
								(TickType_t)2000,// 定时器的周期
								pdFALSE,// 定时器是单次执行还是周期执行, pdTRUE为周期执行,pdFALSE为周期执行
								(void*)2,// 给定时器分配的唯一ID
								(TimerCallbackFunction_t)timeCallBackTask2// 定时器的回调函数
								);
	if(xTimeHandle[0] && xTimeHandle[1])
	{
		xTimerStart(xTimeHandle[0],0); //开启定时器
		xTimerStart(xTimeHandle[1],0); //开启定时器
	}
	else
		//定时器任务创建失败 打开LED8
		changeLedStateByLocation(LED8,ON);
	//打开任务调度器
	vTaskStartScheduler();
}

/*****************************************
* 函数功能:定时器任务
* 函数参数:无
* 函数返回值:无
*****************************************/
void timeCallBackTask(void)
{
	//闪烁LED1
	rollbackLedByLocation(LED1);
}

/*****************************************
* 函数功能:定时器任务2
* 函数参数:无
* 函数返回值:无
*****************************************/
void timeCallBackTask2(void)
{
	//闪烁LED3
	rollbackLedByLocation(LED3);
}

创建一个系统任务来管理两个定时器任务
  创建一个系统任务来开启与关闭定时器任务。由于创建完成定时器任务后,任务仍然处于就绪状态,需要开启函数xTimerStart才能够完成启动,而碰到关闭函数 xTimerStop定时器任务又会处于就绪状态。
  通过不断地开启与关闭,可以实现定时器任务timeCallBackTask与timeCallBackTask2不断地切换,实际上也实现就是LED1与LED2轮流闪烁。

void timeTask1(void);
void timeCallBackTask(void);
void timeCallBackTask2(void);

//任务控制权柄
TaskHandle_t xHandleTsak[4];
// 定时器控制权柄
TimerHandle_t xTimeHandle[2];
int main(void)
{

	BaseType_t xReturn[2];
	xTimeHandle[0] = xTimerCreate(
								(const char *)"task1",// 定时器名字
								(TickType_t)1000,// 定时器的周期
								pdTRUE,// 定时器是单次执行还是周期执行, pdTRUE为周期执行,pdFALSE为周期执行
								(void*)1,// 给定时器分配的唯一ID
								(TimerCallbackFunction_t)timeCallBackTask// 定时器的回调函数
								);
	xTimeHandle[1] = xTimerCreate(
								(const char *)"task2",// 定时器名字
								(TickType_t)2000,// 定时器的周期
								pdTRUE,// 定时器是单次执行还是周期执行, pdTRUE为周期执行,pdFALSE为周期执行
								(void*)2,// 给定时器分配的唯一ID
								(TimerCallbackFunction_t)timeCallBackTask2// 定时器的回调函数
								);
	xReturn[0] = xTaskCreate(
				(TaskFunction_t )timeTask1,//任务入口函数
				(const char *)"timeTask1",//任务名字
				(uint16_t)512,//任务栈大小
				(void*)NULL,//任务入口参数
				1,//任务优先级  优先级越高,任务优先选越高
				&xHandleTsak[2]//任务控制块
				);
	if(xReturn[0] == pdFALSE)
		changeLedStateByLocation(LED7,ON);
	//打开任务调度器
	vTaskStartScheduler();
}

/*****************************************
* 函数功能:定时器管理任务1
* 函数参数:无
* 函数返回值:无
*****************************************/
void timeTask1(void)
{
	uint32_t count = 0;
	const volatile TickType_t xDelay500ms = pdMS_TO_TICKS( 500UL );
	while(1)
	{
		//LED3闪烁
		if(++count % 2)
			rollbackLedByLocation(LED3);
		//开启定时器任务1  关闭定时器任务2
		if(count % 50 == 0)
		{
			xTimerStart(xTimeHandle[0],0);
			xTimerStop(xTimeHandle[1],0);
		}
		//开启定时器任务2  关闭定时器任务1
		if(count % 50 == 25)
		{
			xTimerStart(xTimeHandle[1],0);
			xTimerStop(xTimeHandle[0],0);
		}
		//非阻塞延时1s
		vTaskDelay( xDelay500ms );
	}
}

/*****************************************
* 函数功能:定时器任务
* 函数参数:无
* 函数返回值:无
*****************************************/
void timeCallBackTask(void)
{
	rollbackLedByLocation(LED1);
}

/*****************************************
* 函数功能:定时器任务2
* 函数参数:无
* 函数返回值:无
*****************************************/
void timeCallBackTask2(void)
{
	rollbackLedByLocation(LED2);
}

  本文是基于Cortex-M4核心板实现的,若需要移植FreeRTOS到Cortex-M4上可以参考文章【FreeRTOS】在Cortex-M4开发板上移植FreeRTOS并且实现LED灯闪烁(保姆级教程)
  小编这里还有一篇关于定时器的文章,也欢迎各位点击观看😉😉😉【FreeRTOS】详细讲解FreeRTOS中任务管理并通过示例讲述其用法

  最后 ,也欢迎大家留言或私信交流,大家共同进步!😁😁😁

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值