FreeRTOS学习之任务通知

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”窗口中
如下图
FreeRTOS学习之任务通知_第1张图片
4. 单击”Run”按钮运行
得到如下图
FreeRTOS学习之任务通知_第2张图片
仔细观察图片中蓝色虚线所表示的位置,当send_val的值为0的时候,rec_val的值为1,结合程序源码,应该不难理解任务通知的功能。
前后对比,感受一下任务通知的功能及使用方法。

7. 总结

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

* *
xTaskNotifyGive() ulTaskNotifyTake()

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

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

你可能感兴趣的:(FreeRTOS学习过程)