FreeRTOS的xPortSysTickHandler函数(笔记自用)

xPortSysTickHandler函数在FreeRTOS中在给系统提供系统节拍的系统时钟或是定时器中断中调用的。他更新系统节拍数,检查任务阻塞,任务切换等。

必要知识(粗略)

代码解析必须粗略了解的一些关于FreeRTOS的知识。

就绪列表

由configMAX_PRIORITIES决定大小。

PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ];

任务加入就绪列表时按照各自优先级存入到数组对应的列表中,这样切换任务时从高优先级的列表开始找,这样就可以确保高优先级任务优先。

阻塞列表

PRIVILEGED_DATA static List_t xDelayedTaskList1;                        /*< Delayed tasks. */

PRIVILEGED_DATA static List_t xDelayedTaskList2;                        /*< Delayed tasks (two lists are used - one for delays that have overflowed the current tick count. */

PRIVILEGED_DATA static List_t * volatile pxDelayedTaskList;             /*< Points to the delayed task list currently being used. */

PRIVILEGED_DATA static List_t * volatile pxOverflowDelayedTaskList;     /*< Points to the delayed task list currently being used to hold tasks that have overflowed the current tick count. */

阻塞列表有两个xDelayedTaskList1,xDelayedTaskList2。在创建第一个任务时会初始化就绪,阻塞等等的列表,在这个过程中pxDelayedTaskList指向xDelayedTaskList1,pxOverflowDelayedTaskList指向pxOverflowDelayedTaskList。阻塞列表是一个循环列表。

我们知道FreeRTOS系统在每个系统时钟中断(提供的系统时钟中断)时会更新节拍数,然后检查阻塞任务的阻塞时间是否到达,事件等等时间是否到达。但是这系统中任务阻塞时间一般是延时时间+调用阻塞函数时的系统节拍。系统节拍在启动后一直在累加,以STM32为例系统节拍是32位存储当他溢出后怎么保证阻塞系统的正常运行?FreeRTOS使用两个阻塞列表,pxDelayedTaskList指向的阻塞列表存放未溢出的阻塞任务,pxOverflowDelayedTaskList指向的阻塞列表存放溢出的阻塞任务。在任务调用会导致阻塞的函数时,系统计算阻塞时间(延时时间+调用阻塞函数时的系统节拍)这个数小于调用阻塞函数时的系统节拍时,将任务放入pxOverflowDelayedTaskList指向的阻塞列表,否则放入pxDelayedTaskList。然后在xPortSysTickHandler函数中系统节拍更新时,也会判断系统节拍是否溢出,如果溢出了,就像pxDelayedTaskList和pxOverflowDelayedTaskList的指向互换,然后开始查找是否有阻塞时间到达的任务。

代码解析

void xPortSysTickHandler( void )
{
    uint32_t ulPreviousMask;

	ulPreviousMask = portSET_INTERRUPT_MASK_FROM_ISR();//这是在终端中屏蔽终端的宏。
	{
		/* Increment the RTOS tick. */
		if( xTaskIncrementTick() != pdFALSE )//xTaskIncrementTick()非常重要
		{
			/* Pend a context switch. */
			*(portNVIC_INT_CTRL) = portNVIC_PENDSVSET;//除非Pend中断,切换任务
		}
	}
	portCLEAR_INTERRUPT_MASK_FROM_ISR( ulPreviousMask );
}

/* 更新系统节拍数 */
BaseType_t xTaskIncrementTick( void )
{
    TCB_t * pxTCB;
    TickType_t xItemValue;
    BaseType_t xSwitchRequired = pdFALSE;
    /*判断调度器是否挂起*/
    if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )//没有挂起
	{
        /*
        更新系统节拍数,使用xConstTickCount变量存储因为后续要判断节拍数是否溢出,
        直接使用xTickCount全局变量不好(这个变量如果在其他地方被更改,导致后续判断错误,这样无                
        法确保所有任务的准确运行)
        */
        const TickType_t xConstTickCount = xTickCount + 1;
        xTickCount = xConstTickCount;

        if( xConstTickCount == ( TickType_t ) 0U )//判断是否溢出
		{
			taskSWITCH_DELAYED_LISTS();
		}

        /* 有任务要被唤醒了,xNextTaskUnblockTime记录下一个任务要被唤醒的节拍数  */
        if( xConstTickCount >= xNextTaskUnblockTime )
        {
            for( ;; )
            {
                if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )//阻塞列表为空
                {
                    xNextTaskUnblockTime = portMAX_DELAY;//下一个任务的阻塞时间设置为最大值
                    break;//跳出
                }
                else //不为空
                {
                    //获取阻塞列表头的项的任务控制块
                    pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );
					xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) );
                    
                    /* 找到了阻塞列表中阻塞时间还没到的任务 */
                    if( xConstTickCount < xItemValue )
					{
						/* 更新下一个解除阻塞任务的节拍数 */
						xNextTaskUnblockTime = xItemValue;
						break;    //跳出
					}
                    
                    //阻塞列表移除阻塞时间到了的任务
                    ( void ) uxListRemove( &( pxTCB->xStateListItem ) );
                    
                    //如果该任务还在事件阻塞列表中,也从事件中删除
                    if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
					{
						( void ) uxListRemove( &( pxTCB->xEventListItem ) );
					}
                    //将任务加入到就绪列表中
                    prvAddTaskToReadyList( pxTCB );

                    #if (  configUSE_PREEMPTION == 1 )
					{
						/* 如果是抢占式调度 */
						if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
						{
							xSwitchRequired = pdTRUE;
                          /*
                            函数返回true,由上面        
                            xPortSysTickHandler可知会触发pend中断
                          */
						}
					}
                    #endif
                }
            }
        }
        
        /*时间片调度,如果configUSE_PREEMPTION等于0那么认为不会自动切换任务*/
        #if ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) )
		{
			if( 
            listCURRENT_LIST_LENGTH(&(pxReadyTasksLists[ pxCurrentTCB->uxPriority ]))                 
            > ( UBaseType_t ) 1 
              )
			{
				xSwitchRequired = pdTRUE;
			}
		}
		#endif
    }
    else    //调度器挂起
	{
		++uxPendedTicks;//记录调度器挂起后,系统时钟中断的次数。
	}

    #if ( configUSE_PREEMPTION == 1 )
	{
		//如果xYieldPending 标记为true代表应该执行一次任务切换
        if( xYieldPending != pdFALSE )
		{
			xSwitchRequired = pdTRUE;
		}
	}
	#endif
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值