delayMicroseconds и время выполнения микросекунд необъяснимо

Я только что поэкспериментировал с точностью синхронизации моего Arduino Uno R3, используя этот код:

unsigned long beginTime;
unsigned long endTime;


void setup() {
  Serial.begin(9600); // открываем последовательный порт
}

void loop() {
  beginTime = micros();
  delayMicroseconds(200);
  endTime = micros();
  delay(500);
  Serial.println(endTime-beginTime);
  delay(500);
}

Результаты, которые я получил (просто выдержка):

200
204
200
200
204
204
204
200
212
208
212
204
204

Я пытался понять это поведение, прочитайте эту ссылку Могу ли я сделать delayMicroseconds более точным?.

Из-за времени микросекунд (4 мкс) я понимаю значения между 200 и 208. Но почему я иногда получаю значения 212 микросекунд? Я любопытно, так как я купил Arduino из-за программирования на «голом железе» и ограниченного времени выполнения.

, 👍-1

Обсуждение

Возможно, во время измерения иногда возникают прерывания. Это увеличило бы фактическое время выполнения., @chrisl

попробуйте добавить Serial.flush(), чтобы дождаться завершения Serial, @Juraj

@Juraj: Спасибо за предложение. Я попробовал (т.е. добавил Serial.flush(); после println, но это не сработало., @Michael S

@chrisl: Спасибо, я добавил noInterrupts(); перед первым micros(); и interrupts(); после endTime = micros();. Я получил стабильные значения, 200 или 204 микросекунды, что и ожидалось :). Знаете ли вы, какие прерывания могут быть вызваны при выполнении такой простой программы? Я думал, что Serial.flush(); может остановить любое возможное прерывание, но это не так.., @Michael S

Ядро Arduino использует прерывание таймера для отсчета времени., @Edgar Bonet


1 ответ


Лучший ответ:

2

Ядро Arduino использует аппаратный Timer0 с его ISR (процедурой обслуживания прерываний) для хронометража. В то время как delayMicroseconds() напрямую использует значение аппаратного таймера, delay() и millis() обрабатываются ISR. Он будет вызываться регулярно.

Если ISR выполняется во время вашего измерения, то время выполнения ISR будет добавляться ко времени выполнения вашего измеренного кода. Таким образом, вы иногда получаете более высокий счет.

Чтобы предотвратить это, вы можете либо деактивировать все прерывания (через noInterrupts()), либо деактивировать конкретное прерывание, либо остановив Timer0:

TCCR0B &= ~( (1 << CS02) | (1 << CS01) | (1 << CS00) );

или запустив таймер, но отключив его прерывания:

TIMSK0 = 0;

Подробности см. в техническом описании Atmega328p. , который является микроконтроллером Arduino Uno.

,