SSブログ

macOSからPi Picoを使う - その21 [Pi Pico]

前回まででvendor specific classを実装する大枠はわかったことにする。今回はpico-sdkでTinyUSBを含めるにはどうすればいいかを見る...

15.5  トップレベルの動作

お膳立てはだいたいわかったとして、どうやって実行するかというとこれはexamplesを見ればどれもほとんど同じになっている。

例えば典型的なmidi_textをみると
void led_blinking_task(void);
void midi_task(void);

/*------------- MAIN -------------*/
int main(void)
{
  board_init();

  tusb_init();

  while (1)
  {
    tud_task(); // tinyusb device task
    led_blinking_task();
    midi_task();
  }
  return 0;
}
となっている。examplesの他のもだいたい同じ構造をしている。board_init()はhw/bspのボードごとのファイルの中に定義されている。makeするときにBOARD変数を与えることで読み込まれる。

tusb_init()はsrc/tusb.hに定義されていて、その中ではclassごとの初期化関数を呼んでいる。

whileループの中で呼んでいるtud_task()はやはりsrc/tusb.hに定義されていて、ようするにイベントループの本体である。それと同列にあるled_blinking_task()は一定周期でLEDをブリンクさせるのがmain.cの最後に書いてある。examplesにあるたいていのmain.cに同じLEDブリンクの関数が書いてある。またmidi_task()もこのexampleで固有の作業をしているだけである。

コールバックなどはtud_task()の中から呼ばれるようで、割り込みルーチンからではなさそうである。割り込みは、USBハードウェアとのやりとりに専用のFIFOを持っていて、その読み書きに集中しているようである。割り込みからではないのでコールバックの中での作業には特に制限はなさそうである。ただし当然、二つのコアの両方からTniyUSBの関数を呼んではいけない。TinyUSBに関係する作業はcore0が集中して行うべきである。

Pi Pico sdkも本体はwhileループとして書くのでそれと同じレベルでこれらの関数を呼べばいいだけのようである。もちろん、whileループをの中で長時間かかるような大きな作業をするとUSBがストールすることになるので、注意である。それはPi Picoなんかのベアメタルを使う場合には常識だろう。

具体的にはUSB1.1ではframe syncは1m秒ごとに送られるのでこれに比べて長い処理は避けるべきである。一番USBのハードウェアに近い部分は割り込みで書かれていてFIFOを経由して割り込み外へデータを渡している。FIFOが溢れるとストールする。

ふだんunix/macOSをプログラムしていると組み込み用のコードってすっごく心細く感じる。でもまあ、ずっと昔、Z80の上のCP/Mの上で動くCコンパイラを手に入れたとき、Cでさえすごく大きな仕様に思えた。たとえば浮動小数点演算ライブラリが巨大でメモリを圧迫して、単なる四則演算でもちょっと式が長くなるとスタックを食い尽くすということがあった。それに比べればARM M0+でさえ超高性能で、びくびくするのがそもそも小心すぎる、とは言える。

Pi PicoのRP2040は133MHzのクロックで内部バスやSRAMもクロックに同期して1サイクルで完了するようなので、外に出ない限りは1命令/1クロックに近い性能が出るだろう。そうするとUSB1.1の1frameの間に10万命令ぐらい実行できる。USBのサイクルに最適化されていない作業の目安としては、1ループにこの一桁下の量ぐらいがいいところと考えるべきだろう。

15.6  USB経由のBOOTSEL boot

これまではpico-sdkのstdio_usbを使ってPi PicoをBOOTSELモードリセットできた。TinyUSBを書くとstdio_usbと共存はできない。従って何らかの別の手段を用意しておかないといちいちBOOTSELボタンを押しながらUSBケーブルを抜き差ししないといけなくなる。

探してみるとpico/bootromモジュールに
static void reset_usb_boot(uint32_t usb_activity_gpio_pin_mask,
                           uint32_t disable_interface_mask);
という関数がある。これを呼ぶとBOOTSELモード、すなわちUSBからMSCデバイスとして見えてマウントすることができるようなリブートをすることができるようである。

引数のusb_activity_gpio_pin_maskは呼ばれたときに指定したGPIOピンをアクティブにすることができる。ブートするときにLEDを光らせる、とかいうのに使うようである。が、なんでstaticなんだろう。staticだけどmain関数から呼べる。ちょっと試してみないと本当のところはよくわからない。

もうひとつの引数disable_interface_maskは3通りの値をとって

0 コールドブート
1 USBのMSCをオフにする
2 PICOBOOT

PICOBOOTというのはBOOTSELモードで起動したときにUSB経由でPi Picoをリセットしたりflashやメモリの内容を読み書きするためのもので、boot-ROMに書かれている。従ってsdkにはヘッダだけがあって実装ファイルはない。実際に何に使えるのか、というとよくわからない。

この関数は実際に動作させてみて要確認である。

nice!(0)  コメント(0) 

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。