FreeRTOS臨界セグメントコード


本稿は『ALIENTEK STM 32 F 429 FreeRTOS開発チュートリアル』第4章学習ノートの補足第1章ノート–FreeRTOS概要とソースコードダウンロード第2章ノート–FreeRTOS STM 32 F 4に第3章ノートを移植-FreeRTOSシステム構成第4章ノート-FreeRTOS中断分析

1.臨界セグメントコードの概要


臨界セグメントコードは臨界領域とも呼ばれ、完全に実行され、中断できないコードセグメントを指す.
FreeRTOSは,臨界セグメントコードに入る際に割り込みを閉じる必要があり,臨界セグメントコードを処理した後に割り込みを開く.
FreeRTOSシステム自体には多くの臨界セグメントコードが保護されています

2.タスクレベルの臨界セグメントコード保護


臨界セグメントに入る: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()イネーブル中断
#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
    }
}

portENABLE_INTERUPTS()は、BASEPRIレジスタに0を書き込む、すなわち、マスク優先度が0以下の割り込みであり、0優先度が最も高いため、マスク割り込みを停止する
これにより、複数の臨界セグメントコードがある場合に、ある臨界セグメントコードの終了によって他の臨界セグメントの保護を乱すことなく、すべての臨界セグメントコードが終了してから中断できることが保証される.

3.割り込みレベルの臨界セグメントコード保護


割り込みサービスプログラムで使用される臨界セグメントコード保護
臨界セグメントに入る: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レジスタに値を書き込む

割り込みレベルの臨界コード保護の使用方法:

// 5 
void TIM5_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&TIM5_Handler);
}

// , 
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    uint32_t status_value
    if(htim==(&TIM5_Handler))
    {
        status_value=taskENTER_CRITICAL_FROM_ISR();
        printf("TIM5 .......\r
"
); taskEXIT_CRITICAL_FROM_ISR(status_value); } }