FreeRTOS中Systick的问题

FreeRTOS中Systick的问题_第1张图片

在Cortex-M内核中,系统节拍由Systick时钟提供,当配置好系统滴答时钟后,每次时钟中断就会触发中断处理数xPortSysTickHandler()。

void xPortSysTickHandler( void )
{
    /* The SysTick runs at the lowest interrupt priority, so when this interrupt
     * executes all interrupts must be unmasked.  There is therefore no need to
     * save and then restore the interrupt mask value as its value is already
     * known - therefore the slightly faster vPortRaiseBASEPRI() function is used
     * in place of portSET_INTERRUPT_MASK_FROM_ISR(). */
    vPortRaiseBASEPRI();//屏蔽归属FreeRTOS的中断优先级
    {
        /* Increment the RTOS tick. */
        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();
}

这部分主要是依靠 xTaskIncrementTick(),来判断任务切换是否在此次系统时钟中断时被需要。如果是,则PendSV标记置位,等待触发PendSV中断。来看看 xTaskIncrementTick()。

BaseType_t xTaskIncrementTick( void )
{
    TCB_t * pxTCB;
    TickType_t xItemValue;
    BaseType_t xSwitchRequired = pdFALSE;




    /* Called by the portable layer each time a tick interrupt occurs.
     * Increments the tick then checks to see if the new tick value will cause any
     * tasks to be unblocked. */
    traceTASK_INCREMENT_TICK( xTickCount );




    if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) //调度是否被挂起,默认为否
    {
        /* Minor optimisation.  The tick count cannot change in this
         * block. */
        const TickType_t xConstTickCount = xTickCount + ( TickType_t ) 1;




        /* Increment the RTOS tick, switching the delayed and overflowed
         * delayed lists if it wraps to 0. */
        xTickCount = xConstTickCount;




        if( xConstTickCount == ( TickType_t ) 0U ) /*lint !e774 'if' does not always evaluate to false as it is looking for an overflow. 如果xConstTickCount为0,说明溢出了*/
        {
            taskSWITCH_DELAYED_LISTS();/*切换延迟列表*/
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }




        /* See if this tick has made a timeout expire.  Tasks are stored in
         * the  queue in the order of their wake time - meaning once one task
         * has been found whose block time has not expired there is no need to
         * look any further down the list. */
        if( xConstTickCount >= xNextTaskUnblockTime )
        {
            for( ; ; )
            {
                if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )
                {
                    /* The delayed list is empty.  Set xNextTaskUnblockTime
                     * to the maximum possible value so it is extremely
                     * unlikely that the
                     * if( xTickCount >= xNextTaskUnblockTime ) test will pass
                     * next time through. */
                    xNextTaskUnblockTime = portMAX_DELAY; /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
                    break;
                }
                else
                {
                    /* The delayed list is not empty, get the value of the
                     * item at the head of the delayed list.  This is the time
                     * at which the task at the head of the delayed list must
                     * be removed from the Blocked state. */
                    pxTCB = listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too.  Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */
                    xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) );

关键问题是,这个函数使用到了 pxDelayedTaskList, 这定义在本文件

 
   
PRIVILEGED_DATA static List_t * volatile pxDelayedTaskList;

该变量初始化为0,该变量正常初始化位置在创建 Task 对象等的函数中, 也就是说,如果在Tick中断到来时,如果还没有任务被创建,就会导致不可预期的结果,中断服务函数会使用这个野指针,执行任务切换。这会导致触发栈溢出钩子函数,或者是直接Hardfault。

有些硬件初始化需要借助delay功能,不得不在初始化之前配置SysTick。而又不希望在硬件初始化阶段触发这个Bug。

所以在配置SysTick之前,先创建一个初始化任务,初始化 pxDelayedTaskList 这个指针,在初始化任务里配置SysTick,和其他初始化,这样能够避免此类问题。或者是在配置SysTick的时候屏蔽中断,一切准备就绪后,开启中断。执行vTaskStartScheduler()默认创建一个空闲任务。

==========

往期回顾:

strlen和sizeof的异同

STM32CubeMx的串口DMA收发数据

好看的PCB也是产品的优势

STM32的DMA的五大问题

单片机的各个通信协议的波特率

==========

原文链接:点击阅读原文

平台:博客园

作者:ficusdxx

FreeRTOS中Systick的问题_第2张图片

FreeRTOS中Systick的问题_第3张图片

FreeRTOS中Systick的问题_第4张图片

你可能感兴趣的:(FreeRTOS中Systick的问题)