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¸öʱÖÓ½ÚÅÄ
}
}
}
总结
软件定时器分类周期定时器和单次定时器,会创建、开启、复位、关闭相关函数的使用。
继续加油!