Есть ли более точная альтернатива micros()?

Porosenok
Offline
Зарегистрирован: 06.11.2016

Функция micros() возвращает текущее значение микросекунд с точностью до 4-ех. Но для реализации задачи этого оказалось мало. Возможно ли как-то отсчитать время с точностью до 1мкс?

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

nano(); femto(); )))

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Porosenok, любой аппаратный таймер ардуины может считать по 0,0625 µS , но "накладые расходы" на высокоуровневых языках столь велики, что точнее чем микрос вряд ли получится измерить. Только если писать на ассемблере.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

dimax пишет:

Porosenok, любой аппаратный таймер ардуины может считать по 0,0625 µS , но "накладые расходы" на высокоуровневых языках столь велики, что точнее чем микрос вряд ли получится измерить. Только если писать на ассемблере.

А интересно - IDE ассембрерные вставки поддерживает?
И сам же отвечу, поддерживает, осталось изучить ассемблер )))

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Porosenok пишет:

 Возможно ли как-то отсчитать время с точностью до 1мкс?

Фух! Успел до Клапауция.. Можно! Я - разрешаю!

Porosenok
Offline
Зарегистрирован: 06.11.2016

wdrakula пишет:

Фух! Успел до Клапауция.. Можно! Я - разрешаю!

Спасибо большое! Я обязательно воспользуюсь.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

wdrakula пишет:

Porosenok пишет:

 Возможно ли как-то отсчитать время с точностью до 1мкс?

Фух! Успел до Клапауция.. Можно! Я - разрешаю!

Разрешить мало! А подать идею - как?

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

ua6em пишет:

Разрешить мало! А подать идею - как?

В смысле "как"???? Вы всерьез спрашиваете или просто с аглицким не все гладко, или даташит на кристал не скачивается??

Таймер 16битный запустили на 16МГц и его счетчик читаете. Если внешнее событие - то его на ICP пин ловить можно, если внутреннее, то просто в скобках cli/sti запомнить регистр счетчика.

Поправку на накладные расходы (они всегда в пределах 1 мкс) нужно учесть при отладке программы.

axill
Offline
Зарегистрирован: 05.09.2011

Porosenok пишет:

Функция micros() возвращает текущее значение микросекунд с точностью до 4-ех. Но для реализации задачи этого оказалось мало. Возможно ли как-то отсчитать время с точностью до 1мкс?

можно. Для этого делаем свою настройку таймера так, стобы один счет соотвествовал 1 мкс

но если мы говорим про ардуину с частотой кварца 16мгц то это получится, что между срабатываниями таймера будет всего 16 тактов. Это по сути всего лишь 16 машинных инструкций если исключить те что требуют несколько тактов. Этого от силы хватит на ассемблере чтобы увеличить значение счетчика и покинуть прерывание. На основную программу ресурсов не останется совсем. 

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

Porosenok
Offline
Зарегистрирован: 06.11.2016

Мне надо всего лишь в определённые моменты времени просто ждать прихода фронта с высокой точность. Теоретически же можно только на это время перепрограммировать таймер, а потом вернуть всё взад.

Porosenok
Offline
Зарегистрирован: 06.11.2016

axill пишет:

но если мы говорим про ардуину с частотой кварца 16мгц то это получится, что между срабатываниями таймера будет всего 16 тактов. Это по сути всего лишь 16 машинных инструкций если исключить те что требуют несколько тактов. Этого от силы хватит на ассемблере чтобы увеличить значение счетчика и покинуть прерывание. На основную программу ресурсов не останется совсем. 

Тут не совсем ясно, зачем мне увеличивать значение счётчика. Таймер-то аппаратный.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Porosenok пишет:

Мне надо всего лишь в определённые моменты времени просто ждать прихода фронта с высокой точность. Теоретически же можно только на это время перепрограммировать таймер, а потом вернуть всё взад.

Насколько я помню дэйташит (а конкретно этот вопрос меня не интересовал), разработчиком специально предусмотрен такой режим совместной работы таймера и аппаратного прерывания. Соответственно, ожидаемое разрешение 62.5 нс.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Porosenok пишет:

Мне надо всего лишь в определённые моменты времени просто ждать прихода фронта с высокой точность. Теоретически же можно только на это время перепрограммировать таймер, а потом вернуть всё взад.

У меня есть пример вот почти  того, что вам нужно. Считает длину импульса с точностью 3 такта. Тыц.   Только переделывать под вас я не буду, очень лень  :)

Porosenok
Offline
Зарегистрирован: 06.11.2016

dimax пишет:

У меня есть пример вот почти  того, что вам нужно. Считает длину импульса с точностью 3 такта. Тыц.   Только переделывать под вас я не буду, очень лень  :)

Спасибо. Примерно теперь представляю в какую сторону копать. 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Porosenok пишет:

Мне надо всего лишь в определённые моменты времени просто ждать прихода фронта с высокой точность. Теоретически же можно только на это время перепрограммировать таймер, а потом вернуть всё взад.

Вам надо поймать одиночный импульс?

Porosenok
Offline
Зарегистрирован: 06.11.2016

Всем спасибо, всё получилось. Гугл тоже немного помог. Удалось отследить приход импульса с точностью  большей, чем микросекунда. 

byte t2cnt;
volatile long t2ovf_cnt;

ISR(TIMER2_OVF_vect) {
  t2ovf_cnt++;
}

void SetupTimer2()  {
  TCCR2A = 0;                              //режим 0
  TCCR2B = 0<<CS22 | 0<<CS21 | 1<<CS20;   //Предделитель выключен
  TIMSK2 = 0<<TOIE2;                      //Отключение прерывания по переполнению Timer2

Ну и далее в лупе:

// Посылается импульс
    digitalWrite(TRIGGER_PIN2, HIGH);
    delayMicroseconds(10);
    digitalWrite(TRIGGER_PIN2, LOW); 

    TCNT2 = 0;
    t2ovf_cnt = 0;
    TIMSK2 = 1 << TOIE2; //Включить прерывания по переполнению Timer2
        
    while (digitalRead(ECHO_PIN1) == HIGH);

    t2cnt = TCNT2;
    TIMSK2 = 0 << TOIE2;
        
    speed_dif = (long)((t2ovf_cnt << 8) + t2cnt) ;

 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Porosenok, то, что вы привели не может ничего измерять, если предположить  что 5 строка ловит конец импульса, то что ловит его начало? Это работало бы худо-бедно, если бы импульс начинался точно с момента выполнения команды TCNT2=0. Приведите полный код, может тут чего-то не хватает?

Porosenok
Offline
Зарегистрирован: 06.11.2016

Добавил код передачи импульса. После отправки импульса жду ответ на него. Безусловно, вопросы вызывает строчка

while (digitalRead(ECHO_PIN1) == HIGH);

Если она выполняется за время, меньшее чем 250нс, то ни на что не влияет. В противном случае она и будет определять точность.

Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014

Ардуиновские функции digitalRead (4-6 мкс), digitalWrite (6-8 мкс) и др. довольно медленные. Чтобы было быстрее - нужно прямое обращение к портам через регистры.

http://arduino.ru/forum/obshchii/vremya-vypolneniya-otdelnykh-komand-arduino

 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Porosenok, а как вы определили, что точность улучшилась? while будет выполняться примерно 3..4 µS на один егойный круг. Но это цветочки, ягодки будет если вы измеряете отрезки времени более, чем 0,0625*255=~ 16µS  -таймер уйдёт в прерывание, и будет там "сидеть"  порядка 15 µS. Но если перейти на 16 битный таймер, отказаться от прерываний (макс. время будет 0,0625 * 65535=4mS)и строчку while переписать вставкой на ассемблере, тогда да, точность реально вырастет. Думаю, можно будет "замахнуться" на 1uS

 

Porosenok
Offline
Зарегистрирован: 06.11.2016

Дело в том, что данные получаются именно те, которые должны быть. Допускаю, что это случайность, но проверю.  А так вы меня расстроили. digitalRead(), выполняющаяся так долго - это, честно говоря, вообще ни в какие ворота не лезет. Там всего-то надо считать данные с порта (2-4 такта)!

Значит буду делать вставку на ассемблере. Хотя всё больше в голову закрадывается крамольная мысль, написать вообще всё  на ассемблере... Правда тогда и Ардуино не нужно в принципе... Хм...

 

 
Porosenok
Offline
Зарегистрирован: 06.11.2016

dimax пишет:

Но если перейти на 16 битный таймер, отказаться от прерываний (макс. время будет 0,0625 * 65535=4mS)и строчку while переписать вставкой на ассемблере, тогда да, точность реально вырастет. Думаю, можно будет "замахнуться" на 1uS

16-битный таймер, это таймер1?

dimax пишет:

Думаю, можно будет "замахнуться" на 1uS

Вот тут надо разобраться основательно.

Команд получается три: чтение порта ввода-вывода, условный переход и чтение регистра таймера. Итого по тактам сколько набежать может?

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Porosenok, 16-битный это да, таймер1.  Подсчитать всё по тактам наперёд довольно трудно, для этого нужно  сначала написать все команды) Кстати написать весь обработчик на ассемблере -хорошая мысль, 80% будущего кода уже есть в том примере, что я давал.  Осталось дописать управление пином и задержку. 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Porosenok  Это же для УЗ дистанциометра? что-то во мне зажглось, и написал я этот скетч на асме. Вернее переделал мой же образец.  Триггер 13 пин-выход , эхо 12 пин-вход. Проверил, работает. Компилить в IDE 1.6.8.  Точность очень сложно оценить, тут же накладывается ещё ограничение по точности самого датчика, а он очень склонен к тому, что б гнать болтающиеся данные. Но на хорошей преграде, при точной соосности даже миллиметры стабилизирутся.

 

void setup() {
Serial.begin(9600);
pinMode(13,OUTPUT);
}

void loop() {
float sm = (float) (asm_func() * 625E-4) /58 ;
Serial.println(sm);
}


uint32_t asm_func(){
asm volatile (         
"cli"                     "\n\t" // дабы никто не мешал
/////////конфигурация таймеров///////
"ldi r18,0x83"            "\n\t" // GTCCR=0x83
"out 0x23,r18"            "\n\t" // GTCCR=0x83
"sts 0x80,__zero_reg__"   "\n\t" // TCCR1A=0
"ldi r19,0x4"             "\n\t" 
"sts 0x81,r19"            "\n\t" // TCCR1B=1<<CS12
"sts 0x85, __zero_reg__"  "\n\t"//TCNT1L=0
"sts 0x84, __zero_reg__"  "\n\t"//TCNT1H=0
"sts 0xB0,__zero_reg__"   "\n\t" // TCCR2A=0
"ldi r20,0x1"             "\n\t" 
"sts 0xB1,r20"            "\n\t" // TCCR2B=1<<CS20
"sts 0xB2,__zero_reg__"   "\n\t" // TCNT2=0
"sts 0x70,__zero_reg__"   "\n\t" // TIMSK2=0

"sbi 0x05,5;"    "\n\t" // set 1 лапы d13 ардуино   
"ldi r21,53"     "\n\t" //53 пустых циклов (53*3*0,0625= ширина импульса 10 мкс 
"zero_loop:"     "\n\t" //метка петли       
"dec r21"        "\n\t"  //уменьшать счётчик
"brne zero_loop" "\n\t"  // если не ноль -уходим по метке
"cbi 0x05,5;"    "\n\t" //set 0  лапы d13 ардуино   

// ждём единицу на 12 пине
"wait_start:"             "\n\t"
"sbis 0x3,4"              "\n\t"
"rjmp wait_start"         "\n\t"

"sts 0x43,__zero_reg__"   "\n\t" // GTCCR=0 !!!старт таймеров!!!!

 // импульс пошёл, ждём ноль
"wait_zero:"             "\n\t"
"sbic 0x03,4"             "\n\t"
"rjmp wait_zero"         "\n\t"

"out 0x23,r18"            "\n\t" // GTCCR=0x83 стоп таймеры

//вывод 3х байт
"lds r22, 0xB2"           "\n\t" //  tctn2  0 байт
"lds r23, 0x84"           "\n\t"  // TCNT1L 1 байт
"lds r24, 0x85"           "\n\t"  // TCNT1H 2 байт
"lds r25, __zero_reg__"   "\n\t" // пустой  3 байт
"sei"                     "\n\t"
::: ); }


 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Porosenok пишет:

Дело в том, что данные получаются именно те, которые должны быть. Допускаю, что это случайность, но проверю.  А так вы меня расстроили. digitalRead(), выполняющаяся так долго - это, честно говоря, вообще ни в какие ворота не лезет. Там всего-то надо считать данные с порта (2-4 такта)!

Значит буду делать вставку на ассемблере. Хотя всё больше в голову закрадывается крамольная мысль, написать вообще всё  на ассемблере... Правда тогда и Ардуино не нужно в принципе... Хм...

 

 

Тогда пишите в лоб простую программу
и будет там всего три джампа

(на ассемблере ес-но)

первый джам Дмитрия )))
"wait_start:"             "\n\t"

38 "sbis 0x3,4"              "\n\t"
39 "rjmp wait_start"         "\n\t"

 

 

Porosenok
Offline
Зарегистрирован: 06.11.2016

dimax пишет:

Porosenok  Это же для УЗ дистанциометра? 

Ну, не совсем. Это будет уз анемометр. За скетч спасибо. Посмотрю...

axill
Offline
Зарегистрирован: 05.09.2011

почитайте в даташите про Input Capture Unit

для атмега 328 об этом написано в разделе описания Timer1

это аппаратный способ посчитать время по внешнему сигналу

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Вот ровно все, что написано в крайних 10-ти сообщениях я написал в своем, у Димы пример - тоже хороший.

Все так меня не любят, что не прочитали? И про 16 бит и про ICP.

Толи расстраиваццо, толи гордиццо... вотпрям не знаю... хотя, все же, обидно.

---------------------

Поросенок! Внешнее событие ловите на ICP, ничего точнее и быстрее не найдете. Вариант с "while(digitaRead())" - не выдерживает никакой критики.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

С ICP  конечно лучше, чем с while :) Но тут тоже не всё так гладко, ICP хорош в работе с прерываниями. Без прерывания он не даёт преимуществ перед простым счётным регистром TCNT.   А прерывания - хана точности. В скетче на асме  от 0 до 3 тактов уходит в жернова цикла-опроса фронтов импульса . Т.е. можно потерять в алгоритме от 0 до 6 тактов , это чистая девиация результата. (По факту почему-то не больше трёх биения)  + 2 такта чистых потерь на старт/стоп таймеров. Эти два такта можно прибавить к результату как константу.     С прерываниями об таких малых потерях можно и не мечтать..

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Дмитрий, по коду должно получиться байт 150-200, а скомпилированный 3500, шо там компилятор насобирал?

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

ua6em, там два жирбасика -  float  и Serial()  :)

axill
Offline
Зарегистрирован: 05.09.2011

dimax чем input capture страдает в точности? Прерывание возникает по завершению счета, т.е. Точность измерения зависит только от точности кварца и никак не зависит от кода

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

axill,  ну да, вы конечно правы! Сейчас подумал -да, будет точнее чем ассемблером.  Практически точность в один такт получится при идеальном кварце. Но  есть сильный нюанс -вход в прерывание в автоматически сгенерированном коде ардуины занимает порядка 7 микросекунд, а в прерывании нам нужно успеть обнулить или переписать регистр ICP в свою переменную, поменять бит ICES, что б ловить другой фронт. В общем этой мышиной возьни ещё на десяток тактов. Итого имеем гарантированную длину импульса, которую можно измерить  с помощью ICP -где то от 50 тактов МК, (эта цифра не с потолка, я получал её экспериментально) иначе велик риск пропустить второй фронт импульса.   Ну и конечно ограничение в длине импульса -65536 тактов без использования дополнительных перрываний. А так да, самый идеальный вариант по качеству.

Добавлено: ещё мы не рассмотрели вариант  срабатывания другого прерывания прямо перед ICP. В этом случае наше прерывание оставит свой флажок, и обработчик будет ждать пока кончится предыдущее прерывание. А тут подойдёт новый фронт импульса, и освежит значение ICP. Стало быть обязательно нужно отключать все прерывания кроме кэпчура, иначе результат не гарантирован при любой длине импульса.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Дима! Это УЗ анемометр (как сказал ТС).

То есть, если излучатель и приемник поставить на расстоянии в 1000 мкс в спокойном воздухе (примерно 33 см),  то ЛЮБЫЕ задержки, по определению безразличны.

Важна лишь разница замеров в спокойном и движущемся воздухе, а она, разница, никак не ухудшится от накладных расходов на входы в прерывания. На время замеров можно остановить Таймер0, чтобы даже учет миллис нам не мешал. А измерять на Таймер1 - 16бит.

Разрешение в 1 такт даст точность анемометра в (примерно) 2 см/сек, а если излучатель и приемник разнести на 66 см, то и точность станет в 1 см/сек.

Усреднить по серии в 10-20 замеров, пачками по 4-5, с отбрасыванием мин и макс, вообще не прибор, а конфетка получится! Хоть в поверку сдавай!

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Покумекал, и написал точный измеритель длины восходящего импульса на ICP, но без прерывания, код для меги328. Пригодится как заготовка для каких нибудь проектов.  Метод сохраняет все достоинства ICP, и позволяет измерять более короткие импульсы, нежели с прерыванием. Минимальная длина входящего импульса -14 тактов МК (875nS) . Максимальная 65535 тактов (4mS)  Точность измерения  1 такт МК (62,5 nS)   Недостатки -метод не позволяет определить что импульс был короче или длиннее своего диапазона, и выдаст левые цифры если диапазон был нарушен. На 8 пин подать импульс, функция отдаёт длину импульса в тактах МК. Компилировать в arduino 1.6.8, в других IDE работоспособность  не гарантируется.

 

int16_t asm_func(){
asm volatile (         
"cli"                     "\n\t" // дабы никто не мешал
"sts 0x80,__zero_reg__"   "\n\t" // TCCR1A=0
"ldi r17,0x41"            "\n\t" // RISING ICP detect , start timer1
"sts 0x81,r17"            "\n\t" // TCCR1B= (1<<ICES1)|(1<<CS10)
"sbic 0x16,5"             "\n\t" // TIFR1& (1<<ICF1) Да?
"sbi 0x16,5"              "\n\t" // тогда clear ICF1
"wait_begin:"             "\n\t" // ждём флага ICF 
"sbis 0x16,5"             "\n\t" // TIFR1& (1<<ICF1) Да?
"rjmp wait_begin"         "\n\t" //пропускаем ход
"lds r22, 0x86"           "\n\t" //сохранить ICR1L  
"lds r23, 0x87"           "\n\t" //сохранить ICR1H
"sbi 0x16,5"              "\n\t" //clear ICF1
"ldi r19,0x01"            "\n\t" // FALLING ICP detect
"sts 0x81,r19"            "\n\t" // TCCR1B=(0<<ICES1)|(1<<CS10)
"wait_end:"               "\n\t" // ждём флага ICF 
"sbis 0x16,5"             "\n\t" // TIFR1& (1<<ICF1) Да?
"rjmp wait_end"           "\n\t" // пропускаем ход
"lds r24, 0x86"           "\n\t" //save ICR1L
"lds r25, 0x87"           "\n\t" //save ICR1H
"sub r24, r22"            "\n\t" //ICR1L_new-ICR1L_old
"sbc r25, r23"            "\n\t" //ICR1H_new-ICR1H_old
"sei"                     "\n\t"
::: ); }

 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

dimax, в встроенный Ассемблер разве не признает имен регистров, только номера?

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

andriano, ну да, только регистры. Вроде все внутренние дефайны в avr-gcc сделаны для СИ.  По идее можно сделать перед телом кода шапку дефайнов в асм-формате.

Logik
Offline
Зарегистрирован: 05.08.2014

Что будет если импульс вобще не прийдет? 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Значит, Ардуина будет ждать столько, сколько нужно.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Это да, программа должна 2 раза поймать флаг ICF, таймаута нет. Нет смысла его вносить, -увеличиться обработчик, если таймаут нужен -тогда наверное удобнее просто использовать вариант с прерыванием. Правда есть и компромиссный вариант -включить вотчдог :)

Porosenok
Offline
Зарегистрирован: 06.11.2016

В скетче  из поста #23 функция всегда возвращает 0. Причём даже если  в конце в r25- r22 загрузить любые константы, всё равно возвращает ноль

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Porosenok, не знаю чем помочь, у меня всё работает. По симптому похоже на несоблюдение рекомендации компилить в IDE 1.6.8

Porosenok
Offline
Зарегистрирован: 06.11.2016

Да оно, в принципе, не страшно.  Значения можно и после возвращения из функции с таймеров взять. Тут другие проблемы всплыли. Почти в половине случаев вызова функции при первом же опросе 12-ого вывода получаю ноль, ну и сразу выход из цикла. Хотя на осциллограммах всё чисто. Пока никакой закономерности не выявил.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Porosenok, эээ.. "карты не врут" :)  Раз ноль -значит импульс был, и он был менее 6 или 7 тактов, не помню сколько там длится опрос. И кстати замечу, мой алгоритм начинает отсчёт времени по прибытию "единицы", а из вашего наброска следовало, что прибытие первой единицы уже завершает счёт.

Porosenok
Offline
Зарегистрирован: 06.11.2016

Ну да, карты не врали. Это оказались аппаратные проблемы с помехами от импульсного источника питания компьютера. При подключении осциллографа с общей с компьютером землёй они пропадали.

Вроде всё получилось. Погрешность обнаружения фронта по вашему скетчу выходит в 3 такта, т.е. около 200нс. Этого более чем достаточно. Дело в том, что абсолютные измерения длительности прохождения сигнала от излучателя к приёмнику не нужны, да они и бессмысленны, т.к. значения очень сильно зависят от мгновенного значения температуры. Обнаружение начала импулься тоже не нужно, что видно из осциллограммы. Я провожу два измерения: "туда" и "обратно", т.е. относительные измерения. Разница во времени прохождении сигналов уже очень слабо зависит от температуры и зависит только от скорости ветра.

Как выяснилось, даже 200нс - это излишняя точность. К "софтовым" 200нс приходится добавлять около 600нс "железных" - результат шума микрофонного усилителя. 

 

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

dimax, а почему не оформить в виде обработчика прерывания по захвату таймера? Тогда, если подключить прерывание по переполнению можно измерять бОльшие интервалы. Да и код не будет "тормозить" если импульс не придет вовсе .. можно тупо сделать глобальный флаг "замер завершен" и по нему в программе подсматривать за прерыванием. :)

Timer/counter capture event очень удобно.

E_Krendel
Offline
Зарегистрирован: 24.05.2017

Porosenok пишет:
Я провожу два измерения: "туда" и "обратно", т.е. относительные измерения. Разница во времени прохождении сигналов уже очень слабо зависит от температуры и зависит только от скорости ветра. Как выяснилось, даже 200нс - это излишняя точность.

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

Если это, конечно, ветер, а не аэродинамическая труба для продувки моделей ;)

DetSimen
DetSimen аватар
Онлайн
Зарегистрирован: 25.01.2017

Если хочется странного, чота идет не так. 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

dimax пишет:

Покумекал, и написал точный измеритель длины восходящего импульса на ICP

int16_t asm_func(){
asm volatile (         
"cli"                     "\n\t" // дабы никто не мешал
"sts 0x80,__zero_reg__"   "\n\t" // TCCR1A=0
"ldi r17,0x41"            "\n\t" // RISING ICP detect , start timer1
"sts 0x81,r17"            "\n\t" // TCCR1B= (1<<ICES1)|(1<<CS10)
"sbic 0x16,5"             "\n\t" // TIFR1& (1<<ICF1) Да?
"sbi 0x16,5"              "\n\t" // тогда clear ICF1
"wait_begin:"             "\n\t" // ждём флага ICF 
"sbis 0x16,5"             "\n\t" // TIFR1& (1<<ICF1) Да?
"rjmp wait_begin"         "\n\t" //пропускаем ход
"lds r22, 0x86"           "\n\t" //сохранить ICR1L  
"lds r23, 0x87"           "\n\t" //сохранить ICR1H
"sbi 0x16,5"              "\n\t" //clear ICF1
"ldi r19,0x01"            "\n\t" // FALLING ICP detect
"sts 0x81,r19"            "\n\t" // TCCR1B=(0<<ICES1)|(1<<CS10)
"wait_end:"               "\n\t" // ждём флага ICF 
"sbis 0x16,5"             "\n\t" // TIFR1& (1<<ICF1) Да?
"rjmp wait_end"           "\n\t" // пропускаем ход
"lds r24, 0x86"           "\n\t" //save ICR1L
"lds r25, 0x87"           "\n\t" //save ICR1H
"sub r24, r22"            "\n\t" //ICR1L_new-ICR1L_old
"sbc r25, r23"            "\n\t" //ICR1H_new-ICR1H_old
"sei"                     "\n\t"
::: ); }

 

И как его применять???

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Arhat109-2 пишет:

dimax, а почему не оформить в виде обработчика прерывания по захвату таймера?

Я думал. Если сделать обычное прерывание, то компилятор сначала напихает туда команд  сохранения всех рабочих регистров в стек, это сразу 15-20 тактов  МК коту под хвост.  Этого нельзя допустить.  Значит надо выполнить ISR_NAKED (), сохранить по самому минимуму в стек, затем сохранить куда-то 16 бит данных из регистра ICR, перепрограммировать таймер на ловлю противоположного ипульса, восттановить сохраннёные регистры из стека и можно выходить. Просто на обычной ассемблерной вставке это скорее всего не сделать, придёться цеплять  внешний ассемблерный файл, который подключать к проекту. В общем довольно муторно, конечно была бы какая то серьёзная надобность, можно б было и написать..

ua6em пишет:

И как его применять???

Подать на вход ICP (пин8) импульс, функция выплюнет его длительность в тактах Мк

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Как функцию вызвать? Что-то не пойму, пишу так - tau=asm_func(); OK?