Есть ли способ отключить прерывание через определенный промежуток времени?

Я пытаюсь сэкономить электроэнергию, поставив свое устройство в режим сна, как только он пропустил код один раз и вернул его на каждые 33 миллисекунды. Таким образом, в основном, если код заканчивается в течение 20 миллисекунд, устройство может засыпать в течение оставшихся 13 миллисекунд, и оно будет включено interupt, который будет отключен каждые 33 миллисекунды.

В соответствии с документацией прерывание основано на изменении состояния вывода, но я уверен, что вовремя должен быть такой способ. Если нет, то есть ли способ установить общее время, в течение которого устройство спит? Функция сна, казалось, просто выключила устройство, но может ли быть задан параметр, который нужно просто отключить на определенное время?

2 голоса | спросил Angel Lockhart 15 J0000006Europe/Moscow 2016, 18:08:46

2 ответа


2

Самый простой способ достичь своей цели - настроить таймер, чтобы стрелять каждые 33 мс, и используйте это прерывание, чтобы разбудить MCU. Для этого вам сначала нужно выбрать, какой таймер использовать. На Мега, я скорее всего, использует 16-разрядный таймер, но ATtiny85 имеет только 8-битный таймеры. Затем вам нужно выбрать значение предварительного делителя (тактовая частота разделение) среди доступных для этого таймера. Предделитель должен быть достаточно большой, чтобы избежать переполнения вашего таймера, но меньший предделитель значения дают лучшее временное разрешение.

Затем вы настраиваете таймер для запуска в режиме «CTC» (очистить таймер включен сравните матч). В этом режиме он отсчитывает от нуля до значения, которое вы запрограммирован в регистре OCRxA (где x это число, идентифицирующее таймер), а затем возвращается к нулю. Таким образом, вы можете получить прерывайте каждый цикл таймера OCRxA + 1.

В приведенном ниже примере кода вы должны заменить x соответствующим номер таймера и проверить спецификацию для битов, установленных в элементе управления регистры TCCR*:

 #include <avr/interrupt.h>

// 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();
}

Изменить . Как заметил Гербен, основная библиотека Arduino настраивается Таймер 0 для отправки периодических прерываний (один раз каждые 1024 мкс), который (millis(), delay() и co.). Эта прерывания пробудят MCU и не позволят ему спать до тех пор, пока вы хотите. Простейшее решение, которое реализовано в приведенном выше коде, - отключить прерывания таймера 0. Недостатком является то, что Время работы Arduino больше не будет работать.

Если вам нужны эти функции времени, то правильным решением является один набросок Гербена в его комментарии: установить логическое прерывание которые могут быть проверены в основном цикле. Вот наивная реализация этой идеи: 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. Основной цикл считывает значение time_elapsed и получается быть ложным
  2. В этот момент срабатывает прерывание, и ISR устанавливает, что boolean true
  3. Возвращаясь из ISR, основной цикл не проверяет флаг снова (так как он просто сделал это) и переходит прямо к sleep_mode();.

В этот момент MCU заканчивает спать, хотя он должен выполнять работа, которая должна быть сейчас.

Это состояние гонки не может быть для вас большой проблемой: MCU будет в конце концов разбудил прерывание таймера 0 не более 1024 мкс позже. В некоторых случаях это есть проблема, но, к счастью, есть решение. Он описан на странице о спальном состоянии avr-libc документация . Он полагается на то, что когда прерывания отключен, затем снова активирован с помощью sei(), право машинной инструкции после того, как sei() гарантированно будет выполняться до того, как любое прерывание будет техническое обслуживание. Тогда правильный способ проверить флаг и сон:

 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 сразу.

ответил Edgar Bonet 15 J0000006Europe/Moscow 2016, 18:53:00
0

Существует много видов прерываний. Внешние прерывания (типа, который вы используете с attachInterrupt()), являются только одним.

Почти каждая внутренняя периферия в чипе AVR может генерировать прерывание (или несколько прерываний в некоторых случаях). Одним из наиболее полезных является прерывание таймера , которое запускается одним из таймеров при переполнении таймера.

Я не знаком с механизмами сна AVR, но я бы предположил, что прерывание таймера может разбудить его от сна - предполагается, что таймер может работать во время сна.

Вы должны проверить документацию на чипе на Arduino, а не полагаться на (немного скучную и основную и часто вводящую в заблуждение) документацию на веб-сайте Arduino, когда дело доходит до того, что вы делаете что-то низкоуровневое.

ответил Majenko 15 J0000006Europe/Moscow 2016, 18:12:09

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132