FreeRTOS源码分析-4 SysTick系统时钟详解

目录

1 SysTick初始化

2 SysTick中断服务函数

3 SysTick任务调度


1 SysTick初始化

初始化流程

  • 配置SysTick装载值
  • 使能SysTick时钟源 使能SysTick中断 使能SysTick

其中装载值1ms、10ms、100ms都可以,但是不要小于1ms

//main.c //--->
    osKernelStart(); //--->
        xPortStartScheduler(); //--->
            vPortSetupTimerInterrupt(); //--->

系统节拍初始化源码

在vPortSetupTimerInterrupt中

void vPortSetupTimerInterrupt( void )
{

	/*
        1、操作系统,时针对寄存器操作---效率搞
        2、首先赋值装载寄存器值 = (CPU频率/配置的周期)-1
            2.1 SystemClock_Config()
                       HAL_RCC_ClockConfig() CPU频率在硬件启动时就已经获取了
            2.2 configTICK_RATE_HZ = 1000 由cubemx配置而得
        3、配置控制寄存器
            3.1、开启时钟源
            3.2、使能中断
            3.3、使能systick
        4、可以参考M4权威指南 9.5章节---systick定时器
    */
	portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;
	portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT );
}

FreeRTOS源码分析-4 SysTick系统时钟详解_第1张图片

2 SysTick中断服务函数

中断服务函数流程:

  • 关闭中断
  • Tick值增加SysTick任务调度
  • 启动PendSV
  • 开启中断

系统节拍中断服务函数

void xPortSysTickHandler( void )
{
	/* 1、配置中断屏蔽寄存器
       2、不让中断打断,SysTick中断服务
       3、其实就是进入临界段
     */
	vPortRaiseBASEPRI();
	{
		/* 
            操作系统调度接口
            如果调度器返回true,触发pendSV异常
         */
		if( xTaskIncrementTick() != pdFALSE )
		{
			/* A context switch is required.  Context switching is performed in
			the PendSV interrupt.  Pend the PendSV interrupt. */
			portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
		}
	}
    // 清楚可屏蔽中断,就是打开全部中断---》PendSV就执行
	vPortClearBASEPRIFromISR();
}

问:如何与操作系统内核联系起来?

3 SysTick任务调度

SysTick调度流程

  • 系统节拍数加1,判断是否溢出溢出更新任务锁定时间
  • 判断是否有任务需要解除阻塞,获取延时列表第一个任务控制块(时间排序),获取状态列表值判断时间是否到达,未到达退出
  • 任务阻塞事件到达,从延时列表中删除,从事件列表中删除,添加到就绪列表
  • 如果使用抢占内核,判断任务优先级是否大于当前任务,开启任务调度
  • 如果使用时间片调度,判断当前优先级下是否还有其他任务,开启任务调度器
//SysTick任务调度
BaseType_t xTaskIncrementTick( void )
{
    TCB_t * pxTCB;
    TickType_t xItemValue;
    // 返回值,表示是否进行上下文切换
    BaseType_t xSwitchRequired = pdFALSE;

	/* uxSchedulerSuspended  表示内核是否挂起,pdFalse 内核没有挂起*/
	if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
	{
		/* tick计数增加1 */
		const TickType_t xConstTickCount = xTickCount + 1;

		xTickCount = xConstTickCount;
        /* 判断tick是否溢出 */
		if( xConstTickCount == ( TickType_t ) 0U )
		{
            //如果溢出,更新延时列表
			taskSWITCH_DELAYED_LISTS();
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}

		/* 
            1、如果当前节拍大于时间片的锁定时间
            2、说明有任务需要进行调度了,时间片用完了
        */
		if( xConstTickCount >= xNextTaskUnblockTime )
		{
			for( ;; )//会一直遍历整个任务延时列表,主要目的是,找到时间片最短的任务,进行调度
			{
                //判断任务延时列表中,是否为空,也就是说,有没有任务在等待调度
				if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )
				{
					//如果没有任务等待,把时间片赋值为最大值,不再调度
					xNextTaskUnblockTime = portMAX_DELAY; 
					break;
				} //延时列表不为空
				else
				{
					/* 
                        1、从任务延时列表中,获取第一个任务控制块
                            1.1 延时列表,插入永远是把时间片最短的任务,放在第一个
                        2、获取任务控制块的延时时间
                     */
					pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );
					xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) );

                    //再次判断,这个任务时间片是否到达
					if( xConstTickCount < xItemValue )
					{
						/* 没有到达,把此任务的时间片更新为当前系统的时间片 */
						xNextTaskUnblockTime = xItemValue;
                        //直接退出,不用调度
						break;
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}

					/* 把任务从延时列表中移除 */
					( void ) uxListRemove( &( pxTCB->xStateListItem ) );

					/* 把任务从事件列表中移除 */
					if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
					{
						( void ) uxListRemove( &( pxTCB->xEventListItem ) );
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}

					/* 把任务添加到就绪列表中 */
					prvAddTaskToReadyList( pxTCB );

					/* 抢占式处理 */
					#if (  configUSE_PREEMPTION == 1 )
					{
						/* 
                            1、判断优先级是否大于当前优先级
                                1.1、大于则进行调度
                         */
						if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
						{
							xSwitchRequired = pdTRUE;
						}
						else
						{
							mtCOVERAGE_TEST_MARKER();
						}
					}
					#endif /* configUSE_PREEMPTION */
				}
			}
		}

		/* 时间片处理机制 */
		#if ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) )
		{
            /*1、获取就绪列表的长度
                1.1就绪列表指的是,当前任务优先级的列表
                1.2如果其他任务在就绪列表中,就凯斯调度
			if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ pxCurrentTCB->uxPriority ] ) ) > ( UBaseType_t ) 1 )
			{
				xSwitchRequired = pdTRUE;
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		#endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) */

	}
	else  //内核调度器挂起了
	{
        //挂起的tick+1
		++uxPendedTicks;

	}

    //如果是抢占模式,要开启调度器
	#if ( configUSE_PREEMPTION == 1 )
	{
		if( xYieldPending != pdFALSE )
		{
			xSwitchRequired = pdTRUE;
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	#endif /* configUSE_PREEMPTION */
    //返回调度器状态
	return xSwitchRequired;
}

你可能感兴趣的:(FreeRTOS源码分析,单片机,嵌入式硬件)