Прерывания и micros.

rgsv
Offline
Зарегистрирован: 20.04.2020

День добрый, или ночь.
Задача вроде бы совсем простая. Надо измерять длительность входного импульса.

 

const char sensor_pin = 2;
volatile unsigned long g_time_val = 0;
volatile unsigned long g_last_time=0;
volatile unsigned long g_curtime_time=0;

void interrupt_func() {    
  
 // bool statenative = ( PIND & ( 1 << 2 ) ) ;
 
   g_curtime_time= micros();
   Serial.print ( "start " );    
   Serial.print ( g_curtime_time );    
   Serial.print ( " " );    
   Serial.println (  g_last_time );    
   
      
    if ( g_curtime_time > g_last_time ) { /// overflow check
     g_time_val = g_curtime_time - g_last_time;        
     Serial.println ( g_time_val );    
   } else {
     Serial.println ( "micros overflow" );        
   }
   
   g_last_time = g_curtime_time;
   
  }

void setup() {
  // put your setup code here, to run once:
 Serial.begin(115200);
 pinMode(sensor_pin, INPUT);  
 //attachInterrupt(digitalPinToInterrupt(sensor_pin), interrupt_func, CHANGE);
 attachInterrupt(digitalPinToInterrupt(sensor_pin), interrupt_func, FALLING );
  Serial.println("Start"); 
}

void loop() {
  // put your main code here, to run repeatedly:
  delay(1000);
}

Да я использую отладочный вывод  в прерывании, да это плохо и конечно его не будет.  Я измеряю по падению фронта.

Вижу такую картину  на выводе.

start 5930912 5909172
21740
start 5931900 5930912
988
start 5931772 5931900
micros overflow

start 5977460 5931772
45688
start 5978448 5977460
988
start 6000596 5978448

Как так получается, что время micros при вызове прерывания , получилось меньше чем при прошлом вызове ? Машина времени получается .

nik182
Offline
Зарегистрирован: 04.05.2015

Мильён раз уже написано, что внутрь функции прерывания нельзя всовывать другие прерывания и вообще чем короче, тем лучше. Волатиле переменные и флаг, что прерывание случилось. Вся обработка и вывод в основном цикле. Иначе будут машины времени получаться.

-NMi-
Offline
Зарегистрирован: 20.08.2018

Ачо, прикольна))))

Если (у слона больше) значит (у слона больше)  иначе (да, у слона больше)

Хоть поржал)))

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

rgsv пишет:

Как так получается, что время micros при вызове прерывания , получилось меньше чем при прошлом вызове ?

Ну, Вы же сами пишете про переполнение. Переполнилась и пошла с нуля. Или я чего-то не понял?

rgsv
Offline
Зарегистрирован: 20.04.2020

Смешно аж надорваться можно .
Не думал что это надо тут разжевывать
 

  if ( g_curtime_time > g_last_time ) { 
/*
 он должен быть больше если всё нормально ,делаем вычисление
*/
     g_time_val = g_curtime_time - g_last_time;       
     Serial.println ( g_time_val );   
   } else {
/*
       получилось что  g_curtime_time   < g_last_time, как такое может быть ??? micros переполнился и пошёл с нуля 
       когда же это может быть ?  согласно мануалу "Значение переполняется и сбрасывается на ноль, приблизительно через 70 минут." как бы вообще можно было 
       на это забить. но сделаем. Можно было бы и обработать точно , но меня не сильно волнует пропуск одного или двух импульсов. 
*/
     Serial.println ( "micros overflow" ); 
}    
   g_last_time = g_curtime_time;

Только прошло не 70 минут. А в следующем заходе в прерывание я почему то получаю число явно не пошедшее с нуля, а близкое к текущему но, почему то *меньшее* , чем в прошлый раз.

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

rgsv пишет:

Смешно аж надорваться можно .

Ну, тогда посмейтесь, а я со стороны посмотрю. Только надрываться не надо, здоровье нужно беречь.

rgsv
Offline
Зарегистрирован: 20.04.2020

nik182 пишет:
Мильён раз уже написано, что внутрь функции прерывания нельзя всовывать другие прерывания и вообще чем короче, тем лучше. Волатиле переменные и флаг, что прерывание случилось. Вся обработка и вывод в основном цикле. Иначе будут машины времени получаться.

Да. И долгая обработка прерывания чревата пропуском возможного прерывания, всё что происходит во время обработки прерывания - теряется. Пусть я  потеряю часть импульсов с "ноги" ,  пусть я даже теряю прерывание таймера , и возможно micros потеряет точность. Возможно.
Но число то меньше, на следующем заходе , почему ?

Oreshek
Oreshek аватар
Offline
Зарегистрирован: 19.04.2020

rgsv пишет:

... Надо измерять длительность входного импульса.

Так для этого же есть специальная функция pulseIn http://arduino.ru/Reference/PulseIn.

Почему бы не использовать её?

rgsv
Offline
Зарегистрирован: 20.04.2020

Да , наверное можно. Но не нравится что она блокирующая правда. 
У меня больше вопрос теперь не как решить задачу, а почему так получается.

 

Oreshek
Oreshek аватар
Offline
Зарегистрирован: 19.04.2020

http://arduino.ru/forum/programmirovanie/izmerenie-dlitelnosti - может тут что найдёте?

b707
Offline
Зарегистрирован: 26.05.2017

rgsv пишет:

почему так получается.

 

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

Судя по вашим ответам. вы в этом мало понимаете - тогда просто слушайте. что вам говорят. Оставьте в прерывании только запоминаение длительности, а все расчеты и , тем более, вывод в Сериал - переставьте в ЛУП. Перепишите таким образом код и проверьте - с высокой вероятностью проблема исчезнет. Так вы получите ответ на ваше "почему"

rgsv
Offline
Зарегистрирован: 20.04.2020

Мне не нужно чтобы заработало. Мне хочется понять почему так происходит.
Если бы micros вернул мне тоже самое число , я бы всё понял, но как получается , что оно меньше ?

Что вам не нравится в моих ответах ?
Если я обрабатываю прерывание, отстальные при этом запрещаются. Всё что возникнет во время обработки , взвесится как сработавшее,  то есть если я очень долго обрабатываю int0 , и за это время "родились" 3 прерывания таймера, я обработаю только одно из них после выхода из функции , А если за это время родились 10 int0 , я не обработаю  все 10 , я просто ещё раз обработаю  int0, правда не по факту возникновения, а не пойми когда , и время измерять безсмыслено, так как событие давно прошло. Но это всё по прежнему не объясняет как вышло что время стало меньше.

-NMi-
Offline
Зарегистрирован: 20.08.2018

Значит "рано" ты полез в эту "горку" с штурмом. До вершины не дойдёшь, знаний не хватит. Поиграйся пока без прерываний и даташиты кури усиленно. Прерывания вещь хоть и прикольная, но и на столько же коварная, как и прикольная.

:::::::глупыйсавет, раздербань свой код в отладчике - фсёсампоймёшь, если моцга хватит)))))))))

sadman41
Offline
Зарегистрирован: 19.10.2016

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

rgsv
Offline
Зарегистрирован: 20.04.2020

-NMi-
Вы очень умны и остроумны, спасибо, продолжайте жрать в сторонке.

b707
Offline
Зарегистрирован: 26.05.2017

rgsv - ну что не понятно то? Вы написали черти че, оно не работает, но вы продолжаете спорить?

Вместо спора, если уверены что правы и что убирать всю вашу мишуру из прерывания не нужно  - ну так продемонстрируйте нам такой код с правильным выводом времени

rgsv
Offline
Зарегистрирован: 20.04.2020

b707
А кто спорит то?  Я же не говорю что так делать правильно и хорошо.
Вы объясните , почему так  происходит . Извините но объяснение "не делай так" , это не объяснение.
Если проблема , в длительности обработки,то сокращение времени обработки, это всего лишь уменьшение  вероятности возникновения проблемы, а не её решение.

Я со своей  колокольни вижу ,что прерывания работают также +- как в ДОС . Многопоточности тут нет,  выполняются прерывания последовательно, между их обработкой выполняется одна инструкция основного потока .   Micros возвращает в общем случае значение регистра. 

Как так получается что при следующем заходе в этот обработчик значение меньше прошлого ?
 

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

rgsv пишет:

Как так получается что при следующем заходе в этот обработчик значение меньше прошлого ?
 

Тебе уже НЕ ОДИН РАЗ сказали: НЕЛЬЗЯ ИСПОЛЬЗОВАТЬ Serial ВНУТРИ обработчика прерывания. Поэтому у тебя в твою переменную всякая херня и записывается, ПОНИМАЕШЬ?

Убери ВЕСЬ вывод в Serial из прерывания, и перенеси его в loop. В прерывании взводи флаг, и сохраняй значение micros в свои переменные. В loop, если флаг взведён, то атомарно копируешь значения переменных в локальные, и сравниваешь их хоть до посинения.

Ферштеен зи, нет?

Pyotr
Offline
Зарегистрирован: 12.03.2014

rgsv, посмотрите код функций millis() и micros() в wiring.c
При расчете мкс используется переменная timer0_overflow_count, которая инкреминирутся в прерывании по переполнению ТС0, и значение счетного регистра ТС0.
Так вот, если в один момент TCNT0 был равен 250 и было пропущено ISR(TIMER0_OVF_vect), а вследующий момент TCNT0 стал равен 50 при прежнем значении timer0_overflow_count, то micros() нам даст значение на 200 меньше предыдущего.

Думаю в этом причина.

rgsv
Offline
Зарегистрирован: 20.04.2020

Pyotr
Да. Совершенно верно .
Проглотив то прерывание таймера, которое должно было обработать переполнение таймера  в стандартных недрах Arduino. Счётчик не инкрементировался и значение "откатилось".

На Ардуино , время выполнения обработчика прерывания, не должно быть больше 2 * (интервал таймера.) 

2ALL Никакого шаманизма ,"нельзя " , "не делай" , "земля налетит на небесную ось". "Херня возьмётся не откуда" , " и непонятно куда запишется" .

Всё это в конечном итоге последовательный flow инструкций , и всему есть разумное объяснение.

nik182
Offline
Зарегистрирован: 04.05.2015

rgsv пишет:

....Проглотив то прерывание таймера, которое должно было обработать переполнение таймера  в стандартных недрах Arduino. Счётчик не инкрементировался и значение "откатилось"....

Мне в словах не инкрементировался и "откатилось" видится противоречие. Не может измениться значение если оно  " не инкрементировался" Тут что то другое. Связанное с тем, что сериал работает  по прерыванию а в прерывании они запрещены, а буферы наполняются кое как и после выхода из прерывания на свободу сериал выплёвывает буфер,  в котором непонятно что и как записано, а Вы высокую теорию на эту мешанину наводите. 

 

 

rgsv
Offline
Зарегистрирован: 20.04.2020

Нет никакого противоречия, то прерывание таймера, которое должно было учесть его переполнение, было пропущено, в следствии чего переменная которая считает их количество, не увеличилась, а счётчик пошёл молотить дальше.
В следствии этого потерял в счёте одно преполнение.

В смысле "буферы наполняются кое как " они есть и они наполняются, это же не два параллельных thread которые пишут одновременно в один буфер. Посмотрите HardwareSerial.cpp , если я не ошибаюсь там корректно обрабатывается ситуация , если прерывания запрещены.

Тем более , что проблема не в том что порт шло "левое" значение, а в том что оно действительно "отставало", по значению.

С приёмом да , будут проблемы, хотя надо исходники смотреть .

 

Bruzzer
Offline
Зарегистрирован: 17.03.2020

rgsv пишет:

На Ардуино , время выполнения обработчика прерывания, не должно быть больше 2 * (интервал таймера.) 

Это наверное в лучшем случае. В худшем - 1 интервал. Если запретили прерывания на значении 255, то через интервал и еще несколько тактов произойдет потеря одного переполнения.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

rgsv пишет:

2ALL Никакого шаманизма ,"нельзя " , "не делай" , "земля налетит на небесную ось". "Херня возьмётся не откуда" , " и непонятно куда запишется" .

Всё это в конечном итоге последовательный flow инструкций , и всему есть разумное объяснение.

Безусловно - разумное объяснение есть всему. Но, понимаешь ли, в чём дело: Wiring - это инструмент. И если при помощи этого инструмента нельзя забивать гвозди, а тебе хочется это сделать, то может - взять подходящий задаче инструмент? Именно об этом тебе и говорят, собственно.

Ну а так то - да, когда дури много, можно вообще взять - и отказаться от возможностей Wiring, написав всё самому, с блэкджеком и блядями, и потом долго и героически править уже свои косяки.

Но всегда ведь проще - прикинуться дурачком, делая инструментом то, что делать им нельзя, и вопиять - ДОКОЛЕ? Правда ведь?

З.Ы. Ты, наверное, и инструкции не читаешь никогда, ведь - нахер, разберёмся по ходу, да?

nik182
Offline
Зарегистрирован: 04.05.2015

rgsv пишет:

...Посмотрите HardwareSerial.cpp , если я не ошибаюсь там корректно обрабатывается ситуация , если прерывания запрещены....

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

rkit
Offline
Зарегистрирован: 23.11.2016

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

rgsv
Offline
Зарегистрирован: 20.04.2020

В коде serial, я  этого не вижу,  покажите.

rkit
Offline
Зарегистрирован: 23.11.2016

Нет, я не прав. Дело действительно в переполнении счетчика. Вот код с вырезанными излишками.

unsigned long micros() {
	unsigned long m;
	uint8_t oldSREG = SREG, t;
	
	cli();
	m = timer0_overflow_count;
	t = TCNT0; // текущее состояние timer0. Если оно переполнено, 
             // но это событие не было зарегистрировано в переменной timer0_overflow_count,
             // то время может скакнуть назад.

        // Используются и timer0_overflow_count и TCNT0
	return ((m << 8) + t) * (64 / clockCyclesPerMicrosecond()); 
}