心電計プロジェクト:スケールが出ると心電計らしくなる
みなさま明けましておめでとうございます
当ブログも開設以来7年が経ち、電子工作歴は何と、もう足掛け8年になりました。毎年のお正月には、これまでの総括と今後の抱負を一言載せるのが恒例になっていますが、今年はどうも適当なことが思いつきません。電子工作が、仕事をやめてからの第二の仕事のようになり、余りにも生活に溶け込んでしまったからでしょうか。
昨年、嬉しかったことをひとつご報告しておきます。IT企業に勤めている娘の職場の友人が数名、ブログを読んで、ぜひ話を聞きたいと当研究所を見学してくれたことです。電子工作の面白さをみんなで熱く語りました。ブログのおかげで、色々な人と交流ができる。ありがたいことです。
それはともかく、プロジェクトの続きです。心電計のプロジェクトもいよいよ大詰めに近づき、2.4インチTFT液晶に、それらしい心電波形が出てきました。昨年暮れからの作業の状況を以下にご紹介します。
keiさんありがとう。ピークが出ました(12/20/2014)
心電波形はひとまず出たが、前記事にも書いたように、どうもピーク値(R波)が低すぎるような気がする。これは心臓疾患ではなくてセンサー部からのデータを端折っている(2回に一回しか送っていない)ため、ピーク値を逃している可能性が高い。
UARTで10ビットのデータを2バイトに分けて送出しているが、先頭バイトを識別するため、データの送出を1回休んで間隔を空けている。これをもう少し縮めるか、このあいだコメントで頂いたように、2バイトの先頭ビットに細工をして10ビットデータを送れば、間隔を空けなくてもよさそうだ。
実は、こうしたデータの内部構造にまで立ち入るプロトコルは、なるべくならやりたくなかった。しかし擬似コーディングしてみると、この先頭バイトに識別ビットを載せる方が、下手に時間を空けるより(1回分より少ない時間を作るのが難しい)、はるかに簡単に実装できそうなのだ。keiさんという方からコメントでご提案をいただいた方式である。
10ビット(ADC値)データを2つに分割し(等分に分ける必要はない)、第一バイトのMSBに1を立てて(つまり128以上)、128より小さい第二バイトと区別する。ロジックそのものは至って簡単だが、AVR、ARMの両方のコードを同時に変更する必要がある。
まず、AVRの送出側を変更し、PCのバイナリ―端末アクノリッチでデータを確認する。想定通りのデータを確認してから、ARM側を変更する。何度もロジックを確認してビルドし、祈る気持ちで接続した。 テストする。一発で動いた。良いぞ、コーディングミスはなかったようだ。よーし、ピークのR波が鋭くたち、世間でよく見る心電波形の形になった。良かった、良かった、自分の心臓がおかしくなったわけではない。keiさんありがとうございました。
久しぶりにAVRをいじったので、ついでに気になっていたAVR側のデバッグをした。稀れにだが、デジタルフィルターを通さないノイズの入ったままのデータが送られる時がある。電源の抜き差しを頻繁に行ったときに多い。リセットすれば直る。サンプリングに失敗しているとみられる。
コードを調べていくと、ADコンバーターのサンプリングを10回もやっていることがわかった。一回9μsのADCサイクルで10回というと100μs近くになる。送出間隔は、635μsなので、この程度なら大丈夫だとは思うが、試しにこれを2回に減らしてみた。
あてずっぽうだったが、これが原因だった。何度電源を抜き差ししても一回もトラブルは起きなくなった。よーし快調だ。心電波形はこれでほぼ完全になった。
いよいよ画面に、枠線や、数字を出すステップに来た。それと、トリガーを考えなければならない。今は連続して波形を出しているので、心拍のピークは一定のところに出ない。これを一定の位置から画面に出すようにしたい。これが出来れば本格的な心電図になるのだが、この実現はそう簡単ではない。
画像表示関数をデバッグする(12/21/2014)
この間トラブった描画関数について、作者のねむいさんから丁寧なお詫びコメントが入った。こちらが好き勝手に使ってこけているのだから、恐縮されるとかえってこちらが戸惑ってしまう(オープンソースは無保証が大前提)。
自分だって昔に開発したコードのバグを指摘されるのは、有り難い反面(建前)、実は何となく昔の思い出したくない古傷に触れられる感じで、そう手放しで喜べる出来事ではない(本音)。これが仕事なら本当に助かるけれど、遊びで作ったのにケチをつけられるのはたまらないと思う。
なので、何かこのところ狙い撃ちをするみたいに不具合に遭遇してしまい、かえって申し訳ない気持ちでいっぱいである。荒さがしをしているつもりはないのに結果としてそうなっている。こちらこそ、ごめんなさい、ごめんなさいである。
まあ、それはともかく、新版で解消したので差し替えて下さいということだが、全部を差し替えるのは大変なので、ソースをみてどこが違っていたのか確かめてその修正だけをすることにした。不具合は2つあった(関数Display_DrawLine_If())。
・差分を積み上げるときの数をせっかくABS()で正数にしているのに、元の数で計算。
・if(数値)は、0がfalseで0以外がtrueなのだが、数値>0とすべきをif(数値)としていたために負数がすりぬける。
いずれも、全くのケアレスミスである。しかし所長も正しいコードと比較するまで机上デバッグでは見つけられなかった。ソフト開発というのはこういうものである。まさしく書いたようにしか動かない。どんな簡単なソフトでも動くまで信用してはいけない。
グラフの描画は最初、2重線を引いていたが、折れ線だと1本線でも十分綺麗にでることがわかり一本に減らす。ロジックが少し簡単になった。これで残っている不具合は、電極の装着が依然不安定なことだけになった。
電極は、前にも書いたように暫く(1分程度)同じ位置に固定していると接触抵抗が下がって波形がでてくるのだが、5分以上置いておいても出てこないときがある。始めつけた場所によると思っていたが、どうも断線の可能性が高い気がしてきた。
しかし導通テストでは大丈夫である。しつこく調べるうち、遂に、1本のコードで断線を発見した。かしめてあるコードの中の部分で断線していたのだ。かしめた部分をドライバーでこじてゆるめ、コードを引っ張ったらポロッと切れた銅線が出てきた。こういう微弱電位の断線はたちが悪い。
意外に簡単にトリガーはかけることができた(12/26/2014)
トリガーである。波形を常に一定の場所から描くことである。だんだん、オシロスコープに近い処理が必要になってきた。リアルタイム性は余り求められていないので、トリガーは出てきたデータを一旦バッファーに貯めて、そこから考えれば大抵の方法は思いつく。
メモに沢山思いついたことを書きとめながら色々考えていたが、結局、以下の簡便な方法でやることにした。
・1フレーム分のデータを蓄積し、その中の最大点のポイント(R波)を記録しておく。
・描画は、その最大点より少し前からスタートする。このポイントがフレームの先を越えたらフレームの最初から描く。ここで少しずれるときがあるが、余り厳密には考えない。
・描画と記録の速度を同期させておけば、オーバーランやアンダーランの心配なく、描画が続く(はずである)。
・ただし、フレームの最初の方にピークがあるときは、最大1画面程度まで描画は遅れる。リアルタイムな心電波形ではない。また、最初すぐに波形は出てこない。
仕様はできた。コーディングする。1画面(300ポイント)のバッファーを定義し、バッファーがいっぱいになったタイミングで、描画の位置を計算する。決まればバッファーの中のデータから描画する。あとはリングバッファーの処理と同じである。
少々、トラブルがあったが思ったほど難しくなく、ほどなくトリガーの効いた画像が得られた。タイミングが早くなってもピークを含む心拍波形を常に表示することができるようになり、俄然心電計らしくなったきた。
いやあ、苦節うん十年ではないが、これで、だいたい想定通りの機能は実現した。満足感に浸る。残るは力仕事の枠線(スケール)描画である。そう、数値も出してやりたい。
枠線を出す。やっぱり数値表示が欲しい(12/29/2014)
枠線は簡単に出ると思ったがやはり落とし穴があった。線を増やしていくと全体がハングするのだ。おかしい。デバッガーを動かすが、落ちるところが一定しない。うーむ、何かオーバーフローか何かでデータが壊されている感じだ。
オシロで動きを確かめて原因が判明した。何と枠線を引くのに80msもかかっている。一方、UARTは絶え間なくデータを取り込んでいる。UARTのバッファーは256バイトもあるが、ここが怪しい。
そう、やっぱりオーバーフローだった。0.6ms単位に2バイトのデータを出すから、80msも待たされれば、266バイトのデータがバッファーに溜まる。はい、これではだめです。UARTのバッファーを256バイトから倍の512バイトに拡張する。
ところが、UARTはバッファーサイズを拡げただけでは動かなかったのである。バッファーの変数は16ビットでとってあるのにおかしい。ソースコードを詳しく調べて原因はすぐわかった。関数の中の一時的な変数が全部8ビットで定義されていたというオチである(ねむいさん、ごめんなさい。また見つけちゃった)。
これを直して無事、枠線も出た。しかし、枠線だけでは何となく物足らない。やっぱり数字がついていないと雰囲気が出ない。文字の表示は、この液晶を縦位置で使うために今度の心電計では、この文字表示列を90°右に回転させる必要がある。これは簡単にはできない。
ねむいさんのソースはもちろん2バイトコードの漢字まで表示できるのだが、今度のARMはフラッシュが128KBしかないので全部含めてビルドができない。しかも、開発環境がCooCoxという全く違う環境である。必要なところだけソースツリーからつまみ食いするような形でモジュールを取り出してくるのは、とても気を遣う。
自慢にもならないが、こういう開発の方が、まるごと持ってくるよりはるかに難しいのだ。しかもこればっかりは開発者に聞くわけにはいかない。しかし、やりだしたからにはやめるわけにはいかない。黙々とソースコードを調べては持ち込む作業を続ける。
ねむいさんのソースは相当進化していて、新しいバージョンではフォントのサポートが滅法増えている。最新版ではFONTX2タイプのフォントが10以上もある。ディレクトリの構造が悩ましい。CooCoxで動くかどうか。
ふむ、ふむ、FONTX2のファイル.FNTファイルはバイナリーなので、includeは簡単ではない。どうしているのだろう。おお、ChaNさんのテクニックが使われている。なになに、.incbinというgccのアセンブラーgasのディレクティブだ。うーむ、CooCoxの環境でライブラリーパスはどうなる。
CooCox環境で、ARMの文字表示に成功(1/4/2015)
あっと言う間に2015年を迎えた。今まで我が家で男は、所長とオス猫だけだったのだが、ここ数年婿殿が増えて正月は男女同数になっている。家内が和装にこっていて正月は全員で和服に着替えるのだが、今年はさらに男3人全員が和服に揃えたのでちょっと壮観だった。
自宅の前で記念撮影をする。自宅は近くの神社の参詣経路にあたっている。通行人の視線が眩しい。その足で元旦の初詣に行く。今年は思ったほど混んでいなかった。寒かったからかもしれない。お正月は酒を飲むので電子工作はお休みである。
3日の箱根駅伝が、史上始めての青山学院大の完全優勝で終わって、電子工作の再開である。暮れから始めた文字出力の開発を続ける。文字フォントのincludeが出来ないでいる。環境が、CooCoxなのでリンクの方法が全く見当つかない。
CooCoxのConfiguration画面のcompilerオプションで環境変数の定義は簡単に出来るようになったが、includeパスでは.incbinがファイルを認めてくれない。色々やったがだめである。CooCoxのパスは、実際のディレクトリパスとは全く違う仮想的なパスを定義することが出来るので余計ややこしい。
試行錯誤の末というより、愚直にソースコードのFNTファイルが入るところにフルパスを入れたら見事に認めてくれた。いやあ、最初からこれでやれば良かったのだ。コンパイルが通って一息ついた。ここまで来ればあと少しである。
コマンドを新設して、適当な文字を出すテストルーチンを作る。 ふむ、出ない。ああ、そうだ。初期化を忘れている。前の気圧計の時の初期化ルーチンをそっくり持ち込んで再度テストする。 出た出た。12X6のポイントの小さいフォントだが、画面に文字が出た。
さて、これからまた一仕事である。この文字フォントを右に90°傾けて縦置きにしなければなならない。
縦置きフォントが出た。文字が出て画面は一気にそれらしくなる(1/6/2015)
文字フォントの回転に頭を抱えている。最初、素直にフォントグリフを縦にスキャンするルーチンを擬似コーディングし、実際のソースをみながら開発に入ったところ、描画ルーチンの実装で重大な思い違いを発見して愕然となった。
ねむいさんの描画ルーチンは色々なところで高速化が図られており、文字フォントはひとつづつのピクセルを描画するのではなく、文字フォント枠(レクタングル)をあらかじめ描画ドライバーの原始関数で設定してから、そこへ連続したピクセルデータを送って、ドットが流し込まれる方式がとられている。
画像処理ドライバーで良く使われる高速化手段である。この方式ではスキャンする方向が決まっており(もちろんコマンドで方向は換えられるが、原始関数を駆使する必要あり)、今のままでは、フォントグリフを縦に並べることは出来ない。
一旦、文字を作ってから、縦のドットを横にスキャンするような形でドットを拾って送り込む必要がある。うーむ、単純に考えていたが、文字を回転するのはえらく難しくなってきた。文字が画面上縦になるのを我慢すれば、このまま出しても良いが、やっぱりみっともない。
暫く考えていたが、ここまできて辞めるわけにはいかない。速度は遅くなるかもしれないが、2次元配列にフォントを流し込んでから、これをさらに縦横に転置するロジックを作ることにする。
コーディングしてみると意外に捗った。既存のルーチンをうまく活用してFONTX2形式のフォントを、fontdata[x][y]などの二次元配列に流し込み、このxとyを逆さまにしてやれば良いだけである。
ついでに既存のフォント出力も、この方式に替え、パラメーターでフォントの回転が出来る関数をでっちあげた。意気揚々とテストに入る。まずは、ノーマルの出力(画面の赤)。うむ、問題なく表示された。
次は、問題の回転出力である。うーむ、ゴミが出るだけだ。あ、配列番号が間違っている。配列の番号は0からスタートするので最後からスキャンするときは、1少ない数値から戻らないといけない。一番最後も0を含める必要がある。
よーし、見事に文字列が縦に表示された(画面上では横の緑文字 ABはオリジナル関数から)。ここまで来れば実際の心電波形に数値を入れてみよう。勢いが止まらない。いそいそとコーディングする。数値はとりあえずは固定値で良い(増幅率や時間で変数にするのはあとで)。
出ました。出ました。数値が出ると枠線も俄然本式に見えてくるから不思議だ。急いでリストバンドをはめて実際の心電波形を出し、写真撮影をする。いやあ、ここまで来るのは長かったが、とりあえずは、ちょっと見映えのする画面になった。
| 固定リンク
| コメント (5)
| トラックバック (0)
最近のコメント