红叶何时落水
每个任务都有他特有的属性,这些属性记录着当前任务的状态
相当于新建一个任务对象 对象类为TCB_t/tskTCB动态创建
typedef struct tskTaskControlBlock
{volatile StackType_t *pxTopOfStack;//指向该任务堆栈的最后一项 pxTopOfStack为堆栈栈顶指针 32位 ListItem_t xStateListItem;//定义一个链表 项 ,这个项有着必要的信息 状态链表项 用来记录该任务的状态,是挂起还是就绪ListItem_t xEventListItem;//事件列表项 用于记录自己是否处于延时列表中 UBaseType_t uxPriority; //任务优先级 运行过程中要被识别的优先级 可以被优先级继承机制改变 StackType_t *pxStack;//任务堆栈起始地址 分配的堆栈的起始地址 char pcTaskName[ configMAX_TASK_NAME_LEN ];//任务名字 没啥用#if ( configUSE_MUTEXES == 1 )UBaseType_t uxBasePriority;//任务基础优先级,优先级反转的时候用到,就是说他初始化的那个优先级 UBaseType_t uxMutexesHeld;//任务获取到的互斥信号量个数#endif#if( configUSE_TASK_NOTIFICATIONS == 1 ) volatile uint32_t ulNotifiedValue;//任务通知值 volatile uint8_t ucNotifyState;//任务状态 用于任务通知操作#endif
} tskTCB;
这个是上面含有的链表 项 的定义
struct xLIST_ITEM
{configLIST_VOLATILE TickType_t xItemValue; /*< 列表项的值,降序排列 */struct xLIST_ITEM * configLIST_VOLATILE pxNext; /*< 链表连接 */struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /*< 双向链表 */void * pvOwner; /*<指向那个任务对象TCB_t 中的变量xStateListItem的pvOwner指向TCB,当任务处于就绪态时,pvContainer指向就绪列表其余状态,同理 */ void * configLIST_VOLATILE pvContainer; /*<指向列表项所属的列表 */
};typedef struct xLIST_ITEM ListItem_t;
创建一个新任务
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, //任务函数 这里是一个指针常量,表示函数地址const char * const pcName, //任务名 没啥用const uint16_t usStackDepth, //堆栈大小 单位是字而非字节 不包括TCBvoid * const pvParameters, //任务函数的参数,指针类型,建议结构体传参UBaseType_t uxPriority, //优先级TaskHandle_t * const pxCreatedTask )//句柄 其实就是任务堆栈
删除任务
vTaskDelete();//删除任务,并在空闲任务中释放空间(堆栈以及TCB的空间)TCB空间如果是动态创建任务的话,就是自动分配自动回收,如果是静态创建,就需要初始化一下,而且必须是初始化StaticTask_t该结构体,而不是值初始化一个结构体指针
将该任务挂起,只有调用任务恢复函数 vTaskResume()或 xTaskResumeFromISR()才会恢复该任务,其他的队列,任务通知,delay都不能将其唤醒
xTaskToSuspend(句柄);句柄为NULL时挂起自己 可以通过函数 xTaskGetHandle()来根据任务名字
xTaskResumeFromISR();//中断中调用这类函数,必须检查一下中断结束后的任务与进入中断时的任务是不是同一个,如果不是,就要进行上下文切换,就是改变PSP的指向
YieldRequired=xTaskResumeFromISR(Task2Task_Handler);//恢复任务 2
if(YieldRequired==pdTRUE)
{/*如果函数 xTaskResumeFromISR()返回值为 pdTRUE,那么说明要恢复的这个任务的任务优先级等于或者高于正在运行的任务(被中断打断的任务),所以在退出中断的时候一定要进行上下文切换!*/portYIELD_FROM_ISR(YieldRequired);
}
删除任务
vTaskDelete();
1. 先获取要删除任务的句柄 参数为NULL,就获取调用该任务的句柄 prvGetTCBFromHandle()
#define prvGetTCBFromHandle( pxHandle ) ( ( ( pxHandle ) == NULL ) ? ( TCB_t * ) pxCurrentTCB : ( TCB_t * ) ( pxHandle ) )
2. 从就绪列表中删除该任务
3. 列表优先级切换
#define taskRESET_READY_PRIORITY( uxPriority ) \{ \if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ ( uxPriority ) ] ) ) == ( UBaseType_t ) 0 ) \//判断这个优先级列表里的任务还有没有,如果这个优先级的列表里没有了任务,就执行下面的语句{ \portRESET_READY_PRIORITY( ( uxPriority ), ( uxTopReadyPriority ) ); \//重置优先级} \}
4. 检查是否处于延迟列表中,是的话,从延时列表中删掉
5. 如果要删除的任务正在运行,就将其添加到xTasksWaitingTermination列表中,然后由空闲任务释放其空间
++uxDeletedTasksWaitingCleanUp;表示有多少个任务要被清理
6. 如果要删除的任务没有在运行 prvDeleteTCB() 释放空间 并--uxCurrentNumberOfTasks;表示当前可运行任务数减一
prvResetNextTaskUnblockTime();重新计算下一个任务解锁的时间static void prvResetNextTaskUnblockTime( void ){TCB_t *pxTCB;if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE ){xNextTaskUnblockTime = portMAX_DELAY;}else{( pxTCB ) = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );//获取列表中的第一个任务xNextTaskUnblockTime = listGET_LIST_ITEM_VALUE( &( ( pxTCB )->xStateListItem ) );//延时时间等于任务结构体里的value值,升序排列,value值越大,排的越后面,延时越长}}
7. 如果要删除的任务正在运行,切换上下文
任务挂起
vTaskSuspend() 不能重复挂起,就是说,只要调用恢复API就会被解除悬挂
1. 获取要挂起任务的句柄
2. 就绪,延时列表中移除该任务,并判断当前优先级 它会使xdelay失效,那就意味着接触悬挂,立马加入就绪状态
3. 插入挂起列表 vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) );
4. prvResetNextTaskUnblockTime();
5. 如果该任务正在运行,切换上下文 portYIELD_WITHIN_API();
6. 如果调度器不工作,那么手动切换当前任务指针 vTaskSwitchContext();
恢复被挂起的任务
vTaskResume()
{TCB_t * const pxTCB = ( TCB_t * ) xTaskToResume; if( ( pxTCB != NULL ) && ( pxTCB != pxCurrentTCB ) ) {taskENTER_CRITICAL(); {if( prvTaskIsTaskSuspended( pxTCB ) != pdFALSE ) {( void ) uxListRemove( &( pxTCB->xStateListItem ) ); prvAddTaskToReadyList( pxTCB );if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) {taskYIELD_IF_USING_PREEMPTION();}}}taskEXIT_CRITICAL(); }
}