Как устранить "дребезг" на прерывании?

smagluk
Offline
Зарегистрирован: 01.04.2018

Ситуация.

Необходимо иметь контроль сетевого напряжения  есть/нет.

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

Второе условие - для определения состояния используем прерывание.

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

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

Вот пример скетча.

#define timeSeconds 100

//  на 34 пине мониторим напряжение
const int motionSensor = 34;

volatile uint32_t debounce;

void IRAM_ATTR detectsMovement() {
 
  Serial.println("сработало прерывание!!!");
  if (millis() - debounce >= 1000 && digitalRead(motionSensor)) {
    Serial.println("Вошло в иф!!!");
    Serial.println(debounce);
    debounce = millis();
    Serial.println(debounce);
    Serial.println("Падение сетевого напряжения!!!");
    }

Serial.println("Мимо ифа или !!!");

}

void setup() {
  // инициализируем порт
  Serial.begin(115200);
  // Motion Sensor устанавливаем как  вход (34 пин не имеет резистора подтяжки)
  pinMode(motionSensor, INPUT);
 
// Третий аргумент - это режим. Есть 5 разных режимов:
//    LOW: для запуска прерывания всякий раз, когда вывод LOW;
//    HIGH: для запуска прерывания всякий раз, когда вывод HIGH;
//    CHANGE: для запуска прерывания всякий раз, когда вывод меняет значение - например, с HIGH на LOW или LOW на HIGH;
//    FALLING: когда пин переходит из HIGH в LOW;
//    RISING: запускается, когда пин переходит с LOW на HIGH.

  attachInterrupt(digitalPinToInterrupt(motionSensor), detectsMovement, CHANGE);

}

void loop() {

}

 

smagluk
Offline
Зарегистрирован: 01.04.2018

Замена  CHANGE на FALLING не приводит к нужному результату.

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

А зачем прерывание на такой медленный процесс? Тем более, что в сети бывают перерывы до нескольких периодов, например при переключении у конечного потребителя на резервную линию. Есть несколько разных алгоритмов контроля сети переменного тока 50 Гц но все они имеют время принятия решения существенно превосходящее половину периода сети - 10мс. 

Что бы не было дребезга надо ставить фильтр на входе. Для сетевого напряжения герц на 100.

Из прерывания не стоит ничего выводить. Надо выставлять флаг, и по флагу вывод делать в основном цикле программы.

И что? с 2018 года не научился вставлять текст программы? Почитай в песочнице правила форума.

smagluk
Offline
Зарегистрирован: 01.04.2018

Это проект для запуска генератора  с отслеживанием сетевого/аккумулятора/генератора.

Можно конечно мониторить в цикле и гонять процессор , но проще пробуждать и выполнять определенный код.

А так спит себе процессор и не жрет ресурсов, можно что то еще навесить на него. 

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

Триггер шмитта

smagluk
Offline
Зарегистрирован: 01.04.2018

rkit пишет:

Триггер шмитта

А программно? 

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

smagluk пишет:

rkit пишет:

Триггер шмитта

А программно? 

одновибратор

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

Честно говоря про сон при контроле сетевого напряжения это даже не смешно. Если б от маленького аккумулятора питание и надо как можно дольше прожить... А так выглядит как фобия какая то.   

smagluk
Offline
Зарегистрирован: 01.04.2018

Вот такая штука используется.

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

smagluk пишет:

Вот такая штука используется.

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

smagluk
Offline
Зарегистрирован: 01.04.2018

nik182 пишет:

Что бы не было дребезга надо ставить фильтр на входе. Для сетевого напряжения герц на 100.

Из прерывания не стоит ничего выводить. Надо выставлять флаг, и по флагу вывод делать в основном цикле программы.

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

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

smagluk пишет:

А программно? 

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

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

А запретить флагами только прерывания от одного источника религия не позволяет ???

smagluk
Offline
Зарегистрирован: 01.04.2018

 

Там емкость на входе стоит.

smagluk
Offline
Зарегистрирован: 01.04.2018

Komandir пишет:

А запретить флагами только прерывания от одного источника религия не позволяет ???

А разве так можно? Пойду читать доки.

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

Можно даже "Машку за ляшку" ...

smagluk
Offline
Зарегистрирован: 01.04.2018

nik182 пишет:

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

[/quote]

 

Спасибо за пинок в нужном направлении.

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

smagluk пишет:

Там емкость на входе стоит.

А как же прерывания? С такой емкостью только есть нет индицировать. Или прерывания идёт только при подаче и отключении напряжения? 

smagluk
Offline
Зарегистрирован: 01.04.2018

nik182 пишет:

А как же прерывания? С такой емкостью только есть нет индицировать. Или прерывания идёт только при подаче и отключении напряжения? 

[/quote]

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

При пропадании напряжения, происходит разряд конденсатора через оптрон и вот в этом моменте имеется неустойчивое положение и идет "генерация " импульсов 2-6 шт. Подключал осциллограф и регистрировал. Я даже в IF прописывал сообщения в порт, чтобы считывать их.

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

Далее в программе у меня идет выдержка 5 минут, и если не появляется напряжение , то происходит алгоритм запуска генератора.

Morroc
Offline
Зарегистрирован: 24.10.2016

Если никаких прерываний нет 5 минут и уровень HIGH запускаем генератор, не ?

smagluk
Offline
Зарегистрирован: 01.04.2018

Morroc пишет:

Если никаких прерываний нет 5 минут и уровень HIGH запускаем генератор, не ?

Хм, а действительно, флаг срабатывания + время срабатывания, да пофиг тогда на колличество срабатываний.

спасибо.

Gromozeka
Offline
Зарегистрирован: 06.10.2022

У вас ошибка в алгоритме отслеживания. 

Даже учитывая что debounce будет установлен в 0 в самом начале - у вас происходит отслеживания разницы пока прерывания не было. 

Скажем так - при первом срабатывании прерывания через более чем 1 секунду от старта программы вы попадете внутрь if в функции прерывания потому, что время без прерывания будет подсчитано как дебаунс. 

А вам надо понять было ли пониженное напряжение в течении какого-то времени. А на постоянно пониженное второй раз прерывание не сгенерится. Фактически вам надо отслеживать только прыжки туда сюда, а значит переменная debounce должна обновляться постоянно за пределами if конструкции. 

Я бы сделал по другому, в прерывании учитывал бы время срабатывания и обновлял debounce всегда, как точку старта. А проверку сколько времени было то или иной непрерывно одинаковое (пониженное или повышенное) напряжение отслеживал бы в loop(). 

Для решения запускать или глушить генератор отслеживал бы какое из напряжений (низкое или нормальное) имеет постоянное состоние. 

Добавлю delay() в теле прерывания использовать нельзя. Насколько мне известно - там это не сработает (так вроде в описании), ну и millis внутри прерывания работает 1 раз и всегда в следующие вызовы возвращает тоже самое время. 

smagluk
Offline
Зарегистрирован: 01.04.2018

Насколько корректен такой код внутри функции прерывания?

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

// на 34 пине мониторим напряжение 
const int motionSensor = 34;
volatile boolean flagpower  = true;  // флаг наличия сетевого напряжения
volatile uint32_t debounce=0; // максимальное число 4 294 967 295


void IRAM_ATTR detectsMovement() {

  if (millis() - debounce >= 1000 && digitalRead(motionSensor)) 
    {
      Serial.println("Падение сетевого напряжения!!! это дребезг");
      flagpower = true; 
    } 
  else if (digitalRead(motionSensor))
    {
      flagpower = false;
      Serial.println("Падение сетевого напряжения!!! Окончательно");
    }
  else 
    {
      flagpower = true;
      Serial.println("Напряжение в норме!!!");     
    }
  debounce = millis();
}

void setup() {

  Serial.begin(115200);
  pinMode(motionSensor, INPUT);
  attachInterrupt(digitalPinToInterrupt(motionSensor), detectsMovement, FALLING);
}

void loop() {

}

 

Дим-мычъ
Offline
Зарегистрирован: 20.03.2021

Интересно, как работает Serial.println,  когда прерывания запрещены?

P.S.

Замечание по использованию

Внутри функции обработки прерывания не работает delay(), значения возвращаемые millis() не изменяются. Возможна потеря данный передаваемых по последовательному соединению (Serial data) в момент выполнения функциии обработки прерывания. Переменные, изменяемые в функции, должным быть объявлены как volatile

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

Дим-мычъ пишет:

Интересно, как работает Serial.println,  когда прерывания запрещены?

P.S.

Замечание по использованию

Внутри функции обработки прерывания не работает delay(), значения возвращаемые millis() не изменяются. Возможна потеря данный передаваемых по последовательному соединению (Serial data) в момент выполнения функциии обработки прерывания. Переменные, изменяемые в функции, должным быть объявлены как volatile

Всё неправда, кроме millis()

Дим-мычъ
Offline
Зарегистрирован: 20.03.2021

rkit пишет:

Всё неправда, кроме millis()

Не знаю, у меня было дело Serial.print не работал, пока перед ним sei() не поставил

smagluk
Offline
Зарегистрирован: 01.04.2018

Дим-мычъ пишет:

Не знаю, у меня было дело Serial.print не работал, пока перед ним sei() не поставил

Вывод в порт прекрасно работает, но это для отладки.  Смысл в том , что получать флаг в ифе.

Где то говорили, что case быстрее отрабатывает, но пока это еще не актуально.