FreeRTOS(二)任务基础知识

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_40818798/article/details/89646804

一、前后台系统与RTOS 

  • 前后台系统 = 死循环(通常为1个) + 中断服务程序(通常为若干个)

  1. 应用程序是一个无限循环,循环中调用API函数完成所需的操作,这个大循环就叫做后台系统。
  2. 中断服务程序用于处理系统的异步事件,也就是前台系统。
  3. 前台是中断级,后台是任务级。

  • RTOS实时操作系统(软实时(不太严格) 硬实时(严格响应))

  1. 可剥夺型内核(任务调度器可以剥夺其他任务(优先级低)的CPU使用权
  2. 永远在运行处于就绪态的最高优先级的任务


二、FreeRTOS任务特性

  • 简单。
  • 无使用限制。
  • 支持抢占
  • 支持优先级
  • 每个任务都拥有堆栈导致了 RAM 使用量增大。
  • 如果使用抢占的话的必须仔细的考虑重入的问题

任务状态

任务优先级

  • 0 ~ configMAX_PRIORITIES-1       32个     31为系统时钟 0为空闲任务  故0、31一般不分配给用户任务
  • 数字越大,优先级越高(与ucos相反)
  • ucos最多有64个任务(包括系统任务和用户任务),

任务实现(代码的角度看——函数)

void vATaskFunction(void *pvParameters)
{
    for( ; ; )            
    {
        --任务应用程序--
        vTaskDelay();
    }
    vTaskDelete(NULL); 
}
  • 任务一般为一个死循环,故不能从任务函数中返回或者退出,从任务函数中返回或退出的话就会调用configASSERT(),前提是你定义了 configASSERT()。如果一定要从任务函数中退出的话那一定要调用函数 vTaskDelete(NULL)来删除此任务。 

任务控制块(记录任务属性,相当于身份证)

typedef struct tskTaskControlBlock
{
	volatile StackType_t	*pxTopOfStack;	/*< Points to the location of the last item placed on the tasks stack.  THIS MUST BE THE FIRST MEMBER OF THE TCB STRUCT. */

	#if ( portUSING_MPU_WRAPPERS == 1 )
		xMPU_SETTINGS	xMPUSettings;		/*< The MPU settings are defined as part of the port layer.  THIS MUST BE THE SECOND MEMBER OF THE TCB STRUCT. */
	#endif

	ListItem_t			xStateListItem;	/*< The list that the state list item of a task is reference from denotes the state of that task (Ready, Blocked, Suspended ). */
	ListItem_t			xEventListItem;		/*< Used to reference a task from an event list. */
	UBaseType_t			uxPriority;			/*< The priority of the task.  0 is the lowest priority. */
	StackType_t			*pxStack;			/*< Points to the start of the stack. */
	char				pcTaskName[ configMAX_TASK_NAME_LEN ];/*< Descriptive name given to the task when created.  Facilitates debugging only. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */

	#if ( portSTACK_GROWTH > 0 )
		StackType_t		*pxEndOfStack;		/*< Points to the end of the stack on architectures where the stack grows up from low memory. */
	#endif

	#if ( portCRITICAL_NESTING_IN_TCB == 1 )
		UBaseType_t		uxCriticalNesting;	/*< Holds the critical section nesting depth for ports that do not maintain their own count in the port layer. */
	#endif

	#if ( configUSE_TRACE_FACILITY == 1 )
		UBaseType_t		uxTCBNumber;		/*< Stores a number that increments each time a TCB is created.  It allows debuggers to determine when a task has been deleted and then recreated. */
		UBaseType_t		uxTaskNumber;		/*< Stores a number specifically for use by third party trace code. */
	#endif

	#if ( configUSE_MUTEXES == 1 )
		UBaseType_t		uxBasePriority;		/*< The priority last assigned to the task - used by the priority inheritance mechanism. */
		UBaseType_t		uxMutexesHeld;
	#endif

	#if ( configUSE_APPLICATION_TASK_TAG == 1 )
		TaskHookFunction_t pxTaskTag;
	#endif

	#if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 )
		void *pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ];
	#endif

	#if( configGENERATE_RUN_TIME_STATS == 1 )
		uint32_t		ulRunTimeCounter;	/*< Stores the amount of time the task has spent in the Running state. */
	#endif

	#if ( configUSE_NEWLIB_REENTRANT == 1 )
		/* Allocate a Newlib reent structure that is specific to this task.
		Note Newlib support has been included by popular demand, but is not
		used by the FreeRTOS maintainers themselves.  FreeRTOS is not
		responsible for resulting newlib operation.  User must be familiar with
		newlib and must provide system-wide implementations of the necessary
		stubs. Be warned that (at the time of writing) the current newlib design
		implements a system-wide malloc() that must be provided with locks. */
		struct	_reent xNewLib_reent;
	#endif

	#if( configUSE_TASK_NOTIFICATIONS == 1 )
		volatile uint32_t ulNotifiedValue;
		volatile uint8_t ucNotifyState;
	#endif

	/* See the comments above the definition of
	tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE. */
	#if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )
		uint8_t	ucStaticallyAllocated; 		/*< Set to pdTRUE if the task is a statically allocated to ensure no attempt is made to free the memory. */
	#endif

	#if( INCLUDE_xTaskAbortDelay == 1 )
		uint8_t ucDelayAborted;
	#endif

} tskTCB;

任务堆栈(保护工作环境)

//任务堆栈的变量类型为StackType_t,此变量类型如下:

#define portSTACK_TYPE uint32_t    //4个字节
#define portBASE_TYPE long
typedef portSTACK_TYPE StackType_t; 

//所以要注意,传给创建任务函数的堆栈数值大小×4才是真正的堆栈大小
  • 注意:传给创建任务函数的堆栈数值大小×4才是实际的堆栈大小 

 三、任务的创建与删除

  • 动态方法()

#include "led.h"
#include "delay.h"
#include "sys.h"
#include "FreeRTOS.h"
#include "task.h"

#define START_STK_SIZE 120	//开始任务堆栈
#define START_TASK_PRIO 3	//开始任务优先级

#define TASK1_STK_SIZE 120	//任务1堆栈
#define TASK1_TASK_PRIO 2	//任务1优先级

#define TASK2_STK_SIZE 120	//任务2堆栈
#define TASK2_TASK_PRIO 1	//任务2优先级

void strat_task(void * pvParamemters);
void task1(void * pvParamemters);
void task2(void * pvParamemters);

TaskHandle_t StartTask_Handle;	//开始任务句柄
TaskHandle_t Task1Task_Handle;	//任务1句柄
TaskHandle_t Task2Task_Handle;	//任务2句柄

int main(void)
{
	//NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4	 	 
	delay_init();	    				//延时函数初始化	  
	uart_init(115200);					//初始化串口
	LED_Init();		  					//初始化LED
	xTaskCreate(	(TaskFunction_t) strat_task,	//
					(char *  	   ) "strat_task",
					(uint16_t	   ) START_STK_SIZE,
					(void *        ) NULL,
					(UBaseType_t   ) START_TASK_PRIO,
					(TaskHandle_t *) &StartTask_Handle );
	
    vTaskStartScheduler();          //开启任务调度
}
void strat_task(void * pvParamemters)    //创建task1和task2
{
	xTaskCreate(	(TaskFunction_t ) task1,
					(char *  		) "task1",
					(uint16_t		) TASK1_STK_SIZE,
					(void *			) NULL,
					(UBaseType_t	) TASK1_TASK_PRIO,
					(TaskHandle_t * ) &Task1Task_Handle );
	xTaskCreate(	(TaskFunction_t ) task2,
					(char *  		) "task2",
					(uint16_t		) TASK2_STK_SIZE,
					(void *			) NULL,
					(UBaseType_t	) TASK2_TASK_PRIO,
					(TaskHandle_t * ) &Task2Task_Handle );
	vTaskDelete(StartTask_Handle);
	//等价于vTaskDelete(NULL);删除自身
}

void task1(void * pvParamemters)
{
	while(1)
	{
		LED0 = !LED0;
		vTaskDelay(500);
	}
}
void task2(void * pvParamemters)
{
	while(1)
	{
		LED1 = !LED1;
		vTaskDelay(800);
	}
}

  • 静态方法()

  1. 使用静态方法的话需要将宏configSUPPORT_STATIC_ALLOCATION 设置为1

  2. 需要自己实现以下两个函数:

  3. void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, 
                            StackType_t **ppxIdleTaskStackBuffer, 
                            uint32_t *pulIdleTaskStackSize)    //空闲任务内存分配
    void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer, 
                            StackType_t **ppxTimerTaskStackBuffer, 
                            uint32_t *pulTimerTaskStackSize)   //定时器任务内存分配

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "timer.h"
#include "FreeRTOS.h"
#include "task.h"

//空闲任务任务堆栈
static StackType_t IdleTaskStack[configMINIMAL_STACK_SIZE];
//空闲任务控制块
static StaticTask_t IdleTaskTCB;

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

//任务优先级
#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);


//获取空闲任务地任务堆栈和任务控制块内存,因为本例程使用的
//静态内存,因此空闲任务的任务堆栈和任务控制块的内存就应该
//有用户来提供,FreeRTOS提供了接口函数vApplicationGetIdleTaskMemory()
//实现此函数即可。
//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;
}

int main(void)
{
	//NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4	 
	delay_init();	    				//延时函数初始化	 
	uart_init(115200);					//初始化串口
	LED_Init();		  					//初始化LED
	
	
    //创建开始任务
	StartTask_Handler=xTaskCreateStatic((TaskFunction_t	)start_task,		//任务函数
							(const char* 	)"start_task",		//任务名称
							(uint32_t 		)START_STK_SIZE,//任务堆栈大小
							(void* 		  	)NULL,	//传递给任务函数的参数
							(UBaseType_t 	)START_TASK_PRIO, 	//任务优先级
							(StackType_t*   )StartTaskStack,	//任务堆栈
							(StaticTask_t*  )&StartTaskTCB);	//任务控制块              
    vTaskStartScheduler();          //开启任务调度
}

//开始任务任务函数
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           //进入临界区
    //创建TASK1任务
	Task1Task_Handler=xTaskCreateStatic((TaskFunction_t	)task1_task,		
							(const char* 	)"task1_task",		
							(uint32_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* 	)"task2_task",		
							(uint32_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)
{
	u8 task1_num=0;

	while(1)
	{
		task1_num++;	//任务执1行次数加1 注意task1_num1加到255的时候会清零!!
		LED0=!LED0;
		printf("任务1已经执行:%d次\r\n",task1_num);
		if(task1_num==5) 
		{
			if(Task2Task_Handler != NULL)		//任务2是否存在?	
			{
				vTaskDelete(Task2Task_Handler);	//任务1执行5次删除任务2
				Task2Task_Handler=NULL;			//任务句柄清零
				printf("任务1删除了任务2!\r\n");
			}
		}
        vTaskDelay(1000);              //延时1s,也就是1000个时钟节拍	
	}
}

//task2任务函数
void task2_task(void *pvParameters)
{
	u8 task2_num=0;
	
	while(1)
	{
		task2_num++;	//任务2执行次数加1 注意task1_num2加到255的时候会清零!!
        LED1=!LED1;
		printf("任务2已经执行:%d次\r\n",task2_num);
        vTaskDelay(1000);             //延时1s,也就是1000个时钟节拍	
	}
}


四、任务的挂起与恢复

猜你喜欢

转载自blog.csdn.net/qq_40818798/article/details/89646804