一、FreeRTOS时间片调度概述
FreeRTOS支持多个任务同时拥有一个优先级,这些任务的调度就可以使用时间片来进行调度。在FreeRTOS中允许一个任务允许一个时间片(一个时钟节拍的长度)后让出CPU的使用权,让拥有同优先级的下个任务运行。下图展示了运行在同一优先级的执行时间图。其中的task1、task2、task3是同一优先级N就绪的任务。
- 1、任务3正在运行。
- 2、这时一个时钟节拍中断(滴答定时器中断)发生,任务3的时间片用完,但是任务3还没有执行完。
- 3、FreeRTOS将任务切换到任务1,任务1是优先级N下的下一个就绪任务。
- 4、任务1连续运行至时间片用完。
- 5、任务3再次获取到CPU使用权,接着运行。
- 6、任务3运行完成,调用任务切换函数portYIELD()强行进行任务切换放弃剩余的时间片,从而使优先级N下的下一个就绪的任务运行。
- 7、FreeRTOS切换到任务1。
- 8、任务1执行完其时间片。
二、开启时间片调度
要使用时间片调度的话宏configUSE_PREEMPTION和宏configUSE_TIME_SLICING必须为1。时间片的长度由宏configTICK_RATE_HZ来确定,一个时间片的长度就是滴答定时器的
中断周期,比如configTICK_RATE_HZ为1000,那么一个时间片的长度就是1ms。时间片调度发生在滴答定时器的中断服务函数中,前面讲解滴答定时器中断服务函数的时候说了在中断服务函数SysTick_Handler()中会调用FreeRTOS的API函数xPortSysTickHandler(),而函数xPortSysTickHandler()会引发任务调度,但是这个任务调度是有条件的,函数xPortSysTickHandler()如下:
void xPortSysTickHandler( void )
{
vPortRaiseBASEPRI();
{
/* Increment the RTOS tick. */
if( xTaskIncrementTick() != pdFALSE )
{
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
}
}
vPortClearBASEPRIFromISR();
}
上述代码中只有函数xTaskIncrementTick()的返回值不为pdFALSE的时候就会进行任务调度!其中xTaskIncrementTick()函数代码如下:
BaseType_t xTaskIncrementTick( void )
{
TCB_t * pxTCB;
TickType_t xItemValue;
BaseType_t xSwitchRequired = pdFALSE;
traceTASK_INCREMENT_TICK( xTickCount );
if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
{
#if ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) (1)
{
if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ pxCurrentTCB->uxPriority ] ) ) > ( UBaseType_t ) 1 ) (2)
{
xSwitchRequired = pdTRUE; (3)
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) */
#if ( configUSE_TICK_HOOK == 1 )
{
if( uxPendedTicks == ( UBaseType_t ) 0U )
{
vApplicationTickHook();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_TICK_HOOK */
}
else
{
++uxPendedTicks;
/* The tick hook gets called at regular intervals, even if the
scheduler is locked. */
#if ( configUSE_TICK_HOOK == 1 )
{
vApplicationTickHook();
}
#endif
}
#if ( configUSE_PREEMPTION == 1 )
{
if( xYieldPending != pdFALSE )
{
xSwitchRequired = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_PREEMPTION */
return xSwitchRequired;
}
- (1)、当宏configUSE_PREEMPTION和宏configUSE_PREEMPTION都为1的时候下面的代码才会编译。所以要想使用时间片调度的话这这两个宏都必须为1,缺一不可!
- (2)、判断当前任务所对应的优先级下是否还有其他的任务。
- (3)、如果当前任务所对应的任务优先级下还有其他的任务那么就返回pdTRUE。从上面的代码可以看出,如果当前任务所对应的优先级下有其他的任务存在,那么函数xTaskIncrementTick0就会返回pdTURE,由于函数返回值为pdTURE,因此函数xPortSysTickHandler()就会进行一次任务切换。
三、实验程序展示
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
delay_init(168); //初始化延时函数
uart_init(115200); //初始化串口
LED_Init(); //初始化LED端口
LCD_Init();
POINT_COLOR = RED;
LCD_ShowString(30,10,200,16,16,"LIU YI NIU BI");
LCD_ShowString(30,40,200,16,16,"2021/10/18");
//创建开始任务
xTaskCreate((TaskFunction_t )start_task, //任务函数
(const char* )"start_task", //任务名称
(uint16_t )START_STK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )START_TASK_PRIO, //任务优先级
(TaskHandle_t* )&StartTask_Handler); //任务句柄
vTaskStartScheduler(); //开启任务调度
}
//开始任务任务函数
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建task1任务
xTaskCreate((TaskFunction_t )task1_task,
(const char* )"task1_task",
(uint16_t )TASK1_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK1_TASK_PRIO,
(TaskHandle_t* )&TASK1Task_Handler);
//创建LED1任务
xTaskCreate((TaskFunction_t )task2_task,
(const char* )"task2_task",
(uint16_t )TASK2_STK_SIZE,
(void* )NULL,
(UBaseType_t )TASK2_TASK_PRIO,
(TaskHandle_t* )&TASK2Task_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
//TASK1任务函数
void task1_task(void *pvParameters){
u8 task1_num=0;
while(1){
task1_num++;
LED0=!LED0;
taskENTER_CRITICAL();//进入临界区
printf("任务1已经执行:%d次\r\n",task1_num);
taskEXIT_CRITICAL();//退出临界区
// delay_xms(10);
}
}
//TASK2任务函数
void task2_task(void *pvParameters){
u8 task2_num=0;
while(1){
task2_num++;
LED1=!LED1;
taskENTER_CRITICAL();//进入临界区
printf("任务2已经执行:%d次\r\n",task2_num);
taskEXIT_CRITICAL();//退出临界区
// delay_xms(10);
}
}
通过串口调试助手可以看到打印的信息,就知道执行的过程。
不管是task1_task还是task2_task都是连续执行4、5次,和前面程序设计的一样,说明在一个时间片内一直在运行一个任务,当时间片用完后就切换到下一个任务运行。