任务通知
话不多说,直接上代码
任务通知的函数接口
发送通知
在 任务中发送任务通知的函数均是调用 xTaskGenericNotify()函数进行发送通知,xTaskGenericNotify()函数是一个通用的任务通知发送函数,在任务中发送通知的 API 函 数 , 如 xTaskNotifyGive() 、 xTaskNotify() ,xTaskNotifyAndQuery() , 都 是 以 xTaskGenericNotify()为原型的,只不过指定的发送方式不同而已。
#define xTaskNotify( xTaskToNotify, ulValue, eAction ) xTaskGenericNotify( ( xTaskToNotify ), ( ulValue ), ( eAction ), NULL )
#define xTaskNotifyAndQuery( xTaskToNotify, ulValue, eAction, pulPreviousNotifyValue ) xTaskGenericNotify( ( xTaskToNotify ), ( ulValue ), ( eAction ), ( pulPreviousNotifyValue ) )
#define xTaskNotifyGive( xTaskToNotify ) xTaskGenericNotify( ( xTaskToNotify ), ( 0 ), eIncrement, NULL )
所以,我们其实只需要搞懂
BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue )
只需要搞懂 xTaskGenericNotify
其中,重要的是通知方式
2.接受通知
BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait )
uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait )
ulTaskNotifyTake
返回值为0,表示接受失败。
返回值不为0,表示接受成功。
xTaskNotifyWait
返回值为pdTRUE,表示等待任务通知成功
返回值为pdFALSE,表示等待任务通知失败
示例:
模拟二值信号
void task1(void * pvParameters)
{
/*
中间省略
*/
while(1)
{
key=key_scan(0);
if(key == KEY0_PRES)
{
printf("任务通知模拟二值信号释放!\r\n");
xTaskNotifyGive(task2_hangler);
}
vTaskDelay(10);
}
}
void task2(void * pvParameters)
{
uint32_t rev=0;
while(1)
{
rev=ulTaskNotifyTake(pdTRUE,portMAX_DELAY);
if(rev != 0)
{
printf("接受任务通知成功,模拟获取二值型号量!\r\n");
}
}
}
模拟计数型信号
void task1(void * pvParameters)
{
/*
中间省略
*/
while(1)
{
key=key_scan(0);
if(key == KEY0_PRES)
{
printf("任务通知模拟计数型信号释放!\r\n");
xTaskNotifyGive(task2_hangler);
}
vTaskDelay(10);
}
}
void task2(void * pvParameters)
{
uint32_t rev=0;
while(1)
{
rev=ulTaskNotifyTake(pdFALSE,portMAX_DELAY);
if(rev != 0)
{
printf("rev:%d\r\n",rev);
printf("接受任务通知成功,模拟获取计数型信号量!\r\n");
}
}
}
模拟消息邮箱信号
void task1(void * pvParameters)
{
/*
中间省略
*/
while(1)
{
key=key_scan(0);
if((key !=0)&&(task2_handler != NULL))
{
printf("任务通知模拟信息邮箱发送,发送的键值为:%d\r\n");
xTaskNotify(task2_hangler,key,eSetValueWithOverwrite);
}
vTaskDelay(10);
}
}
void task2(void * pvParameters)
{
uint32_t noyify_val=0;
while(1)
{
xTaskNotifyWait(0,0xFFFFFFFF,&noyify_val,portMAX_DELAY);
switch(noyify_val)
{
case xx:
{
//省略
break;
}
case xx:
{
//省略
break;
}
}
}
}
模拟事件组
#define EVENTBIT_0 (1<<0)
#define EVENTBIT_1 (1<<1)
void task1(void * pvParameters)
{
/*
中间省略
*/
while(1)
{
key=key_scan(0);
if(key == xx )
{
printf("将bit 0位置1\r\n");
xTaskNotify(task2_hangler,EVENTBIT_0,eSetBits);
}
esle if(key == xx )
{
printf("将bit 1位置1\r\n");
xTaskNotify(task2_hangler,EVENTBIT_1,eSetBits);
}
vTaskDelay(10);
}
}
//当biy0,和bit 1,都被置1才触发
//接受函数,退出后一定要清零。
void task2(void * pvParameters)
{
uint32_t noyify_val=0,event_bit = 0;
while(1)
{
xTaskNotifyWait(0,0xFFFFFFFF,&noyify_val,portMAX_DELAY);
if(noyify_val & EVENTBIT_0)
{
event_bit |=EVENTBIT_0;
}
if(noyify_val & EVENTBIT_1)
{
event_bit |=EVENTBIT_1;
}
if(event_bit == (EVENTBIT_0 | EVENTBIT_1))
{
printf("任务通知模拟事件组接受成功!!\r\n");
event_bit=0;
}
}
}
软定时
创建
动态分配内存
/* 使用动态分配内存的方法创建定时器
* pcTimerName:定时器名字, 用处不大, 尽在调试时用到
* xTimerPeriodInTicks: 周期, 以 Tick 为单位
* uxAutoReload: 类型, pdTRUE 表示自动加载, pdFALSE表示一次性
* pvTimerID: 回调函数可以使用此参数, 比如分辨是哪个定时器
* pxCallbackFunction: 回调函数
* 返回值: 成功则返回TimerHandle_t, 否则返回NULL
*/
TimerHandle_t xTimerCreate( const char * const pcTimerName,
const TickType_t xTimerPeriodInTicks,
const UBaseType_t uxAutoReload,
void * const pvTimerID,
TimerCallbackFunction_t pxCallbackFunction );
静态分配内存
/* 使用静态分配内存的方法创建定时器
* pcTimerName:定时器名字, 用处不大, 尽在调试时用到
* xTimerPeriodInTicks: 周期, 以Tick为单位
* uxAutoReload: 类型, pdTRUE表示自动加载, pdFALSE表示一次性
* pvTimerID: 回调函数可以使用此参数, 比如分辨是哪个定时器
* pxCallbackFunction: 回调函数
* pxTimerBuffer: 传入一个StaticTimer_t结构体, 将在上面构造定时器
* 返回值: 成功则返回TimerHandle_t, 否则返回NULL
*/
TimerHandle_t xTimerCreateStatic(const char * const pcTimerName,
TickType_t xTimerPeriodInTicks,
UBaseType_t uxAutoReload,
void * pvTimerID,
TimerCallbackFunction_t pxCallbackFunction,
StaticTimer_t *pxTimerBuffer );
回调函数
回调函数的类型是:
void ATimerCallback( TimerHandle_t xTimer );
typedef void (* TimerCallbackFunction_t)( TimerHandle_t xTimer );
删除
/* 删除定时器
* xTimer: 要删除哪个定时器
* xTicksToWait: 超时时间
* 返回值: pdFAIL表示"删除命令"在xTicksToWait个Tick内无法写入队列
* pdPASS表示成功
*/
BaseType_t xTimerDelete( TimerHandle_t xTimer, TickType_t xTicksToWait );
启动
/* 启动定时器
* xTimer: 哪个定时器
* xTicksToWait: 超时时间
* 返回值: pdFAIL表示"启动命令"在xTicksToWait个Tick内无法写入队列
* pdPASS表示成功
*/
BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait );
/* 启动定时器(ISR版本)
* xTimer: 哪个定时器
* pxHigherPriorityTaskWoken: 向队列发出命令使得守护任务被唤醒,
* 如果守护任务的优先级比当前任务的高,
* 则"*pxHigherPriorityTaskWoken = pdTRUE",
* 表示需要进行任务调度
* 返回值: pdFAIL表示"启动命令"无法写入队列
* pdPASS表示成功
*/
BaseType_t xTimerStartFromISR( TimerHandle_t xTimer,
BaseType_t *pxHigherPriorityTaskWoken );
停止
/* 停止定时器
* xTimer: 哪个定时器
* xTicksToWait: 超时时间
* 返回值: pdFAIL表示"停止命令"在xTicksToWait个Tick内无法写入队列
* pdPASS表示成功
*/
BaseType_t xTimerStop( TimerHandle_t xTimer, TickType_t xTicksToWait );
/* 停止定时器(ISR版本)
* xTimer: 哪个定时器
* pxHigherPriorityTaskWoken: 向队列发出命令使得守护任务被唤醒,
* 如果守护任务的优先级比当前任务的高,
* 则"*pxHigherPriorityTaskWoken = pdTRUE",
* 表示需要进行任务调度
* 返回值: pdFAIL表示"停止命令"无法写入队列
* pdPASS表示成功
*/
BaseType_t xTimerStopFromISR( TimerHandle_t xTimer,
BaseType_t *pxHigherPriorityTaskWoken );
注意,这些函数的xTicksToWait表示的是,把命令写入命令队列的超时时间。命令队 列可能已经满了,无法马上把命令写入队列里,可以等待一会。
xTicksToWait不是定时器本身的超时时间,不是定时器本身的"周期"。
创建定时器时,设置了它的周期(period)。xTimerStart()函数是用来启动定时器。
假设 调用xTimerStart()的时刻是tX,定时器的周期是n,那么在tX+n时刻定时器的回调函数被 调用。
如果定时器已经被启动,但是它的函数尚未被执行,再次执行xTimerStart()函数相当于 执行xTimerReset(),重新设定它的启动时间。
/* 修改定时器的周期
* xTimer: 哪个定时器
* xNewPeriod: 新周期
* xTicksToWait: 超时时间, 命令写入队列的超时时间
* 返回值: pdFAIL表示"修改周期命令"在xTicksToWait个Tick内无法写入队列
* pdPASS表示成功
*/
BaseType_t xTimerChangePeriod( TimerHandle_t xTimer,
TickType_t xNewPeriod,
TickType_t xTicksToWait );
/* 修改定时器的周期
* xTimer: 哪个定时器
* xNewPeriod: 新周期
* pxHigherPriorityTaskWoken: 向队列发出命令使得守护任务被唤醒,
* 如果守护任务的优先级比当前任务的高,
* 则"*pxHigherPriorityTaskWoken = pdTRUE",
* 表示需要进行任务调度
* 返回值: pdFAIL表示"修改周期命令"在xTicksToWait个Tick内无法写入队列
* pdPASS表示成功
*/
BaseType_t xTimerChangePeriodFromISR( TimerHandle_t xTimer,
TickType_t xNewPeriod,
BaseType_t *pxHigherPriorityTaskWoken );
修改周期
/* 复位定时器
* xTimer: 哪个定时器
* xTicksToWait: 超时时间
* 返回值: pdFAIL表示"复位命令"在xTicksToWait个Tick内无法写入队列
* pdPASS表示成功
*/
BaseType_t xTimerReset( TimerHandle_t xTimer, TickType_t xTicksToWait );
/* 复位定时器(ISR版本)
* xTimer: 哪个定时器
* pxHigherPriorityTaskWoken: 向队列发出命令使得守护任务被唤醒,
* 如果守护任务的优先级比当前任务的高,
* 则"*pxHigherPriorityTaskWoken = pdTRUE",
* 表示需要进行任务调度
* 返回值: pdFAIL表示"停止命令"无法写入队列
* pdPASS表示成功
*/
BaseType_t xTimerResetFromISR( TimerHandle_t xTimer,
BaseType_t *pxHigherPriorityTaskWoken );
配置文件FreeRTOSConfig.h中
##define configUSE_TIMERS 1 /* 使能定时器 */
##define configTIMER_TASK_PRIORITY 31 /* 守护任务的优先级, 尽可能高一些 */
##define configTIMER_QUEUE_LENGTH 5 /* 命令队列长度 */
##define configTIMER_TASK_STACK_DEPTH 32 /* 守护任务的栈大小 */
内存管理
申请内存
void *pvPortMalloc( size_t xWantedSize )
释放内存
void vPortFree( void *pv )
获得当前空闲内存的大小
size_t xPortGetFreeHeapSize( void )