Позволяет ли Ардуино на одном прерывании отслеживать оба фронта?

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

Как пример:

void setup ()
{
  // Всяк разные установки и объявления
  attachInterrupt(0, SWITCH_ON, RISING);    
  attachInterrupt(0, SWITCH_OFF, FALLING); 
 }
void loop ()
{
  // Тело программы
}
void SWITCH_ON ()
{
  digitalWrite(pinOut, HIGH);
}
void SWITCH_OFF ()
{
  digitalWrite(pinOut, LOW);
}

Может ли один и тот же пин прерывания отслеживать передний и задний фронты управляющего импульса? Не нашел (подскажите). Или неправильно искал (ткните носом в ссылочку, где почитать).

Заранее спасибо!

trembo
trembo аватар
Offline
Зарегистрирован: 08.04.2011

По моему 5 строка отменяет четвертую....
Может быть надо в самом прерывании менять фронты?
Или если не контролировать куда изменился сигнал то просто
CHANGE ?

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

Sonologist, так нельзя. Либо паралелить 2 пина, либо в режим CHANGE  и  в теле прерывания перечитать порт, и узнать что его вызвало.

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

trembo пишет:

По моему 5 строка отменяет четвертую....

Вот как раз про это я и спросил.

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

dimax пишет:

Sonologist, так нельзя. Либо паралелить 2 пина, либо в режим CHANGE  и  в теле прерывания перечитать порт, и узнать что его вызвало.

Правильно ли я понял, что после события CHANGE надо проверить состояние пина: если оно HIGH, то пришедший CHANGE был передним фронтом, если LOW- то задним. И в зависимости от этого плясать? В принципе - тоже вариант.

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

Sonologist пишет:

Правильно ли я понял, что после события CHANGE надо проверить состояние пина: если оно HIGH, то пришедший CHANGE был передним фронтом, если LOW- то задним. И в зависимости от этого плясать? В принципе - тоже вариант.

ну да, вариант - но можно не успеть

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

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

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

b707 пишет:

ну да, вариант - но можно не успеть

В моем случае "не успеть" не проблема. Поясню. Имеется детектор перехода через ноль. Обычно его импульс стараются сделать как можно тоньше и как можно ближе к истинному переходу через ноль. У меня же задача при приходе переднего фронта  успеть просчитать, надо ли пропускать в нагрузку очередной полупериод или нет. Чтобы времени было достаточно, детектор нуля надо сделать как раз весьма не идеальным. То есть, чтоб передний фронт заметно опережал истинный переход через ноль. А по фронту спада (он точно так же заметно отстает от перехода через ноль) вырубить управляющий импульс триака. На следующем переходе через ноль триак сам заткнется. Так что, если импульс детектора будет длиной (шириной) 1-2 мсек, времени на расчеты хватит с головой.

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

nik182 пишет:

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

Тут вопрос решен уже. Я беру сигнал не непосредственно с детектора нуля, а с триггера на его хвосте, то есть дребезг убран аппаратно: идеальный меандр.

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

Идеально меандра небывает. У Вас есть емкость монтажа, индуктивность проводников и выходное сопротивление генератора меандра. Хорошим осцилографом видно что в таких условиях на фронте импульса всегда есть очень высокочастотные колебания. На этот эффект я как раз и нарвался, когда исследовал почему моргает лампочка за тирристорным регулятором на тиньке, если в прерывании всести проверку на уровень сигнала после фронта.  

asam
Offline
Зарегистрирован: 12.12.2018

Sonologist пишет:

В моем случае "не успеть" не проблема. Поясню. Имеется детектор перехода через ноль. Обычно его импульс стараются сделать как можно тоньше и как можно ближе к истинному переходу через ноль. У меня же задача при приходе переднего фронта  успеть просчитать, надо ли пропускать в нагрузку очередной полупериод или нет. Чтобы времени было достаточно, детектор нуля надо сделать как раз весьма не идеальным. То есть, чтоб передний фронт заметно опережал истинный переход через ноль. А по фронту спада (он точно так же заметно отстает от перехода через ноль) вырубить управляющий импульс триака. На следующем переходе через ноль триак сам заткнется. Так что, если импульс детектора будет длиной (шириной) 1-2 мсек, времени на расчеты хватит с головой.

Если мы говорим о сетевом напряжении, то там все настолько медленно, с точки зрения процессора, все меняется, что воротить какие-то сложные вещи смысла нет. Обычной оптопары и прерывания по LOW достаточно что бы все успеть. Тем более, что прерывание срабатывает чуть раньше, чем на самом деле переход через ноль случается.

Я делал так

В прерывании читал 5 раз подряд состояние пина, если все разы 0, то запрещал прерывания от сети на 10мс и делал рассчет по Брезинхему - запускать или не запускать симистор.  

Для разрешения прерывания обратно через 10мс я использовал 1мс таймер. По началу я им же пользовался для ограничения длинны импульса открывания симистора. Но потом поэкспериментировал и забил. Просто держу его HiGH до конца полуволны.


#define ACINT (INT0) // (D2)
#define AC_DELAY 10
uint8_t gACdelay_cnt;

#define regMax (100)   //How many power levels we want
int8_t gRegError=regMax/2;
uint8_t gPowerPercent=0;             //Current power level

void inline DisableAC_Int()
{
  EIMSK &= ~_BV(ACINT);

}
void inline EnableAC_Int()
{
  EIMSK |= _BV(ACINT);
}


void do_bresenham()
{
    gRegError=gRegError-gPowerPercent;
    if (gRegError<=0)
    {
      gRegError=gRegError+regMax;
      SwitchOn();
    }
    else
    {
      SwitchOff();
    }
}

// zerro crossing interrupt
ISR(INT0_vect)
{
  for (uint8_t cnt=0;cnt<5;++cnt)
  {
    if (readACpin()) return;
  }

  gACdelay_cnt=0;   //disable AC interrupts for AC_DELAY ms
  DisableAC_Int();
  do_bresenham();

}

// 1 mSec interrupts
ISR(TIMER0_COMPA_vect)
{
   gACdelay_cnt++;
   if (gACdelay_cnt>=AC_DELAY)
   {
     gACdelay_cnt=AC_DELAY;
     EnableAC_Int();
   }
}

 

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

nik182 пишет:

Идеально меандра небывает. У Вас есть емкость монтажа, индуктивность проводников и выходное сопротивление генератора меандра. Хорошим осцилографом видно что в таких условиях на фронте импульса всегда есть очень высокочастотные колебания. На этот эффект я как раз и нарвался, когда исследовал почему моргает лампочка за тирристорным регулятором на тиньке, если в прерывании всести проверку на уровень сигнала после фронта.  

Да идеального не бывает, но то, что я сейчас имею вполне себе адекватно отрабатывает. Слава Богу :)

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

asam пишет:

Если мы говорим о сетевом напряжении, то там все настолько медленно, с точки зрения процессора, все меняется, что воротить какие-то сложные вещи смысла нет....

...Я делал так

В прерывании читал 5 раз подряд состояние пина, если все разы 0, то запрещал прерывания от сети на 10мс и делал рассчет по Брезинхему - запускать или не запускать симистор.  

Для разрешения прерывания обратно через 10мс я использовал 1мс таймер. По началу я им же пользовался для ограничения длинны импульса открывания симистора. Но потом поэкспериментировал и забил. Просто держу его HiGH до конца полуволны...

Я использовал два фронта по совету соконфетников. В сетапе воткнул "attachInterrupt(0, TRIAC, CHANGE);"

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

void TRIAC ()  //Греем ТЭН
{
  if (triac_flag==true) //Если нагрев разрешен
    {
      if (digitalRead (2)==HIGH)
        {
          int pulse = 0; 
          int lev = power + err; // Текущий уровень с учетом ошибки дискретизации, сделанной на предыдущем полупериоде.
          if(lev >= 50) 
            {
              pulse = 1; // Нужно бы подать импульс, но проверим на постоянную составляющую. Если pulse == 1, но подача импульса 
                     // приведет к увеличению постоянной составляющей, то отложим подачу импульса на следующий полупериод. 
                     // Он будет другого знака, что приведет к уменьшению постоянной составляющей
            }
          if(ps*pulse*pp > 0) 
            {
              pulse = 0;
            }
          if(pulse) 
            {  
              digitalWrite(8, HIGH); 
              err = lev - 100;  // Считаем ошибку для следующего полупериода
        } 
        else 
          {
            digitalWrite(8, LOW); 
            err = lev;  
          }
        ps += pp*pulse; // Считаем текущую постоянную составляющую
        pp = -pp; // Следующий полупериод будет другого знака
         
    }
  else
    {
      digitalWrite(8, LOW);
    } 
  }
  else
  {
    digitalWrite(8, LOW);
  }
}

 

Onkel
Offline
Зарегистрирован: 22.02.2016

Есть оптроны с двумя светодиодами, например PC814. Вот их обычно и применяют для детектирования нуля. Если Вам нужно максимально быстрый код, то учтите, что команды digitalWrite () занимают 6 -8 мкс, лучше управлять пинами через PORTx= -всего-то пара тактов.. Аналогично digitalRead выполняются за 4-6 мкс, а PINx - за пару тактов, или за так, не помню точно.

Sonologist
Sonologist аватар
Offline
Зарегистрирован: 08.06.2018

Onkel пишет:

Есть оптроны с двумя светодиодами, например PC814. Вот их обычно и применяют для детектирования нуля. Если Вам нужно максимально быстрый код, то учтите, что команды digitalWrite () занимают 6 -8 мкс, лучше управлять пинами через PORTx= -всего-то пара тактов.. Аналогично digitalRead выполняются за 4-6 мкс, а PINx - за пару тактов, или за так, не помню точно.

Все правильно. Я уже прочитал про такое. Но в моем случае речь идет о периодах 1-3 мсек, поэтому можно пока обойтись посконными методами, максимально быстрый код не нужен :) Про 814 я тоже в курсе, но лень было ехать в чипдип, когда в ящике валяется десятка полтора 4N25 и столько же диповских мостиков. ..

Kakmyc
Offline
Зарегистрирован: 15.01.2018

Зачем вообще в таком устройстве (нагревателе), у которого инерция опупительная , отслеживать переход через 0 ?
Там ШИМ на digitalWrite'ах 2секундный точно так же будет работать.