Другие ответы очень хороши, но я хочу уточнить, как это 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 мкс).
micros()
не ISR. Это нормальная функция. Флаг TOV0 позволяет вам проверить оборудование, чтобы увидеть, произошло ли переполнение таймера (но еще не было обработано).Это не неправильно использовать
millis()
или вmicros()
рамках программы прерывания.Это является неправильным использовать их неправильно.
Главное здесь то, что, пока вы находитесь в режиме прерывания, «часы не тикают».
millis()
иmicros()
не изменится (ну,micros()
поначалу изменится, но как только он пройдет ту волшебную миллисекундную точку, где требуется миллисекундный тик, все это развалится).Таким образом, вы можете позвонить
millis()
илиmicros()
узнать текущее время в вашем ISR, но не ожидайте, что это время изменится.Это отсутствие изменений во времени, о которых предупреждают в приведенной вами цитате.
delay()
полагается наmillis()
изменение, чтобы знать, сколько времени прошло. Так как это не изменится,delay()
никогда не закончится.Так что, по существу ,
millis()
иmicros()
сообщит вам время , когда ваш ISR не был вызван независимо от того , когда в вашем ISR вы используете их.источник
micros()
обновления. Он всегда читает регистр аппаратного таймера.Цитируемая фраза - это не предупреждение, а просто заявление о том, как все работает.
Нет ничего плохого в использовании
millis()
илиmicros()
в правильно написанной программе обработки прерываний.С другой стороны, делать что-либо вообще в неправильно написанной подпрограмме прерывания по определению неправильно.
Процедура прерывания, выполнение которой занимает более нескольких микросекунд, по всей вероятности, написана неправильно.
Вкратце: правильно написанная процедура прерывания не вызовет и не столкнется с проблемами
millis()
илиmicros()
.Редактировать: Что касается того, «почему micros ()« начинает вести себя беспорядочно »», как объясняется на веб-странице « Проверка функции Arduino micros »,
micros()
код на обычном Uno функционально эквивалентенЭто возвращает четырехбайтовую длинную без знака, состоящую из трех младших байтов из
timer0_overflow_count
и одного байта из регистра отсчета таймера-0.Значение параметра
timer0_overflow_count
увеличивается примерно раз в миллисекундуTIMER0_OVF_vect
обработчиком прерывания, как объяснено при рассмотрении веб-страницы функции миллидюйма arduino .Перед началом обработчика прерываний аппаратное обеспечение AVR отключает прерывания. Если (например) обработчик прерываний будет работать в течение пяти миллисекунд с прерываниями, которые все еще отключены, то по крайней мере четыре переполнения таймера 0 будут пропущены. [Прерывания, написанные в коде C в системе Arduino, не являются реентерабельными (способными правильно обрабатывать несколько перекрывающихся выполнений в одном и том же обработчике), но можно написать обработчик реентерабельного языка ассемблера, который повторно активирует прерывания до того, как он начнет трудоемкий процесс.]
Другими словами, переполнения таймера не «складываются»; всякий раз, когда переполнение происходит до обработки прерывания от предыдущего переполнения,
millis()
счетчик теряет миллисекунду, а расхождение,timer0_overflow_count
в свою очередь, также даетmicros()
ошибку на миллисекунду.Что касается «короче 500 мкс» в качестве верхнего предела времени для обработки прерываний, «чтобы предотвратить слишком длительное блокирование прерывания таймера», вы можете подняться чуть менее 1024 мкс (например, 1020 мкс) и по-
millis()
прежнему работать, большая часть время. Тем не менее, я рассматриваю обработчик прерываний, который занимает более 5 мкс как медлительный, более 10 мкс как ленивый, более 20 мкс как улитка.источник
micros()
«начать вести себя странно»? А что вы подразумеваете под "правильно написанной программой прерываний"? Я предполагаю, что это означает «короче, чем 500 мкс» (чтобы предотвратить слишком длительное блокирование прерывания таймера), «используя переменные переменные для связи» и «не вызывая библиотечный код», насколько это возможно, есть что-нибудь еще?