カテゴリー「FreeRTOS」の7件の記事

2017年9月28日 (木)

WiFiモジュールESP32で画像付きサーバーの開発に成功

 前回までのブログは、失敗続きで暗かった。この年(もう70才を数年越えた)で、無謀にもみんながやっていないI2Cのクロックストレッチをソフトで実現しようとして、ハードウエアの基礎知識不足を見事にさらけだし、あえなく撤退した。

 しかし、今度の記事は胸を張って明るく報告できる。今流行りのWiFiモジュールESP8266の兄貴分ESP-WROOM-32(以下ESP32)で、画像付きのウェブサーバーの開発に挑み、このほど動かすことが出来たのだ。 Dsc01199

 ESP8266ではフラッシュの部分をファイルシステムにするSPIFFSがArduinoIDEで用意されていたので、これを利用して画像データファイルをホームページに表示することが出来た。ただ、ESP8266では性能的に一杯一杯で、わずか12KB程度のjpegファイルの表示に、ひといき(0.5秒くらいか)時間がかかり実用的とは言えない。

 ESP32は、ESP8266に比べると、CPU数が1から2、クロックも1.5倍(160->240Mhz)と性能が一段と強化されている(他にもBlueToothなど機能も豊富になった)。こいつなら少しは実用的な画像を背景にしたホーム画面や、イラストで作ったボタンが使えるかもしれない。
ここが詳しい

 ところが、ESP32のArduinoIDEの環境では、今のところSPIFFSが動いていないようなのだ。(このブログ参照)しかし、ウエブ情報を集めていくと、製造元(espressif社)の提供する開発環境、ESP-IDFや、他のプロジェクト(Rua-RTOS)で、フラッシュファイルシステムを作ったという話が出てくる。

 うまく動いているらしいが、リンク先が海外で、いまひとつ確実なことがわからない。でもここまできたらESP-IDFを導入して画像が出るところまで動かし、ESP8266との差を確かめたくなった。

 以下は、ESP-IDFの環境整備から、画像表示に成功するまでのESP32開発記録である。実は終盤になって画像を表示させるのにSPIFFSより遥かに簡単な方法が見つかるという、どんでん返しがあったのだが、詳しくは本文で。

ESP-IDF開発環境の導入から始める(9/2/2017)
 ESP-IDFとはArduinoIDEとは別の開発環境である。製造元の正式環境だが、make主体のCUIの世界で、WindowsにLinuxの環境を作るなど、準備に手間がかかり、今まで敬遠してきた。しかし目標である画像の出るサーバー画面の実現のためには避けて通れなくなった。

 気分を新たにして、開発環境を準備する。ハードウエアはArduinoの時と同じ秋月で売られている純正ブレークアウト基板DevkitCを使う。こいつは横幅が広いので普通のブレッドボードに差すとジャンパーが出せなくなるが、ここはミニブレッドボード2枚を並べてしのぐ。

 ESP-IDFのインストールは紹介するサイトが今や山ほどある(以前は少なかったが)。戸惑うことはない。むしろ沢山ありすぎて何を選べば良いのか迷うほどだ。まあ、余りこだわることはない。こことかこのあたりを参考にインストールを始めた。

 最初のmingw(Win上のLinux環境)のインストールファイルが500MB近くもあり、サーバーの線が細くてダウンロードに1時間近くかかったのが問題だったくらいで、インストールそのものは、意外にも順調だった。ここでも「ねむいさん」のブログにお世話になる。彼(彼女?)は何と去年のうちに環境をインストールしテストまでされていた。

 mingw上に自分のホームディレクトリを作り、開発環境は出来上がったが、沢山のサイトを拾い読みしていたために、ホームディレクトリの位置がさまざまで、esp-idf(開発環境本体)のディレクトリパスが中々通らない。何とか、ごまかし、ごまかし(mingwなので、サブディレクトリ区切りがバックスラッシュ\と、/が混在してややこしい)、makeが通るようにする。

 やっと動き出して、中味のファイルや、サンプルコードを覗く。ここはC++でもなく、純然たるCの世界だった。STM32で少しかじったFreeRTOSがメインのOSのようで、何か久しぶりに旧友に会ったような気分でなつかしい。

 ただ、MakeFileのあたりの情報隠蔽がかなり深い。実際のMakefileの中身が全く表に出て来ないので何をやっているのかわからない。ソースがメインプログラムしかないのも不思議である。わからないことだらけだが、とりあえず先に進む。

LチカとHelloWorldは簡単に動いた(9/3/2017)
 以前の記事のとおり、ESP32はArduinoの環境で既に動かし、LEDをウェブから制御できるサーバーまで作った。それでもESP-IDFの環境に慣れるため、サンプルソースにLチカ(blink)と、hello worldのソースがあったので、これらを使って練習することにした。

 ホームディレクトリに、サンプルソースの一式(AVRで言うプロジェクトのようなものか)をコピーし、make menuconfigで、シリアルラインの定義をしたあと、makeに入る。延々とビルドが始まった。

 ふーむ、Lチカくらいで、何か、すべてのリソース(IPV6からBluetooth、SSLまで)をビルドしているようだけど、どう言うことなんだろう。まあ、何十分もかかるわけでもないので待つしかないが、何か無駄のような。

Esp32_spiffs  Lチカは、指示通り、make menuconfigでIOピンの位置を修正する。前に使ったLEDをGPIOに設置し、makeに入る。幸いNO Errorのようだ。続いて、make flashでファームに焼く。これもエラーは出ない。すると、めでたくLEDが1秒近い間隔で点滅し始めた。よーし、動いた。

 次は、Hello worldである。もうひとつ手順が増える。make flashのあと、make monitorでコンソールにUSBコンソールのCOM3仮想ターミナルを立ち上げる。やたらとメッセージが多いが、無事、コンソールにHello World!の文字が10行づつ繰り返された。これも問題ない。

SPIFFSのgithubを見つける(9/6/2017)
 次は、HTTPサーバーだが、その前にこれまで見つけてあるSPIFFSプロジェクトを入れることにした。目星をつけたいくつかのサイトを調べているうち、英文だが、外国人(イタリア?)の英語なので、とても理解しやすいサイトを見つけてある。

 以前に見つけたところとは別のサイトだが、esp-idfの環境で、SPIFFSが出来たという報告である。沢山の感謝とお礼のレスポンス付きだ。これは間違いなく動いているようだ。喜び勇んでダウンロードする。

 順調に進むかと思われたが、そう簡単に問屋は許さなかった。今度も、ディレクトリパスが難しい。make menuconfigそのものが通らない。いくつかの関門を潜り抜け、menuconfigまで行ったが、今度は本番のmakeでビルドエラーが出る。残念!

 何かおかしい。インストールした場所が悪いのか。githubなので、clone先が所定のところにないと、正しく動かないようだ。余り深入りは避け、次の日、出勤した事務所のPCで最初からインストールしてみた。これが不思議、makeが通ったのである。

SPIFFS動作成功(9/8/2017)
 帰宅して、もう一度やり直す。やった。makeが通る。make flashで実際にファーム焼き込み。これもうまく行った。原因はやっぱりgitを展開するディレクトリの位置だったようだ。いやあ気難しい。

 まあ、あまりこれにこだわっていても仕方がない。先に進もう。それにしても単なるフラッシュだけの操作なのだけど、LチカとHelloWorldと同じように、延々とすべてのライブラリのコンパイルが続く。

Ws000032  testSPIFFS.cのソースコードをあらためて精読する。mainで各種の関数をテストし、それをコンソールに出力している。そんなに複雑ではない。これだけで動くようだ。何はともあれmake monitorでコンソールを動かしてみた。

 やった、それらしい出力が次から次に出力される。ディレクトリを増やしたり(mkdir)、ファイル一覧(ls)なども出来る。うむ、うまく動いているようだ。フラッシュファイルにアップロードする方法がまだわからないが、テスト環境には、フラッシュに入れたファイルイメージが残されており、何らかの方法でアップロードは出来るようだ。

 ここまで進むと先は見えてきた。ESP-IDFのサンプルソースにはHTTPサーバーのソースがあるので、このSPIFFSを、サーバーに合体させれば動く見通しがついた。どちらを母体にするか迷ったが、構造の複雑なサーバーのソースコードにSPIFFSの要素を組み込んでいく。その前に、HTTPサーバーを動かしておかないといけない。しかし、これが意外と難渋したのである。

やっとホームページが出せた。サンプルソースでは動かない(9/18/2017)
 サンプルがあるので、簡単にHTTPサーバーは動くかと思ったが、これがなかなかいうことを聞かない。いくつかあるHTTPサーバーのソースの一つをビルドしたが、ホームページ(index.html)を出すようなところが見当たらない(http_request_server)。

 やっていることは、どこからかのソケットを受けてそれをSTDOUT(コンソール)に表示するだけである。これはサーバーではないよね。クライアントがやることだ。少なくともesp-idfのサンプルソースesp-idf/examples/protocols/http_requestと、https_requestにあるソースには、リクエストしかなく、これでは動かない。

 困ったときはgoogle先生である。esp-idf http_server などのキーワードで検索を続けると、別のそれらしいソースコードが見つかった。ドイツの電子工作ショップのサイトで、ソースだけで説明記事に戻れないが、コードを読む限りでは、クライアントのリクエストに対してindex.htmlを返す部分がある。もうひとつタスクが必要のようだ(netconn_server)。

 半信半疑ながら、このソースに取り替えて、再度ビルドしてみる。よーし、OK! 簡単なindex.htmlが表示された。良かった。でも、なぜ本家のサンプルには、本来のHTTPサーバーの雛形がないのだろう。謎である。次はいよいよhtmlファイルのフラッシュファイル化に進む。

HTTPサーバーの解析に夢中になる(9/16/2017)
 フラッシュファイルを入れるため、サーバーのソースを読みふける。おおー、段々全体が見えてきた。嬉しい。いや勉強になる。これまで近づこうとしてなかなか機会のなかったソケットプログラムだ。lwipとか、nghttpとか、新しいプロトコルを知る。

 電子工作ではなくて、ウェブプログラミングで遊んでいる。この世界も複雑で奥が深い。HTTPサーバーの教科書が少ない。事務所の帰りにいつもの秋葉原書泉で参考書を探すが、自分が知りたい基礎の部分を解説したものはなかなか見つけられない。

 このesp-idfの開発環境にも大分慣れてきたが、それににしても、このフルビルドは何とかならないか。延々と必要もないモジュール群をコンパイルしていく。一旦コンポーネント(ライブラリ)が出来ると、あとは少し早くなるが、それまでは大変だ(まあ、モジュールを選択するのも大変なので、こういうやり方もあるか)。

 文字列の連結、文字列のサーチなどArduinoIDFにはあった機能をせっせと開発する。コーディングとしては楽しかったが、これらはすべてCの標準関数(string.h)にあることがわかって、お蔵入りした。完全な無駄足である。

SPIFFSでテキストファイルの送出は成功した(9/22/2017)
 画像表示は、読み込みのとき大きなバッファースペースが必要なので、まずは文字ベースでindex.htmlに埋め込む方法をテストする。これが結構難しい。どうも思ったようにhtmlに展開してくれない。

 それより問題なのは、nett_conn_serverというFreeRTOSのタスク上でSPIFFSを動かすとstack overflowでプログラムが落ちるのである。menuconfigなどでスタックサイズを広げるが改善されない。これもウェブで調べて解決方法が見つかった。タスクを起こす関数に、スタックサイズを指定するパラメーターがあったのだ。

  これを通常の3倍(6144バイト)まで広げてやっとstack overflowは止まった。しかし、それでもindex.htmlに思ったような文字列が出てこない。何回か試行錯誤するうち、重大な誤りをみつけた。

 文字列の長さを得るのに、sizeofを使っていたのだが、ポインターを使った文字列では、これがアドレスを収容するエリア(ここでは4バイト)になってしまうことに、だいぶ後になってから気づくというお粗末である。Esp32spiffs1

 しかも、関数の引数にすると呼ばれた先では、strlenでも文字数が正しく得られない。それやこれやで苦闘の結果、何とか出せたのだが、<object src=XXXXX />のタグでは、スクロールバーのようなものが出てしまう。目的は画像ファイルなので、道草を食いたくないのだが、テキストファイルの表示だけで四苦八苦である。

何と、もっと良い方法を見つけた。バイナリの埋め込み(9/24/2017)
 色々とウェブをさ迷ううち、SPIFFSではない、もっと楽な画像ファイルの表示方法があることを偶然つきとめた。いや情報は浴びるように摂っていれば良いことがあるという言葉どおりの快挙である。

 esp-idf esp32 webserverなどのキーワードで、調べているうち、スマホのきれいな画像のボタンでLEDをウェブから制御しているページを見つけた。

 ボタンがきれいなイラスト画像(pngファイル)である。へえー、サーバーでどこかのクラウドサーバーにリンクして画像を持ってきているのだろうかと、思っていたら、どうもそうではない。Ws000001 mainと同列にある、component.mkファイルにCOMPONET_EMBED_FILES:=でファイル名を定義すると、これをbinary dataとしてフラッシュに埋め込んでくれるというのだ。

 これはすごい。以前、AVRでやったことのある、フラッシュにバイナリーデータを埋め込むobjcopyと同じ手法である。埋め込んだ後、エントリーポイントを取得すれば、プログラムのなかでこのバイナリーデータを使うことが出来る。

 まだSPIFFSではjpegファイルまで進んでいないが、ファイルを読み込まなくても、外部参照だけでデータをとりこめる。しかもバッファーの長さを気にする必要もない(コンスタントデータなのでバッファーの長さは気にしないで良い)。

 これは試してみるしかない。早速、サイトの情報を頼りに、見よう見まねで、適当なjpegファイルをとりだし(SPIFFSスタックの中にあった画像)、component.mkファイルに設定し、ビルドしてみた。おーし、何のエラーも出ずビルドは終わった。

Esp32spiffs2  次はmake flashである。いつもより少し時間がかかるが無事終了した。jpegファイル40KB分だけ遅くなっているか。さあ、make monitorでサーバーを立ち上げる。

 おおー、やった。画像がホームページに出た。しかも早い!一瞬だ。嬉しくて何度もディスプレイの前でガッツポーズをする。ホームページそのものは、画像とテストメッセージ一行の何も意味もないページだが、このあとには無限の可能性が待っている。

SPIFFSでも画像ファイルの送出に成功。そう遅くにもならない(9/25/2017)
 勢いに乗って、SPIFFSでも画像が送れるかやってみる。このnetconn_serverというしかけが良く見えないので、バッファーサイズをどれくらいまで広げられるかわからない。これが、画像ファイルの読み込みを遅らせていた理由だが、もうそんなことは言っていられない。適当なサイズ(4KB)でneconn_write()を繰り返し発行することにする。

 恐らく、バイナリ埋め込みに比べれば遅くで使い物にならないかもしれないが、まずはやってみる。動かしてみた。おやあ、またstack overflowで止まる。祈る気持ちでスタックサイズを2048の3倍からさらに4倍の、8192まで上げて再度動かしてみる。

Esp32_spiffs3  よーし、動いた。そんなに遅くならない。esp-idfについているデバッグ用のメッセージにms単位のタイムスタンプがあるので、これで測ってみると、同じ画像で、埋め込みで平均62msが、SPIFFSだと108msだ。ブロックサイズを広げればもう少し早くなるかもしれない。

 とにかく、ESP32の画像付きサーバーの開発はこれで一段落した。胸を張ってブログに報告できる。

以下に、サーバーのソース一式をONE DRIVEで公開します。ディレクトリごとzipファイルにしたので、これをesp-idfのホームディレクトリに展開し、mainの直上のディレクトリでビルドすれば、バイナリデータを埋め込んでくれます。ソースには2種類のやりかたがコメントとして残っているので、適当に選んでください。WiFiの設定はmake menuconfigで行います。

ここをクリックしてONEDRIVEに行く

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

2011年7月 8日 (金)

FreeRTOSマルチタスク対応UARTとガイガーカウンターキット

LCDをはずしてもFreeRTOSが動くようにする。ロジアナ大活躍(6/28/2011)
 FreeROSのセマフォーが解決して、ちょっと一息ついた。たいしたことでもないものに、自分で自分を追い込んで解決に血道を上げる。確かに、前記事で頂いたコメントの通り、自虐趣味かもしれない(少しむきになって否定したけれど)。しかし、単調な日常生活にこれほどドラマを作ってくれる趣味は、ちょっと他に見あたらない。テニスやスキーなどにもドラマはあるが、ドラマを作るためには、多大な精進と肉体を苛めるトレーニングが要求される(これは自虐とは言わない)。

 電子工作の場合は、こだわるものは目の前に無尽蔵に転がっている。盛んに研究されているプログラムライターなどもそうだ。別のライターで書いたからといってフラッシュに2倍のサイズが書き込めるわけではない。書き込みが速くなるといっても、精々数十キロのフラッシュROMにどれだけ早く書けたところでたかが知れている。しかし、みなさんのこだわりようは、尋常ではない。余程面白いに違いない。 

 それはともかく、当面解決すべき課題がなくなり、体の中にぽっかり空き地が出来たようだ。これまでのメモを取り出して、やろうとしていたことを整理し、優先順位をつけながら次のプロジェクトの準備をぼちぼち進め始めた。

P7084013 本来なら、前にも書いたようにUARTのマルチタスク対応なのだが、どうも気分が乗らない。こういうときは手を動かしているのが一番だ。ブレッドボードに臨時に設置していたFreeRTOS用のI2C LCDを次のプロジェクトに備えて外す作業を始めた。

 元のLPCMプレーヤー(テストベンチとしてブレッドボードに常駐)の方に戻し、LCDなしでARM(LPC2388)基板のFreeRTOSを動かそうとした。これが動かない。ふーむ、LPCMプレーヤーはLCDをはずしていても動いていたのに、何故、ARMでは動かない?

 ソースを見て原因はすぐわかった。I2Cの初期化で、LCDからレスポンスが返ってくるのをループで待ち続けている。これは一番確実な方法だけれど、ここはLCDがなくても事情を理解して先に行って欲しい。いわゆるソフトウエアのRobustness(堅牢さ)というやつである。

 必要とされる初期化待ち時間を仕様(300ms)の倍くらいにして、このループをはずす。あれえ、これでも動かない。どうしてだ。傍らを見ると、これまでの解析に使っていたロジックアナライザーが出たままになっているのに気づいた。そうだ、このロジアナ(ZeroPlus 16128)にはI2Cのプロトコルアナライザーがついているはずだが、まだ使ったことがない。ちょうど良い機会なのでロジアナでI2Cの波形を見てみることにした。

I2cinit うーむ、待ち時間を倍にしても、レディにならずにスタックしている。ループを使った元の状態に戻す。何ということだ。10回ほどリトライした後レディになっているが、その時間は増やした待ち時間より短いではないか。

 ちょっと待て、クロックはどうだ。おやあ、2.2μsしかない。このLCDのI2C最大クロックは400khz(2.5μs)のはずだ。これじゃあ速度違反だ。4μsまで下げる(250khz)。

 なんだなんだ。これでも10回近くリトライし続ける。待っているだけではレディにならないのは同じ。おかしいな。元のAVRのLPCMプレーヤーのI2Cをついでにロジアナで見てみる。これは全く問題なくリトライせずに一回の初期化でACKを返している。

 うーむ、どうしてだ。おかしい、ARMでは何故一回でOKにならない。同じ待ち時間なのに、と、さらに調べそうになって、先に進むのをかろうじてやめた。今自分が何をやっているかに気付いた。これはこれくらいにしておこう。セマフォーに続き、本筋に関係のないことに首をつっこみすぎだ。

 愚直に、ARMのI2Cに有限回ループ処理を加えて戦線を撤収した(20回リトライして、動かなければLCDタスクをサスペンド)。これでARM基板のFreeRTOSはLCDなしでも動くようになった。

 それにしても、大枚4万近くをはたいて導入したこのロジックアナライザー(ZeroPlus 16128)が大活躍だ。I2Cのデータの解析(スタートコンディションやデータ解析)がプロトコルアナライザーで大幅に楽になるだけでなく。宣伝にもあるデータ圧縮が素晴らしい。

 電源をトリガーにしても、LCDがオープニングメッセージを出すところまでのデータが取れる。この間2秒以上離れているが、データ圧縮のお陰でメッセージデータが記録出来ている。

Lcdopening サンプリングクロックを10Mhzとするとメモリが128KBなので、普通にデータをとっていれば、2チャンネル(I2C)だけとしても、60msの間しかデータが取れないところである。ロジアナは事象の起きているところを正確に把握することが、とても難しいのだが(膨大な期間があるので)、こういう風に長期間測定できるとその時点の特定が簡単になり解析が非常に楽になる。

FreeRTOSのキューでUARTを動かす(7/2/2011)
 道草を食っているときではない。FreeRTOSの残された課題を早く解決しておこう。まずは、UART環境の整備だ。これは、これからマルチタスクでロボットなどを動かして行く時の強力なデバッグツールになる。

 これまでの記事にあるように、FreeRTOSのUARTを含めた割り込み環境はRTCと合わせ、やっとのことで何時間動かしてもフリーズしない安定した状況になっている。しかし、UARTそのものはまだ、リエントラントになっていない。誰でも(どのタスクでも)、任意にUARTに送信リクエストを送っても正しくメッセージが出るようにはなっていない。

 マルチタスクOSであるFreeRTOSのUARTとしては、これはまずい。データが化けるくらいならともかく、下手をすると簡単にフリーズしてしまう。デバッグツールには使えない。

 OSの持っている機能、キューでデータをやりとりすれば良いということはわかっているが、このあたりはソースリストをいじって簡単に解決できるレベルではない。もう少し抽象的なレベルに上げて検討する必要がある。

 実装の対象は、ChaNさんのUARTソースである。抽象レベルではわかっていても、このFIFO、入出力バッファー付きのUARTを、どうキュー仕立てのマルチタスク対応UARTにすれば良いのか具体的な手順が見つからない。

 うまい方法が見つからず、何枚もメモシートを書き潰していて、あるとき、突然気がついた。良く考えて見たら、ひとつのUARTの受信ポートに複数のタスクが集まって、データを取り合うことは現実にあり得ない。つまり競合を心配する必要がない。

 これまでは、送受信を対称的に考え、両方を同じ仕様で作ろうとあれこれ考えていた。今度のUARTは送受信ともハードのFIFO、ユーザーのバッファーと、2段の非同期プロセスが存在する。送信は、検討の初期から別タスクを起こして処理することを考えていたが、どうしても受信プロセスがうまくいかなかった。

 受信は難しく考える必要がない。単に受信割り込みでデータをキューに送るだけで、ユーザー関数はそれを待てば良いのである。UARTの受信の速度は、CPUのキューの処理に較べれば圧倒的に遅い。バッファーも必要ないくらいだ。

 送信は、ユーザー関数をキューで受けて、別の常駐タスクを作り、ここで実際のUARTハードへの送り込みをキューイベントをトリガーとして行う。printfがリエントラントでないので、これに対しては、Mutexなどでガードしてやる必要があるが、こうしておけば複数のタスクからの送信要求は安全に処理される。

Ws000000 コンセプトが明確になったので、コーディングにとりかかる。ユーザー向けの送受信関数は、キューステートメント1行のえらい簡単な関数になった。UARTタスクからMonitorタスクを分離し(これが今後のモニターコマンド処理を担う)、UARTタスクはハードの初期化と、送信キューの送出のみに絞る。

 テストしてみる。よーし、動いた。ロジアナで動作を確認する。ふーむ、Mutexを使ってprintfをガードしたけれど、このアプリでは重なることはまずなさそうだ。Mutexもはずす。Mutexだけで、フラッシュは560バイトも使っている。

Prtfmutex さて、今のところFreeRTOSのマルチタスク対応UARTは長時間テストを続けて、順調に動作している。次は、FatFSか。まずシングルタスク用に入れてみよう。

 FreeRTOSマルチタスク対応UARTのソースは以下に置きます。やり方は、これまでの記事を見てください。

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

SparkFunのガイガーカウンターキット到着(7/4/2011)
 そうこうするうちに、4月の末に発注したSparkFunのガイガーカウンターキットが、遂に届いた。入荷は6月第二週の予定であったが、結局6月には届かず、ちょうど2ヶ月かかったことになる。

 6月第二週の情報は、SparkFunの商品ページのコメント欄で得た。このガイガーカウンターキットのコメントには100を越す色々なメッセージが飛び交っている。このコメント欄に、6月にはいってすぐ、「6/1に出荷開始!」というコメントを入れた人がいたので、心待ちにしていたのだが、一向にその気配はなかった。

 すでに代金は、5月末に、クレジットカードの支払いで引き落とされている。段々心穏やかではなくなってきた。それでもFreeRTOSのトラブルシューティングに熱中していて暫く忘れていた。

 トラブルが解決して、一息ついてネットをぶらぶらしていたら、日本のブログでSparkFunのガイガーカウンターキットの制作を報告しているところを見つけた。この人は3ヶ月待たされたらしい。

 やっと外国である日本向けの出荷が始まったのかな。久しぶりにSparkFunのページにログインして自分のOrder Historyを見てみる。おお、NewOderがin delivary に換わった!1日もしないうちに、ステイタスはshippedに換わり、めでたく品物は日本に向けて出発したようである。

S_p7043996 4日ほどで品物が届いた。初めてのSparkFunからの荷物である。記念撮影をしておく。中から小さな赤いSparkFunの箱が出てきた。おやあ、全部配線が済んでいる。何だキットというから半完成品かと思ったら、全部の配線が終わっている。ガイガー管は、配線を束ねるタイラップで基板に固定され、グランド側は、線を巻きつけているだけだ。このあたりの工作が難しいので全部組み立ててあるのだろうか(外国人は、日本人に較べると驚くほど不器用である)。

 取る物もとりあえず、USBジャックを付けて電源を入れる。赤いLEDが煌々と点いて、その隣の緑のLEDが一瞬点いた。シリアル端末を立ち上げて様子を見る。始めは何の反応もなし。暫くして、緑LEDの瞬きとともに端末に01と数字が出た。おお、動いているようだ。

Geiger

 CPUチップの横にあるのがISPピンらしい。SparkFunのサイトに行って、最新版のファームをダウンロードする。ソースの中を調べる。うーむ、このソースでは動いていない。サイトの記事を見ると、どうもV12という放射線量の数を乱数に使うプログラムが送られてきたキットには入っているようだ。その証拠に、端末の0や1が増えている。0はパルス間隔が前の時より短かったとき、1は長かったときを表すようだ。

これだけでは、全くガイガーカウンターとしては用をなさない。最新ファーム(V13)も似たようなもので、1秒間の発生したパルスの数を延々と出しているだけだ。まあ、これは覚悟していた。ISPピンが出ているのでファームの書き換えに支障はない。
 
電源LEDを点滅に換えたりファームを書き換えたり(7/6/2011)

 このガイガーカウンターキットは、USBを通したUART(FT232RLがついている)が唯一のインターフェースで、パルスが来るとLEDが一瞬点くが、音が出るわけでもない。これをポータブルにするのは、独立電源にして、何らかの表示装置をつけないといけない。素人が使うなら音もあったほうが良いかもしれない。

S_p7044002 ただ基板上にはGM管からの出力パルス(トランジスタでバッファー)しか出力が出ておらず、QFPのMega328の空きI/Oピンは全く引き出されていない。LCDをつけるには表面実装のパタンから線を引き出す必要がある。ピン配置と基板をにらめっこして、半田ごてが入りそうなピンを物色する。

 ISPピンヘッダーはパタンがあるので、これをつけるついでに、パラレルLCDをつける半田付けのための練習を兼ねて、電源を入れると点きっ放しになる基板上のLEDをMega328のピンに移して点滅するようにした。パタンを切って銅面を出し、ピンをUEW線(0.2ミリ)でつなぐ。

 順調に作業が済んだ。ソフトの方は手馴れたAVRのプログラミングである。プログラムを変えて、赤のLEDがブリンクするようになった。ただ、この基板はクリスタルをけちってCR発振のクロックなので、余り厳密なことは出来ない。出てくる数値もあくまでも目安程度だろう。

 それでも、GM管から出てくるパルスの間隔を配列に蓄積し、10ヶの移動平均をとって、直近の平均のCPMを出すプログラムの開発にとりかかった。品物が来る前から構想は練ってあったので、コーディング半日、テスト半日で、所定の4桁のCPMがUARTに並ぶプログラムが動いた。

Geiger_2 出てきた数値をPCのExcelでグラフにしてみた。地下室は一般的には、自然放射線量が多いということだが、我家では、明らかに地下の方が低い結果が出た。

 あとは、LCDにこれを移しポータブル化することと、さらにμSv/hなどに換算するロジックや、データログ機能などが待っている。まあ、もともとは、「正しく怖がらなければ」などとブログで大見得を切った手前、何らかの自衛手段を持っておかねばと、はずみで買ってしまったようなガイガーカウンターである。深入りしていけばきりがないので、適当なところで切り上げるつもりだ。

Photo ソースコードは、オリジナルソースがクリエイティブコモンズのAttributeオプションと言うことなので、ソースコードの原作者表示を残し、当ソースもこれを継承していくことにする(作者名を表示すれば、複製、改変、商用可能)。

暫定版ですが、とりあえずソースコードを、例によってAVRStudioのプロジェクトとして下に置きます。SparkFunのガイガーキットだけでなく、パルスの到着間隔を測定表示するプログラムとして他にも応用が可能です。

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

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

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月27日 (金)

雑誌付録ARM基板でSDカードとRTCを動かす

失礼しました。プライオリティが逆でした(5/20/2011)
 このOS、プリエンティティブじゃないなどと言っていたが、こちらの間違いだった。FreeRTOSでのタスクプライオリティは数字が大きいほど優先度が高いのだ。uIPは他のタスクより優先度が高いとばかり思っていたので(実際は普通より低いところで動いていた)、UARTの数字をこれより大きくして、さらに優先度を上げてしまっていた。これではUARTがループウェイトすると、uIPにはCPUが渡らない。同一プライオリティの時しかラウンドロビンでCPUは廻らない。

 設定を逆にしたら、めでたくUARTでどれだけCPUを喰っていても、Webサーバーの処理は遅れないようになった。UARTはセマフォーを使った割り込み待ちを考えていたが、その必要もなくなった。やれやれ、どうも済みませんでした。

S_p5193932

 コマンドシェルは一番低いプライオリティにしておけば、他のタスクの邪魔をしない。ただ、LCDのタスク状態表示には、何かエラーがでている。同じプライオリティのタスクがどこかにあるからだろうか、良くわからない。まだ、このあたりは手探り状態だ。

 とにかく、いつものようにPCのUART端末からモニターとして動くよう、コマンドプロンプトのロジックを作る。まずはエコーバックだ。キーボードから入れたデータをリターンキーで送り返すだけだが、これは、このあと入力データ(コマンド)を解析して各種の作業を行うコマンドシェルになる。

 この辺の開発は、AVRなどと同じなので簡単にできる。ファームを書き直してテストする。うむ、ちゃんとエコーバックされてくる。同時にLEDが点滅し、Webサーバーが順調に2秒ごとにHTTPデータを送り続ける。いやあ、だいぶコンピューターらしくなってきたな。

LPC2388の割り込み環境の設定は難しい(5/23/2011)
 プロンプトまでは快調だったが、その後のFreeRTOSのテストベンチの作業ははかどらない。本来のコマンドプロンプトは、UARTの受信割り込みで処理を開始するのが筋だ。タスクのプライオリティで誤魔化すのは邪道である。しかしUARTの割り込み関数がうまく入らない。

 LPC2388の割り込み関数の指定が良くわからない。現在のソースには、割り込みを使っているUARTが、Buffered UARTとして既にコードが用意されているが、これを動かそうとすると(#ifdefで入れ替わる)、コンパイルエラーになる。

 どうもコンパイラーによって割り込み関数の定義が違うようなのだ。環境変数の__GNUC__を定義すると、割り込みの定義のところの__irqオプションが消えるのでビルドは通るが、このファームではFreeRTOSそのものが全く動かない。他に影響がでているようだ。

 あちこち探し回るが、ソースごとに設定の方法が違っていてどうして良いのかわからない。ChaNさんの雑誌のソースもあるので、ここでの割り込みを調べるが、ここは肝腎なところはすべてアセンブラーで実装されており、残念ながら参考にならない。

 結局、割り込みを使ったUARTは断念することにした。ウェブにあったように、UART受信バッファーを10ms程度のウェイト(OSにCPUを返す)を入れて覗く方式(ポーリング方式)で妥協することにする。UART入力は端末のキーボードからだけなのでこれで何の問題もないのだけれど、何となく気に入らない。それでもこれでLCDでのOSエラーはなくなった。

 同じARMでもNXPの開発環境は、I/Oレジスターのヘッダーファイルが用意されているだけで、アセンブラーのようにせっせと設定していく方法である。STMに較べると遅れているようだ。それでmbedのクラウドIDE方式で一気に飛躍しようとしているのだろうか。そういえばまだmbedは、LEDチカチカを確認しただけで、全く先に進んでいない。こいつはPHY層も含めたイーサネットインターフェースがあるらしいので早くいじってみたいのだが、現在はおあづけである。

LPC2388でSDカードを動かす(5/24/11)
 割り込み環境が思ったように導入できず、少し手詰まりになったので、気分を換えてLAN基板の横にスペースをあけて用意してあったSDカードスロットの実装を始めることにした。アートワークは、これまでの作業の合間に少しづつ描いていて既に完成している。

S_p5273949

 思ったより早くできた。まあ、プルアップ抵抗を5本ほどつなぐだけだけだから大した作業ではない。このあたりのハンダ付けは、汎用基板、プリント基板あわせて10回以上やっている。手馴れた作業である。回路図は、雑誌(インターフェース2009年6月号)記事を使う。もっとも、定数を含めてこれまでに紹介された回路図と殆ど変更はない。MCIモジュール(SDモード4ビットバス)が動くための結線部分が違うだけだ。

 これでLPC2388のLAN&SDカード基板(雑誌付録の拡張基板とほぼ同じ仕様)のハード工作は大体予定通り終わった。SDカードの動作テストを早くやりたいのだが、現在のFreeRTOSのテストベンチに、FatFSを組み込むのは大変だ。

 ねむいさんのFreeRTOS_V6のMakeFileにはFatFSを入れようとした痕跡が残っているが、実際のリソースは入っていない。単に1タスクのファイルシステムだけなら簡単そうだが、FreeRTOSの標準ファイルシステムにするのは、結構大変なはずだ(FatFSそのものはリエントラントなので十分対応可能だが)。

 どうしようか迷ったが、FreeRTOSの1タスクとして入れるのも先に延ばし、雑誌記事のChaNさんの単独システムを先にいれて、SDカード周りの動作だけでも確認することにした(慎重と言うか臆病である)。雑誌のダウンロードサイトから、2009年6月号のソース一式を頂く。以前にご本人のページからダウンロードしてあったソースとFatFSの部分は大きく変わっていないようだ。

 コンパイラーは、WINARMのarm-elf-gccだったが、うちのWINARM環境は自信がないので、いつものようにCodeSourceryのarm-none-eabiに換える。OLED液晶の部分はついていなくても動くと言うことなので、ソースには手を入れない。

 ビルドはリンクしているライブラリもないので、あっさりNo Errorで終了した。しかし、ファーム書き込みは時間がかかる。LFN用の64KBものコードブックをフラッシュに書いているからだ。遅いはずだ。少し待って書き込みは終了した。

 早速、動かしてみる。うんともすんとも言わない。そういえばREADME.TXTにOLEDかファイルモニターの切り替えは、ESCキーを押せとあった。シリアルの速度を合わさないとキーを押してもESCの区別がつかないはずだ。230Kbpsに合わせて、ESCを押す、よーし、Monitorの開始を知らせるオープニングメッセージが出た。手近のSDカードをさしてテスト。うーむ、Disk Errorだ。

 ディスクが入っていないことを示すエラーコードである。さて、動かないとなると最初から腰をすえて調べなければならない。ファームはまず間違いがないだろう。とすると、ハードだが、昔に較べると配線技術は、格段に向上していて最近は配線ミスが殆どない。あっても事前にチェックできている。誤配線よりもっと簡単なところのミスを疑う。

 ここに入っているファームは、ソースコードを見るとSDカードの所在や、ライトプロテクトまでチェックしているフルバージョンだ。まず、これが動いているかどうか調べてみよう。このあたりはSDカードを始めて動かそうとしたとき、散々苦労したところだ。

 CD(カード検出)は負論理なので、このピンはカードを入れたときグランドに落ちていないといけない。テスターで測る。あれえ、グランドになっていない。ややや、このピンはスロット筐体とはつながってる。何だと筐体とグランドピンは浮いているのか。何と、何と、ここは明示的につながないと駄目なのだ。これまでの実装は、このあたりを無視していたことが多いので、分離しているのに気がつかなかった(スロットは秋月で買ったノーブランドの小型のもの)。

Lpc2388_fatfs

 これで動くのではという淡い期待に胸を膨らませながら、筐体とグランドピンをハンダ付けしてテストを再開する。こんなもので動けば楽なもんだけど、そうはうまくいかないだろうな。

 電源を入れる。モニターを出す。初期化コマンドdiを入れる。エラーなし。おっ、動いたか。ファイルリストを出すコマンドを入れる。やった。やりました。お馴染みのファイル名が並んだリストがUARTに並んだ。いやあ、ここも、配線間違いなし。気分が明るい。

 アクセス速度を測る。雑誌の記事によれば、データアクセスは、LPC2388のMCIモジュールを使ったSDカード4ビットバスで、ぶっちぎりの早さ(ねむいさん)だそうだ。測ってみた。すごい。どのSDカードも64KBくらいなら、4MB/sec近辺という猛烈な早さだ。これなら動画も送れそうだ。これは楽しみになってきた。

 このFatFSは、FreeRTOSで動かすという大仕事が残っているが、LPC2388のLAN&SDカード基板はこれで完成した。これまでにかかった費用を計算してみる。PHY層チップ(DP83848C)は1枚こわしたので2枚として¥1200(失敗しなければ¥600)、ピッチ変換基板(アイテムラボ)1ヶ¥180、べたアース基板(サンハヤト)¥250、モジュラージャック(秋月パルストランスLED付き)¥300、SDカードスロット¥150、50Mhzオシレーター¥260、CRは全部で¥100するかしないか、ヘッダーピンソケット¥80くらい(この辺は全て手持ち)。あれこれ合わせて¥2,520。

S_p5273941

 そもそものきっかけ、法外な価格で買うのを反発した雑誌のタイアップLAN&SDカード拡張基板は¥6800だったから、これのほぼ1/3というところか。自分の作業工賃をどう考えるか? DigiKeyのゲタに買ったmbedの¥5400はどう考えるかって? え、まあ、こういうことは聞かないお約束と言うことで。そうかmbedも動かしてやらないといけないな。

FreeRTOSにRTCを入れる(5/26/2011)
 雑誌付録ARM基板LPC2388のLAN&SDカード拡張基板が両方ともうまく動いたので意気が上がっている。矢でも鉄砲でも持ってこいくらいの状態である。調子に乗って、FatFSをFreeRTOSで動かす前に、残っていた課題を片付けることにする。LPC2388のRTC(リアルタイムクロック)機能である。

 この雑誌付録基板は、LPC2388が、バッテリーバックアップのできるRTCを持っているのに、そのバックアップ電源Vbatピンを基板上でVccにつないでしまっており、せっかくの機能が台無しになっている。

 これでは何のためのRTCかわからないと思っていたら、ChaNさんが動画サイトで器用に、0.5ミリピッチのICの足を切ってVbatを独立させている工作を見て、感嘆した。

 当研究所でも、これに刺激され、早くにバッテリーバックアップに挑戦して成功している。しかし、電池をつないだだけで、まだ動くかどうかの確認をしていない。ここは是非試しておきたいところだ。動けば、FatFSのタイムスタンプにも使える。

 今動いているFreeRTOSのソースチェーンには何故かrtc.cは入っているが、どこにもこれを動かすコードは入っていない。ねむいさんのサイトを見てみると、FreeRTOSではなく、単独で実装されているようだ。とりあえず有難くソースを頂く。

 FreeRTOS上では動かなかったのだろうか。ソースを調べていく。そうでもないらしい。ただ、このRTCのコード(rtc_support.cとmain.c)には、割り込みルーチンが入っていて、簡単には入りそうにない。FreeRTOS上の割り込み環境は、結構難しく、UARTの割り込みを使ったコードはうまくビルドできないであきらめている。

 __irqというオプションが割込みハンドラー宣言に入っていて、ここでコンパイルエラーを起こす。ウェブの情報によれば、これをはずして、__attribute__ (((interrupt("IRQ")) に換えると良いというので、そうすると今度は、このオプションはthumbモードではサポートしないとコンパイラーに怒られる。ねむいさんのコードの割り込みにも__irqオプションが入っている。ねむいさんにお助けメールを出したが、忙しいらしく返事は前のようにすぐには戻ってこない。

 出来ないとなると悔しくて余計意地になるのが性分である。色々ウェブをさまよう。解決策は見当たらない。しかし落ち着いて考えてみると、RTCの機能はハードなので、何も割り込みを使わなければ動かないというものではない。では何故割り込みを使っているかというと、1秒ごとにディスプレイに出すためだけのようである。

 ここで閃いた。1秒ごとなら何も割り込みを使う必要ない。OSの元で動いているのだ、別タスクを立ち上げて、1秒ごとにRTCを調べれば良いはずだ。そうか、割り込みを使わないでも出来そうだ。

 思い立った時の手の早さには自信がある。rtc_support.cの割り込み部分を全部とり、見よう見まねで、FreeRTOSの新しいRTCタスクを新設する。このタスクはRTCを初期化して動かした後は、1秒の待ちを入れてUARTに時間を表示する。目覚ましのようなタイマーだってこれで作ろうと思えば作れる。OSにだいぶ慣れてきた。

Lpc2388_rtc

 喜び勇んでビルド。よーし、No Errorだ。ファームを書き込む。UARTを立ち上げる。おおお、初期設定をうながすメッセージがでたぞ。年月日を入れていく。やった。動いた。RTCが時間を表示し始めた。ありゃ、止まっちまった。どうもprintfが大きすぎてスタックがあふれたような止まり方だ。ウェブも止まって暴走している。

 年月日時分秒すべてを出す表示を縮めて再トライ。ついでにタスクのスタックサイズをUARTと同じに広げる。よーし、順調に時間が表示されてきた。バックアップは大丈夫か。電源を切って暫くしてからもういちど動かす。良いぞ、バックアップされている。ほぼ2年ぶりの確認である。いやいや、これで32.767Khzの時計用クリスタルと、コイン電池の顔が立った。

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

2011年5月20日 (金)

FreeRTOSのウェブサーバーが動いた

 連休前から始めた、2年前の雑誌付録ARM(LPC2388)基板につけるLAN基板は、チップ(DP83848C)ひとつを誤操作で失いながら、何とか完成した。電源を入れるとモジュラージャックのリンクやアクセスLEDが点滅しているので、電気物理(PHY)層は動いているようだが、ソフトを何も用意していないので、正常に動いているかどうかはまだ確かめられない。

S_p5173912

Eclipseを思い出すのに大変(5/13/2011)
 前々記事にもあるように、手当たり次第にWebからソースを沢山集めすぎて何にするか迷ってしまった。このLAN基板(PHY層がDP83848)を動かすものだけでも5つ、OSなしのもの2つと、FreeRTOSというOSの元で動くソース3つである。

 LAN基板の動作確認のために未経験のOSも入れるというのは、不確定要素が増えて嬉しくないのだけれど、この基板をカメラ制御のホストにするなど、先の開発のためには、OSが動いていて欲しい。面倒かもしれないが、あえてOSのついている方法を選ぶことにする。

 「ねむい」さんが親切にインストール手順を残しておいてくれているのだが、情けないことにその意味がよくわからない。このソースをうまく入れられる見通しがない。迷ったあげく、結局、正規のサイトが公開している一番新しいV7のデモソースを入れることにした。FreeRTOSも勉強しなくてはならない。

Freertos_web

 FreeRTOSは軽い実装で、殆どの組み込みマイコンで動く移植性が評判のようだ。AVRならMega328クラスでも動くらしい。しかし、まだあまり詳しく調べていない。これからの開発にはUARTなどを使ったモニターコンソールのようなものが欲しいのだが見当たらない。これは自作するしかなさそうだ。

 久しぶりにEclipseを立ち上げて、ソフト開発に着手する。こちらの案内が具体的だったので、これを参考に、FreeRTOS V7のLPC2368のデモソースをプロジェクトにしようとするが(LPC2368と2388はピン数が違うだけで殆ど同じ)、これがうまくいかない。

 情けないことにEclipseの操作法をみんな忘れている。出来合いのソースを使ってEclipseで新しいプロジェクトを作るという作業が出来ない。ウェブに助けを求めるが、Eclipseの情報はあまりにも膨大で的確な回答を得るのは至難の業だ。情報の海に溺れそうになる。

 「import」を使って、所定のディレクトリの上でワークスペースを指定するということを理解するまで大分時間がかかった。 Makefileがあるところをカレントディレクトリにすることで、ソースの中の「unresolved」な変数は殆どなくなった。試しにビルドをしてみる。だめだ。あっという間にエラーで戻ってきた。

 MakeFileの内容とディレクトリーチェーンを何度も確認する。間違っていない。Eclipseには未定義変数を示すエラーは出ていないが、Makeが一瞬に終わってしまう。いろいろなサイト(ここや、ここ)を調べるうち、どうもコンパイラーが、こちらで使っているCodeSourceryと違うことに気がついた。

 MakeFileのコンパイラーの指定を、arm-elf-gccからarm-none-eabi-gccにかえる。よし、少し動いたが、またストップ。今度は何だ。前とエラーメッセージが違う。

error Please uncomment one of the two configPINSEL2_VALUE definitions above, depending on the revision of the LPC2000 device being used.

う、これは、どこかで見たぞ。何だ、何だ、さっき参考にしていたこのページのエラーと同じだ。ディバイスの識別番号を選ばせている。良くわからないが新しい方にして次へ。

no memory region specified for loadable section '.ARM.exidx'
あー、また同じエラーだ。対処法が書いてある。これは楽だ。「ねむい」さんがオリジナルのようだ。

次は?_sbrk,_write,_close,_fstat,_isatty,_lseek,_readのundefinedエラー
これは、今度のソースにはsycalls.cが入っているので出ないはずだ。

 よーし、ビルドが出来た。いやあ、このページのとおりでビルドが通ってしまった。「枯れ井戸Scope」さん、ねむいさんありがとう。 

ねむいさんのバイナリでLAN基板動く!(5/17/2011)
 ビルドは通ったが、このソースにはUARTがついておらずLCDもKeilの評価用ボードについているLCD用ルーチンで、動作確認する術がない。これではもしLAN部分が動かなかった時、手がかりが全くない状態になる。

 迷った挙句、ねむいさんのソースもビルドしてみることにした。ディレクトリーチェーンの様子がわかってきてインストールできそうだ。このソースにはUARTがついているのでこちらを基本にしたい。ただRTOSのバージョンは6で、これがRTOS7.0.0で動くかどうかの不安要素はあるが、ストロベリーリナックスのI2C LCDも動くようになっているので心強い。

 たまたま、OSなしでも動くLPC2388の単独のUARTのバイナリコードが見つかったので、LPC系のシリアルライター、FlashMagicのテストを兼ねて、これを試しに書き込んでみた。よーし、USBを通したUARTが動いた。

S_p5173908

 そうか、ねむいさんのところにはソースだけでなく、バイナリも入っていた。MakeFileが整備されていないのでビルドは通らないが、これをロードすれば、とりあえずLAN基板の動作確認が出来るのではないか。用心のため、このソースについているI2C LCDの実装までしてしまう。これでハードは全く同じになった。

 動かなくて元々である。HEXファイルをFlash Magicで書き込む。200KB以上あるので時間がかかる。何とかエラーなしで書き込めた。いよいよ通電テストである。IPアドレスは何だっけ。ソースリストを確かめる(あとで、ねむいさんのブログの画面写真を拡大するとちゃんと出ていた。さすが、ぬかりがない)。

 このソースはLCDもUARTも別のUSBからのUARTも盛りだくさんの出力が出る。まずは、USBにつながっているUART0から。USBコネクターを差し、TeraTERMを立ち上げる。おおお、字化けはしているが何かメッセージが出始めた。速度を調整して、Tickの回数を示す出力が出た。よし、FreeRTOSそのものはうまく動いているようだ。

 さあ、本題のイーサネットだ。アクセスランプがついたりしてPHY層の部分は何か動いているようだが、上位プロトコルはまだ未検証である。緊張が走る。PCでDOS画面を出し、pingを打つ。

Ws000000

 やりました。やりました。1msでレスポンスが返ってきた。ICMPパケットは通った。さあ、次は、HTMLだ。はやる心を抑えてブラウザーにIPアドレスを打ち込む。おおおー、FreeRTOSのウェブ画面が出た!ひゃっほー、やったぞ。誤配線もなく完全試合だ。2秒ごとにタスクの統計を打ち出してくる。TCP/IPの画面も異常なし。

 いやあ、嬉しい。ねむいさんが折角作ってくれたストロベリーリナックスのI2C LCDは何故か全面LCD点灯(真っ黒)になってしまい調子が悪いが、それを除けばあとは問題ない。

 FreeRTOSの仕組みはまだ殆どわかっていない。まだ何をして良いのかわからない。しかし、とにかくDP83848を使ったLAN基板の100Mbpsのイーサネット・ハードは、これで完全に動いていることが確認された。

 I2C LCDが動かなかったのは別の原因だった。何と雑誌のピンアサインが間違っていたのだ。CN2の38ピンは3.3V、39ピンは5Vとの記載があるが(インターフェース2009年5月号ページ41)、実際はちょうど逆である。3.3Vのはずが、5Vもかかっている。電圧計で確認した。

 サイトには訂正記事が出ているが、こんなところまで調べきれない。電源の間違いは致命的だ。I2C LCDが始め全面点灯するのは過大電圧がかかっていたからだ。もう少しで壊すところだった。プンプン。 隣のピンに移して、I2C LCDも問題なく表示した。サーバーの入力ページから文字を入れてみたが、残念ながらこちらには反映されていないようだ。

ソースビルドに挑戦(5/18/2011)
 仕事から帰って、すぐ工作室にこもる。まだビルドがOKになっていない。動いたのはバイナリーファイルだ。これからこれをいじるのにソースから動かなければ意味がない。V7のソースはビルドできているが、こちらのソースはUARTなどがついておらず不便だ。

 バイナリで動いたねむいさんのソースをベースに開発を進めたいが、このソースはFreeRTOSのV6ベースのようで、V7で動くかどうかは定かではない。最初、試しにビルドしてみたが、makeで殆ど先に行かず門前払いだった。

 しかし、何故上手く行かないのか大分わかってきている。ねむいさんが注釈でFreeRTOSのデモディレクトリに入れるという意味が理解できて来た。要するに、FreeRTOSのソースのディレクトリの構成が、共通部分と機種依存に別れているので、MakeFileを環境に合わせれば良いのだ。バージョンの違いはなさそうだ。

 MakeFileを仔細に調べて、各所で参照しているディレクトリパスを修正していく。すこしづつディレクトリチェーンが合ってきてMakeの時間が増えてきた。おお、最後まで通ったようだ。HEXのファイルサイズを確かめる。少し大きいがほぼ同じサイズだ。これで動くかもしれない。

S_p5193931 ファームの書き込みも順調に済んだ。電源を入れる。よーし、ソースリストに自分が加えた変更どおりのメッセージがI2C LCDに出た。思わずガッツポーズが出る。すべて人さまのソースを借りてのビルドだけれど、これで一山越した。

  ついでにSpeedを示すLEDが点かない不具合を検証する。LEDの方向を間違えていた。これを訂正し、100Mbpsを示す赤のLEDがついた状態を記念撮影する。写真でぼやけたように映っているのは、ハンダ面が表になる基板なのでアクリルの保護カバーをつけたものである。

S_p5173900

 次の課題は、ChaNさんのFatFSの導入とWebからのI/O制御である。ただ、ちょっとみただけでは、FreeRTOSにコードを加える見当は全くつかない。WebServerのソースもファイルシステムを内部的に実現していて難しそうだ。RTCを動かしたいのだが、これもソースリストが余りにも簡単すぎて良くわからない。

 まだまだわからないことだらけだが、これでこのマイコンは、当研究所の4つめのネット対応のマイコンとなった。H8と違ってpingも1msで返って来るし、SDカードのファイルシステムが入れば、今度こそ本格的なWebサーバーに出来る。楽しみである。

FreeRTOSのUARTはいくつもあって(5/19/2011)
 興奮が納まって反省した。人のソースを頂いてそのままビルドし、動いた動いたと喜んでいるだけでは情けない(動くのは当たり前だ)。自分で喜んでいるだけならともかく、ブログで自慢するのも芸のない話だ。少しは創意工夫をした結果を載せるべきだろう。

 と、言うことで、UARTコンソールのための開発を始めることにした。ねむいさんのUARTはprintfを使っているが、入力はない。printfは、syscalls.cにUARTに出す自前の関数を入れて、あとはgccの標準入出力を使っているようだが、このあたりは一番わからないところで、scanfなどは敬遠したいところだ。

 他のサイトの情報によると、FreeRTOSにはUARTの実装にはいくつもの種類があるようで、難しそうだ。NXP社のサンプルソースにはUART関係だけでもserial.c と uart.c がある。バッファーが付いているUARTや、直にデータをとるUARTもある。OSのメッセージキューを使っている方法もある。良くわからない。後閑さんのFreeRTOSのガイドはとてもわかりやすいが、まだFreeRTOSの仕組みが理解できていないので、先に進めない。

 それでも、よくわからないまま、見よう見まねでサンプルソースのuart.cの中のUARTGetchを使って、コマンドプロンプトを出してみる。よーし、データの読み込みのためUARTが止まった。キーボードから一文字入れる。うむ、エコーバックされた。おやあ、ウェブの更新が止まったぞ。おかしい。何のためのOSだ。FreeRTOSはプリエンティティブなOSのはずだ。UARTタスクのループウエイトで他が止まったら困る。

 タスクの優先度が同じなのかもしれない。順位をuIPとUARTで大きく変えてみる(最初から同じではなかった)。しかし、状況は同じだ。OSがプリエンティティブに動いていないのかもしれない。でも調べ方がまだわからない。軽いOSとはいえ、設定するところは山ほどある。

 仕方がないので、これまでAVRでやっていたと同様に、バッファーにデータがあるかどうか調べる自前のUARTGetchar関数をでっち上げ、UARTでは止まらないようにした。

 これで、Webサーバーも動き、LEDも点滅し、UARTもデータを受け取るマルチタスク状態に戻った。しかし、これはUARTが必ず一定時間、vTaskDelay()というCPUを返す待ち時間があるためで、本来のUARTの動きではない。本当は、UARTの受信バッファーにデータの入ることをトリガーに待ち時間が解ける割り込み処理が必要なのだが、FreeRTOSではこれをどうやって実現するかはまだわからない。

 まあ、わからないことは沢山あるが、気ばかりあせっても仕方がない。少しづつ調べていくことにしよう。調べることが沢山あるということは幸せなことなのだ。

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