Чи може бути викликана функція автоматично, коли вхід змінюється?


21

Наразі мій ескіз перевіряє вхідний штифт щоразу навколо головної петлі. Якщо він виявить зміну, він викликає власну функцію, щоб відповісти на неї. Ось код (зведений до основного):

int pinValue = LOW;

void pinChanged()
{
    //...
}

void setup()
{
    pinMode(2, INPUT);
}

void loop()
{
    // Read current input
    int newValue = digitalRead(2);

    // Has the input changed?
    if (newValue != pinValue) {
        pinValue = newValue;
        pinChanged();
    }
}

На жаль, це не завжди працює належним чином для дуже коротких змін на вході (наприклад, коротких імпульсів), особливо якщо loop()це працює трохи повільно.

Чи є спосіб змусити Arduino виявити зміну входу і викликати мою функцію автоматично?


1
Що ви шукаєте для свого зовнішнього переривання
Буцке

Відповіді:


26

Це можна зробити за допомогою зовнішніх переривань. Більшість Arduinos підтримують це лише на обмеженій кількості штифтів. Повна інформація див. У документації на attachInterrupt().

Якщо припустити, що ви використовуєте Uno, ви можете зробити це так:

void pinChanged()
{
    //...
}

void setup()
{
    pinMode(2, INPUT);
    attachInterrupt(0, pinChanged, CHANGE);
}

void loop()
{
}

Це буде викликати pinChanged()кожен раз, коли буде виявлена ​​зміна на зовнішньому перериванні 0. На Uno, що відповідає PIN-коду GPIO 2. Зовнішня нумерація переривань відрізняється на інших платах, тому важливо перевірити відповідну документацію.

Однак у цьому підході є обмеження. Спеціальна pinChanged()функція використовується як звичайний режим переривання (ISR). Це означає, що решта коду (все в loop()) тимчасово зупиняється під час виконання дзвінка. Щоб запобігти зриву будь-яких важливих термінів, ви повинні прагнути зробити ІСР якомога швидшими.

Також важливо зазначити, що під час вашого ISR жодних інших переривань не буде працювати. Це означає, що все, що покладається на переривання (наприклад, ядро delay()та millis()функції), може не працювати належним чином всередині нього.

Нарешті, якщо вашому ISR потрібно змінити будь-які глобальні змінні в ескізі, вони, як правило, повинні бути оголошені як volatile, наприклад:

volatile int someNumber;

Це важливо, оскільки воно повідомляє компілятору, що значення може змінитися несподівано, тому слід бути обережним, щоб не використовувати застарілих копій / кешів.


щодо "коротких імпульсів", згаданих у запитанні, чи є мінімальний час, коли штифт повинен перебувати у стані, щоб він спровокував переривання? (очевидно, це буде набагато менше, ніж опитування, що залежить від того, що ще відбувається в циклі)
sachleen

1
@sachleen Це буде працювати до тих пір, поки це не відбудеться під час виконання функції ISR (як пояснено у відповіді); ось чому pinChanged()має бути якомога коротшим. Отже, як правило, мінімальним часом повинен бути час виконання самої pinChanged()функції.
jfpoilpret

2
+1 за цю дуже детальну відповідь, яка включає всі невмілі речі, про які слід дбати, коли використовуєш переривання!
jfpoilpret

3
На додаток до оголошення загальних глобалів volatile, якщо глобальна змінна ширша за 1 байт, як це деяка кількість, ви повинні захистити від переривання зміни штифта, що виникає між доступом до байтів програмою. Таке твердження someNumber +=5;включає додавання низьких байтів та додавання високих байтів із включеним переносом. Ці дві (більше, для більш широких змінних) не повинні ділитися перериванням. Вимкнення переривань та відновлення їх до та після операції (відповідно) достатньо.
JRobert

@sachleen - щодо мінімального розміру імпульсу. Важко знайти певну відповідь у таблиці, але, судячи з термінів перерв на зміну штифтів, вони фіксуються протягом півгодинного циклу. Після того, як переривання буде "запам'ятоване", воно залишається запам'ятовуваним, доки ISR не починається і не справляється з цим.
Нік Гаммон

5

Будь-який стан зміни на будь-якому штифті, налаштованому як цифровий вхід, може створити переривання. На відміну від унікальних векторів для причин переривань через INT1 або INT2, функція PinChangeInt використовує загальний вектор, а для цього вектора потрібно регулярно виконувати службу переривань (aka ISR), щоб визначити, який контакт був змінений.

На щастя, бібліотека PinChangeInt робить це легко.

PCintPort::attachInterrupt(PIN, burpcount,RISING); // attach a PinChange Interrupt to our pin on the rising edge
// (RISING, FALLING and CHANGE all work with this library)
// and execute the function burpcount when that pin changes

0

У випадку, якщо ви хочете виявити напругу, що переходить поріг , а не просто ВИСОКИЙ або НИЗЬКИЙ, ви можете використовувати аналоговий компаратор. Приклад ескізу:

volatile boolean triggered;

ISR (ANALOG_COMP_vect)
  {
  triggered = true;
  }

void setup ()
  {
  Serial.begin (115200);
  Serial.println ("Started.");
  ADCSRB = 0;           // (Disable) ACME: Analog Comparator Multiplexer Enable
  ACSR =  bit (ACI)     // (Clear) Analog Comparator Interrupt Flag
        | bit (ACIE)    // Analog Comparator Interrupt Enable
        | bit (ACIS1);  // ACIS1, ACIS0: Analog Comparator Interrupt Mode Select (trigger on falling edge)
   }  // end of setup

void loop ()
  {
  if (triggered)
    {
    Serial.println ("Triggered!"); 
    triggered = false;
    }

  }  // end of loop

Це може бути корисно для таких речей, як світлодетектори, де вам може знадобитися виявити зміну (скажімо) 1V на 2V на вході.

Приклад схеми:

введіть тут опис зображення

Ви також можете використовувати блок введення запису на процесорі, який запам'ятає точний час певних входів, зберігаючи поточний підрахунок таймера / лічильника 1. Це дозволяє зберігати точний (ну, майже точний) момент, коли подія виникло інтерес, а не введення затримки (можливо, декількох мікросекунд), перш ніж ISR може бути використаний для фіксації поточного часу.

Для критично важливих додатків це може дати дещо підвищену точність.

Приклад застосування: Перетворіть свій Arduino в тестер конденсаторів

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.