公開日:2022/08/27
title.png

22nd Season


#22-4 超音波センサで距離を測る

 今回のテーマは、「超音波センサを使って距離を測る」です。

ar3a_usonic.jpg

 単に「測る」だけなら、以下の記事で実施済みです。

  「#3-10 超音波センサと焦電型赤外線(PIR)センサ

 復習をかねて、ご確認ください(↑)。

 今回は上の記事と同じことをするのではなく、なるべく高精度に距離を測定してみたいと思います。シーズン3で実施した内容と、シーズン22まで進めてきた、今この段階で実施することが同じでは進歩が無さすぎます。「ハードウェアの実力」も、さりげなく振り回しながら一歩進んだ測定装置を作成しようと思います。

超音波センサ

 超音波センサのおさらいをしておこう、と思ったのですが、メジャなセンサなのにインターネットで検索しても詳しい情報が得られませんでした。

  調査結果を総合すると:


 Logiscopeを使って独自に検証したところ、以下図のタイミング図が正しいと思われます。

a224_usonic.png

 従って、ECHO信号のHIGHパルス幅が超音波の往復時間です。HIGHパルス幅の時間[s]が測定出来たら、そこに音速[m/s]を掛け算すると、往復の距離[m]が求められます。
 ECHO信号のHIGH幅を精度良く測定することが要求されます。

 その他、Logiscopeで波形を確認しながら試したところ、超音波が返ってこなかった場合、約200ms後にECHO=0へ戻りました。これを見る限り、TRIG信号のアサート間隔は>200msとすべきです。
 また、全く使用しない情報なので、どうでも良いのですが、TRIG↓からECHO↑までの時間は、手元のモジュールでは約440μsでした。

音速

 超音波は人間の耳には聞こえないのですが、可聴域(約20〜20k[Hz])の少し外側(〜40kHz)の波動なので、「音🎶」と同じ速さで進むと考えて問題ありません。

 「#3-10 超音波センサと焦電型赤外線(PIR)センサ」の記事では、音速を定数として計算していました。

 音速は、その時の空気の組成比(N2:O2:その他)や、気温、湿度などで変化するようです。一律340m/sで計算していては、高い精度は得られません。
 でも、組成比を測定できるセンサを持ち合わせていないし、より精密な音速を使うのは相当に難しいことです。そこで、もう少し簡略化した近似式を使います(下式)。

  音速≒(331.5+0.61×T)[m/s] …… ここでTは気温[℃]

 上の近似式は、T=0℃に於いて、より精密な音速、対、気温の式を微分した傾き0.61[m/s/℃]を使った近似式だそうです。この式を使って音速を推定するためには、気温を測定するセンサを備える必要があります。

HIGHパルス幅の精密測定

 ECHO信号のHIGHパルス幅を測定するため、「#3-10 超音波センサと焦電型赤外線(PIR)センサ」ではArduino標準ライブラリのpulseInLong()関数を使いました。
 ライブラリのソースまでは確認していませんが、恐らくソフトウェア(C言語)でI/Oポートの状態を監視して、I/OポートがHIGHレベルになっていた時間を計測しているのでしょう。ソフトウェアの場合、MCUの実行速度に依存して、I/Oポートをポーリングするループの実行時間程度に量子化された粗いデータしか計測することができません。
 せめて、アセンブラで最速コードを書けばまだましですが、恐らくArduino標準ライブラリだと、I/Oポート番号から各MCUのポート名に変換して、必要なI/Oレジスタアドレスに変換して、アクセスして、……と、大きなオーバヘッドがあるはずです。
 Arduino UNO (ATmega328 @16MHz)なんていう鈍足MCUでこれをやったら、恐らく±数μs程のばらつきは生じてしまうでしょう。
 一応、pulseInLong()関数が返してくるのは、マイクロ秒単位のデータだという事なのですが、もし、±E[μs]の偏差が有った場合、340m/sの音速を使うと、距離の偏差は±E[s]×340m/sとなります。もし、E≒5μsなら、±1.7mm程の誤差です。観測される距離の範囲は3.4mmにも広がります。
 数mの距離の中で3.4mmなら大した誤差ではありませんが、数cmの距離だと結構な誤差となります。

※ATmega328 @16MHzが1μsの間に実行できる命令数は多くて16個です。I/Oポートのポーリングや、パルスが検出できなかった場合のタイムアウト判定などを実行するのに、たった16個の命令では相当厳しいので、恐らく計測ループ1回分には数μsかかっているはずです。

ハードウェアでHIGHパルス幅を計測する

 より精密に、ECHO信号のHIGHパルス幅を計測するため、ハードウェアの力を借ります。
 pulseInLong()関数に相当する動作をさせるだけの事なので、回路としてはとても簡単なものになります。システムクロック(24MHz)の精度で精密にパルス幅を測定できるのは大きな利点です。

 簡単な回路、と言っても、ディスクリートで作るには十分複雑なので、いつもの通りFPGAボード(Sipeed Lichee Tang Primer)を使います。……ちょっとした回路なんだけど、ササっと設計して使えてしまうのは、FPGAボードの良いところだな〜。

 FPGAボードを使うので、オリジナルCPUコアのMoscoviumまたはNihoniumを使います。測定後の演算には、32ビット整数を使った固定小数演算が必要となるので、オリジナル32ビットCPUコアNihoniumを選択します。

 I/Oポートに接続したECHO信号を監視して、HIGHとなっていた時間をシステムクロック(24MHz)精度で測定。測定結果をI/Oレジスタから読み出し可能とします。ハードウェアとしてはそれだけです。pulseInLong()関数と同じ動きしかしないので、回路はすごく簡単です。
 システムクロック(24MHz)でHIGHパルス幅を計測するので、その解像度は1/(24MHz)≒41.7nsと高精度です。測定中に割り込みがかかることもないし、ソフトウェアでは太刀打ちできない高精度測定が可能となります。

超音波距離測定ユニット(DISTUS)

 超音波距離測定ユニット(DISTUS)の詳細設計を進めて行きます。

 ユーザに、Nihoniumマイコンの汎用I/Oポート(最大16本)の中から、TRIG信号とECHO信号を接続するポートを選択してもらいます。
 DISTUSユニットはTRIG信号を監視していて、TRIG信号にHIGHパルスが発生したら、その後、ECHO信号に発生するHIGHパルス幅を24MHzクロックで計測して、I/Oレジスタから読み出し可能とします。
 DISTUSユニット自体は、何の信号も生成しないので、TRIG信号に相当するHIGHパルス(>10μs)は、汎用I/Oポートを使って、ソフトウェアで生成する必要があります。

 以下にブロック図を示し、各サブブロックの機能設計を進めます。

a224_distus.png

※黄色に着色した箱は、CPUからアクセス可能なI/Oレジスタです。

  各サブブロックの機能設計:

I/Oレジスタ

 以下にDISTUSユニットのI/Oレジスタ表を示し、解説を続けます。

Addr Name R/W Bit:15〜0 Memo
0xf110 distctl R DI
ST
ER
R
BS
Y
measurement control
0xf112 disttrig RW TSEL[15:0] trig port select
0xf114 distecho RW ESEL[15:0] echo port select
0xf116 reserved
0xf118 distcnth R CNT[19:16] round trip counter higher word
0xf11a distcntl R CNT[15:0] round trip counter lower word
0xf11c reserved
0xf11e reserved

 BUSC出力のセレクト信号はbcs_dist_nです。上表の範囲でアサートされるので、DISTUSユニット内では、アドレスbadr[3:0]を復号してレジスタを特定しています。このI/Oユニットはバイトアクセス禁止です。必ずワードアクセスしてください。

  I/Oレジスタ仕様:

 DISTUSユニットは、TRIG信号を生成しませんので、ユーザが汎用I/Oポートを制御して、ソフトウェアで>10μsのHIGHパルスをTRIG信号に出力する必要があります。以下にコード例を示します。

  // trig=1
  ldwi r7,porset
  ldbiu r0,dst_trig
  stw [r7],r0
  // wait 10us
  ldbiu r0,10*(fcpu/1000000)/3+1
  subi r0,1
  bne pcnt-4
  // trig=0
  ldwi r7,porclr
  ldbiu r0,dst_trig
  stw [r7],r0



 次回、Nihoniumマイコンを組み上げて、超音波距離計全体を完成させようと思います。


btn_arduino.png

btn_prev.png btn_next.png

home