FreeRTOS学习之路二:任务定义
任务主体
在FReeRTOS中,一般来说任务就是一个不能返回的函数。
void task1(void *parg)
{
/*无限循环,不能返回*/
while(1)
{
/*任务主体,也就是你想做的事*/
}
}
这就是一个任务,有了任务,就得有一个任务栈,而任务栈又是什么呢
任务栈
回想一下学c语言和单片机的时候,代码主体里面有一些全局变量、中断服务子函数程序等等,这些资源我们该放在哪里呢,这些资源我们就是放在任务栈里,任务栈实质上就是一块存储区域,这里我们可以直接定义一个数组来表明任务栈。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiYTMfhHLlN3XnxCM38FdsYkRGZkRG9lcvx2bjxCMy8VZ6l2cs0zYU5UNJR1TykFROZmQUJWQClGVF5UMR9Fd4VGdsATNfd3bkFGazxycykFaKdkYzZUbapXNXlleSdVY2pESa9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLlNjM5ETYyMDZmNWNzETY5EDOwQjZkhTNhVTMzkjYxI2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
上面这张图就是我们自己定义的任务栈、任务主体入口函数、全局变量、返回地址等等就需要存放在任务栈里面。
任务控制块
任务主体有了,任务栈有了,任务列表也有了,我们需要一个操作符也就是一个结构体来统一三者。
typedef struct tskTaskControlBlock
{
/*< 任务栈的栈顶指针 */
volatile uint32_t *pxTopOfStack;
/*< 任务列表的节点指针,后续可以通过这个挂在任务列表上 */
ListItem_t xStateListItem;
/*< 事件列表节点指针,这个暂时先不用管,后续会讲 */
ListItem_t xEventListItem;
/*< 任务优先级 */
unsigned long uxPriority;
int xTicksToDelay;
/*< 任务栈起始地址,也就是我们定义的数组起始地址 */
int *pxStack;
/*< 任务名称 */
char pcTaskName[ configMAX_TASK_NAME_LEN ];
}tskTCB;
typedef tskTCB TCB_t;
任务创建函数
任务的栈,任务的函数实体,任务的控制块最终需要联系起来才能由系统进行统一调度。那么这个联系的工作就由任务创建函数xTaskCreateStatic()来实现。
注意:此函数是用的静态内存,需要将控制文件宏定义开关打开,此处我们只是学习,暂时不用去管宏开关,静态内存明白了,动态内存的任务创建函数大同小异。
void* xTaskCreateStatic(void* pxTaskCode,/*函数入口地址,就是函数名*/
const char * const pcName,/*任务名称*/
const int ulStackDepth,/*任务栈大小,即是数组大小*/
void * const pvParameters,/*任务形参*/
unsigned long uxPriority,/*任务优先级*/
int * const puxStackBuffer,/*任务栈起始地址*/
TCB_t * const pxTaskBuffer /*任务控制块*/
)
{
TCB_t *pxNewTCB;
void* xReturn;
configASSERT( puxStackBuffer != NULL );/*断言,先不用管*/
configASSERT( pxTaskBuffer != NULL );/*断言,先不用管*/
if( ( pxTaskBuffer != NULL ) && ( puxStackBuffer != NULL ) )
{
/*下面两句则是让任务控制块的任务栈起始地址指针去指向我们传进来的数组也就是任务栈*/
pxNewTCB = ( TCB_t * ) pxTaskBuffer;
pxNewTCB->pxStack = ( StackType_t * ) puxStackBuffer;
/*这是一个宏开关,由于我们选择的是静态内存创建则不执行下列if宏*/
#if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 )
{
pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_AND_TCB;
}
#endif
/*直接跳到这里来 */
/*这个函数才是真正创建了任务, 将栈顶指针,任务名字,任务入口等初始化 */
prvInitialiseNewTask( pxTaskCode, pcName, ulStackDepth, pvParameters, uxPriority, &xReturn, pxNewTCB, NULL );
/*这个函数则将任务添加到任务列表上 */
prvAddNewTaskToReadyList( pxNewTCB );
}
else
{
xReturn = NULL;
}
/*最后返回的这个,也就是已经初始化好、关联好了的任务控制块*/
return xReturn;
}
对prvInitialiseNewTask函数和prvAddNewTaskToReadyList函数想过多理解的话可以去看看源码,这里我就只注释它的作用。
到此为止,一个任务控制块已经挂在任务列表上,可以通过任务控制块去访问任务主体、任务栈、任务优先级等,这个至关重要。
[1]: 参考《FreeRTOS 内核实现与应用开发实战指南