Про функцию micros() — как она устроена…

В предыдущем расследовании стало ясно как работает функция millis(). Есть у Arduino более мелкая функция micros(). Посмотрим, что у нее внутри…

micros()

Возвращает количество микросекунд с момента начала выполнения текущей программы на плате Arduino. Значение переполняется и сбрасывается на ноль, приблизительно через 70 минут.

Долго в «дебрях» подкаталогов каталога установки Arduino IDE блуждать не приходится в том же файле: ..\Arduino\hardware\arduino\cores\arduino\wiring.c находим код, описывающий эту функцию:

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

Здесь я немного повыкидывал строчки кода — условия проверки кристалла — оставлено то, что актуально для младших ардуин: UNO, mini и прочих.

В результате видно, что используется тот же Таймер 0, что и в функции millis(). Но возвращаемое значение микросекунд берется уже с учетом состояния счетчика таймера, а не только счетчика числа его переполнений (см считывание TCNT0 и timer0_overflow_count). Результат упаковывается в четыре байта без знака. (Число переполнений таймера в переменной timer0_overflow_count считается в обработке прерывания от Таймера 0 — см предыдущую статью). Ранее было отмечено, что Таймер 0 в Arduino UNO тактируется с частотой 16МГц/64 — такой уж разработчиками выбран предделитель, чтоб получить период переполнений таймера близкий к 1 мс.

В итоге имеем на выходе число микросекунд, прошедших с момента запуска контроллера. Переполнение такого четырехбайтного счетчика при микросекундных тактах происходит раз в  2 в 32 степени мкс, т.е. примерно раз в 71,5 минуту.

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

Serial.println(clockCyclesPerMicrosecond());

для 16МГц, естественно, получим 16 (можно перевод названия посмотреть: «число циклов в микросекунде»). Т.е. дискрет результата выполнения micros() для UNO 16МГц будет 4мкс!

Отметим интересный факт: даже при запрещенных прерываниях, в отличие от millis(),  функция micros() вернет вполне адекватное значение, правда только в младшем байте. Период его переполнений — 256мкс!

Добавить комментарий

Ваш e-mail не будет опубликован.

Нажимая кнопку "Отправить" Вы даёте свое согласие на обработку введенной персональной информации в соответствии с Федеральным Законом №152-ФЗ от 27.07.2006 "О персональных данных".