« 2014年11月9日 - 2014年11月15日 | トップページ | 2014年12月14日 - 2014年12月20日 »

2014年11月30日 - 2014年12月6日の1件の記事

2014年12月 3日 (水)

心電計プロジェクト:STM32F103の心電波形表示で悪戦苦闘

 かれこれ半年かけた心電計プロジェクトは遅まきながらARM(STM32F103VB)で2.4インチTFT液晶に心電波形を表示するソフト開発まで進んだ。それにしても、液晶を動かすだけに大騒ぎしたり、フォトカプラーの選定に川崎まで高速カプラーを買いに行ったり、センサー部のTiny861のUARTにはまったりして、やたら脱線が多い。 Pb296721

 前の記事から20日余り、少しづつ作業を進め、やっとのことでTFT液晶から実際のグラフが表示されるようにはなった。しかし以前、DACでオシロに出したような綺麗な心電波形はまだ出てこない。これを待っていると次の記事がいつになるかわからないので、とりあえずこのあたりで経過報告をしておくことにする。

USI-UARTは動いた。送信プロトコルを決める(11/11/2014)

 センサー部のTiny861で動かなかったUARTは、もともとISPケーブル経由でPCとつなぐUART(ISP-UARTと勝手に命名)で、ARMへのUARTインターフェースは、最初からバッファリングの出来るUSI-UARTを予定していた。I2Cを通してDACに送っていたデータは今度はARMに送られる。このUARTの移植は問題なく終了し、プログラムは動き始めた。

 UARTらしい波形がオシロから出ている。念のためロジアナで出力波形を確かめた。ところが、これが全くでたらめなデータなのである。ISP-UARTに続きこれもダメかとがっくり来る。どうしてなのだ。何を間違えたのだろう。

 気を取り直して、慎重に状況を最初から調べる。ほどなくロジアナのプロトコルアナライザーのボーレートが設定と違っていることを発見した。早速、設定を合わせる。おお、ちゃんと正しい出力データが出てきた。やれやれ、ほっと胸をなでおろす。

 通ったのは良いが、ここは送信プロトコルが問題で、まだ具体的な仕様を決めていない。10ビットのバイナリ―データが2バイトでセンサー部のディジタルフィルターから送られてくる。データの時間間隔は625μsで、38400bpsのUARTなら2バイトだと、260μs(スタートストップいれて10ビット)の2倍、520μsかかるので、ほぼ連続してデータが送られることになる。

 何もしないで送ると、どれが第一バイトかわからなくなるので、何らかのロジックが必要である。10ビットは、1023が最大値なので、最初、第一バイト(右詰めとして)が3以下という、洒落たロジックで第一バイトを識別しようとした。しかし0~1023の中で、それでも区別できない数値が20個近くあることがわかり(0,1,2,3,256,257,258,259など)、あきらめた。

 結局、採用した方式は、精度を犠牲にして連続して送るのをあきらめ、2バイトと2バイトの送出単位の間に一定の待ち時間を設け、受信側は、必ずその待ち時間だけ待ってから第一バイトを受け取る方式にした。Ws000000

 グラフ表示が最大320ドットで長くでもスキャンは一秒間隔なので、3.3ms単位以下のデータは表示しても意味がない(前の記事の33msは間違い)。1ms程度の待ち時間を入れても大勢には影響ないはずだ。

 テストを進める。Tiny861のPCコンソールモニターでの値と、ロジアナのデータの値がほぼ一致した。待ち時間は、625μsの送出を1回休んで間隔を明ける。従って、送信頻度は、約1.25ms単位となる。  まだ、ARM側のソフトが動いていないが、ロジアナの波形は正しいデータを示している。間違いないようだ。これで、AVR側のソフト開発は一段落である。ARM側のソフト開発の前にフォトカプラーの実装をすませてしまおう。

フォトカプラーとインバーターをつける。インバーターは不要か(11/12/2014)
 センサー基板のすみに、フォトカプラーTLP512と、一緒に買って来たシングルチャネルインバーターTC7S04Fをつける。SOT23-5の極小のチップだ。持ち合わせの変換基板にハンダ付けする。フォトカプラーは論理反転するので元のUARTにするためである。Pc026733

 SOT23-5の変換基板が結構大きく6ピンのフォトカプラーと同じくらいだ。この変換基板はもう1ノッチ小さくできるような気がする。それはともかくハンダ付けは簡単に終わった。配線を確かめて早速テストに入る。

 Tiny861のUART出力ピンを一つ間違えて、出ない出ないと騒いでいたが、オシロをあてて間違いを発見し、フォトカプラー経由でも問題なくUART波形が出た。テストしているうちに、このインバーターもいらないような気がしてきた。Pc026728

 フォトカプラーの出力側を、エミッタフォロワーの形にすれば、順論理になる。どうしようか迷ったが、まあ、配線してしまったものを取り外すほどのことでもない。このままにしておく。バイナリ―端末アクノリッチでUART出力が正しく出ていることを確かめた。これでAVR側は完全である。少しづつではあるが、完成に向けてことが進んでいく。気分が良い。

Ttl  ちまちましたハンダ付けをやっているうち、もう少し手を動かしたくなった。こういうのはやりだすと止まらない。SOT23のインバーターチップを見ていて、これを汎用基板に直接ハンダ付けすれば、変換基板を使わずにもっと小型化ができる気がしてきた。

  そういえばUSBのUART TTLアダプターの受信側に電圧(負論理なのでNO DATA)がかかってAVRが幽霊動作をする現象をさけるため、かなり昔に、トランジスターのエミッタフォロワーで受けて受信側を遮断する「セパレーター」を基板の切れ端に作ったことがある。Pb266713

  AVRは低電圧でも動作するので、USB経由のUARTをつないだままにしておくと、power on resetがかからない。この現象は今度のプロジェクトでも起きて迷惑している。しかし、このセパレーターはブレッドボード用で、こういう基板同士の接続では不便で使っていない。そうだ、SOT23のチップトランジスターなら在庫がある(2SC4116)。これを使って小型化してみよう。

 いつもの脱線である。本題が順調に進んでいないときは、こうした工作を無性にやりたくなるものだ。ちょうど秋月で極小TTL UARTアダプターを買ってきたところだ。こいつにもつけられるよう汎用基板の4×4の切れ端に、ピンヘッダー、チップトランジスターとチップ抵抗2つを実体顕微鏡でハンダ付けする作業に入った。Pc016725

 ほどなく、写真のようなアダプターが完成した。配線図を参考までに。抵抗やスピードアップコンデンサーはチップ部品である。もちろん38400bps程度なら問題なく動く。いやあ、こういう道草を食っているから本題がちっとも進まない。

ARM(STM32F103)のUARTが2つ同時に動かない(11/18/2014)
 ゆるゆるとARMの開発に戻る。Tiny861からの送出データを受け取るUARTの増設だ。ねむいさんから頂いたソースコードは、複数のUARTが定義できるようになっている。始めは簡単に増やせると思っていた。しかし、これがまた難儀したのである。

 ソースのUARTの初期化ルーチン(conio_init(UARTn))が、どうも良くわからない。この初期化ルーチンは引数にUARTの番号を入れてやれば、それに応じたUART(この石はUARTを3つ持っている)を初期化しているので、併設が出来るものだとばかりと思っていた。

 しかし、2つUARTを初期化すると、2つとも動かなくなる。ソースコードを調べ直す。ふーむ、case文以外に#ifのプリプロセッサ―でUARTルーチンを制限している。なぜだろう。調べていくうち、どうもこのコードではUARTをダブって定義できないような感じがしてきた。

 見かけは、引数にUART番号を入れさせて、独立してUARTが併存できるようなコードにはなっているが、2つめを定義すると動かなくなるというのは、何か変数が被っているに違いない。しかしARMのUARTの初期化は、GPIOピンの定義から始まってややこしくAVRのように簡単ではない。ちょっと見ただけでは何が何だかわからない。

 さらに、仔細にコードを調べて行って遂にUARTの構造体が共通であることを発見した。こいつを独立させて(もうひとつ分ける変数があったが)、めでたく2つのUARTを動かすことに成功した。そもそも、2つ同時にUARTを動かす初期化ルーチンではなかったことに早く気付くべきだった。 Ecg_arm_in

 いよいよ、センサー部のTiny861とARM(STM32F103)をフォトカプラー付きUARTでつなぐ。まず、ARMを立ち上げてPCにコンソールを出してから、センサー部の電源を入れる。よっしゃー、PCコンソールから16進表示したセンサーのデータが溢れるように出力された。良いぞ。

UART2つが同時に動いたが、データの取り込みはうまくいかない(11/20/2014)
  このままではコンソールからデータが溢れて中味がわからないので、PCからのコマンドで一部だけ表示するようにソフトをいじり、中味の検証に入った。おやあ、第一バイトと第二バイトを取り違えているデータが頻々と入っている。

 皮肉なことに、正直に何もしないでデータを読む方がかえって間違いが少なくなる。何とも馬鹿にした話である。待ち時間がおかしいのか。コンソールデータだけではわからないのでロジアナを持ち込んで精密に波形を調べ始めた。どうも1msだけでは区別がつかないようである。

 仕方がない。AVRに戻って、待ちの間隔を2倍にした(2回休む)。しかし、2回休むと、AVR側が2バイトづつ送らなくなったり、どうも不安定だ。コードのなかでcontinue文を使ったのが原因のようだ。愚直にif文を区分けして、このトラブルは解消したけれど、これに係っている暇はない。先に進む。

 間隔は、データ2個分1.25msまで空いた。1msの待ちで最初のデータを識別するには十分な時間である。しかし、データは頻々と第一バイトと第二バイトを逆さまに読むケースが続出する。ARMの待ち時間がおかしいのかとGPIOピンでわざわざ時間を出してみたが正しく1msだった。

 うーむ、何が悪いのだろう。この方式では識別ができないのか。別の方法も考えるが、こんな簡単なロジックがうまく動かないというのが気に入らない。別の方式の検討に力が入らない。頭を抱える。

素晴らしく美しい富士をめでる(11/22/2014)
 ちょっと息抜きに、電子工作以外の話題をご紹介。こんなに美しい富士山の姿は、これまで見たことがなかったので、その感動のおすそわけである。 Pb226692

 知人に伊豆高原に別荘を持っている人がいて、毎年、季節の良いころに仲間で利用させてもらっている。今年は紅葉を目当てに友人と何台かの車ででかけた。伊豆は観光シーズン中は海岸沿いの道が大渋滞するので少し遠回りになるが箱根に上がって尾根伝い(天城スカイライン)に行くことが多い。

 箱根の山は、紅葉が中腹から美しく色づき歓声を上げながら山を登っていたが、圧巻は峠を登り切って振り返った時の富士山の姿である。十国峠からの景観が特に素晴らしい。

 晩秋の裾野から雪をかぶった富士山が丸ごと見える。こちら側からの富士は、宝永山の火口壁が目立って山梨側に比べると少し見劣りがするということだが、そんなことを感じさせない迫力である。

  この峠周りルートは、霧が出たりすると地獄になるが、こんなに晴れるのも珍しい。駿河湾から裾野、頂上にいたるまで雲一つないというのは、これまで何回もここを通っているが初めての経験だ。道行く車やツーリングのバイクもあちこちで車を止め、盛んにシャッターを切っていた。

  この景観がめったに見られない証拠は、帰りの道中で明らかになる。この話を聞いた海岸沿いで来た別の車の友人が、天気が良いので帰りは山に上がったのだが、靄(もや)がかかって富士山は全く見ることが出来なかったそうだ。相模湾側は青空が出ていて雲も高かったのだが山の天気というのはこういうものである。

やっと間違いなくデータを受け取れるようになった(11/25/2014)
 旅行から帰って電子工作に戻る。データの受け取りはまだ正確に行えない。こんなにエラーの多い状況ではグラフは曲線にはならず、散布図状態にしかならないだろう。

  ARMのUARTをなめてかかってえらい目にあっている。毎日が暗い。下らないことにこだわっている自分がみじめで余計気持ちが落ち込む。しかし数日後、このトラブルはあっけなく解決した。たまたま居間から地下室のPCルームに降りる階段の途中で間違いに気が付いた。

  これが、電子工作の醍醐味だなどとうそぶくのも悔しい基本的なミスである。今度もわかってしまえば何でこれに気が付かなかったのだろうと思う基本的なミスだったのだが、わかるまでほぼ2日間かかった。

  要するに、keypressed()という関数をキーに受信データの到着時間(バッファーにデータが揃ったとき)を一生懸命測っていたのだが、実際にはデータを読まなかったため、バッファーに次々にデータが貯まり、条件が揃ったときに読むデータは、ずっと前に受信したデータだった、というオチである。

  これでは、何もしない方がデータが正しく読めるはずである。原因がわかってしまえば修正はあっというまだ。データが来たときは、必ず受信関数でデータを読み、バッファーを空にしておくだけである。修正に要した時間は1分もかからない。

  コンパイルしなおして、正しくデータが読めていることを確認する。よーし、画面一杯にデータを出しても、全く乱れはない。やれやれ。あっけなく治ってしまった。 トラブルが解消するたびに、いつも何とかならないかとは思うが、こればっかりは仕方がない。考えたようには動かない。書いたようにしか動かないという格言をかみしめる。

悪戦苦闘が続く。まだ心電波形は出てこない(11/30/2014)
 いよいよ最後のステップ、得られたデータの画面表示である。枠や数字、スケールの表示はあとにするとして(これは面倒だが力仕事)、気になる数値のグラフ表示のロジックをコーディングしながらつめる(本当はおすすめしません)。

  以前考えた通り、出力値(0~1023)を液晶表示ドットY軸(240ドット)に正規化して、2X2ドットのピクセル(最初は赤、画面が暗くなるので黄色に変更)を打っていく。ロジックは何も難しくない。グラフはX軸の最後(320ドット)まで行くと最初に戻る。

  描画のとき、以前、打った値を覚えておき、表示と同時に消せば(黒で描画)、常に一つの波形が表示される理屈である。コードそのものは簡単で開発はすぐ終わった。わくわくしながらビルドする。NO ERRORだ。CoIDEは快適で、コネクターを一切いじらずに、テストまで一気に進める。Pb276718

  デバッガーを抜けてスタート。画面に待望のグラフが出た!おやあ、これはグラフではなく、単なる散布図だ。それに、やけに画面表示が早い。タイマーが機能していないのか。オシロで画像表示のタイミングを調べる。何い150μsだとー、早すぎる(設定は2ms)。ARM(STM32F103)のタイマーにコンペアマッチのコードを入れたが、これが機能していないようだ。

  タイマーの問題はあとにするとして、この散布図状態は明らかにロジックミスだ。何とかしなければいけない。ソースコードを調べる。ここのミスはすぐわかった。保存すべき描画ポイントを間違えている。前の描画ポイントではなく、出すべきポイントを消している。

  それなのにどうして描画されるのだ?このあたりを追及しているヒマはない。とにかく間違っていることは確かなので、正しく描画の後にデータをセイブするロジックに変える。再度テスト。おおー一本線のグラフが出た。いや、何本も線が出る。しかし夜中も2時近い、もう寝よう。

  表示が早すぎるのはタイマーを間違えているからで、これは時間を調整すれば良いのだが、問題は多重に出る線である。値が不安定になっていることを示している。またデータの第一バイトと第二バイトが正確に読めていない可能性が高い。

  寝る前に入った深夜の風呂の中で、また突然、原因に思いあたった。第一バイトと第二バイトをループを別にして読んでいるのだが、描画ルーチンが入ったことによって時間遅れが生じ、第二バイトを飛ばしてしまっているに違いない(第二バイトの読み込みにも時間条件をつけて離れていると無効にしている)。

  考えてみたら、第一バイトを読んだ後、第二バイトが来るまでここで待っていても全く問題ない。無理に非同期に動く必要は何もないのだ。むしろ、描画ルーチンのタイミングによっては、第一バイトだけ読んで表示をする可能性もあり、ここは2バイトを読むまで動かない方が良い。

  ゆっくり風呂に入っていられなくなった。迷ったけれど、風呂から出てジャンバーを着込み、地下の工作ルームに降りてPCの電源を入れ直しCoIDEを立ち上げる。思い立ったら寝られない性質(たち)である。眠気は吹き飛んでいる。Whileを使って第二バイトを待ち、そのあと描画ルーチンに行くように制御を変えた。

 よし、グラフは一本線になった。まだ描画タイミングが150μsと早すぎるので線はめまぐるしく動くが、少なくとも1本だけのグラフで動くようになった。少なくとも多重な線が出ることはない。時間を遅らせるのは明日にしよう。

  次の日、タイマーのデバッグに入る。ARMのタイマーについては、4年前の以前の記事を参考にしていたのだが、結局、このサンプルソースに誤りがあることがわかった(修正してあります)。プリスケールを決めるステートメントに重複があり、2番目のTIM_PrescalerConfig()は、無駄だったのだ。

  これを消去し、やっとタイマーは想定通りの時間で動き始めた。当然、画像もそれらしいゆったりとした心電波形(らしきもの)を表示し始めた。嬉しい。苦労が多ければ多いほどこの喜びはなににもかえがたい。Pb296722

  しかし、プローブを腕に巻き付けて心電波形が出るのを待ったが、穏やかな直線が続くだけで、一向に心電波形らしきものは出てこない。この状態は前にも経験がある。ピークが0.5V P-Pなのに4Vスケールで出しているからだ。

  この改修には少し時間がかかる。どの電圧範囲を広げるかを決めるのは簡単ではない。この前の記事からもう20日が経っているので、そろそろこのあたりでブログに報告することにする。

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

« 2014年11月9日 - 2014年11月15日 | トップページ | 2014年12月14日 - 2014年12月20日 »