3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

EventQueueを使ってみたよ (3)

Last updated at Posted at 2019-12-29

前回の記事では、EventQueueについてThe EventQueue API Tutorialでイベントループを作るところまで読んでみました。今回は、イベントループを使います。

題材について

この記事で題材として取り上げたのは、The EventQueue APIというTutorialです。

イベントループを使う(Using the event loop)

Once you start the event loop, it can post events.

イベントループが走り始めたら、イベントを投稿(post)できます。

Let's consider an example of a program that attaches two interrupt handlers for an InterruptIn object, using the InterruptIn rise and fall functions.

InterruptInオブジェクトのrise関数とfall関数を使って、ひとつのInterruptInオブジェクトにふたつの割り込みハンドラを付けたプログラムの例を考えてみます。

The rise handler will run in interrupt context, and the fall handler will run in user context (more specifically, in the context of the event loop's thread).

riseハンドラは、割り込みcontextで実行され、fallハンドラはユーザcontext(もっと具体的に言うとイベントループスレッドのcontext)で実行されます。

The full code for the example can be found below:

このプログラム例の全コードは、以下の通りです。

#include "mbed.h"

DigitalOut led1(LED1);
InterruptIn sw(USER_BUTTON, PullUp);
EventQueue queue(32 * EVENTS_EVENT_SIZE);
Thread thread;

void rise_handler(void) {
    printf("rise_handler in context %p\r\n", ThisThread::get_id());
    // Toggle LED
    led1 = !led1;
}

void fall_handler(void) {
    printf("fall_handler in context %p\r\n", ThisThread::get_id());
    // Toggle LED
    led1 = !led1;
}

int main() {
    // Start the event queue
    thread.start(callback(&queue, &EventQueue::dispatch_forever));
    printf("Starting in context %p\r\n", ThisThread::get_id());
    // The 'rise' handler will execute in IRQ context
    sw.rise(rise_handler);
    // The 'fall' handler will execute in the context of thread 't'
    sw.fall(queue.event(fall_handler));
}

いよいよ、本格的なコードが出てきましたが、いくつか問題点がありました。

このプログラム例では、押しボタンで操作を行いますが、PSoC 6の評価ボードではSW2端子が定義されていません。代わりにUSER_BUTTONを使いました。また、この端子にはプルアップ抵抗が必要なので、swインスタンス生成時の引数にPullUpを加えました。

このプログラム例では、Thread::gettid()という関数で現在実行中のスレッドIDを取り出しています。ところが、この関数はdeprecated扱いになっています。ここでは、代わりにThisThread::get_id()を使用します。

The above code executes two handler functions (rise_handler and fall_handler) in two different contexts:

このコードでは、二つのハンドラ関数、rise_handlerfall_handlerを二つの異なるcontextで実行しています。

  1. In interrupt context when a rising edge is detected on SW2 (rise_handler).
  1. SW2で立ち上がりエッジが検出されたら割り込みcontextでrise_handlerが実行されます。
  1. In the context of the event loop's thread function when a falling edge is detected on SW2 (fall_handler). queue.event() is called with fall_handler as an argument to specify that fall_handler will run in user context instead of interrupt context.
  1. SW2で立ち下がりエッジが検出されたらイベントループのスレッドのcontextでfall_handlerが実行されます。fall_handlerを引数に持ったqueue.event()が呼び出され、fall_handlerが割り込みcontextの代わりにユーザcontextで実行されます。

これで、押しボタンの「押す」と「放す」の動作に割り込みが関連付けられ、それぞれ別のcontextで実行されるようになりました。
queue.event()は、引数に指定したcallbackをキューに入れる動作をするcallbackを返します。ややこしいな。

This is the output of the above program on an FRDM-K64F board. We reset the board and pressed the SW2 button twice:

このプログラムをFRDM-K64Fボードで実行した結果がこの出力です。ボードをリセットした後、SW2ボタンを二回押しました。

Starting in context 20001fe0
fall_handler in context 20000b1c
rise_handler in context 00000000
fall_handler in context 20000b1c
rise_handler in context 00000000

The program starts in the context of the thread that runs the main function (20001fe0).

main関数が走るスレッドのcontext(20001fe0)でプログラムが走り始めます。

When the user presses SW2, fall_handler is automatically queued in the event queue, and it runs later in the context of thread t (20000b1c).

ユーザがSW2を押したら、fall_handlerが自動的にイベントキューに入り、後でスレッドtのcontext(2000b1c)で実行されます。

When the user releases the button, rise_handler is executed immediately, and it displays 00000000, indicating that the code ran in interrupt context.

ユーザがボタンを放したら、rise_handlerが直ちに実行されます。このとき00000000が表示されていますが、これは割り込みcontextでコードが実行されたことを示します。

という事なんですが、PSoC 6で実行してみたところ、こんな結果にはなりませんでした。

Starting in context 080047ac
fall_handler in context 080051c0


++ MbedOS Error Info ++
Error Status: 0x80010133 Code: 307 Module: 1
Error Message: Mutex: 0x8003C6C, Not allowed in ISR context
Location: 0x100127C5
Error Value: 0x8003C6C
Current Thread: rtx_idle Id: 0x80047F0 Entry: 0x10010359 StackSize: 0x300 StackMem: 0x8004878 SP: 0x80476F4
For more info, visit: https://mbed.com/s/error?error=0x80010133&tgt=CY8CKIT_062_WIFI_BT
-- MbedOS Error Info --

ボタンを押した後のfall_handlerは走ったようなのですが、ボタンを放した後、エラーで停止してしまい、LED1が ーーーー・・・・ というパターンで点滅を始めました。メッセージを読むと、ISR contextでMutexを使用したのが気に入らなかったようです。

実は、このプログラムに問題があることは、この後の部分で記述されているのですが、おそらくこの文書を書いた当時は、プログラムがエラーで停止するような動きにはならなかったのでしょう。

どういう問題があるのかについては、次回に続きます。

追記 (2020-02-08)

このISRでMutexを呼び出したら止まってしまうという問題をgithubのIssueに提起したところ、「printfを使うからまずいんでしょ。UnbufferedSerial::writeを使えばいいんじゃない?」という解決方法が提示されました。

// It's safe to use UnbufferedSerial in ISR context
UnbufferedSerial console(USBTX, USBRX);

void rise_handler(void) {
    char buf[64];
    sprintf(buf, "rise_handler in context %p\n", ThisThread::get_id());
    console.write(buf, strlen(buf));
    // Toggle LED
    led1 = !led1;
}

が、UnbufferedSerialって、どこにあるのさ?

追記 (2020-02-09)

UnbufferedSerial がどこにあるか見つけられないので、代わりになるクラスがないかと探したところ、RawSerialというのを見つけました。

// It's safe to use UnbufferedSerial in ISR context
RawSerial console(USBTX, USBRX);

void rise_handler(void) {
    console.printf("rise_handler in context %p\n", ThisThread::get_id());
    // Toggle LED
    led1 = !led1;
}

コンソールには、以下のように表示されました。

fall_handler in context 08005380
rise_handler in context 080049b0
fall_handler in context 080053rise_handler in context 08005380
80
fall_handler in context rise_handler in context 08005380
08005380
fall_handler in context 08005380
rise_handler in context 080049b0
fall_handler in context 08005380

排他処理が省略されているので、乱れまくりになりました。

関連サイト

Mbed OSのページ
EventQueueのTutorial
Mbed対応Cypress製品のページ

関連記事

EventQueueを使ってみたよ (1)
EventQueueを使ってみたよ (2)
EventQueueを使ってみたよ (3)
EventQueueを使ってみたよ (4)
EventQueueを使ってみたよ (5)
EventQueueを使ってみたよ (6)
EventQueueを使ってみたよ (7)
EventQueueを使ってみたよ (8)
EventQueueを使ってみたよ (9)

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?