文章目录[隐藏]
FreeRTOS复习笔记(六) —— 任务通知
1.本例程思路:
创建两个任务,一个任务用于发送通知,另一个任务用于接收通知,当发送任务中检测到KEY_GPIO(PA0)出现下降沿时发送通知,接收任务接收到通知消息后打印输出
任务通知无需创建,直接调用函数 xTaskNotify 发送消息,xTaskNotifyWait 用于接收消息
2.代码编写
/**
******************************************************************************
* @文件名: xxx.c
* @作 者: author
* @版 本: v1.0.0
* @日 期: 2021.12.27
* @简 介: 任务通知代替消息队列
* @注 意: 无
******************************************************************************
*/
#include "stm32f10x.h"
#include "dr_usart.h"
#include "dr_led.h"
#include "dr_key.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "event_groups.h"
#include "limits.h"
void AppTaskCreateTask(void *);
void LEDTask(void *);
void KEYTask(void *);
/* --- 任务 --- */
TaskHandle_t LEDTask_Handle = NULL;
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
int main(void)
{
/* --------- --------- --------- --------- --------- --------- --------- */
/* 中断分组4 */
NVIC_Priority_Group_Config(); /* * */
/* 串口1配置 */
USART1_Config(); /* * */
/* LED(PA8, PA6)配置 */
LED_GPIO_Config(); /* * */
/* KEY(PA0)配置 */
KEY_GPIO_Config();
/* --------- --------- --------- --------- --------- --------- --------- */
/* 创建任务 */
xTaskCreate(AppTaskCreateTask, "AppTaskCreateTask", 128, NULL, 1, NULL);
/* 开启任务调度 */
vTaskStartScheduler();
while(1);
}
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
void AppTaskCreateTask(void *pvParameters)
{
/* 临界区 */
taskENTER_CRITICAL();
/* 创建任务 */
xTaskCreate(LEDTask, "LEDTask", 128, NULL, 2, &LEDTask_Handle);
xTaskCreate(KEYTask, "KEYTask", 128, NULL, 3, NULL);
vTaskDelete(NULL);
taskEXIT_CRITICAL();
}
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
void LEDTask(void *pvParameters)
{
uint32_t rev1;
BaseType_t xReturn = pdFALSE;
for (;;)
{
/* 清除位置1, 否则清0 */
/* 等待通知, 进入时不清除位 退出时清除位 接收数据 等待超时时间 */
xReturn = xTaskNotifyWait(0, ULONG_MAX, &rev1, portMAX_DELAY);
if (xReturn == pdTRUE)
printf("obtained, %x.\r\n", rev1);
GPIOA->ODR ^= ((uint16_t)0x0100); /* 翻转LED(PA8) */
// printf("LED %s\r\n", ((GPIOA->IDR & 0x0100) != 0) ? "is running." : "stops running.");
vTaskDelay(50);
}
}
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
void KEYTask(void *pvParameters)
{
uint8_t level_high = 0;
uint8_t level_low = 0;
uint8_t edge_fall = 0;
uint8_t edge_rise = 0;
uint8_t _switch = 0;
uint32_t data1 = 0x404;
uint32_t data2 = 0x5a5;
BaseType_t xReturn = pdFALSE;
for (;;)
{
/* 高电平/上升沿检测 */
if ( (GPIOA->IDR & 0x0001) != 0 )
{
if (level_low == 1) edge_rise = 1;
level_low = 0; level_high = 1;
}
/* 低电平/下降沿检测 */
if ( (GPIOA->IDR & 0x0001) == 0 )
{
if (level_high == 1) edge_fall = 1;
level_high = 0; level_low = 1;
}
if (edge_fall) /* 出现下降沿 */
{
if (_switch == 0)
{
_switch = 1;
/**
eNoAction, 通知任务, 不更新通知值
eSetBits, 设置通知值中的位 --- 事件组
eIncrement, 增加通知值 --- 信号量
eSetValueWithOverwrite, 直接更新通知值 --- 消息队列
eSetValueWithoutOverwrite 若前一个通知值被读取才更新通知值 --- 消息队列
*/
/* 任务通知, 任务句柄 待发送数据 内容覆盖 */
xReturn = xTaskNotify(LEDTask_Handle, data1, eSetValueWithOverwrite);
if (xReturn == pdTRUE)
printf("data1 sent successful.\r\n");
}
else
{
if (_switch == 1)
{
_switch = 0;
/* 任务通知, 任务句柄 待发送数据 内容覆盖 */
xReturn = xTaskNotify(LEDTask_Handle, data2, eSetValueWithOverwrite);
if (xReturn == pdTRUE)
printf("data2 sent successful.\r\n");
}
}
}
edge_fall = 0;
edge_rise = 0;
(void)edge_rise;
vTaskDelay(20);
}
}
上述例程为将任务通知代替消息队列使用,还可以使用其代替信号量、事件组。代替信号量时可以直接调用函数 xTaskNotifyGive 释放信号量,使用 ulTaskNotifyTake 获取信号量
本例程思路(代替二值信号量):
创建两个任务,一个任务用于释放信号量,另一个任务用于获取信号量,当发送任务中检测到KEY_GPIO(PA0)出现下降沿时释放信号量,接收任务接收到信号量后打印输出
/**
******************************************************************************
* @文件名: xxx.c
* @作 者: author
* @版 本: v1.0.0
* @日 期: 2021.12.27
* @简 介: 任务通知代替二值信号量
* @注 意: 无
******************************************************************************
*/
#include "stm32f10x.h"
#include "dr_usart.h"
#include "dr_led.h"
#include "dr_key.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "event_groups.h"
#include "limits.h"
void AppTaskCreateTask(void *);
void LEDTask(void *);
void KEYTask(void *);
/* --- 任务 --- */
TaskHandle_t LEDTask_Handle = NULL;
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
int main(void)
{
/* --------- --------- --------- --------- --------- --------- --------- */
/* 中断分组4 */
NVIC_Priority_Group_Config(); /* * */
/* 串口1配置 */
USART1_Config(); /* * */
/* LED(PA8, PA6)配置 */
LED_GPIO_Config(); /* * */
/* KEY(PA0)配置 */
KEY_GPIO_Config();
/* --------- --------- --------- --------- --------- --------- --------- */
/* 创建任务 */
xTaskCreate(AppTaskCreateTask, "AppTaskCreateTask", 128, NULL, 1, NULL);
/* 开启任务调度 */
vTaskStartScheduler();
while(1);
}
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
void AppTaskCreateTask(void *pvParameters)
{
/* 临界区 */
taskENTER_CRITICAL();
/* 创建任务 */
xTaskCreate(LEDTask, "LEDTask", 128, NULL, 2, &LEDTask_Handle);
xTaskCreate(KEYTask, "KEYTask", 128, NULL, 3, NULL);
vTaskDelete(NULL);
taskEXIT_CRITICAL();
}
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
void LEDTask(void *pvParameters)
{
uint32_t NumCount = 0;
for (;;)
{
/* pdTRUE: 退出清零, pdFALSE: 退出减一 */
/* 等待通知, 退出清零 等待超时时间 */
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
printf("obtained, %d times.\r\n", ++NumCount);
GPIOA->ODR ^= ((uint16_t)0x0100); /* 翻转LED(PA8) */
// printf("LED %s\r\n", ((GPIOA->IDR & 0x0100) != 0) ? "is running." : "stops running.");
vTaskDelay(50);
}
}
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
void KEYTask(void *pvParameters)
{
uint8_t level_high = 0;
uint8_t level_low = 0;
uint8_t edge_fall = 0;
uint8_t edge_rise = 0;
uint32_t NumCount = 0;
BaseType_t xReturn = pdFALSE;
for (;;)
{
/* 高电平/上升沿检测 */
if ( (GPIOA->IDR & 0x0001) != 0 )
{
if (level_low == 1) edge_rise = 1;
level_low = 0; level_high = 1;
}
/* 低电平/下降沿检测 */
if ( (GPIOA->IDR & 0x0001) == 0 )
{
if (level_high == 1) edge_fall = 1;
level_high = 0; level_low = 1;
}
if (edge_fall) /* 出现下降沿 */
{
NumCount++;
/* 任务通知, 任务句柄 */
xReturn = xTaskNotifyGive(LEDTask_Handle);
if (xReturn == pdTRUE)
printf("notification has been sent, %d times.\r\n", NumCount);
}
edge_fall = 0;
edge_rise = 0;
(void)edge_rise;
vTaskDelay(20);
}
}
本例程思路(代替计数信号量):
创建两个任务,一个任务用于释放信号量,另一个任务用于获取信号量,当发送任务中检测到KEY_GPIO(PA0)出现一次下降沿时释放一个信号量,接收任务只要检测到存在信号量就一直获取
/**
******************************************************************************
* @文件名: xxx.c
* @作 者: author
* @版 本: v1.0.0
* @日 期: 2021.12.27
* @简 介: 任务通知代替计数信号量
* @注 意: 无
******************************************************************************
*/
#include "stm32f10x.h"
#include "dr_usart.h"
#include "dr_led.h"
#include "dr_key.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "event_groups.h"
#include "limits.h"
void AppTaskCreateTask(void *);
void LEDTask(void *);
void KEYTask(void *);
/* --- 任务 --- */
TaskHandle_t LEDTask_Handle = NULL;
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
int main(void)
{
/* --------- --------- --------- --------- --------- --------- --------- */
/* 中断分组4 */
NVIC_Priority_Group_Config(); /* * */
/* 串口1配置 */
USART1_Config(); /* * */
/* LED(PA8, PA6)配置 */
LED_GPIO_Config(); /* * */
/* KEY(PA0)配置 */
KEY_GPIO_Config();
/* --------- --------- --------- --------- --------- --------- --------- */
/* 创建任务 */
xTaskCreate(AppTaskCreateTask, "AppTaskCreateTask", 128, NULL, 1, NULL);
/* 开启任务调度 */
vTaskStartScheduler();
while(1);
}
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
void AppTaskCreateTask(void *pvParameters)
{
/* 临界区 */
taskENTER_CRITICAL();
/* 创建任务 */
xTaskCreate(LEDTask, "LEDTask", 128, NULL, 2, &LEDTask_Handle);
xTaskCreate(KEYTask, "KEYTask", 128, NULL, 3, NULL);
vTaskDelete(NULL);
taskEXIT_CRITICAL();
}
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
void LEDTask(void *pvParameters)
{
uint32_t xReturn = 0;
for (;;)
{
/* pdFALSE: 退出减一, pdTRUE: 退出清零 */
/* 等待通知, 退出减一 等待超时时间 */
xReturn = ulTaskNotifyTake(pdFALSE, 0);
printf("there are %d values.\r\n", xReturn);
GPIOA->ODR ^= ((uint16_t)0x0100); /* 翻转LED(PA8) */
// printf("LED %s\r\n", ((GPIOA->IDR & 0x0100) != 0) ? "is running." : "stops running.");
vTaskDelay(3000);
}
}
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
void KEYTask(void *pvParameters)
{
uint8_t level_high = 0;
uint8_t level_low = 0;
uint8_t edge_fall = 0;
uint8_t edge_rise = 0;
BaseType_t xReturn = pdFALSE;
for (;;)
{
/* 高电平/上升沿检测 */
if ( (GPIOA->IDR & 0x0001) != 0 )
{
if (level_low == 1) edge_rise = 1;
level_low = 0; level_high = 1;
}
/* 低电平/下降沿检测 */
if ( (GPIOA->IDR & 0x0001) == 0 )
{
if (level_high == 1) edge_fall = 1;
level_high = 0; level_low = 1;
}
if (edge_fall) /* 出现下降沿 */
{
/* 任务通知, 任务句柄 */
xReturn = xTaskNotifyGive(LEDTask_Handle);
if (xReturn == pdTRUE)
printf("released.\r\n");
}
edge_fall = 0;
edge_rise = 0;
(void)edge_rise;
vTaskDelay(20);
}
}
可以使用任务通知代替事件组,使用 xTaskNotify 标记事件,调用 xTaskNotifyWait 获取事件
本例程思路(代替事件组):
创建两个任务,一个任务用于标记事件,另一个任务用于获取事件,当发送任务中检测到KEY_GPIO(PA0)出现第一次下降沿时标记事件1,第二个下降沿标记事件2,第三个下降沿清除所有事件标记,接收任务检测到事件后打印提示,但不清除事件位(可以通过设置清除)
/**
******************************************************************************
* @文件名: xxx.c
* @作 者: author
* @版 本: v1.0.0
* @日 期: 2021.12.27
* @简 介: 任务通知代替事件组
* @注 意: 无
******************************************************************************
*/
#include "stm32f10x.h"
#include "dr_usart.h"
#include "dr_led.h"
#include "dr_key.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "event_groups.h"
#include "limits.h"
void AppTaskCreateTask(void *);
void LEDTask(void *);
void KEYTask(void *);
/* --- 任务 --- */
TaskHandle_t LEDTask_Handle = NULL;
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
int main(void)
{
/* --------- --------- --------- --------- --------- --------- --------- */
/* 中断分组4 */
NVIC_Priority_Group_Config(); /* * */
/* 串口1配置 */
USART1_Config(); /* * */
/* LED(PA8, PA6)配置 */
LED_GPIO_Config(); /* * */
/* KEY(PA0)配置 */
KEY_GPIO_Config();
/* --------- --------- --------- --------- --------- --------- --------- */
/* 创建任务 */
xTaskCreate(AppTaskCreateTask, "AppTaskCreateTask", 128, NULL, 1, NULL);
/* 开启任务调度 */
vTaskStartScheduler();
while(1);
}
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
void AppTaskCreateTask(void *pvParameters)
{
/* 临界区 */
taskENTER_CRITICAL();
/* 创建任务 */
xTaskCreate(LEDTask, "LEDTask", 128, NULL, 2, &LEDTask_Handle);
xTaskCreate(KEYTask, "KEYTask", 128, NULL, 3, NULL);
vTaskDelete(NULL);
taskEXIT_CRITICAL();
}
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
void LEDTask(void *pvParameters)
{
uint32_t _event = 0;
for (;;)
{
/* 等待通知, 进入是否清除 退出是否清除 事件 等待超时时间 */
xTaskNotifyWait(0, 0, &_event, portMAX_DELAY);
if (_event == 0)
printf("obtained, default.\r\n");
if (_event & 0x0001)
printf("obtained, _event1.\r\n");
if (_event & 0x0002)
printf("obtained, _event2.\r\n");
GPIOA->ODR ^= ((uint16_t)0x0100); /* 翻转LED(PA8) */
// printf("LED %s\r\n", ((GPIOA->IDR & 0x0100) != 0) ? "is running." : "stops running.");
vTaskDelay(50);
}
}
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
void KEYTask(void *pvParameters)
{
uint8_t level_high = 0;
uint8_t level_low = 0;
uint8_t edge_fall = 0;
uint8_t edge_rise = 0;
uint8_t _switch = 0;
BaseType_t xReturn = pdFALSE;
for (;;)
{
/* 高电平/上升沿检测 */
if ( (GPIOA->IDR & 0x0001) != 0 )
{
if (level_low == 1) edge_rise = 1;
level_low = 0; level_high = 1;
}
/* 低电平/下降沿检测 */
if ( (GPIOA->IDR & 0x0001) == 0 )
{
if (level_high == 1) edge_fall = 1;
level_high = 0; level_low = 1;
}
if (edge_fall) /* 出现下降沿 */
{
if (_switch == 0)
{
_switch = 1;
/* 任务通知, 任务句柄 0x0001 置位 */
xReturn = xTaskNotify(LEDTask_Handle, 0x0001, eSetBits);
if (xReturn == pdTRUE)
printf("set1 successful.\r\n");
}
else if (_switch == 1)
{
_switch = 2;
/* 任务通知, 任务句柄 0x0002 置位 */
xReturn = xTaskNotify(LEDTask_Handle, 0x0002, eSetBits);
if (xReturn == pdTRUE)
printf("set2 successful.\r\n");
}
else
{
if(_switch == 2)
{
_switch = 0;
/* 任务通知, 任务句柄 0 覆盖 */
xReturn = xTaskNotify(LEDTask_Handle, 0, eSetValueWithOverwrite);
if (xReturn == pdTRUE)
printf("set defualt.\r\n");
}
}
}
edge_fall = 0;
edge_rise = 0;
(void)edge_rise;
vTaskDelay(20);
}
}
3.使用串口观察输出信息
代替消息队列
代替二值信号量(一个释放信号量,一个获取信号量,此处计数代表进行释放/获取的次数)
代替计数信号量(一个任务释放信号量,一个任务获取信号量)
代替事件组(获取到对应事件后打印提示信息,因第二次获取到事件时并未清除第一次获取的事件位,所以会同时打印出 _event1 和 _event2)
经验证,程序运行结果与预想一致
版权声明:本文为CSDN博主「SSS&10&01」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_45691873/article/details/122201629
FreeRTOS复习笔记(六) —— 任务通知
1.本例程思路:
创建两个任务,一个任务用于发送通知,另一个任务用于接收通知,当发送任务中检测到KEY_GPIO(PA0)出现下降沿时发送通知,接收任务接收到通知消息后打印输出
任务通知无需创建,直接调用函数 xTaskNotify 发送消息,xTaskNotifyWait 用于接收消息
2.代码编写
/**
******************************************************************************
* @文件名: xxx.c
* @作 者: author
* @版 本: v1.0.0
* @日 期: 2021.12.27
* @简 介: 任务通知代替消息队列
* @注 意: 无
******************************************************************************
*/
#include "stm32f10x.h"
#include "dr_usart.h"
#include "dr_led.h"
#include "dr_key.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "event_groups.h"
#include "limits.h"
void AppTaskCreateTask(void *);
void LEDTask(void *);
void KEYTask(void *);
/* --- 任务 --- */
TaskHandle_t LEDTask_Handle = NULL;
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
int main(void)
{
/* --------- --------- --------- --------- --------- --------- --------- */
/* 中断分组4 */
NVIC_Priority_Group_Config(); /* * */
/* 串口1配置 */
USART1_Config(); /* * */
/* LED(PA8, PA6)配置 */
LED_GPIO_Config(); /* * */
/* KEY(PA0)配置 */
KEY_GPIO_Config();
/* --------- --------- --------- --------- --------- --------- --------- */
/* 创建任务 */
xTaskCreate(AppTaskCreateTask, "AppTaskCreateTask", 128, NULL, 1, NULL);
/* 开启任务调度 */
vTaskStartScheduler();
while(1);
}
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
void AppTaskCreateTask(void *pvParameters)
{
/* 临界区 */
taskENTER_CRITICAL();
/* 创建任务 */
xTaskCreate(LEDTask, "LEDTask", 128, NULL, 2, &LEDTask_Handle);
xTaskCreate(KEYTask, "KEYTask", 128, NULL, 3, NULL);
vTaskDelete(NULL);
taskEXIT_CRITICAL();
}
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
void LEDTask(void *pvParameters)
{
uint32_t rev1;
BaseType_t xReturn = pdFALSE;
for (;;)
{
/* 清除位置1, 否则清0 */
/* 等待通知, 进入时不清除位 退出时清除位 接收数据 等待超时时间 */
xReturn = xTaskNotifyWait(0, ULONG_MAX, &rev1, portMAX_DELAY);
if (xReturn == pdTRUE)
printf("obtained, %x.\r\n", rev1);
GPIOA->ODR ^= ((uint16_t)0x0100); /* 翻转LED(PA8) */
// printf("LED %s\r\n", ((GPIOA->IDR & 0x0100) != 0) ? "is running." : "stops running.");
vTaskDelay(50);
}
}
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
void KEYTask(void *pvParameters)
{
uint8_t level_high = 0;
uint8_t level_low = 0;
uint8_t edge_fall = 0;
uint8_t edge_rise = 0;
uint8_t _switch = 0;
uint32_t data1 = 0x404;
uint32_t data2 = 0x5a5;
BaseType_t xReturn = pdFALSE;
for (;;)
{
/* 高电平/上升沿检测 */
if ( (GPIOA->IDR & 0x0001) != 0 )
{
if (level_low == 1) edge_rise = 1;
level_low = 0; level_high = 1;
}
/* 低电平/下降沿检测 */
if ( (GPIOA->IDR & 0x0001) == 0 )
{
if (level_high == 1) edge_fall = 1;
level_high = 0; level_low = 1;
}
if (edge_fall) /* 出现下降沿 */
{
if (_switch == 0)
{
_switch = 1;
/**
eNoAction, 通知任务, 不更新通知值
eSetBits, 设置通知值中的位 --- 事件组
eIncrement, 增加通知值 --- 信号量
eSetValueWithOverwrite, 直接更新通知值 --- 消息队列
eSetValueWithoutOverwrite 若前一个通知值被读取才更新通知值 --- 消息队列
*/
/* 任务通知, 任务句柄 待发送数据 内容覆盖 */
xReturn = xTaskNotify(LEDTask_Handle, data1, eSetValueWithOverwrite);
if (xReturn == pdTRUE)
printf("data1 sent successful.\r\n");
}
else
{
if (_switch == 1)
{
_switch = 0;
/* 任务通知, 任务句柄 待发送数据 内容覆盖 */
xReturn = xTaskNotify(LEDTask_Handle, data2, eSetValueWithOverwrite);
if (xReturn == pdTRUE)
printf("data2 sent successful.\r\n");
}
}
}
edge_fall = 0;
edge_rise = 0;
(void)edge_rise;
vTaskDelay(20);
}
}
上述例程为将任务通知代替消息队列使用,还可以使用其代替信号量、事件组。代替信号量时可以直接调用函数 xTaskNotifyGive 释放信号量,使用 ulTaskNotifyTake 获取信号量
本例程思路(代替二值信号量):
创建两个任务,一个任务用于释放信号量,另一个任务用于获取信号量,当发送任务中检测到KEY_GPIO(PA0)出现下降沿时释放信号量,接收任务接收到信号量后打印输出
/**
******************************************************************************
* @文件名: xxx.c
* @作 者: author
* @版 本: v1.0.0
* @日 期: 2021.12.27
* @简 介: 任务通知代替二值信号量
* @注 意: 无
******************************************************************************
*/
#include "stm32f10x.h"
#include "dr_usart.h"
#include "dr_led.h"
#include "dr_key.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "event_groups.h"
#include "limits.h"
void AppTaskCreateTask(void *);
void LEDTask(void *);
void KEYTask(void *);
/* --- 任务 --- */
TaskHandle_t LEDTask_Handle = NULL;
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
int main(void)
{
/* --------- --------- --------- --------- --------- --------- --------- */
/* 中断分组4 */
NVIC_Priority_Group_Config(); /* * */
/* 串口1配置 */
USART1_Config(); /* * */
/* LED(PA8, PA6)配置 */
LED_GPIO_Config(); /* * */
/* KEY(PA0)配置 */
KEY_GPIO_Config();
/* --------- --------- --------- --------- --------- --------- --------- */
/* 创建任务 */
xTaskCreate(AppTaskCreateTask, "AppTaskCreateTask", 128, NULL, 1, NULL);
/* 开启任务调度 */
vTaskStartScheduler();
while(1);
}
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
void AppTaskCreateTask(void *pvParameters)
{
/* 临界区 */
taskENTER_CRITICAL();
/* 创建任务 */
xTaskCreate(LEDTask, "LEDTask", 128, NULL, 2, &LEDTask_Handle);
xTaskCreate(KEYTask, "KEYTask", 128, NULL, 3, NULL);
vTaskDelete(NULL);
taskEXIT_CRITICAL();
}
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
void LEDTask(void *pvParameters)
{
uint32_t NumCount = 0;
for (;;)
{
/* pdTRUE: 退出清零, pdFALSE: 退出减一 */
/* 等待通知, 退出清零 等待超时时间 */
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
printf("obtained, %d times.\r\n", ++NumCount);
GPIOA->ODR ^= ((uint16_t)0x0100); /* 翻转LED(PA8) */
// printf("LED %s\r\n", ((GPIOA->IDR & 0x0100) != 0) ? "is running." : "stops running.");
vTaskDelay(50);
}
}
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
void KEYTask(void *pvParameters)
{
uint8_t level_high = 0;
uint8_t level_low = 0;
uint8_t edge_fall = 0;
uint8_t edge_rise = 0;
uint32_t NumCount = 0;
BaseType_t xReturn = pdFALSE;
for (;;)
{
/* 高电平/上升沿检测 */
if ( (GPIOA->IDR & 0x0001) != 0 )
{
if (level_low == 1) edge_rise = 1;
level_low = 0; level_high = 1;
}
/* 低电平/下降沿检测 */
if ( (GPIOA->IDR & 0x0001) == 0 )
{
if (level_high == 1) edge_fall = 1;
level_high = 0; level_low = 1;
}
if (edge_fall) /* 出现下降沿 */
{
NumCount++;
/* 任务通知, 任务句柄 */
xReturn = xTaskNotifyGive(LEDTask_Handle);
if (xReturn == pdTRUE)
printf("notification has been sent, %d times.\r\n", NumCount);
}
edge_fall = 0;
edge_rise = 0;
(void)edge_rise;
vTaskDelay(20);
}
}
本例程思路(代替计数信号量):
创建两个任务,一个任务用于释放信号量,另一个任务用于获取信号量,当发送任务中检测到KEY_GPIO(PA0)出现一次下降沿时释放一个信号量,接收任务只要检测到存在信号量就一直获取
/**
******************************************************************************
* @文件名: xxx.c
* @作 者: author
* @版 本: v1.0.0
* @日 期: 2021.12.27
* @简 介: 任务通知代替计数信号量
* @注 意: 无
******************************************************************************
*/
#include "stm32f10x.h"
#include "dr_usart.h"
#include "dr_led.h"
#include "dr_key.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "event_groups.h"
#include "limits.h"
void AppTaskCreateTask(void *);
void LEDTask(void *);
void KEYTask(void *);
/* --- 任务 --- */
TaskHandle_t LEDTask_Handle = NULL;
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
int main(void)
{
/* --------- --------- --------- --------- --------- --------- --------- */
/* 中断分组4 */
NVIC_Priority_Group_Config(); /* * */
/* 串口1配置 */
USART1_Config(); /* * */
/* LED(PA8, PA6)配置 */
LED_GPIO_Config(); /* * */
/* KEY(PA0)配置 */
KEY_GPIO_Config();
/* --------- --------- --------- --------- --------- --------- --------- */
/* 创建任务 */
xTaskCreate(AppTaskCreateTask, "AppTaskCreateTask", 128, NULL, 1, NULL);
/* 开启任务调度 */
vTaskStartScheduler();
while(1);
}
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
void AppTaskCreateTask(void *pvParameters)
{
/* 临界区 */
taskENTER_CRITICAL();
/* 创建任务 */
xTaskCreate(LEDTask, "LEDTask", 128, NULL, 2, &LEDTask_Handle);
xTaskCreate(KEYTask, "KEYTask", 128, NULL, 3, NULL);
vTaskDelete(NULL);
taskEXIT_CRITICAL();
}
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
void LEDTask(void *pvParameters)
{
uint32_t xReturn = 0;
for (;;)
{
/* pdFALSE: 退出减一, pdTRUE: 退出清零 */
/* 等待通知, 退出减一 等待超时时间 */
xReturn = ulTaskNotifyTake(pdFALSE, 0);
printf("there are %d values.\r\n", xReturn);
GPIOA->ODR ^= ((uint16_t)0x0100); /* 翻转LED(PA8) */
// printf("LED %s\r\n", ((GPIOA->IDR & 0x0100) != 0) ? "is running." : "stops running.");
vTaskDelay(3000);
}
}
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
void KEYTask(void *pvParameters)
{
uint8_t level_high = 0;
uint8_t level_low = 0;
uint8_t edge_fall = 0;
uint8_t edge_rise = 0;
BaseType_t xReturn = pdFALSE;
for (;;)
{
/* 高电平/上升沿检测 */
if ( (GPIOA->IDR & 0x0001) != 0 )
{
if (level_low == 1) edge_rise = 1;
level_low = 0; level_high = 1;
}
/* 低电平/下降沿检测 */
if ( (GPIOA->IDR & 0x0001) == 0 )
{
if (level_high == 1) edge_fall = 1;
level_high = 0; level_low = 1;
}
if (edge_fall) /* 出现下降沿 */
{
/* 任务通知, 任务句柄 */
xReturn = xTaskNotifyGive(LEDTask_Handle);
if (xReturn == pdTRUE)
printf("released.\r\n");
}
edge_fall = 0;
edge_rise = 0;
(void)edge_rise;
vTaskDelay(20);
}
}
可以使用任务通知代替事件组,使用 xTaskNotify 标记事件,调用 xTaskNotifyWait 获取事件
本例程思路(代替事件组):
创建两个任务,一个任务用于标记事件,另一个任务用于获取事件,当发送任务中检测到KEY_GPIO(PA0)出现第一次下降沿时标记事件1,第二个下降沿标记事件2,第三个下降沿清除所有事件标记,接收任务检测到事件后打印提示,但不清除事件位(可以通过设置清除)
/**
******************************************************************************
* @文件名: xxx.c
* @作 者: author
* @版 本: v1.0.0
* @日 期: 2021.12.27
* @简 介: 任务通知代替事件组
* @注 意: 无
******************************************************************************
*/
#include "stm32f10x.h"
#include "dr_usart.h"
#include "dr_led.h"
#include "dr_key.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "event_groups.h"
#include "limits.h"
void AppTaskCreateTask(void *);
void LEDTask(void *);
void KEYTask(void *);
/* --- 任务 --- */
TaskHandle_t LEDTask_Handle = NULL;
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
int main(void)
{
/* --------- --------- --------- --------- --------- --------- --------- */
/* 中断分组4 */
NVIC_Priority_Group_Config(); /* * */
/* 串口1配置 */
USART1_Config(); /* * */
/* LED(PA8, PA6)配置 */
LED_GPIO_Config(); /* * */
/* KEY(PA0)配置 */
KEY_GPIO_Config();
/* --------- --------- --------- --------- --------- --------- --------- */
/* 创建任务 */
xTaskCreate(AppTaskCreateTask, "AppTaskCreateTask", 128, NULL, 1, NULL);
/* 开启任务调度 */
vTaskStartScheduler();
while(1);
}
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
void AppTaskCreateTask(void *pvParameters)
{
/* 临界区 */
taskENTER_CRITICAL();
/* 创建任务 */
xTaskCreate(LEDTask, "LEDTask", 128, NULL, 2, &LEDTask_Handle);
xTaskCreate(KEYTask, "KEYTask", 128, NULL, 3, NULL);
vTaskDelete(NULL);
taskEXIT_CRITICAL();
}
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
void LEDTask(void *pvParameters)
{
uint32_t _event = 0;
for (;;)
{
/* 等待通知, 进入是否清除 退出是否清除 事件 等待超时时间 */
xTaskNotifyWait(0, 0, &_event, portMAX_DELAY);
if (_event == 0)
printf("obtained, default.\r\n");
if (_event & 0x0001)
printf("obtained, _event1.\r\n");
if (_event & 0x0002)
printf("obtained, _event2.\r\n");
GPIOA->ODR ^= ((uint16_t)0x0100); /* 翻转LED(PA8) */
// printf("LED %s\r\n", ((GPIOA->IDR & 0x0100) != 0) ? "is running." : "stops running.");
vTaskDelay(50);
}
}
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
void KEYTask(void *pvParameters)
{
uint8_t level_high = 0;
uint8_t level_low = 0;
uint8_t edge_fall = 0;
uint8_t edge_rise = 0;
uint8_t _switch = 0;
BaseType_t xReturn = pdFALSE;
for (;;)
{
/* 高电平/上升沿检测 */
if ( (GPIOA->IDR & 0x0001) != 0 )
{
if (level_low == 1) edge_rise = 1;
level_low = 0; level_high = 1;
}
/* 低电平/下降沿检测 */
if ( (GPIOA->IDR & 0x0001) == 0 )
{
if (level_high == 1) edge_fall = 1;
level_high = 0; level_low = 1;
}
if (edge_fall) /* 出现下降沿 */
{
if (_switch == 0)
{
_switch = 1;
/* 任务通知, 任务句柄 0x0001 置位 */
xReturn = xTaskNotify(LEDTask_Handle, 0x0001, eSetBits);
if (xReturn == pdTRUE)
printf("set1 successful.\r\n");
}
else if (_switch == 1)
{
_switch = 2;
/* 任务通知, 任务句柄 0x0002 置位 */
xReturn = xTaskNotify(LEDTask_Handle, 0x0002, eSetBits);
if (xReturn == pdTRUE)
printf("set2 successful.\r\n");
}
else
{
if(_switch == 2)
{
_switch = 0;
/* 任务通知, 任务句柄 0 覆盖 */
xReturn = xTaskNotify(LEDTask_Handle, 0, eSetValueWithOverwrite);
if (xReturn == pdTRUE)
printf("set defualt.\r\n");
}
}
}
edge_fall = 0;
edge_rise = 0;
(void)edge_rise;
vTaskDelay(20);
}
}
3.使用串口观察输出信息
代替消息队列
代替二值信号量(一个释放信号量,一个获取信号量,此处计数代表进行释放/获取的次数)
代替计数信号量(一个任务释放信号量,一个任务获取信号量)
代替事件组(获取到对应事件后打印提示信息,因第二次获取到事件时并未清除第一次获取的事件位,所以会同时打印出 _event1 和 _event2)
经验证,程序运行结果与预想一致
版权声明:本文为CSDN博主「SSS&10&01」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_45691873/article/details/122201629
暂无评论