实时操作系统Freertos开坑学习笔记:(五):任务调度和时间片调度

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

不多逼逼,直接上内容:
在这里插入图片描述
在这里插入图片描述
这些内容基本上都是讲解源码,比较深,比较枯燥。

一、任务调度

1.开启任务调度器函数

在这里插入图片描述

void vTaskStartScheduler( void )
{
    
    
    BaseType_t xReturn;

    /* Add the idle task at the lowest priority. */
    #if ( configSUPPORT_STATIC_ALLOCATION == 1 )
        {
    
    
            StaticTask_t * pxIdleTaskTCBBuffer = NULL;
            StackType_t * pxIdleTaskStackBuffer = NULL;
            uint32_t ulIdleTaskStackSize;

            /* The Idle task is created using user provided RAM - obtain the
             * address of the RAM then create the idle task. */
            vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &ulIdleTaskStackSize );
            xIdleTaskHandle = xTaskCreateStatic( prvIdleTask,
                                                 configIDLE_TASK_NAME,
                                                 ulIdleTaskStackSize,
                                                 ( void * ) NULL,       /*lint !e961.  The cast is not redundant for all compilers. */
                                                 portPRIVILEGE_BIT,     /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */
                                                 pxIdleTaskStackBuffer,
                                                 pxIdleTaskTCBBuffer ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */

            if( xIdleTaskHandle != NULL )
            {
    
    
                xReturn = pdPASS;
            }
            else
            {
    
    
                xReturn = pdFAIL;
            }
        }
    #else /* if ( configSUPPORT_STATIC_ALLOCATION == 1 ) */
        {
    
    
            /* The Idle task is being created using dynamically allocated RAM. */
            xReturn = xTaskCreate( prvIdleTask,
                                   configIDLE_TASK_NAME,
                                   configMINIMAL_STACK_SIZE,
                                   ( void * ) NULL,
                                   portPRIVILEGE_BIT,  /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */
                                   &xIdleTaskHandle ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */
        }
    #endif /* configSUPPORT_STATIC_ALLOCATION */

    #if ( configUSE_TIMERS == 1 )
        {
    
    
            if( xReturn == pdPASS )
            {
    
    
                xReturn = xTimerCreateTimerTask();
            }
            else
            {
    
    
                mtCOVERAGE_TEST_MARKER();
            }
        }
    #endif /* configUSE_TIMERS */

    if( xReturn == pdPASS )
    {
    
    
        /* freertos_tasks_c_additions_init() should only be called if the user
         * definable macro FREERTOS_TASKS_C_ADDITIONS_INIT() is defined, as that is
         * the only macro called by the function. */
        #ifdef FREERTOS_TASKS_C_ADDITIONS_INIT
            {
    
    
                freertos_tasks_c_additions_init();
            }
        #endif

        /* Interrupts are turned off here, to ensure a tick does not occur
         * before or during the call to xPortStartScheduler().  The stacks of
         * the created tasks contain a status word with interrupts switched on
         * so interrupts will automatically get re-enabled when the first task
         * starts to run. */
        portDISABLE_INTERRUPTS();

        #if ( configUSE_NEWLIB_REENTRANT == 1 )
            {
    
    
                /* Switch Newlib's _impure_ptr variable to point to the _reent
                 * structure specific to the task that will run first.
                 * See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html
                 * for additional information. */
                _impure_ptr = &( pxCurrentTCB->xNewLib_reent );
            }
        #endif /* configUSE_NEWLIB_REENTRANT */

        xNextTaskUnblockTime = portMAX_DELAY;
        xSchedulerRunning = pdTRUE;
        xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT;

        /* If configGENERATE_RUN_TIME_STATS is defined then the following
         * macro must be defined to configure the timer/counter used to generate
         * the run time counter time base.   NOTE:  If configGENERATE_RUN_TIME_STATS
         * is set to 0 and the following line fails to build then ensure you do not
         * have portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() defined in your
         * FreeRTOSConfig.h file. */
        portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();

        traceTASK_SWITCHED_IN();

        /* Setting up the timer tick is hardware specific and thus in the
         * portable interface. */
        if( xPortStartScheduler() != pdFALSE )
        {
    
    
            /* Should not reach here as if the scheduler is running the
             * function will not return. */
        }
        else
        {
    
    
            /* Should only reach here if a task calls xTaskEndScheduler(). */
        }
    }
    else
    {
    
    
        /* This line will only be reached if the kernel could not be started,
         * because there was not enough FreeRTOS heap to create the idle task
         * or the timer task. */
        configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY );
    }

    /* Prevent compiler warnings if INCLUDE_xTaskGetIdleTaskHandle is set to 0,
     * meaning xIdleTaskHandle is not used anywhere else. */
    ( void ) xIdleTaskHandle;

    /* OpenOCD makes use of uxTopUsedPriority for thread debugging. Prevent uxTopUsedPriority
     * from getting optimized out as it is no longer used by the kernel. */
    ( void ) uxTopUsedPriority;
}

比较复杂而且难,我也看不懂。后面再说。
在这里插入图片描述

2.启动第一个任务

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
只能说这部分确实是枯燥难懂,我目前也不想花时间了解,等以后有时间再来深入探究吧。

二、任务切换的原理

在这里插入图片描述
任务切换的本质:就是CPU寄存器的切换。
假设当由任务A切换到任务B时,主要分为两步:
第一步:需暂停任务A的执行,并将此时任务A的寄存器保存到任务堆栈,这个过程叫做保存现场;
第二步:将任务B的各个寄存器值(被存于任务堆栈中)恢复到CPU寄存器中,这个过程叫做恢复现场;对任务A保存现场,对任务B恢复现场,这个整体的过程称之为:上下文切换
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
也很复杂,我也不想去深入了解,哎。就先留个图。

三、时间片调度

1.基本概念

在这里插入图片描述
首先,时间片调度机制是针对同等优先级的任务的。
其次,一个时间片大小:在FreeRTOS中,一个时间片就等于SysTick 中断周期,也就是1ms。
重点是这几句话:
在这里插入图片描述

2.看一个实际例程

在这里插入图片描述
注意,两个任务的优先级要相同。
代码如下(示例):

/* 任务一,实现LED0每500ms翻转一次 */
void task1( void * pvParameters )
{
    
    
    uint32_t task1_num = 0;
    while(1)
    {
    
    
        taskENTER_CRITICAL();               /* 进入临界区 */
        printf("task1运行次数:%d\r\n",++task1_num);
        taskEXIT_CRITICAL();                /* 退出临界区 */
        delay_ms(10);
    }
}

/* 任务二,列表项的插入和删除实验 */
void task2( void * pvParameters )
{
    
    
    uint32_t task2_num = 0;
    while(1)
    {
    
    
        taskENTER_CRITICAL();               /* 进入临界区 */
        printf("task2运行次数:%d\r\n",++task2_num);
        taskEXIT_CRITICAL();                /* 退出临界区 */
        delay_ms(10);
    }
}

①我们设置滴答定时器中断时间为50ms,即一个时间片是50ms,那么任务一有一个delay10ms的延时(这里不能用系统非阻塞延时),算上延时和打印语句,总共可能会执行四次左右,然后一个时间片消耗完,转向任务二也执行4次,二者循环往复

②为什么要加入临界区保护呢?

答:因为 printf(“task1运行次数:%d\r\n”,++task1_num);这个语句的执行也会花一点时间,那么如果不加临界段保护,当每执行到第五次时,可能还没有把文本打印完,就到了50ms的时间片了,导致立刻跳到另一个任务执行,使得数据不完整。

③现象不解释了,就是次数不断增大。

猜你喜欢

转载自blog.csdn.net/qq_53092944/article/details/132591033