15、任务通知
15.1 任务通知简介
任务通知:用来通知任务的,任务控制块中的结构体成员变量 ulNotifiedValue就是这个通知值
任务通知的更新方式:
1、不覆盖接受任务的通知值
2、覆盖接受任务的通知值
3、更新接受任务通知值的一个或多个bit
4、增加接受任务的通知值
任务通知的优势与劣势:
优势:效率更高、使用内存更小。
劣势:无法发送数据给ISR、无法广播给多个任务、无法缓存多个数据、发送受阻不支持阻塞。
任务通知值的更新方式有多种类型:
1、计数值(数值累加,类似信号量)
2、相应位置一(类似事件标志组)
3、任意数值(支持覆写和不覆写,类似队列)
任务通知状态:
任务通知状态共有三种取值:
15.2 任务通知API函数
任务通知主要有两类API函数:①发送通知,②接收通知
注意:发送通知API函数可以用于任务和中断服务函数,接受通知API函数只能用于任务中。
①发送通知相关API函数:
实际上,这三个发送通知的函数本质上都是调用同一个函数xTaskGenericNotify(),只不过可修改的形参数量不同
②接收通知相关API函数:
ulTaskNotifyTake():
xTaskNotifyWait():
15.3 任务通知模拟二值信号量编程实战
freertos_demo.c:
/**
****************************************************************************************************
* @file freertos.c
* @author 正点原子团队(ALIENTEK)
* @version V1.4
* @date 2022-01-04
* @brief FreeRTOS 移植实验
* @license Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
****************************************************************************************************
* @attention
*
* 实验平台:正点原子 精英F103开发板
* 在线视频:www.yuanzige.com
* 技术论坛:www.openedv.com
* 公司网址:www.alientek.com
* 购买地址:openedv.taobao.com
*
****************************************************************************************************
*/
#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./SYSTEM/delay/delay.h"
#include "./MALLOC/malloc.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "event_groups.h"
/******************************************************************************************************/
/*FreeRTOS配置*/
/* START_TASK 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define START_TASK_STACK_SIZE 128
#define START_TASK_PRIORITY 1
TaskHandle_t Start_Task_Handler;
void start_task( void * pvParameters );
/* Task1 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK1_STACK_SIZE 128
#define TASK1_PRIORITY 2
TaskHandle_t Task1_Handler;
void task1( void * pvParameters );
/* Task2 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK2_STACK_SIZE 128
#define TASK2_PRIORITY 3
TaskHandle_t Task2_Handler;
void task2( void * pvParameters );
/******************************************************************************************************/
/*
* @brief FreeRTOS例程入口函数
* @param 无
* @retval 无
*/
void freertos_demo(void)
{
xTaskCreate(( TaskFunction_t ) start_task,
(char * ) "start_task",
(configSTACK_DEPTH_TYPE) START_TASK_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) START_TASK_PRIORITY,
(TaskHandle_t * ) &Start_Task_Handler );
vTaskStartScheduler();//开启任务调度器
}
void start_task( void * pvParameters )
{
taskENTER_CRITICAL(); /* 进入临界区 */
/*创建任务1*/
xTaskCreate(( TaskFunction_t ) task1,
(char * ) "task1",
(configSTACK_DEPTH_TYPE) TASK1_STACK_SIZE,
(void * ) NULL,
( UBaseType_t ) TASK1_PRIORITY,
(TaskHandle_t * ) &Task1_Handler );
/*创建任务2*/
xTaskCreate(( TaskFunction_t ) task2,
(char * ) "task2",
(configSTACK_DEPTH_TYPE) TASK2_STACK_SIZE,
(void * ) NULL,
( UBaseType_t ) TASK2_PRIORITY,
(TaskHandle_t * ) &Task2_Handler );
vTaskDelete(NULL);//删除开始任务
taskEXIT_CRITICAL(); /* 退出临界区 */
}
/*任务一,发送任务通知值*/
void task1( void * pvParameters )
{
uint8_t Key = 0;
while(1)
{
Key =key_scan(0);
if(Key == KEY0_PRES)
{
printf("任务通知模拟二值信号量释放!\r\n");
xTaskNotifyGive(Task2_Handler);
}
vTaskDelay(10);
}
}
/*任务二,接收任务通知值*/
void task2( void * pvParameters )
{
uint32_t Rev = 0;
while(1)
{
Rev = ulTaskNotifyTake(pdTRUE,portMAX_DELAY);
if(Rev != 0)
{
printf("任务通知接收成功!\r\n");
}
}
}
15.4 任务通知模拟消息邮箱实战
/**
****************************************************************************************************
* @file freertos.c
* @author 正点原子团队(ALIENTEK)
* @version V1.4
* @date 2022-01-04
* @brief FreeRTOS 移植实验
* @license Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
****************************************************************************************************
* @attention
*
* 实验平台:正点原子 精英F103开发板
* 在线视频:www.yuanzige.com
* 技术论坛:www.openedv.com
* 公司网址:www.alientek.com
* 购买地址:openedv.taobao.com
*
****************************************************************************************************
*/
#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./SYSTEM/delay/delay.h"
#include "./MALLOC/malloc.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "event_groups.h"
/******************************************************************************************************/
/*FreeRTOS配置*/
/* START_TASK 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define START_TASK_STACK_SIZE 128
#define START_TASK_PRIORITY 1
TaskHandle_t Start_Task_Handler;
void start_task( void * pvParameters );
/* Task1 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK1_STACK_SIZE 128
#define TASK1_PRIORITY 2
TaskHandle_t Task1_Handler;
void task1( void * pvParameters );
/* Task2 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK2_STACK_SIZE 128
#define TASK2_PRIORITY 3
TaskHandle_t Task2_Handler;
void task2( void * pvParameters );
/******************************************************************************************************/
/*
* @brief FreeRTOS例程入口函数
* @param 无
* @retval 无
*/
void freertos_demo(void)
{
xTaskCreate(( TaskFunction_t ) start_task,
(char * ) "start_task",
(configSTACK_DEPTH_TYPE) START_TASK_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) START_TASK_PRIORITY,
(TaskHandle_t * ) &Start_Task_Handler );
vTaskStartScheduler();//开启任务调度器
}
void start_task( void * pvParameters )
{
taskENTER_CRITICAL(); /* 进入临界区 */
/*创建任务1*/
xTaskCreate(( TaskFunction_t ) task1,
(char * ) "task1",
(configSTACK_DEPTH_TYPE) TASK1_STACK_SIZE,
(void * ) NULL,
( UBaseType_t ) TASK1_PRIORITY,
(TaskHandle_t * ) &Task1_Handler );
/*创建任务2*/
xTaskCreate(( TaskFunction_t ) task2,
(char * ) "task2",
(configSTACK_DEPTH_TYPE) TASK2_STACK_SIZE,
(void * ) NULL,
( UBaseType_t ) TASK2_PRIORITY,
(TaskHandle_t * ) &Task2_Handler );
vTaskDelete(NULL);//删除开始任务
taskEXIT_CRITICAL(); /* 退出临界区 */
}
/*任务一,发送任务通知值*/
void task1( void * pvParameters )
{
uint8_t Key = 0;
while(1)
{
Key =key_scan(0);
if((Key != 0) && (Task2_Handler != NULL))
{
printf("任务通知模拟消息邮箱发送,发送的键值为:%d\r\n",Key);
xTaskNotify( Task2_Handler, Key, eSetValueWithOverwrite );
}
vTaskDelay(10);
}
}
/*任务二,接收任务通知值*/
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;
}
}
}
15.5 任务通知模拟事件标志组编程实战
/**
****************************************************************************************************
* @file freertos.c
* @author 正点原子团队(ALIENTEK)
* @version V1.4
* @date 2022-01-04
* @brief FreeRTOS 移植实验
* @license Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
****************************************************************************************************
* @attention
*
* 实验平台:正点原子 精英F103开发板
* 在线视频:www.yuanzige.com
* 技术论坛:www.openedv.com
* 公司网址:www.alientek.com
* 购买地址:openedv.taobao.com
*
****************************************************************************************************
*/
#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./SYSTEM/delay/delay.h"
#include "./MALLOC/malloc.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "event_groups.h"
/******************************************************************************************************/
/*FreeRTOS配置*/
/* START_TASK 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define START_TASK_STACK_SIZE 128
#define START_TASK_PRIORITY 1
TaskHandle_t Start_Task_Handler;
void start_task( void * pvParameters );
/* Task1 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK1_STACK_SIZE 128
#define TASK1_PRIORITY 2
TaskHandle_t Task1_Handler;
void task1( void * pvParameters );
/* Task2 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK2_STACK_SIZE 128
#define TASK2_PRIORITY 3
TaskHandle_t Task2_Handler;
void task2( void * pvParameters );
/******************************************************************************************************/
#define EVENT_BIT0 (1<<0)
#define EVENT_BIT1 (1<<1)
/*
* @brief FreeRTOS例程入口函数
* @param 无
* @retval 无
*/
void freertos_demo(void)
{
xTaskCreate(( TaskFunction_t ) start_task,
(char * ) "start_task",
(configSTACK_DEPTH_TYPE) START_TASK_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) START_TASK_PRIORITY,
(TaskHandle_t * ) &Start_Task_Handler );
vTaskStartScheduler();//开启任务调度器
}
void start_task( void * pvParameters )
{
taskENTER_CRITICAL(); /* 进入临界区 */
/*创建任务1*/
xTaskCreate(( TaskFunction_t ) task1,
(char * ) "task1",
(configSTACK_DEPTH_TYPE) TASK1_STACK_SIZE,
(void * ) NULL,
( UBaseType_t ) TASK1_PRIORITY,
(TaskHandle_t * ) &Task1_Handler );
/*创建任务2*/
xTaskCreate(( TaskFunction_t ) task2,
(char * ) "task2",
(configSTACK_DEPTH_TYPE) TASK2_STACK_SIZE,
(void * ) NULL,
( UBaseType_t ) TASK2_PRIORITY,
(TaskHandle_t * ) &Task2_Handler );
vTaskDelete(NULL);//删除开始任务
taskEXIT_CRITICAL(); /* 退出临界区 */
}
/*任务一,发送任务通知值*/
void task1( void * pvParameters )
{
uint8_t Key;
while(1)
{
Key =key_scan(0);
if(Key == KEY0_PRES)
{
printf("将Bit0位置1\r\n");
xTaskNotify( Task2_Handler, EVENT_BIT0, eSetBits );
}
else if(Key == KEY1_PRES)
{
printf("将Bit1位置1\r\n");
xTaskNotify( Task2_Handler, EVENT_BIT1, eSetBits );
}
vTaskDelay(10);
}
}
/*任务二,接收任务通知值*/
void task2( void * pvParameters )
{
uint32_t Notify_Val = 0,Event_Bit = 0;
while(1)
{
xTaskNotifyWait( 0, 0xFFFFFFFF, &Notify_Val, portMAX_DELAY );
if(Notify_Val & EVENT_BIT0)
{
Event_Bit |= EVENT_BIT0;
}
if(Notify_Val & EVENT_BIT1)
{
Event_Bit |= EVENT_BIT1;
}
if(Event_Bit ==(EVENT_BIT0 | EVENT_BIT1))
{
printf("任务通知模拟事件标志组接收成功!\r\n");
Event_Bit = 0;
}
}
}
16、软件定时器
16.1 软件定时器简介
什么是定时器?
定时器:从指定的时刻开始,经过一个指定时间,然后触发一个超时事件,用户可以自定义定时器的周期。
硬件定时器:
芯片本身自带的定时器模块,硬件定时器的精度一般很高,每次在定时时间到达之后就会自动触发一个中断,用户在中断服务函数中处理信息。
软件定时器:
是指具有定时功能的软件,可设置定时周期,当指定时间到达后要调用回调函数(也称超时函数),用户在回调函数中处理信息
软件定时器的特点:
软件定时器服务任务的作用:
1、负责软件定时器超时的逻辑判断。
2、调用超时软件定时器的超时回调函数。
3、处理软件定时器命令队列。
在FreeRTOS中,提供了许多软件定时器相关的 API 函数,这些 API 函数大多都是往定时器的队列中写入消息(发送命令),这个队列叫做软件定时器命令队列,是提供给 FreeRTOS 中的软件定时器使用的,用户是不能直接访问的。
软件定时器有两种状态:
1、休眠态:软件定时器可以通过其句柄被引用,但是因为没有运行,所以其定时超时回调函数不会被执行。
2、运行态:运行态的定时器,当指定时间到达之后,它的超时回调函数会被调用。
如何让软件定时器从休眠态转变为运行态?
通过发送命令队列。
在FreeRTOS中,提供了两种软件定时器:
单次定时器:单次定时器一旦定时超时,只会执行一次其软件定时器超时回调函数,不会自动重新开启定时,不过可以被手动重新开启。
周期定时器:周期定时器一旦启动以后就会在执行完回调函数以后自动的重新启动 ,从而周期地执行其软件定时器回调函数。
16.2 软件定时器相关API函数
软件定时器结构体成员如下:
软件定时器的相关API函数如下:
创建软件定时器API函数:
开启软件定时器API函数:
停止软件定时器API函数:
复位软件定时器API函数:
更改软件定时器超时时间API函数:
16.3 软件定时器编程实战
/**
****************************************************************************************************
* @file freertos.c
* @author 正点原子团队(ALIENTEK)
* @version V1.4
* @date 2022-01-04
* @brief FreeRTOS 移植实验
* @license Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
****************************************************************************************************
* @attention
*
* 实验平台:正点原子 精英F103开发板
* 在线视频:www.yuanzige.com
* 技术论坛:www.openedv.com
* 公司网址:www.alientek.com
* 购买地址:openedv.taobao.com
*
****************************************************************************************************
*/
#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./SYSTEM/delay/delay.h"
#include "./MALLOC/malloc.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "event_groups.h"
/******************************************************************************************************/
/*FreeRTOS配置*/
/* START_TASK 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define START_TASK_STACK_SIZE 128
#define START_TASK_PRIORITY 1
TaskHandle_t Start_Task_Handler;
void start_task( void * pvParameters );
/* Task1 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK1_STACK_SIZE 128
#define TASK1_PRIORITY 2
TaskHandle_t Task1_Handler;
void task1( void * pvParameters );
void Timer1_Callback( TimerHandle_t pxTimer );
void Timer2_Callback( TimerHandle_t pxTimer );
/******************************************************************************************************/
TimerHandle_t Timer1_Handler = 0; /*单次定时器*/
TimerHandle_t Timer2_Handler = 0; /*周期定时器*/
/*
* @brief FreeRTOS例程入口函数
* @param 无
* @retval 无
*/
void freertos_demo(void)
{
xTaskCreate(( TaskFunction_t ) start_task,
(char * ) "start_task",
(configSTACK_DEPTH_TYPE) START_TASK_STACK_SIZE,
(void * ) NULL,
(UBaseType_t ) START_TASK_PRIORITY,
(TaskHandle_t * ) &Start_Task_Handler );
vTaskStartScheduler();//开启任务调度器
}
void start_task( void * pvParameters )
{
taskENTER_CRITICAL(); /* 进入临界区 */
/*单次定时器*/
Timer1_Handler = xTimerCreate( "Timer1", /*定时器名称*/
1000, /*超时周期*/
pdFALSE, /*单次定时器*/
(void *)1, /*定时器ID*/
Timer1_Callback /*回调函数*/);
/*周期定时器*/
Timer2_Handler = xTimerCreate( "Timer2", /*定时器名称*/
1000, /*超时周期*/
pdTRUE, /*周期定时器*/
(void *)2, /*定时器ID*/
Timer2_Callback /*回调函数*/);
/*创建任务1*/
xTaskCreate(( TaskFunction_t ) task1,
(char * ) "task1",
(configSTACK_DEPTH_TYPE) TASK1_STACK_SIZE,
(void * ) NULL,
( UBaseType_t ) TASK1_PRIORITY,
(TaskHandle_t * ) &Task1_Handler );
vTaskDelete(NULL);//删除开始任务
taskEXIT_CRITICAL(); /* 退出临界区 */
}
/*任务一,按键扫描并控制定时器*/
void task1( void * pvParameters )
{
uint8_t Key = 0;
while(1)
{
Key = key_scan(0);
if(Key == KEY0_PRES)
{
xTimerStart( Timer1_Handler, portMAX_DELAY );
xTimerStart( Timer2_Handler, portMAX_DELAY );
}
else if(Key == KEY1_PRES)
{
xTimerStop( Timer1_Handler, portMAX_DELAY );
xTimerStop( Timer2_Handler, portMAX_DELAY );
}
vTaskDelay(10);
}
}
/*Timer1超时回调函数*/
void Timer1_Callback( TimerHandle_t pxTimer )
{
static uint32_t Timer = 0;
printf("Timer1运行次数:%d\r\n",++Timer);
}
/*Timer2超时回调函数*/
void Timer2_Callback( TimerHandle_t pxTimer )
{
static uint32_t Timer = 0;
printf("Timer2运行次数:%d\r\n",++Timer);
}