(日々の記録)デジタルオシロを買った。EEPROMの動作までSTM32とTFT液晶のグラフィック気圧計は、I2Cインターフェースの気圧センサーがひとまず動いて、未完成のハード機能は、測定データを蓄積する同じI2CのEEPROM(フラッシュROM)だけとなった。このハードが動けば気圧計に必要な機能はすべてが揃う。I2CシリアルEEPROMは、秋月で買ったMicroChip社の24FC256(¥90)である。1Mhzで動き32KB蓄積できる。チップそのものは、MPL115をつけるときに一緒に基板上に実装してしまってある。必要な作業は、このEEPROMのソフト開発だけである。I2Cインターフェース(マスター)は、既にMPL115のための関数群が開発済みでこれを利用できる。EEPROMそのものは、AVRで既に何度か経験しているので、最初は軽く動くだろうと嵩を括っていた。ところが、これがまともに動かないのである。ソースコードは、STマイクロの標準ライブラリを利用して、お手本どおりに書いている。間違えようの無いコードである。それなのにどうにも安定しない。この前の記事より、そろそろ20日が経とうとしている。何かにこだわりだすとそこから抜けられない性格が仇になって、先に進めなかったが、悪戦苦闘の末、数日前、ようやく想定どおり動くようになった。これでやっとブログに報告することができる(2012/4/13記)。センサーから出てきた気圧値の補正は大変だった(3/28/2012)I2Cインターフェースの気圧センサーMPL115A2は思ったより簡単に、データを吐き出してくれた。このセンサーは出てきた気圧と温度のデータを複雑な演算式で計算しないと求める気圧値がでてこない。大気圧はその場所の温度によって変わることは承知していたが、こんなに複雑な式で実際の気圧を求めているとは知らなかった。大気圧というものが、空気の密度であり、そもそもの天気図の低気圧、高気圧というものは、冷たい空気と暖かい空気の集団の振る舞いであることは理屈ではわかるが、気象観測上の気圧を、何の基準で決めて測っていくのかと言うことになると俄然難しくなる。このセンサーの補正係数というのは、気圧ADコンバーター(ADC)が出してくる値を温度で少し補正するだけのものと思っていたが調べてみると、そんななまやさしいことではなかった。第一、係数は負数で、ADCが出してくる数値は、気圧が高くなれば低くなるのだ。補正係数は、16ビットで6つもある(O-familyさんのサイトの邦文マニュアルがおすすめ)。係数は固定小数点で、出てきた数値を9ビットも右シフト(数値の1/512ということ)する係数もある。こんな係数は無視できるのかと思っていたらとんでもない。計算して入れ込まないと正しい気圧値にならない。係数値そのものは、どうもダイナミックに変化している様子はない。工場で出荷の時に個別の石に応じて補正値を入れているだけと見られる。マニュアルにもあるように、用意された係数の2つ、32ビットは、最初から0である。自分が入手した石だけが0かもしれないので、プログラムを汎用化するためにはここも計算してやらなければならないところだが、結果を早く見たいので、とりあえずこの2つの係数は無視して、コーディングを続ける(ウェブサイトで見る限り、この石の係数は全部0のようだ)。固定小数点の計算方法を勉強し直す。加減算は、小数点の位置を揃え、乗除算は、そのまま整数とみなして計算したあと、小数点をつけなおせば、普通に演算できることがわかった。有効数字に注意していないと正しい値を失う。いえば、O-Familyさんが、この気圧計を作っているときに、あれこれぼやいていたことを思い出す。こんな苦労しないと実際の気圧が求められないとは思っていなかった。途中でやめるわけにはいかない。データシートにある計算式をコーディングしては、電卓で計算し直して実際の値を確かめ先に進む。精度はみなさんと同じ5桁を目標とし、hPaの小数点一位までが有効数字となるようプログラムを作っていった。ロジックが殆ど出来上がった頃、インターフェースのI2Cのご機嫌が悪くなった。ADCからの気圧・温度と係数値を取り出すとき、別々なら問題ないのに連続してにデータをとるとおかしくなる。測定の間に待ち時間を入れたり、スタートコンディションを入れたりしてみたが、前のように解決しない。I2Cって、こんなに不安定なのか。いーえ、悪いのは全部私でした。連続したデータ取得の最後にNACKを送るため、ACKをdisableするコマンドを送ったまま次の通信を始めていた。通信の始めに、ACKをenableするコマンドを追加したら、ぴたっとこのトラブルは解消した。不安定だと決め付けたMPL115さん、I2Cさんごめんなさい。このあいだ係数値がおかしくなるのも実は、原因はこれだったのだ。あのときはI2Cインターフェースそのものを初期化していたので直ったように見えただけである。例の「STM32マイコン徹底入門」のI2Cの説明(P301あたり)にはこれが抜けている。ENABLE);で、ACKを有効に戻しておかなければならない。DISABLEにしてしまうと、スタートコンディション発行でもENABLEに戻らない。それはともかく、MPL115から、やっとそれらしい気圧がでるようになった。UARTで測定値を出しながら、ウェブの天気図と見比べる。当研究所の標高が45m(Google標高マップで確認)で、補正(高度差8mでおよそ1hPaとして5.6hPa)をするが、どうも出てくる値は低すぎる。ウェブを探し回って、気象庁の気圧データを見つけて驚いた。大手町(標高6m)の測定値と標高差で換算すると正しいのである。大手町の測定値と、天気図の公式海面気圧値とは、4hPa近い差がある。9hPa余りを足すと天気図にでている気圧値と等しくなる。わからないが、とにかく補正は上手く行ったようである。10秒ごとに気圧値を延々とUARTに出して暫く楽しむ。すんさんは数値が安定しないという話だったが、気圧、温度ともADC値の変動は±1から2の範囲である。大きくぶれることもない。これなら安定していると言えるではないか。ブレッドボードでなく、基板に実装したからかもしれない。フラッシュROMの読み書き関数を工夫する(4/2/2012)開発は、EEPROM(フラッシュROM)のソフト開発に移った。I2CはMPL115で既に動いている。いえ、今度のEEPROMは、わずかなデータの読み出ししかしていないMPL115と違って、沢山のデータを読み書きする。気圧計でのEEPROMの使い方は、これまでの温度ロガーや、ガイガーカウンターのログと全く同じである。測定レコードを次々にシーケンシャル(順次)に記録して行き、読み出しは最初のレコードから連続して読む使い方である。ランダムアクセスはまずやることはないだろう。それなら、AVRをはじめた頃に作った、標準入出力的なストリーム関数を、STM32で作ってやればよい。EEPROMのアクセスは普通のメモリと違って、ページというブロック単位でしか読み書きできないので、そのしかけが必要だと当初は考えていた。ところが、EEPROMのデータシートを見て間違いに気づいた。ディバイス固有のページ単位(24FC256では64バイト)でしか読み書きができないのは、連続したアクセスのときだけで、1バイト単位の読み書きなら、ページの途中の任意のアドレスからでも読み書きができることがわかった。それなら、1バイト単位の読み書き関数を作ってしまえば、即、ストリーム関数ができあがる。これは簡単だと思った瞬間、いつもの「へそ曲がり」がむくむくと起き上がった。バイト単位のアクセスがEEPROMで出来るからといって、1バイトごとにEEPROMにアクセスしていては、少しデータ量が多くなれば、とんでもない時間がかかってしまう。EEPROMは1回のI/Oオペレーションの後は、書き込みでは数ms、読み込みでも数百μsの待ち時間が必要になるからだ。今度のプロジェクトは速度のことは全く気にする必要がない。気圧データの収録はいくら早くても分単位である。そこは、それ、長年の経験で身についた職人気質が許さない。工夫すれば、何十倍も早いアクセスが出来るものを、それをしないで通り過ぎることは自尊心が大きく傷つくような気がする。今は必要なくても将来のために作っておくべきだろう。それに、いかにも1バイトづつアクセスしているようで、実は裏でバッファーのようなものに貯めておいて高速化するしかけは、創作心(ごころ)を、強く刺激する。このあいだから気になっているソフトウエア開発手法の丁度良い題材でもある。ページ単位に読み書きして1バイトづつ出し入れする関数を考えていたのだ。当初の路線どおり、少し難しい方法で、EEPROM関数を開発することにした(これが、このあと大変な目に逢うことになるとは知る由もないのだが)。Coding)について紹介してきた。http://gataro-avr-ken.cocolog-nifty.com/blog/2009/07/post-a68c.html(擬似コーディングの実例を載せて紹介)http://gataro-avr-ken.cocolog-nifty.com/blog/2009/11/xbee-dbc0.html(やりかたについて詳しく解説)http://gataro-avr-ken.cocolog-nifty.com/blog/2011/08/sparkfun-0d8b.html(凡人が規模の大きいプログラムを書ける手段として)電子工作の世界に入って、意外にもソフトウエア開発の苦手な人を数多く見つけ、こういう方のために、何かお役に立ちたいと考えた結果のことである。電子工作、特に組み込みコンピューターを使った工作の面白さは、無機質な部品を集めて命があるかのようなしかけを作るハード工作の面白さ(プラモデルのようなものでも)だけでなく、ソフトウエアによって自分が考えたとおりに、しかけを思うがままに動かす喜びにあると思っているが、これが他人のソースコードに頼っているだけでは、楽しみは半分しか味わえない。ソフトウエア開発は勿論、プログラミング能力以外に、さまざまな別の技術と経験を必要とする専門分野だが、少なくともプログラム開発力、ロジックや、アルゴリズムを作る能力養成には、擬似コーディングがその大きな手助けになると確信している。この話をまたする気になったのは最近の次の出来事がきっかけである。AVRの入門サイトでは有名なkumanさんの掲示板が一時閉鎖されたときのことである。心無い人の多重書き込みが閉鎖の直接の原因だとは思うが、実はそれまでに気になっていたことがある。この掲示板に初心者が、プログラムのソースコードをひんぱんに書き込み、それに対して色々な人が助言しているのだが、話がどうもかみあわない。問題なのが、すべてがソースコードレベルで議論が進んでいることだ。プログラムの議論なのだから極く当たり前のように思えるが、私に言わせるとこれはまずい。ソフトウエアの中で、最も論理性の高いソースコードは、また、最も人間にとって理解しにくい言語だからである。このレベルでプログラムについて議論することは、余程熟練した人でないとお互いの言おうとしていることが先方に伝わらず、おかしなことになる可能性が高い。これは、計算機言語を、機械語->アセンブラー->C言語->JAVA(FORTRANやCOBOL)->BASICという順番(最後の2つは議論があろうが)に並べてみると私の言っていることが少し理解されるかもしれない。高級言語という言い方もあるが、上の順番が、右に行けば行くほど人間に理解しやすい形になっている。この右端といえども一筋縄ではいかない。書いてあることを短時間で理解することが難しい。「プログラムは考えた通りには動かない。書いたとおりに動く」という格言が生まれるゆえんである。考えた通りにプログラムを作ろうと思うなら、自然言語、つまり普通の言葉で、完全に論理(ロジック)を確認してからコーディングに入れば良い。ところが、ソフトウエアの開発の経験が浅い人になればなるほど、ロジックをコンピューター言語で検証したがる。これが長年ソフトウエアを開発してきた所長の疑問である。コンピューターは簡単に言えば用意されたロジックをただひたすら忠実に実行する電気モーターのようなものである。何か知的な賢い判断をするように見えても、それはすべて中に入っているロジックがやるのであって、他に何か別の機械が入っているわけではない。OSなどとえらそうな名前で呼ばれているプログラムでも、全部ユーザープログラムと変わらないロジックのかたまりである。ロジックさえ間違いなければ言語は何であっても実現可能である。それなら日本語でこれからやろうとすることを全部説明できるのなら、プログラムは成功したのも同然なのだけど、どうも、このあたりが理解されていない。キャッシュ方式のEEPROM関数開発で大苦労(4/8/2012)そんな能書きを垂れながら、EEPROMのページをキャッシュして1バイトづつアクセスする関数を作り始めたのだが、これが難義した。擬似コーディングの講釈ばっかりで実際にやらなかった「とがめ」が出たのである。始めに白状してしまえば、今度の開発では、擬似コードを実際には真面目に作っていなかった(理由はあとで説明)。そのため、プログラムの完成まで、とんでもなく長期間を要した。あまりにも思ったとおりに動かないので、てっきりハードか、STマイクロのライブラリに原因があるのではと疑っていたが、わかってしまえば全部、こちらの単純なミスであることが判明した。いう開発はしてはいけないという見本のような開発だったが、恥を忍んで全部、正直に事実を述べ、今後の問題解決の題材にしていただこうと思う。このブログにも、このあたりをさぼって開発に苦労した話は何度も登場しているが、また同じことをやってしまったのである。擬似コードは書いたことは書いた。コンセプトレベルの擬似コーディングで細かいところまでつめなかった。弁解がましいがこのあたりのソフト開発は現役時代を含めて無数の経験がある。細かいところのロジックは頭の中にあるとばかり、いきなりソースコードを書き始めたのが敗因だった。基本ロジックそのものは簡単だ。EEPROMをアクセスするときに、実際にはページ単位にしか読み書きしないようにソフトを構成する。トップレベル、すなわちレコードを記録したり読み出したりするアプリケーションレベルは1バイト単位の読み書き関数でデータを扱う。裏で、そのデータの存在するページ全体をキャッシュ(単なるバッファー)に読み込み、同じページにあれば、それを返す(read)か、そのキャッシュに書き込み(write)、別のリクエストがあったときは、このキャッシュをEEPROMに書き戻し、リクエストされたページを読み込んで、何食わぬ顔でデータを返す(またはデータをキャッシュに書き込む)。ソフトの処理が終わるときには、必ずキャッシュを書き戻す(closeまたはsync)。ディスクファイルなどのアクセスでは定番の方法である。いうときの常道として関数を3階層に分ける。EEP_Openなどの、一文字入出力、ストリームの初期化などの関数で、次のレベルが、pg_init,pg_writeなどの、キャッシュページをハンドリングする中間関数、一番下のI2Cを直接動かすところが、I2C_1_の接頭辞をつけた、MPL115と共用しているI2Cの原始関数である(巻末のソースコード参照)。何が悪かったのかを検証する(4/10/2012)コンセプトは固まっている。擬似コーディングは、本来、これをさらに逐次的に細かくロジックをつめる。各関数の入出力と機能を整理して詳細に落としていく。ここが肝心なところなのだが、今回は、慣れていることもあって、殆ど省略してコーディングに入った。これがやっぱり結果として、大失敗だった。細かい顛末は煩雑になるので省略するとしてどんなところで落とし穴に落ちていたか、列挙してみる。・STM32がリトルエンディアンであることに気がつかなかった。構造体にmemcpyでデータを移し、これのHigherバイトとLowerバイトをEEPROMアドレスにして、でたらめなアドレスになっているのに暫く気がつかなかった(ロジアナを見て、びっくり仰天)。・EEPROMの最初のページはデータではなく、ストリームの始点と終点アドレス(リングバッファーなので移動していく)や、シグネチャー(ストリームが有効かどうかを識別する特定の文字)を入れるページにしていたが、ここのデータを正しくプログラムの変数に移していなかった。ファイルのクローズのとき。・グローバル構造体の名前が、別のローカル変数と重複していることに気づかず、全く違う変数になっていた。Cコンパイラーは、構造体名と変数名が重複していてもコンパイルエラーにしない。構造体名を使っているつもりが、重複したローカル変数の方になってしまっていることに気づかず、動作が完全に狂っていた。した誤りは、もし擬似コーディングを念入りにやっておけば、それぞれの各部の機能が明確になっているので間違いが初期の段階で見つかるのだが、この整理が出来ていないと、何となく動いたあと、どこに原因があるのか突き止めるのが非常に難しくなる。いやあ、時間がかかった。四六時中コーディングしていたわけではないが、コーディングを始めて安定してデータが出入りするのを確認するまで、ほぼ1週間かかった。C言語で200ステップ余り。たいした量でもないのに最近になく手間のかかった開発となった。途中で泥縄の擬似コーディングを書いたりして大騒ぎの結果、やっと安定してEEPROMにデータが入るようになった。UARTの書き込みコマンドでデータを次々に追記していき、別のコマンドで貯めこんだデータを一気に吐き出す。このストリーム関数は、リングバッファーになっているので、これを確かめなければならない。ヘッダーファイル(I2C_eeprom.h)で設定しているEEPROMの最大バイト数を、256バイトまで縮めてオーバーラップのテストをする。よーし、最初のデータから消されてデータが次々に入り、テストステートメントの開始カラム数が増えていく。