« 2015年7月12日 - 2015年7月18日 | トップページ | 2015年9月13日 - 2015年9月19日 »

2015年8月23日 - 2015年8月29日の1件の記事

2015年8月24日 (月)

電子工作迷走。超音波距離測定(HC-SR04)のI2C化が難しい

 今回もブログの更新が長い間滞った。正直なものでブログのアクセス回数は間違いなく記事更新間隔に反比例する。商売ではないので別に構わないのだが、アクセスが減っていくのを見ているのは余り気分が良くないものである。

 この間、電子工作をやっていなかったわけでもない。むしろ最近になく没頭しているのだが、結果が出ないので記事に出来ないだけである。先月、EdisonのPWM動作をオシロで確認して、実際にサーボを動かしたところまでは順調だったのだが、そのあとがいけない。

 思いつきで始めた全く別の工作にはまり、泥沼から抜け出すことができなくなった。少しづつ動いてはいるのだが、どうもすっきりした形にならない。その工作というのは、タイトルに書いた超音波測定ユニット(HC-SR04)のI2C化である。

 あとで詳しく説明するが、要するにAVRのGPIOでI2Cのスレーブ側を開発して、HC-SR04をEdisonでも動かそうという目論見である。気楽に作り始めて次々に落とし穴にはまって身動きが出来ないでいる。余り日をあけるわけにもいかないので、このあたりで、この一か月の経過をご報告しておこう。

EdisonのPWMで2つのサーボモーターが同時に動いた(7/25/2015)
 前回記事にあるように、mraaライブラリーを使ってEdisonのPWM出力波形をオシロで確認することはできた。次はこのPWM波形で実際にサーボモーターを動かすことである。パンとチルトの角度(0から180)の数字をシリアルコンソールから入力し、PWMパルスに変換する。コンソール入力ソフトは、通常のgetchar()とかfgets()などの標準関数を使う。01p7257211 ただ、Linux(UNIX)の標準入出力関数にはAVRなどと違ってキー入力を教えてくれる関数がない。これなしにコンソール入力コマンドを出すと、そこで処理が止まってしまうので具合が悪いのだが今回はコンソール入力をキーにパンやチルトをやるので問題はない。

 コーディングそのものはたいしたことはない。ただEdison内の開発環境は、まともなエディターもなく(あのviしかない)、リストの印刷もできない貧弱なものだが、紙に印刷してからデバッグするような大層なことはやらないし、ウェブなどで制御するのはまだ先の話だ。

 コンソール入力のフォーマットは、>角度1 角度2(enter)の入力で角度1のパン、角度2のチルトとなり、>角度3(ブランク)(enter) とやれば、角度3のパンだけ、>(ブランク)角度4(enter)とやれば、角度4のチルトだけということにする。

 テキスト入力(0から180までの数字キャラクタ)の解析(パース)は、入力の最後から文字を拾って行くというのが鉄則である。無駄がない。文字列の頭から解析しはじめると泥沼になる。擬似コーディングで何度もシミュレーションして仕上げる。こういうパズル的なコーディングは久しぶりなので楽しい。

 早速テストだ。いきなりPWMにつなぐのではなく、コンソール出力で2つの数字が間違いなくとりこめているかを確認する。2組の数字のときはやさしいが、一つだけの数字のときは、かえって面倒である。

 ふーむ、どうしても、後の組の数字が10倍になってしまう。何度もロジックを見直すが間違っていない。こうなったら、変数を片っ端から出力させてみる。いわゆるprintfデバッグである。

Edisonpwm  文字数を出してみて原因がすぐわかった。標準入力は、文字列の最後に必ず'¥0'(0X00)がついて文字数がひとつ多いのだ。ロジックでは無精してブランク以外を数字(0-9)とみなしていたので、これも数字とみなされ、あとが10倍されていた。やれやれプログラムは書いたようにしか動かない。

 コンソール出力に目指す数字が出たので、本番のPWM入力に進む。こちらは、浮動小数点なので単純に数式をいれるだけですむ。このあたりのCプログラミングはLinuxなのでとても楽である。整数しか使えないときは、変数を32ビットにしたり桁溢れに気を遣ったりする必要があるが、全く心配しないで気楽にコーディングできる。

 以前に開発したmraaライブラリーのテストプログラムのソースをこのコンソール入力のプログラムに足して、いよいよサーボのテストである。サーボモーターの動力はとりあえず手持ちのリチウム電池で独立させる(サーボの突入電流をさけるためEdisonの電源と共用しない)。

 EdisonのコンソールでCプログラムをスタートさせる。プロンプトがでて順調に立ち上がった。数字を入れる。ブルッとカメラが上下と左右に同時に動いた。念のため入れたオシロには、数ミリsec遅れて出る2つのPWMパルスが映っている。

 よーし、動いたぞ。片側だけの数字入力のテストもする。数字出力のテストがすんでいるので、それぞれカメラが、ピッ、ピッと左右上下に反応する。何でもないことだけれど無性に嬉しい。暫く夢中になってカメラを動かして楽しんだ。

(ここにパンチルトの数字をPWMに変えるCプログラムソースを置きます。何かの参考まで)
「pantilt.c」をダウンロード

Edisonで2台目のウェブカメラ完成(7/27/2015)
 サーボがコンソールから操作できるようになったので、勢いに乗って画像も出すようにしてみた。2台目のパンチルト式ウェブカメラである。カメラの操作はSSHで、画面はHTMLなので実用性はまだゼロだが、とりあえずでも動くことをこの目で見ておきたい。

 EdisonのコンソールをPC上で2つ立ち上げて、片側でウェブカメラを動かし、もう一方は、パンチルト操作のCプログラムを動かす。PCの方はブラウザーも立ち上げる。カメラは30万画素の操作キットのカメラだ。

 PCのウェブ画面で映像を確認する。よーし画面がでた。パンチルトのコンソールに戻って、コマンドを投入する。カメラは、ビビッという音と共に所定の方向に向いた。画像は一瞬見えなくなるが、納まると画像が見えて、別のシーンが映った。ははは、出来た、出来たぞ。大きく動かしたときはカメラを固定した蒲鉾の板を手で持っていないと、ひっくり返る。

 しかし、こんな手元でも、自分の手以外で画像が変わるのを見るのは感動である。片側数値だけ(パンチルトどちらか)の操作は、カメラが余り揺れないので画像の乱れは少なくて済む。コマンドの投入で目指した目標にカメラの画面が移動できるか遊ぶ(これは意外と難しい)。02p7257212

 いずれにしても、これで当研究所での2台目のパンチルト操作の出来るウェブカメラが実現した。これの具体的な用途はまだない。猫カメラはアクリルケースに入れないとこいつも攻撃される。でも、まあとにかく一段落である。

 残っていることは、カメラのパンチルト操作を画面の左右上下のバーでやれるようにすることだが、これは所長の不得意なウェブプログラミングになるのですぐには手が出ない。このあたりはアプリケーションによっても大きく仕様が変わるところなので簡単には先に進めない、とやりたくない理由を上げて自己を正当化する。

次の野望は超音波測定ユニットのI2C化(7/28/2015)
 実は前から企画しているプロジェクトがある。EdisonのI2Cシフターを買って来たころ思いついた企画だ。手元の部品箱には、超音波を使った距離測定ユニット(HC SR04)が使用されないまま、ころがっている。一年ほど前にはやったことがあって当研究所も買ってあった。

 昔から秋月あたりで売っている、Parallax社のものと形態がそっくりで、価格がべらぼうに安い。確か通常の電子部品店ではなく、アマゾンなどから売り出され、最近は秋月やスイッチサイエンスなどの電子部品ショップでも売り出した。 Hc_sr04

 どうも中華コピー品らしいが、動作報告がウェブに出るようになり、それほど評判も悪くなさそうだ。なにしろ、オリジナルは¥3000近くしているものが、¥400近辺である。当研究所にいつもコメントを寄せてくれる「ばんと」さんも以前動作報告されている。結構、精度も高いようだ。

 このユニットのインターフェースは5Vでアナログなので、EdisonやRaspiとは少々相性が悪い。このSR04を動かすArduinoのシールドがあるようだが、こちらは今さらArduinoをやるつもりはない。それにEdisonのArduinoボードは大分お高い。

 AVRか何かでI2CでSR04の測定の開始や、測定した距離値をディジタルで送るようにしてやれば、使い道が広がるのではないかと思う。やることは、I2CスレーブのインタフェースをAVR Tinyの8ピンシリーズで実現することである。

 I2Cスレーブと言えば昔々、AVRを始めたころUSIインターフェースのライブラリを開発しているが、これはAtmel社のサンプルソースをほぼ忠実になぞったものでオリジナリティに欠ける。しかも8ピンのAVRはUSIを持っていない。GPIOで作れば今度のやつはスクラッチなので大威張りだ。

まずはSR04距離測定ユニットのテスト。すんなり動いた(7/30/2015)
 おりしも時を同じくして、電子パーツショップのスイッチサイエンスのページに、件(くだん)のSR04のなかに不具合があるという記事が出た。問題があるという新製品の写真を子細に調べると、これはまさしくアマゾンで買った製品そのもので、秋月のものも写真で見た限りは同じである。もし、スイッチサイエンスのページのとおりだとしたら大問題である。

 I2Cの前にここの実装を先にやって、本当に動かないのかどうか確かめてみることにした。I2CレシーブはTiny8ピンシリーズで実装するつもりである。これも時期を合わせたかのように、秋月でTiny85が妥当な価格で売り出された。

 こちらには以前、DigiKeyで送料無料にするゲタのひとつとして、Tiny85をいくつか買ってある(¥200前後だったか)。しかし、これでいきなり開発するのはちょっとハードルが高い。というので、ちょうど手元にあったTiny861で実装にとりかかる。

 このあいだサーボモーターの実験をしたTiny861一式がブレッドボードに残っている。USIを使ったUARTが入っており手間が省ける。割り込みも使わない、タイマーひとつだけで、やってきたパルスの時間を測るだけである。大した時間もかからずにプログラムが出来た。早速テストに入る。

05p7307215_2  結果としては、不具合は全くなく、問題なしに2cmから3.5m近くまで計測が出来た。確かに、3.5mあたりを超えると、Echoパルスが戻らずラインが1になったままになるが、リセットしなければ元に戻らないわけではない。再度トリガーパルスをかければ復帰する。特に問題もない。

 とりたてて大げさに言うような不具合ではなさそうだ。スイッチサイエンスの使っているArduinoのプログラムなどでうまく動いていない可能性が疑われる。タイムアウトを設け、ある程度の時間、パルスが戻らなければ待つのをやめれば済む話である(当プログラムでもそうした)。

04p7307214_2 SR04の動きは確認できたので、いよいよソフトI2Cスレーブの開発に移ることにする。とりあえずは、このTiny861のセットに追加して実験し、うまく動けば、秋月で販売が始まったTiny85あたりで実装することにする。

GPIOのソフトI2Cスレーブプログラムの擬似コーディングは終了(8/8/2015)
 GPIOによるI2Cスレーブの自作はI2Cマスターと違って、そう簡単ではない。最初に触れたように、当研究所では発足当初の7年前、Tiny26でI2Cスレーブを開発したが、これはUSIインターフェース(Universal Serial Interface)というハードを使って、それもソースコードはAtmelのアプリケーションノートを丸ごと参考にして作ったものだ(デッドコピーではないが)。

 今度は、USIのないTiny8ピンシリーズが目標なので、これを利用することは出来ない。あらためてソースを引っ張り出して調べる。割り込み駆動とUSIの機構を駆使した複雑な形だ。GPIO化にあたって重要な課題が見つかった。USIはI2Cのスタート/ストップコンディションを検知するハードを持っているのに、GPIOではこれも手作りする必要がある。

 I2Cスレーブはあくまでも受け身の立場なので、通信ラインのSDA(データ)とSCL(クロック)は、データの採集や、収集とは別に、独立して割り込みをハンドリングしていないと、スタートコンディションなどは採集することはできない。

 あわててTiny8ピンシリーズの割り込みラインを調べた。良かった。外部割込みはひとつだけだが、ピンチェンジが使える。何とか、この石でも2本の割り込みラインは確保できそうだ(デバッグ用のUARTは出力専用になるが)。

 Tiny861は外部割込み2本とピンチェンジ1本があるが、外部割込みは割り込み条件が2本で共通なのは注意しておかないといけない(実は開発途上で発見してえらい目に会った)。リソースの確認をして、いよいよ擬似コーディングにとりかかった。結構、時間がかかる。

 SCL(クロック)のパルスで駆動するのがI2Cレシーブの基本なので、どうしても割り込み関数の中が重くなる。C言語では、レジスターの退避などでタスクスイッチが重くなる。しかし、これはやむを得ない。結局、ソースは、USIを使ったAtmelのアプリケーションノートのと同じような構造になってしまった。

 スタートコンディションを検知するルーチン(SDAのピンチェンジ)が悩ましい。このあたりは割り込みが重なり、オーバーヘッドが気になる。理屈ではこれで良いのだが、果たして考えたように動いてくれるか。3日近くかけて、擬似コードは何とか出来上がった。

ISP-UARTでつまづいている(8/10/2015)
 I2Cスレーブの開発が一段落したので、実際のコーディングの前に、I2Cマスターのドライバーの制作に取り掛かった。I2Cマスターは、これまでに沢山開発しており、参考にするソースは選ぶのに迷うくらいだ。

 秋月のRTC(リアルタイマークロック)を動かした7年以上前のソフト(記念すべき最初の作品、温度ロガー)から、最近のLPCMプレーヤーのI2CインターフェースのLCDドライバーまで沢山ある。一番古いが、みなさんに使ってもらっているRTCを動かしたUSIを使ったI2Cマスターが信頼性が高そうなのでこれを選ぶ。

 そういえば、このソースは、ISPケーブルで動くUARTが不調で、別のGPIOのUARTを使っている。そうだ、ちょうど良い機会なので、このデバッグもしてしまおう。UARTをISP-UARTで動くよう換装する。

 これが、つまづきの始まりだった。やっぱりまた動かないのである。オシロで波形を見てみると、PCからのキー入力は入ってきているが、受信が全く動かない。何かループをしている感じである。デバッガーを入れれば何かつかめるのかもしれないが、この程度でデバッガーをセットアップするのも難儀な話である。ロジアナも、こういう暴走状態の時はあまり役に立たない。

 あーでもない、こーでもないとソースコードを調べるうち、遂に問題点を発見した。発見してしまえば何故こんな簡単な間違いに気が付かなかったのかと思うぐらい、馬鹿馬鹿しいミスなのだが、バグと言うのはこういうものである。ピンチェンジの受信割り込みで、ピンが立下りかどうかを調べるIF文の条件が間違っていた。これまでに何回も経験したつまらないミスだ。

 ポートの1ビットをテストするときによくやる方法で、PORT & (1<<Pin番号)の結果を1で比較していたというお粗末である。最初のコードは、たまたまPin番号が0だったのでこれで動いていたのだが、ピンが移動していたのでバグになってしまったようだ。

こんどは、USIを使った前のI2Cマスターが動かない(8/12/2015)
 ようやっとUARTが動いて、I2Cマスターの動作確認に入る。スレーブはまだ出来ていない。出来たとしてもいきなりつなぐのは無謀だ。当然、確認はオシロで波形を調べることになる。ところが信頼性の高いと思われたUSIのI2Cマスターが思うような動きをしてくれない。

 最初は良いのだが、2回目になると全く波形が出なくなる。電源を切って、再度投入すると、1回目は動くがやはり次から駄目になる。頭を抱える。一難去ってまた一難である。単なるドライバーのつもりのI2Cマスターのデバッグをこれからまたやらされる羽目になった。

 どうもI2Cラインのハード設定がおかしいようである。I2Cのラインインターフェースはとても微妙に出来ていて、単純なポートのオンオフではなく、入出力方向の切替で行う。ラインをハイインピーダンス化する形でラインを上げるようだが、どうもこのあたりの設定がおかしい。

 暫く迷ったが、これ以上USIのI2Cマスターのデバッグをする気力がどうにも生まれない。I2Cのマスターなら、まだ他にも手段がある。GPIOのもあるし、いざとなったらMegaシリーズのハードのI2Cを使うことも出来る。マスターの開発をしているわけではないのだ。

GPIOのI2Cマスターは簡単に動き接続テストに移る(8/13/2015)
 お盆が近づいて事務所もお休みである。猛暑なので外に出かける気も起きない。このところ熱心に通っているジム(ヨガとプール)もお盆休みである。必然として、工作室にこもる時間が増える。日がな一日、電子工作で過ごすが、進展はいまいち順調ではない。

 潔くあきらめたUSIのI2Cマスターに替わってGPIO を使ったものにライブラリをとりかえる。このオリジナルは、ChaN氏のソースで、LPCMプレーヤーのミニLCD(I2C)に使っている。さすがはChaN氏制作のソースである。はなから全く問題なく動いた。

 綺麗にオシロにI2Cマスターの一連の波形が並んだ。始めからこれにしておけばよかった。I2Cのマスター書き込み宣言は、スレーブアドレスがないということで戻ってきているが、これはスレーブ側が動いていないので問題ない。

 いよいよ問題のスレーブのコーディングである。久しぶりに心が踊る。擬似コーディングから実際のソースに落としていく。SCLの両エッジの割り込みを受けて、ステートマシンでビットデータをハンドリングしていくのだが、スタートコンディションの検知と、ACK/NACKを伝えるステージのタイミングが難しい。実際に動くことを祈って、せっせとコーディング。ほどなく出来上がった。 07p8247217

スレーブは、はなからロジアナの力を借りる。(8/14/2015)
 ISP-UARTのトラブルにこりて、ここは最初からロジアナでデバッグを始めることにしている。割り込み駆動のプログラムのデバッグは普通のprintfデバッグでは出来ない。マイクロセカンドオーダーで動く割り込みルーチンに、printfなどのミリセカンドレベルのルーチンを入れたらデバッグにも何もならなくなる。

 久しぶりのロジアナである。あらかじめGPIOピンで沢山のプローブ点を入れ、逐一、ステートマシンの進行を追うことにする。各ブロックの入り口と出口にプルーブピンをいれて、これをロジアナで観察する。これまでのところ最強の解析手段である。

 ブレッドボードに2つのCPUを乗せ、スレーブ側、マスター側それぞれ1つづつUARTを立ち上げ、ついでにSR04も一緒に実装して、最終的には距離がマスター側のUARTに出力されるようにセットした。上には賑やかにロジアナのジャンパーコードが盛り上がる。

 スレーブ側はSR04の測定システムを兼ねており、スレーブのPCコンソールのリターンキーを押せば、距離値が出てくる。マスター側からの指示で、スレーブ側のHR04にトリガーがかかり、測定した距離値がマスター側のUARTに出てくるというのが、当面の最終ゴールである。

 ただ開発の手段が少しわずらわしい。マスター側のUARTをISP-UARTにしたので、スレーブ側のコンパイルの度に、ISPケーブルを抜き差ししなければならない。それでも準備が整って、いよいよマスター側のコマンドでスレーブが動くかどうかを試すところまできた。ドキドキの瞬間である。

驚くべき事実が判明。外部割込みがLowレベルでしか動かない(8/15/2015)
 コマンドを入れる。残念、スレーブアドレスがないというすげないメッセージである。これは想定したことで、むしろ、ロジアナの波形の解析が重要だ。すぐにロジアナの設定にとりかかる。

 ロジアナをスタートさせて、再度コマンドを打つ。出た出た。ロジアナから色とりどりの波形が出た。このロジアナにはI2Cのプロトコルアナライザーがあるので、まず、それの確認だ。いくつか設定して、無事、オシロで見ていたのと同じ波形が出ていることを確認した。

 しかし、GPIOピンで発生させたロジアナの波形(事実)は目を疑うものだった。まるででたらめに割り込みが起きているとしか思えない。SDAに設定したINT0とSCLのINT1の外部割込みの出方が考えた通りのところで全く起きていないのだ。I2c861

 やっぱり懸念した通り、オーバーヘッドが大きくて、割り込みがまともに動いていない。いくつかの割り込みは無視されている。特にSDAの割り込みは両エッジで動かなければいけないので、この間のSCL割り込みが見えなく、このままでは全くデータ収集(送出)ができない。それにしても、割り込みの起き方が不審である。エッジと一致していない。

 良くわからないので、まずは割り込みをひとつだけ(SCLの動き)にしてみる。おやあ、割り込みの出方が変わった。外部割込みは、立ち上がり、立下り、両端など発生条件を選べる。変えてみるが、全く変化がない。調べているうちに、これはLowレベル割り込みではないかと思われる状況だ。

 INT0(SCL)からINT1(SDA)に替えてみる。やっぱりそうだ。SDAはSCLに比べて変化が少ないのでそれがよけいはっきりわかる。だれかが割り込み条件を設定するMCUSRをいじったかもしれないので、プログラムのループの始まる直前でMCUSRそのものを出力してみたが、正しい設定になっている。

 それでも割り込みの起きる状態は、設定レジスターが00のLowレベル割り込みの設定と考えると合った動きをしている。I2Cのピンで使うと割り込みは、正しく設定できないのだろうか。まさか。

お馬鹿なミス。MCUSRとMCUCRを間違えていた。2日間の無駄(8/17/2015)
 Tiny861が悪いのか、それともこのチップだけが悪いのか疑って、手持ちと交換したが同じ(当たり前か)。ウェブにそれらしいエラータ情報はない。こんな基本的なバグが市販のチップにあるわけがないと思うのだが、現実には、全く想定した割り込みになっていない。

 どこも悪くないので、今度もCPUの種類を換えてみることにした。Tiny861からTiny2313に換装しようとソースを見ていた時である。861と2313とは外部割り込みの設定レジスター名は殆ど同じだが、念のため割り込み設定レジスターMCUSRを見てみたら、2313ではMCUCRになっている。

 えー、ここだけ違うのと861のデータシートを見たら同じMCUCR!! えっえっえー、ソースを見る。確かにMCUSRだ。何というお馬鹿な話だ。たちの悪いことに、MCUSRというレジスターは、861に存在し、コンパイルエラーにはならない。全く無関係のレジスターに設定値をえんこらえんこらセットして動かない、動かないと騒いでいたわけだ。情けなさに冷や汗が出る。

 気を取り直して、レジスター名をMCUCRに替えてテストする。ちゃんと割り込みは、立下りや立ち上がりで起きるようになった。これでスレーブの動きが少しづつさまになってきた。しかし、基本的には、危惧した通り、割り込みルーチンにはいってくる時期が完全に遅れている。特に、2つの割り込み(SDAとSCL)が被るときは、どちらかの割り込みが無視され、正しくデータを受け取れない。

 本来なら、これであきらめるべきなのだろうが、しつこいことでは人に負けない性格である。何とか、動かしてやろうという闘争心が盛り上がった。スレーブのGPIO版がウェブ上にないのが良くわかった。余程クロックが早くなければ、この割り込みのオーバーヘッドでまともな処理はできないのだ。

 現象を色々考え、スタートコンディションを検知する割り込みルーチン(SDAの両エッジで起きる)をシーケンスの最初だけにし、一旦スタートコンディションを受けたら、割り込みを止め、SCLだけの割り込みルーチンだけで動かすことにした。I2c861_8_21

 こうすると、ストップコンディションは受けることが出来ない。ただ、これはスレーブにとってはそう致命的な状況にはならない。SR04のデータ送出位なら、一連のシーケンスが終わったあと、タイムアウトをとって送信終了とすれば済む話で、受信ならマスターからNACKが来る。

タイムアウト機能を入れてやっと送受信ができた(8/21/2015)
 I2Cインターフェースのデータビットの最後のACK/NACKを返す方法が難しかったが、何とか、1バイトのマスターからスレーブへの送信(これはコマンド用)と、2バイトのスレーブからマスターへのデータ転送が動くようになった。

 どちらもまだタイムアウトを頼りにする送受信(一秒タイムアウト)なので実用には乏しい。それでも目指すSR04のI2C化にだいぶ近づいた。ブログを整理してみると、もう6ページ以上の量になった。このあたりで記事にしておこう。  
(ソースコード等は、もう少しまともなものになるまでお待ちください) 

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

« 2015年7月12日 - 2015年7月18日 | トップページ | 2015年9月13日 - 2015年9月19日 »