freertos——任务通知知识总结与任务通知模拟及信号量实验、消息邮箱实验、事件标志组实验

1.任务通知概念

任务通知:用来通知任务的,任务控制块中的结构体成员变量 ulNotifiedValue就是这个通知值,不需要另外创建一个结构体可以直接接受别人发过来的通知
freertos——任务通知知识总结与任务通知模拟及信号量实验、消息邮箱实验、事件标志组实验_第1张图片

2.任务通知的优势及劣势

任务通知的优势:
效率更高 :使用任务通知向任务发送事件或数据比使用队列、事件标志组或信号量快得多
使用内存更小:使用其他方法时都要先创建对应的结构体,使用任务通知时无需额外创建结构体

任务通知的劣势:
无法发送数据给ISR:ISR没有任务结构体,所以无法给ISR发送数据。但是ISR可以使用任务通知的功能,发数据给任务。
无法广播给多个任务:任务通知只能是被指定的一个任务接收并处理
无法缓存多个数据:任务通知是通过更新任务通知值来发送数据的,任务结构体中只有一个任务通知值,只能保持一个数据。
发送受阻不支持阻塞:发送方无法进入阻塞状态等待

3.任务结构体中任务通知值和通知状态

typedef  struct  tskTaskControlBlock 
{
	… …
    	#if ( configUSE_TASK_NOTIFICATIONS  ==  1 )
        	volatile  uint32_t    ulNotifiedValue [ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
        	volatile  uint8_t      ucNotifyState [ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
    	endif
	… …
} tskTCB;
#define  configTASK_NOTIFICATION_ARRAY_ENTRIES	1  	/* 定义任务通知数组的大小, 默认: 1

一个是 uint32_t 类型,用来表示通知值
一个是 uint8_t 类型,用来表示通知状态

4 任务通知状态

#define taskNOT_WAITING_NOTIFICATION ( ( uint8_t ) 0 ) /* 任务未等待通知 /
#define taskWAITING_NOTIFICATION ( ( uint8_t ) 1 ) /
任务在等待通知 /
#define taskNOTIFICATION_RECEIVED ( ( uint8_t ) 2 ) /
任务在等待接收 */
未等待通知:初始化状态
等待通知:接收方已经准备好了(调用了接收任务通知函数),等待发送方给个通知
等待接收:发送方已经发送出去(调用了发送任务通知函数),等待接收方接收

5.任务通知API函数

1)发送通知相关API函数
freertos——任务通知知识总结与任务通知模拟及信号量实验、消息邮箱实验、事件标志组实验_第2张图片
注意;发送通知API函数可以用于任务和中断服务函数中
BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify,
UBaseType_t uxIndexToNotify,
uint32_t ulValue,
eNotifyAction eAction,
uint32_t * pulPreviousNotificationValue)
freertos——任务通知知识总结与任务通知模拟及信号量实验、消息邮箱实验、事件标志组实验_第3张图片
2)接收任务通知相关API函数
freertos——任务通知知识总结与任务通知模拟及信号量实验、消息邮箱实验、事件标志组实验_第4张图片
1)#define ulTaskNotifyTake( xClearCountOnExit , xTicksToWait )
ulTaskGenericNotifyTake( ( tskDEFAULT_INDEX_TO_NOTIFY ),
( xClearCountOnExit ),
\ ( xTicksToWait ) )
形参: uxIndexToWaitOn 任务的指定通知(任务通知相关数组成员)
xClearCountOnExit 指定在成功接收通知后,将通知值清零或减 1,
pdTRUE:把通知值清零;
pdFALSE:把通知值减一
xTicksToWait 阻塞等待任务通知值的最大时间

返回值:0 接收失败
其他 接收成功,返回任务通知的通知值

2)#define xTaskNotifyWait( ulBitsToClearOnEntry, \
ulBitsToClearOnExit, \
pulNotificationValue, \
xTicksToWait)
xTaskGenericNotifyWait( tskDEFAULT_INDEX_TO_NOTIFY,
\ ( ulBitsToClearOnEntry ),
\ ( ulBitsToClearOnExit ),
\ ( pulNotificationValue ),
\ ( xTicksToWait ) )

形参: uxIndexToWaitOn 任务的指定通知(任务通知相关数组成员)
ulBitesToClearOnEntry 等待前清零指定任务通知值的比特位(旧值对应bit清0)
ulBitsToClearOnExit 成功等待后清零指定的任务通知值比特位(新值对应bit清0)
pulNotificationValue 用来取出通知值(如果不需要取出,可设为NULL)
xTicksToWait 阻塞等待任务通知值的最大时间

返回值:0 接收失败
其他 接收成功,返回任务通知的通知值

总结:
1)当任务通知用作于信号量时,使用函数获取信号量:ulTaskNotifyTake()
2)当任务通知用作于事件标志组或队列时,使用此函数来获取: xTaskNotifyWait()

6.任务通知的几种方式

typedef enum
{
eNoAction = 0, /* 无操作 /
eSetBits /
更新指定bit /
eIncrement /
通知值加一 /
eSetValueWithOverwrite /
覆写的方式更新通知值 /
eSetValueWithoutOverwrite /
不覆写通知值 */
} eNotifyAction;

7.任务通知模拟信号量实验

freertos——任务通知知识总结与任务通知模拟及信号量实验、消息邮箱实验、事件标志组实验_第5张图片
代码:

#include "freertos_demo.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"

/*1.FreeRTOS配置*/

/* 1.1 START_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define START_TASK_PRIO 1                   /* 任务优先级 */
#define START_STK_SIZE  128                 /* 任务堆栈大小 */
TaskHandle_t            StartTask_Handler;  /* 任务句柄 */
void start_task(void *pvParameters);        /* 任务函数 */

/* 1.2 TASK1 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK1_PRIO      2                   /* 任务优先级 */
#define TASK1_STK_SIZE  128                 /* 任务堆栈大小 */
TaskHandle_t            Task1Task_Handler;  /* 任务句柄 */
void task1(void *pvParameters);             /* 任务函数 */

/* 1.3 TASK2 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK2_PRIO      3                   /* 任务优先级 */
#define TASK2_STK_SIZE  128                 /* 任务堆栈大小 */
TaskHandle_t            Task2Task_Handler;  /* 任务句柄 */
void task2(void *pvParameters);             /* 任务函数 */

/**
 * 2 FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */
void freertos_demo(void)
{
    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();
}

/**
 * 3 start_task
 * @param       pvParameters : 传入参数(未用到)
 * @retval      无
 */
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           /* 进入临界区 */
    /* 创建任务1 */
    xTaskCreate((TaskFunction_t )task1,
                (const char*    )"task1",
                (uint16_t       )TASK1_STK_SIZE,
                (void*          )NULL,
                (UBaseType_t    )TASK1_PRIO,
                (TaskHandle_t*  )&Task1Task_Handler);
    /* 创建任务2 */
    xTaskCreate((TaskFunction_t )task2,
                (const char*    )"task2",
                (uint16_t       )TASK2_STK_SIZE,
                (void*          )NULL,
                (UBaseType_t    )TASK2_PRIO,
                (TaskHandle_t*  )&Task2Task_Handler);
    vTaskDelete(StartTask_Handler); /* 删除开始任务 */
    taskEXIT_CRITICAL();            /* 退出临界区 */
}

/**
 * 3 task1发送任务通知值
 * @param       pvParameters : 传入参数(未用到)
 * @retval      无
 */
void task1(void *pvParameters)
{
    uint8_t key = 0;
    
    while (1)
    {
       
            key = key_scan(0);

            switch (key)
            {
                case KEY0_PRES:                                         /* 发送任务通知 */
                {
                    xTaskNotifyGive(Task2Task_Handler);
                    printf("任务通知发送,模拟二值信号量释放!\r\n");
                    break;
                }
                default:
                {
                    break;
                }
        }

        vTaskDelay(10);
    }
}

/**
 * 3 task2 接收任务通知值
 * @param       pvParameters : 传入参数(未用到)
 * @retval      无
 */
void task2(void *pvParameters)
{
    uint32_t rev = 0;
    while (1)
    {
        rev = ulTaskNotifyTake(pdTRUE,portMAX_DELAY);
        if(rev != 0)
        {
            printf("接收任务通知成功,模拟获取二值信号量!\r\n");
        }
    }
}

8.任务通知模拟计数型信号量实验

void task2(void *pvParameters)
{
    uint32_t rev = 0;
    while (1)
    {
        rev = ulTaskNotifyTake(pdTRUE,portMAX_DELAY);
        if(rev != 0)
        {
            printf("rev:%d\r\n",rev);
        }
        vTaskDelay(1000);
    }
}

9.任务通知模拟事件标志组实验

freertos——任务通知知识总结与任务通知模拟及信号量实验、消息邮箱实验、事件标志组实验_第6张图片

#include "freertos_demo.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"

/******************************************************************************************************/
/*1.FreeRTOS配置*/

/* 1.1 START_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define START_TASK_PRIO 1                   /* 任务优先级 */
#define START_STK_SIZE  128                 /* 任务堆栈大小 */
TaskHandle_t            StartTask_Handler;  /* 任务句柄 */
void start_task(void *pvParameters);        /* 任务函数 */

/* 1.2 TASK1 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK1_PRIO      2                   /* 任务优先级 */
#define TASK1_STK_SIZE  128                 /* 任务堆栈大小 */
TaskHandle_t            Task1Task_Handler;  /* 任务句柄 */
void task1(void *pvParameters);             /* 任务函数 */

/* 1.3 TASK2 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK2_PRIO      3                   /* 任务优先级 */
#define TASK2_STK_SIZE  128                 /* 任务堆栈大小 */
TaskHandle_t            Task2Task_Handler;  /* 任务句柄 */
void task2(void *pvParameters);             /* 任务函数 */

//1.4 位0、位1宏定义
#define EVENTBIT_0 (1 << 0)
#define EVENTBIT_1 (1 << 1)



/**
 * 2 FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */
void freertos_demo(void)
{
    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();
}

/**
 * 3 start_task
 * @param       pvParameters : 传入参数(未用到)
 * @retval      无
 */
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           /* 进入临界区 */
    /* 创建任务1 */
    xTaskCreate((TaskFunction_t )task1,
                (const char*    )"task1",
                (uint16_t       )TASK1_STK_SIZE,
                (void*          )NULL,
                (UBaseType_t    )TASK1_PRIO,
                (TaskHandle_t*  )&Task1Task_Handler);
    /* 创建任务2 */
    xTaskCreate((TaskFunction_t )task2,
                (const char*    )"task2",
                (uint16_t       )TASK2_STK_SIZE,
                (void*          )NULL,
                (UBaseType_t    )TASK2_PRIO,
                (TaskHandle_t*  )&Task2Task_Handler);
    vTaskDelete(StartTask_Handler); /* 删除开始任务 */
    taskEXIT_CRITICAL();            /* 退出临界区 */
}

/**
 * 3 task1发送任务通知值
 * @param       pvParameters : 传入参数(未用到)
 * @retval      无
 */
void task1(void *pvParameters)
{
    uint8_t key = 0;
    
    while (1)
    {
       
            key = key_scan(0);

            switch (key)
            {
                case KEY0_PRES:                                         /* 发送任务通知 */
                {
                    printf("将bit0位置置1\r\n");
                    xTaskNotify( Task2Task_Handler,EVENTBIT_0, eSetBits);
                    break;
                }
                 case KEY1_PRES:                                         /* 发送任务通知 */
                {
                     printf("将bit1位置置1\r\n");
                    xTaskNotify( Task2Task_Handler,EVENTBIT_1, eSetBits);
                   
                    break;
                }
                default:
                {
                    break;
                }
            
        }
        
        vTaskDelay(10);
    }
}

/**
 * 3 task2 接收任务通知值
 * @param       pvParameters : 传入参数(未用到)
 * @retval      无
 */
void task2(void *pvParameters)
{
    uint32_t notify_val = 0,event_bit=0;
    while (1)
    {
       xTaskNotifyWait( 0, 0xFFFFFFFF,&notify_val,portMAX_DELAY);
       if(notify_val & EVENTBIT_0)
       {
          event_bit |= EVENTBIT_0;
       } 
       if(notify_val & EVENTBIT_1)
       {
          event_bit |= EVENTBIT_1;
       } 
         if(event_bit ==( EVENTBIT_0 | EVENTBIT_1))
       {
          printf("任务通知模拟事件标志组接收成功\r\n");
          event_bit = 0;
       } 

    }
}

10.任务通知模拟消息邮箱实验

freertos——任务通知知识总结与任务通知模拟及信号量实验、消息邮箱实验、事件标志组实验_第7张图片


#include "freertos_demo.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
 #include "./BSP/LED/led.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"

/******************************************************************************************************/
/*1.FreeRTOS配置*/

/* 1.1 START_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define START_TASK_PRIO 1                   /* 任务优先级 */
#define START_STK_SIZE  128                 /* 任务堆栈大小 */
TaskHandle_t            StartTask_Handler;  /* 任务句柄 */
void start_task(void *pvParameters);        /* 任务函数 */

/* 1.2 TASK1 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK1_PRIO      2                   /* 任务优先级 */
#define TASK1_STK_SIZE  128                 /* 任务堆栈大小 */
TaskHandle_t            Task1Task_Handler;  /* 任务句柄 */
void task1(void *pvParameters);             /* 任务函数 */

/* 1.3 TASK2 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define TASK2_PRIO      3                   /* 任务优先级 */
#define TASK2_STK_SIZE  128                 /* 任务堆栈大小 */
TaskHandle_t            Task2Task_Handler;  /* 任务句柄 */
void task2(void *pvParameters);             /* 任务函数 */

/******************************************************************************************************/



/**
 * 2 FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */
void freertos_demo(void)
{
    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();
}

/**
 * 3 start_task
 * @param       pvParameters : 传入参数(未用到)
 * @retval      无
 */
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           /* 进入临界区 */
    /* 创建任务1 */
    xTaskCreate((TaskFunction_t )task1,
                (const char*    )"task1",
                (uint16_t       )TASK1_STK_SIZE,
                (void*          )NULL,
                (UBaseType_t    )TASK1_PRIO,
                (TaskHandle_t*  )&Task1Task_Handler);
    /* 创建任务2 */
    xTaskCreate((TaskFunction_t )task2,
                (const char*    )"task2",
                (uint16_t       )TASK2_STK_SIZE,
                (void*          )NULL,
                (UBaseType_t    )TASK2_PRIO,
                (TaskHandle_t*  )&Task2Task_Handler);
    vTaskDelete(StartTask_Handler); /* 删除开始任务 */
    taskEXIT_CRITICAL();            /* 退出临界区 */
}

/**
 * 3 task1发送任务通知值
 * @param       pvParameters : 传入参数(未用到)
 * @retval      无
 */
void task1(void *pvParameters)
{
    uint8_t key = 0;
    
    while (1)
    {
       
        key = key_scan(0);
        if((key != 0) && (Task2Task_Handler != NULL))
        {
            printf("任务通知模拟消息邮箱发送,发送的键值:%d\r\n",key);
            xTaskNotify(Task2Task_Handler,key,eSetValueWithOverwrite);
        }
        vTaskDelay(10);
    }
}

/**
 * 3 task2 接收任务通知值
 * @param       pvParameters : 传入参数(未用到)
 * @retval      无
 */
void task2(void *pvParameters)
{
    uint32_t notify_val = 0;
    while (1)
    {
        xTaskNotifyWait(0,0xFFFFFFFF,&notify_val,portMAX_DELAY);
        switch(notify_val)
        {
            case KEY0_PRES:
                printf("接收到的通知为:%d\r\n",notify_val);
                LED0_TOGGLE();
                break;
             case KEY1_PRES:
                printf("接收到的通知为:%d\r\n",notify_val);
                LED1_TOGGLE();
                break;
             default : 
                break;
        }
    }
}

你可能感兴趣的:(单片机,嵌入式硬件,stm32)