嵌入式FreeRTOS学习八,xTaskCreate创建任务的细节以及恢复中断任务实现
创始人
2024-01-22 07:36:50
0

一.创建任务函数xTaskCreate

任务也不是很复杂的东西,任务也就是一个函数xTaskCreate。简单得说,创建一个任务,你得提供它的执行函数,你得提供它的栈的大小,函数的执行空间,函数的优先级等重要的条件。因为任务在运行中,任务函数有调用关系,有局部变量,这些都保存在任务的栈里面;任务有可能被切换,有可能被暂停,这时候CPU寄存器中断现场数据都保存在栈里面。

BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,const char * const pcName, 
/*lint !e971 Unqualified char types are allowed for strings and single characters only. */const configSTACK_DEPTH_TYPE usStackDepth,void * const pvParameters,UBaseType_t uxPriority,TaskHandle_t * const pxCreatedTask )

参数说明:
(1)TaskFunction_t   : typedef   void   (*TaskFunction_t)( void * );    函数指针
(2)const  char  *  const  pcName  :  任务名字
(3)configSTACK_DEPTH_TYPE   :   #define  configSTACK_DEPTH_TYPE   uint16_t   是无符号的2字节数值,表示栈的深度大小,实际由malloc函数分配大小
(4)void  *  const  pvParameters  :是要传入的参数
(5)UBaseType_t   uxPriority   :    typedef  unsigned   short   UBaseType_t;  是一个无符号的整形数,表示优先级的大小,数值越大优先级越大
(6)TaskHandle_t  *  const   pxCreatedTask :这里面有一个TCB结构体指针,传出去的参数

 

TCB_t的全称为Task Control Block,也就是任务控制块,这个结构体包含了一个任务所有的信息,但是源代码中存在大量的条件配置选项,以下屏蔽掉的都是可以通过条件来配置的选项,通过条件来决定哪些定义使用或者不用,暂时不需要用到这些,对条件配置项进行屏蔽,TCB最主要的参数在上面它的定义以及相关变量的解释如下

typedef struct tskTaskControlBlock             {// 这里栈顶指针必须位于TCB第一项是为了便于上下文切换操作,详见xPortPendSVHandler中任务切换的操作。volatile StackType_t    *pxTopOfStack;    // 表示任务状态,不同的状态会挂接在不同的状态链表下ListItem_t            xStateListItem;    // 事件链表项,会挂接到不同事件链表下ListItem_t            xEventListItem;        // 任务优先级,数值越大优先级越高UBaseType_t            uxPriority;            // 指向堆栈起始位置,这只是单纯的一个分配空间的地址,可以用来检测堆栈是否溢出StackType_t            *pxStack;            // 任务名char                pcTaskName[ configMAX_TASK_NAME_LEN ];/*
//以下屏蔽掉的都是可以通过条件来配置的选项,通过条件来决定哪些定义使用或者不用,暂时不需要用到这 
//些,屏蔽掉,TCB最主要的参数在上面
//#####################################################################################// MPU相关暂时不讨论#if ( portUSING_MPU_WRAPPERS == 1 )xMPU_SETTINGS    xMPUSettings;        #endif// 指向栈尾,可以用来检测堆栈是否溢出#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// 任务的一个标签值,可以由用户自定义它的意义,例如可以传入一个函数指针可以用来做Hook    函数调用#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// 支持NEWLIB的一个变量#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// 错误标识#if( configUSE_POSIX_ERRNO == 1 )int iTaskErrno;#endif
//###################################################################################
*/} tskTCB;typedef tskTCB TCB_t;

=========================================================================

二.创建任务的具体内部细节

以简单的任务创建函数为例,这里分别创建了三个简单任务vTask1,vTask2,vTask3

void vTask1( void *pvParameters )
{    /* 任务函数的主体一般都是无限循环 */for( ;; ){flagIdleTaskrun = 0;flagTask1run = 1;flagTask2run = 0;flagTask3run = 0;/* 打印任务的信息 */printf("T1\r\n");				}
}
void vTask2( void *pvParameters )
{	/* 任务函数的主体一般都是无限循环 */for( ;; ){flagIdleTaskrun = 0;flagTask1run = 0;flagTask2run = 1;flagTask3run = 0;/* 打印任务的信息 */printf("T2\r\n");				}
}void vTask3( void *pvParameters )
{	const TickType_t xDelay5ms = pdMS_TO_TICKS( 5UL );		/* 任务函数的主体一般都是无限循环 */for( ;; ){flagIdleTaskrun = 0;flagTask1run = 0;flagTask2run = 0;flagTask3run = 1;/* 打印任务的信息 */printf("T3\r\n");				// 如果不休眠的话, 其他任务无法得到执行vTaskDelay( xDelay5ms );}
}
//主函数的实现
int main( void )
{prvSetupHardware();	xTaskCreate(vTask1, "Task 1", 1000, NULL, 0, NULL);xTaskCreate(vTask2, "Task 2", 1000, NULL, 0, NULL);xTaskCreate(vTask3, "Task 3", 1000, NULL, 2, NULL);/* 启动调度器 */vTaskStartScheduler();/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */return 0;
}

三.TCB任务结构体在RAM内存中的存在

下面的图中,将一整个tsTaskControlBlock结构体代码数据放入RAM内存中,表示出在内存中分配一个TCB结构体的效果(只画图表示效果,实际上可能不完全一样);可以看到,在RAM内存中分配的栈空间保存的数据有:

栈顶指针pxTopOfStack;  指向划分出来的内存空间的最后一个数据保存的位置
状态链表xStateListItem;
事件链表 xEventListItem;    
任务优先级 uxPriority;         
指向堆栈起始位置指针 pxStack;指向划分出来的内存空间的起始地址位置          
任务名 pcTaskName[ configMAX_TASK_NAME_LEN ];

 =========================================================================

四.任务函数创建时栈空间分配和大小的问题

//任务创建函数
xTaskCreate(vTask1, "Task 1", 1000, NULL, 0, NULL);

在创建这个任务的时候,传进来的参数是保存在哪里呢?首先看传进来的栈的大小这个参数1000,这时需要弄明白两个问题。第一是栈是从哪里分配?第二是栈的大小怎么确定?

第一个问题栈是从哪里分配?栈其实就是一块空闲的内存,FreeRTOS的heap2.cpp文件中关于是定义了一个巨大的全局数组ucHeap,这个数组没人使用,作为一块空闲的内存,在以后的栈的空间的分配就从这个巨大的数组里面划分可用的内存,来给某个任务当做栈来使用,如上图所示;由宏定义可知数组的大小为17*1024个字节。

第二个问题,例子中栈的大小怎么确定?可以根据程序员出入的值的大小进行分配,例子中划分出1000*4字节的内存,然后内存的起始空间保存在pxStack指针中。

 =========================================================================

五.任务函数创建时函数指针和参数保存问题和作用

//任务创建函数
xTaskCreate(vTask1, "Task 1", 1000, NULL, 0, NULL);

第一个参数是函数指针xTaskCreat其实就是函数的地址addrF,当我们想要去启动这个任务,或者调用这个函数的时候,我们可以让CPU中的R15寄存器,也就是PC寄存器的值等于函数的地址。addrF即可。

第四个参数是随着函数传入的参数值,通常保存在R0寄存器中。

=========================================================================

六.任务处于暂停的状态恢复运行

系统从定义的全局数组ucHeap中分配可用的空间给任务函数xTaskCreate使用,例子中分配了1000*4的内存空间,pxStack指向内存开始的地址位置,pxTopOfStack指向内存中最后一个数据保存的位置。

我们在创建任务的时候,任务函数xTaskCreate已经创建了这部分内存,帮我们修改了RAM内存里面的内容,在TCB结构体里面,我们没有看到传入的参数和函数指针,其实任务函数已经把这些值写入了CPU内存中的R15和R0等寄存器,以便恢复任务时使用。

刚创建任务的时候,任务还没有运行,属于某种暂停状态,当任务被中断,系统将当前任务的寄存器入栈进行现场保护,其中包括R15函数返回地址,R0参数等;然后开始运行中断任务,中断任务结束,则开始恢复RAM内存中栈里面的各种寄存器现场,从R15函数返回地址开始执行,跳转回上一个被中断的任务,继续执行。如上图所示。

 

 

相关内容

热门资讯

美联储会议纪要:SRF 有助于... 多数参会者就 SRF 交易中央清算的可能性发表了意见 。在发表意见的参会者中,几乎所有人都指出,SR...
因侵害商标权纠纷,水星家纺起诉... 天眼查APP显示,近日,上海水星家用纺织品股份有限公司新增一则开庭公告,案由为“侵害商标权纠纷”,原...
因合同纠纷,王力安防起诉永康市... 天眼查APP显示,近日,王力安防科技股份有限公司新增一则开庭公告,案由为“合同纠纷”,原告为王力安防...
俄罗斯将分流日本旅游客源!普京... 俄罗斯总统普京表示,针对中国公民赴俄罗斯免签政策将于近期生效。据悉,这一政策的落地,也将与中方此前已...
因建设工程合同纠纷,漳州市永通... 天眼查APP显示,近日,漳州市永通运输服务有限公司新增一则开庭公告,案由为“建设工程合同纠纷”,原告...
因买卖合同纠纷,南方泵业起诉吉... 天眼查APP显示,近日,南方泵业股份有限公司新增一则开庭公告,案由为“买卖合同纠纷”,原告为南方泵业...
因买卖合同纠纷,深圳市液力士科... 天眼查APP显示,近日,深圳市液力士科技有限公司新增一则开庭公告,案由为“买卖合同纠纷”,原告为深圳...
华发股份子公司起诉吉林天茂 请... 11月19日,珠海地产龙头华发股份(SH600325,股价4.77元,市值131.28亿元)公告称,...
因承揽合同纠纷,太原市星原机械... 天眼查APP显示,近日,太原市星原机械贸易有限公司新增一则开庭公告,案由为“承揽合同纠纷”,原告为太...
郑州拟推行房屋体检制度:率先对... 11月18日,河南省郑州市住房保障和房地产管理局发布《关于印发郑州市城镇房屋安全管理“三项制度”试点...