【FreeRTOS#4】任务创建与删除实例

发布于:2025-07-02 ⋅ 阅读:(22) ⋅ 点赞:(0)

一、实验要求

 学会 xTaskCreate( ) 和 vTaskDelete( ) 的使用:

  • start_task:用来创建其他的三个任务。
  • task1:实现LED1每500ms闪烁一次。
  • task2:实现LED2每500ms闪烁一次。
  • task3:判断按键KEY1是否按下,按下则删掉task1。

接下来的内容将会特别绕,很多概念捋不清的建议回到上一节看看。

二、重要部分

(1)参数配置初始化

        这里定义了创建任务的函数需要的入口参数;

#include "freertos_demo.h"
/*necc header files*/
#include "FreeRTOS.h"
#include "task.h"
#include "gpio.h"

/*start tack config*/
#define START_TASK_STACK 128
#define STACK_TASK_PRIORITY 1
void start_task(void * pvParameters);
TaskHandle_t start_task_handle;

/* task1 config*/
#define START_TASK1_STACK 128
#define STACK_TASK1_PRIORITY 2
void task1(void * pvParameters);
TaskHandle_t start_task1_handle;

/* task2 config*/
#define START_TASK2_STACK 128
#define STACK_TASK2_PRIORITY 3
void task2(void * pvParameters);
TaskHandle_t start_task2_handle;

/* task3 config*/
#define START_TASK3_STACK 128
#define STACK_TASK3_PRIORITY 4
void task3(void * pvParameters);
TaskHandle_t start_task3_handle;

(2)入口函数

        在入口函数中,我们来建立一个优先级最低的启动任务。

/*start task*/
void freertos_start(void){
		/* 1.creat the start files*/
		xTaskCreate((TaskFunction_t) start_task,  // 任务函数指针
             (char*) "start_task",    //任务函数名首地址
              (configSTACK_DEPTH_TYPE) START_TASK_STACK,  //任务栈深度 单位:32bit = = 4 Byte
             (void*) NULL,  //给任务函数的传参,这里不用传,根据需要设计
             (UBaseType_t) STACK_TASK_PRIORITY, //任务优先级,数字越大越高
             (TaskHandle_t *) &start_task_handle);	// 任务句柄的指针
		
		/* 2.run the switcher*/
		vTaskStartScheduler();  //启动任务调度器
}

        在这里解释一下我们创建任务时,为什么要传任务句柄的地址,而不是任务句柄本身。大家想一个道理,任务句柄(本质是指针)是我们在上一个配置环节自定义声明的,并没有给他赋予明确的地址。

        而freertos要做的一件事情,就是把我们的句柄通过系统的映射关系与TCB捆绑到一块。

        如果我们直接传句柄进去,那么任务创建函数对作为全局变量的句柄是没有改动的。

        这里再辨析以下HAL库外设句柄和FreeRTOS任务句柄的区别

        1. HAL库外设句柄本质是一个结构体名称,比如htim,huart等等。我们想要让函数修改实质性的修改它的内容,就必须传地址进去。如果传的是结构体名,对global的结构体本身是没有影响的。

        2. FreeRTOS的任务句柄,如上图所说,本质是一个映射TCB的指针。

        两者的核心点是都需要取地址来传参,进而修改全局量。

(3)启动函数

void start_task(void * pvParameters){
		xTaskCreate((TaskFunction_t) task1,
             (char*) "task1",
              (configSTACK_DEPTH_TYPE) START_TASK1_STACK,
             (void*) NULL,
             (UBaseType_t) STACK_TASK1_PRIORITY,
             (TaskHandle_t *) &start_task1_handle);
		xTaskCreate((TaskFunction_t) task2,
             (char*) "task2",
              (configSTACK_DEPTH_TYPE) START_TASK2_STACK,
             (void*) NULL,
             (UBaseType_t) STACK_TASK2_PRIORITY,
             (TaskHandle_t *) &start_task2_handle);
		xTaskCreate((TaskFunction_t) task3,
             (char*) "task3",
              (configSTACK_DEPTH_TYPE) START_TASK3_STACK,
             (void*) NULL,
             (UBaseType_t) STACK_TASK3_PRIORITY,
             (TaskHandle_t *) &start_task3_handle);
		vTaskDelete(NULL);
}

        此函数负责创建其他三个目标任务,用完立刻删除自己。

(4)其他任务函数

        前两个目标任务都很简单,死循环里,死循环中要有一个FreeRTOS的delay函数模拟阻塞。注意不要用HAL库的delay,你即使delay再长,FreeRTOS也不会理解。

        比较有难度的一个地方是任务三的按键消抖控制。

        在FreeRTOS中,硬件方面的消抖延迟我们用HAL库的delay,软件方面的模拟阻塞延迟我们用FreeRTOS系统的delay。

        在这个过程中,注意调节系统delay的大小,如果过小,会让优先级最高的task3始终处于运行态,不给其他任务一点机会。

        如果过大,按键可能要死死的长按才能起效果。这个过程需要不断上手感受、调试才能ok的

void task1(void * pvParameters){
		while(1){
//				HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);
				HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,1);
				vTaskDelay(500);
		}
}

void task2(void * pvParameters){
		while(1){
//		    HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);
					HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,0);
				vTaskDelay(500);
		}
}

void task3(void * pvParameters){
		while(1){
				if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==0){
						HAL_Delay(10);
							if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==0){
									vTaskDelete(start_task1_handle);
									start_task1_handle=NULL;
							}
						
				}
						vTaskDelay(200);
		}
}

(5)主函数

        主函数中我们include进free_rtos自编程序的头文件,然后在死循环前调用入口函数就行。

        需要注意的是,只要这一步做了,那么main函数中入口函数之后的内容永远也不会运行。

完整代码

main.c

/* USER CODE BEGIN Includes */
#include "freertos_demo.h"
/* USER CODE END Includes */


  /* USER CODE BEGIN 2 */
	freertos_start();
  /* USER CODE END 2 */

freertos_demo.h

#ifndef __FREERTOS_DEMO_H
#define __FREERTOS_DEMO_H
#endif

#include "stm32f1xx_hal.h"

void freertos_start(void);
void start_task(void * pvParameters);
void task1(void * pvParameters);
void task2(void * pvParameters);
void task3(void * pvParameters);

freertos_demo.c

#include "freertos_demo.h"
/*necc header files*/
#include "FreeRTOS.h"
#include "task.h"
#include "gpio.h"

/*start tack config*/
#define START_TASK_STACK 128
#define STACK_TASK_PRIORITY 1
void start_task(void * pvParameters);
TaskHandle_t start_task_handle;

/* task1 config*/
#define START_TASK1_STACK 128
#define STACK_TASK1_PRIORITY 2
void task1(void * pvParameters);
TaskHandle_t start_task1_handle;

/* task2 config*/
#define START_TASK2_STACK 128
#define STACK_TASK2_PRIORITY 3
void task2(void * pvParameters);
TaskHandle_t start_task2_handle;

/* task3 config*/
#define START_TASK3_STACK 128
#define STACK_TASK3_PRIORITY 4
void task3(void * pvParameters);
TaskHandle_t start_task3_handle;


/*start task*/
void freertos_start(void){
		/* 1.creat the start files*/
		xTaskCreate((TaskFunction_t) start_task,
             (char*) "start_task",
              (configSTACK_DEPTH_TYPE) START_TASK_STACK,
             (void*) NULL,
             (UBaseType_t) STACK_TASK_PRIORITY,
             (TaskHandle_t *) &start_task_handle);	
		
		/* 2.run the switcher*/
		vTaskStartScheduler();
}

void start_task(void * pvParameters){
		xTaskCreate((TaskFunction_t) task1,
             (char*) "task1",
              (configSTACK_DEPTH_TYPE) START_TASK1_STACK,
             (void*) NULL,
             (UBaseType_t) STACK_TASK1_PRIORITY,
             (TaskHandle_t *) &start_task1_handle);
		xTaskCreate((TaskFunction_t) task2,
             (char*) "task2",
              (configSTACK_DEPTH_TYPE) START_TASK2_STACK,
             (void*) NULL,
             (UBaseType_t) STACK_TASK2_PRIORITY,
             (TaskHandle_t *) &start_task2_handle);
		xTaskCreate((TaskFunction_t) task3,
             (char*) "task3",
              (configSTACK_DEPTH_TYPE) START_TASK3_STACK,
             (void*) NULL,
             (UBaseType_t) STACK_TASK3_PRIORITY,
             (TaskHandle_t *) &start_task3_handle);
		vTaskDelete(NULL);
}

void task1(void * pvParameters){
		while(1){
//				HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);
				HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,1);
				vTaskDelay(500);
		}
}

void task2(void * pvParameters){
		while(1){
//		    HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);
					HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,0);
				vTaskDelay(500);
		}
}

void task3(void * pvParameters){
		while(1){
				if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==0){
						HAL_Delay(10);
							if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==0){
									vTaskDelete(start_task1_handle);
									start_task1_handle=NULL;
							}
						
				}
						vTaskDelay(200);
		}
}

总结

这部分开始,内容将会极其抽象复杂,对于基础内容需要不断回看。重在梳理概念之间的关系。


网站公告

今日签到

点亮在社区的每一天
去签到