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の時刻表示メッセージや、時刻入力でも使われている。
ただ、この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では全くテストされていない。要するにこれにこだわる理由は何もない。送信バッファーもないし、これを動かすことに力をかけるより、最初から作り直したほうが早そうだ。
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さんの掲示板で見た「ばんと」さんのアクリル曲げ器を、自分でも作りたくなり、大分前に秋月の調光器キットだけを買ってあった。
開発が思うように進まないので、気を紛らわそうと、このキットを組み立て、ケースに作りこんだ。手を動かしていないといられない性分になったこともある。白熱電球のスタンドでテストする。ボリュームを少し動かしただけで明かりが急に変化して少しおかしいが、とりあえず調節は出来るようになった。
東急ハンズで、アルミパイプや、耐熱ファイバーケーブル、ニクロム線などを買ってきた。ニクロム線が、みなさんが買っている値段とは違う法外な高値(0.5ミリ5mで¥500!)だったが、一箇所で間に合うので目をつぶって一緒に買う。東急ハンズですべての部品が揃った。全部で駐車料が無料になるぎりぎりの¥2000ちょっと。
ニクロム線と接続ケーブルの間は、圧着端子とネジ止めでつなぐ。耐熱ファイバーをパイプから少し長めに伸ばして、接続部分が触れないように工夫する。ここにスイッチを置くアクリルカバーを作りたいが、「鶏と卵」問題で、とりあえずはバラックとする。
調光器で試運転する。これも、こういうときのために買ってあった放射温度計(通販で¥3500)で測るが、思わしいデータは出ない。温度がばらばらだ。おまけに低いと思ってうっかり触ったら、やけどまでしてしまった。適温にする調光器の調節が難しい。
このあと、調光器キットのコンデンサーを間違えて接続したことがわかり、電圧の調整がスムーズになって、150℃前後の温度が作れるようになった。早速、アクリルの端切れを曲げてみる。うむ、正確に測って曲げれば、色々なものが作れそうだ。
調整に苦労しているうちに、マイコンでこのパイプの温度制御をやりたくなった。高温のセンサーとなる熱電対を調べる。これがまた結構面白いのでついはまってしまう。調べると欲しくなり、仕事の帰り、久しぶりに秋月電子に寄って熱電対を買ってきてしまった(¥400)。
熱電対は工作心を刺激する部品だ。このセンサーは、2極の電極の間の温度差しか出力しないので、低い方の電極の温度を知っていないと本来の温度にならない。別の温度センサー(又は氷)と組み合わせる必要がある。しかも、1℃あたりの起電力が41μVと(K型熱電対の場合)、極小なので精度の良いオペアンプで増幅する必要がある。
興味に任せて色々調べていたら、この世界にもICがあることがわかった。アナデバのAD594という、冷温側のセンサーを内蔵したオペアンプや、MAXIMのMAX6675などは、何と出力がデジタルのSPIで出てくるものなど、結線だけで簡単に測定ができるようになっている。値段はいずれも¥1000以上するので(¥1300~1500)、急には手が出せないが、いずれDigiKeyで買って見よう。こういう「ゆるい」電子工作も面白い。
やっとのことでセマフォーが動いた。しかしまだバグがとれない(6/15/2011)
アクリル曲げ器の工作の合間にも、あきらめきれずにセマフォーをテストしていた。何しろ至極単純なバイナリセマフォー(一回きりの待ち)が動かないのである。他のソースコードをいくつか調べても違ったことをしているわけではない。
このウェブサーバーのサンプルソースにも、同じような割り込みから処理を再開するのに、このセマフォーは用いられ(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受信はポーリングでデータを待つことにする。やることはまだまだ沢山ある。
| 固定リンク
「電子工作」カテゴリの記事
- 生存証明2(2022.10.19)
- 生存証明(2022.01.23)
- パソコン連動テーブルタップの修理を諦めて自作(2021.02.16)
- 半年ぶりのブログ更新に漕ぎつけた(2019.09.19)
- 研究所活動は停滞したままでCCDカメラ顕微鏡導入など(2019.02.08)
「ARM」カテゴリの記事
- 心電計プロジェクト:スケールが出ると心電計らしくなる(2015.01.08)
- 心電計プロジェクト:TFT液晶に念願の心電波形が出た(2014.12.18)
- 心電計プロジェクト:STM32F103の心電波形表示で悪戦苦闘(2014.12.03)
- 心電計プロジェクト:CooCoxでARMの表示系ソフトを開発する(2014.10.16)
- 心電計プロジェクト:表示部のARM基板の開発環境を一新する(2014.09.19)
「FreeRTOS」カテゴリの記事
- WiFiモジュールESP32で画像付きサーバーの開発に成功(2017.09.28)
- FreeRTOSマルチタスク対応UARTとガイガーカウンターキット(2011.07.08)
- FreeRTOSのセマフォーの不具合がやっと解決(2011.06.25)
- FreeRTOSでバッファー付きUARTを動かす(2011.06.18)
- FreeRTOSでユーザー割り込み環境が動いた(2011.06.04)
コメント