STM32---FreeRTOS任务通知
一、简介
1、任务通知简介
任务通知:用来通知任务的,任务控制块中的结构体成员变量 ulNotifiedValue就是这个通知值。
使用队列、信号量、事件标志组时都需另外创建一个结构体,通过中间的结构体进行间接通信!
使用任务通知时,任务结构体TCB中就包含了内部对象,可以直接接收别人发过来的"通知"
2、任务通知值的更新方式
不覆盖接受任务的通知值
覆盖接受任务的通知值
更新接受任务通知值的一个或多个bit
增加接受任务的通知值
只要合理,灵活的利用任务通知的特点,可以在一些场合中替代队列、信号量、事件标志组!
3、任务通知的优势和劣势
4、任务通知值和通知状态
任务都有一个结构体:任务控制块TCB,它里边有两个结构体成员变量:
一个是 uint32_t 类型:用来表示通知值
一个是 uint8_t 类型:用来表示通知状态
1、任务通知值的更新方式有多种类型:
2、任务通知状态:
二、任务通知相关API函数介绍
三、实验
1、模拟二值信号量
main.c
#include "stm32f10x.h"
#include "FreeRTOS.h"
#include "task.h"
#include "freertos_demo.h"
#include "Delay.h"
#include "sys.h"
#include "usart.h"
#include "LED.h"
#include "Key.h"
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组 4
uart_init(115200);
delay_init();
Key_Init();
LED_Init();
// 创建任务
FrrrRTOS_Demo();
}
freertos_demo.c
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "event_groups.h"
#include "LED.h"
#include "Key.h"
#include "usart.h"
#include "delay.h"
/******************************************************************任务配置****************************************************/
//任务优先级
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_TASK_STACK_SIZE 128
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);
//任务优先级
#define TASK1_PRIO 2
//任务堆栈大小
#define TASK1_STACK_SIZE 128
//任务句柄
TaskHandle_t Task1_Handler;
//任务函数
void task1(void *pvParameters);
//任务优先级
#define TASK2_PRIO 3
//任务堆栈大小
#define TASK2_STACK_SIZE 128
//任务句柄
TaskHandle_t Task2_Handler;
//任务函数
void task2(void *pvParameters);
/******************************************************************任务函数****************************************************/
void FrrrRTOS_Demo(void)
{
//创建开始任务
xTaskCreate((TaskFunction_t )start_task, //任务函数
( char* )"start_task", //任务名称
(uint16_t )START_TASK_STACK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )START_TASK_PRIO, //任务优先级
(TaskHandle_t* )&StartTask_Handler); //任务句柄
// 启动任务调度
vTaskStartScheduler();
}
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建1任务
xTaskCreate((TaskFunction_t )task1,
(const char* )"task1",
(uint16_t )TASK1_STACK_SIZE,
(void* )NULL,
(UBaseType_t )TASK1_PRIO,
(TaskHandle_t* )&Task1_Handler);
//创建2任务
xTaskCreate((TaskFunction_t )task2,
(const char* )"task2",
(uint16_t )TASK2_STACK_SIZE,
(void* )NULL,
(UBaseType_t )TASK2_PRIO,
(TaskHandle_t* )&Task2_Handler);
vTaskDelete(NULL); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
//1 发送任务通知值
void task1(void *pvParameters)
{
uint8_t key = 0;
while(1)
{
key = Key_GetNum();
if(key == 2)
{
printf("任务通知模拟二值信号释放\r\n");
xTaskNotifyGive(Task2_Handler);
}
vTaskDelay(10);
}
}
// 任务2 接受任务通知值
void task2(void *pvParameters)
{
uint32_t rev = 0;
// 任务主循环
while (1)
{
rev = ulTaskNotifyTake(pdTRUE,portMAX_DELAY);
if(rev != 0)
{
printf("接受任务通知成功,模拟获取二值信号量\r\n");
}
}
}
key.c
#include "stm32f10x.h" // Device header
#include "FreeRTOS.h"
#include "task.h"
#include "usart.h"
#include "Delay.h"
/**
* 函 数:按键初始化
* 参 数:无
* 返 回 值:无
* 按键:PB4/PB12/PB14
*/
void Key_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟
/*GPIO初始化*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_12 | GPIO_Pin_14;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
/**
* 函 数:按键获取键码
* 参 数:无
* 返 回 值:按下按键的键码值,范围:0~3,返回0代表没有按键按下
* 注意事项:此函数是阻塞式操作,当按键按住不放时,函数会卡住,直到按键松手
*/
uint8_t Key_GetNum(void)
{
uint8_t KeyNum = 0; //定义变量,默认键码值为0
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_4) == 0) //读PB4输入寄存器的状态,如果为0,则代表按键1按下
{
KeyNum= GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_4);
delay_xms(20); //延时消抖
while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_4) == 0); //等待按键松手
delay_xms(20); //延时消抖
KeyNum = 1; //置键码为1
}
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12) == 0)
{
KeyNum= GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12);
delay_xms(20);
while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12) == 0);
delay_xms(20);
KeyNum = 2;
}
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0)
{
KeyNum= GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14);
delay_xms(20);
while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0);
delay_xms(20);
KeyNum = 3;
}
return KeyNum; //返回键码值,如果没有按键按下,所有if都不成立,则键码为默认值0
}
1、实验解析
按下按键,模拟二值信号量的释放和接受;
2、模拟计算型信号量
freertos_demo.c
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "event_groups.h"
#include "LED.h"
#include "Key.h"
#include "usart.h"
#include "delay.h"
/******************************************************************任务配置****************************************************/
//任务优先级
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_TASK_STACK_SIZE 128
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);
//任务优先级
#define TASK1_PRIO 2
//任务堆栈大小
#define TASK1_STACK_SIZE 128
//任务句柄
TaskHandle_t Task1_Handler;
//任务函数
void task1(void *pvParameters);
//任务优先级
#define TASK2_PRIO 3
//任务堆栈大小
#define TASK2_STACK_SIZE 128
//任务句柄
TaskHandle_t Task2_Handler;
//任务函数
void task2(void *pvParameters);
/******************************************************************任务函数****************************************************/
void FrrrRTOS_Demo(void)
{
//创建开始任务
xTaskCreate((TaskFunction_t )start_task, //任务函数
( char* )"start_task", //任务名称
(uint16_t )START_TASK_STACK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )START_TASK_PRIO, //任务优先级
(TaskHandle_t* )&StartTask_Handler); //任务句柄
// 启动任务调度
vTaskStartScheduler();
}
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建1任务
xTaskCreate((TaskFunction_t )task1,
(const char* )"task1",
(uint16_t )TASK1_STACK_SIZE,
(void* )NULL,
(UBaseType_t )TASK1_PRIO,
(TaskHandle_t* )&Task1_Handler);
//创建2任务
xTaskCreate((TaskFunction_t )task2,
(const char* )"task2",
(uint16_t )TASK2_STACK_SIZE,
(void* )NULL,
(UBaseType_t )TASK2_PRIO,
(TaskHandle_t* )&Task2_Handler);
vTaskDelete(NULL); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
//1 发送任务通知值
void task1(void *pvParameters)
{
uint8_t key = 0;
while(1)
{
key = Key_GetNum();
if(key == 2)
{
printf("任务通知模拟二值信号释放\r\n");
xTaskNotifyGive(Task2_Handler);
}
vTaskDelay(10);
}
}
// 任务2 接受任务通知值
void task2(void *pvParameters)
{
uint32_t rev = 0;
// 任务主循环
while (1)
{
rev = ulTaskNotifyTake(pdFALSE,portMAX_DELAY);
if(rev != 0)
{
printf("rev:%d\r\n",rev);
}
vTaskDelay(1000);
}
}
1、实验解析
快速点击按键,模拟计数型信号量的接受和释放;
3、模拟信号邮箱实验
freertos_demo.c
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "event_groups.h"
#include "LED.h"
#include "Key.h"
#include "usart.h"
#include "delay.h"
/******************************************************************任务配置****************************************************/
//任务优先级
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_TASK_STACK_SIZE 128
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);
//任务优先级
#define TASK1_PRIO 2
//任务堆栈大小
#define TASK1_STACK_SIZE 128
//任务句柄
TaskHandle_t Task1_Handler;
//任务函数
void task1(void *pvParameters);
//任务优先级
#define TASK2_PRIO 3
//任务堆栈大小
#define TASK2_STACK_SIZE 128
//任务句柄
TaskHandle_t Task2_Handler;
//任务函数
void task2(void *pvParameters);
/******************************************************************任务函数****************************************************/
void FrrrRTOS_Demo(void)
{
//创建开始任务
xTaskCreate((TaskFunction_t )start_task, //任务函数
( char* )"start_task", //任务名称
(uint16_t )START_TASK_STACK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )START_TASK_PRIO, //任务优先级
(TaskHandle_t* )&StartTask_Handler); //任务句柄
// 启动任务调度
vTaskStartScheduler();
}
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建1任务
xTaskCreate((TaskFunction_t )task1,
(const char* )"task1",
(uint16_t )TASK1_STACK_SIZE,
(void* )NULL,
(UBaseType_t )TASK1_PRIO,
(TaskHandle_t* )&Task1_Handler);
//创建2任务
xTaskCreate((TaskFunction_t )task2,
(const char* )"task2",
(uint16_t )TASK2_STACK_SIZE,
(void* )NULL,
(UBaseType_t )TASK2_PRIO,
(TaskHandle_t* )&Task2_Handler);
vTaskDelete(NULL); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
//1 发送任务通知值
void task1(void *pvParameters)
{
uint8_t key = 0;
while(1)
{
key = Key_GetNum();
if((key != 0) && (Task2_Handler != NULL))
{
printf("任务通知模拟消息邮箱发送,发送的键值为:%d\r\n",key);
xTaskNotify( Task2_Handler, key, eSetValueWithOverwrite );
// 发送任务通知给任务2,使用 eSetValueWithOverwrite 模式
// Task2_Handler: 任务2的句柄
// key: 发送的值
// eSetValueWithOverwrite: 覆盖模式,新的值会覆盖旧的值
}
vTaskDelay(10);
}
}
// 任务2 接受任务通知值
void task2(void *pvParameters)
{
uint32_t rev = 0;
// 任务主循环
while (1)
{
xTaskNotifyWait(0,0xFFFFFFFF,&rev,portMAX_DELAY );
// 等待任务通知,portMAX_DELAY 表示无限等待
// 0: 不清除任何通知位
// 0xFFFFFFFF: 接收所有通知值
// &rev: 存储接收到的通知值
// portMAX_DELAY: 无限等待,直到收到通知
switch(rev){
case 2:
{
printf("接收到的任务通知值为:%d\r\n",rev);
LED1_Turn();
break;
}
case 3:
{
printf("接收到的任务通知值为:%d\r\n",rev);
LED2_Turn();
break;
}
};
}
}
1、实验解析
4、模拟时间标志组
freertosc_demo.c
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "event_groups.h"
#include "LED.h"
#include "Key.h"
#include "usart.h"
#include "delay.h"
/******************************************************************任务配置****************************************************/
//任务优先级
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_TASK_STACK_SIZE 128
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);
//任务优先级
#define TASK1_PRIO 2
//任务堆栈大小
#define TASK1_STACK_SIZE 128
//任务句柄
TaskHandle_t Task1_Handler;
//任务函数
void task1(void *pvParameters);
//任务优先级
#define TASK2_PRIO 3
//任务堆栈大小
#define TASK2_STACK_SIZE 128
//任务句柄
TaskHandle_t Task2_Handler;
//任务函数
void task2(void *pvParameters);
#define EVENTBIT0 (1<<0)
#define EVENTBIT1 (1<<1)
/******************************************************************任务函数****************************************************/
void FrrrRTOS_Demo(void)
{
//创建开始任务
xTaskCreate((TaskFunction_t )start_task, //任务函数
( char* )"start_task", //任务名称
(uint16_t )START_TASK_STACK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )START_TASK_PRIO, //任务优先级
(TaskHandle_t* )&StartTask_Handler); //任务句柄
// 启动任务调度
vTaskStartScheduler();
}
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建1任务
xTaskCreate((TaskFunction_t )task1,
(const char* )"task1",
(uint16_t )TASK1_STACK_SIZE,
(void* )NULL,
(UBaseType_t )TASK1_PRIO,
(TaskHandle_t* )&Task1_Handler);
//创建2任务
xTaskCreate((TaskFunction_t )task2,
(const char* )"task2",
(uint16_t )TASK2_STACK_SIZE,
(void* )NULL,
(UBaseType_t )TASK2_PRIO,
(TaskHandle_t* )&Task2_Handler);
vTaskDelete(NULL); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
//1 发送任务通知值
void task1(void *pvParameters)
{
uint8_t key = 0;
while(1)
{
key = Key_GetNum();
if(key ==2)
{
printf("更新bit0位,将其置1\r\n");
xTaskNotify(Task2_Handler,EVENTBIT0,eSetBits);
}else if(key ==3){
printf("更新bit1位,将其置1\r\n");
xTaskNotify(Task2_Handler,EVENTBIT1,eSetBits);
}
vTaskDelay(10);
}
}
// 任务2 接受任务通知值
void task2(void *pvParameters)
{
uint32_t rev = 0;
uint32_t bit = 0;
// 任务主循环
while (1)
{
xTaskNotifyWait(0,0xFFFFFFFF,&rev,portMAX_DELAY );
// 等待任务通知,portMAX_DELAY 表示无限等待
// 0: 不清除任何通知位
// 0xFFFFFFFF: 接收所有通知值
// &rev: 存储接收到的通知值
// portMAX_DELAY: 无限等待,直到收到通知
if(rev&EVENTBIT0)
{
bit|= EVENTBIT0;
}
if(rev&EVENTBIT1)
{
bit|= EVENTBIT1;
}
if(bit == (EVENTBIT0|EVENTBIT1))
{
printf("任务通知模拟事件标志组接收成功\r\n");
bit = 0;
}
}
}