RTOS (日本語)

Handbook » RTOSの日本語翻訳(ドラフト1.0)

                 原文:Handbook » RTOS http://mbed.org/handbook/RTOS

mbed RTOS APIはCMSIS RTOS C APIを単純にC++エンキャプスレートしたものである。

RTOS Library URL: http://mbed.org/projects/libraries-testing/svn/rtos

スレッド

スレッド・クラスでシステム内のスレッド関数を定義、生成または制御することができる。main関数は特別な関数で、システム初期化時に起動され、初期優先順位osPriorityNormalを持つ。

#include "mbed.h"
#include "rtos.h"

DigitalOut led1(LED1);
DigitalOut led2(LED2);

void led2_thread(void const *argument) {
    while (true) {
        led2 = !led2;
        Thread::wait(1000);
    }
}

int main() {
    Thread thread(led2_thread);
    
    while (true) {
        led1 = !led1;
        Thread::wait(500);
    }
}

main関数 main関数自身もRTOSによってスケジュールされる一つ目のスレッドである。

相互排他

相互排他(Mutex)はスレッドの実行を同期するために使用される。例えば、共有資源へのアクセスを保護するため。

ISR T相互排他(Mutex)メソッドは割込みサービス・ルーチン(ISR)から呼び出すことはできない。

http://mbed.org/media/uploads/emilmont/mutex.png

#include "mbed.h"
#include "rtos.h"

Mutex stdio_mutex; 

void notify(const char* name, int state) {
    stdio_mutex.lock();
    printf("%s: %d\n\r", name, state);
    stdio_mutex.unlock();
}

void test_thread(void const *args) {
    while (true) {
        notify((const char*)args, 0); Thread::wait(1000);
        notify((const char*)args, 1); Thread::wait(1000);
    }
}

int main() {
    Thread t2(test_thread, (void *)"Th 2");
    Thread t3(test_thread, (void *)"Th 3");
    
    test_thread((void *)"Th 1");
}

C標準ライブラリの相互排他 ARM C標準ライブラリは既にstdioアクセスを保護するための相互排他を装備しているので、M3mbedでは上の例は必要ない。 一方、ARM microlib (M0 mbedで使用されている)はデフォルトのstdio相互排他を提供していないので上の例が必要となる。

ISR内のprintf ARM C標準ライブラリ内に相互排他があるので、ISR内でprintfを使用することはできない!

セマフォ

セマフォはある種類の共有資源のプールに対するスレッド・アクセスを管理するのに特に便利である。

http://mbed.org/media/uploads/emilmont/semaphore.png

#include "mbed.h"
#include "rtos.h"

Semaphore two_slots(2);

void test_thread(void const *name) {
    while (true) {
        two_slots.wait();
        printf("%s\n\r", (const char*)name);
        Thread::wait(1000);
        two_slots.release();
    }
}

int main (void) {
    Thread t2(test_thread, (void *)"Th 2");
    Thread t3(test_thread, (void *)"Th 3");
    
    test_thread((void *)"Th 1");
}

シグナル

各スレッドはシグナルを通知されたり、待ったりすることができる:

/media/uploads/takashikojo/signal2.png

#include "mbed.h"
#include "rtos.h"

DigitalOut led(LED1);

void led_thread(void const *argument) {
    while (true) {
        // Signal flags that are reported as event are automatically cleared.
        Thread::signal_wait(0x1);
        led = !led;
    }
}

int main (void) {
    Thread thread(led_thread);
    
    while (true) {
        Thread::wait(1000);
        thread.signal_set(0x1);
    }
}

キュー

キューによって、生産スレッドから消費スレッドに対してデータへのポインターを待ち行列に入れることができる:

http://mbed.org/media/uploads/emilmont/messagequeue.png

Queue<message_t, 16> queue;

message_t *message;

queue.put(message);

osEvent evt = queue.get();
if (evt.status == osEventMessage) {
    message_t *message = (message_t*)evt.value.p;

メモリー・プール

MemoryPoolクラスは固定サイズのメモリー・プールを定義、管理するために使用する。

MemoryPool<message_t, 16> mpool;

message_t *message = mpool.alloc();

mpool.free(message);

mail.cpp

#include "mbed.h"
#include "rtos.h"

typedef struct {
    float    voltage;   /* AD result of measured voltage */
    float    current;   /* AD result of measured current */
    uint32_t counter;   /* A counter value               */
} message_t;

MemoryPool<message_t, 16> mpool;
Queue<message_t, 16> queue;

/* Send Thread */
void send_thread (void const *argument) {
    uint32_t i = 0;
    while (true) {
        i++; // fake data update
        message_t *message = mpool.alloc();
        message->voltage = (i * 0.1) * 33; 
        message->current = (i * 0.1) * 11;
        message->counter = i;
        queue.put(message);
        Thread::wait(1000);
    }
}

int main (void) {
    Thread thread(send_thread);
    
    while (true) {
        osEvent evt = queue.get();
        if (evt.status == osEventMessage) {
            message_t *message = (message_t*)evt.value.p;
            printf("\nVoltage: %.2f V\n\r"   , message->voltage);
            printf("Current: %.2f A\n\r"     , message->current);
            printf("Number of cycles: %u\n\r", message->counter);
            
            mpool.free(message);
        }
    }
}

メール

メールは、キューにメッセージ(ポインタだけでなく)をアロケートするためのメモリー・プールの機能が追加されたような動作をする。

http://mbed.org/media/uploads/emilmont/mailqueue.png

#include "mbed.h"
#include "rtos.h"

/* Mail */
typedef struct {
  float    voltage; /* AD result of measured voltage */
  float    current; /* AD result of measured current */
  uint32_t counter; /* A counter value               */
} mail_t;

Mail<mail_t, 16> mail_box;

void send_thread (void const *argument) {
    uint32_t i = 0;
    while (true) {
        i++; // fake data update
        mail_t *mail = mail_box.alloc();
        mail->voltage = (i * 0.1) * 33; 
        mail->current = (i * 0.1) * 11;
        mail->counter = i;
        mail_box.put(mail);
        Thread::wait(1000);
    }
}

int main (void) {
    Thread thread(send_thread);
    
    while (true) {
        osEvent evt = mail_box.get();
        if (evt.status == osEventMail) {
            mail_t *mail = (mail_t*)evt.value.p;
            printf("\nVoltage: %.2f V\n\r"   , mail->voltage);
            printf("Current: %.2f A\n\r"     , mail->current);
            printf("Number of cycles: %u\n\r", mail->counter);
            
            mail_box.free(mail);
        }
    }
}

RTOSタイマー

RtosTimerクラスでシステム内のタイマー関数の生成、制御ができる。タイマー関数は、単発あるいは周期的な、ある一定期間が終了した時点で呼ばれる。タイマーは始動、再始動または停止させることができる。タイマーはosTimerThread内で取り扱われる。コールバック関数はこのスレッドの管理下で実行され、CMSIS-RTOS API呼出しを使用することができる。

http://mbed.org/media/uploads/emilmont/rtostimer.png

#include "mbed.h"
#include "rtos.h"

DigitalOut LEDs[4] = {
    DigitalOut(LED1), DigitalOut(LED2), DigitalOut(LED3), DigitalOut(LED4)
};

void blink(void const *n) {
    LEDs[(int)n] = !LEDs[(int)n];
}

int main(void) {
    RtosTimer led_1_timer(blink, osTimerPeriodic, (void *)0);
    RtosTimer led_2_timer(blink, osTimerPeriodic, (void *)1);
    RtosTimer led_3_timer(blink, osTimerPeriodic, (void *)2);
    RtosTimer led_4_timer(blink, osTimerPeriodic, (void *)3);
    
    led_1_timer.start(2000);
    led_2_timer.start(1000);
    led_3_timer.start(500);
    led_4_timer.start(250);
    
    Thread::wait(osWaitForever);
}

割込みサービス・ルーチン(ISR)

同様のRTOS APIをISR内で使用することができる。二つの注意点:

  • 相互排他(Mutex)は使用できない。
  • ISR内での待ちは許されない:メソッド・パラメータ内のすべてのタイムアウト値は0に設定されなければならない(待ち無し)

#include "mbed.h"
#include "rtos.h"

Queue<uint32_t, 5> queue;

DigitalOut myled(LED1);

void queue_isr() {
    queue.put((uint32_t*)2);
    myled = !myled;
}

void queue_thread(void const *argument) {
    while (true) {
        queue.put((uint32_t*)1);
        Thread::wait(1000);
    }
}

int main (void) {
    Thread thread(queue_thread);
    
    Ticker ticker;
    ticker.attach(queue_isr, 1.0);
    
    while (true) {
        osEvent evt = queue.get();
        if (evt.status != osEventMessage) {
            printf("queue->get() returned %02x status\n\r", evt.status);
        } else {
            printf("queue->get() returned %d\n\r", evt.value.v);
        }
    }
}

タイムアウトのデフォルト値 mbed rtos APIでは、生産者メソッドに対してデフォルト値としてタイムアウト値0(待ち無し)、また消費者メソッドのonWaitForever(無限待ち)が選択されている。

典型的な生産者のシナリオは、あるイベントを知らせるための割込みを発生させる周辺機器かも知れない:対応する割込みサービス・ルーチン内では待つことはできない(待ちはシステム全体をデッドロックさせてしまうかも知れない)。一方で、消費者はイベントを待つバックグランドのスレッドかも知れない。この事例では、期待されるデフォルトの挙動はこのイベントが生成されるまでCPUサイクルを消費しない、つまりosWaitForeverである。

ISR内の待ち無し ISR内のrtosオブジェクト・メソッドの呼出し時にはすべてのタイムアウト・パラメータは0(待ち無し)に設定されていること: ISR内での待ちは許されない。

スタック構造 スタック構造はCMSIS-RTOSの実装に深く依存している。

最初のレファレンス実装として、我々はRTXを使用している。RTX実装の制限の一つは、最大スレッド数とメモリー・プール用のスタックのサイズを静的に定義しなければならない点である。

この最初のRTXの構成はmbedユーザからのフィードバックによっては変更する可能性が高い。

  •                    mbed NXP LPC11U24mbed  NXP LPC1768
  • 最大ユーザスレッド+(タイマー)     3 + (1)          7 + (1)
  • デフォルトスタックサイズ         0.5 Kb           1 Kb

RAMの使用 rtosライブラリーをインポートすると、そのmbedの利用可能RAMの25%がスタックのために消費される。

構成 必要に応じて構成をエディットできるように、現在のRTX実装のソースを間もなくリリースする予定である。 さらに、将来、公式mbed RTOSのために、最大スレッド数を指定するためにコンフィグ・ファイルをエディティング する必要のない実装を支持する(Additionally, in the future, for the mbed RTOS, we will favour implementations that do not require the editing of a configuration file to specify the maximum number of threads. 現状のスタック構成は一時的、変更される可能性のある実装詳細事項である。

状態とエラーコード

CMSIS-RTOS関数の返却値は以下の通り:

  • osOK: 関数実行完了: 正常
  • osEventSignal: 関数実行完了; シグナル・イベント発生.
  • osEventMessage: 関数実行完了; メッセージ・イベント発生.
  • osEventMail: 関数実行完了; メール・イベント発生.
  • osEventTimeout: 関数実行完了; タイムアウト発生.
  • osErrorParameter: パラメータ・エラー: 必要パラメータが無い、または間違ったオブジェクトが指定された.
  • osErrorResource: 資源不足: 指定された資源は利用不能
  • osErrorTimeoutResource: 指定された資源は指定時間内に利用不能: 指定された資源は指定時間内に利用不能
  • osErrorISR: ISR内で利用できない: 指定された関数は割込みサービス・ルーチン内で呼出し不可
  • osErrorISRRecursive: 同一オブジェクトのISRからの複数回関数呼出し
  • osErrorPriority: システムが優先順位を決定できない、または不正な優先順位
  • osErrorNoMemory: システムのメモリー枯渇:実行のためのメモリーのアロケートまたは予約ができなかった
  • osErrorValue: パラメータ値は許された範囲外
  • osErrorOS:不特定RTOSエラー:実行時エラーであるが、その他のエラーメッセージに該当しない

osEvent

osEvent構造体はQueueとMailオブジェクトによって返却される。この構造体はエラーコードと実データへのポインタの両方を含む。

  * status       [osStatus    ]: status code (event or error information)

union: either {
  * value
    * v          [uint32_t    ]: message as 32-bit value 
    * p          [void *      ]: message or mail as void pointer 
} or {
  * def
    * mail_id    [osMailQId   ]: mail id obtained by osMailCreate 
    * message_id [osMessageQId]: message id obtained by osMessageCreate
}


Please log in to post comments.