目录
一、任务的挂起与恢复的API函数
1.1、任务挂起函数介绍
1.2、任务恢复函数介绍(任务中恢复)
1.3、任务恢复函数介绍(中断中恢复)
二、任务挂起与恢复实验
一、任务的挂起与恢复的API函数
API函数 | 描述 |
vTaskSuspend() | 挂起任务 |
vTaskResume() | 恢复被挂起的任务 |
xTaskResumeFromISR() | 在中断中恢复被挂起的任务 |
挂起:挂起任务类似暂停,可恢复;删除任务,无法恢复
恢复:恢复被挂起的任务
FromISR:带 FromISR 后缀是在中断函数中专用的 API 函数
1.1、任务挂起函数介绍
void vTaskSuspend(TaskHandle_t xTaskToSuspend);
形参 | 描述 |
xTaskToSuspend | 待挂起任务的任务句柄 |
1、此函数用于挂起任务,使用时需将宏 INCLUDE_vTaskSuspend 配置为 1
2、当传入的参数为NULL,则代表挂起任务自身(当前正在运行的任务)
无论优先级如何,被挂起的任务都将不再被执行,直到任务被恢复
1.2、任务恢复函数介绍(任务中恢复)
void vTaskResume(TaskHandle_t xTaskToResume);
形参 | 描述 |
xTaskToResume | 待恢复任务的任务句柄 |
使用该函数注意宏:INCLUDE_vTaskSuspend 必须定义为 1
任务无论被 vTaskSuspend() 挂起多少次,只需在任务中调用 vTaskResume() 恢复一次,就可以继续运行。且被恢复的任务会进入就绪态!
1.3、任务恢复函数介绍(中断中恢复)
该函数专用于中断服务函数中,用于解挂被挂起任务
BaseType_t xTaskResumeFromISR(TaskHandle_t xTaskToResume);
形参 | 描述 |
xTaskToResume | 待恢复任务的任务句柄 |
函数:xTaskResumeFromISR 返回值描述如下:
返回值 | 描述 |
pdTRUE | 任务恢复后需要进行任务切换 |
pdFALSE | 任务恢复后不需要进行任务切换 |
使用该函数注意宏:INCLUDE_vTaskSuspend 和 INCLUDE_xTaskResumeFromISR 必须定义为 1
注意:中断服务程序中要调用 FreeRTOS 的 API 函数则中断优先级不能高于 FreeRTOS 所管理的最高优先级
二、任务挂起与恢复实验
将设计四个任务:start_task、task1、task2、task3
四个任务的功能如下
start_task:用来创建其他三个任务
task1:实现 LED0 每 500ms 闪烁一次
task2:实现 LED1 每 500ms 闪烁一次
task3:判断按键按下逻辑,KEY0 按下,挂起 task1,按下 KEY1 在任务中恢复 task1,按下 WKUP,在中断中恢复 task1(外部中断线实现)
这里需要设置中断优先级分组,将所有优先级位分配为抢占优先级位
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
任何其他配置都会使 configMAX_SYSCALL_INTERRUPT_PRIORITY 设置与分配给各个外设中断的优先级之间的直接关系复杂化
main.c
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./BSP/EXTI/exti.h"
#include "freertos_demo.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */
delay_init(168); /* 延时初始化 */
usart_init(115200); /* 串口初始化为115200 */
led_init(); /* 初始化LED */
lcd_init(); /* 初始化LCD */
key_init(); /* 初始化按键 */
extix_init(); /* 外部中断初始化 */
freertos_demo();
}
freertos_demo.c
#include "freertos_demo.h"
/******************************************************************************************************/
/*FreeRTOS配置*/
/* START_TASK 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define START_TASK_PRIO 1 /* 任务优先级 */
#define START_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t StartTask_Handler; /* 任务句柄 */
void start_task(void *pvParameters); /* 任务函数 */
/* TASK1 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK1_PRIO 2 /* 任务优先级 */
#define TASK1_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t Task1Task_Handler; /* 任务句柄 */
void task1(void *pvParameters); /* 任务函数 */
/* TASK2 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK2_PRIO 3 /* 任务优先级 */
#define TASK2_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t Task2Task_Handler; /* 任务句柄 */
void task2(void *pvParameters); /* 任务函数 */
/* TASK3 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK3_PRIO 4 /* 任务优先级 */
#define TASK3_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t Task3Task_Handler; /* 任务句柄 */
void task3(void *pvParameters); /* 任务函数 */
/******************************************************************************************************/
/* LCD刷屏时使用的颜色 */
uint16_t lcd_discolor[11] = {WHITE, BLACK, BLUE, RED,
MAGENTA, GREEN, CYAN, YELLOW,
BROWN, BRRED, GRAY};
/* FreeRTOS例程入口函数 */
void freertos_demo(void)
{
lcd_show_string(10, 10, 220, 32, 32, "STM32", RED);
lcd_show_string(10, 47, 220, 24, 24, "Task Create & Del", RED);
lcd_show_string(10, 76, 220, 16, 16, "ATOM@ALIENTEK", RED);
lcd_draw_rectangle(5, 110, 115, 314, BLACK);
lcd_draw_rectangle(125, 110, 234, 314, BLACK);
lcd_draw_line(5, 130, 115, 130, BLACK);
lcd_draw_line(125, 130, 234, 130, BLACK);
lcd_show_string(15, 111, 110, 16, 16, "Task1: 000", BLUE);
lcd_show_string(135, 111, 110, 16, 16, "Task2: 000", BLUE);
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();
}
/* start_task */
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); /* 进入临界区 */
/* 创建任务1 */
xTaskCreate((TaskFunction_t)task1, /* 任务函数 */
(const char *)"task1", /* 任务名称 */
(uint16_t)TASK1_STK_SIZE, /* 任务堆栈大小 */
(void *)NULL, /* 传入给任务函数的参数 */
(UBaseType_t)TASK1_PRIO, /* 任务优先级 */
(TaskHandle_t *)&Task1Task_Handler); /* 任务句柄 */
/* 创建任务2 */
xTaskCreate((TaskFunction_t)task2, /* 任务函数 */
(const char *)"task2", /* 任务名称 */
(uint16_t)TASK2_STK_SIZE, /* 任务堆栈大小 */
(void *)NULL, /* 传入给任务函数的参数 */
(UBaseType_t)TASK2_PRIO, /* 任务优先级 */
(TaskHandle_t *)&Task2Task_Handler); /* 任务句柄 */
/* 创建任务3 */
xTaskCreate((TaskFunction_t)task3, /* 任务函数 */
(const char *)"task3", /* 任务名称 */
(uint16_t)TASK3_STK_SIZE, /* 任务堆栈大小 */
(void *)NULL, /* 传入给任务函数的参数 */
(UBaseType_t)TASK3_PRIO, /* 任务优先级 */
(TaskHandle_t *)&Task3Task_Handler); /* 任务句柄 */
vTaskDelete(StartTask_Handler); /* 删除开始任务 */
taskEXIT_CRITICAL(); /* 退出临界区 */
}
/* task1 */
void task1(void *pvParameters)
{
uint32_t task1_num = 0;
while (1)
{
lcd_fill(6, 131, 114, 313, lcd_discolor[++task1_num % 11]);
lcd_show_xnum(71, 111, task1_num, 3, 16, 0x80, BLUE);
LED0_TOGGLE();
vTaskDelay(500);
}
}
/* task2 */
void task2(void *pvParameters)
{
uint32_t task2_num = 0;
while (1)
{
lcd_fill(126, 131, 233, 313, lcd_discolor[11 - (++task2_num % 11)]);
lcd_show_xnum(191, 111, task2_num, 3, 16, 0x80, BLUE);
LED1_TOGGLE();
vTaskDelay(500);
}
}
/* task3 */
void task3(void *pvParameters)
{
uint8_t key = 0;
while (1)
{
key = key_scan(0);
switch (key)
{
case KEY0_PRES: /* 挂起任务1 */
{
vTaskSuspend(Task1Task_Handler);
break;
}
case KEY1_PRES: /* 恢复任务1 */
{
vTaskResume(Task1Task_Handler);
break;
}
default:
{
break;
}
}
vTaskDelay(10);
}
}
freertos_demo.h
#ifndef __FREERTOS_DEMO_H
#define __FREERTOS_DEMO_H
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/KEY/key.h"
#include "./BSP/LCD/lcd.h"
#include "FreeRTOS.h"
#include "task.h"
void freertos_demo(void);
#endif
exti.c
#include "./BSP/EXTI/exti.h"
/* task1任务句柄 */
extern TaskHandle_t Task1Task_Handler;
/* 外部中断初始化程序 */
void extix_init(void)
{
GPIO_InitTypeDef gpio_init_struct;
key_init();
gpio_init_struct.Pin = WKUP_GPIO_PIN;
gpio_init_struct.Mode = GPIO_MODE_IT_RISING; /* 上升沿触发 */
gpio_init_struct.Pull = GPIO_PULLDOWN;
HAL_GPIO_Init(WKUP_GPIO_PORT, &gpio_init_struct); /* WKUP配置为上升沿触发中断 */
HAL_NVIC_SetPriority(EXTI0_IRQn, 5, 0); /* 抢占5,子优先级0 */
HAL_NVIC_EnableIRQ(EXTI0_IRQn); /* 使能中断线0 */
}
/* WK_UP 外部中断服务程序 */
void EXTI0_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(WKUP_GPIO_PIN); /* 调用中断处理公用函数 清除KEY_UP所在中断线 的中断标志位,中断下半部在HAL_GPIO_EXTI_Callback执行 */
__HAL_GPIO_EXTI_CLEAR_IT(WKUP_GPIO_PIN); /* HAL库默认先清中断再处理回调,退出时再清一次中断,避免按键抖动误触发 */
}
/* 中断服务程序中需要做的事情,在HAL库中所有的外部中断服务函数都会调用此函数 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
delay_ms(20); /* 消抖 */
switch (GPIO_Pin)
{
case WKUP_GPIO_PIN:
if (WK_UP == 1)
{
BaseType_t xYieldRequired;
xYieldRequired = xTaskResumeFromISR(Task1Task_Handler); /* 恢复任务1 */
if (xYieldRequired == pdTRUE) /* 恢复任务优先级高于当前任务优先级 */
portYIELD_FROM_ISR(xYieldRequired); /* 进行任务切换 */
}
break;
default:
break;
}
}
exti.h
#ifndef __EXTI_H
#define __EXTI_H
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/KEY/key.h"
#include "./BSP/LED/led.h"
#include "FreeRTOS.h"
#include "task.h"
void extix_init(void); /* 外部中断初始化 */
#endif