プログラムを読んでみよう⑤ ―― 音検出 sound.c
10行目から12行目には、音の検出にかかわるハードウェア資源を#defineを使って別名定義しています。こうしておくことで、接続ポートが変わったり、音を検出するときの信号レベルが逆になったりしても、ここだけ修正してビルドし直せばよくなります。
26行目からは音を待つ関数WaitSound()があります。基本は28行目のwhile文です。これで、対応ポート(PORT_SOUND)がINACTIVE(未検出)の間、ループして待ちます。ただし、引数chktimeにCHECK_TIMEが指定されたときは(29行目)、30行目でReadTimer()を呼んでタイマ値をチェックし、TIME_LIMIT時間を経過していたら音待ちを中断してERRORで帰ります。TIME_LIMITは別途timeruser.hで定義しています。
50行目からは、無音状態を検出(確認)するための関数WaitInactive()です。基本的な動作である53行目から59行目のwhileループは、チェックするポートのレベルが逆なだけでWaitSound()と同じです。大きく異なるのは、それら全体がさらにdo~whileのループにあるところです。
まずは53行目からのループを実行して無音を検出します。その後、61行目で5ミリ秒待って、63行目のwhile文の条件で再度無音かどうかをチェックしています。無音であればdo~while()を抜けてOKで戻りますが、63行目でACTIVEレベルが検出された場合は、もう一度最初から処理をやりなおします。
なぜこのようなことをしているかというと、スプーンは打ち付けると激しく振動するために、回路での波形整形で補正しきれずに写真1のように汚い波形になってしまうことがあるためです。一瞬無音レベルに戻ったと認識されても、実はまだ振動が続いているということがあります。その状態で次の音待ちに進んでしまうと、振動による波形を次の音と勘違いして誤動作してしまいます。そのため、少し(ここでは5ミリ秒)待って再度ポートをチェックすることで、この問題を回避しようとしています。これでも完全ではありませんが、実用上は十分です。
一般の組込みソフトウェアでも、スイッチの読み取りなどでは、やはり同様の処理を行っています。スイッチも、内部では金属の板を接点にぶつける構造になっているものがあり、床でボールが弾むのと同じように振動してノイズ波形を出してしまいます。このような現象をチャタリングといいます。スイッチなど機械部品を使う組込みソフトウェアでは必ず登場する言葉なので覚えておきましょう。
スプーンをたたいた音は超音波センサで電気信号に変換しますが、この変換したての波形は写真2のような形をしています。
これは比較的きれいに音が鳴った場合の波形で、約40KHzの振動がだんだん減衰していっています。このような信号をマイコンで(ソフトウェアで)直接読みとるのはかなり難しいです。振動しているので、たまたまポートを読みに行ったときに音信号が来ていても0か1かが判定しにくいのです。そこでマジカルボックスでは、波形を整形する回路を通すことで、写真1のような比較的きれいな波形にしてマイコンに伝えています。
76行目からは、サンプリングのときに使う関数IsHit()があります。これはいたって簡単、SampleTimeで与えられる一定時間内にスプーン音が検出されればON(84~85行目)を返します。注意ポイントは、途中で音を検出したからといって、そこでループを中段したりはしない点です。必ずSampleTime分の時間この関数にとどまります。