Я пытаюсь укусить данные DMX, и это требует импульсов 4us. Не очень удачно с результатами, я проверяю, насколько хорош Arduino в задержке ... Кажется, это довольно ужасно.
Вот небольшой тест, который я сделал:
unsigned long ptime;
void setup() {
Serial.begin(9600);
}
void loop() {
ptime = micros();
delayMicroseconds(4);
Serial.println(micros() - ptime);
delay(1000); //just to keep the serial monitor from going nuts
}
И результаты:
8 4 8 4 4 4 4 4 8 8 8 8 4 8 8 4 4 8 4 4 4 8 8 8 4 8 4
Я был шокирован, насколько это плохо. Это вдвое больше времени, которое я хотел отложить, но это даже не соответствует тому, где я мог бы просто разделить на 2!
Что я могу сделать, чтобы получить правильные, последовательные результаты?
arduino-mega
timers
time
bwoogie
источник
источник
Ответы:
Как объяснялось в предыдущих ответах, ваша настоящая проблема не в точности
delayMicroseconds()
, а в разрешенииmicros()
.Однако, чтобы ответить на ваш актуальный вопрос , есть более точная альтернатива
delayMicroseconds()
: функция_delay_us()
из AVR-libc является точной по циклу и, например,делает именно то, что говорит. Главное предостережение в том, что аргумент должен быть константой времени компиляции. Вы должны для
#include <util/delay.h>
того, чтобы иметь доступ к этой функции.Также обратите внимание, что вам нужно блокировать прерывания, если вы хотите точную задержку.
Изменить : В качестве примера, если бы я должен был генерировать импульс 4 мкс на PD2 (контакт 19 на Мега), я бы поступил следующим образом. Во-первых, обратите внимание, что следующий код
генерирует длительный импульс 0,125 мкс (2 такта ЦП), потому что это время, необходимое для выполнения инструкции, которая устанавливает порт
LOW
. Затем просто добавьте недостающее время в задержку:и у вас есть тактовая ширина импульса. Стоит отметить, что этого нельзя достичь
digitalWrite()
, так как вызов этой функции занимает около 5 мкс.источник
Ваши результаты теста вводят в заблуждение.
delayMicroseconds()
фактически задерживается довольно точно (для задержек более 2 или 3 микросекунд). Вы можете просмотреть его исходный код в файле /usr/share/arduino/hardware/arduino/cores/arduino/wiring.c (в системе Linux; или по аналогичному пути в других системах).Тем не менее, разрешение
micros()
составляет четыре микросекунды. (См., Например, страницу garretlab оmicros()
.) Следовательно, вы никогда не увидите показания от 4 до 8 микросекунд. Фактическая задержка может составлять всего несколько циклов в течение 4 микросекунд, но ваш код сообщит об этом как 8.Попробуйте выполнить 10 или 20
delayMicroseconds(4);
вызовов подряд (дублируя код, не используя цикл), а затем сообщите результатmicros()
.источник
digitalWrite()
, для выполнения которого требуется несколько микросекунд.delayMicroseconds()
? Я не вижу в этом ничего лучшего, чем округление вниз. ¶ Что касается источника неточности, если подпрограмма становится встроенной, то время зависит от окружающего кода. Вы можете прочитать списки сборки или разборки, чтобы увидеть. (См. Раздел «Составление списков сборок» в моем ответе на вопрос « Эквивалент для PORTB» в Arduino Mega 2560 , который в любом случае может иметь отношение к вашему проекту бит-бингаmicros()
имеет хорошо документированное разрешение 4 мкс.Вы можете улучшить разрешение, изменив прескалер на Таймер 0 (конечно, он выбрасывает цифры, но вы можете это компенсировать).
В качестве альтернативы используйте Таймер 1 или Таймер 2 с прескалером 1, который дает разрешение 62,5 нс.
Это будет медленно в любом случае.
Ваш вывод точно соответствует разрешению 4 мкс в
micros()
сочетании с тем фактом, что иногда вы получаете два «тика», а иногда один, в зависимости от того, когда именно вы начали отсчет времени.Ваш код является интересным примером ошибки измерения.
delayMicroseconds(4);
будет задерживаться в течение около 4 мкс. Однако ваши попытки измерить его виноваты.Кроме того, если произойдет прерывание, оно немного увеличит интервал. Вам нужно отключить прерывания, если вы хотите точную задержку.
источник
При измерении с помощью осциллографа я обнаружил, что:
delayMicroseconds(0)
=delayMicroseconds(1)
= 4 мкс реальная задержка.Итак, если вы хотите задержку 35 мкс, вам нужно:
источник
Реализация Arduino является довольно общей, поэтому может быть не такой эффективной в некоторых приложениях. Есть несколько способов для коротких задержек, каждый из которых имеет свои недостатки.
Используйте нет. Каждый из них - одна инструкция, так что 16-е из нас.
Используйте tcnt0 напрямую. Каждый из них имеет значение 4us, так как прескалер установлен на 64. Вы можете изменить прескейкер для достижения 16-тикратного разрешения.
Используйте галочки, вы можете реализовать клон Systick и использовать его в качестве основы задержки. Он предлагает более высокое разрешение плюс точность.
редактировать:
Я использовал следующий блок для определения времени различных подходов:
до этого я сбрасывал прескалер таймера 0 на 1: 1, поэтому каждый тик TCNT0 составляет 1/16 микросекунды.
Надеюсь, поможет.
источник
TCNT0
занимает 1 цикл ЦП.