目录
前面我们已经梳理了 FreeRTOS 中链表的相关内容,从本篇开始,我们将学习什么是任务,以及动态和静态创建任务的方法。
我们在前面提到过,任务就是一个个不断执行功能的死循环函数。在裸机系统中,单片机实现的所有功能都放在while循环下。在 FreeRTOS 中也是一样,只不过将不同的功能放在不同的死循环函数中,由调度器来管理这些任务的运行。
在程序运行时,全局变量与局部变量的定义,中断发生时当前状态的存储,函数返回地址等都需要内存来存放。这些内容都存放在栈里面。在裸机系统中,上述所有的东西都存在同一段栈空间内。
对于操作系统来讲,栈的分配与释放均由其自动实现。如果把裸机系统的循环看作一个任务的话,由于操作系统存在多个任务且任务间相互独立,所以每个任务都有一段独立的栈空间。
任务控制块
在 task.c 中,有一个任务控制块(TCB)的声明。TCB就相当于任务的身份证,包括任务的所有信息,比如任务的栈指针, 任务名等。通过任务控制块,操作系统可以通过任务控制块来实现对任务的全部操作。
下面为TCB结构体声明。
typedef struct tskTaskControlBlock
{
volatile StackType_t *pxTopOfStack;
#if ( portUSING_MPU_WRAPPERS == 1 )
xMPU_SETTINGS xMPUSettings;
#endif
ListItem_t xStateListItem;
ListItem_t xEventListItem;
UBaseType_t uxPriority;
StackType_t *pxStack;
char pcTaskName[ configMAX_TASK_NAME_LEN ];
#if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) )
StackType_t *pxEndOfStack;
#endif
#if ( portCRITICAL_NESTING_IN_TCB == 1 )
UBaseType_t uxCriticalNesting;
#endif
#if ( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxTCBNumber;
UBaseType_t uxTaskNumber;
#endif
#if ( configUSE_MUTEXES == 1 )
UBaseType_t uxBasePriority;
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;
#endif
#if ( configUSE_NEWLIB_REENTRANT == 1 )
struct _reent xNewLib_reent;
#endif
#if( configUSE_TASK_NOTIFICATIONS == 1 )
volatile uint32_t ulNotifiedValue;
volatile uint8_t ucNotifyState;
#endif
#if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )
uint8_t ucStaticallyAllocated;
#endif
#if( INCLUDE_xTaskAbortDelay == 1 )
uint8_t ucDelayAborted;
#endif
} tskTCB;
以下对一些重要内容进行解释。
pxTopOfStack:栈顶指针
xStateListItem:链表节点。在链表部分,节点内有个TCB指针指向该节点;在TCB中,就是通过这个指针来指向这个节点。
xEventListItem:这个是指向事件链表的指针,功能和上个一样。
uxPriority:优先级。
pxStack:任务栈起始地址。
pcTaskName:任务名称,内容格式为字符串,长度由宏 configMAX_TASK_NAME_LEN 来控制,这个宏在 FreeRTOSConfig.h 中定义,默认为 16。
接下来,我们就开始进行简单实战来验证。
环境准备
无论是创建动态还是静态任务,我们都需要做一些提前准备,比如配置创建任务方式的宏,还有硬件初始化等。
任务创建宏
首先,我们需要在 FreeRTOSConfig.h 中定义支持动态和静态声明的宏。这两个宏在 FreeRTOS.h 中有相关定义,在其中如果我们没有定义下面这两个宏,系统则会默认定义他们。<