STM32のUSB仮想UARTモニター遂に完成
今年の連休は殆ど外に出かけず、いわゆるCQ-STARMと呼ばれるDWM2008年5月号の付録基板、STM32F103をGNUベースで開発するプロジェクトに没頭していた。こんなに夢中になったのも久しぶりである。やっとのことでUSBの仮想COMポートが実用レベルの域に達した。PCから簡易モニターが開けるのでUARTを通じて開発を進めることが出来る。いやあ今度も大冒険だった。
仮想UARTからの送信データが抜ける(5/4/09)
STM32のUSB仮想UARTは、とりあえず送受信は出来るようになった。しかし、この雑誌のファーム(蛙がピョン)が使っているUSBへの送信関数は不安定極まりない。特に、連続して送ると前のメッセージを無視して最後の送信メッセージしかPCに届かない。
受信は出来ているようなので1文字単位のエコーバックは出来るが、ARM側からの連続した送信は全く駄目である。それに受信に関しては不審なことがある。この前、書いたようにデバッグ用に用意されている2文字のコマンドがCD-ROMに入っていたオリジナルのファームに戻してみても、全く機能していないことである。どうもおかしい。
ソースコードを調べて行ってその原因がわかった。メインループの中で受信バッファーにデータがあるかどうかをバッファーのデータサイズ変数(count_out)で聞いている。受信バッファーは、USBの受信エンドポイントから直接移しているだけでキューイングなどはやっていない。しかもこの変数、count_outは、このループの最後でクリアされている。キーボードの入力はどんなに早くても1/10秒単位である。これでは2文字のデータがUSBのエンドポイントのバッファーに揃うことは絶対にありえない。
送信がおかしいのは、恐らく送信バッファーで、まだ送っていない前のデータを新しいデータで塗りつぶしているからであろう。USBはホスト(PC側)が完全な主導権を握っており、UARTのときは、ディバイス(ARM)側から送信は、ホストのVCPドライバーからのポーリングで送信するはずで確か、最小でも1ms間隔だったと記憶している。バッファーから正しくデータが送られてバッファーが空にならない限り、次のデータは送れないはずだが、このコードはそういう制御は何もしていない。
受信の方の不備といい、送信のところといい、どうもこのソースコードの作者は、USBが非同期で沢山のバッファーを通して動いていることを理解しているか疑わしい。困ったことになった。お手本が頼りにならない。加速度センサーのADコンバータが既に動いているし、これをベースにアプリが広げられるという最初の期待はもろくも裏切られた。
確かにキーボードから「*」の入力で連続した加速度センサーの数値を表示することには成功している。しかし、これは加速度センサーの表示タイミングがUSBの送信間隔よりはるかに遅く、せいぜい10msに一回程度だから、たまたまボロが出なかっただけである。これはオリジナルのSTマイクロのVCPソースからじっくり調べる必要がある。
sprintfは動いたぞ(5/5/09)
こどもの日の今日は雨。晴れのときは何となく落ち着かないが、雨のときは心置きなくARMマイコンと過ごすことが出来る。このところARMの話題ばかりで、がた老AVR研究所と名前をつけた手前、AVRのことを期待して来られる読者には申し訳ないが、アクセスログを見ていると殆どの読者はキーワード検索で来られるようだ。余り気にすることはないのかもしれない。
それはともかく、何とか動かすためにこのあいだコメントアウトしたsprintfである。書式付の出力関数は、フラッシュが大きくなっているので気楽に使えるし、今後のデバッグの生産性のためにも生き返らせたい。仮想UARTの不具合究明を少しお休みし、こちらの方をやってみることにした。
まず、この前のウェブの情報には、問題の_sbrkのミニマムコードが掲載されている。真面目にコードを読んでいく。何だ、そんなに難しいことはやっていない。ヒープアドレスのエントリーを要求に応じて上げているだけだ。その間が確保したメモリというわけだ。メモリの解放は考える必要はない。スタックと被るとエラーを出すようになっているが、これも今のところいらないだろう。
_endという外部変数が気になったが、リンカーが設定するというコメントを信じてソースを追加しコンパイルしてみた。おおー、NO ERRORである。調子に乗って、オリジナルソースのsprintfのコメントの部分をはずしビルドする。これも問題ない。いよいよテストだ。「*」を入れる。やった。やりました。加速度センサーの数字が見事に出力された。オリジナルと全く同じ機能が、GNUベースで実現した。
STM32で、GNUの標準ライブラリが使えないというのは、このあたりに原因があるのかも知れない。私の場合、リンクエラーが出たので顕在化したが、どこかのライブラリの中でもし何もしない_sbrkのスタブルーチンが用意されてしまっていると動かないことになる。
仮想UARTは本家のSTマイクロのサイトでも紛糾(5/6/09)
お手本にしようとした雑誌の内蔵ファーム(蛙がピョン)の応用をあきらめて、もとのSTマイクロのVirtualComPort(以後VCP)のデモプログラムで仮想UARTの実装を始めた。内蔵ファームの仮想UARTの部分は、このVCPのソースを殆どそっくり利用していたので、理解が早い。送信バッファーの取り扱いがわかった。最初から書き込まず、足しこんでいる。そうか、これなら内蔵ファームのようにデータは失われない。
その内蔵ファームについて前回の記事では、その後の情報収集で少し誤解があったようなので訂正しておきたい。2文字コマンドは絶対動かないと書いたのだが、これはGainerプロジェクトの仕様なのだそうだ。キーボードでなく、別のマシンからの入力を想定しているのでバグではない。キーボードから入れるときは、端末プログラムで入力コマンドをカット&ペーストすると動くと言う。
実際に試してみて動くことを確認した。情報源のサイトでは「変な仕様だなあ」などのコメントがついていたが、同誌の次号6月号3章の記事を読み直して少し謎がとけた。この記事の中で、コマンドがすべてASCIIなのに端末側にわざわざフリーウエアのAcknowrich(アクノリッチ)を要求している理由が全くわからなかったのだが、こういうことだったのである。それなら最初から、そう書いてくれれば混乱することはなかったし、アクノリッチを使うこともなかったように思うのだが。
それはさておき、STマイクロのVCPデモソースの解析の方である。ここではmain.cとhwconfig.cで、USBの受信バッファーのデータサイズ(count_out)分をUARTへ送り、UARTからのデータは、UARTの受信割込みの度に、USBの送信バッファーに書き足している。USBの送信バッファーはエンドポイントの割込みを処理するusb_endp.cで定期的にハードのパケットレジスターに送信バッファーサイズ(count_in)だけ書き込まれる。いずれも処理のあとにバッファーサイズの値は0にリセットされる。
問題なさそうである。これを元に鼻歌まじりで、1文字送受信関数、USB_putc、USB_getcを作り、USB_putcを使って、文字列用のUSB_putlineまで用意して、仮想UART超簡易モニターを実装した。STM32の開発ベースになるものである。とりあえずは、入力をエコーバックし、リターンキーでこれまでのデータを送出、0、1、でLEDのOFF/ONという仕様だ。ファームを書き換え、意気揚々とテストに入る。
と、これが言うことをきかないのである。受信は問題ないが、ホストへの送信が前と同じようにうまくいかない。一文字なら良いが(エコーバックは完全だ)、一気に送るとデータ落ちが激しい。それにおかしなことにデバッグ用のLEDをONするステートメントをメインループからはずすとハングする。???である。
おかしいな。送信バッファーは64バイトもある。いくら実際のUARTよりはるかに早いタイミングでプログラムからバッファーを書き換えても、USBの割込みが入ってハードのエンドポイントレジスターに書き込むときは、正しいバッファーの内容と、そのサイズを示しているはずだ。現象はあきらかに書き足す処理の時に割込みが入って、サイズとバッファーの内容との不整合が起きているとしか考えられない。バッファー転送とサイズ変更の間で割り込まれないようにしておく必要がある。
しかし、STM32で割込み禁止をかける方法がわからない。8ビットのAVRと違ってSTM32 の割込み環境は目茶目茶複雑だ。下手にかけると他がおかしくなる。それにLEDのステートメントがないとハングアップするというのも奇怪な話である。
USBの知識も生半可なので少し勉強し直し、色々いじってみたが改善しない。うーむ、よくわからない。思い余って、このあいだ試して味をしめた生のステートメントをGoogleに投げる方法をやってみた。キーワードは、ユーザーバッファーから実際のエンドポイントにデータを移すSTマイクロ提供のライブラリ関数、UserToPMABufferCopy(...)である。
すると、また当たったのである。こんどもSTマイクロのフォーラムだ。それも私と全く同じ仮想UARTのデータ抜けで困っている人たちの議論である。3ページ分ある。英語の斜め読みなので、完全に理解したとは言い難いが、UARTが低速のときはUSBがパケットを送るタイミングとぶつからないが、高速になると取りこぼすバグの対処の議論である。
色々な改善案の出し合いがあって、最終的には、一方がsystickを使った新しいコードを提案してスレッドは終わっている。しかし、このコードはのちほど本人が否定して、問題は解決されていない。STマイクロの対応(発言)はない。去年(2008年4月)の話なのでソースはV1.0ベースだ。これを受けて次のバージョン(V2)でどうなったか、USBライブラリをダウンロードしてみたが、何も変わっていなかった。ただこれはこの関数UserToPMABufferCopyだけの問題ではない。V3のVCPのデモソースは見つけられなかった。
さて、どうするか、である。割込みを止めようとウェブを探し回ったら、何とユーザーモードでは割込みをペンディングできないことがわかった。うーむ、やっぱり32ビットプロセッサーは難しい。LEDを動かさないと何も動かずハングするとか、count_outなどの外部変数を監視していても変化しないとか、まだわからないことだらけだ。
NOP_PROCESS()ですべてが解決した(5/8/09)
AVRだと気楽に割込みを止めてアトミックオペレーションに備えることが出来るが、ARMあたりはRTOS(リアルタイムOS)を意識しているので、そう簡単にユーザーレベルで割込みを止められたら確かにおかしくなることはわかる。ただ、今の現象はあきらかに、USBのエンドポイントの送出割込みがバッファー転送中に起きていると思われる現象だ。
向こうが待ってくれないなら、こちらで待つ(バッファーが0になるのを待つ)ようにしようとしたが、これも出来ない。モニターに使うUARTだから早さは求めない。バッファーサイズが0になるのを待って、1バイトづつ送っても問題ないのだが、whileループで、これを調べても変化しない。ここでループしたままになる。ユーザープログラムからこのバッファーサイズ(正確には変化)が見えないのである。
別のお手本を探すしかないかと、あきらめ気分になっていたとき、STマイクロのソースを見ていてふと目に留まったものがある。何かのフラグを調べているプロセスでwhileのあとの処理クローズにNOP_Process()という関数が入っている。これは何だろう。コンパイラーの最適オプションでおかしくなるのをさけるためなら、インラインアセンブラのasm("nop")で良いのに何故わざわざ別の関数を呼んでいるのだろう。
頭の中に何か灯りがともった。何かにおう。これが長年この世界にいた勘かもしれない。もしかしたらもしかするかもしれない。エンドポイントにデータを送る関数USB_Putc()でbuffer_outが0になるまで、このNOP_Process()で待ってみることにしてみた。バッファーを送った直後なら、次のデータ転送の時に割り込まれる可能性は殆どない。ついでにはずすとハングするLEDのステートメントもこの関数にしてみる。だめもとである。あまり期待もせずテストしてみた。
何と、何と、これが見事に機能したのである。長いメッセージも全くデータ落ちなく出力される。LEDプロセスがなくても問題なく動く。なぜ動くのか理由はわからない。ただ山勘でやってみたのがあたった。一回の転送タイミングで1バイトしか送れない(1msのタイミングで8kbps)が、今はこれで十分である。調整していけばもう少し早くなるだろう。
いやあ、嬉しい。世の中がばら色に見える。寝ている女房を起こして喜びを分かち合いたい気分である。STマイクロのフォーラムであれほど紛糾していたのだから、殆どあきらめていた。それが低速とは言え、モニタープログラムが問題なく動いた。鼻が高い。リターンキーで、貯めこんだメッセージを1字も抜けずに表示するし、LEDも0、1のキーで点滅する。満足、満足である。
今のところ高速化は必要ないが、バッファーをリングバッファーにするか、バッファーを書き換えるのときにUSBの割込みをペンディングする方法が見つかれば、もっと高速に送れる。まあ、これは先の話である。それよりも次はJTAG環境を整備したい。Dfu経由だと一回の書き換えに25クリックもかかる。
| 固定リンク
「ARM」カテゴリの記事
- 心電計プロジェクト:スケールが出ると心電計らしくなる(2015.01.08)
- 心電計プロジェクト:TFT液晶に念願の心電波形が出た(2014.12.18)
- 心電計プロジェクト:STM32F103の心電波形表示で悪戦苦闘(2014.12.03)
- 心電計プロジェクト:CooCoxでARMの表示系ソフトを開発する(2014.10.16)
- 心電計プロジェクト:表示部のARM基板の開発環境を一新する(2014.09.19)
コメント
コメントありがとうございました。
いやあ感激です。役に立ったと言われるのも嬉しいですが、楽しんで読んでいただけたと聞くと今までの苦労が吹き飛びます。友人からは「長すぎる」と言われていますが、この過程が面白いんですよね。
投稿: がた老 | 2009年5月10日 (日) 11時54分
はじめまして、こんにちは!
Cortex-M3のポテンシャルに着目しているところへSTM32(CQ-STARM)の記事だったので、思わず数日分を読ませていただきました。試行錯誤された甲斐がありましたね!何だかうれしく感じてしまいました。
投稿: genie | 2009年5月 9日 (土) 23時56分