系统时钟管理

SYSTICK

处理系统时钟节拍

  • 就是在每次 SysTick 中断发生的时候,将全局变量 xTickCount 的值加 1,也就是将系统时钟节拍计数器的值加 1。

处理阻塞任务列表

  • 就是判断阻塞态任务列表中是否有阻塞任务超时,如果有,就将阻塞时间超时的阻塞态任务移到就绪态任务列表中,准备执行。
  • 同时在系统时钟节拍计数器xTickCount 的加 1 溢出后,将两个阻塞态任务列表调换,这是 FreeRTOS 处理系统时钟节拍计数器溢出的一种机制。

处理时间片调度

  • 就是在每次系统时钟节拍加 1 后,切换到另外一个同等优先级的任务中运行,要注意的是,此函数只是做了需要进行任务切换的标记,在函数退出后,会统一进行任务切换,因此时间片调度导致的任务切换,也可能因为有更高优先级的阻塞任务就绪导致任务切换,而出现任务切换后运行的任务比任务切换前运行任务的优先级高,而非相等优先级。

延时函数

vTaskDelay()

  • 相对延时,从该函数开始延时
  • 延时结束在系统滴答定时器中断中恢复,将任务切换到就绪态

vTaskDelayUntil()

  • 绝对延时,从此任务起始 开始延时,即此任务每次执行n个时间片
  • 函数 xTaskDelayUntil()对任务进行延时的操作,是相对于任务上一次阻塞超时的时间,而不是相对于系统当前的时钟节拍计数器的值
  • 其中 3 是其他任务执行所耗时间
  • 在这里插入图片描述

xTaskAbortDelay()

  • 终止任务延时函数
  • 函数 xTaskAbortDelay()会将阻塞任务从阻塞态任务列表中移除,并将任务添加到就绪态任务列表中。
  • 因为有任务添加到就绪态任务列表中,因此需要的启用抢占式调度的情况下,判断刚添加就绪态任务列表中的任务是否为系统中优先级最高的任务,如果是的话,就需要进行任务切换。
  • 任务被阻塞可能不仅仅因为是被延时,还有可能是在等待某个事件的发生,如果任务是因为等待事件而被阻塞,那么中断阻塞的时候,需要将任务从所在事件列表中移除。
void vTaskDelay( const TickType_t xTicksToDelay )
{
    BaseType_t xAlreadyYielded = pdFALSE;
    /* 只有在延时时间大于 0 的时候,
    * 才需要进行任务阻塞,
    * 否则相当于强制进行任务切换,而不阻塞任务
    */
    if( xTicksToDelay > ( TickType_t ) 0U )
    {
        configASSERT( uxSchedulerSuspended == 0 );
        /* 挂起任务调度器 */
        vTaskSuspendAll();
        {
            /* 用于调试,不用理会 */
            traceTASK_DELAY();
            /* 将任务添加到阻塞态任务列表中 */
            prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE );
        }
        /* 恢复任务调度器运行,
        * 调用此函数会返回是否需要进行任务切换
        */
        xAlreadyYielded = xTaskResumeAll();
    }
    else
    {
    	mtCOVERAGE_TEST_MARKER();
    }
    /* 根据标志进行任务切换 */
    if( xAlreadyYielded == pdFALSE )
    {
    	portYIELD_WITHIN_API();
    }
    else
    {
    	mtCOVERAGE_TEST_MARKER();
    }
}
void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement )
{
	TickType_t xTimeToWake;
	BaseType_t xAlreadyYielded, xShouldDelay = pdFALSE;

		configASSERT( pxPreviousWakeTime );
		configASSERT( ( xTimeIncrement > 0U ) );
		configASSERT( uxSchedulerSuspended == 0 );

		vTaskSuspendAll();
		{
			/* Minor optimisation.  The tick count cannot change in this
			block. */
			const TickType_t xConstTickCount = xTickCount;

            /* 计算任务下一次阻塞超时的时间,
            * 这个阻塞超时时间是相对于上一次阻塞超时的时间的
            */
			xTimeToWake = *pxPreviousWakeTime + xTimeIncrement;
            /* 如果在上一次阻塞超时后,
            * 系统时钟节拍计数器溢出过
            */
			if( xConstTickCount < *pxPreviousWakeTime )
			{
                /* 只有在下一次阻塞超时时间也溢出,
                * 并且下一次阻塞超时时间大于系统时钟节拍计数器的值时,
                * 需要做相应的溢出处理,否则就好像没有溢出
                */		
				if( ( xTimeToWake < *pxPreviousWakeTime ) && ( xTimeToWake > xConstTickCount ) )
				{
                    /* 标记因为溢出,需要做相应的处理 */
					xShouldDelay = pdTRUE;
				}
				else
				{   
					mtCOVERAGE_TEST_MARKER();
				}
			}
			else
			{
				   /* 系统时钟节拍计数器没有溢出,
                    * 但是下一次阻塞超时时间溢出了,
                    * 并且下一次阻塞超时时间大于系统时钟节拍计数器的值时,
                    * 需要做相应的溢出处理
                    */
				if( ( xTimeToWake < *pxPreviousWakeTime ) || ( xTimeToWake > xConstTickCount ) )
				{
                    /* 标记因为溢出,需要做相应的溢出处理 */
					xShouldDelay = pdTRUE;
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}

			/* 更新上一次阻塞超时时间为下一次阻塞超时时间 */
			*pxPreviousWakeTime = xTimeToWake;
            
			/* 根据标记,做相应的溢出处理 */
			if( xShouldDelay != pdFALSE )
			{
				traceTASK_DELAY_UNTIL( xTimeToWake );

				/* 将任务添加到阻塞态任务列表中 */
				prvAddCurrentTaskToDelayedList( xTimeToWake - xConstTickCount, pdFALSE );
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
        /* 恢复任务调度器运行,
        * 调用此函数会返回是否需要进行任务切换
        */
		xAlreadyYielded = xTaskResumeAll();

		/* 根据标志进行任务切换 */
		if( xAlreadyYielded == pdFALSE )
		{
			portYIELD_WITHIN_API();
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
}
Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐