freeRTOS滴答时钟相关源码分析

本文详细分析了FreeRTOS的滴答时钟和任务调度相关源码,包括vPortSetupTimerInterrupt函数的时钟配置、xPortSysTickHandler中断处理函数中的任务调度机制以及xTaskIncrementTick中的时间片管理。作者指出configTICK_RATE_HZ配置的重要性,并解释了如何通过 SysTick 定时器触发任务切换。
摘要由CSDN通过智能技术生成

最近学习白问网韦东山老师在B站开源的freeRTOS课程,网址:韦东山直播公开课:RTOS实战项目之实现多任务系统 第1节:裸机程序框架和缺陷_哔哩哔哩_bilibili和7天物联网训练营【第2期】7天物联网智能家居实战训练营

在学习过程中按照韦老师的方法分析了下freeRTOS源码,如果有不对的地方请指证。
 

freeRTOS滴答时钟相关源码分析,基于cubemx生成的freeRTOS工程

xPortStartScheduler	
	    vPortSetupTimerInterrupt
	
	
	void vPortSetupTimerInterrupt( void )
	{
		
		/* 
			1、操作系统,是针对寄存器操作---效率高
			2、首先赋值装载寄存器值 = (CPU频率/配置周期)-1
				2.1、HAL_RCC_ClockConfig() cpu频率实在硬件启动时,就已经获取了
				2.2、configTICK_RATE_HZ = 1000 是由cubemx配置而得
			3、配置控制寄存器
				3.1、开启时钟源
				3.2、使能中断
				3.3、使能systick
			4、可以参考,M4权威指南  9.5章节----systick定时器
			
			5、configTICK_RATE_HZ
				5.1、建议不要小于1ms
				5.2、建议以整数倍赋值  10ms 100ms  1000ms  便于计算管理

		/* Configure SysTick to interrupt at the requested rate. */
		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 );
	}




xPortSysTickHandler
        xTaskIncrementTick

void xPortSysTickHandler( void )
{
    
	
	/*关闭中断,不让中断打断systick中断服务,就是进入临界段*/
	vPortRaiseBASEPRI();
	{
		     操作系统调度接口
			如果调度器返回true,触发pendSV异常
		if( xTaskIncrementTick() != pdFALSE )
		{
			
			触发pendSV异常
			portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
		}
	}
	清除中断屏蔽,打开中断,执行pendSV异常
	vPortClearBASEPRIFromISR();
}






//SysTick任务调度
BaseType_t xTaskIncrementTick( void )
{
   TCB_t * pxTCB;
   TickType_t xItemValue;
   //返回值,表示是否进行上下文切换
   BaseType_t xSwitchRequired = pdFALSE;

	
	traceTASK_INCREMENT_TICK( xTickCount );
	/*内核调度器是否有挂起,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( ;; )//会一直遍历整个任务延时列表,主要目的是,找到时间片最短的任务,进行切换
			{
			
			    //1、判断任务延时列表中,是否为空,也就是说,有没有任务在等待调度
				if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )
				{
					//如果没有任务等待,把时间片赋值为最大值,不再调度
					xNextTaskUnblockTime = portMAX_DELAY; 
					break;
				}
				else
				{
					
					/* 
						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

		#if ( configUSE_TICK_HOOK == 1 )
		{
			/* Guard against the tick hook being called when the pended tick
			count is being unwound (when the scheduler is being unlocked). */
			if( uxPendedTicks == ( UBaseType_t ) 0U )
			{
				vApplicationTickHook();
			}
			else
			{
				mtCOVERAGE_TEST_MARKER();
			}
		}
		#endif /* configUSE_TICK_HOOK */
	}
	//内核调度器挂起了
	else
	{
		++uxPendedTicks;//挂起的tick+1

		/* 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;
}


 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值