FreeRTOS学习之任务通知

来源:互联网 发布:淘宝店铺怎么换客服 编辑:程序博客网 时间:2024/05/17 01:17

1. 作用

任务通知功能是干什么用的呢?举个例子,假设一种场景,当按下按键时,LED灯会亮。比较简单的实现代码如下:

void func(void){    if(pressed)        led on;}

在基于时间片任务调度下,基于模块化的考虑,或者基于可读性的考虑,也可以按照如下代码实现

unsigned char button_state;void led(void){    if (button_state == PRESSED)      led on;}void button(void){    while (button_gpio == PRESSED)       button_state = PRESSED;  }void func(void){    while(1)    {        button();        led();    }}

但是在有操作系统的情况下,每个任务都是一个死循环,那么这两个函数之间该如何通讯呢?这就是任务通知干的活。当然队列、信号量等都可以使用,这儿我们只讨论用任务通知如何实现该功能.

2. 任务通知的优点:

用事件通知解除阻塞任务会比二值信号量快45%并且使用更少的RAM空间。

3. 性能优势和使用限制

在实现相同功能的情况下,任务通知具有速度和RAM空间的双重优势。但是这种优势也有一定的限制:
- 只有唯一的任务可以接受通知的情况下,才可以使用。这个条件已经可以满足大部分的应用了。
- 只有以下情况,任务通知才可以替代队列:当有一个处在阻塞状态下的任务等待通知时,那个发送通知的任务如果不能马上完成发送,必须不能进入阻塞状态等待发送完成。

4. 裸机实例

说了这么多,到底是干嘛用的,估计还是不清楚,简短的例子是最好的老师,举个例子。看下面一段代码

我们先用裸机代码实现如下功能,功能很简单:

static void RecFunc(void);static void SendFunc(void);unsigned int send_val;unsigned int rec_val;static void SendFunc(void){      for(;;)    {        delay_5ms();        if (send_val == 5)        {            send_val = 0;        }        else         {            send_val++;        }    }}static void RecFunc(void){    for(;;)    {      rec_val = 0;      /*wait here until send_val == 5*/      while(send_val != 5);      rec_val = 1;      delay_5ms();    }}  void main(void){    SendFunc();    RecFunc();}

5. 带操作系统实例

再用带操作系统的代码实现以上功能,乍看一下,代码很长,去掉注释,仔细分析一下,核心代码就两三行,耐心看完:

/**  ******************************************************************************  * @file    test_task_notification.c   * @author  cyf  * @version V1.0  * @date    2016-7-29 10:19:12  * @brief   学习FreeRTOS的任务通知功能使用方法  *//*  *这个例子没有实际的使用意义,仅仅是为了演示任务通知功能的使用 * *在函数SendFunc()中,send_val 的值会每间隔5ms自增1,当send_val=5时,会通知RecFunc(),条件已满足, *你可以执行操作了,通知完后,会清零send_val. * *函数Recfunc()里的rec_val值会一直为0,只有在接收到任务通知时,会使rec_val=1,设置为1后,马上就会变为0,等待下一次任务通知 * */#include "FreeRTOS.h"#include "task.h"static void RecFunc(void *pvParameters);static void SendFunc(void *pvParameters);/*TN is short for Task Notification*/#define TN_STACK_SIZE       configMINIMAL_STACK_SIZE#define TN_PRIORITY             ( tskIDLE_PRIORITY + 2 )unsigned int send_val;unsigned int rec_val;static TaskHandle_t xRecTask = NULL;/* * 任务通知发送函数 */static void SendFunc(void *pvParameters){    TickType_t xDelay = 5/portTICK_PERIOD_MS;    for(;;)    {        vTaskDelay(xDelay);        if (send_val == 5)        {            /*Send a notificaton to RecFunc(),bringing it out of the Blocked state*/            xTaskNotifyGive(xRecTask);            send_val = 0;        }        else         {            send_val++;        }    }}/* * 任务通知接收函数 */static void RecFunc(void *pvParameters){    TickType_t xDelay = 5/portTICK_PERIOD_MS;    for(;;)    {      rec_val = 0;      /*Block to wait for SendFunc() to notify this task*/        ulTaskNotifyTake(pdTRUE,portMAX_DELAY);      rec_val = 1;      vTaskDelay(xDelay);      }}/* * 生产两个任务实例,本函数在main函数中被调用 * 函数SendFunc设置条件 * 函数RecFunc接收条件,当满足条件后,执行操作 * */void vCreateTaskNotification(void){    xTaskCreate(SendFunc,"Send",TN_STACK_SIZE,NULL,TN_PRIORITY,NULL);    xTaskCreate(RecFunc,"Send",TN_STACK_SIZE,NULL,TN_PRIORITY,&xRecTask);}/***************************************** END OF FILE ********************************************************/

6. 调试分析

编译后,我们进入keil的调试功能:
1. 调出“Logic Analyzer”窗口
2. 调出“Symbol Window”的串口
3. 把变量send_val、rec_val加入到Logic Analyzer”窗口中
如下图
这里写图片描述
4. 单击”Run”按钮运行
得到如下图
这里写图片描述
仔细观察图片中蓝色虚线所表示的位置,当send_val的值为0的时候,rec_val的值为1,结合程序源码,应该不难理解任务通知的功能。
前后对比,感受一下任务通知的功能及使用方法。

7. 总结

任务通知功能主要使用了两个函数来实现,一个是xTaskNotifyGive(),一个是ulTaskNotifyTake(),

* * xTaskNotifyGive() ulTaskNotifyTake() 给 拿

我给你,你才能拿,拿到了,才能去干活, 拿不到就等着

当然,这里只是举例两个简单的函数,还有一下其他的函数及参数,具体的可以去看一下官网的介绍,解释的很详细。

0 0