割り込みルーチン内でmillis()およびmicros()を使用する


13

のドキュメントattachInterrupt()

... millis()カウントは割り込みに依存しているため、ISR内でインクリメントすることはありません。機能するためdelay()に割り込みが必要なので、ISR内で呼び出された場合は機能しません。micros()最初は動作しますが、1〜2ミリ秒後に不規則に動作し始めます。...

どのようにmicros()違いますかmillis()(その精度の点を除きます)?上記の警告はmicros()、割り込みルーチン内での使用は常に悪い考えであることを意味していますか?

コンテキスト- 低パルス占有率測定したいので、入力信号が変化したときにルーチンをトリガーし、現在の時刻を記録する必要があります。

回答:


16

他の答えは非常に良いですが、どのようにmicros()機能するかについて詳しく説明したいと思います。それは常に、現在のハードウェアタイマー(おそらく読み込みTCNT0常にハードウェアによって更新されている)(実際には、理由は64のプリスケーラのすべての4マイクロ秒)。次に、タイマー0オーバーフローカウントを追加します。これは、タイマーオーバーフロー割り込み(256倍)によって更新されます。

したがって、ISR内でもmicros()更新に依存できます。ただし、長すぎるとオーバーフローの更新に失敗し、返される結果が低下します(つまり、253、254、255、0、1、2、3などを取得します)

これはmicros()-他のプロセッサの定義を削除するためにわずかに単純化されています:

unsigned long micros() {
    unsigned long m;
    uint8_t oldSREG = SREG, t;
    cli();
    m = timer0_overflow_count;
    t = TCNT0;
    if ((TIFR0 & _BV(TOV0)) && (t < 255))
        m++;
    SREG = oldSREG;
    return ((m << 8) + t) * (64 / clockCyclesPerMicrosecond());
}

上記のコードではオーバーフローが許可されているため(TOV0ビットをチェックします)、割り込みがオフになっているが1 だけオーバーフローしている場合に対処できます。2つのオーバーフローを処理するための準備はありません。


TLDR;

  • ISR内で遅延を行わないでください
  • それらを行う必要がある場合は、で時間を計ることができますが、ではありませmicros()millis()。また、delayMicroseconds()可能性があります。
  • 500 µs程度以上遅延しないでください。そうしないと、タイマーオーバーフローが見逃されます。
  • わずかな遅延でさえ、着信シリアルデータを見逃すことがあります(115200ボーでは、87 µsごとに新しいキャラクターを取得します)。

micros()で使用される以下のステートメントを理解できません。詳しく説明してもらえますか?ISRが書き込まれているため、TOV0フラグはISRに入るとすぐにクリアされます。したがって、以下の条件が真にならない場合があります。if((TIFR0&_BV(TOV0))&&(t <255))m ++;
ラジェシュ

micros()ISRではありません。これは通常の機能です。TOV0フラグを使用すると、ハードウェアをテストして、タイマーオーバーフローが発生したかどうかを確認できます(ただし、まだ処理されていません)。
ニックギャモン

テストは割り込みをオフにして行われるため、テスト中にフラグは変更されません。
ニックギャモン

だから、micros()関数内でcli()の後、オーバーフローが発生した場合、それをチェックする必要があると言うのですか?意味がある。それ以外の場合、microsは関数ではありませんが、TIMER0_OVF_vect ISRはTIFR0のTOV0をクリアします。
ラジェシュ

また、なぜTCNT0が255と比較されて、255未満であるかを確認しますか?cli()の後、TCNT0が255に達したらどうなりますか?
ラジェシュ

8

使用したり、割り込みルーチン内で使用するのは間違っていません。millis()micros()

ある誤っそれらを使用するために間違いました。

ここでの主なことは、あなたが割り込みルーチンにいる間、「クロックが刻々と過ぎない」ということです。 millis()そして、micros()(、うまく変更されませんmicros()最初になりますが、それはミリ秒の目盛りは、それがすべて離れて落ちる必要とされている魔法のミリ秒地点を過ぎて行く後。)

したがって、ISR内で現在の時刻を確認しmillis()たりmicros()、確認したりすることはできますが、その時刻が変わることを期待しないでください。

それはあなたが提供する引用で警告されている時間のその変化の欠如です。 どれだけ時間が経過したかを知るためdelay()millis()変化に依存しています。変わらないので、終わらせるdelay()ことはできません。

だから、基本的にmillis()micros()あなたの時間教えてくれるあなたのISRが呼び出されたあなたのISRにあなたがそれらを使用する際に関係なく。


3
いいえ、micros()更新します。常にハードウェアタイマーレジスタを読み取ります。
ニックギャモン

4

引用されたフレーズがありません、それは単に物事の仕組みについての声明で、警告。

適切に作成された割り込みルーチンを使用すること、millis()またはmicros()適切に作成された割り込みルーチン内で本質的に問題はありません。

一方、不適切に記述された割り込みルーチン内で何もしないことは、定義上間違っています。

ジョブを実行するのに数マイクロ秒以上かかる割り込みルーチンは、おそらく不適切に記述されています。

要するに:A-適切に記述された割り込みルーチンは発生しませんか、との出会いの問題millis()micros()

編集:Arduino micros機能の調査」Webページで 説明されているように、「なぜmicros()が「不安定に動作し始める」」micros()に関して、通常のUnoのコードは機能的に同等です

unsigned long micros() {
  return((timer0_overflow_count << 8) + TCNT0)*(64/16);
}

これはtimer0_overflow_count、タイマー0カウントレジスタからの最下位3バイトと1バイトから構成される4バイトの符号なしlongを返します。

timer0_overflow_countよるミリ秒ごとに1回程度、インクリメントされTIMER0_OVF_vect、割り込みハンドラなどで説明したArduinoのミリ機能の検査 Webページ。

割り込みハンドラーが開始される前に、AVRハードウェアは割り込みを無効にします。(たとえば)割り込みハンドラが、割り込みがまだ無効になっている状態で5ミリ秒間実行されると、少なくとも4つのタイマー0オーバーフローが失われます。[ArduinoシステムのCコードで記述された割り込みはリエントラントではありません(同じハンドラー内で複数の重複する実行を正しく処理できます)。

つまり、タイマーオーバーフローは「スタック」しません。前回のオーバーフローからの割り込みが処理される前にオーバーフローが発生するたびに、millis()カウンタは、ミリ秒を失い、そしての不一致timer0_overflow_count順番にはなりmicros()すぎミリ秒で間違っています。

「500μsより短い」を割り込み処理の上限と見なすと、「タイマー割り込みが長すぎる間ブロックされないようにする」ために、1024μs未満(1020μsなど)に達することができますがmillis()、ほとんどの場合、時間。ただし、5μs以上かかるスラグード、10μs以上が怠slot、20μs以上がカタツムリのような割り込みハンドラーを考えます。


「物事の仕組み」、特にmicros()「不安定な振る舞いを始める理由」について詳しく説明してください。「適切に作成された割り込みルーチン」とはどういう意味ですか?私はそれが「500usより短い」(タイマー割り込みが長すぎるためにブロックされないようにする)、「通信に揮発性変数を使用する」、「可能な限りライブラリコードを呼び出さない」を意味すると思いますが、他に何かありますか?
ペトルプドラク

PetrPudlák@、編集を参照
jwpat7 -ジェームズWaldby
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.