Измерение времени прерывания

DmitryR
Offline
Зарегистрирован: 12.05.2017

ок, ждём понедельник

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

так, ну раз у Вас железо не дома, мы сейчас плотно поработать не сможем?

Давайте согласуем время, когда сможем?

DmitryR
Offline
Зарегистрирован: 12.05.2017

Да, до понедельника доступа к железу нет. Я думаю можно в понедельник в 6 утра по москве?  

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

Помилуйте, в 6 утра по Москве я сплю как убитый. В 19 по Москве?

DmitryR
Offline
Зарегистрирован: 12.05.2017

У меня просто часовой пояс UTC+7, т.е. в 19 у меня 23, доступ к железу могу получить с 6 (мск) до 15 (мск), здание закрывают, меня выгонят

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

Хреново. А домой забрать нельзя?

DmitryR
Offline
Зарегистрирован: 12.05.2017

Осциллограф надо тащить с магнитопроводом, могут не разрешить 

DmitryR
Offline
Зарегистрирован: 12.05.2017

Давайте я в понедельник попробую, там либо заберу всё домой, либо отпишусь о результатах

DmitryR
Offline
Зарегистрирован: 12.05.2017

Код работает: 
 

#define SIGNAL_PIN 3         
#define CONTROL_PERIOD  10000ul  

enum METER_STATES: unsigned long {
  WAITING_FOR_SIGNAL = 0,  
  MEASURING_START,
  MEASURING_END,                
  READY                    
};

static volatile METER_STATES meterState = WAITING_FOR_SIGNAL;
static volatile unsigned long startSignalTime = 0, endSignalTime = 0, firstSignalTime = 0;

void measure_startISR(void) {
 sei();
 startSignalTime = micros();
 pinMode(SIGNAL_PIN, INPUT);
 attachInterrupt(SIGNAL_PIN - 2, measure_endISR, RISING);  
}

void measure_endISR(void) {
 sei();
 endSignalTime = micros(); 
 pinMode(SIGNAL_PIN, INPUT);
 meterState = MEASURING_END;  
}

void setup() {
  Serial.begin(115200);
  pinMode(SIGNAL_PIN, INPUT);
  meterState = WAITING_FOR_SIGNAL;
  attachInterrupt(SIGNAL_PIN - 2, measure_startISR, FALLING);
  //firstSignalTime = micros();
}

void loop() {
    if (meterState == MEASURING_END) {
      if (micros() -  startSignalTime  >= CONTROL_PERIOD) {
      detachInterrupt(SIGNAL_PIN - 2);
      meterState = READY; }               
      }
    else {
      if (meterState == READY){
      Serial.print("Result is: ");
      Serial.print(endSignalTime - startSignalTime);
      Serial.println(" microseconds");
      }
    }
  }

Только лично мне не понятно почему работает именно, когда здесь пишешь микро, а не endSignalTime: if (micros() - startSignalTime >= CONTROL_PERIOD) 

и ещё есть такая странность, при выдаче результата, он его пишет, пока не сделаешь сброс:

Result is: 1580 microseconds
Result is: 1580 microseconds
Result is: 1580 microseconds
Result is: 1580 microseconds
Result is: 1580 microseconds
Result is: 1580 microseconds
Result is: 1580 microseconds
Result is: 1580 microseconds
Result is: 1580 microseconds
Result is: 1580 microseconds
Result is: 1580 microseconds
Result is: 1580 microseconds
Result is: 1580 microseconds
Result is: 1580 microseconds
Result is: 1580 microseconds
Result is: 1580 microseconds
Result is: 1580 microseconds
Result is: 1580 microseconds
Result is: 1580 microseconds
Result is: 1580 microseconds
Result is: 1580 microseconds
Result is: 1580 microseconds
Result is: 1580 microse

  

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

DmitryR пишет:

Код работает: 

Отлично, согласитеь, здорово самому дожать!

DmitryR пишет:

Только лично мне не понятно почему работает именно, когда здесь пишешь микро, а не endSignalTime: if (micros() - startSignalTime >= CONTROL_PERIOD) 

Так правильно. Это ж Вы для определения таймаута пишете. Вам нужно текущее время. а endSignalTime в начале то ещё и не определён!

DmitryR пишет:

и ещё есть такая странность, при выдаче результата, он его пишет, пока не сделаешь сброс:

Ну, это-то понятно. Когда я советовал добавить состояние "Аминь" - имелось в виду, что это состояние когда ВСЕ ЗАКОНЧЕНО. Ваше же Ready - означает, что закончено, но ещё не отпечатано.

Поэтому,

1. либо завдените ещё одно состояние таки "ВСЁ ЗАКОНЧЕНО" и устанваливайте его после строки 46. Тогда if в строке 43 сработает только щдин раз.

2. либо оставьте как есть, но печать перенесите "после строки 39", а нынешние строки 42-48 тогда не нужны вовсе.

 

DmitryR
Offline
Зарегистрирован: 12.05.2017

//Отлично, согласитеь, здорово самому дожать!

Да, с этим не поспоришь, главное что стал понимать работу кода

Осталось теперь довести всё измерительное устройство до ума, но это уже не так сложно. 

Спасибо большое за помощь! 

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

Уф, даже попкорн кончился ... надеюсь на осеннюю пересдачу автор таки успел. Абыдна будет, если отчислят.. ведь разобрался. ;)

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

Arhat109-2 пишет:

Уф, даже попкорн кончился ...

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

DmitryR
Offline
Зарегистрирован: 12.05.2017

//Уф, даже попкорн кончился ... надеюсь на осеннюю пересдачу автор таки успел. Абыдна будет, если отчислят.. ведь разобрался. ;)

Не расстраивайтесь, эту задачу доделаю, за новую возьмусь, так что запасайтесь

Schrodinger_Kater
Offline
Зарегистрирован: 30.05.2017

Искал ответ на вопрос, как внутри прерывания получить текущее время, наткнулся на данную дискуссию. Вам бы стоило, друзья мои, внимательней прочесть "Замечание по использованию" функции attachInterrupt():

Внутри функции обработки прерывания не работает delay(), значения возвращаемые millis() не изменяются. ...

Тоже самое относится и к micros().

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

Обоже! Наконец то стало ясно!

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Ненуачо, хорошо же, еще один Шрёдингер просветлел. 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

А если он узнает, что при входе в прерывание можно их опять разрешить одной командой - вапще в нирвану впадёт. 

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

Schrodinger_Kater пишет:
Вам бы стоило, друзья мои, внимательней прочесть "Замечание по использованию" функции attachInterrupt():
Обзятельно сделаем. Спасибо за совет.

Schrodinger_Kater
Offline
Зарегистрирован: 30.05.2017

Ладно, ладно, просто мимо проходил, решил поумничать. xD

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

Но конкретно по приведенному коду (последнему), не вижу особого смысла разрешать прерывания внутри обработчика - до входа в обработчик они все равно разрешены, а micros() их все равно запрещает. Здесь разрешение прерываний перед функцией повышает точность всего на ~4 мкс относительно входа. И плюс, не совсем понятно зачем надо было volatile делать static - в описании attachInterrupt указано только volatile.

Schrodinger_Kater
Offline
Зарегистрирован: 30.05.2017

DetSimen пишет:

Ненуачо, хорошо же, еще один Шрёдингер просветлел. 

"Kater" - разговорное, может означать как похмелье, так и кота в частности. xD

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

Schrodinger_Kater пишет:

 не совсем понятно зачем надо было volatile делать static - в описании attachInterrupt указано только volatile.

Это просто грамотная привычка - не расширять область видимости без необходимости.

DmitryR
Offline
Зарегистрирован: 12.05.2017

Schrodinger_Kater пишет:

Искал ответ на вопрос, как внутри прерывания получить текущее время, наткнулся на данную дискуссию. Вам бы стоило, друзья мои, внимательней прочесть "Замечание по использованию" функции attachInterrupt():

Внутри функции обработки прерывания не работает delay(), значения возвращаемые millis() не изменяются. ...

Тоже самое относится и к micros().

Здравствуйте, можно объяснение для не ведующих, относительно фразы: значения возвращаемые millis() не изменяются. ... ?

 

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

А что Вам непонятно в этой фразе? "Не изменяются" означает "остаются теми же самыми". Сколько не вызывай, всегда возвращает одно и тоже.

Schrodinger_Kater
Offline
Зарегистрирован: 30.05.2017

DmitryR пишет:

Здравствуйте, можно объяснение для не ведующих, относительно фразы: значения возвращаемые millis() не изменяются. ... ?

Дело в том, что внутренние часы (точнее счетчик, сделанный кратным микросекундам) обновляются по прерыванию внутреннего таймера, а при входе в обработчик прерывания, все остальные прерывания преостанавливаются и возобновляются только после выхода из текущего обработчика (в один момент времени обрабатывается только одно прерывание). Соответственно, без какого-либо воздействия на обработку прерываний внутри обработчика, многократный вызов функций millis() и micros() будет возвращать одно и тоже значение. То есть, если появится желание измерить время работы кода внутри обработчика прерывания (читаем время в начале, читаем время в конце - разность значений и есть время), то результат будет "0", так как внутри обработчика не вызываются другие обработчики, в частности прерывания по таймеру. Здесь как раз и полезно вызвать разрешение прерываний sei() или interrupts() перед последним (в конце кода) вызовом функции millis() или micros().

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

Schrodinger_Kater пишет:
полезно вызвать разрешение прерываний sei() или interrupts() перед последним (в конце кода) вызовом функции millis() или micros().
Зачем? Как это поможет?

Schrodinger_Kater
Offline
Зарегистрирован: 30.05.2017

ЕвгенийП пишет:

Это просто грамотная привычка - не расширять область видимости без необходимости.

Если я правильно понял из описания volatile:

Переменная должна быть объявлена volatile, когда её значение может быть изменено чем-либо за пределами того участка программы, где она объявлена, например, параллельно выполняющимся процессом. В Arduino единственным местом, где это может проявиться, является участок программы, ассоциированным с прерываниями, вызванный программой обработки прерываний. См. AttachInterrupt()

это было сделано сугубо для прерываний. Не влияет ли значение static на компилятор, заставляя его сохранять переменную в регистр, а не в ОЗУ? Тогда, по идее, внутри обработчика она может не обновляться. На мой взгляд, нет необходимости использовать static там, где он неприменим.

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

Ну, вот Вы про volatile прочитали. А теперь прочитайте ещё что означает static для глобальной переменной, а уж потом будете рассуждать где он применим, а где - нет.

Schrodinger_Kater
Offline
Зарегистрирован: 30.05.2017

ЕвгенийП пишет:

Schrodinger_Kater пишет:
полезно вызвать разрешение прерываний sei() или interrupts() перед последним (в конце кода) вызовом функции millis() или micros().
Зачем? Как это поможет?

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

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

Schrodinger_Kater пишет:

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

Да? Ну, если Вы в это верите, то делайте :))))

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

Schrodinger_Kater пишет:

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

Вы знаете, что Совецкий союз развалился, когда правительство начало экспериментировать с очередями и разрешениями? Так что давайте, не тово... А то все ваши ардуины распадутся на транзисторы и шины данных

Schrodinger_Kater
Offline
Зарегистрирован: 30.05.2017

ЕвгенийП пишет:

Schrodinger_Kater пишет:

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

Да? Ну, если Вы в это верите, то делайте :))))

А вы, я смотрю, скептически относитесь к способностям Котов Шрёдингера. xD

volatile unsigned long check_time = 0;
volatile bool loop_check = false;

void sleep() { long random_value = random(); }

void check_call() {
  interrupts(); unsigned long begin_time = micros();
  sleep();
  interrupts(); unsigned long end_time = micros();
  //
  check_time = end_time - begin_time;
  loop_check = true;
}

void setup() {
  Serial.begin(9600);
  attachInterrupt(1, check_call, CHANGE);
}

void loop() {
  if ( loop_check ) {
    loop_check = false;
    Serial.print("Timer successfully updated ("); Serial.print(check_time); Serial.println(")");
  }
  delay(100);
}

Ну и результат, соответственно:

Timer successfully updated (56)
Timer successfully updated (56)
Timer successfully updated (56)
Timer successfully updated (56)
Timer successfully updated (52)
Timer successfully updated (56)
Timer successfully updated (56)

sadman41 пишет:

Schrodinger_Kater пишет:

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

Вы знаете, что Совецкий союз развалился, когда правительство начало экспериментировать с очередями и разрешениями? Так что давайте, не тово... А то все ваши ардуины распадутся на транзисторы и шины данных

В данном конкретном случае: гонка вооружений - занятие достаточно веселое. ;-)

 

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

Насчёт котов не знаю, а вот насчёт очереди ... и каков же её размер? Сколько прерываний могут одновременно ожидать?

Schrodinger_Kater
Offline
Зарегистрирован: 30.05.2017

А вот результат, если убрать первое разрешение interrupts():

Timer successfully updated (56)
Timer successfully updated (60)
Timer successfully updated (60)
Timer successfully updated (64)

Несколько микросекунд уходит на вход в обработчик.

Schrodinger_Kater
Offline
Зарегистрирован: 30.05.2017

ЕвгенийП пишет:

Насчёт котов не знаю, а вот насчёт очереди ... и каков же её размер? Сколько прерываний могут одновременно ожидать?

К сожалению мне лень искать, посмотрите здесь, раздел 14 описывает непосредственно прерывания: http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2549-8-bit-AVR-Mic...

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

Schrodinger_Kater пишет:
мне лень искать
Ну, лень, тогда извините.

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

Ожидать могут все, а вот как только разрешение поднять, то будет выполнена самая приорететная. При этом , цитата, The user software can write logic one to the I-bit to enable nested interrupts. All enabled interrupts can then interrupt the current interrupt routine. Что будет дальше даже боюсь предположить, потому что, цитата, The I-bit is automatically set when a Return from Interrupt instruction – RETI – is executed. И получается, к процедуре прерывания , в которой принудительно разрешили прерывания будет завершена только после выполнения всех прерываний стоящих в очереди.

 

Schrodinger_Kater
Offline
Зарегистрирован: 30.05.2017

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

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

Dr. Mario
Offline
Зарегистрирован: 29.10.2016

DmitryR пишет:

Код работает: 

Строки SIGNAL_PIN - 2 это прикол такой?

DmitryR
Offline
Зарегистрирован: 12.05.2017

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

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

Dr. Mario пишет:

Строки SIGNAL_PIN - 2 это прикол такой?

Типа да, а что - не прикольно? Вам больше нравится макрос digitalPinToInterrupt?