????????之前的代码里为了实现LED灯闪烁效果,需要先将LED点亮然后延时一段时间后将其熄灭。在这段延时等待期间MCU一直在做无意义的等待计数,为了提高MCU机时的有效利用率,可以让MCU在延时的这段时间内执行其他任务。任务自身发起延时后放弃MCU使用权导致自身的运行发生阻塞,这种延时就叫做阻塞式延时。
????????RTOS一个基于优先级的抢占式内核,为了使高优先级的任务不至于一直独占MCU的使用权,需要其在合适的时机让出MCU的使用权,给其他任务使用MCU的机会。阻塞式延时RTOS提供的让高优先级任务主动放弃MCU使用权的方法之一。
? ? ? ? 在RTOS中会出现在某个时间内所有的任务都处于非就绪态的情况,这会导致系统处于无任务可运行的状况,为了防止这种状况的出现,RTOS需要提供一个最低优先级的任务,在没有其他任务可执行的时候去执行这个任务,这个最低优先级的任务就是空闲任务。
? ? ?一、空闲函数设计? ?
????????新增一个最低优先级的空闲任务,并配置其相关的任务栈和任务控制块。在进行RTOS初始化的时候,创建这个任务。
List taskDelayList;
#define TASK_IDLE_STACK_SIZE 128
UINT32 taskIdleStack[TASK_IDLE_STACK_SIZE];
TaskTcb taskIdleTcb;
/**********************************************************************************************
* @brief
*
* @param None
* @retval None
**********************************************************************************************/
void TaskIdle(void)
{
while(1)
{
LED_SetRed();
}
}
/**********************************************************************************************
* @brief
*
* @param None
* @retval None
**********************************************************************************************/
void TaskScheduleInit(void)
{
UINT16 i = 0;
for(i = 0; i <= OS_TASK_MAX_PRIORITIES; i++)
{
ListInit(&taskTcbPtrPrioTbl[i]);
}
ListInit(&taskDelayList);
BitMapInit(&taskPrioBitMap);
TaskCreate(&taskIdleTcb, TaskIdle,taskIdleStack, TASK_IDLE_STACK_SIZE, OS_TASK_MAX_PRIORITIES, 0);
}
二、延时函数设计
????????为了方便增加和删除延时任务,增加一个延时任务的链表taskDelayList,将需要延时任务都挂载在这个链表下面,在系统Tick时遍历这个链表进行延时逻辑处理。在任务控制块里增加延时节点taskDelayNode和延时计数变量delayTicks。
typedef struct STRUCT_TASK_TCB
{
UINT32 *stackPtr; /*< Points to the location of the last item placed on the tasks stack. THIS MUST BE THE FIRST MEMBER OF THE TCB STRUCT. */
Node taskTcbNode;
Node taskDelayNode;
UINT32 delayTicks;/* Nbr ticks to delay task or, timeout waiting for event */
UINT8 priority; /*< The priority of the task. 0 is the highest priority. */
UINT8 timeSliceSet;
UINT8 timeSlice;
}TaskTcb,*TaskTcbPtr;
????????在任务创建函数里对延时相关变量进行初始化,延时任务链表taskDelayList已在TaskScheduleInit函数里初始化。
void TaskCreate(TaskTcb *pTcb, FunPtr pTaskFun, UINT32* pTaskStack, UINT32 stackSize, UINT8 taskPrio, UINT8 timeSlice)
{
UINT32 *pStack;
UINT32 cnt;
for(cnt = 0; cnt < stackSize*sizeof(UINT32);cnt++)/* Fill the stack with a known value to assist debugging. */
{
*((UINT8*)pTaskStack + cnt) = OS_STACK_FILL_BYTE;
}
pStack = &pTaskStack[stackSize];/* Get top address of stack, because grows from high memory to low*/
pStack = (UINT32 *)((UINT32)(pStack) & 0xFFFFFFF8u);/* AAPCS(ARM application procedure all standard) Align the stack to 8bytes */
/*Registers stacked as if auto-saved on exception*/
*(--pStack) = (UINT32)0x01000000uL; /*xPSR bit24 thumb state bit*/
*(--pStack) = (UINT32)pTaskFun; /*Entry Point(PC)*/
*(--pStack) = (UINT32)0x12121212uL; /*R14 (LR)*/
*(--pStack) = (UINT32)0x12121212uL; /* R12*/
*(--pStack) = (UINT32)0x03030303uL; /* R3*/
*(--pStack) = (UINT32)0x02020202uL; /* R2*/
*(--pStack) = (UINT32)0x01010101uL; /* R1*/
*(--pStack) = (UINT32)0x00000000u; /* R0*/
/*Remaining registers saved on process stack*/
*(--pStack) = (UINT32)0x11111111uL; /* R11*/
*(--pStack) = (UINT32)0x10101010uL; /* R10*/
*(--pStack) = (UINT32)0x09090909uL; /* R9*/
*(--pStack) = (UINT32)0x08080808uL; /* R8*/
*(--pStack) = (UINT32)0x07070707uL; /* R7*/
*(--pStack) = (UINT32)0x06060606uL; /* R6*/
*(--pStack) = (UINT32)0x05050505uL; /* R5*/
*(--pStack) = (UINT32)0x04040404uL; /* R4*/
pTcb->stackPtr = pStack;
pTcb->priority = taskPrio;
pTcb->delayTicks = 0;
if(timeSlice != 0)
pTcb->timeSliceSet = timeSlice;
else
pTcb->timeSliceSet = OS_TASK_DEFAULT_TIME_SLICE;
pTcb->timeSlice = pTcb->timeSliceSet;
NodeInit(&pTcb->taskDelayNode, pTcb);
NodeInit(&pTcb->taskTcbNode, pTcb);
ListInsertAtTail(&taskTcbPtrPrioTbl[taskPrio], &pTcb->taskTcbNode);
BitMapSetBit(&taskPrioBitMap, taskPrio);
}
????????在延时函数里设置需要延时的Tick数量,将需要延时的任务加入链表taskDelayList,并将当前任务从任务优先级调度链表里删除,随后进行任务调度将当前任务阻塞,去运行其他就绪的最高优先级任务。
/**********************************************************************************************
* @brief
*
* @param None
* @retval None
**********************************************************************************************/
void TaskDelay(UINT32 ticks)
{
pOsCurTcb->delayTicks = ticks;
ListInsertAtTail(&taskDelayList, &(pOsCurTcb->taskDelayNode));
ListRemoveNode(&taskTcbPtrPrioTbl[pOsCurTcb->priority], &(pOsCurTcb->taskTcbNode));
if(taskTcbPtrPrioTbl[pOsCurTcb->priority].nodeCount == 0)
{
BitMapDelBit(&taskPrioBitMap, pOsCurTcb->priority);
}
TaskSchedule();
}
????????在SysTick中断里对延时任务链表循环处理,若有任务延时结束则将其唤醒并加入任务优先级调度链表。
/**********************************************************************************************
* @brief
*
* @param None
* @retval None
**********************************************************************************************/
void SysTick_Handler(void)
{
NodePtr pHeadNode;
TaskTcbPtr pTaskTcb;
pHeadNode = taskDelayList.pHeadNode;
for(UINT16 i = 0; i < taskDelayList.nodeCount; i++)
{
pTaskTcb = (TaskTcbPtr)(pHeadNode->pOwner);
if(pTaskTcb->delayTicks)
{
pTaskTcb->delayTicks--;
if(pTaskTcb->delayTicks == 0)
{
ListRemoveNode(&taskDelayList, pHeadNode);
ListInsertAtTail(&taskTcbPtrPrioTbl[pTaskTcb->priority], &(pTaskTcb->taskTcbNode));
BitMapSetBit(&taskPrioBitMap, pTaskTcb->priority);
}
}
pHeadNode = pHeadNode->next;
}
if(pOsCurTcb->timeSlice)
pOsCurTcb->timeSlice--;
if(taskTcbPtrPrioTbl[pOsCurTcb->priority].nodeCount > 1)
{
if(pOsCurTcb->timeSlice == 0)
{
pHeadNode = ListPopHead(&taskTcbPtrPrioTbl[pOsCurTcb->priority]);
ListInsertAtTail(&taskTcbPtrPrioTbl[pOsCurTcb->priority],pHeadNode);
pTaskTcb = (TaskTcbPtr)(taskTcbPtrPrioTbl[pOsCurTcb->priority].pHeadNode->pOwner);
pTaskTcb->timeSlice = pTaskTcb->timeSliceSet;
}
}
TaskSchedule();
}
????????之前的链表节点删除函数ListRemoveNode逻辑上未考虑删除头节点的情况,这里完善一下
/**********************************************************************************************
* @brief
*
* @param None
* @retval None
**********************************************************************************************/
void ListRemoveNode(ListPtr pList, NodePtr pNode)
{
if(pList->pHeadNode == pNode)
{
pList->pHeadNode = pNode->next;
}
pNode->prev->next = pNode->next;
pNode->next->prev = pNode->prev;
//NodeInit(pNode,pNode->pOwner);
pList->nodeCount--;
if(pList->nodeCount == 0)
pList->pHeadNode = NULL;
}
三、仿真验证
? ? ? ? 创建两个优先级不同的任务,若高优先级的任务未进入延时阻塞状态则低优先级的任务就不会运行,同时当所有的任务都进入阻塞状态后,系统就会运行空闲任务。
/**********************************************************************************************
* @brief StartTask
*
* @param None
* @retval None
**********************************************************************************************/
void StartTask(void)
{
while(1)
{
LED_SetGreen();
TaskDelay(1500);
LED_ResetGreen();
TaskDelay(1500);
}
}
/**********************************************************************************************
* @brief SecondTask
*
* @param None
* @retval None
**********************************************************************************************/
void SecondTask(void)
{
static UINT16 ticks;
while(1)
{
LED_SetBlue();
TaskDelay(2500);
LED_ResetBlue();
TaskDelay(2500);
}
}
????????在main函数里创建StartTask任务优先级设置为0,创建SecondTask任务优先级设置为1。
int main()
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
LED_GPIO_Config();
SysTickRateInit();
TaskScheduleInit();
TaskCreate(&StartTaskTcb, StartTask, StartTaskStack, START_TASK_SIZE,0,2);
TaskCreate(&SecondTaskTcb, SecondTask, SecondTaskStack, SECOND_TASK_SIZE,1,3);
OsTaskStart();
return 0;
}
????????StartTask任务调用TaskDelay函数后进入阻塞状态,系统进行任务调度后会运行SecondTask任务。
????????StartTask阻塞后系统运行Second任务,注意此次调度是由TaskDelay函数引起的,并未进过SysTick进行调度,所以StartTask此时delayTicks仍是1500
? ? ? ? 当两个任务都进入阻塞状态后系统运行空闲任务。
?????????在SysTick中断里对延时任务进行处理
????????StartTask延时结束继续运行?
?
|