Top -> FreeRtos -> co-routine.html
Co-routines
[Getting Started]
コルーチン状態
コルーチンは厳しいRAM 制約がある非常に小さいプロセッサの上で使用を意図したものです。

コルーチンは次の状態の1つを取ることができます:


* Running

コルーチンが実際に実行しているとき、それは実行状態にあると言います。 コルーチンは現在プロセッサを利用しています。

* Ready

Ready状態のコルーチンは実行が可能な(ブロック状態に無い)状態ですが現在実行していない。 コルーチンがレディ状態にあるのは:

1.他に同等以上の優先度のコルーチンがすでに実行状態にある、あるいは
2.タスクが実行状態にある− アプリケーションがタスクとコルーチン両方を使っている場合がこれに相当する。

* Blocked

コルーチンが現在一時的あるいは外部のイベントを待っている場合ブロック状態にあると言う。 例えば、コルーチンが crDELAY () を呼びした時、遅延時間に到達するまでブロック状態になる。 ブロックされたコルーチンはスケジューリングされない。

コルーチンにはタスク中断状態に相当するものはありません。 これは後のリリースで加えられるかもしれません。
有効なコルーチン状態遷移

コルーチンプライオリティ


有効なコルーチン状態遷移

コルーチンプライオリティ
それぞれのコルーチンが0から(configMAX_CO_ROUTINE_PRIORITIES - 1)まで優先度を割り当てます。
configMAX_CO_ROUTINE_PRIORITIES はFreeRTOSConfig.h の中で定義されて、アプリケーションベースによって埋め込まれアプリケーション上で使用できる。 大きいconfigMAX_CO_ROUTINE_PRIORITIES値はFreeRTOS カーネルがより多くの RAM を消費することになる。

小さい優先度番号は低優先度コルーチンを意味する。

コルーチン優先度は他のコルーチンに対してだけ関係する。 同じアプリケーションの中でタスクとコルーチンを混用した場合、タスクは常にコルーチンの以上の優先度付けることになる。

コルーチンを実装します
コルーチンは次の構造を持ちます:
void vACoRoutineFunction( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
        {
            crSTART( xHandle );
            for( ;; )
            {
                -- Co-routine application code here. --
            }
            crEND();
        }
crCOROUTINE_CODE はvoid関数と定義されて、引数としてxCoRoutineHandle とインデックスがあります。 コルーチンの関数実装方法はすべて(上記)タイプでなければならない。 コルーチンはxCoRoutineCreate() によって作成される。

注意するべきポイント:
 * すべてのコルーチン関数は crSTART () へのコールでスタートしなくてはなりません。
 * すべてのコルーチン関数は crEND () へのコールで終わらなくてはなりません。
 * コルーチン関数は決してreturnしてはならない。典型的には無限ループとして実装されます。
 * 複数のコルーチンが一つのコルーチン関数から作成することができます。
uxIndex パラメータはこのようなコルーチンを区別する手段として提供されます。 下に例を示す


スケジューリングコルーチン
コルーチンはvCoRoutineSchedule () 繰り返しコールによってスケジュールされます。 vCoRoutineSchedule () を呼び出す最も良い場所はidleタスクフックの中からです。 アプリケーションが単にコルーチンを使うだけのでも、スケジューラ開始時に自動的にアイドルタスクが作成され、実行される。 下の例を見てください。

タスクとコルーチンの混合
アイドルタスクの中からコルーチンをスケジュールすることはタスクとコルーチンが同じアプリケーションの中で容易に混用できることを可能にします。 これのことにより、実行可能なアイドルタスクより高い優先度のタスクがないとき、コルーチンは実行する。 下の例を見てください

限界と制約事項
コルーチンのRAM 使用量の少なさは同等のタスクと比較すると若干の制約を犠牲にして優位にあります。これがコルーチンの使用目的になります。コルーチンの使用はタスクよりいっそう制約が多くて複雑です。
* スタックを共有します
コルーチンがブロックするとき、コルーチンのスタックは保守されません。
このことはスタック上に割り当てた変数は放棄されることを意味する。 これを克服するために、ブロックコールされるような変数はスタティック宣言しなくてはなりません。
例えば:
    void vACoRoutineFunction( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
    {
        static char c = 'a';
        // Co-routines must start with a call to crSTART().
        crSTART( xHandle );
        for( ;; )
        {
            // If we set c to equal 'b' here ...
            c = 'b';
            // ... then make a blocking call ...
            crDELAY( xHandle, 10 );
            // ... c will only be guaranteed to still
            // equal 'b' here if it is declared static// (as it is here).
        }
            // Co-routines must end with a call to crEND().
        crEND();
    }
スタック共有の結果コルーチンをブロックするにはコルーチン関数自体からのみ行うことができます
例えば:
    void vACoRoutineFunction( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
    {
        // Co-routines must start with a call to crSTART().
        crSTART( xHandle );
        for( ;; )
        {
            // It is fine to make a blocking call here,
            crDELAY( xHandle, 10 );
            // but a blocking call cannot be made from within// vACalledFunction().
            vACalledFunction();
        }
        // Co-routines must end with a call to crEND().
        crEND();
    }

    void vACalledFunction( void )
    {
          // Cannot make a blocking call here!
    }
* switch文の使用
FreeRTOS ダウンロードに含められたデフォルトコルーチン実装はswitch文の中からのブロックコールを許可していない。
例えば:
    void vACoRoutineFunction( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
    {
        // Co-routines must start with a call to crSTART().
        crSTART( xHandle );
        for( ;; )
        {
            // It is fine to make a blocking call here,
            crDELAY( xHandle, 10 );

            switch( aVariable )
            {
                case 1 : // Cannot make a blocking call here!
                         break;
                default: // Or here!
            }
        }
        // Co-routines must end with a call to crEND().
        crEND();
    }

Quick Example
この簡単な事例でコルーチンの用途をデモをします。
1. LED をフラッシュさせるために単純なコルーチンを作成すること
次のコードは何もしないが、周期的に LED をフラッシュさせる非常に単純なコルーチンを定義します。
    void vFlashCoRoutine( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
      {
          // Co-routines must start with a call to crSTART().
          crSTART( xHandle );

          for( ;; )
          {
              // Delay for a fixed period.
              crDELAY( xHandle, 10 );

              // Flash an LED.
              vParTestToggleLED( 0 );
          }

          // Co-routines must end with a call to crEND().
          crEND();
      }

これだけ。

2.コルーチンのスケジュール
コルーチンはvCoRoutineSchedule () の繰り返しコールによってスケジュールされます。 これをする最も良い場所はアイドルタスクの中のIdleタスクフックで書くことです。
最初に configUSE_IDLE_HOOK をFreeRTOSConfig.h の中で1にセットしてください。 それからアイドルタスクフックを書いてください:
    void vApplicationIdleHook( void )
    {
        vCoRoutineSchedule( void );
    }
代わりに、もしアイドルタスクが他のいかなる機能も実行していないなら、
ループの中から vCoRoutineSchedule () を呼び出す方がより効率的である。
    void vApplicationIdleHook( void )
    {
        for( ;; )
        {
            vCoRoutineSchedule( void );
        }
    }
3.コルーチンを作成して、そしてスケジューラーを始動させてください
コルーチンはmain () の中で作成する。
    #include "task.h"
    #include "croutine.h"
    #define PRIORITY_0 0
    void main( void )
    {
        // In this case the index is not used and is passed // in as 0.
        xCoRoutineCreate( vFlashCoRoutine, PRIORITY_0, 0 );
        // NOTE:  Tasks can also be created here!// Start the scheduler.
          vTaskStartScheduler();
    }
4.インデックスパラメータを使う
同じ関数から8つのコルーチンを作成すると仮定する。 各々コルーチンが異なったレートで異るLED をフラッシュさせることとする。 インデックスパラメータはコルーチン関数自身の中からコルーチンを区別する目的で使われる。

今回は8つのコルーチンを作成して、そしてそれぞれに異なったインデックスを引き渡す。
    #include "task.h"
    #include "croutine.h"

    #define PRIORITY_0        0
    #define NUM_COROUTINES    8

    void main( void )
    {
    int i;

        for( i = 0; i < NUM_COROUTINES; i++ )
        {
            // This time i is passed in as the index.
            xCoRoutineCreate( vFlashCoRoutine, PRIORITY_0, i );
        }

        // NOTE: Tasks can also be created here!

        // Start the scheduler.
        vTaskStartScheduler();
    }
コルーチン関数はそれでそれぞれが異なった LED とフラッシュレートを使う。
    const int iFlashRates[ NUM_COROUTINES ] = { 10, 20, 30, 40, 50, 60, 70, 80 };
    const int iLEDToFlash[ NUM_COROUTINES ] = { 0, 1, 2, 3, 4, 5, 6, 7 }

    void vFlashCoRoutine( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
    {
        // Co-routines must start with a call to crSTART().
        crSTART( xHandle );

        for( ;; )
        {
            // Delay for a fixed period.  uxIndex is used to index into
            // the iFlashRates.  As each co-routine was created with
            // a different index value each will delay for a different
            // period.
            crDELAY( xHandle, iFlashRate[ uxIndex ] );

            // Flash an LED.  Again uxIndex is used as an array index,
            // this time to locate the LED that should be toggled.
            vParTestToggleLED( iLEDToFlash[ uxIndex ] );
        }

        // Co-routines must end with a call to crEND().
        crEND();
    }

デモアプリケーション例
待ち行列とコルーチンを使うデモの二つのファイルがダウンロードできます:

1. crflash.c

これは機能上標準的なデモファイル flash.c と等しいですが、タスクの代わりにコルーチンを使います。 加えるに、そしてただデモンストレーション目的で、コルーチンの中から直接 LED をつけたり消したりする代わりに、(上の速い事例に従って)つけたり消したりされるべきである LED の数は待ち行列の上により高い優先権コルーチンに渡されます。

2. crhook.c

割り込みからコルーチンへデータを渡すデモをします。 チックフック関数がデータソースとして使用します。

PCと ARM Cortex-M3 デモアプリケーションはこれらのサンプルコルーチンファイルを使うためにすでに事前設定されてリファレンスとして使用できます。他のデモアプリケーションはタスクのみを使うように設定されています。しかし、下の手順に従えば、容易にコルーチンをデモするように変換できます。 これは flash.c で機能実装しているものを crflash.c の中で機能実装でリプレースします:

1. FreeRTOSConfig.h で、 configUSE_CO_ROUTINES と configUSE_IDLE_HOOK を1にセットしてください。

2.IDE プロジェクトあるいはプロジェクトメイクファイルで(デモプロジェクトに依存している):

@.FreeRTOS/Demo/Common/Minimal/flash.c に対する参照を FreeRTOS/Demo/Common/Minimal/crflash.c で置き換えてください。
A.ビルドにファイル FreeRTOS/Source/croutine.c を加えてください。

3. main.c で:

@.コルーチンマクロと関数プロトタイプを含むヘッダファイル croutine.h をインクルードしてください。
A. flash.h を crflash.h に取り替えてください。
B.flashタスク vStartLEDFlashTasks () を作る関数コールを取り除いてください・・・。
C・・・、flashコルーチンを作成すべきコルーチンの数nでvStartFlashCoRoutines (n)を作る機能で置き換えてください。 それぞれのコルーチンが異なったレートで異なった LED をフラッシュさせます。
D.コルーチンをスケジューリングするアイドルフック関数を加えてください:
    void vApplicationIdleHook( void )
    {
        vCoRoutineSchedule( void );
    }
もし main() がすでにアイドルフックを含んでいるなら、ただ既存のフック関数に vCoRoutineSchedule () へのコールを加えるだけです。

4。 フラッシュタスクをフラッシュコルーチンで置き換えることはスタックがより少なくなることを意味し、より少ないヒープスペースをスケジューラーのために取っておくことができます。
もしプロジェクトのビルドでcroutine.c を含めるには十分でない RAM 容量ならば、portTOTAL_HEAP_SPACE の定義を FreeRTOSConfig.h の中で(2 * portMINIMAL_STACK_SIZE)まで下げてください。

TOPページ