从 V8.2.0 版本开始,FreeRTOS 新增了任务通知这个功能,可以使用任务通 知来代替信号量、消息队列、事件组等这些东西。使用任务通知的话效率会更高。 本章要实现的功能是:使用任务通知方式实现消息队列、二值信号量、计数信号 量、事件标记功能实验。本章分为如下几部分内容: 11.1 任务通知简介 11.2 常用任务通知 API 函数 11.3 硬件设计 11.4 软件设计 11.5 实验现象
11.1 任务通知简介
FreeRTOS 从 V8.2.0 版本开始提供任务通知这个功能,每个任务都有一个 32 位的通知值,在大多数情况下,任务通知可以替代二值信号量、计数信号量、 事件组,也可以替代长度为 1 的队列(可以保存一个 32 位整数或指针值)。 相对于以前使用 FreeRTOS 内核通信的资源,必须创建队列、二进制信号量、 计数信号量或事件组的情况,使用任务通知显然更灵活。按照 FreeRTOS 官方的 说法,使用任务通知比通过信号量等 IPC 通信方式解除阻塞的任务要快 45%, 并且更加省 RAM 内存空间(使用 GCC 编译器,-o2 优化级别),任务通知的使 用无需创建队列。想要使用任务通知,必须将 FreeRTOSConfig.h 中的宏定义 configUSE_TASK_NOTIFICATIONS 设置为 1,其实 FreeRTOS 默认是为 1 的,所 以任务通知是默认使能的。 FreeRTOS 提供以下几种方式发送通知给任务 : ●发送通知给任务,如果有通知未读,不覆盖通知值。 ●发送通知给任务,直接覆盖通知值。 ●发送通知给任务,设置通知值的一个或者多个位,可以当做事件组来使用。 ●发送通知给任务,递增通知值,可以当做计数信号量使用。 通过对以上任务通知方式的合理使用,可以在一定场合下替代 FreeRTOS 的 信号量,队列、事件组等。 当然,凡是都有利弊,不然的话 FreeRTOS 还要内核的 IPC 通信机制干嘛, 消息通知虽然处理更快,RAM 开销更小,但也有以下限制 : ●只能有一个任务接收通知消息,因为必须指定接收通知的任务。 ●只有等待通知的任务可以被阻塞,发送通知的任务,在任何情况下都不会 因为发送失败而进入阻塞态
11.2 常用任务通知 API 函 数
11.2.1.1 xTaskNotifyGive()
xTaskNotifyGive()是一个宏,宏展开是调用函数 xTaskGenericNotify( ( xTaskToNotify ), ( 0 ), eIncrement ,NULL),即向 一个任务发送通知,并将对方的任务通知值加 1。该函数可以作为二值信号量和 计数信号量的一种轻量型的实现,速度更快,在这种情况下对象任务在等待任务 通知的时候应该是使用函数 ulTaskNotifyTake() 而不是 xTaskNotifyWait()。 xTaskNotifyGive()不能在中断里面使用,而是使用具有中断保护功能的 vTaskNotifyGiveFromISR()来代替。该函数的具体说明如下
11.2.1.2 vTaskNotifyGiveFromISR()
vTaskNotifyGiveFromISR()是 vTaskNotifyGive()的中断保护版本。用于在 中断中向指定任务发送任务通知,并更新对方的任务通知值(加 1 操作),在 某些场景中可以替代信号量操作,因为这两个通知都是不带有通知值的。该函数 的具体说明如下
从上面的函数说明我们大概知道 vTaskNotifyGiveFromISR()函数作用,每 次调用该函数都会增加任务的通知值,任务通过接收函数返回值是否大于零,判 断是否获取到了通知,任务通知值初始化为 0,(如果与信号量做对比)则对应 为信号量无效。当中断调用 vTaskNotifyGiveFromISR()通知函数给任务的时候, 任务的通知值增加,使其大于零,使其表示的通知值变为有效,任务获取有效的 通知值将会被恢复
11.2.1.3 xTaskNotify()
FreeRTOS 每个任务都有一个 32 位的变量用于实现任务通知,在任务创建 的时候初始化为 0。这个 32 位的通知值在任务控制块 TCB 里面定义。 xTaskNotify()用于在任务中直接向另外一个任务发送一个事件,接收到该任务 通知的任务有可能解锁。如果你想使用任务通知来实现二值信号量和计数信号 量,那么应该使用更加简单的函数 xTaskNotifyGive(),而不是使用 xTaskNotify(),xTaskNotify()函数在发送任务通知的时候会指定一个通知值, 并且用户可以指定通知值发送的方式。 注意:该函数不能在中断里面使用,而是使用具体中断保护功能的版本函数 xTaskNotifyFromISR()。xTaskNotify()函数的具体说明如下
11.2.1.4 xTaskNotifyFromISR( )
用于在中断中向指定的任务发送一个任务通知,该任务通知是带有通知值并 且用户可以指定通知的发送方式,不返回上一个任务在的通知值。函数的具体说 明如下
11.2.2.1 xTaskNotifyAndQuery()
xTaskNotifyAndQuery()与 xTaskNotify()很像,都是调用通用的任务通知 发送函数 xTaskGenericNotify() 来实现通知的发送,不同的是多了一个附加的 参数 pulPreviousNotifyValue 用于回传接收任务的上一个通知值,函数原型具 体见代码
11.2.2.2 xTaskNotifyAndQueryFromISR()
xTaskNotifyAndQueryFromISR()是 xTaskNotifyAndQuery()的中断版本,用 于向指定的任务发送一个任务通知,并返回对象任务的上一个通知值,该函数也 是一个宏定义,真正实现发送通知的是 xTaskGenericNotifyFromISR()。 xTaskNotifyAndQueryFromISR()函数说明如下。
11.2.3 获取任务通知函数 既然 FreeRTOS 中发送任务的函数有那么多个,那么任务怎么获取到通知 呢?我们说了,任务通知在某些场景可以替代信号量、消息队列、事件等。获取 任务通知函数只能用在任务中,没有带中断保护版本,因此只有两个 API 函数: ulTaskNotifyTake()和 xTaskNotifyWait()。前者是为代替二值信号量和计数信 号量而专门设计的,它和发送通知 API 函数 xTaskNotifyGive()、 vTaskNotifyGiveFromISR()配合使用;后者是全功能版的等待通知,可以根据不 同的参数实现轻量级二值信号量、计数信号量、事件组和长度为 1 的队列。 所有的获取任务通知 API 函数都带有指定阻塞超时时间参数,当任务因为 等待通知而进入阻塞时,用来指定任务的阻塞时间,这些超时机制与 FreeRTOS 的消息队列、信号量、事件等的超时机制一致
11.2.3.1 ulTaskNotifyTake()
ulTaskNotifyTake()作为二值信号量和计数信号量的一种轻量级实现,速度 更快。如果 FreeRTOS 中使用函数 xSemaphoreTake() 来获取信号量,这个时候 则可以试试使用函数 ulTaskNotifyTake()来代替。 对于这个函数,任务通知值为 0,对应信号量无效,如果任务设置了阻塞等 待,任务被阻塞挂起。当其他任务或中断发送了通知值使其不为 0 后,通知变 为有效,等待通知的任务将获取到通知,并且在退出时候根据用户传递的第一个 参数 xClearCountOnExit 选择清零通知值或者执行减一操作。 xTaskNotifyTake()在退出的时候处理任务的通知值的时候有两种方法,一 种是在函数退出时将通知值清零,这种方法适用于实现二值信号量;另外一种是 在函数退出时将通知值减 1,这种方法适用于实现计数信号量。当一个任务使用 其自身的任务通知值作为二值信号量或者计数信号量时,其他任务应该使用函数 xTaskNotifyGive()或者 xTaskNotify( ( xTaskToNotify ), ( 0 ),eIncrement ) 来向其发送信号量。如果是在中断中,则应该使用他们的中断版本函数。该函数 的具体说明如下
11.2.3.2 xTaskNotifyWait()
xTaskNotifyWait()函数用于实现全功能版的等待任务通知,根据用户指定 的参数的不同,可以灵活的用于实现轻量级的消息队列队列、二值信号量、计数 信号量和事件组功能,并带有超时等待。函数的具体说明如下。
11.3整体代码
1.任务通知代替消息队列
#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "key.h"
#include "limits.h"
//任务优先级
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_STK_SIZE 128
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);
//任务优先级
#define LED1_TASK_PRIO 2
//任务堆栈大小
#define LED1_STK_SIZE 50
//任务句柄
TaskHandle_t LED1Task_Handler;
//任务函数
void led1_task(void *pvParameters);
//任务优先级
#define Receive1_TASK_PRIO 3
//任务堆栈大小
#define Receive1_STK_SIZE 512
//任务句柄
TaskHandle_t Receive1Task_Handler;
//任务函数
void Receive1_task(void *pvParameters);
//任务优先级
#define Receive2_TASK_PRIO 4
//任务堆栈大小
#define Receive2_STK_SIZE 512
//任务句柄
TaskHandle_t Receive2Task_Handler;
//任务函数
void Receive2_task(void *pvParameters);
//任务优先级
#define Send_TASK_PRIO 5
//任务堆栈大小
#define Send_STK_SIZE 512
//任务句柄
TaskHandle_t SendTask_Handler;
//任务函数
void Send_task(void *pvParameters);
#define USE_CHAR 0 /* 测试字符串的时候配置为 1 ,测试变量配置为 0 */
/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
int main()
{
SysTick_Init(72);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
LED_Init();
KEY_Init();
USART1_Init(115200);
printf("FreeRTOS任务通知代替消息队列实验\r\n");
printf("按下KEY_UP或者KEY1进行任务消息通知发送 \n");
//创建开始任务
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(); //进入临界区
//创建LED1任务
xTaskCreate((TaskFunction_t )led1_task,
(const char* )"led1_task",
(uint16_t )LED1_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED1_TASK_PRIO,
(TaskHandle_t* )&LED1Task_Handler);
//创建Receive1任务
xTaskCreate((TaskFunction_t )Receive1_task,
(const char* )"Receive1_task",
(uint16_t )Receive1_STK_SIZE,
(void* )NULL,
(UBaseType_t )Receive1_TASK_PRIO,
(TaskHandle_t* )&Receive1Task_Handler);
//创建Receive2任务
xTaskCreate((TaskFunction_t )Receive2_task,
(const char* )"Receive2_task",
(uint16_t )Receive2_STK_SIZE,
(void* )NULL,
(UBaseType_t )Receive2_TASK_PRIO,
(TaskHandle_t* )&Receive2Task_Handler);
//创建Send任务
xTaskCreate((TaskFunction_t )Send_task,
(const char* )"Send_task",
(uint16_t )Send_STK_SIZE,
(void* )NULL,
(UBaseType_t )Send_TASK_PRIO,
(TaskHandle_t* )&SendTask_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
//LED1任务函数
void led1_task(void *pvParameters)
{
while(1)
{
LED1=0;
vTaskDelay(200);
LED1=1;
vTaskDelay(800);
}
}
//Receive1任务函数
void Receive1_task(void *pvParameters)
{
BaseType_t xReturn = pdTRUE;
#if USE_CHAR
char *r_char;
#else
uint32_t r_num;
#endif
while(1)
{
//获取任务通知 ,没获取到则一直等待
xReturn=xTaskNotifyWait(0x0, //进入函数的时候不清除任务bit
ULONG_MAX, //退出函数的时候清除所有的bit
#if USE_CHAR
(uint32_t *)&r_char, //保存任务通知值
#else
&r_num, //保存任务通知值
#endif
portMAX_DELAY); //阻塞时间
if( pdTRUE == xReturn )
#if USE_CHAR
printf("Receive1_Task 任务通知消息为 %s \n",r_char);
#else
printf("Receive1_Task 任务通知消息为 %d \n",r_num);
#endif
}
}
//Receive2任务函数
void Receive2_task(void *pvParameters)
{
BaseType_t xReturn = pdTRUE;
#if USE_CHAR
char *r_char;
#else
uint32_t r_num;
#endif
while(1)
{
//获取任务通知 ,没获取到则一直等待
xReturn=xTaskNotifyWait(0x0, //进入函数的时候不清除任务bit
ULONG_MAX, //退出函数的时候清除所有的bit
#if USE_CHAR
(uint32_t *)&r_char, //保存任务通知值
#else
&r_num, //保存任务通知值
#endif
portMAX_DELAY); //阻塞时间
if( pdTRUE == xReturn )
#if USE_CHAR
printf("Receive2_Task 任务通知消息为 %s \n",r_char);
#else
printf("Receive2_Task 任务通知消息为 %d \n",r_num);
#endif
}
}
//Send任务函数
void Send_task(void *pvParameters)
{
BaseType_t xReturn = pdPASS;
u8 key=0;
#if USE_CHAR
char test_str1[] = "this is a mail test 1";/* 邮箱消息test1 */
char test_str2[] = "this is a mail test 2";/* 邮箱消息test2 */
#else
uint32_t send1 = 1;
uint32_t send2 = 2;
#endif
while(1)
{
key=KEY_Scan(0);
if(key==KEY_UP_PRESS)
{
xReturn = xTaskNotify(Receive1Task_Handler, /*任务句柄*/
#if USE_CHAR
(uint32_t)&test_str1, /* 发送的数据,最大为4字节 */
#else
send1, /* 发送的数据,最大为4字节 */
#endif
eSetValueWithOverwrite );/*覆盖当前通知*/
if( xReturn == pdPASS )
printf("Receive1_Task_Handle 任务通知消息发送成功!\r\n");
}
else if(key==KEY1_PRESS)
{
xReturn = xTaskNotify(Receive2Task_Handler, /*任务句柄*/
#if USE_CHAR
(uint32_t)&test_str2, /* 发送的数据,最大为4字节 */
#else
send2, /* 发送的数据,最大为4字节 */
#endif
eSetValueWithOverwrite );/*覆盖当前通知*/
if( xReturn == pdPASS )
printf("Receive2_Task_Handle 任务通知消息发送成功!\r\n");
}
vTaskDelay(20);
}
}
2.任务通知代替二值信号量
#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "key.h"
//任务优先级
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_STK_SIZE 128
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);
//任务优先级
#define LED1_TASK_PRIO 2
//任务堆栈大小
#define LED1_STK_SIZE 50
//任务句柄
TaskHandle_t LED1Task_Handler;
//任务函数
void led1_task(void *pvParameters);
//任务优先级
#define Receive1_TASK_PRIO 3
//任务堆栈大小
#define Receive1_STK_SIZE 512
//任务句柄
TaskHandle_t Receive1Task_Handler;
//任务函数
void Receive1_task(void *pvParameters);
//任务优先级
#define Receive2_TASK_PRIO 4
//任务堆栈大小
#define Receive2_STK_SIZE 512
//任务句柄
TaskHandle_t Receive2Task_Handler;
//任务函数
void Receive2_task(void *pvParameters);
//任务优先级
#define Send_TASK_PRIO 5
//任务堆栈大小
#define Send_STK_SIZE 512
//任务句柄
TaskHandle_t SendTask_Handler;
//任务函数
void Send_task(void *pvParameters);
/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
int main()
{
SysTick_Init(72);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
LED_Init();
KEY_Init();
USART1_Init(115200);
printf("FreeRTOS任务通知代替二值信号量实验\r\n");
printf("按下KEY_UP或者KEY1进行任务与任务间的同步\n");
//创建开始任务
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(); //进入临界区
//创建LED1任务
xTaskCreate((TaskFunction_t )led1_task,
(const char* )"led1_task",
(uint16_t )LED1_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED1_TASK_PRIO,
(TaskHandle_t* )&LED1Task_Handler);
//创建Receive1任务
xTaskCreate((TaskFunction_t )Receive1_task,
(const char* )"Receive1_task",
(uint16_t )Receive1_STK_SIZE,
(void* )NULL,
(UBaseType_t )Receive1_TASK_PRIO,
(TaskHandle_t* )&Receive1Task_Handler);
//创建Receive2任务
xTaskCreate((TaskFunction_t )Receive2_task,
(const char* )"Receive2_task",
(uint16_t )Receive2_STK_SIZE,
(void* )NULL,
(UBaseType_t )Receive2_TASK_PRIO,
(TaskHandle_t* )&Receive2Task_Handler);
//创建Send任务
xTaskCreate((TaskFunction_t )Send_task,
(const char* )"Send_task",
(uint16_t )Send_STK_SIZE,
(void* )NULL,
(UBaseType_t )Send_TASK_PRIO,
(TaskHandle_t* )&SendTask_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
//LED1任务函数
void led1_task(void *pvParameters)
{
while(1)
{
LED1=0;
vTaskDelay(200);
LED1=1;
vTaskDelay(800);
}
}
//Receive1任务函数
void Receive1_task(void *pvParameters)
{
while(1)
{
//获取任务通知 ,没获取到则一直等待
ulTaskNotifyTake(pdTRUE,portMAX_DELAY);
printf("Receive1_Task 任务通知获取成功!\n\n");
}
}
//Receive2任务函数
void Receive2_task(void *pvParameters)
{
while(1)
{
//获取任务通知 ,没获取到则一直等待
ulTaskNotifyTake(pdTRUE,portMAX_DELAY);
printf("Receive2_Task 任务通知获取成功!\n\n");
}
}
//Send任务函数
void Send_task(void *pvParameters)
{
BaseType_t xReturn = pdPASS;
u8 key=0;
while(1)
{
key=KEY_Scan(0);
if(key==KEY_UP_PRESS)
{
xReturn = xTaskNotifyGive(Receive1Task_Handler);
if( xReturn == pdTRUE )
printf("Receive1_Task_Handle 任务通知发送成功!\r\n");
}
else if(key==KEY1_PRESS)
{
xReturn = xTaskNotifyGive(Receive2Task_Handler);
if( xReturn == pdTRUE )
printf("Receive2_Task_Handle 任务通知发送成功!\r\n");
}
vTaskDelay(20);
}
}
3.任务通知代替计数信号量
#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "key.h"
//任务优先级
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_STK_SIZE 128
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);
//任务优先级
#define LED1_TASK_PRIO 2
//任务堆栈大小
#define LED1_STK_SIZE 50
//任务句柄
TaskHandle_t LED1Task_Handler;
//任务函数
void led1_task(void *pvParameters);
//任务优先级
#define Take_TASK_PRIO 3
//任务堆栈大小
#define Take_STK_SIZE 512
//任务句柄
TaskHandle_t TakeTask_Handler;
//任务函数
void Take_task(void *pvParameters);
//任务优先级
#define Give_TASK_PRIO 4
//任务堆栈大小
#define Give_STK_SIZE 512
//任务句柄
TaskHandle_t GiveTask_Handler;
//任务函数
void Give_task(void *pvParameters);
/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
int main()
{
SysTick_Init(72);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
LED_Init();
KEY_Init();
USART1_Init(115200);
printf("FreeRTOS任务通知代替计数信号量实验\r\n");
printf("车位默认值为0个,按下KEY1申请车位,按下KEY2释放车位!\n\n");
//创建开始任务
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(); //进入临界区
//创建LED1任务
xTaskCreate((TaskFunction_t )led1_task,
(const char* )"led1_task",
(uint16_t )LED1_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED1_TASK_PRIO,
(TaskHandle_t* )&LED1Task_Handler);
//创建Take任务
xTaskCreate((TaskFunction_t )Take_task,
(const char* )"Take_task",
(uint16_t )Take_STK_SIZE,
(void* )NULL,
(UBaseType_t )Take_TASK_PRIO,
(TaskHandle_t* )&TakeTask_Handler);
//创建Give任务
xTaskCreate((TaskFunction_t )Give_task,
(const char* )"Give_task",
(uint16_t )Give_STK_SIZE,
(void* )NULL,
(UBaseType_t )Give_TASK_PRIO,
(TaskHandle_t* )&GiveTask_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
//LED1任务函数
void led1_task(void *pvParameters)
{
while(1)
{
LED1=0;
vTaskDelay(200);
LED1=1;
vTaskDelay(800);
}
}
//Give任务函数
void Give_task(void *pvParameters)
{
BaseType_t xReturn = pdPASS;
u8 key=0;
while(1)
{
key=KEY_Scan(0);
if(key==KEY2_PRESS)
{
xTaskNotifyGive(TakeTask_Handler);//发送任务通知
if ( pdPASS == xReturn )
printf( "KEY2被按下,释放1个停车位。\n" );
}
vTaskDelay(20);
}
}
//Take任务函数
void Take_task(void *pvParameters)
{
uint32_t take_num = pdTRUE;
u8 key=0;
while(1)
{
key=KEY_Scan(0);
if(key==KEY1_PRESS)
{
//获取任务通知 ,没获取到则不等待
take_num=ulTaskNotifyTake(pdFALSE,0);//pdFALSE,让值减1,有一个任务占用,就减1
if(take_num > 0)
printf( "KEY1被按下,成功申请到停车位,当前车位为 %d \n", take_num - 1);
else
printf( "KEY1被按下,车位已经没有了,请按KEY2释放车位\n" );
}
vTaskDelay(20);
}
}
4.任务通知代替事件组
#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "key.h"
#include "limits.h"
//任务优先级
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_STK_SIZE 128
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);
//任务优先级
#define LED1_TASK_PRIO 2
//任务堆栈大小
#define LED1_STK_SIZE 50
//任务句柄
TaskHandle_t LED1Task_Handler;
//任务函数
void led1_task(void *pvParameters);
//任务优先级
#define LED2_TASK_PRIO 3
//任务堆栈大小
#define LED2_STK_SIZE 50
//任务句柄
TaskHandle_t LED2Task_Handler;
//任务函数
void led2_task(void *pvParameters);
//任务优先级
#define KEY_TASK_PRIO 4
//任务堆栈大小
#define KEY_STK_SIZE 512
//任务句柄
TaskHandle_t KEYTask_Handler;
//任务函数
void key_task(void *pvParameters);
#define KEY1_EVENT (0x01 << 0)//设置事件掩码的位0
#define KEY2_EVENT (0x01 << 1)//设置事件掩码的位1
/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
int main()
{
SysTick_Init(72);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
LED_Init();
KEY_Init();
USART1_Init(115200);
printf("FreeRTOS任务通知代替事件组实验\r\n");
printf("按下KEY1|KEY2发送任务事件通知!\n");
//创建开始任务
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(); //进入临界区
//创建LED1任务
xTaskCreate((TaskFunction_t )led1_task,
(const char* )"led1_task",
(uint16_t )LED1_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED1_TASK_PRIO,
(TaskHandle_t* )&LED1Task_Handler);
//创建LED2任务
xTaskCreate((TaskFunction_t )led2_task,
(const char* )"led2_task",
(uint16_t )LED2_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED2_TASK_PRIO,
(TaskHandle_t* )&LED2Task_Handler);
//创建KEY任务
xTaskCreate((TaskFunction_t )key_task,
(const char* )"key_task",
(uint16_t )KEY_STK_SIZE,
(void* )NULL,
(UBaseType_t )KEY_TASK_PRIO,
(TaskHandle_t* )&KEYTask_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
//LED1任务函数
void led1_task(void *pvParameters)
{
while(1)
{
LED1=0;
vTaskDelay(200);
LED1=1;
vTaskDelay(800);
}
}
//LED2任务函数
void led2_task(void *pvParameters)
{
BaseType_t xReturn = pdTRUE;
uint32_t r_event = 0; /* 定义一个事件接收变量 */
uint32_t last_event = 0;/* 定义一个保存事件的变量 */
while(1)
{
//获取任务通知 ,没获取到则一直等待
xReturn = xTaskNotifyWait(0x0, //进入函数的时候不清除任务bit
ULONG_MAX, //退出函数的时候清除所有的bitR
&r_event, //保存任务通知值
portMAX_DELAY); //阻塞时间
if( pdTRUE == xReturn )
{
last_event |= r_event;
if(last_event == (KEY1_EVENT|KEY2_EVENT))
{
last_event=0;
printf ( "KEY1与KEY2都按下\n");
LED2=!LED2;
}
else
last_event = r_event;
}
}
}
//KEY任务函数
void key_task(void *pvParameters)
{
u8 key=0;
while(1)
{
key=KEY_Scan(0);
if(key==KEY1_PRESS)
{
printf ( "KEY1被按下\n" );
/* 触发一个事件1 */
xTaskNotify((TaskHandle_t )LED2Task_Handler,//接收任务通知的任务句柄
(uint32_t )KEY1_EVENT,//要触发的事件
(eNotifyAction)eSetBits);//设置任务通知值中的位
}
else if(key==KEY2_PRESS)
{
printf ( "KEY2被按下\n" );
/* 触发一个事件2 */
xTaskNotify((TaskHandle_t )LED2Task_Handler,//接收任务通知的任务句柄
(uint32_t )KEY2_EVENT,//要触发的事件
(eNotifyAction)eSetBits);//设置任务通知值中的位
}
vTaskDelay(20);
}
}