FreeRTOS操作系统软件定时器(十)

FreeRTOS总结


前言

硬件定时器
CPU内部自带的定时器模块,通过初始化、配置可以实现定时,定时时间到以后就会执行相应的定时器中断处理函数。硬件定时器一般都带有其它功能,比如PWM输出、输入捕获等等功能。但是重要的是缺点是硬件定时器数量少!!

软件定时器
软件定时器允许设置一段时间,当设置的时间到达之后就执行指定的功能函数,被定时器调用的这个功能函数叫做定时器的回调函数。回调函数的两次执行间隔叫做定时器的定时周期,简而言之,当定时器的定时周期到了以后就会执行回调函数。
提示:以下是本篇文章正文内容,下面案例可供参考

一、回调函数是什么?

软件定时器的回调函数是在定时器服务任务中执行的,***所以一定不能在回调函数中调用任何会阻塞任务的 API 函数!***比如,定时器回调函数中千万不能调用 vTaskDelay()、vTaskDelayUnti(),还有一些访问队列或者信号量的非零阻塞时间的 API 函数也不能调用。

FreeRTOS 提供了很多定时器有关的 API 函数,这些 API 函数大多都使用 FreeRTOS的队列发送命令给定时器服务任务。这个队列叫做定时器命令队列。定时器命令队列是提供给FreeRTOS 的软件定时器使用的,用户不能直接访问!

二、定时器配置

1.软件定时器的分类

(1)软件定时器分两种:单次定时器和周期定时器
(2)***单次定时器***的话定时器回调函数就执行一次,比如定时 1s,当定时时间到了以后就会执行一次回调函数,然后定时器就会停止运行。
周期定时器一旦启动以后就会在执行完回调函数以后自动的重新启动,这样
回调函数就会周期性的执行

2.定时器相关的API函数

在这里插入图片描述
1、函数 xTimerReset()
复位一个软件定时器,此函数只能用在任务中,不能用于中断服务函数!此函数是一个宏,真正执行的是函数 xTimerGenericCommand(),函数原型如下:

BaseType_t xTimerReset( TimerHandle_t xTimer, 
TickType_t xTicksToWait )

参数:
xTimer: 要复位的软件定时器的句柄。
xTicksToWait: 设置***阻塞时间***,调用函数 xTimerReset ()开启软件定时器其实就是向定时器命令队列发送一条 tmrCOMMAND_RESET 命令,既然是向队列发送消息,那肯定会涉及到入队阻塞时间的设置。
返回值:
pdPASS:软件定时器复位成功,其实就是命令发送成功。
pdFALL:软件定时器复位失败,命令发送失败。
2、函数 xTimerResetFromISR()
此函数是 xTimerReset()的中断版本,此函数用于中断服务函数中!此函数是一个宏,真正执行的是函数 xTimerGenericCommand(),函数原型如下:

BaseType_t xTimerResetFromISR( TimerHandle_t xTimer,
BaseType_t * pxHigherPriorityTaskWoken );

参数:
xTimer: 要复位的软件定时器的句柄。
pxHigherPriorityTaskWoken: 记退出此函数以后是否进行任务切换,这个变量的值函数会自动设置的,用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为 pdTRUE 的时候在退出中断服务函数之前一定要进行一次任务切换。
返回值:
pdPASS: 软件定时器复位成功,其实就是命令发送成功。
pdFAIL: 软件定时器复位失败,命令发送失败。
在这里插入图片描述
1、函数 xTiemrCreate()
此函数用于创建一个软件定时器,所需要的内存通过动态内存管理方法分配。新创建的软件 定 时 器 处 于 休 眠 状 态 , 也 就 是 未 运 行 的 。 函 数 xTimerStart() 、 xTimerReset() 、xTimerStartFromISR() 、xTimerResetFromISR() 、 xTimerChangePeriod() 和xTimerChangePeriodFromISR()可以使新创建的定时器进入活动状态,此函数的原型如下:

TimerHandle_t xTimerCreate( const char * const pcTimerName,
 TickType_t xTimerPeriodInTicks,
 UBaseType_t uxAutoReload,
 void * pvTimerID,
 TimerCallbackFunction_t pxCallbackFunction )

参数:
pcTimerName: 软件定时器名字,名字是一串字符串,用于调试使用。

xTimerPeriodInTicks : 软件定时器的定时器周期, 单位是时钟节拍数。可以借助
portTICK_PERIOD_MS 将 ms 单位转换为时钟节拍数。举个例子,定时器的周期为 100 个时钟节拍的话,那么 xTimerPeriodInTicks 就为100,当定时器周期为 500ms 的时候 xTimerPeriodInTicks 就可以设置为(500/ portTICK_PERIOD_MS)。

uxAutoReload: 设置定时器模式,单次定时器还是周期定时器?当此参数为 pdTRUE
的时候表示创建的是周期定时器。如果为 pdFALSE 的话表示创建的是单次定时器。

pvTimerID: 定时器 ID 号,一般情况下每个定时器都有一个回调函数,当定时器定
时周期到了以后就会执行这个回调函数。但是 FreeRTOS 也支持多个定时器共用同一个回调函数,在回调函数中根据定时器的 ID 号来处理不同的定时器。

pxCallbackFunction: 定时器回调函数,当定时器定时周期到了以后就会调用这个函数。

返回值:
NULL: 软件定时器创建失败。
其他值: 创建成功的软件定时器句柄。
在这里插入图片描述
1、函数 xTimerStart()
启动软件定时器,函数 xTimerStartFromISR()是这个函数的中断版本,可以用在中断服务函数中。如果软件定时器没有运行的话调用函数 xTimerStart()就会计算定时器到期时间,如果软件定时器正在运行的话调用函数xTimerStart()的结果和 xTimerReset()一样。此函数是个宏,真
正执行的是函数 xTimerGenericCommand,函数原型如下:

BaseType_t xTimerStart( TimerHandle_t xTimer, 
TickType_t xTicksToWait )

参数:
xTimer: 要开启的软件定时器的句柄。
xTicksToWait: 设置阻塞时间,调用函数 xTimerStart()开启软件定时器其实就是向定时器命令
队列发送一条 tmrCOMMAND_START 命令,既然是向队列发送消息,那肯
定会涉及到入队阻塞时间的设置。
返回值:
pdPASS: 软件定时器开启成功,其实就是命令发送成功。
pdFAIL: 软件定时器开启失败,命令发送失败。
2、函数 xTimerStartFromISR()
此函数是函数 xTimerStart()的中断版本,用在中断服务函数中,此函数是一个宏,真正执行的是函数 xTimerGenericCommand(),此函数原型如下:

BaseType_t xTimerStartFromISR( TimerHandle_t xTimer,
BaseType_t * pxHigherPriorityTaskWoken );

参数:
xTimer: 要开启的软件定时器的句柄。
pxHigherPriorityTaskWoken: 标记退出此函数以后是否进行任务切换,这个变量的值函数会自动设置的,用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为 pdTRUE 的时候在退出中断服务函数之前一定要进行一次任务切换。

返回值:
pdPASS: 软件定时器开启成功,其实就是命令发送成功。
pdFAIL: 软件定时器开启失败,命令发送失败。
在这里插入图片描述
1、函数 xTimerStop()
此函数用于停止一个软件定时器,此函数用于任务中,不能用在中断服务函数中!此函数
是一个宏,真正调用的是函数xTimerGenericCommand(),函数原型如下:

BaseType_t xTimerStop ( TimerHandle_t xTimer, 
TickType_t xTicksToWait )

参数:
xTimer: 要停止的软件定时器的句柄。
xTicksToWait: 设置阻塞时间,调用函数 xTimerStop()停止软件定时器其实就是向定时器命令
队列发送一条 tmrCOMMAND_STOP 命令,既然是向队列发送消息,那肯定会涉及到入队阻塞时间的设置。
返回值:
pdPASS: 软件定时器停止成功,其实就是命令发送成功。
pdFAIL: 软件定时器停止失败,命令发送失败。
1、函数 xTimerStopFromISR()
此函数是 xTimerStop()的中断版本,此函数用于中断服务函数中!此函数是一个宏,真正执行的是函数 xTimerGenericCommand(),函数原型如下:

BaseType_t xTimerStopFromISR( TimerHandle_t xTimer,
BaseType_t * pxHigherPriorityTaskWoken );

参数:
xTimer: 要停止的软件定时器句柄。
pxHigherPriorityTaskWoken: 标记退出此函数以后是否进行任务切换,这个变量的值函数会自动设置的,用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为 pdTRUE 的时候在退出中断服务函数之前一定要进行一次任务切换。
返回值:
pdPASS: 软件定时器停止成功,其实就是命令发送成功。
pdFAIL: 软件定时器停止失败,命令发送失败。
在这里插入图片描述
1、函数 xTimerReset()
复位一个软件定时器,此函数只能用在任务中,不能用于中断服务函数!此函数是一个宏,真正执行的是函数 xTimerGenericCommand(),函数原型如下:

BaseType_t xTimerReset( TimerHandle_t xTimer, 
TickType_t xTicksToWait )

参数:
xTimer: 要复位的软件定时器的句柄。
xTicksToWait: 设置阻塞时间,调用函数 xTimerReset ()开启软件定时器其实就是向定时器命令队列发送一条 tmrCOMMAND_RESET 命令,既然是向队列发送消息,那肯定会涉及到入队阻塞时间的设置。
返回值:
pdPASS: 软件定时器复位成功,其实就是命令发送成功。
pdFAIL: 软件定时器复位失败,命令发送失败。
2、函数 xTimerResetFromISR()
此函数是 xTimerReset()的中断版本,此函数用于中断服务函数中!此函数是一个宏,真正执行的是函数 xTimerGenericCommand(),函数原型如下:

BaseType_t xTimerResetFromISR( TimerHandle_t xTimer,
BaseType_t * pxHigherPriorityTaskWoken );

参数:
xTimer: 要复位的软件定时器的句柄。
pxHigherPriorityTaskWoken: 记退出此函数以后是否进行任务切换,这个变量的值函数会自动设置的,用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为 pdTRUE 的时候在退出中断服务函数之前一定要进行一次任务切换。
返回值:
pdPASS: 软件定时器复位成功,其实就是命令发送成功。
pdFAIL: 软件定时器复位失败,命令发送失败。

三、 软件定时器实验

在这里插入图片描述
在这里插入图片描述
代码:

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "timer.h"
#include "lcd.h"
#include "key.h"
#include "beep.h"
#include "malloc.h"
#include "string.h"
#include "FreeRTOS.h"
#include "task.h"
/************************************************
 ALIENTEK ¾«Ó¢STM32F103¿ª·¢°å FreeRTOSʵÑé17-1
 FreeRTOSÈÎÎñ֪ͨģÄâ¶þÖµÐźÅÁ¿ÊµÑé-HAL¿â°æ±¾
 ¼¼ÊõÖ§³Ö£ºwww.openedv.com
 ÌÔ±¦µêÆÌ£ºhttp://eboard.taobao.com 
 ¹Øע΢ÐŹ«ÖÚƽ̨΢Ðźţº"ÕýµãÔ­×Ó"£¬Ãâ·Ñ»ñÈ¡STM32×ÊÁÏ¡£
 ¹ãÖÝÊÐÐÇÒíµç×ӿƼ¼ÓÐÏÞ¹«Ë¾  
 ×÷ÕߣºÕýµãÔ­×Ó @ALIENTEK
************************************************/

//ÈÎÎñÓÅÏȼ¶
#define START_TASK_PRIO		1
//ÈÎÎñ¶ÑÕ»´óС	
#define START_STK_SIZE 		256  
//ÈÎÎñ¾ä±ú
TaskHandle_t StartTask_Handler;
//ÈÎÎñº¯Êý
void start_task(void *pvParameters);

//ÈÎÎñÓÅÏȼ¶
#define TASK1_TASK_PRIO		2
//ÈÎÎñ¶ÑÕ»´óС	
#define TASK1_STK_SIZE 		256  
//ÈÎÎñ¾ä±ú
TaskHandle_t Task1Task_Handler;
//ÈÎÎñº¯Êý
void task1_task(void *pvParameters);

//ÈÎÎñÓÅÏȼ¶
#define DATAPROCESS_TASK_PRIO 3
//ÈÎÎñ¶ÑÕ»´óС	
#define DATAPROCESS_STK_SIZE  256 
//ÈÎÎñ¾ä±ú
TaskHandle_t DataProcess_Handler;
//ÈÎÎñº¯Êý
void DataProcess_task(void *pvParameters);


//ÓÃÓÚÃüÁî½âÎöÓõÄÃüÁîÖµ
#define LED1ON	1
#define LED1OFF	2
#define BEEPON	3
#define BEEPOFF	4
#define COMMANDERR	0XFF

//½«×Ö·û´®ÖеÄСд×Öĸת»»Îª´óд
//str:Ҫת»»µÄ×Ö·û´®
//len£º×Ö·û´®³¤¶È
void LowerToCap(u8 *str,u8 len)
{
	u8 i;
	for(i=0;i<len;i++)
	{
		if((96<str[i])&&(str[i]<123))	//Сд×Öĸ
		str[i]=str[i]-32;				//ת»»Îª´óд
	}
}

//ÃüÁî´¦Àíº¯Êý£¬½«×Ö·û´®ÃüÁîת»»³ÉÃüÁîÖµ
//str£ºÃüÁî
//·µ»ØÖµ: 0XFF£¬ÃüÁî´íÎó£»ÆäËûÖµ£¬ÃüÁîÖµ
u8 CommandProcess(u8 *str)
{
	u8 CommandValue=COMMANDERR;
	if(strcmp((char*)str,"LED1ON")==0) CommandValue=LED1ON;
	else if(strcmp((char*)str,"LED1OFF")==0) CommandValue=LED1OFF;
	else if(strcmp((char*)str,"BEEPON")==0) CommandValue=BEEPON;
	else if(strcmp((char*)str,"BEEPOFF")==0) CommandValue=BEEPOFF;
	return CommandValue;
}

int main(void)
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//ÉèÖÃϵͳÖжÏÓÅÏȼ¶·Ö×é4	 
	delay_init();	    				//ÑÓʱº¯Êý³õʼ»¯	 
	uart_init(115200);					//³õʼ»¯´®¿Ú
	LED_Init();		  					//³õʼ»¯LED
	KEY_Init();							//³õʼ»¯°´¼ü
	BEEP_Init();						//³õʼ»¯·äÃùÆ÷
	LCD_Init();							//³õʼ»¯LCD
	my_mem_init(SRAMIN);            	//³õʼ»¯ÄÚ²¿ÄÚ´æ³Ø

	POINT_COLOR=RED;
   	LCD_ShowString(10,10,200,16,16,"ATK STM32F103/407");	
	LCD_ShowString(10,30,200,16,16,"FreeRTOS Examp 17-1");
	LCD_ShowString(10,50,200,16,16,"Task Notify Bina Sem");
	LCD_ShowString(10,70,200,16,16,"Command data:");
	
	//´´½¨¿ªÊ¼ÈÎÎñ
    xTaskCreate((TaskFunction_t )start_task,            //ÈÎÎñº¯Êý
                (const char*    )"start_task",          //ÈÎÎñÃû³Æ
                (uint16_t       )START_STK_SIZE,        //ÈÎÎñ¶ÑÕ»´óС
                (void*          )NULL,                  //´«µÝ¸øÈÎÎñº¯ÊýµÄ²ÎÊý
                (UBaseType_t    )START_TASK_PRIO,       //ÈÎÎñÓÅÏȼ¶
                (TaskHandle_t*  )&StartTask_Handler);   //ÈÎÎñ¾ä±ú              
    vTaskStartScheduler();          //¿ªÆôÈÎÎñµ÷¶È
}

//¿ªÊ¼ÈÎÎñÈÎÎñº¯Êý
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           //½øÈëÁÙ½çÇø
	
    //´´½¨TASK1ÈÎÎñ
    xTaskCreate((TaskFunction_t )task1_task,             
                (const char*    )"task1_task",           
                (uint16_t       )TASK1_STK_SIZE,        
                (void*          )NULL,                  
                (UBaseType_t    )TASK1_TASK_PRIO,        
                (TaskHandle_t*  )&Task1Task_Handler);   
    //´´½¨TASK2ÈÎÎñ
    xTaskCreate((TaskFunction_t )DataProcess_task,     
                (const char*    )"keyprocess_task",   
                (uint16_t       )DATAPROCESS_STK_SIZE,
                (void*          )NULL,
                (UBaseType_t    )DATAPROCESS_TASK_PRIO,
                (TaskHandle_t*  )&DataProcess_Handler); 
    vTaskDelete(StartTask_Handler); //ɾ³ý¿ªÊ¼ÈÎÎñ
    taskEXIT_CRITICAL();            //Í˳öÁÙ½çÇø
}

//task1ÈÎÎñº¯Êý
void task1_task(void *pvParameters)
{
	while(1)
	{
		LED0=!LED0;
    vTaskDelay(500);             	//ÑÓʱ500ms£¬Ò²¾ÍÊÇ500¸öʱÖÓ½ÚÅÄ	
	}
}

//DataProcess_taskº¯Êý
void DataProcess_task(void *pvParameters)
{
	u8 len=0;
	u8 CommandValue=COMMANDERR;
	u32 NotifyValue;
	
	u8 *CommandStr;
	POINT_COLOR=BLUE;
	while(1)
	{
		
		NotifyValue=ulTaskNotifyTake(pdTRUE,portMAX_DELAY);	//»ñÈ¡ÈÎÎñ֪ͨ   2.×èÈûʱ¼ä
		if(NotifyValue==1)									//ÇåÁã֮ǰµÄÈÎÎñֵ֪ͨΪ1£¬ËµÃ÷ÈÎÎñ֪ͨÓÐЧ
		{
			len=USART_RX_STA&0x3fff;						//µÃµ½´Ë´Î½ÓÊÕµ½µÄÊý¾Ý³¤¶È
			CommandStr=mymalloc(SRAMIN,len+1);				//ÉêÇëÄÚ´æ
			sprintf((char*)CommandStr,"%s",USART_RX_BUF);
			CommandStr[len]='\0';							//¼ÓÉÏ×Ö·û´®½áβ·ûºÅ
			LowerToCap(CommandStr,len);						//½«×Ö·û´®×ª»»Îª´óд		
			CommandValue=CommandProcess(CommandStr);		//ÃüÁî½âÎö
			if(CommandValue!=COMMANDERR)
			{
				LCD_Fill(10,90,210,110,WHITE);				//Çå³ýÏÔʾÇøÓò
				LCD_ShowString(10,90,200,16,16,CommandStr);	//ÔÚLCDÉÏÏÔʾÃüÁî
				printf("ÃüÁîΪ:%s\r\n",CommandStr);
				switch(CommandValue)						//´¦ÀíÃüÁî
				{
					case LED1ON: 
						LED1=0;
						break;
					case LED1OFF:
						LED1=1;
						break;
					case BEEPON:
						BEEP=1;
						break;
					case BEEPOFF:
						BEEP=0;
						break;
				}
			}
			else
			{
				printf("ÎÞЧµÄÃüÁÇëÖØÐÂÊäÈë!!\r\n");
			}
			USART_RX_STA=0;
			memset(USART_RX_BUF,0,USART_REC_LEN);			//´®¿Ú½ÓÊÕ»º³åÇøÇåÁã
			myfree(SRAMIN,CommandStr);						//ÊÍ·ÅÄÚ´æ
		}
		else 
		{
			vTaskDelay(10);      //ÑÓʱ10ms£¬Ò²¾ÍÊÇ10¸öʱÖÓ½ÚÅÄ	
		}
	}
}





总结

软件定时器分类周期定时器和单次定时器,会创建、开启、复位、关闭相关函数的使用。
继续加油!

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 在CubeMX中配置定时器 首先在CubeMX的"Pinout & Configuration"页面中选择一个可用的定时器,例如TIM2。 然后在"Clock Configuration"页中,启用TIM2的时钟,并配置其时钟频率。可以将TIM2的时钟频率设置为100kHz,用于实现一个10ms的定时器。 接下来,在"Configuration"页面,将TIM2配置为定时器模式,并设置其预分频器和计数器值,以实现一个10ms的定时器。例如,选择TIM2的时钟预分频器为999,计数器值为99,可以得到一个10ms的定时器。 2. 生成FreeRTOS定时器中断 在"Project Manager"页面中,将FreeRTOS添加到项目中,并在FreeRTOSConfig.h文件中启用定时器中断。 //启用FreeRTOS定时器中断 #define configUSE_TIMERS 1 接下来,在FreeRTOSConfig.h文件中配置定时器中断的优先级。可以选择一个较高的优先级,以确保定时器中断能够及时地被处理。 //定时器中断的优先级 #define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES - 1) 3. 实现定时器中断处理函数 在项目的main.c文件中,实现定时器中断处理函数。首先定义一个变量用于计数,每次定时器中断时将该变量加1。然后在定时器中断处理函数中,调用FreeRTOS的xTaskIncrementTick()函数,以增加FreeRTOS的系统时间计数器。 //定义一个变量用于计数 volatile uint32_t ulTickCounter = 0; //定时器中断处理函数 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { //计数加1 ulTickCounter++; //增加FreeRTOS的系统时间计数器 if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) { xTaskIncrementTick(); } } 4. 启动定时器 在main函数中,调用HAL_TIM_Base_Start_IT()函数启动定时器。这将启用定时器中断,并开始计时。 int main(void) { //初始化FreeRTOS和HAL库 HAL_Init(); SystemClock_Config(); MX_FREERTOS_Init(); //启动定时器 HAL_TIM_Base_Start_IT(&htim2); //启动FreeRTOS的调度器 vTaskStartScheduler(); //永远不会到达这里 while (1) { } } 通过以上步骤,就可以在FreeRTOS中使用定时器中断了。每当定时器中断发生时,FreeRTOS的系统时间计数器就会增加1,从而实现任务调度和延时等功能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值