« 雑誌付録ARM基板でSDカードとRTCを動かす | トップページ | FreeRTOSでバッファー付きUARTを動かす »

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でもバッファー付きの送信が動かせる見通しがたった。いやあ、今度も長かったな。ねむいさんにまた助けられた。ありがとうございました。

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

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

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

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

|

« 雑誌付録ARM基板でSDカードとRTCを動かす | トップページ | FreeRTOSでバッファー付きUARTを動かす »

電子工作」カテゴリの記事

ARM」カテゴリの記事

FreeRTOS」カテゴリの記事

コメント

毎度、毎度同じパターンですみません(笑)。
でも、これが面白くて電子工作やっているようなもので
(おい)、前にも書きましたが、一種の「中毒」です。
ほほえんでいただけるのは、嬉しいですね。この気持ちを
共感してくれる人が他にいるということですから。

投稿: がた老 | 2011年6月 7日 (火) 16時11分

>やりました。やりました。

いつも、このくだりに来ると、ほほえんでしまいます。
レベルは違っても、心境がよくわかるからでしょうね。

投稿: きゅうる村 | 2011年6月 5日 (日) 07時54分

コメントを書く



(ウェブ上には掲載しません)


コメントは記事投稿者が公開するまで表示されません。



トラックバック


この記事へのトラックバック一覧です: FreeRTOSでユーザー割り込み環境が動いた:

« 雑誌付録ARM基板でSDカードとRTCを動かす | トップページ | FreeRTOSでバッファー付きUARTを動かす »