Использование millis () и micros () внутри процедуры прерывания

Документация для attachInterrupt() говорит:

  

... millis() полагается на прерывания для подсчета, поэтому он никогда не будет увеличиваться внутри ISR. Поскольку delay() требует прерываний для работы, он не будет работать, если вызван внутри ISR. micros() работает изначально, но начнет вести себя беспорядочно через 1-2 мс. ...

Как micros() отличается от millis() (за исключением, конечно, их точности)? Означает ли вышеупомянутое предупреждение, что использование micros() внутри процедуры прерывания всегда является плохой идеей?

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

8 голосов | спросил Petr Pudlák 24 MarpmThu, 24 Mar 2016 21:47:56 +03002016-03-24T21:47:56+03:0009 2016, 21:47:56

3 ответа


11

Другие ответы очень хорошие, но я хочу подробно остановиться на том, как работает micros(). Он всегда считывает текущий аппаратный таймер (возможно, TCNT0), который постоянно обновляется аппаратным обеспечением (фактически, каждые 4 Â из-за предварительного делителя 64). Затем он добавляет в счетчик переполнения таймера 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), поэтому он может справиться с переполнением, в то время как прерывания отключены, но только один раз - нет средств для обработки двух переполнений.


TLDR;

  • Не делайте задержек внутри ISR
  • Если вы должны сделать это, вы можете время с помощью micros(), но не millis(). Также возможно delayMicroseconds().
  • Не задерживайте более 500 мкс или так, иначе вы пропустите переполнение таймера.
  • Даже короткие задержки могут привести к тому, что вы пропустите входящие последовательные данные (при 115200 бодах вы получите новый символ каждые 87 Âμ).
ответил Nick Gammon 25 MaramFri, 25 Mar 2016 01:41:29 +03002016-03-25T01:41:29+03:0001 2016, 01:41:29
5

Неправильно использовать millis() или micros() в рамках процедуры прерывания.

Неправильно использовать неправильно.

Главное, что вы находитесь в режиме прерывания «часы не тикают». millis() и micros() не изменится (ну, сначала micros(), но как только он пройдет мимо этой магической миллисекундной точки где требуется миллисекундный тик, все это разваливается.)

Таким образом, вы можете, конечно, вызвать millis() или micros(), чтобы узнать текущее время в вашем ISR, но не ожидайте, что это время изменится.

Это отсутствие изменений во времени, о котором предупреждается в приведенной вами цитате. delay() зависит от изменения millis(), чтобы узнать, сколько времени прошло. Поскольку он не меняет delay(), он не может закончиться.

Таким образом, по существу millis() и micros() сообщит вам время , когда ваш ISR был вызван , независимо от того, когда в вашем ISR вы используйте их.

ответил Majenko 24 MarpmThu, 24 Mar 2016 22:40:18 +03002016-03-24T22:40:18+03:0010 2016, 22:40:18
4

Цитата фразы not предупреждение, это просто заявление о том, как все работает.

Нет ничего по-настоящему неправильного в использовании millis() или micros() в правильно написанной процедуре прерывания.

С другой стороны, делать что-либо вообще в некорректно написанной процедуре прерывания по определению неверно.

Процедура прерывания, которая занимает больше нескольких микросекунд для выполнения своей работы, по всей вероятности, неправильно написана.

Вкратце: правильно написанная процедура прерывания не вызовет или не столкнется с проблемами с millis() или micros().

Изменить: Относительно «whyc micros ()» начинает вести себя беспорядочно », как описано в« исследование микросфер Arduino - веб-страница, код micros() на обычный Uno функционально эквивалентен

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

Это возвращает четырехбайтовый беззнаковый длинный, состоящий из трех младших байтов из timer0_overflow_count и одного байта из регистра счетчика таймера 0.

timer0_overflow_count увеличивается примерно один раз в миллисекундах с помощью обработчика прерываний TIMER0_OVF_vect, как описано в

ответил James Waldby - jwpat7 24 MarpmThu, 24 Mar 2016 22:26:00 +03002016-03-24T22:26:00+03:0010 2016, 22:26:00

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

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

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