ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

八. 任务延时列表的实现

八. 任务延时列表的实现 在本章之前 为了实现任务的阻塞延时在任务控制块(TCB)中内置了一个延时变量xTicksToDelay。每当任务需要延时的时候就初始化 xTicksToDelay 需要延时的时间 然后将任务挂起这里的挂起只是将任务在优先级位图表 uxTopReadyPriority 中对应的位清零并不会将任务从就绪列表中删除。一个32位的变量每一位就代表每一个优先级假如25个优先级的一个任务需要挂起怎么做的也是先将它25位清零就回去运行24位的这个任务如果恢复的时候就将25位置一。挂起的时候在更新时基的函数中就把任务中的xTicksToDelay的值自减为什么自减就是为了判断是否到了时间如果不自减的话怎么判断他有没有到时间。如果到了时间就将该位 置一恢复该任务的运行。这种延时有什么缺点呢每次更新时基的时候都要把任务扫描一遍有很多任务时太耗时间也有一个优点就是方便理解。这里我们慢慢过渡一下延时列表有什么作用它的实现有什么好处在FreeRTOS中有一个任务延时列表其实有两个为了方便讲解原理我们假设一个但它们的原理是一样的当任务需要延时的时候则先将任务挂起即先将任务从就绪列表删除然后插入到任务延时列表同时更新下一个任务的解锁时刻变量xNextTaskUnblockTime的值。xNextTaskUnblockTime的值等于系统时基计数器的值xTickCount加上任务需要延时的值xTicksToDelay。任务延时列表维护着一条双向链表每个节点代表正在延时的任务节点按照延时时间大小做升序排列。每次在SysTick中断就比较一下系统时基计数器的值xTickCount与下一个任务的解锁时刻变量xNextTaskUnblockTime的值。假设当前xTickCount100,也就是说现在系统已经跑了100个Tick,此刻我们调用了延时的函数延时了50个Tick,那我应该什么时候唤醒它10050150当我系统跑到150个Tick的时候就把任务唤醒。所以xNextTaskUnblockTime150个Tick每次更新系统时基的时候就比较一下xTickCount的值有没有到150假如现在到了110跟150作比较还是没到所以也不用唤醒它假如150刚好到了那么就可以立即去唤醒一个阻塞的任务假如还有个任务延时100个Tick,那唤醒的时间是不是200那100个Tick没到的时候200个Tick肯定也没到。当我唤醒150的时候要更新一下xNextTaskUnblockTime下一个可能也就200了那么系统会继续跑160的时候比较也没到到200的时候我就可以唤醒200的这个东西了。时间没到的时候就单独的记录xTickCount的值就好了。FreeRTOS定义了两个任务延时列表当系统时基计数器xTickCount没有溢出时用一条列表当xTickCount溢出后用另外一条列表。简单来说一条是正常的列表一条是溢出的列表。见下表(在task.c中定义)static List_t xDelayedTaskList1; static List_t xDelayedTaskList2; static List_t * volatile pxDelayedTaskList; static List_t * volatile pxOverflowDelayedTaskList;我们再来实现以下任务延时列表的初始化任务延时列表属于任务列表的一种也是在prvInitialiseTaskLists()函数中初始化(在task.c中添加标号1的内容)/* 初始化任务相关的列表 */ void prvInitialiseTaskLists( void ) { UBaseType_t uxPriority; for( uxPriority ( UBaseType_t ) 0U; uxPriority ( UBaseType_t ) configMAX_PRIORITIES; uxPriority ) { vListInitialise( ( pxReadyTasksLists[ uxPriority ] ) ); } vListInitialise( xDelayedTaskList1 );//初始化列表1(1) vListInitialise( xDelayedTaskList2 );//初始化列表2(1) pxDelayedTaskList xDelayedTaskList1;//将延时列表指向列表1(1) pxOverflowDelayedTaskList xDelayedTaskList2;//将溢出列表指向列表2(1) }接下来我们再实现一个变量xNextTaskUnblockTime:他是一个在task.c中定义的一个静态变量。还有一个溢出的计数我们一起COPY过来。static volatile TickType_t xNextTaskUnblockTime ( TickType_t ) 0U; static volatile BaseType_t xNumOfOverflows ( BaseType_t ) 0;当系统计数器的值xTickCount的值与xNextTaskUnblockTime相等时就表示有任务延时到期了需要将该任务就绪。xNextTaskUnblockTime在vTaskStartScheduler()时初始化一个最大的值portMAX_DELAY放在创建空闲任务之后。见下面代码/* 在task.c的vTaskStartScheduler()中添加 */ xNextTaskUnblockTime portMAX_DELAY; xTickCount ( TickType_t ) 0U;//是一个系统时间的计数值这里的portMAX_DELAY和xTickCount 我们之前都已经提前声明定义过。后面也是修改代码的过程一步步接近目标我们接着往下看1.修改vTaskDelay()需要将当前任务插入到延时列表中在task.c中的vTaskDelay()中修改void vTaskDelay( const TickType_t xTicksToDelay ) { TCB_t *pxTCB NULL; /* 获取当前任务的TCB */ pxTCB pxCurrentTCB; /* 设置延时时间 */ pxTCB-xTicksToDelay xTicksToDelay; /* 将任务从就绪列表移除 */ //uxListRemove( ( pxTCB-xStateListItem ) ); // taskRESET_READY_PRIORITY( pxTCB-uxPriority ); /* 将任务插入到延时列表 */ prvAddCurrentTaskToDelayedList( xTicksToDelay ); /* 任务切换 */ taskYIELD(); }目前这个函数prvAddCurrentTaskToDelayedList()还没有实现。我们实现一下//函数声明 static void prvAddCurrentTaskToDelayedList(TickType_t xTicksToWait)//一定要记得提前声明一下 //函数定义 static void prvAddCurrentTaskToDelayedList(TickType_t xTicksToWait) { TickType_t xTimeToWake; /* 获取系统时基计数器xTickCount的值 */ const TickType_t xConstTickCount xTickCount; /* 将任务从就绪列表中移除 */ if(uxListRemove((pxCurrentTCB-xStateListItem)) (UBaseType_t)0) { /* 将任务在优先级位图中对应的位清除 */ portRESET_READY_PRIORITY(pxCurrentTCB-uxPriority, uxTopReadyPriority); } /* 计算延时到期时系统时基计数器xTickCount的值是多少 */ xTimeToWake xConstTickCount xTicksToWait; //150 - 200 - 300 /* 将延时到期的值设置为节点的排序值 */ listSET_LIST_ITEM_VALUE((pxCurrentTCB - xStateListItem), xTimeToWake); //设置value值 /* 溢出 */ if(xTimeToWake xConstTickCount) { vListInser(pxOverflowDelayedTaskList, (pxCurrentTCB-xStateListItem)); } else /* 没有溢出 */ { vListInser(pxDelayedTaskList, (pxCurrentTCB-xStateListItem)); /* 更新下一个任务解锁时刻变量xNextTaskUnblockTime的值 */ if(xTimeToWake xNextTaskUnblockTime) { xNextTaskUnblockTime xTimeToWake; } } }2.修改xTaskIncrementTick():更新时基在更新的过程中判断是否到期void xTaskIncrementTick( void ) { TCB_t * pxTCB; TickType_t xItemValue; const TickType_t xConstTickCount xTickCount 1; xTickCount xConstTickCount; /* 如果xConstTickCount溢出则切换延时列表 */ if( xConstTickCount ( TickType_t ) 0U ) { taskSWITCH_DELAYED_LISTS(); } /* 最近的延时任务延时到期 */ if( xConstTickCount xNextTaskUnblockTime ) { for( ;; ) { if( listLIST_IS_EMPTY( pxDelayedTaskList ) ! pdFALSE ) { /* 延时列表为空设置xNextTaskUnblockTime为可能的最大值 */ xNextTaskUnblockTime portMAX_DELAY; break; } else /* 延时列表不为空 */ { pxTCB ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); xItemValue listGET_LIST_ITEM_VALUE( ( pxTCB-xStateListItem ) ); /* 直到将延时列表中所有延时到期的任务移除才跳出for循环 */ if( xConstTickCount xItemValue ) { xNextTaskUnblockTime xItemValue; break; } /* 将任务从延时列表移除消除等待状态 */ ( void ) uxListRemove( ( pxTCB-xStateListItem ) ); /* 将解除等待的任务添加到就绪列表 */ prvAddTaskToReadyList( pxTCB ); } } }/* xConstTickCount xNextTaskUnblockTime */ /* 任务切换 */ portYIELD(); }实现taskSWITCH_DELAYED_LISTS()切换延时列表实际就是更换pxDelayedTaskList和pxOverflowDelayedTaskList这两个指针的指向//在task.c中添加 /* *当系统时基计数器溢出的时候延时列表pxDelayedTaskList和 *pxOverflowDelayedTaskList要互相切换 */ #define taskSWITCH_DELAYED_LISTS()\ {\ List_t *pxTemp;\ pxTemp pxDelayedTaskList;\ pxDelayedTaskList pxOverflowDelayedTaskList;\ pxOverflowDelayedTaskList pxTemp;\ xNumOfOverflows;\ prvResetNextTaskUnblockTime();\ }static void prvResetNextTaskUnblockTime( void )//放在前面声明一下 //函数定义 static void prvResetNextTaskUnblockTime( void ) { TCB_t *pxTCB; if( listLIST_IS_EMPTY( pxDelayedTaskList ) ! pdFALSE ) { /* The new current delayed list is empty. Set xNextTaskUnblockTime to the maximum possible value so it is extremely unlikely that the if( xTickCount xNextTaskUnblockTime ) test will pass until there is an item in the delayed list. */ xNextTaskUnblockTime portMAX_DELAY;//设置为最大值表示没有任务阻塞在这里 } else { /* The new current delayed list is not empty, get the value of the item at the head of the delayed list. This is the time at which the task at the head of the delayed list should be removed from the Blocked state. */ ( pxTCB ) ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); xNextTaskUnblockTime listGET_LIST_ITEM_VALUE( ( ( pxTCB )-xStateListItem ) ); } }将此处设置为1相关函数是#define taskRESET_READY_PRIORITY( uxPriority ) \ { \ if( listCURRENT_LIST_LENGTH( ( pxReadyTasksLists[ ( uxPriority ) ] ) ) ( UBaseType_t ) 0 ) \ { \ portRESET_READY_PRIORITY( ( uxPriority ), ( uxTopReadyPriority ) ); \ } \ }最后验证PASS有些地方不是很理解但是还是要慢慢消化后面会贯通的。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表