一定の時間が経過した後に割り込みを設定する方法はありますか?

私はコードを一度実行して33ミリ秒ごとにそれを戻すと、デバイスをスリープ状態にして節電しようとしています。基本的に、コードが20ミリ秒で実行を終了すると、デバイスは残りの13ミリ秒間スリープ状態になり、33ミリ秒ごとにオフになるように設定されているインターハンドによってオンになります。

ドキュメントによると、割り込みはピンの状態の変更に基づいていますが、時間通りにそれをベースにする方法がなければならないと確信しています。デバイスがスリープしている合計時間を設定する手段がない場合は、スリープ機能はデバイスをオフにしたようですが、一定の時間だけオフにするパラメータを与えることはできますか?

2
Arduino Megaを定期的に起動するタイマーの設定例については、この回答を参照してください。
追加された 著者 Sprogz,
TIMSK1 = _BV(OCIE1A);//TIMER1_COMPA割り込みを有効にするは、コメントが示すように割り込みを有効にします。
追加された 著者 Sprogz,
これは普通のavr-libcですが、Arduino環境下で動作します。
追加された 著者 Sprogz,
質問をフォローしてください、このコードはArduinoですか?私はmainメソッドを参照していますが、setup()やloop()はありません。
追加された 著者 Cicely Mason,
私はスリープ機能を見ましたが、どこで実際の割り込みを設定していますか?
追加された 著者 Cicely Mason,

2 答え

The easiest way to achieve your purpose seems to be to configure a timer to fire every 33 ms, and use this interrupt to wake up the MCU. For this, you first have to choose what timer to use. On the Mega, I would rather use the 16-bit timer, but the ATtiny85 only has 8-bit timers. Next you have to choose a prescaler value (clock frequency division) among those available for that timer. The prescaler should be large enough to avoid overflowing your timer, but smaller prescaler values give better time resolution.

次に、 "CTCモード"で実行するようにタイマーを設定します(タイマーをクリア コンペアマッチ)。このモードでは、ゼロから値をカウントします OCRxA レジスタにプログラムされています( x は、 タイマー)、ゼロに戻ります。あなたは すべての OCRxA + 1 タイマサイクルを中断します。

以下のサンプルコードでは、 x を適切な タイマー番号をチェックし、ビットシートがコントロールに設定されているかどうかをチェックします TCCR * を登録します。

#include 

// Interrupt period, in prescaled clock cycles.
const uint16_t PERIOD = ...; //or uint8_t for an 8-bit timer.

// The timer interrupt is used ONLY to wake the CPU, so there is no job
// to be done inside the interrupt service routine.
EMPTY_INTERRUPT(TIMERx_COMPA_vect);

void setup()
{
   //Disable Timer 0 interrupts.
   //Warning: this will prevent the Arduino from keeping time.
    TIMSK0 = 0;

   //Configure Timer x.
    TCCRxA = 0;           //undo the timer config done...
    TCCRxB = 0;           //...by the Arduino core library
    TCNTx = 0;            //reset the timer
    OCRxA = PERIOD - 1;   //set the period
    TIMSKx = _BV(OCIExA); //enable TIMERx_COMPA interrupt
    TCCRxA = ...;         //set the counting mode to CTC...
    TCCRxB = ...;         //...and set the prescaler
}

void loop()
{
   //Sleep while waiting for the timer interrupt.
    sleep_mode();

   //Now that we have been waken up...
    do_the_job();
}

Edit: As noticed by Gerben, the Arduino core library configures Timer 0 to send periodic interrupts (one every 1024 µs), which are used for timekeeping (millis(), delay() and co.). This interrupts will wake up the MCU and prevent it from sleeping as long as you want. The simplest solution, which is implemented in the code above, is to disable the Timer 0 interrupts. The drawback is that the Arduino timekeeping functions will not work anymore.

これらの計時機能が必要な場合は、適切な解決策は 彼のコメントでGerbenによってスケッチされたもの:割り込みにブール値を設定する メインループでチェックすることができます。ここには素朴な実装があります このアイデアの EMPTY_INTERRUPT(TIMERx_COMPA_vect);

volatile bool time_elapsed;

ISR(TIMERx_COMPA_vect)
{
    time_elapsed = true;
}

次に、メインループで、ISRがフラグを設定するまで繰り返しスリープします。

void loop()
{
   //Warning: race condition here!
    while (!time_elapsed)
        sleep_mode();
    time_elapsed = false;

   //Now that we have been waken up...
    do_the_job();
}

コメントに記載されているように、この素​​朴な実装では、 競合状態として知られています。問題は、次のような状況にあります。

  1. The main loop reads the value of time_elapsed and it turns out to be false
  2. At this point the interrupt fires and the ISR sets that boolean to true
  3. Returning from the ISR, the main loop does not test the flag again (since it just did it) and goes straight to sleep_mode();.

この時点で、MCUは実行する必要があるものの、スリープ状態になります 今は期限が切れている仕事。

This race condition may not a be a big deal to you: The MCU will be eventually woken up by the Timer 0 interrupt no more than 1024 µs later. In some cases it is an issue, but fortunately there is a proper solution. It is described in the page about sleeping of the avr-libc documentation. It relies on the fact that when interrupts are disabled, then enabled again with sei(), the machine instruction right after the sei() is guaranteed to be executed before any interrupt is serviced. Then the proper way to test the flag and sleep is:

void loop()
{
    while (!time_elapsed) {
        cli();
        if (!time_elapsed) {
            sleep_enable();
            sei();         //interrupts enabled
            sleep_cpu();   //this will be executed before any ISR
            sleep_disable();
        }
        sei();
    }
    time_elapsed = false;

   //Now that we have been waken up...
    do_the_job();
}

このバージョンでは、テストの直後に割り込みが発生した場合 if(!time_elapsed)の場合、 sleep_cpu()の後にサービスされます。 MCUをすぐに目覚めさせる効果があります。

2
追加された
気づいてくれてありがとう@Gerben。あなたは、絶対に正しい。私はこの問題を説明する答えを修正しました。
追加された 著者 Sprogz,
カウンタは0から OCRxA (含む)をカウントするので、期間は OCRxA + 1 です。期間はプリスケールされたクロックサイクルです(プリスケーラの設定を参照)。
追加された 著者 Sprogz,
競争状態の良い解決策。
追加された 著者 Al.,
Timer0(ミリ秒と遅延のために使用される)もデバイスをウェイクアップさせます。割り込みの中でブール値を設定する必要があるかもしれないので、メインループはスリープ状態に戻る必要があるかどうかを確認することができます
追加された 著者 Al.,
うわーは大いに役立ちますが、なぜ PERIOD から1を引いていますか?その他の例もそうしました。また、 PERIOD は割り込み間の期間であるはずですが、正しいのでしょうか?私のプロジェクトでは、33ミリ秒に相当するクロックサイクル数に等しい値に設定しました。右?
追加された 著者 Cicely Mason,

多くの種類の割り込みがあります。 attachInterrupt()で使用する種類の外部割り込みは1つだけです。

AVRチップ内のほとんどすべての内部ペリフェラルは、割り込み(または場合によっては複数の割り込み)を生成する可能性があります。一番便利なのは、タイマーのオーバーフロー時にタイマーの1つがトリガーするタイマー割り込みです。

私はAVRのスリープメカニズムに精通していませんが、スリープ中にタイマーを実行できると仮定して、タイマー割り込みがスリープから復帰すると仮定します。

このような低レベルの作業を行う場合は、ArduinoのWebサイトにある(幾分かまいませんが、しばしば誤解を招く)文書に頼るのではなく、Arduinoのチップのドキュメントをチェックする必要があります。

0
追加された
あなたは正しいです:タイマー割り込みは、MCUを起動させることができますが、それはいわゆる "IDLE"モードでスリープしている場合のみです...これはデフォルトのスリープモードです。
追加された 著者 Sprogz,
@AngelLockhart:そのと異なることはよくありません。おそらくATmega2560とATtiny84Aの16ビットタイマに同じコードを使用できます。どんなATtinyをターゲットにしていますか?どのクロック周波数ですか?
追加された 著者 Sprogz,
両者の動作原理はほぼ同じです。数字は変更されます(時間とクロック周波数の計算)、レジスタ名が変更される可能性があります(利用可能なタイマーの数などによって異なります)。しかし、一般的には、同じ幅広いファミリにチップを設計する場合には、これらのような論理ブロックのコピーアンドペーストがたくさんあります。
追加された 著者 Majenko,
タイマーはクロックティックで動作します。実行する必要がある時間を構成するクロックティックの数を計算する必要があります。もちろん、クロックが動作している速度に応じて変化します。
追加された 著者 Majenko,
はい。おそらく#ifdef ... #endifを多用しているので、ターゲットのマイクロコントローラとプログラムの両方のドキュメントを読むべきです
追加された 著者 Majenko,
@EdgarBonet私はATtiny85を使用していますが、番号が変わる理由も説明できますか?私の計画は、プログラムが millis()で実行された時間を計算し、それを33ミリ秒から減算することでした。それはうまくいかないでしょうか?
追加された 著者 Cicely Mason,
タイマ割り込みの設定は、マイクロコントローラごとに異なりますか?私はArduino Megaにこのコードを書いて、それをATtinyに移植しようとしています。
追加された 著者 Cicely Mason,