2016年12月11日日曜日

PIC16F1939でHC-SR04を使う

PIC16F1939を使ってHC-SR04を使ってみました。
HC-SR04は超音波を使用した距離センサーで2cm~400cmまで対応しているそうです。
このセンサーはAmazonで200円程度(中国配送)で販売しているので気軽に使うことができます。
このセンサーを使用してみた感想ですが、近距離(~1mくらい)では誤差がプラマイ5cm以下位で結構良いと思いました。
しかし。自分の使い方が悪いのか、長距離(1.5m~2m以上)を測ろうとすると短距離の数値が出てきてしまいました。しかしたまに出る正しい数値の精度は良好です。



このセンサーの使い方を説明します。
このセンサーからは4ピン出ていて、Vcc,Echo,Trig,Gndです。VccとGndは電源(5v)です。
距離取得までの手順ですが、簡単に説明していくと、まずTrig端子に10μs以上のパルスを与えます。その後、しばらくするとEcho端子からパルスが出力されます。このパルスの長さが超音波が対象物まで往復するのに掛かった時間になります。

サンプルプログラム
PIC16F1939を使用したサンプルプログラムです。
このサンプルでは、HC-SR04以外にAQM1602Aというi2cLCDとタクトスイッチと10Kの抵抗を使用しています。
余談ですがAQM1602という液晶は秋月電子で売っているのですが、めちゃくちゃ便利なのでおすすめです。sc1602とかより断然いいです。
PICと各パーツとの接続は
RB5 --- HC-SR04のEcho
RD6 --- HC-SR04のTrig
RC3 --- AQM1602AのSCL
RC4 --- AQM1602AのSDA
RB0 --- タクトスイッチ
簡単な回路なので回路図は省略します。
PICと接続するスイッチはRB0を10Kの抵抗でプルアップしてください。なので、
スイッチを押したら0Vになるようにしてください。
あとPICのMCLRピンの抵抗もお忘れなく。

Echoパルスの時間の測り方っていろいろあると思うんですが、今回のプログラムでは、タイマー1のゲート機能を使用しました。今回のゲート機能の動作は、タイマー1ゲートピン(RB5)がHIの間のみタイマーをカウントするというものです。
今回、タイマーはFosc / 4を源として、1/4プリスケーラを使用しています。
あと今回のプログラムではセンサーから取得できる最大距離は557cmになっています。
これだと若干無駄な気がするので、プリスケーラを1/1にすれば最大距離は約139cmになってしまいますが、精度は上がると思います。プリスケーラを1/1するには
T1CONの値を0b00100101 から 0b00000101へ
a = ((0.03125 * counter * 4 * 4) / 29.4117) / 2 の部分を
a = ((0.03125 * counter * 4 ) / 29.4117) / 2 へ変更してください。
あと上のa=の式は距離を求めるものなのですが、0.03125は32Mhzの周期(単位μs)、
29.4117は音が1cm進むのに掛かる時間(単位μs)となっています。

サンプルプログラムの内容ですが、電源を入れると画面には何も表示されません。
スイッチを押すたびにlcdの1行目にタイマーの値が、2行目に対象物との距離(単位cm)が表示されます。あともしタイマーの値が32767(16bit int型最大値)を超えていると-表示になります。
ですがプログラム中には正常に動作しています。

質問、指摘等ありましたら是非コメントお願いします。
ありがとうございました。

8 件のコメント:

  1. 写真の右側のledはなんのためでしょうか?

    返信削除
    返信
    1. コメントありがとうございます。
      電源確認用です。

      削除
  2. 了解です!
    ありがとうございます!
    それと、プルアップ抵抗はlcd内部のものでもいいのでしょうか?

    返信削除
  3. それで大丈夫だと思います。
    自分は基板にもとから載ってたものを使っています。

    返信削除
  4. タクトスイッチを使わずに常にディスプレイに値を表示することは可能ですか?
    また可能であれば、修正点を教えてもらえますか?

    返信削除
    返信
    1. 返信が遅くなってしまって申し訳ありません。
      そのようにするのであれば、while(1)内の次の三行を消してみてください。
      while(PORTBbits.RB0);
      delay_ms(20);
      while(!PORTBbits.RB0);
      こちらでは動作を確認していないので確実ではないですが、これで良いと思います。

      削除
    2. 出来ました!ありがとうございます!!
      加えて質問なんですが、常にLEDを光らせておいて、距離の値aが10cmになったらLEDを消灯するというプログラムを作りたいのですが、if文で条件式を書いてもうまくいきません。なにかいいアドバイスがあったらお願いします。

      削除
    3. 返信遅れてしまいすみませんでした。
      今更になりますが、一応返信させていただきます。
      例えばRB0にLEDにLEDに接続するとすればwhile(1)文を下記のように書き換えれば10cmでLEDが点灯します。
      しかし、距離はdouble変数に格納しているので、ジャスト10cmは厳しいかと思いますので、
      while(1)内のif文を if(a >= 9.5 && a <= 10.5) の様に変えてみてもいいかと思います。
      こうすることで距離が10cm プラマイ0.5cmの範囲で光るようになります。

      while(1){
      TMR1H = 0;
      TMR1L = 0;
      T1GCONbits.TMR1GE = 1;
      PORTDbits.RD6 = 1;
      __delay_us(11);
      PORTDbits.RD6 = 0;
      delay_ms(200);
      counter = (TMR1H << 8) + TMR1L;
      a = ((0.03125 * counter * 4 * 4) / 29.4117) / 2;
      if(a == 10){
      LATBbits.RB0 = 0;
      } else {
      LATBbits.RB0 = 0;
      }
      }

      削除