目录
一、任务的挂起与恢复的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