FreeRTOSシステムカーネル制御関数

28821 ワード

本稿は『ALIENTEK STM 32 F 429 FreeRTOS開発チュートリアル』第10章学習ノート第1章ノート–FreeRTOS概要とソースコードダウンロード第2章ノート–FreeRTOS STM 32 F 4に移植第3章ノート-FreeRTOSシステム構成第4章ノート-FreeRTOS中断分析第4章ノート補完-FreeRTOS臨界セグメントコード第5章ノート-FreeRTOSタスク基礎第6章ノート-FreeRTOSタスクAPI関数の使用第7章ノート-FreeRTOSリストとリスト項目第8章ノート-1-FreeRTOSタスク作成第8章メモ-2-FReeRTOSタスクスケジューラ第9章メモ-FReeRTOSタスク切り替え
FreeRTOSカーネル制御関数はカーネルが使用する関数で、一般的なアプリケーションでは使用されません
関数#カンスウ#
説明
taskTIELD()
タスクの切り替え
taskENTER_CRITICAL()
ミッションで使用する臨界領域に入ります
taskEXIT_CRITICAL()
タスクで使用する臨界領域を終了
taskENTER_CRITICAL_FROM_ISR()
サービス関数を中断するための臨界領域に入る
taskEXIT_CRITICAL_FROM_ISR()
サービス関数を中断するための臨界領域を終了
taskDISABLE_INTERRUPTS()
クローズブレーク
taskENABLE_INTERRUPTS()
オープンブレーク
vTaskStartScheduler()
タスクスケジューラを開く
vTaskEndScheduler()
タスクスケジューラを閉じる
vTaskSuspendAll()
タスクスケジューラを一時停止
xTaskResumeAll()
リカバリタスクスケジューラ
vTaskStepTick()
システムビート値の設定

taskYIELD()

#define taskYIELD()     portYIELD()

#define portYIELD()                                 
{
    portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
    __dsb( portSY_FULL_READ_WRITE );            
    __isb( portSY_FULL_READ_WRITE );            
}

portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT:
#define portNVIC_INT_CTRL_REG       ( * ( ( volatile uint32_t * ) 0xe000ed04 ) )
#define portNVIC_PENDSVSET_BIT      ( 1UL << 28UL )

割り込み制御およびステータスレジスタICSR(アドレス:0 xE 000_ED 04)は、ICSRの28番目のビットに1サスペンションPendSV(PendSV割り込み開始)を書き込み、PendSV割り込みサービス関数でタスク切替を行う
__dsb( portSY_FULL_READ_WRITE );__isb(portSY_FULL_READ_WRITE):dsbとisbがデータ同期分離と命令同期分離を完了する役割は、以前のメモリアクセス操作と命令の両方が完了することを保証することである(個人的にはPendSVがオンになっていることを確認することであると考える)
割り込みレベルタスク切替関数はportYIELD_FROM_ISR():
#define portEND_SWITCHING_ISR( xSwitchRequired ) if( xSwitchRequired != pdFALSE ) portYIELD()
#define portYIELD_FROM_ISR( x ) portEND_SWITCHING_ISR( x )

割り込みレベルのタスク切替関数も、関数portYIELD()を呼び出すことで完了していることがわかります.

taskENTER_CRITICAL() & taskEXIT_CRITICAL()


タスクレベルの臨界セグメントコードとして保護
臨界セグメントに入る:taskENTER_CRITICAL()
終了臨界セグメント:taskEXIT_CRITICAL()
この2つの関数は、次のように定義されたマクロ定義です.
#define taskENTER_CRITICAL() portENTER_CRITICAL()
#define taskEXIT_CRITICAL() portEXIT_CRITICAL()

それぞれ関数portENTERをカプセル化CRITICAL()とportEXIT_CRITICAL()であり、カプセル化された2つの関数もマクロ定義であり、以下のように定義される.
#define portENTER_CRITICAL() vPortEnterCritical()
#define portEXIT_CRITICAL() vPortExitCritical()

関数vPortEnterCritical()とvPortExitCritical()はファイルport.cでは、関数のプロトタイプは次のとおりです.
void vPortEnterCritical( void )
{
    portDISABLE_INTERRUPTS();
    uxCriticalNesting++;
    if( uxCriticalNesting == 1 )
    {
        configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );
    }
}

void vPortExitCritical( void )
{
    configASSERT( uxCriticalNesting );
    uxCriticalNesting--;
    if( uxCriticalNesting == 0 )
    {
        portENABLE_INTERRUPTS();
    }
}

関数vPortEnterCritical()に入り、portDISABLE_を先に実行します.INTERUPTS()関数(クローズ割り込み)
さらにuxCriticalNesting++を実行し、変数に変数を加えて臨界セグメントネスト回数を表す
次に1の場合、断言関数に入ります.
configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );

#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); } 

関数vPortExitCritical()に入りconfigASSERT(uxCriticalNesting)を実行します.
更にuxCriticalNestingに対して1を減らして変数が0の時だけ関数portENABLEを呼び出しますINTERUPTS()イネーブル中断
これにより、複数の臨界セグメントコードがある場合に、ある臨界セグメントコードの終了によって他の臨界セグメントの保護を乱すことなく、すべての臨界セグメントコードが終了してから中断できることが保証される.

taskENTER_CRITICAL_FROM_ISR() & taskEXIT_CRITICAL_FROM_ISR()


割り込みサービスプログラムで使用される臨界セグメントコード保護
臨界セグメントに入る:taskENTER_CRITICAL_FROM_ISR()
終了臨界セグメント:taskEXIT_CRITICAL_FROM_ISR()
#define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR()
#define taskEXIT_CRITICAL_FROM_ISR( x ) portCLEAR_INTERRUPT_MASK_FROM_ISR( x )

#define portSET_INTERRUPT_MASK_FROM_ISR() ulPortRaiseBASEPRI()
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortSetBASEPRI(x)

ulPortRaiseBASEPRI():
static portFORCE_INLINE uint32_t ulPortRaiseBASEPRI( void )
{
uint32_t ulReturn, ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;

    __asm
    {
        mrs ulReturn, basepri
        msr basepri, ulNewBASEPRI
        dsb
        isb
    }

    return ulReturn;
}

まずBASEPRIレジスタの値を読み出しulReturnに保存する
configmaX_をSYSCALL_INTERRUPT_PRIORITY BASEPRIレジスタに書き込む
ulReturnの値を返します
vPortSetBASEPRI():
static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
{
    __asm
    {
        msr basepri, ulBASEPRI
    }
}

BASEPRIレジスタに値を書き込む
割り込みレベルの臨界コード保護の使用:
{
    status_valie=taskENTER_CRITICAL_FROM_ISR();
    ...
    taskEXIT_CRITICAL_FROM_ISR(STATUS_VALUE);
}

taskDISABLE_INTERRUPTS() & taskENABLE_INTERRUPTS()

#define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI()
#define portENABLE_INTERRUPTS() vPortSetBASEPRI( 0 )

static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
{
    __asm
    {
        /* Barrier instructions are not used as this function is only used to
        lower the BASEPRI value. */
        msr basepri, ulBASEPRI
    }
}
/*-----------------------------------------------------------*/

#define configMAX_SYSCALL_INTERRUPT_PRIORITY  ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )

static portFORCE_INLINE void vPortRaiseBASEPRI( void )
{
uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;

    __asm
    {
        /* Set BASEPRI to the max syscall priority to effect a critical
        section. */
        msr basepri, ulNewBASEPRI
        dsb
        isb
    }
}

portENABLE_INTERUPTS()は、BASEPRIレジスタに0を書き込む、すなわちマスク優先度が0以下の割り込みであり、0優先度が最も高いため、マスク割り込み(オープン割り込み)を停止する
portDISABLE_INTERUPTS()は、BASEPRIレジスタにconfigmaX_を書き込むSYSCALL_INTERRUPT_PRIORITY(最低優先度)、configmaX_より低いSYSCALL_INTERRUPT_PRIORITY優先度の割り込みはブロックされているので、ブロック割り込み

vTaskStartScheduler()

void vTaskStartScheduler( void )
{
BaseType_t xReturn;


    /* Add the idle task at the lowest priority. */
    #if( configSUPPORT_STATIC_ALLOCATION == 1 )
    {
        StaticTask_t *pxIdleTaskTCBBuffer = NULL;
        StackType_t *pxIdleTaskStackBuffer = NULL;
        uint32_t ulIdleTaskStackSize;

        /* The Idle task is created using user provided RAM - obtain the
        address of the RAM then create the idle task. */
        vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &ulIdleTaskStackSize );
        xIdleTaskHandle = xTaskCreateStatic(    prvIdleTask,
        "IDLE",
        ulIdleTaskStackSize,
        ( void * ) NULL,
        ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ),
        pxIdleTaskStackBuffer,pxIdleTaskTCBBuffer ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */

        if( xIdleTaskHandle != NULL )
        {
            xReturn = pdPASS;
        }
        else
        {
            xReturn = pdFAIL;
        }
    }
    #else
    {
        /* The Idle task is being created using dynamically allocated RAM. */
        xReturn = xTaskCreate(  prvIdleTask,
                                "IDLE", configMINIMAL_STACK_SIZE,
                                ( void * ) NULL,
                                ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ),
                                &xIdleTaskHandle ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */
    }
    #endif /* configSUPPORT_STATIC_ALLOCATION */

    #if ( configUSE_TIMERS == 1 )
    {
        if( xReturn == pdPASS )
        {
            xReturn = xTimerCreateTimerTask();
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
    }
    #endif /* configUSE_TIMERS */

    if( xReturn == pdPASS )
    {
        /* Interrupts are turned off here, to ensure a tick does not occur
        before or during the call to xPortStartScheduler().  The stacks of
        the created tasks contain a status word with interrupts switched on
        so interrupts will automatically get re-enabled when the first task
        starts to run. */
        portDISABLE_INTERRUPTS();

        #if ( configUSE_NEWLIB_REENTRANT == 1 )
        {
            /* Switch Newlib's _impure_ptr variable to point to the _reent
            structure specific to the task that will run first. */
            _impure_ptr = &( pxCurrentTCB->xNewLib_reent );
        }
        #endif /* configUSE_NEWLIB_REENTRANT */

        xNextTaskUnblockTime = portMAX_DELAY;
        xSchedulerRunning = pdTRUE;
        xTickCount = ( TickType_t ) 0U;

        /* If configGENERATE_RUN_TIME_STATS is defined then the following
        macro must be defined to configure the timer/counter used to generate
        the run time counter time base. */
        portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();

        /* Setting up the timer tick is hardware specific and thus in the
        portable interface. */
        if( xPortStartScheduler() != pdFALSE )
        {
            /* Should not reach here as if the scheduler is running the
            function will not return. */
        }
        else
        {
            /* Should only reach here if a task calls xTaskEndScheduler(). */
        }
    }
    else
    {
        /* This line will only be reached if the kernel could not be started,
        because there was not enough FreeRTOS heap to create the idle task
        or the timer task. */
        configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY );
    }

    /* Prevent compiler warnings if INCLUDE_xTaskGetIdleTaskHandle is set to 0,
    meaning xIdleTaskHandle is not used anywhere else. */
    ( void ) xIdleTaskHandle;
}

if(configSUPPORT_STATIC_ALLOCATION==1){......}:アイドルタスクを作成し、静的メモリを使用して関数xTaskCreateStatic()を使用してアイドルタスクを作成する場合は、優先順位は(tskIDLE_PRIORITY|portPRIVILEG_BIT)、マクロtskIDLE_PRIORITYとportPRIVILEGE_BITはいずれも0であり,つまり空きタスクの優先度が最も低いことを示す.
Else{xReturn=xTaskCreate(…#endif:このとき動的メモリを使用して関数xTaskCreate()を使用してタスクを作成する
if(configUSE_TIMES==1){…..} #endif:ソフトウェアタイミングを使用する場合、関数xTimerCreateTimerTask()を使用してタイマサービスタスクを作成します.タイマーサービスタスクの主なコード:
xReturn = xTaskCreate(  prvTimerTask,
                        "Tmr Svc",
                         configTIMER_TASK_STACK_DEPTH,
                        NULL,
                        ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT,
                        &xTimerTaskHandle );

if(xReturn==pdPASS)portDISABLE_INTERUPTS():アイドルタスクとタイマタスクが正常に作成された場合、割り込みを閉じます.
if(configUSE_NEWLIB_REENTRANT==1)……#endif:関連マクロが構成されていればNEWLIBが可能
xNextTaskUnblockTime = portMAX_DELAY;xSchedulerRunning = pdTRUE;xTickCount = ( TickType_t ) 0U; :関連するグローバル変数を設定し、変数xSchedulerRunningがpdTRUEに設定されると、スケジューラが実行を開始することを示す
portCONFIGURE_TIMER_FOR_RUN_TIME_STATS():マクロconfigGENERATE_RUN_TIME_STATSが1の場合はイネーブルタイム統計機能が必要であり、ここではこのマクロをイネーブルしないタイマ/カウンタを配置する必要がある
if(xPortStartScheduler() != pdFALSE)…else{}:関数xPortStartScheduler()を呼び出してスケジューラ起動に関連するハードウェア(滴下タイマ、FPUユニット、PendSV割り込みなど)を初期化しますスケジューラ起動に成功するとifやelseには動作しません.関数に戻り値がないためです
else{configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY );}: プログラムがここまで実行されたのは、システムカーネルが起動に成功しなかったことを示しています.なぜなら、アイドルタスクまたはタイマタスクを作成するときに十分なメモリがないため、断言関数でエラーを報告します.

vTaskEndScheduler()


タスクスケジューラをオフにします.FreeRTOSでは、この関数はX 86アーキテクチャのプロセッサにのみ使用されます.
void vTaskEndScheduler( void )
{

    portDISABLE_INTERRUPTS();
    xSchedulerRunning = pdFALSE;
    vPortEndScheduler();
}

まず割り込みをオフにし、タスクスケジューラの停止をマークし、vPortEndScheduler()関数を実行します.ハードウェア層の割り込みをオフにする処理関数です.ソースコードには具体的な操作はありません.実際のプロセッサに基づいて作成します.
void vPortEndScheduler( void )
{
    /* Not implemented in ports where there is nothing to return to.
    Artificially force an assert. */
    configASSERT( uxCriticalNesting == 1000UL );
}

vTaskSuspendAll()

void vTaskSuspendAll( void )
{
    ++uxSchedulerSuspended;
}

関数はネストされたカウンタ(uxSchedulerSuspended)を1つ追加し、関数xTaskResumeAll()を使用してタスクスケジューラを復元し、vTaskSuspendAll()を数回呼び出すと、最終的にスケジューラを復元するにはxTaskResumeAll()を数回呼び出す必要があります.
スケジューラがハングアップされると、本来ブロック状態からレディ状態への優先度がxのタスクは、タスクレディリストpxReadyTaskLists[x]には追加されず、xPendingReadyListのリストに追加される

xTaskResumeAll()

BaseType_t xTaskResumeAll( void )
{
TCB_t *pxTCB = NULL;
BaseType_t xAlreadyYielded = pdFALSE;

    /* If uxSchedulerSuspended is zero then this function does not match a
    previous call to vTaskSuspendAll(). */
    configASSERT( uxSchedulerSuspended );

    /* It is possible that an ISR caused a task to be removed from an event
    list while the scheduler was suspended.  If this was the case then the
    removed task will have been added to the xPendingReadyList.  Once the
    scheduler has been resumed it is safe to move all the pending ready
    tasks from this list into their appropriate ready list. */
    taskENTER_CRITICAL();
    {
        --uxSchedulerSuspended;

        if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
        {
            if( uxCurrentNumberOfTasks > ( UBaseType_t ) 0U )
            {
                /* Move any readied tasks from the pending list into the
                appropriate ready list. */
                while( listLIST_IS_EMPTY( &xPendingReadyList ) == pdFALSE )
                {
                    pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList ) );
                    ( void ) uxListRemove( &( pxTCB->xEventListItem ) );
                    ( void ) uxListRemove( &( pxTCB->xStateListItem ) );
                    prvAddTaskToReadyList( pxTCB );

                    /* If the moved task has a priority higher than the current
                    task then a yield must be performed. */
                    if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
                    {
                        xYieldPending = pdTRUE;
                    }
                    else
                    {
                        mtCOVERAGE_TEST_MARKER();
                    }
                }

                if( pxTCB != NULL )
                {
                    /* A task was unblocked while the scheduler was suspended,
                    which may have prevented the next unblock time from being
                    re-calculated, in which case re-calculate it now.  Mainly
                    important for low power tickless implementations, where
                    this can prevent an unnecessary exit from low power
                    state. */
                    prvResetNextTaskUnblockTime();
                }

                /* If any ticks occurred while the scheduler was suspended then
                they should be processed now.  This ensures the tick count does
                not slip, and that any delayed tasks are resumed at the correct
                time. */
                {
                    UBaseType_t uxPendedCounts = uxPendedTicks; /* Non-volatile copy. */

                    if( uxPendedCounts > ( UBaseType_t ) 0U )
                    {
                        do
                        {
                            if( xTaskIncrementTick() != pdFALSE )
                            {
                                xYieldPending = pdTRUE;
                            }
                            else
                            {
                                mtCOVERAGE_TEST_MARKER();
                            }
                            --uxPendedCounts;
                        } while( uxPendedCounts > ( UBaseType_t ) 0U );

                        uxPendedTicks = 0;
                    }
                    else
                    {
                        mtCOVERAGE_TEST_MARKER();
                    }
                }

                if( xYieldPending != pdFALSE )
                {
                    #if( configUSE_PREEMPTION != 0 )
                    {
                        xAlreadyYielded = pdTRUE;
                    }
                    #endif
                    taskYIELD_IF_USING_PREEMPTION();
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
    }
    taskEXIT_CRITICAL();

    return xAlreadyYielded;
}

configASSERT(uxSchedulerSuspended):uxSchedulerSuspendedがスケジューラが保留されていないことを証明し、スケジューラの保留関数に対応する数が一致しない場合、エラーと断言する
taskENTER_CRITICAL():臨界領域に入る
-uxSchedulerSuspended:スケジューラがネストされたカウンタを掛けて1を減らす
if(uxSchedulerSuspended=(UBAseType_t)pdFALSE):uxSchedulerSuspendedが0の場合、すべての保留が開始され、スケジューラが動作します
if(uxCurrentNumberOfTasks>(UBAseType_t)0 U):タスク数が0より大きい場合、実行を開始します.
while(listLIST_IS_EMPTY(&xPendingReadyList)==pdFALSE):ループ処理リストxPendingReadyListは、リストが空でない限りリストxPendingReadyListにタスクが掛かっていることを示し、これらのタスクをリストxPendingReadyListから削除してタスク対応の準備リストに追加する必要がある
pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY(&xPendingReadyList):リストxPendingReadyListを巡回し、リストxPendingReadyListに掛けられたタスクに対応するタスク制御ブロックを取得する
(void)uxListRemove(&(pxTCB->xEventListItem):イベントリストからタスクを削除
(void)uxListRemove(&(pxTCB->xStateListItem):ステータスリストからタスクを削除
prvAddTaskToReadyList(pxTCB):呼び出し関数は、xPendingReadyListリストから取得したタスクを準備リストに追加します.
if(pxTCB->uxPriority>=pxCurrentTCB->uxPriority){......}:タスクの優先度が現在実行中のタスクより高いか否かを判断し、xYieldPendingをpdTRUEに付与した場合、タスク切替が必要であることを示す
if( pxTCB != NULL ){prvResetNextTaskUnblockTime();}:タスクスケジューリングが円滑に行われると、将来のロック解除時間が再計算されないように、ロック解除時間が再計算される
UBaseType_t uxPendedCounts = uxPendedTicks…mtCOVERAGE_TEST_MARKER()}}:タスクスケジュール気候を保留し、各滴下タイマの割り込みはxTickCountを更新せず、uxPendedTicksでスケジューラ保留中のクロック数を記録し、スケジューラ時にクロック割り込みが発生した場合、このとき処理すべきであり、滴下カウントがスライドせず、いかなる遅延タスクも正しい時間に回復することを確保できる
if(xYieldPending!=pdFALSE):タスクの優先度が現在実行されているタスクより高いと判断した場合、タスクの切り替えが必要です.
xAlreadyYielded=pdTRUE:関数xTaskResumeAll()にタグ付けされたタスクの切り替え
taskYIELD_IF_USING_PREEMPTION():この関数を呼び出してタスク切り替えを行います
#define taskYIELD_IF_USING_PREEMPTION() portYIELD_WITHIN_API()
#define portYIELD_WITHIN_API portYIELD

実はportYIELD()でタスク切り替え
taskEXIT_CRITICAL():クリティカルゾーンを終了
return xAlreadyYielded:Bianビームを返し、xTaskResumeAll()でタスクの切り替えが行われたかどうかを示します.

vTaskStepTick()


システムビート値を設定し、FreeRTOSの低消費電力ticklessモードを使用する場合、マクロconfigUSE_TICKLESS_IDLEが1の場合に使用
#if ( configUSE_TICKLESS_IDLE != 0 )

    void vTaskStepTick( const TickType_t xTicksToJump )
    {
        configASSERT( ( xTickCount + xTicksToJump ) <= xNextTaskUnblockTime );
        xTickCount += xTicksToJump;
        traceINCREASE_TICK_COUNT( xTicksToJump );
    }

#endif

ポタポタカウンタにポタポタジャンプ値を加算