前言
在上一篇博客笔者介绍了操作系统中的调度算法,调度算法的本质就是选择下一个任务。
如果读者认真看了上一篇文章的内容,那么接下来讲解的Sparrow的算法应该是非常清晰易懂的。
在实时操作系统Sparrow RTOS中,我们引入了优先级的概念,就是为了使任务的运行更加具有实时性,优先级由我们手动进行调整,往往我们希望优先级高的任务优先执行,那么,该怎么做呢?
调度算法的设计
我们使用ReadyBitTable这个uint32_T类型的变量来标识就绪的任务,只要任务是就绪态的,我们就执行 ReadyBitTable |= (1 << uxPriority)操作,这样ReadyBitTable 中为1的位就表示有就绪的任务。
那么如何得到对应的优先级数字呢?这里我们可以计算从高位到低位,离最近的1有多少个0,然后用31减去这个数目,就可以得到优先级的数字了。
在arm架构中,恰好有这么一条汇编指令clz,它可以计算从高位到低位,离最近的1之间的0的数目,正好符合我们的需求。
举例如下:
就绪表 | 值 | clz的结果 | 最大优先级 |
---|---|---|---|
ReadyBitTable | 00000000000000000000000000000100 | 29 | 2 |
ReadyBitTable | 00000000000000000000000100000011 | 23 | 8 |
不知道读者发现没有,其实这种方法跟上一节中所讲的RTThread的算法思想是一样的,只是我们使用clz指令代替了查表法。在FreeRTOS和RT-Thread等RTOS中,其实都有使用clz指令的方法,它们跟Sparrow的算法都大差不差。
修改程序
既然我们已经知道了思路,那么让我们开始写代码:
uint32_t ReadyBitTable = 0;//添加到合适的地方即可__attribute__( ( always_inline ) ) static inline uint8_t FindHighestPriority( void )
{uint8_t TopZeroNumber;uint8_t temp;__asm volatile("clz %0, %2\n""mov %1, #31\n""sub %0, %1, %0\n":"=r" (TopZeroNumber),"=r"(temp):"r" (ReadyBitTable));return TopZeroNumber;
}void vTaskSwitchContext( void )
{pxCurrentTCB = TcbTaskTable[ FindHighestPriority()];
}
修改xTaskCreat函数,添加一行代码:
void xTaskCreate( TaskFunction_t pxTaskCode,const uint16_t usStackDepth,void * const pvParameters,//You can use it for debugginguint32_t uxPriority,TaskHandle_t * const self )
{uint32_t *topStack =NULL;TCB_t *NewTcb = (TCB_t *)heap_malloc(sizeof(TCB_t *));*self = ( TCB_t *) NewTcb;TcbTaskTable[uxPriority] = NewTcb;//NewTcb->uxPriority = uxPriority;NewTcb->pxStack = ( uint32_t *) heap_malloc( ( ( ( size_t ) usStackDepth ) * sizeof( uint32_t * ) ) );topStack = NewTcb->pxStack + (usStackDepth - (uint32_t)1) ;topStack = ( uint32_t *) (((uint32_t)topStack) & (~((uint32_t) aligment_byte)));NewTcb->pxTopOfStack = pxPortInitialiseStack(topStack,pxTaskCode,pvParameters,self);pxCurrentTCB = NewTcb;ReadyBitTable |= (1 << uxPriority); //添加这一行
}
修改空闲任务,任务内容为进入低功耗模式:
void EnterSleepMode(void)
{SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;__WFI();
}//Task handle can be hide, but in order to debug, it must be created manually by the user
TaskHandle_t leisureTcb = NULL;void leisureTask( )
{//leisureTask content can be manually modified as neededwhile (1) {EnterSleepMode();}
}
实验
验证思路
设置一个任务的优先级为最大,观察这个任务是不是一直在执行。
程序
修改任务:
//Task Area!The user must create task handle manually because of debugging and specification
TaskHandle_t tcbTask1 = NULL;
TaskHandle_t tcbTask2 = NULL;void led_bright( )
{while (1) {}
}void led_extinguish( )
{while (1) {HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);HAL_Delay(500);HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);HAL_Delay(500);switchTask();}
}
当然,请确保led_extinguish优先级足够大:
void APP( )
{xTaskCreate( led_bright,128,NULL,1,&tcbTask1);xTaskCreate( led_extinguish,128,NULL,8,&tcbTask2);
}
实验现象:
stm32f103c8t6上的led灯一直在闪烁,说明任务led_extinguish一直在执行。
总结
介绍了Sparrow的调度算法,然后编写程序实现了优先级抢占算法的设计,最后修改原先的工程对调度算法进行验证。
本次实验的文件夹的地址:skaiui2/SKRTOS_sparrow at experiment
有需要的读者可以自取。