Могу ли я сделать delayMicroseconds более точным?

Я пытаюсь бить данные DMX, и для этого нужны импульсы 4us. Не имея большой удачи в результатах, я проверяю, насколько хорошо Ардуино задерживается ... Кажется, это ужасно.

Вот небольшой тест, который я сделал:

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!

Есть ли что-нибудь, что я могу сделать, чтобы получить правильные, согласованные результаты?

8 голосов | спросил bwoogie 22 AMpSat, 22 Apr 2017 05:17:58 +030017Saturday 2017, 05:17:58

5 ответов


6

Как объяснялось в предыдущих ответах, ваша фактическая проблема не является точность delayMicroseconds(), а скорее разрешение micros().

Однако, чтобы ответить на ваш фактический вопрос , существует более точная альтернатива delayMicroseconds(): функция _delay_us() от AVR-libc является точной по циклу и, например,

 _delay_us(1.125);

делает именно то, что он говорит. Главное предостережение состоит в том, что аргумент должен быть константой времени компиляции. Вы должны #include <util/delay.h> в чтобы иметь доступ к этой функции.

Обратите внимание, что вам нужно блокировать прерывания, если вы хотите, чтобы точная задержка.

Изменить . В качестве примера, если бы я должен был генерировать импульс 4Â на PD2 (вывод 19 на Mega), я бы сделал следующее. Во-первых, обратите внимание, что следующий код

 noInterrupts();
PORTD |= _BV(PD2);   // PD2 HIGH
PORTD &= ~_BV(PD2);  // PD2 LOW
interrupts();

делает длительный импульс 0.125 Âμs (2 процессора), потому что это времени, которое требуется для выполнения команды, которая устанавливает порт LOW. Затем, просто добавьте недостающее время в задержку:

 noInterrupts();
PORTD |= _BV(PD2);   // PD2 HIGH
_delay_us(3.875);
PORTD &= ~_BV(PD2);  // PD2 LOW
interrupts();

, и вы получите точную длительность импульса. Стоит отметить, что это не может быть достигнуто с помощью digitalWrite(), как вызов этой функции занимает около 5Â Âμs.

ответил Edgar Bonet 22 AMpSat, 22 Apr 2017 10:14:57 +030014Saturday 2017, 10:14:57
3

Результаты вашего теста вводят в заблуждение. delayMicroseconds() на самом деле задерживается достаточно точно (для задержек более 2 или 3 микросекунд). Исходный код можно проверить в файле /usr/share/arduino/hardware/arduino/cores/arduino/wiring.c (в системе Linux или на каком-то аналогичном пути в других системах).

Однако разрешение micros() равно четырем микросекундам. (См., Например, страница garretlab о micros () .) Следовательно, вы никогда не увидите показания между 4 микросекундами и 8 микросекундами. Фактическая задержка может составлять всего несколько циклов в течение 4 микросекунд, но ваш код сообщит об этом как 8.

Попробуйте выполнить 10 или 20 micros() вызовы в строке (путем дублирования кода, а не с помощью цикла), а затем сообщить результат delayMicroseconds(4);.

ответил James Waldby - jwpat7 22 AMpSat, 22 Apr 2017 05:38:34 +030038Saturday 2017, 05:38:34
2
  

Я проверяю, насколько хорошо Ардуино задерживается ... Кажется, это ужасно.

micros() имеет документально подтвержденный разрешение 4 Âμs.

Вы можете улучшить разрешение, изменив предварительный делитель для таймера 0 (конечно, это выбрасывает цифры, но вы можете компенсировать это).

Альтернативно используйте Таймер 1 или Таймер 2 с предварительным делителем 1, который дает разрешение 62,5 нс.


 Serial.begin(9600);

Это все равно будет медленным.


  

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

Ваш результат точно соответствует 4-мерному разрешению micros() в сочетании с тем фактом, что иногда вы получаете два «тикания», а иногда и один, в зависимости от того, когда вы начали отсчет времени.


Ваш код является интересным примером ошибки измерения. delayMicroseconds(4); задержится на близком расстоянии до 4 мкс. Однако ваши попытки измерить это виноваты.

Кроме того, если происходит прерывание, оно немного растянет интервал. Вам нужно отключить прерывания, если вы хотите точную задержку.

ответил Nick Gammon 22 AMpSat, 22 Apr 2017 09:19:05 +030019Saturday 2017, 09:19:05
1

При измерении с помощью осциллографа я обнаружил, что:

delayMicroseconds(0) = delayMicroseconds(1) = 4 μs реальная задержка.

Итак, если вам нужна задержка 35 ¼ ¼, вам нужно:

delayMicroseconds(31);
ответил sigi 1 J000000Saturday17 2017, 03:20:40
0
  

Есть ли что-нибудь, что я могу сделать, чтобы получить правильные, согласованные результаты?

Реализация Arduino довольно общая, поэтому может быть не столь эффективной в некоторых приложениях. Есть несколько способов для коротких задержек, каждый из которых имеет свои недостатки.

  1. Используйте nop. Каждая из них - одна инструкция, так что 16 из нас.

  2. Используйте tcnt0 напрямую. Каждый из них - 4us, так как предварительный делитель установлен на 64. Вы можете изменить дозатор, чтобы достичь разрешения 16-го.

  3. Используйте тики, вы можете реализовать клон из systick и использовать его в качестве основы задержки. Он предлагает более точное разрешение и точность.

изменить:

Я использовал следующий блок времени для различных подходов:

time0=TCNT0;
delay4us();             //65
//t0delayus(4*16);          //77
//_delay_us(4);             //65
//delayMicroseconds(4);     //45
time1=TCNT0 - time0;        //64 expected

до этого я сбросил предварительный делитель таймера 0 до 1: 1, чтобы каждый тик TCNT0 составлял 1/16 секунды микросекунды.

  1. delay4us () создается из NOP (); он произвел задержку в 65 тиков или чуть больше 4us;
  2. t0delayus () создается с задержкой таймера0. это вызвало задержку в 77 тиков; немного хуже, чем delay4us ()
  3. _delay_us () - это функция gcc-avr. производительность наравне с delay4us ();
  4. delayMicroseconds () произвела задержку в 45 тиков. способ, которым arduino реализовал свои функции синхронизации, имеет тенденцию к недооценке, если только в среде с другими прерываниями.

надеюсь, что это поможет.

ответил dannyf 1 J000000Saturday17 2017, 13:22:49

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

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132