Точная задержка
- Войдите на сайт для отправки комментариев
Доброго времени суток,
оговорюсь сразу я новичок и пришёл с твёрдым намереньем нервировать матёрую часть бывалых пользователей форума. ;)
Цель:
Настраиваемый 2x-канальный триггер сигнала с временой разницей триггера (задержкой) в районе (1-10 мкс, шаг 1 мкс).
К примеру: На пине D1 (по сигналу кнопки) состояние меняется с 0 на 1, далее в зависимости от выбранной задержки к примеру 2 мкс меняеся состояние с 0 на 1 на пине D2. Через 100-300 миллисекунд состояние на обоих пинах (D1, D2) возвращается снова в состояние 0.
Главная проблемма это задержка. По нижеуказанному скетчу принцип реализуется, но стабильно работает только с 5-6 мкс.
void setup()
{
pinMode(13,INPUT_PULLUP); // кнопка
pinMode(12, OUTPUT); // Trigger ch1
pinMode(11, OUTPUT); // Trigger ch2
}
void loop()
{
if(digitalRead(13)==LOW)
{
digitalWrite(12, LOW);
delayMicroseconds(10);
digitalWrite(11, LOW);
delay (200);
digitalWrite(12, HIGH);
digitalWrite(11, HIGH);
}
}
Для начала использовал Arduino UNO, потом решил взять более шуструю платформу NodeMCU на базе ESP8266. Она отказалась работать по скетчу для ардуино, пришлось немного переписать но на команды delayMicroseconds() в области 1-50 мкс она отказывается реагировать. Минимальная задержка 5 мкс.
void setup()
{
pinMode(D3, INPUT); // кнопка
pinMode(D1, OUTPUT); // Trigger ch1
pinMode(D2, OUTPUT); // Trigger ch2
}
void loop()
{
if(digitalRead(D3)==LOW)
{
digitalWrite(D1, LOW);
delayMicroseconds(4);
digitalWrite(D2, LOW);
}
else
{
digitalWrite(D1, HIGH);
digitalWrite(D2, HIGH);
}
}
Получается что проблемма в примитивном скетче с большими задержками а так же футкции "delay". Вероятно что micros() тоже не вариант из-за разрешения 4 мкс.
Каким способом можно реализовать стабильнуюб точную задержку на этих платформах в этом временном промежутке (1-10 мкс)?
Заранее спасибо.
Цель: Настраиваемый 2x-канальный триггер ....
Вот здесь не понял. Так, какова же всё-таки цель? Нервировать или триггер сделать?
(я говорю только про ардуино)
Насколько точную? Шаг в 62,5 наносекунд (если считать кварц идеальным) устроит? Если да, то делается просто при помощи таймеров. Если нет - то никаким.
Можно запытать _delay_us(0.5);
Но от digitalWrite() желательно избавиться сразу, если речь зашла о таких интервалах.
Можно запытать _delay_us(0.5);
Фига-се! А я и не знал, что она void _delay_us(double __us). Наверняка, видел когда-то, но прочно забыл :)
Да... но есть проблема - в последний раз, когда я пытался её использовать, она требовала inline указания числа, с переменной не прокатывает. Видимо как-то там, внутрях, разворачивается в NOP-ы при компиляции.
Так само-собой. Вот она
void _delay_us(double __us) { double __tmp ; #if __HAS_DELAY_CYCLES && defined(__OPTIMIZE__) && \ !defined(__DELAY_BACKWARD_COMPATIBLE__) && \ __STDC_HOSTED__ uint32_t __ticks_dc; extern void __builtin_avr_delay_cycles(unsigned long); __tmp = ((F_CPU) / 1e6) * __us; #if defined(__DELAY_ROUND_DOWN__) __ticks_dc = (uint32_t)fabs(__tmp); #elif defined(__DELAY_ROUND_CLOSEST__) __ticks_dc = (uint32_t)(fabs(__tmp)+0.5); #else //round up by default __ticks_dc = (uint32_t)(ceil(fabs(__tmp))); #endif __builtin_avr_delay_cycles(__ticks_dc); #else uint8_t __ticks; double __tmp2 ; __tmp = ((F_CPU) / 3e6) * __us; __tmp2 = ((F_CPU) / 4e6) * __us; if (__tmp < 1.0) __ticks = 1; else if (__tmp2 > 65535) { _delay_ms(__us / 1000.0); } else if (__tmp > 255) { uint16_t __ticks=(uint16_t)__tmp2; _delay_loop_2(__ticks); return; } else __ticks = (uint8_t)__tmp; _delay_loop_1(__ticks); #endif }По поводу "нервировать..." это шутка))) В основном новички на форумах хватают вершки, переспрашивают одно и тоже... мало чего понимают в тематике.... чем нервируют более опытных товарищей )))
Шаг в 62,5 нано было бы идеально....
Я был бы более чем доволен погрешностью в +/- 250 нано. )))
Ну, та в путь, всё пишется в три строки. Гуглите "работа с таймерами ардуино" или просто смотрите даташит (что лучше).
Только помните, что это низкоуровневые вещи, т.е. для разных контроллеров (например, для Uno и Mega) это пишется по-разному. Всё равно в три строчки, но по-разному.
Но от digitalWrite() желательно избавиться сразу, если речь зашла о таких интервалах.
Вот тут 10 раз согласен digitalWrite()- очень медленная функция с проверкой всего начиная от динозавров. Пишите прямо в порт.
Угу. Можно попробовать что то типа (с нумерацией мог накосячить)
#define PIN11_PORTB 3 // 11я нога ардуины #define PIN12_PORTB 4 // 12я нога ардуины ... PORTB|= (1<<PIN12_PORTB); { asm ("nop"); asm ("nop"); asm ("nop"); ... } PORTB|= (1<<PIN11_PORTB);Число nop подобрать. Если много получается - сунуть в цикл for.
Угу. Можно попробовать что то типа (с нумерацией мог накосячить)
#define PIN11_PORTB 3 // 11я нога ардуины #define PIN12_PORTB 4 // 12я нога ардуины ... PORTB|= (1<<PIN12_PORTB); { asm ("nop"); asm ("nop"); asm ("nop"); ... } PORTB|= (1<<PIN11_PORTB);Число nop подобрать. Если много получается - сунуть в цикл for.
Спасибо, большое за конкретный ответ, попробую и отпишусь что получилось.
Доброго временни суток,
сново вернулся к этой теме. По поду работы с таимерами и управлнни портами через регисты проинформировался, но не попрактиковался ))) Как говорится на предложенных примерах всё понятно, а вот самому написать сложновато ))) Но об этом позже, когда будут конкретные вопросы.
Для начала решение от Morroc.
На вскидку прошил Ардуино предложенным скетчем:
#define PIN11_PORTB 3 // 11я нога ардуины #define PIN12_PORTB 4 // 12я нога ардуины void setup() { pinMode(13,INPUT_PULLUP); } void loop() { if(digitalRead(13)==HIGH) { PORTB|= (1<<PIN12_PORTB); { asm ("nop"); asm ("nop"); ... } PORTB|= (1<<PIN11_PORTB); } }работает, очень даже точно. Спасибо за простое решение. Но есть одна несущественная мелоч. Изменение состояния происходит не мгновенно а логарифмически и занимает примерно 3-4 µs. Соединение к осцилографу ни каких поразитных емкостей не имеет.
Для сравнения, изменения состояния по скетчу который я вначале предложил, происходит мгновенно. При этом подключение остаётся тем же.
{ pinMode(13,INPUT_PULLUP); // кнопка pinMode(12, OUTPUT); // Trigger ch1 pinMode(11, OUTPUT); // Trigger ch2 } void loop() { if(digitalRead(13)==HIGH) { digitalWrite(12, LOW); delayMicroseconds(1); digitalWrite(11, LOW); delay (200); digitalWrite(12, HIGH); digitalWrite(11, HIGH); } }Простите, т.е. Вы хотите сказать, что изменение состояния пина по команде
"происходит не мгновенно а логарифмически и занимает примерно 3-4 µs", в то время как изменение состояния пина по команде
"происходит мгновенно"?
Простите, но это бред - этого не может быть потому, что этого не может быть никогда. Команда B, внтури кода всё равно исполняет исполняет команду A. Команда A - это шататный метод изменения состояния пина - им пользуются все, в том числе и команда B.
Ищите ошибку в своих измерениях или их интерпретации.
а порты на вывод настроить?
pinMode(11,OUTPUT) или DDRB = (1<<DDRB3)
pinMode(12,OUTPUT) или DDRB = (1<<DDRB4)
или так: DDRB = (1<<DDRB3)|(1<<DDRB4);
Если следовать логике то да, это бред. А если учесть что соединения, настройки осцилографа... я не трогал и просто перезалил скетч то это становится неоспоримым фактом)))
Ищите ошибку. Например, то, о чём сказал коллега постом выше. Нет? Ищите ещё! Где-нибудь
шнурок развязалсяконтакт отпал, что угодно. Но так не бывает.Действиткльно причина была в этом. Нужно было задикларировать модус портов.
Спасибо большое! ;)
Действиткльно причина была в этом. Нужно было задикларировать модус портов.
Спасибо большое! ;)
ну так порты настроены на ввод, если не переключить на вывод, то запись 1 подключает внутренний резистор (аналогично команде pinMode(pin INPUT_PULLUP)), получается интегрирующая цепь, резистор, входная ёмкость осциллографа, оно передний фронт и затягивало ...
)))
Вот выдрал из старого проекта точную задержку.
Понятно, что для точного времени задержки надо запретить прерывания и расчитать времена преамбулы и возврата.
// Задержка в тактах CPU в диапазоне 15-65535. inline void tunedDelay(uint16_t delay) { uint8_t tmp=0; asm volatile( " sbiw R24, 15\n" " mov %1, %A0 \n" " andi %1, 0x03\n" "_cl_%=:\n" " sbiw R24, 4\n" " brsh _cl_%=\n" " cpi %1, 1\n" " breq _cl_1_%=\n" " cpi %1, 0\n" " breq _end_%=\n" " cpi %1, 2\n" " breq _end_%=\n" " rjmp _end_%=\n" "_cl_1_%=:\n" " nop\n" " nop\n" " nop\n" "_end_%=: " : "+r" (delay), "+a" (tmp) : "0" (delay) ); }