[FreeRTOS] [STM32] 05 Use of FreeRTOS - Static creation of tasks

My plan when learning FreeRTOS is to first learn how to use it and then work my way up.

The core of the RTOS system is task management
To get started with the RTOS system, you must first master the tasks创建, 删除, 挂起 and 恢复 and other operations

Three elements of the task

任务主体函数, 任务栈, 任务控制块 are the three elements of a task

Define the task function, which needs to be modified in static modeFreeRTOSConfig.h

1. Free RTOS uses xTaskCreate() or xTaskCreateStatic() to create tasks. The parameters of these two functions pxTaskCode are also functions. , called 任务函数. The role of the task function is to specifically implement a certain function. For example, if we want to light up the LED light, then the content of the task function is to light up the light.

2. We need to define this task function.
The official task function template provided by FreeRTOS is as follows:

void vATaskFunction(void *pvParameters) (1)
{
    
    
	for( ; ; ) (2)
	{
    
    
		
		/*--任务应用程序-- 
		  比如点亮一个LED,
		   启动一个马达
		*/(3)
		
		vTaskDelay(); (4)
	}
	
	/* 不能从任务函数中返回或者退出, 从任务函数中返回或退出的话就会调用
	configASSERT(),前提是你定义了 configASSERT()。如果一定要从任务函数中退出的话那一定 
	要调用函数 vTaskDelete(NULL)来删除此任务。*/
	vTaskDelete(NULL); (5)
}

So we can imitate this and write

static void LED_Task (void* parameter)(1)
{
    
    
	while (1) (2)
	{
    
    
		LED1_ON;(3)
		vTaskDelay(500); /* 延时 500 个 tick */ (4)

		LED1_OFF;
		vTaskDelay(500); /* 延时 500 个 tick */
	}
	//vTaskDelete(NULL); (5)
}

(1). The essence of the task function is also a function. The return type must be void type, and the task's The parameters are also void pointer types.
(2). The specific execution process of the task is a large loop. for(; ; ) represents a loop and has the same function as while(1)
( 3). Inside the loop is the real task code
(4) FreeRTOS delay function. It is not necessary to use the delay function here. Other API functions only need to allow FreeRTOS to switch tasks. All are possible, such as requesting semaphores, queues, etc., or even calling the task scheduler directly. But the most commonly used one is the delay function of FreeRTOS.
(5) Task functions are generally not allowed to jump out of the loop. If you must jump out of the loop, you must call the function vTaskDelete(NULL) to delete the task after jumping out of the loop!

Idle task and timer task stack function implementation, implemented by the user in static mode

1. When using static method to create a task, the macro definition inFreeRTOSConfig.h fileconfigSUPPORT_STATIC_ALLOCATION must be 1.
2. Implement two functions vApplicationGetIdleTaskMemory() and vApplicationGetTimerTaskMemory(), and set the stack size of the Idle task and the Timer task...

Implementation of idle task and timer task stack functions

/* 空闲任务任务堆栈 */ 
static StackType_t Idle_Task_Stack[configMINIMAL_STACK_SIZE]; 
/* 定时器任务堆栈 */ 
static StackType_t Timer_Task_Stack[configTIMER_TASK_STACK_DEPTH]; 

/* 空闲任务控制块 */ 
static StaticTask_t Idle_Task_TCB; 
/* 定时器任务控制块 */ 
static StaticTask_t Timer_Task_TCB; 
 
 /**
 *******************************************************************
	 * @brief 获取空闲任务的任务堆栈和任务控制块内存
	 * ppxTimerTaskTCBBuffer : 任务控制块内存
	 * ppxTimerTaskStackBuffer : 任务堆栈内存
	 * pulTimerTaskStackSize : 任务堆栈大小
	 * @author fire
	 * @version V1.0
	 * @date 2018-xx-xx
 **********************************************************************
 */
 void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, 
 StackType_t **ppxIdleTaskStackBuffer, 
 uint32_t *pulIdleTaskStackSize) 
 {
    
     
 *ppxIdleTaskTCBBuffer=&Idle_Task_TCB;/* 任务控制块内存 */ 
 *ppxIdleTaskStackBuffer=Idle_Task_Stack;/* 任务堆栈内存 */
 *pulIdleTaskStackSize=configMINIMAL_STACK_SIZE;/* 任务堆栈大小 */ 
 } 
 
 /**
 *********************************************************************
	 * @brief 获取定时器任务的任务堆栈和任务控制块内存
	 * ppxTimerTaskTCBBuffer : 任务控制块内存
	 * ppxTimerTaskStackBuffer : 任务堆栈内存
	 * pulTimerTaskStackSize : 任务堆栈大小
	 * @author fire
	 * @version V1.0
	 * @date 2018-xx-xx
 **********************************************************************
 */
 void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer, 
 StackType_t **ppxTimerTaskStackBuffer, 
 uint32_t *pulTimerTaskStackSize) 
 {
    
     
 *ppxTimerTaskTCBBuffer=&Timer_Task_TCB;/* 任务控制块内存 */ 
 *ppxTimerTaskStackBuffer=Timer_Task_Stack;/* 任务堆栈内存 */ 
 *pulTimerTaskStackSize=configTIMER_TASK_STACK_DEPTH;/* 任务堆栈大小 */ 
 }

Define task stack

/* AppTaskCreate 任务任务堆栈 */
static StackType_t AppTaskCreate_Stack[128];

/* LED 任务堆栈 */
static StackType_t LED_Task_Stack[128];

At present, we have only created one task. When the task enters the delay, because there is no other ready user task, the system will enter the idle task. The idle task is a task started by the FreeRTOS system itself, with the lowest priority. When there are no ready tasks in the entire system, the system must ensure that there is a task running. The idle task is designed for this. When the user task delay expires, the idle task will be switched back to the user task.

In the FreeRTOS system, each task is independent, and their running environment is stored separately in their stack space. After defining the task function, we also need to define a stack for the task. Currently we are using static memory, so the task stack is an independent global variable.

In most systems, stack space address alignment is required. In FreeRTOS, it is aligned with 8 bytes, and it will check whether the stack is aligned. portBYTE_ALIGNMENT is in portmacro. A macro defined in h has a value of 8, which means it is configured to be aligned by 8 bytes. Of course, the user can choose to align by bytes such as 1, 2, 4, 8, 16, 32 etc.

Define task control block

The task control block is a structure with many members, which together describe all the information of the task.

 /* AppTaskCreate 任务控制块 */
static StaticTask_t AppTaskCreate_TCB;
/* AppTaskCreate 任务控制块 */
 static StaticTask_t LED_Task_TCB;

Static creation of tasks

任务主体函数After , 任务栈, 任务控制块 are created. xTaskCreateStatic(), FreeRTOS API function connects the task body function, task stack (static) and task control block (static), so that the task can be started by the system at any time.

/* 创建 AppTaskCreate 任务 */
AppTaskCreate_Handle = xTaskCreateStatic((TaskFunction_t)AppTaskCreate, //任务函数(1)
											(const char* )"AppTaskCreate",//任务名称(2)
											(uint32_t )128, //任务堆栈大小 (3)
											(void* )NULL, //传递给任务函数的参数(4)
											(UBaseType_t )3, //任务优先级 (5)
											(StackType_t* )AppTaskCreate_Stack, //任务堆栈(6)
											(StaticTask_t* )&AppTaskCreate_TCB); //任务控制块(7)

(1): Task entry function, that is, the name of the task function, we need to define and implement it ourselves
(2): Task name, in string form, the maximum length is macro specification defined in a> (6): The starting address of the task stack, only in , the task creation function xTaskCreate() will return a pointer pointing to the task control block, which is a piece of memory dynamically allocated in the xTaskCreate() function.   Pointer. When , you need to pass the predefined task control block to the task initialization function xTaskCreateStatic() (7): Task control block pointer. When using in (5): Priority of the task. The priority range is determined according to the macro (4): Task entry function parameter, configure it to 0 or NULL when not in use (3): Task stack size, unit is word, under 32-bit processor ( STM32), one word is equal to 4 bytes, then the task size is 128 * 4 bytesFreeRTOSConfig.hconfigMAX_TASK_NAME_LEN


FreeRTOSConfig.hconfigMAX_PRIORITIES
使用静态内存的时候才需要提供,在使用动态内存的时候会根据提供的任务栈大小自动创建
静态内存
使用动态内存

Start task

 if (NULL != AppTaskCreate_Handle) /* 创建成功 */
			 vTaskStartScheduler(); /* 启动任务,开启调度 */

After the task is created, it is in the task ready state (Ready), and the task in the ready state can participate in the scheduling of the operating system. But at this time, the task has only been created, the task scheduler has not been opened, and the idle task and timer task have not been created (if the macro definition configUSE_TIMERS is enabled).

For each operating system, the task scheduler is only started once and will not be executed again. The function to start the task scheduler in FreeRTOS isvTaskStartScheduler(), and when the task scheduler is started It will not return. From now on, task management is managed by FreeRTOS. This is the first step to truly enter the real-time operating system.

main.c function

1.Create the project, transplant FreeRTOS, and add it to the project
2. Complete the hardware initialization
3. After modifying the configuration file and creating a task, you can implement the first main.c file.
When using static creation tasks, you must configure the macro in FreeRTOSConfig.h to 1configSUPPORT_STATIC_ALLOCATION

/**
*********************************************************************
* @file main.c
* @author fire 参照野火FreeRTOS
* @version V1.0
* @date 2018-xx-xx
* @brief FreeRTOS v9.0.0 + STM32 工程模版
 *************************************************************************
 * 包含的头文件
 *************************************************************************
 */
 /* FreeRTOS 头文件 */
 #include "FreeRTOS.h"
 #include "task.h"
 /* 开发板硬件 bsp 头文件 硬件初始化文件,自行实现*/
 #include "bsp_led.h"
 #include "bsp_usart.h"
 
 /**************************** 任务句柄 ********************************/
 /*
 * 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
 * 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
 * 这个句柄可以为 NULL。
 */
 /* 创建任务句柄 */
 static TaskHandle_t AppTaskCreate_Handle;
 /* LED 任务句柄 */
 static TaskHandle_t LED_Task_Handle;
 
 /******************************* 内核对象句柄 **************************/
 /*
 * 信号量,消息队列,事件标志组,软件定时器这些都属于内核的对象,要想使用这些内核
 * 对象,必须先创建,创建成功之后会返回一个相应的句柄。实际上就是一个指针,后续我
 * 们就可以通过这个句柄操作这些内核对象。
 *
 * 
 内核对象说白了就是一种全局的数据结构,通过这些数据结构我们可以实现任务间的通信,
 * 任务间的事件同步等各种功能。至于这些功能的实现我们是通过调用这些内核对象的函数
 * 来完成的
 *
 */
 
 
 /************************** 全局变量声明 *******************************/
 /*
 * 当我们在写应用程序的时候,可能需要用到一些全局变量。
 * 静态方法创建任务的时候,需要自行实现任务堆栈和任务控制块,作为参数传递
 */
 /* AppTaskCreate 任务任务堆栈 */
 static StackType_t AppTaskCreate_Stack[128];
 /* LED 任务堆栈 */
 static StackType_t LED_Task_Stack[128];
 
 /* AppTaskCreate 任务控制块 */
 static StaticTask_t AppTaskCreate_TCB;
 /* AppTaskCreate 任务控制块 */
 static StaticTask_t LED_Task_TCB;
 
 /* 空闲任务任务堆栈 FreeRTOS创建*/
 static StackType_t Idle_Task_Stack[configMINIMAL_STACK_SIZE];
 /* 定时器任务堆栈 FreeRTOS创建*/
 static StackType_t Timer_Task_Stack[configTIMER_TASK_STACK_DEPTH];
 
 /* 空闲任务控制块 */
 static StaticTask_t Idle_Task_TCB;
 /* 定时器任务控制块 */
 static StaticTask_t Timer_Task_TCB;
 
 /*
 *************************************************************************
 * 函数声明
 *************************************************************************
 */
 static void AppTaskCreate(void);/* 用于创建任务 */
 
 static void LED_Task(void* pvParameters);/* LED_Task 任务实现 */
 
 static void BSP_Init(void);/* 用于初始化板载相关资源 */
 
 /**
 * 使用了静态分配内存,以下这两个函数是由用户实现,函数在 task.c 文件中有引用
 *当且仅当 configSUPPORT_STATIC_ALLOCATION 这个宏定义为 1 的时候才有效
 */
 void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer,
 StackType_t **ppxTimerTaskStackBuffer,
 uint32_t *pulTimerTaskStackSize);
 
 void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer,
 StackType_t **ppxIdleTaskStackBuffer,
 uint32_t *pulIdleTaskStackSize);
 
 /*****************************************************************
 * @brief 主函数
 * @param 无
 * @retval 无
 * @note 第一步:开发板硬件初始化
 第二步:创建 APP 应用任务
 第三步:启动 FreeRTOS,开始多任务调度
 ****************************************************************/
 int main(void)
 {
    
    
	 /* 开发板硬件初始化 */
	 BSP_Init();

	 /* 创建 AppTaskCreate 任务 */
	 AppTaskCreate_Handle = xTaskCreateStatic((TaskFunction_t )AppTaskCreate,
					 (const char* )"AppTaskCreate",//任务名称
					 (uint32_t )128, //任务堆栈大小
					 (void* )NULL,//传递给任务函数的参数
					 (UBaseType_t )3, //任务优先级
					 (StackType_t* )AppTaskCreate_Stack,
					 (StaticTask_t* )&AppTaskCreate_TCB);
 
 if (NULL != AppTaskCreate_Handle) /* 创建成功 */
		 vTaskStartScheduler(); /* 启动任务,开启调度 */
 
		 while (1); /* 正常不会执行到这里 */
 }
 
 
 /***********************************************************************
 * @ 函数名 : AppTaskCreate
 * @ 功能说明: 为了方便管理,所有的任务创建函数都放在这个函数里面
 * @ 参数 : 无
 * @ 返回值 : 无
 ***************************************************************/
 static void AppTaskCreate(void)
 {
    
    
	 taskENTER_CRITICAL(); //进入临界区
 
 /* 创建 LED_Task 任务 */
 LED_Task_Handle = xTaskCreateStatic((TaskFunction_t )LED_Task, //任务函数
										 (const char*)"LED_Task",//任务名称
										 (uint32_t)128, //任务堆栈大小
										 (void* )NULL, //传递给任务函数的参数
										 (UBaseType_t)4, //任务优先级
										 (StackType_t*)LED_Task_Stack,//任务堆栈
										 (StaticTask_t*)&LED_Task_TCB);//任务控制块
 
 if (NULL != LED_Task_Handle) /* 创建成功 */
		 printf("LED_Task 任务创建成功!\n");
 else
			 printf("LED_Task 任务创建失败!\n");
 
		 vTaskDelete(AppTaskCreate_Handle); //删除 AppTaskCreate 任务
 
 	taskEXIT_CRITICAL(); //退出临界区
 }
 
 
 
 /**************************************************************
 * @ 函数名 : LED_Task
 * @ 功能说明: LED_Task 任务主体
 * @ 参数 :
 * @ 返回值 : 无
 ********************************************************************/
 static void LED_Task(void* parameter)
 {
    
    
	 while (1) {
    
    
	 LED1_ON;
	 vTaskDelay(500); /* 延时 500 个 tick */	 
	 LED1_OFF;
	 vTaskDelay(500); /* 延时 500 个 tick */
	 }
 }
 
 /***********************************************************************
 * @ 函数名 : BSP_Init
 * @ 功能说明: 板级外设初始化,所有板子上的初始化均可放在这个函数里面
 * @ 参数 :
 * @ 返回值 : 无
 *********************************************************************/
 static void BSP_Init(void)
 {
    
    
	 /*
	 * STM32 中断优先级分组为 4,即 4bit 都用来表示抢占优先级,范围为:0~15
	 * 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,
	 * 都统一用这个优先级分组,千万不要再分组,切忌。
	 */
 NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
 
	 /* LED 初始化 */
	 LED_GPIO_Config();
	 
	 /* 串口初始化 */
	 USART_Config();
 
 }
 
 
 /**
 **********************************************************************
 * @brief 获取空闲任务的任务堆栈和任务控制块内存
 * ppxTimerTaskTCBBuffer : 任务控制块内存
 * ppxTimerTaskStackBuffer : 任务堆栈内存
 * pulTimerTaskStackSize : 任务堆栈大小
 * @author fire
 * @version V1.0
 * @date 2018-xx-xx
 **********************************************************************
 */
 void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer,
									 StackType_t **ppxIdleTaskStackBuffer,
									 uint32_t *pulIdleTaskStackSize)
 {
    
    
 *ppxIdleTaskTCBBuffer=&Idle_Task_TCB;/* 任务控制块内存 */
 *ppxIdleTaskStackBuffer=Idle_Task_Stack;/* 任务堆栈内存 */
 *pulIdleTaskStackSize=configMINIMAL_STACK_SIZE;/* 任务堆栈大小 */
 }
 
 /**
 *********************************************************************
 * @brief 获取定时器任务的任务堆栈和任务控制块内存
 * ppxTimerTaskTCBBuffer : 任务控制块内存
 * ppxTimerTaskStackBuffer : 任务堆栈内存
 * pulTimerTaskStackSize : 任务堆栈大小
 * @author fire
 * @version V1.0
 * @date 2018-xx-xx
 **********************************************************************
 */
 void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer,
									 StackType_t **ppxTimerTaskStackBuffer,
									 uint32_t *pulTimerTaskStackSize)
 {
    
    
	 *ppxTimerTaskTCBBuffer=&Timer_Task_TCB;/* 任务控制块内存 */
	 *ppxTimerTaskStackBuffer=Timer_Task_Stack;/* 任务堆栈内存 */
	 *pulTimerTaskStackSize=configTIMER_TASK_STACK_DEPTH;/* 任务堆栈大小 */
 }
 
 /************************END OF FILE****************************/
 

Guess you like

Origin blog.csdn.net/apythonlearner/article/details/133752195