モダンOSのお砂場(89) NucleoでArduinoからMbed OS6、printf

Joseph Halfmoon

Arduino APIとの比較を通じてArm純正RTOS、Mbed OS6の入出力API群を練習してます。今回は入出力というより、デバッグ用のAPIです。printfね。USBを通じてホストPCの仮想シリアルポートにいろいろ情報を送り出すもの。いつもお世話になっておりますが、改めて再確認っと。

※「モダンOSのお砂場」投稿順Indexはこちら

何はなくても printf さえ使えれば、「printfデバッグ」という古来より綿々と繰り返されてきた「泥臭い」技が今でも有効であります。この頃は多くのマイコンがリモートデバッグをサポートしているので、高価なエミュレータなどが無くてもソースを開いたIDEの上でブレークしたり、変数を読み書きしたりできるのですが、お惚け老人はついつい古(いにしえ)の風習に頼ってしまうのでありますぞ。

ただし、古より「printfには注意せよ」と教わってまいりました。曰く、

    1. ライブラリがデカい、コミコミ、フルセットだと何十Kバイトもある
    2. 処理に時間がかかる。下手すると何十ミリ秒もかかる

1は、たかだか数十Kバイト~数百KバイトのFlashを搭載している機種であれば、本末転倒、驚天動地の容量であります。そこで枝葉を削りに削った「なんちゃってprintf」を使うというのがマイコン業界での保守本流?ホントか?

2は、これ一発で「実時間性」などほぼほぼ吹き飛ぶインパクト。デバッグのための最後の砦、最後っ屁的にprintfしたらそこで実時間的処理は中止だあ。

それでも printf が無くならないのは、それだけの絶大なご利益があるとのことかと思います。

Arduino環境での print/println

さて、Arduino環境では、以前はデバッガ使うことが一般的でなかったこともあり、printfに相当するAPIが幅を利かせてます。

    • print
    • println

の二つです。その差は「出力後改行」するprintlnと、「改行しない」printということで明々白々。printfと異なり、基本、引数にとれるのは一個だけです。その代わり、整数、文字列、浮動小数と何でもござれ、便利です。なお、整数についてのみ、DEC、HEXなどの出力フォーマット指定を第2引数に与えられます。

USBシリアルポートに向いた Serial という名のオブジェクトが事前定義済なのでいつでも呼び出すことができます。初期化は以下の如し。

Serial.begin(9600);
while(!Serial) {}

まあ、2行目は無くても動作するかもですが、一応、Serialオブジェクトが使えるようになるまで「待つ」というのがお作法みたいです。その後は、Serial.print(チョメチョメ)みたいな形でUSBシリアルに出力が可能だと。

ただし、Serialという名のオブジェクトはUSBシリアルに向いているだけでなく、UART(調歩同期式レシーバ・トランスミッタ)を使った入出力に使えるものみたいです。Arduinoの機種によってちょっと凸凹があります。UNO R3とR4は互換性高いですが、それでもわずかに違いがあります。老人が調査したところではこんな感じ。

    • UNO R3でもUNO R4 でも Serial はUSBシリアル用に向いている。USBシリアルを使うだけなら同じように使える。
    • UNO R3の場合、UARTは1チャンネルしか搭載がなく、USBインタフェースチップに結線してあるRXがD0端子、TXがD1端子にも結線されてある。よって、Serialオブジェクトに向けて読み書きすると同時にD0、D1端子にも見えている(D0、D1端子は既に電気的に活きてる状態なのでピンソケットへの接続は慎重に。)
    • UNO R4の場合、複数チャネルのUARTを使うことができる。D0端子、D1端子にでているUART信号はSerial1というオブジェクトを使えば、USBシリアルとは電気的に独立に使用することができる。
    • UNO R4の場合、もう1チャンネルのUARTが使えそうなピン配置になっているが、オブジェクトは定義されていない?自前でレジスタ操作したら動くのか?

コマケー話だな。

Mbed OS6上でのprintf

通常、Mbed OS6上では、特定のピンに対してインスタンスを生成することで入出力が使えるようになるお作法です。しかし「デバッグ」向けのprintf()についてはインスタンスの生成など不要。デフォルト設定のまま(9600bps、 8ビット、ノンパリティ、ストップ1ビット)であれば何も宣言せずに、即 printf()関数をフツーに呼び出せます。

見掛けはフツーのprintfに見えますが、実はかなり「サブセット」です。デフォルト状態だと、%f書式指定子など使用できないものが結構あります(STM32F446REは浮動小数点演算命令を持っているにも関わらず。)floatや64ビット整数使いたい場合には要configです。詳しくは以下で。

Minimal printf and snprintf

printf以外に、putchar、getcharもオブジェクトを生成しなくてもUSBシリアル相手に即使えるようです。

Mbed OS2との互換性

Mbed OS2は、Arm社の「オンライン・コンパイラ」の時代に良く使われていた古いバージョンのOSです。「ベアメタル」環境が基本で、各社固有のハードウエアなどを直接使わずにArmコア・マイコンを簡単に使用できたので人気でした。ただし、現在のKeil Studio Cloudでは「過去のもの」扱いになってます。Mbed OS2のソースはMbed OS6用に「書き換える」必要があります。

このUSBシリアルに対する出力もMbed OS2とOS6の違いがアカラサマになる部分です。OS2の時代は、お作法どおり、

    • SerialクラスのインスタンスをUARTの端子名を指定して作成
    • 作製したインスタンスに対してprintfする

というスタイルでした。USBシリアルも他のUARTと同じ扱いです。なお、USBシリアルについては インスタンス名を pc などとするのが定番になってました。

しかし、単なるSerialというクラスは廃止されてしまいました。その折の記事が以下に。

モダンOSのお砂場(49) Mbed OS6、「Serial」そういえばなくなってたのね

現在のOS6上でUARTを使おうとする場合、以下のどちらかのクラスを利用することになります。

    • BufferedSerial
    • UnbefferedSerial

どちらのクラスも以前のSerialクラスとはメソッドの互換性が無いです。その辺の使い方がメンドクなった分、printfを簡単に使えるようにした、ってこってすかい?

なお、STM32F446REの場合、UARTは2本だけですが、USART(同期/非同期両方できるタイプ)が4本もあります。合計6本あるので使い放題?

今回はおさらいだけ。

モダンOSのお砂場(88) NucleoでArduinoからMbed OS6、アナログ出力 へ戻る

モダンOSのお砂場(90) NucleoでArduinoからMbed OS6、シリアル通信 へ進む