タスク制御用API関数とタスクの作り方


【タスクの生成と削除】

タスクの生成と削除を行うAPI関数で下表のような関数が用意されています。

関数名 機能と書式
xTaskCreate タスクの生成
《書式》
  portBASE_TYPE xTaskCreate( pdTASK_CODE pvTaskCode,
      const portCHAR* const pcName, unsigned portSHORT usStackDepth,
      void* pvParameters, unsigned portBASE_TYPE uxPriority,
      xTaskHandle* pvCreatedTask);
    pvTaskCode  :タスクの開始位置ポインタ
    pcName    :タスク名(デバッグ用)最大文字数制限あり
    usStackDepth :スタックに格納保持するデータ数
    pvParameters :タスク生成時のパラメータへのポインタ
    uxPriority  :生成するタスクの優先レベル
    pvCreatedTask :生成タスクの参照用ハンドル
    戻り値    :正常時 = pdPASS
vTaskDelete タスクの削除
《インクルード》#include _vTaskDelete 1
《書式》
  void vTaskDelete(xTaskHandle pxTask);
   pxTask :タスクのハンドル
《使用例》
  void Example1(void){
    xTaskHandle xHandle;
    xTaskCreate(vTask1, "Task1", 100, NULL, 1, &xHandle);
    ----
    vTaskDelete(xHandle);
  }

【FreeRTOSの制御】

FreeRTOSの動作開始停止や、全タスク一斉の休止、再開などの制御をするAPI関数です。

関数名 機能と書式
vTaskStartScheduler FreeRTOSの動作を開始させる
《書式》
  void vTaskStartScheduler(void);
vTaskEndScheduler FreeRTOSの動作を停止させる
《書式》
  void vTaskEndScheduler(void);
 タイマ1の割り込み停止、全タスクが削除される
vTaskSuspendAll 全タスクをサスペンド(休止)状態とする、割り込みは動作継続
《書式》
  void vTaskSuspendAll(void);
cTaskResumeAll 全タスクの休止状態を解除し動作を再開させる
《書式》
  portBASE_TYPE xTaskResumeAll(void);
   戻り値: pdTRUE = 正常  pdFALSE = 異常

【タスクの作り方】

FreeRTOS配下で動作するタスクの作り方は下記のようにします。

@ タスクはひとつの独立した関数として作成します。
  名称は任意で引数としてpvParametersというポインタを使うことができます。
  このパラメータは上表のxTaskCreate()の引数としてポインタの形で引き渡す
  ことができます。
A タスク関数内部は、ローカル変数があれば最初に宣言定義を記述します。
B タスク起動時に1回だけ実行する処理があれば初期化部として記述します。
  ここにはタスク内の変数や、内蔵モジュールなどの初期化を記述します。
C タスクの機能部は、while(1)またはfor(;;)の形で永久ループとして作成します。
  RTOSの機能を使う場合には、この中でAPI関数を使って記述します。
  必要であれば、タスク生成時に引数としてパラメータを渡すことができます。



下記は実際に作成したタスクの最も簡単な例となります。
この3つのタスクの機能は、それぞれ異なる一定間隔でLATAに接続された発光
ダイオードの点滅を実行します。

タスクのパラメータpvParametersは使っていませんが基本形ですので記述は必要です。
ここでは、どのタスクにもローカル変数宣言部、初期化部はありません。
タスク機能の中で一定時間待つために、下表にあるvTaskDelayというFreeRTOSの
API関数を使っています。この関数により、タスクがいったんブロック状態(待ち状態)
となり他のタスクに実行が移ります。
また、タスク実行中にタイマ1の割り込みが入れば、FreeRTOSに実行が移り、他に
優先順位の高いタスクがあればそちらに実行が移ります。



【タスクの生成と動作開始】

上記で作成したタスクの関数をFreeRTOSのタスクとして登録し、スケジューリング
の対象とする必要があります。このために使うのが上表のxTaskCreate()関数です。
タスク生成を行うのはメイン関数の中です。
実際の例は下記のようになります。この例では3つのタスクを生成しています。
宣言定義の部分でタスクとなる関数のプロトタイピングをしておきます。

メイン関数の初期化部で、各種の初期設定をし、xTaskCreate()関数を使って
3つのタスクを生成しています。
すべて同じ優先レベル1でスタック格納数も100個と同じにしています。
この例のように使わないパラメータの部分はNULLで定義します。
タスク名の"T1"とか"T2"は、デバッグの際に使うもので、この名前でログが出力されます。
PIC24/dsPICの場合は、これを符号付の型にするためのキャストの追加が必要です。

タスク生成が完了したら、vTaskStartScheduler()関数を実行してFreeRTOSの実行
を開始させます。
通常のシステムでは、タスクはいったん生成したら、タスク削除はせず永久に動作
継続させるのが普通の使い方です。
したがって、メモリ管理も固定配置方式のheap_1.cをリンクして使います。



★★★ この例のプロジェクトファイル1式 ダウンロード(sample1)


【タスクの優先順位】

タスクには優先順位が付けられます。FreeRTOSがスケジューリングする際に
このタスクの優先順位が高いものから先に実行権を与えます。
上記の例の場合には、すべて同じレベル1としましたが、この例の場合には
タスクごとにレベルを異なるものにしても、vTaskDelay()関数を実行すると、
その時点でブロック状態になりますから、この間に優先順位が低いものが実行
可能となりますので、同じレベルの場合と全く変わらずに動作します。

しかし、下記のようなタスクとすると、様子は全く異なるものになります。
このタスクは遅延にループディレイを使っています。つまりディレイ時間の間も
タスクとして実行時間を占有してしまいます。
この場合3つのタスクがすべて同じ優先順位の場合には、タイマ1の割り込みで
順番にタスクが切り替えられて実行されますので、上記の例と同じように
動作し、3個のLEDすべてがそれぞれの時間間隔で点滅します。

 しかし、どれかひとつでも優先順位が高いものがあると、そのタスクだけに
すべての実行時間を占有されてしまいますので、優先レベルの高いタスクの
1個のLEDしか点滅をしません。



★★★ この例のプロジェクトファイル1式 ダウンロード(sample2)


【タスク制御用API関数】

タスクの状態を制御するためのAPI関数には下表のものが用意されています。
関数名 機能と書式
vTaskDelay 指定されたTick数だけタスクを遅延させる。
《インクルード》#include _vTaskDelay 1
《書式》
   void vTaskDelay(portTickType xTicksToDelay);
    xTicksToDelay :遅延させる時間の指定 リアルタイムの時間にするには
             portTICK_RATE_MSで除算した値とする
《使用例》
  void Example2(void){
    while(1){
      LATA = ^0xFF;
      vTaskDelay(500 / portTICK_RATE_MS);
    }
  }  
vTaskDelayUntil 指定した時間までタスクを遅延させる。一定周期でタスクを起動するのに使う
《インクルード》#include _vTaskDelayUntil 1
《書式》
   void vTaskDelayUntil(portTickType* pxPreviousWakeTime,
              portTickType xTimeIncrement);
    pxPreviousWakeTime:前回起動した時間変数へのポインタ
              初期化が必要
    xTimeIncrement
 :起動サイクル時間
《使用例》
  void Example3(void) {
    portTickType xLastWakeTime;
    const portTickType xFrequency = 10;
    xLastWakeTime = xTaskGetTickCount();  //初期値のセット
    while(1){
      vTaskDelayUntil(&xLastWakeTime, xFrequency);
    }
  }
uxTaskPriorityGet 指定したタスクの優先順位を取得する
《インクルード》 #include _vTaskPriorityGet 1
《書式》
  unsigned portBASE_TYPE uxTaskPriorityGet(xTaskHandle pxTask);
    pxTask :取得するタスクのハンドル
       (NULLを指定すると呼び出し元指定となる)
    戻り値 :優先順位
vTaskPrioritySet 指定したタスクに優先順位を設定する
《インクルード》 #include _vTaskPrioritySet 1
《書式》
  void vTaskPrioritySet(xTaskHandle pxTask,
             unsigned portBASE_TYPE
uxNewPriority);
     pxTask      :タスクのハンドル
             (NULLを指定すると呼び出し元指定となる)
     uxNewPriority :優先順位
vTaskSuspend 指定タスクをサスペンド状態(休止状態)にする
《インクルード》 #include _vTaskSuspend 1
《書式》
  void vTaskSuspend(xTaskhandle pxTaskToSuspend);
    pxTaskToSuspend :休止させるタスクのハンドル
            (NULLを指定すると呼び出し元指定となる)
vTaskResume 指定タスクを休止状態からレディー状態とする
《インクルード》  #include _vTaskSuspend 1
《書式》
   void vTaskResume(xTaskHandle pxTaskToResume);
     pxTaskToResume :レディーにするタスクのハンドル
vTaskResumeFromISR 割り込み処理ルーチン内から指定タスクをレディー状態にする
《インクルード》 #include _vTaskSuspend 1
         #include _xTaskResumeFromISR 1
《書式》
  portBASE_TYPE vTaskResumeFromISR(xTaskHandle pxTaskToResume);
    pxTaskToResume :レディーにするタスクのハンドル
    戻り値     :pdTRUE = 正常  pdFALSE = 異常
vTaskSetApplicationTaskTag アプリだけで使用するタグを付加する。FreeRTOSは使わない
《インクルード》 #include configUSE_APPLICATION_TASK_TAG  1
《書式》
   void vTaskSetApplicationTaskTag(xTaskHandle xTask,
                pdTASK_HOOK_CODE pxTagValue);
      xTask    :タスクのハンドル値
      pxTagValue :タスクタグとして付加するデータへのポインタ
《使用例》
  void vATask(void *pvParameters){
    vTasksetApplicationTaskTag(NULL, (void *) 1);
    for(;;){
      //タスクのコード
    }
  }
vTaskCallApplicationTaskHook デバッグ用のトレースのとき、タスクのフックの出力用としてタグを使えるようにする
《インクルード》 #include configUSE_APPLICATION_TASK_TAG 1
《書式》
   portBASE_TYPE xTaskCallApplicationTaskHook(xTaskHandle xTask,
               void *pvParameter);
     xTask   :タスクのハンドル値
     pvParameter:フックの際に出力するタグへのポインタ

【周期起動タスクの作り方】

一定周期ごとに機能を繰り返すようなタスクは、組み込みシステムには
必ず必要になります。このような周期起動するタスクの作り方には
2種類の方法が提供されています。

上表にあるvTaskDelay()関数とvTAskDelayUntil()関数を使う方法の2種類
です。

【方法1】 vTaskDelay()関数による方法
 実際の例題を示すと下記のようになります。タスクごとにwhile(1)の永久
ループの中に一定時間の遅延を挿入していますから、遅延が終了すると
再度同じことを繰り返し実行することになります。
これで一定周期でタスクが機能を繰り返すことになりますが、この一定と
いう時間は他の割り込みやタスク実行の状況により時間がずれることに
なりますので、余り正確な時間間隔ではありません。

(注意)
vTaskDelay()関数を使う場合には、FreeRTOSconfig.h内で
     #include _vTaskDelay 1 とする必要があります。



【方法2】xTaskDelayUntil()関数のよる方法
 これも実際の例題を示すと下記のようになります。
xLastExecutionTimeという現在Tick値の変数を用意し、これに現在のTickの
カウント値を初期値として代入します。
その後タスクのwhile(1)の永久ループの中で、vTaskDelayUntil()関数を使い
この現在Tick値と周期時間を指定すれば、Tickのカウント値が指定した周期
時間だけ増加したら、タスクの待ちを解除して先に進みます。
これでこの関数以下の機能が実行されます。
このとき毎回現在Tick値を更新しますので、常に指定した周期でタスクが起動
されることになります。しかもこの周期はTickのカウント増し分でチェックされます
から、途中で割り込み処理などでTickのタイミングがずれても、かなり正確な
一定周期となります。

(注意)vTaskDelayUntil()関数を使う場合には、FreeRTOSconfig.h内で
     #include _vTaskDelayUntil 1 とする必要があります。






目次に戻る