うさちゃんと一緒(2) Cooperative Multitaskingで吉例Lチカ

Joseph Halfmoon

懐かしいZ80的命令を持つ、うさちゃん印のRabbit4000を使用中です。前回「売り」のEthernetから始めてしまい、新マイコン入手時の吉例を飛ばしてしまいました。今回は、Rabbit用の言語処理系 Dynamic Cの特長の一つ、Cooperative Multitaskingを使って「Lチカ」してみます。

メインボード上に搭載されているLEDはEthernet用なのですが、メインボードを「刺してある」プロトタイピングボード上には、2個の赤色LEDが搭載されています。回路図を見るとどちらもPort Bという名のパラレルポートに接続されていました。DS2、DS3と呼ばれています。回路図を見ると以下のビットで制御できるようです。

  • DS2、Port Bのbit 2
  • DS3、Port Bのbit 3

今回は上記の2つを光らせてみたいと思います。ただ「素のループ」でLチカしたのでは「うさちゃん印の特長」が伝わりません。 Dynamic Cの特長の一つ、Cooperative Multitaskingを使ってみたいと思います。

なお、さらに凄いことに「うさちゃん印」はプリエンプティブなRTOSのRabbit移植版も含んでました。今回は外部ライブラリを参照する必要のあるRTOSではなく、「言語仕様に含まれている」Cooperativeな方を使ってみます。

Cooperative Multitasking

呼び方はいろいろですが、Cooperativeなマルチタスク(あるいはコルーチン)をサポートしている言語処理系や、OSは多いようです。軽くて、実装しやすく、「それほどシビア」な制御が必要でないのならば、複数タスクを並行に処理していくにはこれで十分なことが多いからではないかと思います。お世話になっている例でいうと、MicroPython の uasyncio (MicroでないフルのPythonでもasyncio使えます)が該当すると思います。その件を使っている投稿が以下に。

MicroPython的午睡(46) uasyncioでMQTT送受信、ATOMLite

またRTOSも似た機能をサポートしています。Arm Mbed OS6では、EventQueueという名でイベントドリブンな「プリエンプティブでない」マルチタスクをサポートしています。投稿は以下に。

モダンOSのお砂場(34) Mbed OS6、EventQueueを使ってみる

Mbed OSがサポートしているものを、FreeRTOSが出来ないということもありません。以下の投稿は「プリエンプティブ」なマルチタスクのタイトルですが、比較相手の「ブロッキングAPI」を使ったマルチタスクは、EventQueueに近い機能じゃないかと思います。

モダンOSのお砂場(29) FreeRTOS、プリエンプティブなスケジューリング

Rabbit上での実装が独特なのは、Dynamic Cという「C言語処理系」の独自拡張としてCooperativeなマルチタスクが実装されている、ということです。多くの場合、C言語の言語仕様ではなく、外部のライブラリやOS機能で実装すると思うのですが、「うさちゃん印」はCを拡張するのに躊躇しないところが清々しいというか、独自の世界に浸ってます。

他のCコンパイラではお目にかからない筈のキーワード costate を使って記述した「Lチカ」が以下に。間の抜けたコードですが、分かり易さ優先ということで。

#class auto
#use RCM40xx.LIB

#define DS2_LED		(2)
#define DS3_LED		(3)

main()
{
    int LED2, LED3;
    brdInit();
    BitWrPortI(PBDR, &PBDRShadow, 1, DS2_LED); // 1=OFF
    BitWrPortI(PBDR, &PBDRShadow, 1, DS3_LED); // 1=OFF
    LED2=1;
    LED3=1;
    
    while (1) {
        costate {
            waitfor(DelayMs(1000));
            if (LED2==1) {
                BitWrPortI(PBDR, &PBDRShadow, 0, DS2_LED);
                LED2=0;
            } else {
                BitWrPortI(PBDR, &PBDRShadow, 1, DS2_LED);
                LED2=1;
            }
        }
        costate {
            waitfor(DelayMs(500));
            if (LED3==1) {
                BitWrPortI(PBDR, &PBDRShadow, 0, DS3_LED);
                LED3=0;
            } else {
                BitWrPortI(PBDR, &PBDRShadow, 1, DS3_LED);
                LED3=1;
            }
        }
    }
}

他にも独自の世界はいろいろ広がっており、#class とか、#use とか見たことのないプリプロセッサ命令?も存在します。また機会を見つけて説明したいと思います。

ビルドと実行

ビルドと実行は簡単です。統合開発環境でもある Dynamic C で上記のソースを記述してコンパイルするだけです。今回もFlashではなくRAM上にオブジェクトを展開してみました。メニューから一発でダウンロードOK。Runボタンを押せば即座に走り始めます。なお、デフォルトで「逆アセンブラ画面」も開きます。勿論Cのソースレベルでデバッグできるのですが、Dynamic C、アセンブラを見せたがる傾向が強いです。私は好きですが。以下を見ると、BitWrPortIというPort操作の関数に見えたものは、インライン展開されてアセンブラに落ちていることが分かります。Z80に似てますが拡張されてますな。

Blink_DC

上記を走らせると、DS2とDS3が点滅します。そしてDS3の点滅周期はDS2の半分に見えます。

うさちゃん、簡単にマルチタスク「的」なコードが書けるのね。

うさちゃんと一緒(1) Rabbit4000にPingしてお返事をしてもらう へ戻る

うさちゃんと一緒(3) Rabbitとの通信をラズパイWiresharkで観察 へ進む