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

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

Имеется такой вопрос: как посчитать время, в течении которого программа выполняет внешние прерывание, в микросекундах?
Имеется основной код, в котором в зависимости от входных условий наступает внешнее прерывание, длительность которого будет примерно несколько тысяч микросекунд. Соответственно по выходу из прерывания счётчик Arduino nano (Atmega 328) переполняется несколько раз (вычитал, что переполнение происходит при 1024 мксек). Поэтому время, в течении которого длилось прерывание, получается некорректным (считаем разность между временем, когда наступило прерывание и когда оно закончилось).

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

Буду благодарен если посоветуйте как можно реализовать данную задачу или подскажите литературу (в интернете пока ничего толкового не нашёл).

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

Правильнее не делать в прерывании ничего такого. что может потребовать несколько миллисекунд (нескольких тысяч мкс). Общепринятый подход таков - если нужно по прерыванию делать что-то длинное - в прерывании только взводим флаг (операция занимает всего несколько тактов МК) и выходим из прерывания, а собственно всю работу делает основной цикл программы.

 

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

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

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

О каком счётчике речь? Давайте код.

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

DmitryR пишет:

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

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

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

Да собственно данный код писался Вами, чем Вы в своё время мне очень помогли
Только на тот момент я измерял миллисекунды, а теперь надо микро
 

#define SIGNAL_PIN 3         // Определяем пин 3 как сигнальный 

#define CONTROL_PERIOD  1ul  // Определяем время ожидания 1 млсек

// Возможные состояния измерителя

enum METER_STATES: unsigned long {
  WAITING_FOR_SIGNAL = 0,   // = 0 Ожидание LOW на сигнальный пин
  MEASURING,                // = 1 Начинаем измерение
  READY                     // = 2 Конец измерений
};

// Переменная для хранени текущего состояния измерителя
// Изначальное состояние - ожидание нажатия кнопки

static volatile METER_STATES meterState = WAITING_FOR_SIGNAL;

// Переменная для хранения времени начала отсчёта и времени окончания отсчёта

static volatile unsigned long startSignalTime = 0, endSignalTime = 0;

//////////////////////////////////
//
// Функция - обработчик поступающего во 
// время измерения сигнала. Выполняется,
// когда на сигнальном пине появляется HIGH
//
void measureISR(void) {
  endSignalTime = millis(); // просто запоминем время
}

//////////////////////////////////
//
// Функция 
//
void waitingISR(void) {
  startSignalTime = endSignalTime = millis(); // запоминаем время
  detachInterrupt(SIGNAL_PIN - 2);
  pinMode(SIGNAL_PIN, INPUT);
  attachInterrupt(SIGNAL_PIN - 2, measureISR, FALLING);
  meterState = MEASURING; // Изменяем состояние на "измеряем"
}

void setup(void) {
  Serial.begin(115200);
  pinMode(SIGNAL_PIN, INPUT);
  attachInterrupt(SIGNAL_PIN - 2, waitingISR, RISING);
}

void loop(void) {
  if (meterState == MEASURING) {
      //
      // Проверяем, что с момент последнего HIGH 
      // прошло >= CONTROL_PERIOD миллисекунд
      //
      if (millis() - endSignalTime >= CONTROL_PERIOD) {
        //
        // если прошло, то заканчиваем измерения
        //
        detachInterrupt(SIGNAL_PIN - 2);
      meterState = READY; // закончили
      Serial.print("Result is: ");
      Serial.print(endSignalTime - startSignalTime);
      Serial.println(" milliseconds");
      }
  }
}

 

Волшебник
Offline
Зарегистрирован: 22.12.2016

Вставляете текст программы, которая в прерывании должна выполняться , в основной поток и обрамляете микрос() до и после. Разность даёт время. А то что она не в прерывании значения не имеет, практически.

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

А где micros в коде?

И я так и не понял о каком счётчие Вы говорите.

Если Вы говорите о micros, так он переполняется совсем не так часто, как Вы думаете - раз в 4 294 967 295 / 1000000 секунд, т.е. реже, чем раз в час.

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

Пробовал отрабатывать данный код на millis и на micros, на millis результат похож на правду, но подучается с большой погрешностью (т.е. выводится целое число, например 6 миллисекунд, а время сигнала на осциллографе примерно 6350 микросекунд), попробовал заменить на micros, с надеждой, что получу тот же результат, только более точный (например 6354 микросекунд), но после замены на микрос получил бредовый результат.
Потом начитался примерно таких выдержек "ISR должны быть как можно более короткими и быстрыми. Если в вашем скетче используется несколько ISR, одновременно можно запустить лишь одну, а другие будут выполнятся после — в зависимости от имеющегося приоритета. Счетчик в функции millis() полагается на прерывания, поэтому внутри ISR работать не будет. Поскольку функции delay() для работы тоже требуются прерывания, внутри ISR она тоже работать не будет. Функция micros() первое время будет работать нормально, но спустя 1-2 миллисекунды начнутся перебои. Функции delayMicroseconds() счетчик не требуется, поэтому она будет работать в нормальном режиме." Из чего сделал вывод о переполнении

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Точность micros, если я не ошибаюсь 4 микросекунды....

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

С точностью до 4х микросекунд вполне устроит

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

Нет, дело там не в переполнении. А не работает она в ISR именно потому, что запрещены прерывания.

Вам правильно сказали, что сидеть в ISR целые миллисекунды - плохая идея, но если уж никак - просто разрешите другие прерывания, т.е. поставьте в начале Вашей ISR вызов sei(); и микрос начнёт нромально работать. Но Вы должны понимать, что прерывание от таймера будет каждые 4 мкс ненадолго прерывать Вашу ISR.

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

Правильно поставил? 26 и 33 строки
 

#define SIGNAL_PIN 3         // Определяем пин 3 как сигнальный 

#define CONTROL_PERIOD  1ul  // Определяем время ожидания 1 млсек

// Возможные состояния измерителя

enum METER_STATES: unsigned long {
  WAITING_FOR_SIGNAL = 0,   // = 0 Ожидание LOW на сигнальный пин
  MEASURING,                // = 1 Начинаем измерение
  READY                     // = 2 Конец измерений
};

// Переменная для хранени текущего состояния измерителя
// Изначальное состояние - ожидание нажатия кнопки

static volatile METER_STATES meterState = WAITING_FOR_SIGNAL;

// Переменная для хранения времени начала отсчёта и времени окончания отсчёта

static volatile unsigned long startSignalTime = 0, endSignalTime = 0;

// Функция - обработчик поступающего во время измерения сигнала
// Выполняется,когда на сигнальном пине появляется HIGH

void measureISR(void) {
  sei();
  endSignalTime = micros(); // Запоминем время
}

// Функция - обработчик начала измерения

void waitingISR(void) {
  sei();
  startSignalTime = endSignalTime = micros(); // запоминаем время
  detachInterrupt(SIGNAL_PIN - 2);
  pinMode(SIGNAL_PIN, INPUT);
  attachInterrupt(SIGNAL_PIN - 2, measureISR, FALLING);
  meterState = MEASURING;  // Изменяем состояние на "измерение"
}

void setup(void) {
  Serial.begin(115200);
  pinMode(SIGNAL_PIN, INPUT);
  attachInterrupt(SIGNAL_PIN - 2, waitingISR, RISING);
}

void loop(void) {
  if (meterState == MEASURING) {
      //
      // Проверяем, что с момент последнего HIGH 
      // прошло >= CONTROL_PERIOD миллисекунд
      //
      if (micros() - endSignalTime >= CONTROL_PERIOD) {
        //
        // Если прошло, то заканчиваем измерение
        //
        detachInterrupt(SIGNAL_PIN - 2);
      meterState = READY;                 // Закончили
      Serial.print("Result is: ");
      Serial.print(endSignalTime - startSignalTime);
      Serial.println(" microseconds");
      }
  }
}

 

ssss
Offline
Зарегистрирован: 01.07.2016

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

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

Вроде ничего, только обратите внимание на строку 53. Она ведь задаёт максимальное время измерения (т.е. никей таймаут), если я првильно понял. Так вот этот таймаут у Вас сейчас всего одна микросекунда.

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

Попробовал на железе, не работает
Вот результат:

Result is: 164 microseconds
Result is: 160 microseconds
Result is: 160 microseconds
Result is: 160 microseconds
 

Вот код: 

#define SIGNAL_PIN 3           // Определяем цифровой пин 3 как сигнальный 

#define CONTROL_PERIOD  500ul  // Определяем время ожидания 100 мксек

// Возможные состояния измерителя

enum METER_STATES: unsigned long {
  WAITING_FOR_SIGNAL = 0,   // = 0 Ожидание LOW на сигнальный пин
  MEASURING,                // = 1 Начинаем измерение
  READY                     // = 2 Конец измерений
};

// Переменная для хранени текущего состояния измерителя
// Изначальное состояние - ожидание нажатия кнопки

static volatile METER_STATES meterState = WAITING_FOR_SIGNAL;

// Переменная для хранения времени начала отсчёта и времени окончания отсчёта

static volatile unsigned long startSignalTime = 0, endSignalTime = 0;

// Функция - обработчик поступающего во время измерения сигнала
// Выполняется,когда на сигнальном пине появляется LOW

void measureISR(void) {
  sei();
  endSignalTime = micros(); // Запоминем время
}

// Функция - обработчик начала измерения

void waitingISR(void) {
  sei();
  startSignalTime = endSignalTime = micros(); // запоминаем время
  detachInterrupt(SIGNAL_PIN - 2);
  pinMode(SIGNAL_PIN, INPUT);
  attachInterrupt(SIGNAL_PIN - 2, measureISR, FALLING);
  meterState = MEASURING;  // Изменяем состояние на "измерение"
}

void setup(void) {
  Serial.begin(115200);
  pinMode(SIGNAL_PIN, INPUT);
  attachInterrupt(SIGNAL_PIN - 2, waitingISR, RISING);
}

void loop(void) {
  if (meterState == MEASURING) {
      //
      // Проверяем, что с момент последнего HIGH 
      // прошло >= CONTROL_PERIOD микросекунд
      //
      if (micros() - endSignalTime >= CONTROL_PERIOD) {
        //
        // Если прошло, то заканчиваем измерение
        //
        detachInterrupt(SIGNAL_PIN - 2);
      meterState = READY;                 // Закончили
      Serial.print("Result is: ");
      Serial.print(endSignalTime - startSignalTime);
      Serial.println(" microseconds");
      }
  }
}
Вот осциллограмма: 
zujrIAmsBUo.jpg
DmitryR
Offline
Зарегистрирован: 12.05.2017

При этом вместо 160, он может выдавать другие значения: 204, 404 и тд
А если поменять на millis, то:

Result is: 6 milliseconds
Result is: 7 milliseconds
Что уже более похоже на правду
nik182
Offline
Зарегистрирован: 04.05.2015

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

Нет, дело там не в переполнении. А не работает она в ISR именно потому, что запрещены прерывания.

Вам правильно сказали, что сидеть в ISR целые миллисекунды - плохая идея, но если уж никак - просто разрешите другие прерывания, т.е. поставьте в начале Вашей ISR вызов sei(); и микрос начнёт нромально работать. Но Вы должны понимать, что прерывание от таймера будет каждые 4 мкс ненадолго прерывать Вашу ISR.

Ставить в прерывании sei(); бесполезно. В 328 нет возможности вызвыть прерывание из прерывания, поэтому прерывание блокикует вызов других прерываний. Если во время прерывания вызывается другое, то только выставляется флаг и оно будет вызвано только после завершения текущего. Если флагов несколько, то после выхода из прерывания будет вызвано первым прерывание с большим приоритетом. Именно поэтому любая обработка в нутри прерывания длинее 8 микросекунд нарушает счёт micros. У процессора нет информации, что прерывание вызвано повторно. Именно поэтому требуетмый алгоритм подсчета времени прерывания не будет работать. На stm32 работать будет. Там можно  прерывание превать прерыванием с большим приоритетом.

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

а в принципе эту задачу реально выполнить на atmega328? используя например assembler?

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

DmitryR, а зачем вы вообще иземеряете время в прерывании? Для точных измерений внешнего сигнала существуют другие методики.

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

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

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

DmitryR, в МК есть свой 16-битный таймер, нафига внешний? У таймера свой вход, он так-же может вызывать прерывание по любому фронту входящего сигнала, и хранить длительность в своих регистрах.

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

я так понимаю это уже на assembler надо программировать? Подключать таймер, обращаться к регистрам ....

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

А на си это все типа сделать низя ?

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

nik182 пишет:

Ставить в прерывании sei(); бесполезно. В 328 нет возможности вызвыть прерывание из прерывания, поэтому прерывание блокикует вызов других прерываний. 

Да, что Вы, Господь с Вами. Давайте сделаем так: заведём два таймера. Один срабатывает почти сразу, зажигает светодиод и погружается в мёртвый цикл пока истинна некая переменная. Когда (и если) он выйдет из цикла, гасит светодиод. А второй срабатывает через несколько секунд и делает ту самую переменную цикла false.

По Вашей логике, поскольку цикл находится внутри ISR, он никогда не закончится, т.к. второе прерывание не произойдёт и переменную цикла никто не сделает false.

Пробуем:

static volatile bool WaitForTimerOneSignal = true;

/////////////////////////////////////////////////////////
//
//	Заводим таймер 1 на прерывание через 3 секунды
//
static void setupTimerOne(void) {
	TCCR1A = 0;
	TCCR1B = 5; // делитель частоты - 1024
	TCNT1 = 18660;
	TIMSK1 = 1; // прерывание по переполнению
}

/////////////////////////////////////////////////////////
//
//	Заводим таймер 2 на прерывание через 16 миллисекунд
//
static void setupTimerTwo(void) {
	TCCR2A = 0;
	TCCR2B = 7; // делитель частоты - 1024
	TCNT2 = 0;
	TIMSK2 = 1; // прерывание по переполнению
}

void setup(void) {
	pinMode(LED_BUILTIN, OUTPUT);
	setupTimerOne(); // через 3 секунды
	setupTimerTwo(); // через 16 ьиллисекунд
}

void loop(void) {}


ISR(TIMER2_OVF_vect) {
	digitalWrite(LED_BUILTIN, HIGH); // зажигаем светодид
	TIMSK2 = 0; // больше прерываний не нужно
	sei();  // разрешаем другие прерывания
	// Если прерывание от таймера 1 не сработает,
	// то этот цикл будет вечным и светодиод никогда 
	// не погаснет
	while(WaitForTimerOneSignal);
	//
	digitalWrite(LED_BUILTIN, LOW); // гасим светодиод
}

ISR(TIMER1_OVF_vect) {
	TIMSK1 = 0;  // больше прерываний не нужно
	WaitForTimerOneSignal = false; // сигнализируем, что перывание сработало
}

Как видите, светодиод благополучно гаснет, значит, прерывание срабатывает. 

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

Вы правильно говорите, что новое прерывание запрещено пока обрабатывается предыдущее, только Вы смысл sei не поняли - она как раз сбрасывает флаг, говорящий о том, что идёт обработка прерывания. Ппосле неё новое прерывание вполне себе может обрабатываться.

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

DmitryR, у Вас путаница в логике программы. Проверяйте логику. Не в таймерах счастье. Попытйтесь разобраться, чему у Вас равен в endSignalTime строке 53 в том случае, если строка 27 ещё не отработала, а чему равен, если уже отработала. Ну, и что Вы там считаете?

Если до субботы не разберёстесь, смогу помочь. Буду онлайн примерно часов с 10 до 12. 

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

Хорошо, попробую разобраться, потом ещё отработать на железе

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

В примере второй таймер заводится не точно на 16 мс, а приблизительно, правильно?

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

Да, конечно, там просто полный цикл 8-битного таймера - 16 384 микросекунды.

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

Да. Согласен. Вызывается. Буду знать.

Остался только вопрос. А повторно прерванное после sei если вызовется что будет?

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

Вы имеете в виду на ту же ISR если прилетит? Ничего страшного. Отработает если только ISR написана так, что не ломается от повторного входа. Дело в том, что локальные переменные (если есть) у второго входа будут свои - не те же самые, что у первого, а вот глобальные - те же самые и за них они могут подраться.

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

nik182, есть ещё штатная опция запуска неблокирующего прерывания ISR(имя, ISR_NOBLOCK);

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

Попробовал ещё раз разобраться, появились вопросы

1) Задали константы

01 #define SIGNAL_PIN 3           
02  
03 #define CONTROL_PERIOD  500ul

2) Создаём 3 константы - состояния устройства

07 enum METER_STATES: unsigned long {
08   WAITING_FOR_SIGNAL = 0,   
09   MEASURING,               
10   READY                     
11 };

3) Создаём статические переменные для запоминания времени срабатывания прерывания и времени выхода из него

16 static volatile METER_STATES meterState = WAITING_FOR_SIGNAL;
17  
18  
20 static volatile unsigned long startSignalTime = 0, endSignalTime = 0;

4) Прерывание - измерение

25 void measureISR(void) {
26   sei();
27   endSignalTime = micros(); 

5) Прерывание, в котором при первом спадающем фронте начинается измерение времени

void waitingISR(void) {
33   sei();
34   startSignalTime = endSignalTime = micros(); 
35   detachInterrupt(SIGNAL_PIN - 2);
36   pinMode(SIGNAL_PIN, INPUT);
37   attachInterrupt(SIGNAL_PIN - 2, measureISR, FALLING);
38   meterState = MEASURING;  
39 }

6) Код, выполняемый при запуске, т.е. ожидание начала сигнала, и запуск внешнего прерывания waitingISR

41 void setup(void) {
42   Serial.begin(115200);
43   pinMode(SIGNAL_PIN, INPUT);
44   attachInterrupt(SIGNAL_PIN - 2, waitingISR, RISING);
45 }

7) Основной код, выполняемый в цикле, задача которого отслеживание пропадания сигнала

47 void loop(void) {
48   if (meterState == MEASURING) {
49       //
50       // Проверяем, что с момент последнего HIGH
51       // прошло >= CONTROL_PERIOD микросекунд
52       //
53       if (micros() - endSignalTime >= CONTROL_PERIOD) {
54         //
55         // Если прошло, то заканчиваем измерение
56         //
57         detachInterrupt(SIGNAL_PIN - 2);
58       meterState = READY;                 // Закончили
59       Serial.print("Result is: ");
60       Serial.print(endSignalTime - startSignalTime);
61       Serial.println(" microseconds");
62       }
63   }
64 }

Вопросы:
- с 1 по 18 строки всё понятно, 
- в в 20 я так понимаю делаем, что-то типа обнуления переменных?
- с 7 по 11 не совсем понятен смысл этого перечесления состояний
- с 25 по 27 похоже на прерывание, которое задаёт начало измерению, непонятна строка endSignalTime = micros(); 
- с 32 по 39 прерывание - ожидание; вызывает прерывание по спаду фронта сигнала, начинающее измерение, переводит измеритель в состояние MEASURING
Почему используется 2 прерывания? зачем необходимо прерывание waitingISR?
- строка 44:   attachInterrupt(SIGNAL_PIN - 2, waitingISR, RISING); - прерывание, вызывающее waitingISR, по возрастания фронта?
- 53:  Проверка условия того, что время с последнего HIGH 
>= CONTROL_PERIOD
micros() - endSignalTime : что отражает endSignalTime ?

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

DmitryR пишет:

Почему используется 2 прерывания? зачем необходимо прерывание waitingISR?

А вот это я как раз у Вас хотел спросить. Нафига оно Вам? Если Вы про то, что я когда когда-то писал (я толком не помню), так скорее всего, что Вы просили, то я и писал не особо зная задачу.

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

Вот смотрите, прибилзительно рассуждайте так. К трём состояниям "ждем", "измеряем" и "закончили", добавим четвёртое - "аминь".

1. Устанваливаем состояние "ждём".
2. В loop проверяем
       ЕСЛИ состояние=="измеряем" И истек таймаут ТО {
               говорим, что не дожались;
               устанавливаем endTime на текущее время
              переходим в состояние "закончили"
      ИНАЧЕ ЕСЛИ состояние == "закончили"
             печатаем  endTime-startTime
            переходим в состояние "аминь"
     (Все - в лупе больше не делаем ни хрена!!!)
3. ISR устрена так
    ЕСЛИ состояние == "ждём" ТО
             запоминаем текущее время в startTime
             переходим в состояние "измеряем"
    ИНАЧЕ ЕСЛИ состояние == "измеряем" ТО
            запоминаем текущее время в endTime
           переходим в состояние "закончили"

Вот сосбственно и всё, что нужно написать (если я правильно понимаю задачу).

Проверьте на правильность понимани и напишите.

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

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

На всякий случай напомню задачу: 2_0.png

CONTROL_RERIOD - должен быть больше длительности последнего пика (сиреневый сигнал), после которого устанавливается HIGH

Код, который получился:

#define SIGNAL_PIN 3         
#define CONTROL_PERIOD  50ul  

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;

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);
}

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

 

 

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

Первый 12 строк я думаю работают нормально
Далее идёт такая логика:
строка 31 - устанавливаем состояние "ожидание сигнала"

строка 32 - ждём наступления прерывания measure_startISR которое вызывается спадом сигнала с HIGH на LOW

По наступлению данного условия наступает прерывание measure_startISR - строка 14
При этом запонимается время начала 
startSignalTime = micros();

При нарастании сигнала с LOW на HIGH - строка 18, наступает прерывание measure_endISR - строка 21
Которая запоминает время 
endSignalTime = micros(); 
и переводит состояние в 
MEASURING_END

Далее должно провериться условие (endSignalTime - startSignalTime  >= CONTROL_PERIOD)
Если условие выполняется, то READY и выводим результат

 

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

Но получается, что control period определяется по периоду при LOW, а надо при HIGH

ssss
Offline
Зарегистрирован: 01.07.2016

Уже давно забыл как на ваших мегах всё так плохо, но картиночка прям дежавю. Где-то я её уже видел при данном вопросе. Пишу относительно СТМ32, но может вы сумеете транспонировать решение под себя, исходя их ваших реалий.

1. Мерять каждый период, потом посчитать их сумму. Для СТМ32 это совсем не трудно, там есть такой режим таймера. Т.к. последний период не полный, на дополнительном канале сравнения выставить защитный интервал чуть больше периода. Период же, насколько я понял, +- известен. По факту защитного интервала суммируем периоды + длительность последнего отрицательного импульса.

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

ssss, вы ему уже советовали stm :) Когда он вставил картинку с затухающей синусоидой, я сразу вспомнил его первую тему .  Оказывается уж пятый месяц товарищ бьётся, никак добротность у индуктивности не измерит.. За это время можно было и СИ и STM изучить, и за полчаса всё написать..

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

DmitryR,

я тут собирался быть с 10, но простотите, обстоятельства изменились, буду с 18.

ssss
Offline
Зарегистрирован: 01.07.2016

dimax пишет:

ssss, вы ему уже советовали stm :) Когда он вставил картинку с затухающей синусоидой, я сразу вспомнил его первую тему .  Оказывается уж пятый месяц товарищ бьётся, никак добротность у индуктивности не измерит.. За это время можно было и СИ и STM изучить, и за полчаса всё написать..

+100500 !

Даже добавить нечего.

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

Про пятый месяц: сугубо по датам, да. А по факту нет. 
Эта задача не является у меня основной, просто желательно сделать, от этого будет польза, что-то типа спортивного интереса. 
Про СИ, STM и тд: учу по мере возможности, но не получается на это выделять достаточное количество времени 

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

Алгоритм мне представляется совсем простым и даже без прерываний.

Стоим ждём LOW. Запоминаем время и сохроняем его как  начало,

ждем HIGH, обзываем концом, ждем LOW.

Если LOW раньше timeout, опять ждем HIGH 

Иначе  время = начало-конец.

Время можно из microseconds() брать или таймер 16 битный запустить. С ним всяко время двухбайтное быстрее счититься будет, чем четырёхбайтное.  

 

 

 

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

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

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

Приведеннный пример должен работать. Если нет - увеличте CONTROL_PERIOD до 1000 и даже до 2000. Если  все равно не работает, ошибки  в схеме.

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

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

в 15м сообщении осциллограмма прям с ноги arduino nano

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

OK! Тогда увеличь интервал и замени в строке37 сообщения 15 FALLING на CHANGE. Должно теперь работать.

......

Проверяй, Чего ждешь? :)

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

У меня всё железо не дома) теперь только в понедельник могу 
А в чём суть замены на change? там же по спаду 

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

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

 

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

А когда Вы говорили, что приведеннный пример должен работать, это речь о коде в 15м сообщении или в 34м?

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

Они ничем не отличаются, кроме длительности интервала. Еще раз - поставьте не 50 и не 500 а 1000 или 2000.

Давайте "госДуму" закроем? Ставьте чейндж и 2000, сообщите о результате. А радость человеческого общения - перенесем к друзьям и пиву, ОК?