FreeRTOS 学习笔记(5)—— 任务管理实践

一、任务创建和删除实验(动态方法)

1. 实验设备

正点原子 Mini STM32F103RCT6 开发板。

2. 实验目的

学习 xTaskCreate() 和 vTaskDelete() 这两个 FreeRTOS 的任务创建和删除的 API 函数的使用。

3. 实验设计

本实验设计三个任务:Start_Task、Task1_Task 和 Task2_Task ,这三个任务的任务功能如下:

Start_Task:用来创建其他两个任务。

Task1_Task:当此任务运行 5 次以后就会调用函数 vTaskDelete() 删除任务 Task2_Task,此任务也会控制 LED0 的闪烁,并且周期性的刷新 LCD 指定区域的背景颜色。

Task2_Task:此任务普通的应用任务,此任务也会控制 LED1 的闪烁,并且周期性的刷新 LCD 指定区域的背景颜色。

4. 实验程序

(1)任务设置

#define START_TASK_PRIO  1           // 任务优先级
#define START_STK_SIZE   128         // 任务堆栈大小
TaskHandle_t StartTask_Handler;      // 任务句柄
void Start_Task(void *pvParameters); // 任务函数声明

#define TASK1_TASK_PRIO  2           // 任务优先级
#define TASK1_STK_SIZE   128         // 任务堆栈大小
TaskHandle_t Task1Task_Handler;      // 任务句柄
void Task1_Task(void *pvParameters);        // 任务函数声明

#define TASK2_TASK_PRIO  3           // 任务优先级
#define TASK2_STK_SIZE   128         // 任务堆栈大小
TaskHandle_t Task2Task_Handler;      // 任务句柄
void Task2_Task(void *pvParameters);        // 任务函数声明

// LCD 刷屏时使用的颜色
int LCD_DisColor[14] = {WHITE, BLACK, BLUE, BRED,
                        GRED, GBLUE, RED, MAGENTA,
                        GREEN, CYAN, YELLOW, BROWN,
                        BRRED, GRAY};

(2)main() 函数

int main(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); // 设置系统中断优先级分组
    delay_init();                                   // 延时函数初始化
    uart_init(115200);                              // 初始化串口
    LED_Init();                                     // 初始化LED
    LCD_Init();                                     // 初始化LCD
	
	POINT_COLOR = BLACK;               // 设置LCD显示颜色为黑色
    LCD_DrawRectangle(5, 5, 234, 106); // 画一个矩形
    POINT_COLOR = RED;                 // 设置LCD显示颜色为红色
    LCD_ShowString(75, 10, 200, 16, 16, (u8 *)"STM32F103RCT6");
    LCD_ShowString(60, 30, 200, 16, 16, (u8 *)"FreeRTOS Examp 3");
    LCD_ShowString(50, 50, 200, 16, 16, (u8 *)"Task Creat and Del");
    LCD_ShowString(90, 70, 200, 16, 16, (u8 *)"Xiao Xie");
    LCD_ShowString(80, 90, 200, 16, 16, (u8 *)"2023/07/12");

    // 创建开始任务
    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(); // 开启 FreeRTOS 的任务调度器,FreeRTOS 开始运行
}

(3)任务函数

// 开始任务函数
void Start_Task(void *pvParameters)
{
    taskENTER_CRITICAL(); // 进入临界区
    // 创建一个任务来执行开关中断的动作
    // 创建Task1任务
    xTaskCreate((TaskFunction_t)Task1_Task,
                (const char *)"Task1_Task",
                (uint16_t)TASK1_STK_SIZE,
                (void *)NULL,
                (UBaseType_t)TASK1_TASK_PRIO,
                (TaskHandle_t *)&Task1Task_Handler);

    // 创建Task2任务
    xTaskCreate((TaskFunction_t)Task2_Task,
                (const char *)"Task1_Task",
                (uint16_t)TASK2_STK_SIZE,
                (void *)NULL,
                (UBaseType_t)TASK2_TASK_PRIO,
                (TaskHandle_t *)&Task2Task_Handler);

    vTaskDelete(StartTask_Handler); // 删除开始任务
    taskEXIT_CRITICAL();            // 退出临界区
}

// Task1任务函数
void Task1_Task(void *pvParameters)
{
    u16 Task1_RunNum = 0;                // Task1任务运行次数
    POINT_COLOR = BLACK;                 // 设置LCD显示颜色为黑色
    LCD_DrawRectangle(5, 110, 115, 314); // 画一个矩形
    LCD_DrawLine(5, 130, 115, 130);      // 画线
    POINT_COLOR = BLUE;                  // 设置LCD显示颜色为蓝色
    LCD_ShowString(7, 111, 110, 16, 16, (u8 *)"Task1 Run:000");
    while (1)
    {
        ++Task1_RunNum; // Task1任务运行次数加1
        LED0 = !LED0;
        printf("Task1任务执行次数:%d次\r\n", Task1_RunNum);
        if (Task1_RunNum == 5)
        {
            vTaskDelete(Task2Task_Handler); // Task1任务执行5次删除任务Task2
            printf("Task1 Delete Task2!!!\r\n");
        }
        LCD_Fill(6, 131, 114, 313, LCD_DisColor[Task1_RunNum % 14]); // 填充区域
        LCD_ShowxNum(86, 111, Task1_RunNum, 3, 16, 0x80);            // 显示任务执行次数
        vTaskDelay(1000);                                            // 延时1s ==> 1000个时钟节拍
    }
}

// Task2任务函数
void Task2_Task(void *pvParameters)
{
    u8 Task2_RunNum = 0;                   // Task2任务运行次数
    POINT_COLOR = BLACK;                   // 设置LCD显示颜色为黑色
    LCD_DrawRectangle(125, 110, 234, 314); // 画一个矩形
    LCD_DrawLine(125, 130, 234, 130);      // 画线
    POINT_COLOR = BLUE;                    // 设置LCD显示颜色为蓝色
    LCD_ShowString(127, 111, 110, 16, 16, (u8 *)"Task2 Run:000");
    while (1)
    {
        ++Task2_RunNum; // Task2任务运行次数加1
        LED1 = !LED1;
        printf("Task2任务执行次数:%d次\r\n", Task2_RunNum);
        LCD_ShowxNum(206, 111, Task2_RunNum, 3, 16, 0x80);                  // 显示任务执行次数
        LCD_Fill(126, 131, 233, 313, LCD_DisColor[13 - Task2_RunNum % 14]); // 填充区域
        vTaskDelay(1000);                                                   // 延时1s ==> 1000个时钟节拍
    }
}

5. 实验程序运行结果分析

 FreeRTOS 学习笔记(5)—— 任务管理实践_第1张图片

从图中可以看出任务 2 运行了 5 次就停止,而任务 1 运行了 48 次。通过串口调试助手观察,一开始任务 1 和任务 2 是同时运行的,由于任务 2 的优先级比任务 1 的优先级高,所以任务 2 先输出信息。当任务 1 运行了 5 次以后任务 1 就删除了任务 2,最后只剩下了任务 1 在运行。

二、任务创建和删除实验(静态方法)

1. 实验设备

正点原子 Mini STM32F103RCT6 开发板。

2. 实验目的

学习 xTaskCreateStatic() 静态创建任务 API 函数的使用。

3. 实验设计

本实验设计与上述实验展示效果一致,不同点为任务的创建采用静态方法。

4. 实验程序

(1)系统设置

使用静态方法创建任务的时候需要将宏 configSUPPORT_STATIC_ALLOCATION 设置为 1,在文件 FreeRTOS.h 中设置,如下所示:

#define configSUPPORT_STATIC_ALLOCATION 1  // 静态内存

宏 configSUPPORT_STATIC_ALLOCATION 定义为 1 以后编译一次,会提示我们有两个函数未定义,如图所示:

这个在我们讲 FreeRTOS 的配置文件 FreeRTOSConfig.h 的时候就说过了,如果使用静态方法的话需要用户实现两个函数 vApplicationGetIdleTaskMemory() 和 vApplicationGetTimerTaskMemory() 。通过这两个函数来给空闲任务和定时器服务任务的任务堆栈和任务控制块分配内存,这两个函数我们在 main.c 中定义,定义如下:

static StackType_t IdleTaskStack[configMINIMAL_STACK_SIZE];      // 空闲任务任务堆栈
static StaticTask_t IdleTaskTCB;                                 // 空闲任务控制块
static StackType_t TimerTaskStack[configTIMER_TASK_STACK_DEPTH]; // 定时器服务任务堆栈
static StaticTask_t TimerTaskTCB;                                // 定时器服务任务控制块

// 获取空闲任务地任务堆栈和任务控制块内存
// ppxIdleTaskTCBBuffer:任务控制块内存
// ppxIdleTaskStackBuffer:任务堆栈内存
// pulIdleTaskStackSize:任务堆栈大小
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, 
                                   StackType_t **ppxIdleTaskStackBuffer, 
                                   uint32_t *pulIdleTaskStackSize)
{
	*ppxIdleTaskTCBBuffer = &IdleTaskTCB;
	*ppxIdleTaskStackBuffer = IdleTaskStack;
	*pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
}
// 获取定时器服务任务的任务堆栈和任务控制块内存
// ppxTimerTaskTCBBuffer:任务控制块内存
// ppxTimerTaskStackBuffer:任务堆栈内存
// pulTimerTaskStackSize:任务堆栈大小
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer, 
                                    StackType_t **ppxTimerTaskStackBuffer, 
                                    uint32_t *pulTimerTaskStackSize)
{
	*ppxTimerTaskTCBBuffer = &TimerTaskTCB;
	*ppxTimerTaskStackBuffer = TimerTaskStack;
	*pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
}

可以看出这两个函数很简单,用户定义静态的任务堆栈和任务控制块内存,然后将这些内存传递给函数参数。最后创建空闲任务和定时器服务任务的 API 函数会调用 vApplicationGetIdleTaskMemory() 和 vApplicationGetTimerTaskMemory() 来获取这些内存。

(2)任务设置

#define START_TASK_PRIO 1                   // 任务优先级
#define START_STK_SIZE 128                  // 任务堆栈大小
StackType_t StartTaskStack[START_STK_SIZE]; // 任务堆栈
StaticTask_t StartTaskTCB;                  // 任务控制块
TaskHandle_t StartTask_Handler;             // 任务句柄
void Start_Task(void *pvParameters);        // 任务函数声明

#define TASK1_TASK_PRIO 2                   // 任务优先级
#define TASK1_STK_SIZE 128                  // 任务堆栈大小
StackType_t Task1TaskStack[TASK1_STK_SIZE]; // 任务堆栈
StaticTask_t Task1TaskTCB;                  // 任务控制块
TaskHandle_t Task1Task_Handler;             // 任务句柄
void Task1_Task(void *pvParameters);        // 任务函数声明

#define TASK2_TASK_PRIO 3                   // 任务优先级
#define TASK2_STK_SIZE 128                  // 任务堆栈大小
StackType_t Task2TaskStack[TASK2_STK_SIZE]; // 任务堆栈
StaticTask_t Task2TaskTCB;                  // 任务控制块
TaskHandle_t Task2Task_Handler;             // 任务句柄
void Task2_Task(void *pvParameters);        // 任务函数声明

// LCD 刷屏时使用的颜色
int LCD_DisColor[14] = {WHITE, BLACK, BLUE, BRED,
                        GRED, GBLUE, RED, MAGENTA,
                        GREEN, CYAN, YELLOW, BROWN,
                        BRRED, GRAY};

(3)main() 函数

int main(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); // 设置系统中断优先级分组
    delay_init();                                   // 延时函数初始化
    uart_init(115200);                              // 初始化串口
    LED_Init();                                     // 初始化LED
    LCD_Init();                                     // 初始化LCD
	
	POINT_COLOR = BLACK;               // 设置LCD显示颜色为黑色
    LCD_DrawRectangle(5, 5, 234, 106); // 画一个矩形
    POINT_COLOR = RED;                 // 设置LCD显示颜色为红色
    LCD_ShowString(75, 10, 200, 16, 16, (u8 *)"STM32F103RCT6");
    LCD_ShowString(60, 30, 200, 16, 16, (u8 *)"FreeRTOS Examp 4");
    LCD_ShowString(50, 50, 200, 16, 16, (u8 *)"Task Creat and Del");
    LCD_ShowString(90, 70, 200, 16, 16, (u8 *)"Xiao Xie");
    LCD_ShowString(80, 90, 200, 16, 16, (u8 *)"2023/07/12");

    // 创建开始任务  返回值 = 任务句柄
    StartTask_Handler = xTaskCreateStatic((TaskFunction_t)Start_Task,   // 任务函数
                                         (const char *)"Start_Task",    // 任务名称
                                         (uint16_t)START_STK_SIZE,      // 任务堆栈大小
                                         (void *)NULL,                  // 传递给任务函数的参数
                                         (UBaseType_t)START_TASK_PRIO,  // 任务优先级
                                         (StackType_t*)StartTaskStack,  // 任务堆栈
				                         (StaticTask_t*)&StartTaskTCB); // 任务控制块

    vTaskStartScheduler(); // 开启 FreeRTOS 的任务调度器,FreeRTOS 开始运行
}

(4)任务函数

// 开始任务函数
void Start_Task(void *pvParameters)
{
    taskENTER_CRITICAL(); // 进入临界区
    // 创建一个任务来执行开关中断的动作
    // 创建Task1任务  返回值 = 任务句柄
    Task1Task_Handler = xTaskCreateStatic((TaskFunction_t)Task1_Task,
                                         (const char *)"Task1_Task",
                                         (uint16_t)TASK1_STK_SIZE,
                                         (void *)NULL,
                                         (UBaseType_t)TASK1_TASK_PRIO,
                                         (StackType_t*)Task1TaskStack,  // 任务堆栈
				                         (StaticTask_t*)&Task1TaskTCB); // 任务控制块

    // 创建Task2任务  返回值 = 任务句柄
    Task2Task_Handler = xTaskCreateStatic((TaskFunction_t)Task2_Task,
                                         (const char *)"Task1_Task",
                                         (uint16_t)TASK2_STK_SIZE,
                                         (void *)NULL,
                                         (UBaseType_t)TASK2_TASK_PRIO,
                                         (StackType_t*)Task2TaskStack,  // 任务堆栈
				                         (StaticTask_t*)&Task2TaskTCB); // 任务控制块

    vTaskDelete(StartTask_Handler); // 删除开始任务
    taskEXIT_CRITICAL();            // 退出临界区
}

// Task1任务函数
void Task1_Task(void *pvParameters)
{
    u16 Task1_RunNum = 0;                // Task1任务运行次数
    POINT_COLOR = BLACK;                 // 设置LCD显示颜色为黑色
    LCD_DrawRectangle(5, 110, 115, 314); // 画一个矩形
    LCD_DrawLine(5, 130, 115, 130);      // 画线
    POINT_COLOR = BLUE;                  // 设置LCD显示颜色为蓝色
    LCD_ShowString(7, 111, 110, 16, 16, (u8 *)"Task1 Run:000");
    while (1)
    {
        ++Task1_RunNum; // Task1任务运行次数加1
        LED0 = !LED0;
        printf("Task1任务执行次数:%d次\r\n", Task1_RunNum);
        if (Task1_RunNum == 5)
        {
            vTaskDelete(Task2Task_Handler); // Task1任务执行5次删除任务Task2
            printf("Task1 Delete Task2!!!\r\n");
        }
        LCD_Fill(6, 131, 114, 313, LCD_DisColor[Task1_RunNum % 14]); // 填充区域
        LCD_ShowxNum(86, 111, Task1_RunNum, 3, 16, 0x80);            // 显示任务执行次数
        vTaskDelay(1000);                                            // 延时1s ==> 1000个时钟节拍
    }
}

// Task2任务函数
void Task2_Task(void *pvParameters)
{
    u8 Task2_RunNum = 0;                   // Task2任务运行次数
    POINT_COLOR = BLACK;                   // 设置LCD显示颜色为黑色
    LCD_DrawRectangle(125, 110, 234, 314); // 画一个矩形
    LCD_DrawLine(125, 130, 234, 130);      // 画线
    POINT_COLOR = BLUE;                    // 设置LCD显示颜色为蓝色
    LCD_ShowString(127, 111, 110, 16, 16, (u8 *)"Task2 Run:000");
    while (1)
    {
        ++Task2_RunNum; // Task2任务运行次数加1
        LED1 = !LED1;
        printf("Task2任务执行次数:%d次\r\n", Task2_RunNum);
        LCD_ShowxNum(206, 111, Task2_RunNum, 3, 16, 0x80);                  // 显示任务执行次数
        LCD_Fill(126, 131, 233, 313, LCD_DisColor[13 - Task2_RunNum % 14]); // 填充区域
        vTaskDelay(1000);                                                   // 延时1s ==> 1000个时钟节拍
    }
}

5. 实验程序运行结果分析

实验程序运行结果与上述动态方法创建删除实验一致。

三、任务挂起和恢复实验

1. 实验设备

正点原子 Mini STM32F103RCT6 开发板。

2. 实验目的

学习使用 FreeRTOS 的任务挂起和恢复相关 API 函数,包括 vTaskSuspend()、vTaskResume() 和 xTaskResumeFromISR() 。

3. 实验设计

本实验设计 4 个任务:Start_Task、Key_Task、Task1_Task 和 Task2_Task,这四个任务的任务功能如下:

Start_Task:用来创建其他 3 个任务。

Key_Task:按键服务任务,检测按键的按下结果,根据不同的按键结果执行不同的操作。

Task1_Task:应用任务 1。

Task2_Task:应用任务 2。

实验需要三个按键,KEY0、KEY1 和 KEY_UP,这三个按键的功能如下:

KEY0:此按键为输入模式,用于挂起任务 1 和任务 2 的运行。

KEY1:此按键为输入模式,用于恢复任务 2 的运行。

KEY_UP:此按键为中断模式,在中断服务函数中恢复任务 1 的运行。

4. 实验程序

(1)任务设置

#define START_TASK_PRIO 1            // 任务优先级
#define START_STK_SIZE 128           // 任务堆栈大小
TaskHandle_t StartTask_Handler;      // 任务句柄
void Start_Task(void *pvParameters); // 任务函数声明

#define KEY_TASK_PRIO 2              //任务优先级
#define KEY_STK_SIZE 128             //任务堆栈大小
TaskHandle_t KeyTask_Handler;        //任务句柄
void Key_Task(void *pvParameters);   //任务函数声明

#define TASK1_TASK_PRIO 3            // 任务优先级
#define TASK1_STK_SIZE 128           // 任务堆栈大小
TaskHandle_t Task1Task_Handler;      // 任务句柄
void Task1_Task(void *pvParameters); // 任务函数声明

#define TASK2_TASK_PRIO 4            // 任务优先级
#define TASK2_STK_SIZE 128           // 任务堆栈大小
TaskHandle_t Task2Task_Handler;      // 任务句柄
void Task2_Task(void *pvParameters); // 任务函数声明

// LCD 刷屏时使用的颜色
int LCD_DisColor[14] = {WHITE, BLACK, BLUE, BRED,
                        GRED, GBLUE, RED, MAGENTA,
                        GREEN, CYAN, YELLOW, BROWN,
                        BRRED, GRAY};

(2)main() 函数

int main(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); // 设置系统中断优先级分组4
    delay_init();                                   // 延时函数初始化
    uart_init(115200);                              // 初始化串口
    LED_Init();                                     // 初始化LED
	KEY_Init();                                     // 初始化按键
	EXTIX_Init();                                   // 初始化外部中断
    LCD_Init();                                     // 初始化LCD
	
	POINT_COLOR = BLACK;               // 设置LCD显示颜色为黑色
    LCD_DrawRectangle(5, 5, 234, 106); // 画一个矩形
    POINT_COLOR = RED;                 // 设置LCD显示颜色为红色
    LCD_ShowString(75, 10, 200, 16, 16, (u8 *)"STM32F103RCT6");
    LCD_ShowString(60, 30, 200, 16, 16, (u8 *)"FreeRTOS Examp 5");
    LCD_ShowString(50, 50, 200, 16, 16, (u8 *)"Task Susp and Resum");
    LCD_ShowString(90, 70, 200, 16, 16, (u8 *)"Xiao Xie");
    LCD_ShowString(80, 90, 200, 16, 16, (u8 *)"2023/07/12");

    // 创建开始任务
    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(); // 开启 FreeRTOS 的任务调度器,FreeRTOS 开始运行
}

(3)任务函数

// 开始任务函数
void Start_Task(void *pvParameters)
{
    taskENTER_CRITICAL(); // 进入临界区
    // 创建一个任务来执行开关中断的动作	
	// 创建Key任务
    xTaskCreate((TaskFunction_t)Key_Task,
                (const char *)"Key_Task",
                (uint16_t)KEY_STK_SIZE,
                (void *)NULL,
                (UBaseType_t)KEY_TASK_PRIO,
                (TaskHandle_t *)&KeyTask_Handler);
	
    // 创建Task1任务
    xTaskCreate((TaskFunction_t)Task1_Task,
                (const char *)"Task1_Task",
                (uint16_t)TASK1_STK_SIZE,
                (void *)NULL,
                (UBaseType_t)TASK1_TASK_PRIO,
                (TaskHandle_t *)&Task1Task_Handler);

    // 创建Task2任务
    xTaskCreate((TaskFunction_t)Task2_Task,
                (const char *)"Task1_Task",
                (uint16_t)TASK2_STK_SIZE,
                (void *)NULL,
                (UBaseType_t)TASK2_TASK_PRIO,
                (TaskHandle_t *)&Task2Task_Handler);

    vTaskDelete(StartTask_Handler); // 删除开始任务
    taskEXIT_CRITICAL();            // 退出临界区
}

// Key任务函数
void Key_Task(void *pvParameters)
{
	while (1)
	{
		switch (KEY_Scan(0)) // 按键扫描
		{
			case KEY0_PRES:
				vTaskSuspend(Task1Task_Handler); // 挂起任务1
				printf("挂起任务1的运行!\r\n");
				vTaskSuspend(Task2Task_Handler); // 挂起任务2
				printf("挂起任务2的运行!\r\n");
				break;
			case KEY1_PRES:
				vTaskResume(Task2Task_Handler);  // 恢复任务2
				printf("恢复任务2的运行!\r\n");				
				break;
			default:
				break;
		}
		vTaskDelay(10); // 延时10ms 
	}
}

// Task1任务函数
void Task1_Task(void *pvParameters)
{
    u16 Task1_RunNum = 0;                // Task1任务运行次数
    POINT_COLOR = BLACK;                 // 设置LCD显示颜色为黑色
    LCD_DrawRectangle(5, 110, 115, 314); // 画一个矩形
    LCD_DrawLine(5, 130, 115, 130);      // 画线
    POINT_COLOR = BLUE;                  // 设置LCD显示颜色为蓝色
    LCD_ShowString(7, 111, 110, 16, 16, (u8 *)"Task1 Run:000");
    while (1)
    {
        ++Task1_RunNum; // Task1任务运行次数加1
        LED0 = !LED0;
        printf("Task1任务执行次数:%d次\r\n", Task1_RunNum);
        LCD_Fill(6, 131, 114, 313, LCD_DisColor[Task1_RunNum % 14]); // 填充区域
        LCD_ShowxNum(86, 111, Task1_RunNum, 3, 16, 0x80);            // 显示任务执行次数
        vTaskDelay(1000);                                            // 延时1s ==> 1000个时钟节拍
    }
}

// Task2任务函数
void Task2_Task(void *pvParameters)
{
    u16 Task2_RunNum = 0;                  // Task2任务运行次数
    POINT_COLOR = BLACK;                   // 设置LCD显示颜色为黑色
    LCD_DrawRectangle(125, 110, 234, 314); // 画一个矩形
    LCD_DrawLine(125, 130, 234, 130);      // 画线
    POINT_COLOR = BLUE;                    // 设置LCD显示颜色为蓝色
    LCD_ShowString(127, 111, 110, 16, 16, (u8 *)"Task2 Run:000");
    while (1)
    {
        ++Task2_RunNum; // Task2任务运行次数加1
        LED1 = !LED1;
        printf("Task2任务执行次数:%d次\r\n", Task2_RunNum);
        LCD_ShowxNum(206, 111, Task2_RunNum, 3, 16, 0x80);                  // 显示任务执行次数
        LCD_Fill(126, 131, 233, 313, LCD_DisColor[13 - Task2_RunNum % 14]); // 填充区域
        vTaskDelay(1000);                                                   // 延时1s ==> 1000个时钟节拍
    }
}

(4)中断初始化及处理过程

// 外部中断初始化函数
void EXTIX_Init(void)
{
	EXTI_InitTypeDef EXTI_InitStructure;	
	NVIC_InitTypeDef NVIC_InitStructure;

	// 外部中断,需要使能AFIO时钟
  	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

	KEY_Init(); // 初始化按键对应IO模式

    // GPIOA.0 中断线以及中断初始化配置
  	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);

  	EXTI_InitStructure.EXTI_Line = EXTI_Line0;
  	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;	
  	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; // 上升沿触发
  	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  	EXTI_Init(&EXTI_InitStructure); // 根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
		
	NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;			 // 使能按键所在的外部中断通道
  	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x06; // 抢占优先级6 
  	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00;		 // 子优先级0
  	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				 // 使能外部中断通道
  	NVIC_Init(&NVIC_InitStructure); 
}

// 外部中断服务函数
void EXTI0_IRQHandler(void)
{	
	BaseType_t YieldRequired;	
	if(EXTI_GetITStatus(EXTI_Line0) == SET)
	{
		delay_xms(10);   //消抖
		if(WK_UP == SET)	
		{
			YieldRequired = xTaskResumeFromISR(Task1Task_Handler); // 恢复任务1
			printf("恢复任务1的运行!\r\n");
			if(YieldRequired == pdTRUE)
			{
				/*如果函数xTaskResumeFromISR()返回值为pdTRUE,那么说明要恢复的这个
				任务的任务优先级等于或者高于正在运行的任务(被中断打断的任务),所以在
				退出中断的时候一定要进行上下文切换!*/
				portYIELD_FROM_ISR(YieldRequired);
			}
		}
	}
 	EXTI_ClearITPendingBit(EXTI_Line0);    //清除LINE0上的中断标志位  
}

设置中断优先级时候,如果中断服务函数要使用 FreeRTOS 的 API 函数的话那么中断优先级一定要低于 configMAX_SYSCALL_INTERRUPT_PRIORITY!这里设置为 6。程序中通过调用函数 xTaskResumeFromISR() 来恢复任务 2 的运行。根据函数 xTaskResumeFromISR() 的返回值来确定是否需要进行上下文切换。当返回值为 pdTRUE 的时候就需要调用函数 portYIELD_FROM_ISR() 进行上下文切换,否则的话不需要。

5. 实验程序运行结果分析

编译并下载程序到开发板中,通过按不同的按键来观察任务的挂起和恢复的过程,如图所示:FreeRTOS 学习笔记(5)—— 任务管理实践_第2张图片

从图中可以看出,一开始任务 1 和任务 2 都正常运行,当按下按键 KEY0 挂起任务 1 和任务 2 以后,任务 1 和任务 2 都停止运行,直到下一次通过按键 WK_UP 和按键 KEY1 重新恢复任务 1 或者任务 2 的运行。通过观察可以发现,保存任务运行次数的变量都没有发生数据丢失,如果用任务删除和重建的方法这些数据必然会丢失掉的!

你可能感兴趣的:(STM32,#STM32F103,#FreeRTOS,学习,笔记,stm32)