你知道FreeRTOS的故事吗?

你知道FreeRTOS的故事吗?

RTOS系统的核心就是任务管理,FreeRTOS也例外。

1. 多任务系统

​ ✨51、AVR、STM32单片机裸机(未使用系统)一般都是在main函数的while(1)一个大循环里完成所有的操作,即应用程序是一个无限的循环,循环中调用相应的函数完成所需的处理。有时候我们也需要中断完成一些处理。相当于多任务而言,这就是单任务系统,也称作前后台系统,中断服务函数作为前台程序大循环while(1)为后台程序
在这里插入图片描述

  1. 前后台系统实时性差,前后台系统各个任务都是排队等着轮流执行。
  2. 所有任务的优先级都是一样的。

多任务系统会把一个大问题“分而治之”,把大问题划分成很多个小问题,逐步的把小问题解决掉,大问题也就随之解决了,这些小问题可以单独的作为一个小任务来处理。这些小任务是并发处理的,注意,并不是说同一时刻一起执行很多个任务,而是由于每个任务执行的时间很短,导致看起来像是同一时刻执行了很多个任务一样。

​ 多个任务带来了一个新的问题,究竟哪个任务先运行,哪个任务后运行呢?完成这个功能的东西在RTOS系统中叫做任务调度器。不同的系统其任务调度器的实现方法也不同,比如FreeRTOS是一个抢占式的实时多任务系统,那么其任务调度器也是抢占式的,运行过程如图所示:
在这里插入图片描述

2. FreeRTOS任务

2.1 任务的特性

  1. 在使用RTOS的时候一个实时应用可以作为一个独立的任务。

  2. 每个任务都有自己的运行环境,不依赖于系统中其他任务或者RTOS调度器。

  3. 任何一个时间点只能有一个任务运行,具体运行哪个任务是由RTOS调度器来决定,RTOS调度器因此就会重复的开启、关闭每个任务。

  4. 任务不需要了解RTOS调度器的具体行为。

  5. RTOS调度器的职责是确保当一个任务开始执行的时候其上下文环境(寄存器值,堆栈内容等)和上一次任务退出的时候相同。为了做到这一点,每个任务都必须都有堆栈,当任务切换的时候将上下文环境保存在堆栈中,这样当任务再次执行的时候就可以从堆栈中取出上下文环境,任务恢复运行。

  1. 简单
  2. 没有使用限制
  3. 支持抢占
  4. 支持优先级
  5. 每个任务都拥有堆栈导致了RAM使用量增大
  6. 如果使用抢占的话必须仔细考虑重入的问题

2.2 任务状态

运行态

​ 当一个任务正在运行时,那么就说这个任务处于运行态,处于运行态的任务就是当前正在使用处理器的任务。如果使用的是单核处理器的话那么不管在任何时刻永远都只有一个任务处于运行态。

就绪态

​ 处于就绪态的任务是那些已经准备就绪(这些任务没有被阻塞或者挂起),可以运行的任务,但是处于就绪态的任务还没有运行,因为有一个同优先级或者更高优先级的任务正在运行!

阻塞态

​ 如果一个任务当前正在等待某个外部事件的话就说它处于阻塞态,比如说如果某个任务调用了函数vTaskDelay()的话就会进入阻塞态,直到延时周期完成。任务在等待队列、信号量、事件组、通知或互斥信号量的时候也会进入阻塞态。任务进入阻塞态会有一个超时时间,当超过这个超时时间任务就会退出阻塞态,即使所等待的事件还没有来临!

挂起态

​ 像阻塞态一样,任务进入挂起态以后也不能被调度器调用进入运行态,但是进入挂起态的任务没有超时时间。任务进入和退出挂起态通过调用函数vTaskSuspend()和xTaskResume()。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WEWuyWMu-1621431936262)(C:\Users\53557\AppData\Roaming\Typora\typora-user-images\image-20210519211800935.png)]

2.3 任务优先级

​ 每个任务都可以分配一个从0-(configMAX PRIORITIES-1)的优先级configMAX PRIORITIES在文件FreeRTOSConfig.h中有定义,前面我们讲解FreeRTOS系统配置的时候已经讲过了。如果所使用的硬件平台支持类似计算前导零这样的指令(可以通过该指令选择下一个要运行的任务,Cortex-M处理器是支持该指令的),并且宏configUSE PORTOPTIMISED TASKSELECTION 也设置为了 1,那么宏configMAXPRIORITIES不能超过32!也就是优先级不能超过32级。其他情况下宏configMAX PRIORITIES可以为任意值,但是考虑到RAM的消耗,宏configMAX PRIORITIES最好设置为一个满足应用的最小值。
​ 优先级数字越低表示任务的优先级越低,0的优先级最低,configMAX PRIORITIES-1的优先级最高。空闲任务的优先级最低,为0。
​ FreeRTOS调度器确保处于就绪态或运行态的高优先级的任务获取处理器使用权,换句话说就是处于就绪态的最高优先级的任务才会运行。当宏configUSE-TIME-SLICING定义为1的时候多个任务可以共用一个优先级,数量不限。默认情况下宏configUSE-TIMESLICING在文件FreeRTOS.h中已经定义为1。此时处于就绪态的优先级相同的任务就会使用时间片轮转调度器获取运行时间。

/***************************************************************************************************************/
/*                                        FreeRTOS基础配置配置选项                                              */
/***************************************************************************************************************/
#define configUSE_PREEMPTION					1                       //1使用抢占式内核,0使用协程
#define configUSE_TIME_SLICING					1						//1使能时间片调度(默认式使能的)
#define configUSE_PORT_OPTIMISED_TASK_SELECTION	1                       //1启用特殊方法来选择下一个要运行的任务
                                                                        //一般是硬件计算前导零指令,如果所使用的
                                                                        //MCU没有这些硬件指令的话此宏应该设置为0!

2.4 任务实现

​ 在使用FreeRTOS的过程中,我们要使用函数xTaskCreate()或xTaskCreatestatic()来创建任务,这两个函数的第一个参数pxTaskCode,就是这个任务的任务函数。什么是任务函数?任务函数就是完成本任务工作的函数。我这个任务要干嘛?要做什么?要完成什么样的功能都是在这个任务函数中实现的。比如我要做个任务,这个任务跑马灯,那么这个跑马灯的程序就是任务函数中实现的.FreeRTOS官方给出的任务函数模板如下;

/*-----------------------------------------------------------
 * TASK CREATION API
 *----------------------------------------------------------*/

/**
 * task. h
 *<pre>
 BaseType_t xTaskCreate(
							  TaskFunction_t pvTaskCode,
							  const char * const pcName,
							  uint16_t usStackDepth,
							  void *pvParameters,
							  UBaseType_t uxPriority,
							  TaskHandle_t *pvCreatedTask
						  );</pre>
 *
 * Create a new task and add it to the list of tasks that are ready to run.
 *
 * Internally, within the FreeRTOS implementation, tasks use two blocks of
 * memory.  The first block is used to hold the task's data structures.  The
 * second block is used by the task as its stack.  If a task is created using
 * xTaskCreate() then both blocks of memory are automatically dynamically
 * allocated inside the xTaskCreate() function.  (see
 * http://www.freertos.org/a00111.html).  If a task is created using
 * xTaskCreateStatic() then the application writer must provide the required
 * memory.  xTaskCreateStatic() therefore allows a task to be created without
 * using any dynamic memory allocation.
 *
 * See xTaskCreateStatic() for a version that does not use any dynamic memory
 * allocation.
 *
 * xTaskCreate() can only be used to create a task that has unrestricted
 * access to the entire microcontroller memory map.  Systems that include MPU
 * support can alternatively create an MPU constrained task using
 * xTaskCreateRestricted().
 *
 * @param pvTaskCode Pointer to the task entry function.  Tasks
 * must be implemented to never return (i.e. continuous loop).
 *
 * @param pcName A descriptive name for the task.  This is mainly used to
 * facilitate debugging.  Max length defined by configMAX_TASK_NAME_LEN - default
 * is 16.
 *
 * @param usStackDepth The size of the task stack specified as the number of
 * variables the stack can hold - not the number of bytes.  For example, if
 * the stack is 16 bits wide and usStackDepth is defined as 100, 200 bytes
 * will be allocated for stack storage.
 *
 * @param pvParameters Pointer that will be used as the parameter for the task
 * being created.
 *
 * @param uxPriority The priority at which the task should run.  Systems that
 * include MPU support can optionally create tasks in a privileged (system)
 * mode by setting bit portPRIVILEGE_BIT of the priority parameter.  For
 * example, to create a privileged task at priority 2 the uxPriority parameter
 * should be set to ( 2 | portPRIVILEGE_BIT ).
 *
 * @param pvCreatedTask Used to pass back a handle by which the created task
 * can be referenced.
 *
 * @return pdPASS if the task was successfully created and added to a ready
 * list, otherwise an error code defined in the file projdefs.h
*
* Example usage:
   <pre>
 // Task to be created.
 void vTaskCode( void * pvParameters )
 {
	 for( ;; )
	 {
		 // Task code goes here.
	 }
 }

2.5 任务控制模块

​ FreeRTOS的每个任务都有一些属性需要存储,FreeRTOS把这些属性集合到一起用一个结构体来表示,这个结构体叫做任务控制块:TCB_t,在使用函数xTaskCreate()创建任务的时候就会自动的给每个任务分配一个任务控制块。在老版本的FreeRTOS中任务控制块叫做tskTCB,新版本重命名为TCB_t,但是本质上还是tskTCB,此结构体在文件tasks.c中有定义,如下:

#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )

	BaseType_t xTaskCreate(	TaskFunction_t pxTaskCode,
							const char * const pcName,
							const uint16_t usStackDepth,
							void * const pvParameters,
							UBaseType_t uxPriority,
							TaskHandle_t * const pxCreatedTask ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
	{
	TCB_t *pxNewTCB;  (1)
	BaseType_t xReturn;
        
/*
 * Task control block.  A task control block (TCB) is allocated for each task,
 * and stores task state information, including a pointer to the task's context
 * (the task's run time environment, including register values)
 */
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;
        
/* The old tskTCB name is maintained above then typedefed to the new TCB_t name
below to enable the use of older kernel aware debuggers. */
typedef tskTCB TCB_t;

2.6 任务堆栈

​ FreeRTOS之所以能正确的恢复一个任务的运行就是因为有任务堆栈在保驾护航,任务调度器在进行任务切换的时候会将当前任务的现场(CPU寄存器值等)保存在此任务的任务堆栈中,等到此任务下次运行的时候就会先用堆栈中保存的值来恢复现场,恢复现场以后任务就会接着从上次中断的地方开始运行。
​ 创建任务的时候需要给任务指定堆栈,如果使用的函数xTaskCreate()创建任务(动态方法)的话那么任务堆栈就会由函数xTaskCreate()自动创建。
​ 如果使用函数xTaskCreateStatic()创建任务(静态方法)的话就需要程序员自行定义任务堆栈,然后堆栈首地址作为函数的参数puxStackBuffer传递给函数,如下:

TaskHandle_t xTaskCreateStatic(	TaskFunction_t pxTaskCode,
									const char * const pcName,
									const uint32_t ulStackDepth,
									void * const pvParameters,
									UBaseType_t uxPriority,
									StackType_t * const puxStackBuffer,   (1)
									StaticTask_t * const pxTaskBuffer ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
	{
	TCB_t *pxNewTCB;
    
    
(1) 任务堆栈,需要用户定义,然后将堆栈首地址传递给这个参数。

堆栈大小

​ 我们不管是使用函数xTaskCreate()还是xTaskCreateStatic()创建任务都需要指定任务堆栈大小。任务堆栈的数据类型为StackType_t,StackType_t本质上是uint32_t,在portmacro.h中有定义,如下:

typedef unsigned           int uint32_t;
#define portSTACK_TYPE	uint32_t
#define portBASE_TYPE	long

typedef portSTACK_TYPE StackType_t;

​ 可以看出StackType_t类型的变量为4个字节,那么任务的实际堆栈大小就应该是我们所定义的4倍

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值