« 2017年7月 | トップページ | 2017年11月 »

2017年9月の2件の記事

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)

2017年9月 2日 (土)

ソフトI2Cはクロックストレッチまで手を出してあえなく沈没

 I2Cのスレーブライブラリの改訂が泥沼に入り込んだまま動けない。アナログの超音波距離計測センサーHC-SR04(以下SR04)を、電源3.3V、I2C化したブレークアウト基板を作ったところまでは良かったが、そのあと、計測を高速で動かそうとして、ソフトI2Cスレーブのプログラミングに大苦労し、気が付いたら一か月が経っていた。

 そもそも、このTiny85を使ったソフトI2Cのライブラリーは、もう2年も前に開発したもので、ソースコードまで公開している。今度の開発でいくつかの不具合が見つかったので、改訂版を出せばそれですむ話なのだが、せっかく再公開するのだから、もう少し機能を増やしておこうと欲を出したのが間違いの始まりだった。Dsc01170

 ソースコードをレビューしているうち、最初の版は、I2Cのマスター読み込みのストップコンディション検知が未実装だったことがわかった。ブログ記事では作ったことになっているが、ソースコードを読んでみると実装されていない。

 スレーブからマスターへの送信(マスター読み込み)の終了は、最後のデータバイトにマスターがNACK応答を返せば止まるので、ストップコンディションは不要と言えば不要である。しかし出来ていると書いてしまった手前、放置するわけにもいかなくなった。それにマスター書き込みの方は、リピートスタートまで(もちろんストップも)実装したのに、何となく落ち着きが悪い。

 そこで、これを追加しようと気楽にコードを加えたのだが、これがうまくいかない。出来上がったプログラムはどうやってもストップコンディションを受け付けないのである。マスターはストップコンディションを出しているはずなのにロジアナで見ると波形が出ていない。謎である。

 脱線につぐ脱線というのが、がた老AVR研究所の通例ではあるが、今回はどうもいけない。迷路に入りすぎだ。I2Cのスレーブ側のプログラムは、すべて割り込み駆動で、SR04を制御するバックグラウンドルーチンとは非同期で同時に動く。デバッグは一筋縄では行かない。

 こういう細かいことに興味のない読者の方々には申し訳ないが、実は所長は、こうした箱庭のような8ビットマイコンのマルチタスク的プログラミングが好きで、つい深入りしてしまう。ロジックアナライザーで全体の様子が手に取るように把握できるようになったので余計やめられなくなっている。

 以下は、この泥沼のようなプログラム開発の一か月の顛末である。しかも今回は撤退に次ぐ撤退の苦しい開発となった。まあ、こういうときもあると自分を慰めている。

懸案のI2C通信と計測の同時処理はあきらめる(8/6/2017)
 前回記事で、SR04のエコーと、I2C通信の共通変数(タイマーキャリーカウンター)を分離して、正しいデータが出たと喜んだのもつかの間、少し待ち時間を短くしていくとまたデータが不正確になってきた。どうも距離の計測値が低すぎるし、安定した値がでてこない。

 ソースコードをもう一度入念に調べる。すると、共用していないはずのタイマールーチンに続々とおかしな使い方が見えてきた。要するに被っていたのはキャリー変数だけではなかったのである。

 まず、肝心のタイマーのおおもとのレジスターTCNT0をI2Cが始まるたびに0に戻していた。最大でも0.26ms(8ビットのオーバーフロー)、音速にして5センチ余りの誤差は、今度の用途(人感センサー)を考えると、そう大きな問題ではないが正確でなくなることには間違いない。Ws000029 さらに、I2Cの通信が終わるたびに、タイマーのオーバーフロー割り込みを止めてしまっていることが判明した。タイマーそのものは動いていても、8ビットタイマーのキャリーが増えていかないのは致命的だ。これが、ときどき距離が不正確になる犯人であった。

 この一週間、デバッグに奔走していた。調べれば調べるほど、エコー時間計測とI2C通信の非同期処理は不可能であることがわかってきた。要するにタイマーが一つしかないところで、タイムアウトと計測を一緒にやろうというのが無理筋だったのである。

 とにかく、こんなにリソースを共用していたらマルチタスクもあったものではない。計測期間中にI2C通信を行うソフトの開発は中止することにした。今回の無駄足は疲労感が強い。もっと早く気が付くべき基本のところを忘れて、深入りしてしまったからだ。 

 当初の方式、センサーのエコーを少し長めに待ち(SR04の最大エコー30ms程度)、データを読み込む方式に戻した。これでも100ms以内で連続して計測が出来る。まあ、人感センサーならこの程度で十分だろう。

マスター読み込みのストップコンディション検知にチャレンジ(8/16/2017)
 エコー期間中にI2Cを動かして計測間隔を短縮しようという目論見は完全にはずれた。意気込んでプログラミングをしようと振り上げた拳(こぶし)の行き先がない。この高ぶった気分を鎮めるために、何か手を動かさないと納まりがつかなくなってしまった。

 そこで、未実装の「マスター読み込み」シーケンスのストップコンディションの検知機能を開発することにした。しかし、これも難儀したのである。しかけは、「マスター書き込み」と同じように最初のデータビットのクロック(SCL)立ち上がり区間で、データライン(SDA)を監視して、ストップコンディションを判定する。

 「マスター書き込み」では、スタートコンディション(これはリピートスタート)もストップコンディションも順調に検知し、動作も確認している。簡単に動くだろうと気楽にコーディングしたのだが、全く動かない。かえって、全体の動作が不安定になってしまった。Dsc01195

 いつものロジックアナライザーにプローブ点を増やして解析する。波形を見ると、マスターがストップコンディションを実行しているのにSDAが上がっていない。つまりストップコンディションが発生していない状態であることが分かった。謎である。

 メモに何枚もタイミングチャートを描いて調べていく。ひとつづつの動きを詳細に確認していくと、驚くべき事実が浮かび上がってきた。「マスター書き込み」なら容易に出来る処理は、「マスター読み込み」では不可能ではないかということである。

ソフトでストップコンディションは作れない(8/20/2017)
 誤解を招かないように補足すれば、ソフトウエアでは実現できないということである。ハードウエアならSCLがHIGHのときのSDAの動きを監視する機構を加えれば良いので問題はない。しかし、ソフトでは同時処理が出来ないので、「マスター読み込み」ではSCLクロックが来る前に、スレーブは次に送るべきビット値を用意してSDAに乗せておかなければならない。

 もし、その値がLOW(0)のときは、ストップコンディションを阻止してしまい、スレーブ側ではそれを検知できないということになる。そもそも、「マスター読み込み」はスレーブ側がSDAラインを上下させてデータをマスターに送る。スレーブがSDAを下げてしまうと、マスターはこれをHIGHにすることが出来ない。

 元から不可能な機能を作ろうと、もがいていたことになる。ack/nackビットを受け取って次のデータの第一ビットの間中SDAラインをスレーブは占有してしまうので、スタート/ストップコンディションをマスターが出せないのである。「マスター書き込み」では、SDAラインはマスターが制御するので問題ない。

 とにかく、「マスター読み込み」のとき、ソフトでスレーブ側のストップコンディション検知は実装できないことが明らかになった。この2週間余りの作業は全く無意味なものになった。お馬鹿としか言いようがない。エコー期間のI2C騒動と、このストップコンディション開発を撤退するにあたり、反省をこめて何が悪かったのかまとめてみる。

教訓:

  •  一度に複数の機能をデバッグしてはいけない。どれが原因か特定するのが難しい(欲張るな)。プログラムをなめてかかって、いくつかの機能をまとめてコーディングしていた。原因が見つけにくいうえに、単独でバグをつぶしていくより、結果として余計な時間がかかっている。
  • 基本的な機能から確認していかないと何をやっているのかわからなくなる(急がば回れ)原理を理解せずに、小手先のコーディングが多すぎた。タイミングチャートをいくら詳しく描いて、コードを工夫しても、I2Cの基本を忘れていたら何にもならない。
  • 思い込みは可能な限り見直す(これは定数だから変わらないと思い込むな)わかっているはずなのに、この思い込みというやつは魔物である。すべてを疑うという視点をなくさないようにと気を配っていても、つい先のことに気を取られて基本部分を忘れがちになる。

 やれやれ、現役時代、さんざん後輩に言い聞かせてきた原則ばかりである。このブログにも偉そうに何度も同じようなことを書いてきた。それがこのざまである。お恥ずかしい。まさしくプログラムは考えたようには動かない、書いたようにしか動かない、である。

 この記事はあとから書いているので、まだ冷静な話になっているが、デバッグの最中は暗中模索、ロジアナの前で悪戦苦闘であった。もうやめようかと何度も思った。Tiny85のファーム書き換えはSR04を外す必要があり、ビルドを繰り返しているうち、ブレッドボードの接触不良で動作が不安定になったりする。散々である。

 ただ、ロジアナの威力は今さらながらすごいと感動した。プローブの数を増やしていけば、色々な所の可視化が可能になる。今回も、マスター側からもプローブを出せることに気づいたのは大きかった。これによって、マスター側が正常に動いていることを確認でき、自分の愚かな思い込みに気づくことが出来たからだ。

超音波センサーユニットの配線図とライブラリソースコードの再公開(8/19/2017)
 とにかく開発は一段落した。ライブラリーも安定して、これまでのように時々、0値が連続したり、異常値が出たりすることはなくなった。思い当たる不具合は、これまでのところ全部つぶした。

 最後までわからなかった偶に起きるデータ異常値は、ロジアナの波形を精密に調べなおして、SCL割り込みがタイマー割り込み(オーバーフロー)によって遅れ、その結果、SCLクロックが外れているのを発見した。I2cslaveoverhead

 タイミングによっては、これがスタート/ストップコンディションとみなされ、そのあとのデータが欠落する。Tiny85のCPUクロックは8MHzで、内蔵CRクロックなのでこれ以上は上げられない。SCL割り込みのオーバーヘッドは8μs内外で、クロックが40Khz(半サイクル12.5μs)のI2Cでは、ギリギリである。

 I2Cはこれ以上遅くしたくないので、タイムアウト用のタイマーの割り込みを起こさないようにするしかない。プリスケールを8から64に上げ、メッセージが16文字以内なら割り込みが起きないようにした。テストの結果、20文字以上送っても、数値の乱れはなくなった(必ず起きるわけではない)。

 お約束のソースコードと配線図を掲載する。I2Cライブラリーそのものは上記のようにすべて改善に失敗したが、マスター側とSR04制御部には若干の機能を追加してある。
 以下に例によってZIPで固めたソースライブラリ、readme.txt、配線図ファイル(.bs3)を置きます。 
   「I2CslaveSet_2.zip」をダウンロード

 コマンドの解説など詳しくはreadme.txtに説明した。以前の公開したライブラリーはうまく動かないところもあるので、この版のライブラリを使われることをお勧めする(当該ブログ記事にも注記)。

I2CスレーブライブラリーとSR04測定ルーチンの改善点(8/21/2017)
 テスト用の親機のTiny861とのペアで追加した機能と、修正した不具合の部分を以下にまとめておく。

I2cslave_85_2

追加した機能:
(1)    連続距離測定
SR04の距離測定が0.2秒間隔で開始され、UARTコンソールに測定値が1行づつ表示される。人感センサーなどで機器を動かしながら最適位置を見つけるのに便利。リターンキーを押すことで測定は中止される。

(2)    スレーブ側からの独自データ送出
サンプルとして、スレーブ側のソフトのバージョン番号をフラッシュに定義し、それを表示させる。I2C化するディバイスのレジスター値などを表示するのに使える。

(3)    バッファーデータの16進データ表示
スレーブの持つ送受信バッファー(36バイト)を全部表示する。本来はデバッグ用。

修正した不具合
(1)    マスター読み込みでのスレーブ側の不正データ送信のバグ修正
タイマー割り込みのオーバーヘッドで、送ったデータが欠落したり、文字化けが起きていたのを修正。

(2)    スレーブ側の連続データ送出でのバグ修正
マスターから送ったデータの戻しでバッファーサイズ分を全部表示できなかったのを修正。

 いやいや、長い間かかったけれど、何とか納まった。ウェブで見る限り、AVRマイコンでソフトI2Cスレーブの開発例は殆どない。あっても、すべてUSIというAVR独自のハードインターフェースを使ったもので、この開発のようにGPIOだけで作った例はひとつもない(自分で探した限り)。

 ちょっと鼻が高いのだが、これは完全な自己満足の世界である。ということで、さらに挑戦しようと、欲を出して恥の上塗りをすることになった。それが、次に紹介するクロックストレッチの実装である。まあ、笑わずに聞いてください。

クロックストレッチ実装の野望、もろくも砕ける(8/23/2017)
 I2Cは、原則、すべての通信の主導権をマスターが握っており、マスターの出すクロック(SCL)に従って複数のスレーブが動作する。これに対し、クロックストレッチとは、I2C通信でスレーブが、マスターを待たせる唯一の手段である。

 スレーブの処理速度が遅く、マスターの要求に応えられないときなどに使う。SCLラインをいずれかのスレーブがLOW(0)に落とすことによって、マスターはSCLの発行を止め、スレーブが解放(HIGH)にすることで再開する。
実装は簡単に見える。マスター側で、クロックライン(SCL)を監視していて、誰か(スレーブの誰か)がSCLをLOW(0)に落とせば(いわゆるワイアードNOR)、それがHIGHになるまで通信を止める(クロックパルスを延期する)。

 スレーブ側はもっと簡単である。必要な時にSCLをLOWにしてしまえば、すべての交信はストップする。また、HIGHに戻せば、マスター側が待っているから再開する(はず)。鼻歌交じりで、双方をコーディングし、勇んでテストに入った。これが地獄の始まりとは露知らず。

 テストの前は、クロックストレッチが実際に行われているかの実測のため、タイマーを再整備したりして余裕だったのだが、実際には全く動かなかった。スレーブでクロックストレッチをかけた途端、すべての反応は止まる。ハングアップである。

 ロジアナで様子を見る。通信シーケンスは、スレーブがクロックストレッチをかけたとみられるところから、見事にSCLがLOWになったまま、最後まで動かない。あれ、おかしい。マスターが待つのはともかく、スレーブがどうして引きすられる?

 スレーブには1秒タイムアウトが設けてあって、I2Cの通信が1秒以内に終わらないと通信を終了させて初期状態にもどるようになっているはずだ。

 あっあっあー、だめだ。スレーブ側の割り込みプログラムのなかで、SCLがHIGHになるのを待っている。これではバックグラウンドのコマンドでいくらSCLを元に戻そうにも、永遠にバックグラウンド側に戻ってこない。いわゆるデッドロックである。

 悪あがきで、多重割り込みでスレーブ側に短いタイムアウトを設け、別のところで待つようなしかけも考えた。avr-libcには多重割り込みを認めるマクロはあるが(リニアPCMプレーヤーで使用)、今度の場合は、バックグラウンドに戻っても、スレーブの割り込みプログラムに帰れないので意味がない。

 クロックストレッチをスレーブ側が要求するとき、あらかじめ割り込みルーチンの進入を止めておくという姑息な方法もあるが、マスター側の通信要求がいつ発生するか分からないときに、スレーブを止めるのは著しく可用性を失う。

 要するに、ハードでのみ実現できる機能で、そもそもソフトでやろうというのが無謀だったのだ。クロックストレッチという誰も実現していない機能を実現してやろうという野望は、もろくも消えた。

 やれやれ、三度にわたる敗退である。暫くは立ち上がれそうにない。ただ、この電子工作は、定年後の重要なライフワークのひとつになっており、もう今さら止めるわけにはいかない。

 今、考えている次のテーマは、ESP32で、課題になっていたSPIFFS(フラッシュメモリを使ったディスクシステム)だ。フォーラムなどを見ていると、ESP-IDFという、Arduinoとは違うプラットホームで動き始めたようだ。これを試すのも悪くないかと思っている。

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

« 2017年7月 | トップページ | 2017年11月 »