パルスのエッジ割り込みとタイマー割り込みを使った計測に変更してみた。

以前の投稿で、一定時間内に入ったパルスの数を数えて、ワイヤレスモジュール(nRF21L01+)を使って通信するという実験をしましたが、いくつかの不具合があったので、修正してみることにしました。

【不具合の色々】

  1. 10秒毎のタイマー割り込みを設定していたつもりだったが、正確に10秒になっていない
  2. パルス入力に「チャタリング除去」を使っていたが、測定対象のパルスの幅が非常に短く、場合によっては捕まらない事がある。
  3. 測定結果をUSB経由のシリアル通信でパソコンに取り込んで使う必要があるが、何らかの理由でパソコン側のシリアルバッファにデータが溜まってしまった際(吐き出す処理が滞った場合)、実際に計測された時刻がいつだったのかわからなくなる。

さらに、この際、余分な機能(温度測定とか)を全部取り除いて、コードをきれいに書き直すことにしました。

【不具合への対応】

上記の各不具合に対して、次のような対応をしました。

  1. Timer2割り込みに設定する値を修正して、ほぼ正確に10秒毎に割り込みが掛かるようにした。
  2. パルス入力を”INT0”ピンを使ったエッジ割り込み(立上り)に変更した。
  3. パルス計測結果をRFで(nRF24L01経由で)送信する際に、RTCモジュールを使って「送信時刻」を送信パケットのデータに含めることにした。

【1: Timer2割り込みを正確に10秒毎に】

まずは、以下の単純コードでTimer2とSerial.printだけのコードで、確認してみました。

このコードを走らせてみると、設定した通り、10000msec毎に正確に 10000ずつ差のある mills()の数字が表示されます。ということは、ATmega328のコントローラー自体は、ちゃんと正確に 10000ms毎に割り込みをかけているつもりになっている事になります。

ただ、実際の時計と比較して長時間走らせると、ずれが発生しているのは事実ですので、要するにArduino Nanoのクロック精度がその程度ということだと思われます。 

Timer2の設定を10000 (すなわち10.000秒)にして、RTC (DS3231のReal Time Clock)の時計出力と比較実験すると、約114回毎、すなわち1140秒毎に約1秒遅れていくことが観測されていますので、ズレは、1/1140 (=0.0877%) 程度となります。 Arduino Nanoに搭載されているクロック源は、16MHzのセラミック振動子(セラロック)であり、仕様書によると初期周波数の偏差は±0.5%にもなりますので、今回の実験結果は 0.09%程度というのは、充分仕様内と言えます。 これ以上の精度をセラロックに求めるのは無理ですので、まぁ、今回のプロジェクトでは、「現物合わせ」の手法で対応するのが妥当という事になりますね。

もっと、精度が必要な場合は、水晶振動子を使うしか無いですね。

現物合わせとしては、Timer2の割り込み周期を ずれている分だけ長くしてあげることにしました。10000 * (1/1140 + 1) = 約10008.7719 (四捨五入して 10009) を設定して実験してみたところ、(実用的なレベルで)正確に10秒ごとに割り込みが入るようになりました。

追記: 周波数カウンタとかオシロとかが手元にあれば、確実に検証できるでしょう。安いUSBのオシロが欲しくなってきました。

【2.パルス入力をINT0割り込みへ変更】

最初に設計した際、パルスは通常のDigital Readで読んで、20msec毎にチャタリング処理をした上で、パルスの有り無しを判断するように作りました。実験中に普通のタクトスイッチでパルス検討する際には、これが必須でした(チャタリングを取らないと、まともなパルス計測ができません)

ところが、実際に測定したい装置に導入してみた際、パルスのデューティー比が非常に大きく、”H”レベルが50msec から 70msec程度、”L”レベルが800msec程度であったため、チャタリング処理が逆に邪魔をして、パルスを取り込めない場合が時々ありました。この装置に於けるパルスを現場で確認したところ、チャタリングは無く、きれいな波形でしたので、この際、割り込みを使ったパルス検出方法へ変更することにしました。 (チャタリングがある場合には、割り込みによるパルス入力検出は難しい気がします)

Arduinoに於けるパルス割り込みは、INT0 (D2 ピン) で簡単に使えます。単純化したコードはこんな感じです。10秒の間に入ったパルスの数を数えて、10秒ごとにシリアルに書き出すソフトです。前述した通り、10秒の割り込みには、Timer2を使いますが、クロック精度の現物合わせで 10009という補正したカウントで割り込みをかけます。

常識として、「割り込みの中で時間のかかる処理は、避けるべき」という鉄則がありますので、どちらの割り込みも、フラグを設定したり、カウンターをインクリメントするだけにして、時間のかかる処理は、メインループの中でTimer2の割り込み毎に処理します。

これで、D2ピン(INT0)に接続された信号の、すべての立上りエッジを割り込みで取り込んで、カウントできるので、いくらパルスのデューティーが大きくても(どれだけ、パルスの”H”期間が短くても)正確に、パルス数を数えられます。逆に、副作用としては、パルスにチャタリングがある場合は、正しく数えられません。(チャタリングを全部数えてしまうから・・・)

【パルスを計測した時刻を正確に出力できるようにする】

【次ページへ続きます】