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

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

【不具合の色々】

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

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

【不具合への対応】

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

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

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

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

#include <MsTimer2.h>	//タイマー割り込み用のヘッダー

#define TMR2_INT 10000	//10秒なら10000 msecと設定すれば良いはずなのだが・・・A
volatile bool isTimer2Int = false;	//割り込みあり無しをメインループへ伝えるフラグ

void timer2ISR()	//Timer2割り込みの処理ルーチン。出来るだけ早く抜ける
{
  isTimer2Int = true; // ISR for Timer2
}

void setup()
{
  Serial.begin(115200);
  MsTimer2::set(TMR2_INT, timer2ISR);	//割り込み周期と割り込みルーチンの設定
  MsTimer2::start(); // Timer2割り込み開始
}

void loop()
{

  if (isTimer2Int) {	//割り込みが入った次のメインループで実際の処理を実施する
    isTimer2Int = false;	//割り込みフラグをクリア
    Serial.println(millis());
  }
} 


このコードを走らせてみると、設定した通り、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の割り込み毎に処理します。

#include <MsTimer2.h>

#define TMR2_INT 10009	// 0.09%の補正後
volatile bool isTimer2Int = false;
void timer2ISR()	// Timer2割り込み処理ルーチン
{
  isTimer2Int = true; // ISR for Timer2	フラグを立てるだけで抜ける。
}

#define INPUT_PIN_1 2	// 割り込みピンはD2 (INT0)を使用。
volatile uint16_t pulse_count = 0;	//パルス計数用のカウンタ (Volatile宣言・・・勉強中・・・)
void risingISR() 	// パルス立上りエッジの割り込み処理ルーチン
{
  ++pulse_count;	// カウンターをIncrementするだけで抜ける。
}


/****************************************
    Setup
*****************************************/
void setup()
{
  Serial.begin(115200); // 115200 baud

  MsTimer2::set(TMR2_INT, timer2ISR);
  MsTimer2::start(); // interrupt start

  pinMode(INPUT_PIN_1, INPUT_PULLUP); // pulse input pin
  attachInterrupt(0, risingISR, RISING); //pin D2 (INT0) RISING EDGE detect
	// この記述で、「INT0信号の立上りエッジ検出し、指定されたルーチンを開始する」になります

}

/**************************************************
   Main Loop
**************************************************/
void loop()
{

  if (isTimer2Int) {	// 10秒毎
    isTimer2Int = false;	//フラグをクリア
    c = pulse_count;	// この瞬間までに計測された数字をコピーしてクリアする。
    pulse_count = 0;	// この2つの文の間に割り込みが掛かるとずれるリスクがあるので、
			// 本当は割り込みを禁止すべき・・・と思う。
    Serial.println(c);	// 数をシリアルに出力
  } 
} 


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

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

【次ページへ続きます】

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です