Перейти к содержанию

Почему pulseIn() возвращает 0?

Обновлено Редакция GAW

pulseIn(pin, state, timeout) меряет длительность одного импульса заданного уровня и проходит три этапа. Сначала она ждёт, пока закончится импульс, который мог уже идти в момент вызова, — иначе отмерила бы его хвост. Потом ждёт начала нового импульса нужного уровня и, наконец, его конца. Если на любом этапе время вышло, функция возвращает 0 — отдельного кода ошибки у неё нет.

Когда получается ноль

СитуацияПочему 0
В state указан не тот уровеньфункция ждёт импульс, которого нет: у датчика с активным низким уровнем нужен LOW
Сигнала нет: обрыв, не тот вывод, датчик не запущенначало импульса так и не наступило
Импульс длиннее таймаутатаймаут отсчитывается от вызова, и на импульс не хватает времени
Вызов пришёлся на середину импульса, а следующий придёт нескоровремя ушло на ожидание конца текущего и начала следующего
Вывод в воздухе, сигнал от датчика с открытым коллекторомбез подтяжки уровень не меняется или «шумит»

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

Таймаут под задачу

// Echo pulse is expected below 25 ms: wait at most 30 ms instead of 1 s
unsigned long width = pulseIn(7, HIGH, 30000UL);
if (width == 0) {
  // no pulse: sensor missing, out of range or wrong level
}

Таймаут задаётся в микросекундах и типа unsigned long; по умолчанию в Arduino.h — 1 000 000 мкс. При коротком таймауте loop() не замирает на секунду, когда импульса нет.

Как micros() получает время и почему у неё шаг 4 мкс, рассказано в уроке «millis, micros и delay». Для точного измерения длительности без блокировки служит захват по входу таймера, а пример для STM32 — в ответе «Как измерить частоту таймером STM32».

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

Чем pulseIn() отличается от pulseInLong()?

pulseIn() считает такты в цикле на ассемблере и лучше подходит для коротких импульсов, особенно при запрещённых прерываниях. pulseInLong() меряет время через micros(), поэтому при запрещённых прерываниях не работает, но точнее на длинных импульсах, когда прерывания таймера мешают счёту тактов.

Почему pulseIn() тормозит всю программу?

Она блокирующая: пока ждёт, loop() стоит, а без импульса — весь таймаут, по умолчанию целую секунду. Укажите таймаут чуть больше самого длинного ожидаемого импульса или меряйте прерыванием: attachInterrupt по изменению уровня и micros() в обработчике.

Можно ли так измерить частоту сигнала?

Можно: период равен сумме pulseIn(pin, HIGH) и pulseIn(pin, LOW), измеренных подряд, но это два разных периода сигнала. Для стабильной частоты точнее аппаратный захват таймера — вход ICP1 Timer1 на выводе 8 Uno.

Источники

  1. Arduino. ArduinoCore-avr 1.8.8: cores/arduino/wiring_pulse.c — pulseIn: таймаут переводится в число проходов цикла countPulseASM, около 16 тактов на проход; при истечении возвращается 0; работает с импульсами от 2–3 мкс до 3 минут, вызывать нужно хотя бы за несколько десятков микросекунд до начала импульса; лучше работает с короткими импульсами при запрещённых прерываниях; pulseInLong: ждёт конца предыдущего импульса, начала нового и его конца, каждый раз сравнивая micros() − startMicros с таймаутом, отсчитанным от вызова, и возвращает 0 при превышении; опирается на micros() и не работает при запрещённых прерываниях
  2. Arduino. ArduinoCore-avr 1.8.8: cores/arduino/Arduino.h — объявления pulseIn(pin, state, timeout = 1000000L) и pulseInLong(pin, state, timeout = 1000000L) — таймаут по умолчанию 1 000 000 мкс
  3. Microchip. ATmega48A/PA/88A/PA/168A/PA/328/P Data Sheet (DS40002061B) — разд. 1 (расположение выводов): PB0 — PCINT0/CLKO/ICP1; разд. 14.3.1: ICP1 — вход захвата Timer/Counter1; разд. 16.6 — блок захвата Timer1

Нашли ошибку?