« 2011年5月 | トップページ | 2011年7月 »

2011年6月の3件の記事

2011年6月25日 (土)

FreeRTOSのセマフォーの不具合がやっと解決

 雑誌付録ARM(LPC2388)基板のFreeRTOS7.0.0で、ChaNさんのバッファー付きUARTを動かすことには成功したが、セマフォーを使った受信処理はうまく動かず後味の悪い結果が残った。

 前記事にあるように、UART出力が数十分後にフリーズするという不具合がどうしても解決できない。結局この不具合究明は一時あきらめ、次の課題を、 

・ChaNさんのFatFSをFreeRTOSで動かしてSDカードが読み書きできるようにする。
・UARTをすべてOSのキューを使って入出力し、マルチタスクに対応させる。

ということに決めた。このあたりまで動けば、この基板のOSはロボットなどの多重同時制御のプラットホームとして十分な機能を持つことになり、ネットを使ったカメラ制御などのアプリケーションが簡単に開発できるようになる。

S_p6183986 ただ、FatFSを完全なマルチタスクで動くようにするのは、そう簡単ではない。コードそのものはリエントラントで書かれているが、これをいくつものタスクが同時に動いても良いようにするには、コードだけでなくディスクなどの単一リソースの排他制御を完全に行わないと、デタラメな書き込みでディスクは、あっという間に破壊される。

 しかし、検討に力が入らない。最初はシングルタスクで動くだけで良いと思っていても、頭の中で、何かがひっかかって先に進まない。そう、セマフォー不具合の問題が頭から離れないのである。

 それにしても、このしつこさは何だろう。もし、これが仕事だったら、こんな瑣末なことにこだわっていたら仕事にならない。上司から大目玉を喰らうか、プロジェクトだったら大迷走、中小企業だったら間違いなく倒産である。やっぱり、これは現役時代の反動ではないかと思う。

 人は笑うかもしれないが、不思議なことに、こういう下らないことにこだわってあれこれ悩んでいる時の方が、逆に自分が自由な気分になっていることに気づく。仕事の時はこんな悠長なことを考えている余裕はなかった。一時(いっとき)でも全体から見て効果、実効のある方向を模索することが求められ、始終あせっていた。

 その焦燥感が、現役を退くと消えた。まわりを気にせず解決を求めてさ迷っていても、誰もとがめる人はいない。いろいろ気配り心配りする方がストレスは溜まるのだ。単純に思考しているほうがむしろ幸せなときなのである。それに悩んだことが大きければ大きいほど、それが解決した時には大きなご褒美が待っている。

セマフォーを解析して不具合を追及する(6/19/2011)
 そんなことで、次の課題はぶちあげたものの、実は、あまりやる気が起こらず、暇さえあればセマフォーのトラブルシューティングをやっていた。

 まず、不具合の状況を整理する。セマフォーを導入する前は、リアルタイマークロック(RTC)からの1秒毎のUARTのメッセージを何時間出していても全くトラブルがないのに対し、受信割り込みを待つためにセマフォーを使うと、20分前後で全タスクがフリーズする。

 セマフォーが動くUART受信部を全く動かさなくても、不思議なことにフリーズする。ここが謎である。ただし、時刻を表示しなければ、セマフォーを入れていても症状は発生しない。フリーズするタイミングは時間ではない。UARTから出力するデータの数である。

 セマフォーを組み込むことで、時刻表示のUART出力に不具合が生じ、時限爆弾のように、何千回かあとで問題が顕在化する(そう見えるだけなのかもしれないが)。フリーズするまでの時間は最初10分程度だったのが、いつのころからか20~30分程度に伸びた。正確な時間をとってみると、最初は13分で、増えたときが26分。時間がちょうど倍と言うのが悩ましい。

 セマフォーの中のソースコードを解析する。セマフォーは独自のソースではなく、キューの関数を流用している。サイズが0でキューの数が1のキューをハンドリングする。mallocでメモリ(わずかだが)を取っていることがわかった。

 そういえば、時刻を表示するのに使うprintfは、mallocを使っている。printfはリエントラントではないので、受信のエコーバックと被ればおかしくなる可能性はあるが、別に被っているわけではなく(受信を一切しなくても起きる)、これがフリーズの原因とは考えにくい。しかし、念のため、printfをやめて自前の送信関数を作って時刻表示させてみる。やっぱりフリーズすることに変わりがない。

 FreeRTOSのメモリ管理は、いくつか種類があって、良く見たら、ねむいさんが公式デモソースのメモリ管理の設定をスタティック管理(heap_2.c)から、gcc標準のmallocを使ったダイナミック管理(heap_3.c)に換えていた。もしや、これかと、heap_2.cに戻してテストしてみた。

 しかし、フラッシュサイズが20%近く増えただけで、結果は全く変わらなかった。いやあ、難しいものだ。JTAGなどのデバッガーも考えたが、フリーズした地点のアドレス(恐らくBad Interruptで不正番地)がわかるだけで、このあとの解析はARMやFreeRTOSの内部環境を熟知していなければ、簡単には手をつけられない。

最初から徹底的に調べるも万策がつきる(6/20/2011)
 だいたい、最初にセマフォーが動き始めた時から動きが不審である。1回空振りをしないとセマフォーが働かなかった。何か、セマフォーに関して不具合報告はないかと、ウェブでキーワードを換えて何度も検索するが、それらしい記述は、前のSTマイクロのときのようにヒットしてくれない。お、似たようなことをしていると良く見ると、自分のブログの記事だったりして苦笑いである。

 しかも悔しいことに、このデモソースの中の別のセマフォーは何の問題なく動いている。他の例でも問題があるような話は何もない。それなのに、自分のだけがおかしいのである。不愉快なこと極まりない。

 こうなったら意地になるのがいつもの癖である。いちからセマフォーを少しづつ設定して動きを探ることにした。まず、セマフォーハンドルのメモリ上の定義だけをして、他はすべてコメントアウトしてビルドしテストする。これは問題ない(まあ、当たり前か)。

 次は、セマフォーハンドルの初期化。 これは以前、タスクでやって問題を起こしているが、もう一度、main()と、vUartTask()でそれぞれ初期化してみる。これもどちらも問題なし。

 続いて、セマフォーのTakeステートメント(待つ方)を入れる。Give(許可する方)が動いていないので、キーボード入力は出来ないが、そのまま動かしてみる。これも問題なく時刻を吐き続ける。

 今度は、セマフォーのGiveFromISRステートメントを割り込みルーチンに組み込み、テストする。キーボード入力しなければここは通らないので問題ないはずだ。ややや、違う。これだけでフリーズした。ここを一回も通っていないのは、ロジックアナライザーで確かめてある。何ということだ。通っていないルーチンの組み込みでトラブルが起きる。???である。

 これは一体どうしたことだ。セマフォーを入れたことによって、割込みルーチンの構造が変わったのか。コードを入れただけで命令を実行しなくてもトラブルが起きる。うーむ、根が深そうだ。

 フリーズの原因と見られる、RTCタスクのprintfの出力は、最終的にはUARTタスクの中の関数、uart0_putを使う。セマフォーを動かしているのはuart0_getという全く関係のない関数だ。何故それで無関係のuart0_putを使うタスクがフリーズするのか理解できない。ただ割り込みルーチンが共通なので、このあたりが臭いことは確かだ。

 セマフォーを入れることで、何かコンテキストスイッチのルーチンが組み込まれたのか。まさか、そんなわけはない。Takeなら、コンテキストスイッチが起きて、状態が変わる可能性があるが、GiveFromISRは、単にキューエリアに何か書くだけだ。状態が変わるとは思えない。しかも、その発行もしていないのだ。

キューを調べ始めて意外なものを発見(6/23/2011)
  悔しいけれどセマフォーの不具合究明はこれ以上は諦めることにする。諦めるのは2回目である。さすがにもう無理だ。調べるところがない。

 気を取り直して、前に決めた課題にとりかかる。FatFSは少し重いので、UARTのキュー化の方から始める。本来マルチタスクOSでUARTを使おうというときは、キューでメッセージを一箇所のUARTタスクに送り、ここで一手に出力するというのが正式な導入である。このデモソースでも、LCDがゲートキーパーとして、この方法で動いている。

 セマフォーで受信割り込みを監視するなどというのは、小手先のOSの利用に過ぎない。本当はキューからやるべきだったのだ。キューを勉強しながら、セマフォーを諦めた自分を一生懸命慰める。

 ただ、UARTをキューで取り扱うのは、そう簡単ではない。printfなどリエントラント化されていないサービス関数をどう取り扱うかが課題である。それでもこの方法は、幸い、ウェブにお手本ある。PICがターゲットだが、このあたりは余り変わりはない。これを少し真面目に読み込む。

 読んでいるうちに、FreeRTOS7のデモソースにも、キューで動くUARTの見本があることがわかった。そうだ、以前、ARM7_LPC2106_GCCのフォルダーでserial.cとして、UARTソースを見たけれど、それらしい。

 始め見たときは、FreeRTOS特有の長い長い変数で、あまりにも読みにくく、敬遠していたのだが、今読み返してみると、大分読めるようになっていた。FreeRTOSの理解が進んだのだろう。おおよそ何をやっているのかわかるようになってきた。

Arm そのうち、意外な部分を発見した。割り込みルーチンが違う形になっている。ありゃあ、これまでのものと大分違うぞ。ラッパーが作ってあり、コンテキストのセーブ/リストアをこのルーチンでやっていて、本来の割り込みルーチンはここから呼ばれるだけである。

 つまり、これまでは、割り込みルーチンのコンパイルモードをARMモードにし、関数の前後に、SAVE_CONTEXT();とRESTORE_CONTEXT();を挟むだけだったが、ここでは、インラインアセンブラーの、

__asm volatile ("bl 割り込みエントリ");  // 本来の割り込みエントリーにリンクする

と、この前後をSAVE_CONTEXT();とRESTORE_CONTEXT(); ではさんだラッパー(Wrapper)関数を作り、この2つの関数に対し、

void 割り込みラッパーエントリ( void ) __attribute__ ((naked));
void 割り込みエントリ( void ) __attribute__ ((noinline))

というコンパイラーオプションをつけている。割り込みラッパーエントリが実際の初期化のときに、VICテーブルに定義する割り込みエントリとなる。

 ふーむ、何か閃いたぞ。現在の割り込み処理とやっていることとあまり大きな変化があるとは思えないし、現在のデモソースの他の割り込みルーチンも、この方式は採っていなくて問題なく動いている。

 しかし、わざわざラッパーまで作って分けてあることには何か理由があるのだろう。セマフォーの不具合と関係しているのかもしれない。これは試してみる価値があるのではないか。

遂に成功。1時間経っても止まらない(6/24/2011)
 大した手間ではない。UARTとRTCの割り込みルーチンに上記のラッパーをそれぞれ組み込む。セマフォーを全部戻して、26分でフリーズする元の形にしてビルドする。フラッシュサイズの増加は、殆どなかった。

 テストする。こいつは、すぐに結果が出ない。UARTコンソールを立ち上げ、これを横目にウェブブラウザーを見ながら、時間が経つのを待つ。フリーズする26分が近づいてくる。胸の鼓動が段々高くなって、その場にいたたまれなくなる。端末は黙々と時間を刻んでいく。

 このRTCは結構精度が良く、日差数秒で数週間経つがまだ分の遅れも出ていない。いや、そんなことを言っている場合ではない。27分を越えた。まだフリーズしない。30分を過ぎた。うまく行ったのではないか。ただ、この不具合、26分のその倍、52分で止まったこともあるし、一度はフリーズせずにリセットだけして先に進んだこともある。安心はできない。

 1時間半が経った。まだ時刻表示は止まらない。歓喜がじわじわとこみあげてくる。どうも解決したようだ。他の割り込みが、この方法を使わずに何故上手く行っているのかは説明できないが、少なくとも、UART割込み、セマフォーの組み合わせでの不具合は、ラッパー関数の追加で解決した。

 いやあ、今度も長くかかった。結局、不具合がわかってから解決まで、ほぼ2週間かかっている。何度も書いているけれど、この解決した時の達成感は何物にも替えがたい。しつこく追いかけた甲斐があった。これまでの悩みが吹き飛んで、無上の喜びになる。電子工作の明らかに、変な楽しみ方のひとつである。わざわざ難問を設けて、その解決を追求するのだから。

Semaphoreuart その後、一晩、動かしっぱなしにして、フリーズしないことを確かめた。これで心置きなく、次の課題に取り組める。なぜそんなにOSにこだわるのか。ロボット制御には不可欠な機能だから、と、ちょっと偉そうに答えてみる。そろそろmbed(LPC1768)も触ってあげないといけないな。

 ソースコードの公開については迷った。こちらが付け加えたコードは少なく、ChaNさんや、ねむいさんのコードが多い。公開するのは正直言って気が引ける。まあ、でも少しは、みなさんの役に立つこともあるかと思い、アップすることにする。少なくとも、セマフォー付きUARTは完全に動く。モニターとしてはまだ未完成で、入力した文字はリターンキーで戻ってくるだけである。

 ビルドするためには、FreeRTOSの適切なディレクトリーチェーンの中におく必要がある。詳しくは当ブログのバックナンバー記事、ねむいさんの書いたreadme.txt等を参照されたい。

ここに、Eclipseのプロジェクトとしてのソースフォルダーをzipで固めたものを置きます。
解凍したフォルダーをFreeRTOSの適切なディレクトリに置き、makefileのあるディレクトリを
Eclipseのwork spaceにしてください。

「ARM7_LPC2388_20100121.zip」をダウンロード

| | コメント (9) | トラックバック (0)

2011年6月18日 (土)

FreeRTOSでバッファー付きUARTを動かす

 雑誌付録ARM基板(LPC2388)にFreeRTOS(7.0.0)をインストールし、割り込みでRTC(リアルタイマークロック)を動かすことに成功した。その勢いを駆って、本来の目的であるバッファー付きUARTの実装を目指した。

 しかし、これが難航しているのである。2週間も経つのに、まだこれで完全と言える段階に到達しない。思うようにいかないものだから、つい他のものに目移りしてしまって余計進捗が遅れる。これ以上日をあけるとわかりにくくなるので、このあたりで報告しておくことにする。

バッファー付きUARTは一文字入出力までしか動かない(6/5/2011)

 ARMでのFreeRTOS特有のコンテキスト保存のやり方を知って割り込みルーチンが無事動き(前記事)、次の割り込みを使ったバッファー付きUARTの実装は簡単に思えた。受信割り込みを何で待たせるか、セマフォーにするか、それとも全体をキューで仕立てるか、贅沢な悩みを抱えながら余裕を持ってソースコードを調べ始めた。

 このソースコードは、元祖はFreeRTOS V7の公式サンプルソースで、KEILの評価ボード(MBC2300)のイーサネットでウェブサーバーが動く。これにねむいさんがV6のとき、I2CやUARTなどを追加した。このUARTは割り込みを使わず、ポーリングで直接、送受信レジスターを叩いてデータをやりとりする方式で、これまで問題なく動いている。RTCの時刻表示メッセージや、時刻入力でも使われている。

S_p6043961 ただ、このUARTソースには、このポーリング方式以外に割り込みを使ったバッファー付きUARTのコードが入っており、#ifdefで切り替えられるようになっている。オリジナルは、Copyright(C) 2006, NXP Semiconductorとあるように、チップ製造元のフィリップスのサンプルソースのようだ。

 バッファー付きのコードの方はこれまで試してみたが全く動かなかった。一文字も出力せずいきなりハングする。RTCで割込み環境をテストしていたのは、このバッファー付きUARTをちゃんと動かすためでもある。受信はともかく、これからUARTをデバッグに使うためには、高速でデバッグメッセージが出せるように、バッファー付きの送信にしておきたいからである。

 #ifdefをバッファー付きの方に切り替え、RTCのときに得たFreeRTOS上での割り込み手順を、このソースのUART割り込みルーチンに適用していく。たいした手間ではない。期待に胸をふくらませてビルドする。受信割り込みは、用心してまだセマフォーなどは使わない。

 テストする。ふむ、最初の数文字は出力されたが、それ以上はハングアップした。ただ全く動かないのではなく、わずかだが文字が出た。道は少し開いたようだ。一旦、処理の殆どをコメントアウトし、LEDを使って少しづつ機能を調べていく。デバッグの重要な武器であるprintfはUARTの開発のときには使えない。いざとなったらロジアナでLEDのピンをプローブにして調べる。

 ARMはAVRと違ってUART割り込みは、送受信とも同じ割り込みベクターに来る。まず、受信が出来ているか調べる。LEDを割り込み部に挟んで、キーボードを打つ。ちゃんとデータを読んでいるようだ。しかし、データが来ていない。自前の関数(待ちを入れないためのラッパー)が邪魔をしていることがわかった。これをとりはずす。受信はOKになった(と思われる)。

 次は、送信である。エコーバックを戻す送信関数をコメントから本番に戻して再度ビルドする。よし、入れた文字が画面に出力された。エコーバックも問題ないようだ。これまでに直したところは、コンテキストのリカバーのあとに、割り込み終了のステートメントが入っていたケアレスミスだけである。快調なペースだ。

 1文字単位の入出力はOKとなった。残るは、連続文字出力である。オープニングメッセージを出すようにコメントをはずす。しかし、これはやはりまだ駄目だ。数文字出たところでハングアップする。今夜はもう遅いのでこのあたりで作業を終了する。

LPC2388のUARTを勉強する(6/6/2011)
 次の日は仕事だったが、帰宅して早々に地下のPCルームにこもる。割り込みを使った送信ロジックは、レジスターの送信終了を単に待つロジックに較べると、はるかに複雑である。連続送信の不具合がそう簡単に解決しないことはわかっている。

 UARTの送信割り込みは通常、送信レジスターが空の時にあがる。この割り込みを受けて割り込みルーチンは、バッファーからデータを送信レジスターに送り込み続ける。バッファーはリングバッファーになっていて、ユーザー側の送信関数は単に、バッファーにデータを置いていくだけである。これでユーザー側は、送信レジスターが空くのを気にせず効率よく送信出来る仕組みだ。

 この方式ではバッファーが空になると、割り込みを上げ続けるので、このときは割り込みを止めなければならない。リングバッファーの制御などにも気を配らないとデータは失われる。

 これまでARMのUARTではデータシートを殆ど見ずに、人の書いたソースを見よう見まねで、いじってきたが、このあたりからは無理だ。デバッグに入る前に、基本に戻ってデータシートで少しまともにLPC2388のUARTを調べ始めた。

 おお、このUARTは自動ボーレート設定機能まで持ったフル装備のUARTだ。16バイトの送受信FIFO(先入れ先出しbuffer)を持っており、受信FIFOではデータ量を指定して割り込みを発生できる。昔のPCについていて460Kbpsまでサポートしていた16550というUARTチップと同一機能ということだ。

 で、サンプルソースはどうしている。あれえ、バッファー付きといっても、送信はFIFOを使っているだけで、ユーザーバッファーを持っていない。メモリ内の移動の速度に較べれば、FIFOレジスターへのデータ送り込みはかなり遅いはずで、その証拠に送り終わるのを待つしかけがある。ここはメモリの送信バッファーが欲しいところだ。

 それに、コードを見る限り、FIFOにデータがなくなると絶えず割り込みがかかって(フラグを上げているだけだが)、わずかでもオーバーヘッドが気になる。さらに不具合の原因とみられる条件が見つかった。

 FIFOに収容した送信データがなくなると割り込みが起き、割り込みルーチンでフラグを上下しているが、送信関数でもこのフラグを動かしているので、複数のデータを送り込んだ場合、オペレーションが重なってハングする恐れがある。ここは、UART割り込みがここで起こらないよう処理の前後に割り込みをマスクする手順が必要だ。どうも、このソースの信用がおけなくなってきた。

 そのうち、このソースコードに関して大きな思い違いをしていたことに気づいた。このUARTソースはねむいさんが、どこからか持ってきたソースで、FreeRTOS用の公式サンプルソースではない。ねむいさんは、このUARTの割り込み版を使わずポーリング版を動かしただけで、恐らくこちらには何も触れていない。

つまり、このバッファー付きUARTはFreeRTOSでは全くテストされていない。要するにこれにこだわる理由は何もない。送信バッファーもないし、これを動かすことに力をかけるより、最初から作り直したほうが早そうだ。

Ws000000

ChaNさんのUARTは簡単にFreeRTOSで動いた(6/8/2011)
フルスクラッチでUARTを書こうと意気込んでは見たが、へたれである。すぐ思い直す。サンプルがこれだけあるのに何もそこまですることもない。これまでの先人の成果を利用しないのは、資源の無駄遣いである。地球にやさしくない(と勝手な理屈をつける)。

 誰のソースを選ぶかと言うと、これはもう1もなく2もなく、ChaNさんのFatFSのUARTソースである。外国人の作った長ったらしい変数に較べれば読み易く、何と言ってもコーディングのセンスが良い。(あまりにもスマートで解読に時間がかかるときもあるが)。

 このソースは、これまでのFatFSについていたUARTと殆ど同じで、大きなユーザーバッファーを送受信とも持っている。これまでのUARTをrenameして退避させ、ChaNさんのUARTをプロジェクトに持ち込む。入出力の関数名を揃える。最初はセマフォーを使わず、割り込みルーチンだけをFreeRTOSの仕様に合わせる。

 変更は僅かである。開発は順調に終わってテストに入る。うーむ、動かない。動いているコードと見比べる。コンパイルエラーは出ていないので変数名の間違いはない。ふむ、この最後のベクターインタラプトを終了させる、VICVectAddr = 0;    /* Acknowledge Interrupt */というおまじないがない。

 こんなもので、動かなくなることがあるのか、半信半疑だったが、ないことは確かなので入れてみた。ありゃあ、動いたー。一文字入出力も、多バイト出力も全く問題ない。あっけなく割り込みUARTのFreeRTOS版ができてしまった。やっぱり自前で作ったほうが良かったかな。達成感が違うもの。

セマフォーで難航する(6/10/2011)
 このままでもFreeRTOSの割り込み付きUARTなのだが、これだけではあまりにも芸がない。特に、受信はFIFOを使っているとはいえ、受信関数uart0_getcは、割り込みではなくバッファーにデータが入るのを延々と待つ方式である。OSのUARTらしくない。

 OSのセマフォーを使えば、この間はCPUがシステムに帰り、リソースの無駄遣いを減らせる。FreeRTOSの演習のつもりで、セマフォーを導入することにした。これが実現できれば、ソースを公開しても恥ずかしくないはずだ。ところがこれが難航した。功名心にかられてやるとろくなことがない。

 セマフォーが解けた(giveされたあと)、受信バッファーにデータが入ってこない。UARTのFIFOをさらに研究する。はじめ8バイトとってあった割り込みトリガーレベルを1バイト(FIFOなしと同じ)にして割り込みがすぐでるようにしたが、うまくいかない。受信バッファーにデータが入ったかチェックすればセマフォー入りでも動くが、これでは何のためにセマフォーをいれたのかわからない。ポーリングより原始的になる。

 しかも、12~20分くらいでハングアップすることも判明した。泣き面に蜂である。ロジアナを持ち出してLEDのところをプローブポイントにして調べるが、今ひとつ解明の足がかりにならない。

セマフォーが動いていないことがはっきりした(6/13/2011)
 2日間、かかりきりで調べまわったが、解決しない。たいした話(受信データをポーリングで待つか、割り込みで入るか、どっちでも大差はない)ではないのだが、どうもすっきりしない。

 LEDのプローブ点を何度か変えてテストするうち、意外なことが判明した。UARTの割り込みの不具合ではなく、セマフォーそのものが全く機能していない。つまり、セマフォーをtake(渡されるのを待つ)するステートメントでタスクが止まらないですり抜けていることがわかった。

 どうも納得できない。他のサンプルコーディングと全く同じコーディングなのに、その通りに動かないというのは、不愉快なものである。一字、一字ステートメントを確かめる。どこにも間違いはない。変数の受け渡しがおかしいのかと、main.cとuart0.cの間で変数定義をあちこち替えてみたり、volatileをつけたりはずしたりするが、変化なし(static volatile XXXではエラーになる!)。

 UARTモニターにしか使わなければ、UARTの受信はキーボードからだけで、ポーリング(人間相手なら10ms間隔で十分)で何の問題もないのだけれど、セマフォーとか、キューなどのタスク間の同期制御は、OSの重要な基本機能の一つである。

 これが動かせなければOSを使った意味がない。OSは、この基板で、モーター制御などのロボットを視野に入れているので、何としても動かしておきたい。しかし、セマフォーはがんとして言うことを聞かない。

アクリル曲げ器を作るついでに温度制御の野心(6/14/2011)
 セマフォーを使ったUARTが思うように動かないのでストレスが溜まっている。実は、kumanさんの掲示板で見た「ばんと」さんのアクリル曲げ器を、自分でも作りたくなり、大分前に秋月の調光器キットだけを買ってあった。

S_p6183980 開発が思うように進まないので、気を紛らわそうと、このキットを組み立て、ケースに作りこんだ。手を動かしていないといられない性分になったこともある。白熱電球のスタンドでテストする。ボリュームを少し動かしただけで明かりが急に変化して少しおかしいが、とりあえず調節は出来るようになった。

 東急ハンズで、アルミパイプや、耐熱ファイバーケーブル、ニクロム線などを買ってきた。ニクロム線が、みなさんが買っている値段とは違う法外な高値(0.5ミリ5mで¥500!)だったが、一箇所で間に合うので目をつぶって一緒に買う。東急ハンズですべての部品が揃った。全部で駐車料が無S_p6183966料になるぎりぎりの¥2000ちょっと。

 ニクロム線と接続ケーブルの間は、圧着端子とネジ止めでつなぐ。耐熱ファイバーをパイプから少し長めに伸ばして、接続部分が触れないように工夫する。ここにスイッチを置くアクリルカバーを作りたいが、「鶏と卵」問題で、とりあえずはバラックとする。

 調光器で試運転する。これも、こういうときのためにS_p6183963買ってあった放射温度計(通販で¥3500)で測るが、思わしいデータは出ない。温度がばらばらだ。おまけに低いと思ってうっかり触ったら、やけどまでしてしまった。適温にする調光器の調節が難しい。

 このあと、調光器キットのコンデンサーを間違えて接続したことがわかり、電圧の調整がスムーズになって、150℃前後の温度が作れるようになった。早速、アクリルの端切れを曲げてみる。うむ、正確に測って曲げれば、色々なものが作れそうだ。

S_p6183965 調整に苦労しているうちに、マイコンでこのパイプの温度制御をやりたくなった。高温のセンサーとなる熱電対を調べる。これがまた結構面白いのでついはまってしまう。調べると欲しくなり、仕事の帰り、久しぶりに秋月電子に寄って熱電対を買ってきてしまった(¥400)。

 熱電対は工作心を刺激する部品だ。このセンサーは、2極の電極の間の温度差しか出力しないので、低い方の電極の温度を知っていないと本来の温度にならない。別の温度センサー(又は氷)と組み合わせる必要がある。しかも、1℃あたりの起電力が41μVと(K型熱電対の場合)、極小なので精度の良いオペアンプで増幅する必要がある。

S_p6183981

 興味に任せて色々調べていたら、この世界にもICがあることがわかった。アナデバのAD594という、冷温側のセンサーを内蔵したオペアンプや、MAXIMのMAX6675などは、何と出力がデジタルのSPIで出てくるものなど、結線だけで簡単に測定ができるようになっている。値段はいずれも¥1000以上するので(¥1300~1500)、急には手が出せないが、いずれDigiKeyで買って見よう。こういう「ゆるい」電子工作も面白い。

やっとのことでセマフォーが動いた。しかしまだバグがとれない(6/15/2011)

S_p6183967 アクリル曲げ器の工作の合間にも、あきらめきれずにセマフォーをテストしていた。何しろ至極単純なバイナリセマフォー(一回きりの待ち)が動かないのである。他のソースコードをいくつか調べても違ったことをしているわけではない。

 このウェブサーバーのサンプルソースにも、同じような割り込みから処理を再開するのに、このセマフォーは用いられ(uIP.cでパケットが送られてくるのを待つ)、何の問題もなく動いている。何かがあるとするなら、ソースファイルを異にして、グローバル変数(セマフォーハンドル)を、extern(外部参照)で受けているくらいだ。

 まさかとは思ったが、念のため、同一ソースファイルでexternを経由せずセマフォーを動かしてみた。C言語からみれば全く同じ条件で、変るわけはないのだが、ものは試しである。

 これが、何と、驚いたことに、main.c上のセマフォーtakeが正常どおり動いたのである。これまでuart0.cに置いてあったセマフォーがどうやっても素通りしていたのに、ちゃんとgiveされるのを待つようになった。割り込みルーチンでgiveするとスペックどおり、次のステップに進む。

 一体どういうことだ。グローバル変数での受け渡しには警告すらでていない。それなのに、何故同一ファイル上でのセマフォーは動いて、別のファイルのルーチンでは動かないのだ。全く理解できない。volatileはつけてもつけなくても同じ。

 その後少しづつステートメントをずらして、一旦、main.c(セマフォーハンドルを定義したところ)で、セマフォーtakeを一回空振り(待ち時間を0にする)させておくと、別ファイルにあるセマフォーも動くことがわかった。わけはわからないが、とりあえずセマフォー付きのUARTの完成である。

 しかし、セマフォー付きのUARTは動いたとはいえ、まだ問題がある。前から気になっていた長時間時刻を表示させるとハングするトラブルが解消されない。最初は、13分、プログラムをいじっているうちに26分、RTCでUARTに時刻を表示し続けると確実にハングする。

 時刻を表示しなければ問題ない。奇妙なことにセマフォーを設定しただけ( vSemaphoreCreateBinary(セマフォーハンドル))で、ハングする。どうもこうなるとFreeRTOSのバグ臭い。RTCのprintfでのメモリー操作とぶつかっているような気がして、自前のuart0_put関数で作り直したが結果は同じだった。

 あまりこればっかりに関わっても埒が明かない。少し冷静になって周りを見てみよう。当面セマフォーからは撤退してUART受信はポーリングでデータを待つことにする。やることはまだまだ沢山ある。

| | コメント (0) | トラックバック (0)

2011年6月 4日 (土)

FreeRTOSでユーザー割り込み環境が動いた

 ここ1週間、悩んできたFreeRTOSのユーザー割り込み環境がやっとのことで通った。2年前のソースの作者にまでお助けメールを出して大騒ぎしていた一件は、何とか解決した。振り返ってみれば、何でこんなに遠回りしたのだろうと呆れるばかりだが、トラブルシューティングというのは大体こんなものだ。今度の顛末(てんまつ)も、「問題解決」という命題には恰好のケーススタディになるだろう。

LPC2388の割り込み環境を研究する(5/28/2011)
 前回の記事で、雑誌付録ARM基板(LPC2388)に入れたOS、FreeRTOSでRTC(リアルタイマークロック)が動き出したことを報告した。しかし、このRTCでは割り込みを使っていない。

 OSの元では、RTCは必ずしも割り込みを使う必要はない。それぞれのタスクが独立して動けるので、OSのタイマー(システムに戻るだけでループしていない)で1秒ごとにタスクを起して、時刻を表示させれば、自分で割り込みを使うより簡単に時刻表示が実現できる。

 割り込みにこだわるのは、実はUARTにある。現在のテストベンチのUARTは、割り込みを使ったBufferedモードが動かず、受信は10msごとにバッファーにデータが来ているかを調べるポーリング方式である。

UARTの受信はPCからのキーボード入力だけなので、このままで特に何の問題もない。それよりも、このテストベンチをこれからモーター制御や、カメラ制御などに使っていくためには、むしろUART送信の方を割り込み方式にしておく必要がある。

 割り込みを使わない現在の送信ロジックは、送信レジスターにデータを入れたら送り終わるのをただ待っているだけである。CPUの処理に較べれば、死ぬほど遅い(CPUは数十ナノセカンド、UARTは数百マイクロセカンド)UART送信が終わるまでループして待っていたら、たとえその間のCPUをOSに返すとしても、デバッグ対象のタスク環境が乱れてデバッグにならなくなる。UARTは是非割り込みで動かしておきたい。

Arm

 RTCが動き出して気持ちにゆとりが生まれた。UARTを割り込み化するのは簡単ではないので、このRTCをベンチに、少し腰をすえてARMの割り込みを基本から勉強する気になった。2年前に買ったまま放ってあった「ARM組み込みソフトウエア入門」(CQ出版社)などを取り出して読み始める。ここには1章、30ページ近くを費やして割り込みの詳しい解説がある。

 そうこうするうちに、ねむいさんから返事が返ってきた。なになに、ソースにヘッダーファイル、irq.hをインクルードして、__irqオプションを生かせば動くのではと言うメールだった。確かに、割り込みルーチンを定義している現在のUART.cや、RTC_support.cには、#include irq.hがない。これで動けばラッキーこのうえない。わざわざ割り込みを勉強する必要もない。喜び勇んで、RTC_support.cにirq.hを入れて動かしてみた。しかし、残念ながら、コンパイルエラーはなくなったが、ハングする状態に変わりはなかった。念のためUARTにも入れてみたが、動かないことは同じ。

FreeRTOSでユーザー割り込みは使えないのか(5/30/2011)

 FreeRTOSのソースは読みにくい。外国人のソースコードの特徴は変数がやたらと長いことで、英語が母国語の彼らにはこれで可読性が高まるのだろうが、日本人にはただ読みにくいだけで馴染めない。FreeRTOSも変数だけでなく、変数タイプにも長ったらしいprefixを追加したりしているのでなおさらのこと読みにくい。

 入れてみて文句を言ってみても始まらないが、FreeRTOSのtutrial(教育環境)はあまり親切とはいえない(ちょっとまともなリファレンスブックは有料になってしまう)。実践的なところの解説が不足している。移植性を謳うのなら、機種ごとに、systickタイマーを何にしているのか、どんなリソースをOSが使うのか(割り込みベクターなど)くらいの基本的なことが、書いてあれば助かるのだが、このあたりの情報は少ない。ソースコードを見れば、ということなのか。

F_rtos_eclipse

 八つ当たりしているのは、FreeRTOSで割り込みを使って、しかも動いているサンプルコードがなかなか見つからないからだ。同じ機種でないと、このあたりは参考にならない。新しく入れたRTCはともかく、既にFrerRTOSのサンプルソースに入っているUARTの割り込みが動かないのが謎である。

 割り込み環境を用意するirq.cには、ユーザーが簡単に割り込みベクターを追加して割り込み処理を加えるサービス関数install_irq()が用意されている。それなのに、それを使った割り込み環境のUARTはハングして動かない。

 スタンドアロン(ベアメタルと最近は格好良く呼ぶ)では、みんな楽々割り込みを動かしている。それはスタートアップルーチンにアセンブラーで補完した多重割り込みに備える処理が加えられているからだ。参考書や、ウェブの解説で見るとおりのことをしている。

 ところがFreeRTOSのスタートアップルーチンboot.sは殆ど裸に等しい。それに、このクラスのARMのCPUは、32ビットのARMモードと、16ビットのThumbモードの命令系統が混在しており、余計事態をややこしくしている。

 FreeRTOSで動くARMのUART、タイマーのサンプルコードくらいWebで探せばいくらでも見つかると思ったが、意外に少ない。それもいくつかあるように見えたUARTは、殆どがSTM32でもお世話になったMartin Thomas氏のUARTを元にしている。彼のベアメタルで動かしているUARTソースは割り込みを使ったBufferd UARTなのに、FreeRTOSではコメントアウトされている。これがあやしい。

 そのうち、彼のベアメタルのソースに気になるコメントを見つけた。どうもこれが犯人のような感じだ。2007年5月2日付のMartin Thomas氏のARM LPC2388用 uartサンプルでのコメントである。サービスルーチンのひとつarmVIC.cのヘッダーファイルarmVIC.hには、次のようなマクロの定義がある。そこのコメントに、
/*************************************************************
*
* MACRO Name: ISR_ENTRY()
*
* Description:
*    This MACRO is used upon entry to an ISR.  The current version of
*    the gcc compiler for ARM does not produce correct code for
*    interrupt routines to operate properly with THUMB code. 

「現在のARM用gccコンパイラーは、Thumbモードのコードでは正しい割り込みルーチンを作れない」とある。このあと、このソースコードにはこのマクロが使われている。

 どうもThumbモードでは、gccコンパイラーが適正なコードを作れないようだ。uart.cや、rtc_support.cはThumbモードのソースである。ウェブを見てみると確かに、gccコンパイラーの、バージョン3は、こういうバグがあったみたいだが、4では改善されたように見える。

 しかし、公式のARMの情報センターでは、現在のバージョンでもThumbモードでの多重割り込みは、gccコンパイラーではアセンブラーをつけないと動かないと書いてある。良くわからない。

 ということで、藁にでもすがる思いで、コンパイラーを最新のものに換えてみることにした。CodeSourceryの最新版(Sourcery G++ Lite 2011.03-42)をインストールする。gccは、2年前の、4.3.2から、4.5.2に上がった。しかし、予想したとおり結果は同じだった。コンパイルのオプションエラーになっていたno-dwarf2-cfi-asmオプションはエラーが出なくなったが、何故かファームのフラッシュサイズが20%以上増えた。このオプションのせいでもない。

デバッグ用にLEDを実装するが状況は暗い(5/31/2011)
 霧が深い。割り込みルーチンをARMモードにしてみることも考えたが、副作用が怖い。やってみようという気にならない。#ifdefで割り込みUARTをコメントアウトしてあるということは、どちらでも動くからで、もし駄目なら何らかのコメントがあるはずだ。Thumbモードで動いていたのではないか。ことさらARMモードにする理由が見つからない。

S_p6043960 仕方がないので、もう少し動かしながらデバッグしてみようと、LEDを2つ追加して実装した。このあたりのデバッグは、前にも書いたように、printfでは話にならない。UARTでメッセージを出す間もなくハングアップしてしまうからだ。LEDなら遅れない。

 LEDのI/Oピンは、WebのCGIを動かしているIOページのピンを選ぶ。こうしておけば動作確認が簡単にできる。特に問題なくウェブからLEDのON/OFFが出来るようになった。本当はそれどころでないのだが、暫くウェブでLEDを点けたり消したりして遊ぶ。

 割り込みルーチンにLEDの出力ステートメントを入れる。点灯しない。予想に反してここへ来ていないことがわかった。これまで調べていたレジスターのセーブ/リストアといった問題でない。もともとの割り込みベクターの設定がおかしい。事態は振り出しに戻ってしまった。

Freertos_led

 割り込み環境は調べれば調べるほど難しい。霧が深まるばかりで出口は見えない。あの参考書には、多重割り込み優先度つきなどという複雑な処理の方法が延々と解説されているだけで、頭の中の混乱は返って増すばかりである。

 FreeRTOSの簡単なインストールを解説したサイトを見つけた。これを見ていると割り込み環境が余りにも複雑なので、ChaNさんのFatFSプログラムをFreeRTOS化したほうが早そうな気がする妄想にとりつかれる。いやいやそんなわけはないのだけど。

頭を冷やしてもっと合理的なアプローチを考え直す(6/1/2011)
 4日近く、調べまわってやることがなくなった。少し頭を冷やすことにした。今やっていることの本当の目的は何なのか、自問してみる。FreeRTOSで沢山の仕事を同時に簡単に動かせるような環境を作るのが目的で、OSの中のロジックを理解することではない(これはこれで面白いが)。

 それなら、現在動いているソースのなかから、割り込みを使っているところを調べて、徹底的にこれを真似れば動くはずだ。要するに、大上段に振りかざして正面から問題を解決するのではなく、コバンザメのように利用できるものは何でも利用する姿勢でないと、こういう複雑な環境はいじれない。

 Thumbモードで割り込みを動かそうと四苦八苦しているが、何もそれにこだわることはない。そうなのだ現在のFreeRTOSでは、割り込みを使っているところはすべてARMモードでコンパイルしている。どうも、ここにカギがありそうだ。今動いているルーチンをそっくり真似てみよう。

 FreeRTOSのソースから、苦労して、割り込みハンドラーと、割り込みの初期設定しているコードを洗い出す。systickタイマーのTimer0のところport_ISR.cと、イーサネットのuIPの下部ルーチンEMAC_ISR.cのところだ。調べた結果、これらは2つとも自前で割り込みベクターを設定していて、例のirq.cの設定サービス関数を使っていない。うーむ、難しいな。

 さらにウェブを探していると、意外なメッセージを発見した。ねむいさんがUSBの独自ルーチンを開発する時、「FreeRTOSのやりかたを真似て割り込みルーチンを作った」とある。あ、これ、これ、これだ。ねむいさんが追加したUSB仮想COM、vcom.cは、インストールしたときハードがないので早々とコメントアウトして中味を見ていない。そういえばMakefileでもこのvcom.cはARMモードでコンパイルするようになっていた。

やっとのことで割り込みが通った(6/3/2011)
 そうか、これだ。何かつながったぞ。山が当ったような気がする。これを参考にしよう。vcom.cをあらためて詳しく調べる。それによると、

・__irqオプションは使っていない。__attribute__オプションもつけない。

・Thumbモードでなく、ARMモードでコンパイルする。

・普通のVIC設定関数install_irq()で割り込みベクターを設定する。

・FrerRTOSのコンテキストセーブ/リストアをするマクロ、portSAVE_CONTEXT()と、portRESTORE_CONTEXT()を使う。このためFreeRTOS.hをインクルードする。

・処理が終わったらVICVectAddr = 0などで、処理終了を知らせる。(6/13/2011追記)

 早速、rtc_support.cのRTC_Handler()に適用する。たいした変更ではない。ARMモードのコンパイルで副作用が出るのが心配されたが、無事何事もなくビルドは終了した。コアサイズも前と変わらない。

 今度は何か上手くいきそうな予感がする。期待が高まる。FlashMagicのファーム書き込みが終わるのがもどかしい。そろそろJTAG環境を作って書き込みを早くしたいのだが、その暇はない。

 ファーム書き込みが終わった。リセットする。おおー、動作中を示すLEDのブリンクが止まらない。割込みルーチンに入れたLEDが点灯する。やりました。やりました。UARTを立ち上げる。1秒ごとに時刻が表示される。これはまだOSのタスクウエイトで出しているメッセージだが、少なくとも割り込みルーチンはハングせずに通過している(LEDが点いている)。

 コードを割り込みのたびにフラグを上げて、メインの方でこのフラグを見て時刻を表示するロジックに換え、割り込みの中のLEDを点滅するように換えて再度テストする。

S_p6043958

 よーし、想定どおりにマシンは動いた。FreeRTOSデフォルトのLED点滅以外に、RTC割り込みルーチンに入れたLEDが1秒ごとの点滅を繰り返す。数時間放置する。問題なく動いている。嬉しい。これでUARTでもバッファー付きの送信が動かせる見通しがたった。いやあ、今度も長かったな。ねむいさんにまた助けられた。ありがとうございました。

今回の問題解決のポイントをまとめてみる。

・頭に血を昇らせずに、時々追求を休止し、現在位置を確認して、攻める方向を整理する。

・冷静に一番合理的なアプローチを決めたら、そこへ資源を集中する。

・情報は可能な限り集め、常に頭の中で情報を泳がせる。思わぬところで、つながりが見えて問題が解決する糸口になることがある。

| | コメント (2) | トラックバック (0)

« 2011年5月 | トップページ | 2011年7月 »