FreeRTOS复习笔记(二) —— 任务
一、创建任务
1.本例程思路:
1). 调用 xTaskCreateStatic 函数静态创建任务 LED_Task;
LED_Task 控制 LED 每间隔一段时间翻转一次
2). 调用 xTaskCreate 函数动态创建任务 AppTaskCreate_Task;
AppTaskCreate_Task 动态创建任务 LED_Task 与 LED2_Task;
LED_Task 与 LED2_Task 分别控制 LED 与 LED2 每隔一段时间翻转一次
本篇将静态创建与动态创建写入同一段代码,使用 _USE_STATIC_MODE 与 _USE_DYNAMIC_MODE 进行区分
其中,
动态创建方式,初始化相关资源(中断, GPIO等)后,直接调用任务创建函数 xTaskCreate 创建任务,随后开启任务调度器即可(任务函数执行的内容自行实现);
而静态创建方式,需要手动开辟存储空间(任务堆栈, 任务控制块,包括IdleTask与TimerTask),随后可以直接调用 xTaskCreateStatic 函数创建任务,调用 vTaskStartScheduler 函数开启任务调度器
启动任务调度器后,任务调度器会自动创建一个IdleTask任务,以保证始终能有一个任务在运行,所以使用静态方式创建任务时需要手动为IdleTask分配空间(实现 vApplicationGetIdleTaskMemory 函数)
2.代码编写
/**
******************************************************************************
* @文件名: xxx.c
* @作 者: author
* @版 本: v1.0.0
* @日 期: 2021.12.21
* @简 介: 无
* @注 意: 无
******************************************************************************
*/
#include "stm32f10x.h"
#include "dr_usart.h"
#include "dr_led.h"
#include "FreeRTOS.h"
#include "task.h"
/**
* #define _USE_STATIC_MODE //使用[静态]方式创建任务
* #define _USE_DYNAMIC_MODE //使用[动态]方式创建任务
*/
#if defined(_USE_STATIC_MODE)
#define USER_DEFAULT_STACK_SIZE ((unsigned short)128) //任务堆栈大小
/* IdleTask 任务堆栈 任务控制块 */
StackType_t Idle_Task_Stack[USER_DEFAULT_STACK_SIZE]; //
StaticTask_t Idle_Task_TCB; //
/* TimerTask */
StackType_t Timer_Task_Stack[USER_DEFAULT_STACK_SIZE]; //
StaticTask_t Timer_Task_TCB; //
/* LED_Task 任务堆栈 任务控制块 任务句柄 任务函数 */
StackType_t LED_Task_Stack[USER_DEFAULT_STACK_SIZE]; //
StaticTask_t LED_Task_TCB; //
TaskHandle_t LED_Task_Handle;
void LED_Task(void *);
#endif
#if defined(_USE_DYNAMIC_MODE)
/* 任务函数 */
void AppTaskCreate_Task(void *);
void LED_Task(void *);
void LED2_Task(void *);
#endif
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
int main(void)
{
/* --------- --------- --------- --------- --------- --------- --------- */
/* 中断分组4 */
NVIC_Priority_Group_Config(); /* * */
/* 串口1配置 */
USART1_Config(); /* * */
/* LED(PA8, PA6)配置 */
LED_GPIO_Config(); /* * */
/* --------- --------- --------- --------- --------- --------- --------- */
/* 创建LED_Task */
#if defined(_USE_STATIC_MODE)
LED_Task_Handle = xTaskCreateStatic( LED_Task, //指向任务函数
"LED_Task", //任务名称
USER_DEFAULT_STACK_SIZE, //任务堆栈大小
(void *)NULL, 1, //任务参数, 任务优先级
LED_Task_Stack, //任务堆栈地址
&LED_Task_TCB ); //任务控制块地址
#endif
/* 创建AppTaskCreate_Task */
#if defined(_USE_DYNAMIC_MODE)
/* 任务函数 任务名称 堆栈大小 任务参数(无) 任务优先级 任务句柄(未用到) */
xTaskCreate(AppTaskCreate_Task, "AppTaskCreate_Task", 128, NULL, 1, NULL);
#endif
/* 开启任务调度 */
vTaskStartScheduler();
while(1);
}
#if defined(_USE_DYNAMIC_MODE)
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
void AppTaskCreate_Task(void *pvParameters)
{
/* 进入临界 */
taskENTER_CRITICAL();
/* 创建LED_Task */
xTaskCreate(LED_Task, "LED_Task", 128, NULL, 2, NULL);
/* 创建LED2_Task */
xTaskCreate(LED2_Task, "LED2_Task", 128, NULL, 3, NULL);
/* 删除AppTaskCreate_Task */
vTaskDelete(NULL);
/* 退出临界 */
taskEXIT_CRITICAL();
}
#endif
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
void LED_Task(void *pvParameters)
{
for(;;)
{
/* 翻转LED */
GPIOA->ODR ^= ((uint16_t)0x0100);
/* 任务切换 */
vTaskDelay(50);
}
}
#if defined(_USE_DYNAMIC_MODE)
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
void LED2_Task(void *pvParameters)
{
for(;;)
{
/* 翻转LED2 */
GPIOA->ODR ^= ((uint16_t)0x0040);
/* 任务切换 */
vTaskDelay(50);
}
}
#endif
#if defined(_USE_STATIC_MODE)
/**
* @简 介: 分配IdleTask空间
* @参 数: TCB Stack StackSize
* @返回值: 无
*/
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer,
StackType_t **ppxIdleTaskStackBuffer,
uint32_t *pulIdleTaskStackSize )
{
*ppxIdleTaskTCBBuffer = &Idle_Task_TCB; //指向任务控制块
*ppxIdleTaskStackBuffer = Idle_Task_Stack; //指向任务堆栈
*pulIdleTaskStackSize = USER_DEFAULT_STACK_SIZE; //任务堆栈大小
}
/**
* @简 介: 分配TimerTask空间
* @参 数: TCB Stack StackSize
* @返回值: 无
*/
void vApplicationGetTimerTaskMemory( StaticTask_t **ppxTimerTaskTCBBuffer,
StackType_t **ppxTimerTaskStackBuffer,
uint32_t *pulTimerTaskStackSize )
{
*ppxTimerTaskTCBBuffer = &Timer_Task_TCB;
*ppxTimerTaskStackBuffer = Timer_Task_Stack;
*pulTimerTaskStackSize = USER_DEFAULT_STACK_SIZE;
}
#endif
3.调用逻辑分析仪观察(动态时,两个LED应同时翻转)
静态创建(任务:LED_Task)
动态创建(任务:LED_Task,LED2_Task)
经验证,程序运行结果与预想一致
二、任务挂起与恢复
1.本例程思路:
分别创建两个任务,LEDTask 与 KEYTask,LEDTask 控制 LED 每隔一段时间翻转一次,KEYTask 检测 KEY_GPIO(PA0) 的状态,第一次出现下降沿时调用 vTaskSuspend 函数挂起 LEDTask,第二次出现下降时调用 vTaskResume 函数沿恢复 LEDTask,依次循环
2.代码编写
/**
******************************************************************************
* @文件名: xxx.c
* @作 者: author
* @版 本: v1.0.0
* @日 期: 2021.12.23
* @简 介: 无
* @注 意: 无
******************************************************************************
*/
#include "stm32f10x.h"
#include "dr_usart.h"
#include "dr_led.h"
#include "dr_key.h"
#include "FreeRTOS.h"
#include "task.h"
/* 任务句柄 */
TaskHandle_t LEDTask_Handle = NULL;
/* 任务函数 */
void AppTaskCreateTask(void *);
void LEDTask(void *);
void KEYTask(void *);
/**
* @简 介: 无
* @参 数: 无
* @返回值: 无
*/
int main(void)
{
/* --------- --------- --------- --------- --------- --------- --------- */
/* 中断分组4 */
NVIC_Priority_Group_Config(); /* * */
/* 串口1配置 */
USART1_Config(); /* * */
/* LED(PA8, PA6)配置 */
LED_GPIO_Config(); /* * */
/* KEY(PA0)配置 */
KEY_GPIO_Config();
/* --------- --------- --------- --------- --------- --------- --------- */
/* 创建任务AppTaskCreateTask */
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)
{
for (;;)
{
/* LED翻转 */
GPIOA->ODR ^= ((uint16_t)0x0100);
/* 信息打印:LED点亮, LED is running.; LED熄灭, LED stops running. */
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;
for (;;)
{
if ( (GPIOA->IDR & 0x0001) != 0 ) /* level_high */
{
if(level_low == 1)
edge_rise = 1; /* edge_rise */
level_low = 0;
level_high = 1;
} /* --- 高电平/上升沿检测 --- */
if ( (GPIOA->IDR & 0x0001) == 0 ) /* level_low */
{
if(level_high == 1)
edge_fall = 1; /* edge_fall */
level_high = 0;
level_low = 1;
} /* --- 低电平/下降沿检测 --- */
if(edge_fall)
{
if(_switch == 0) /* 第一次下降沿 */
{
_switch = 1;
printf("--- Suspend ---.\n");
/* 挂起LEDTask */
vTaskSuspend(LEDTask_Handle);
}
else
{
_switch = 0;
printf("--- Resume ---.\n");
/* 恢复LEDTask */
vTaskResume(LEDTask_Handle);
}
}
edge_fall = 0;
edge_rise = 0;
(void)edge_rise;
/* 任务切换 */
vTaskDelay(20);
}
}
3.调用逻辑分析仪观察
经验证,程序运行结果与预想一致