Аппаратное прерывание вызова двух ISR

Я потратил на это три дня, и либо я чего-то не замечаю, либо что-то просто не так.

Дверной звонок. Выключатель передней двери и выключатель задней двери. Две процедуры прерывания обслуживания, по одной прикрепленной к каждому контакту. Рудиментарный дебютизм в ISR, кажется, работает.

Но по какой-то причине обе процедуры прерывания срабатывают при нажатии любого из переключателей. Я пробовал ПОДНИМАТЬСЯ, ПАДАТЬ, ОПУСКАТЬСЯ, МЕНЯТЬСЯ, и все это работает одинаково. Я попробовал INPUT_PULLUP и INPUT_PULLDOWN и изменил полярность на переключателях. Никаких изменений. Я пробовал разные вывода, цифровые, аналоговые. Кажется, ничто не удерживает обоих ИСРОВ от стрельбы.

Нажав кнопку передней двери, он должен напечатать: входная дверь высокая

Отпуская кнопку передней двери входная дверь низкая

Нажатие кнопки задней двери задняя дверь высокая

Отпуская кнопку задней двери задняя дверь низкая

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

Нажатие кнопки входной двери: задняя дверь низкая входная дверь высокая

Отпуская кнопку передней двери входная дверь низкая

Нажатие кнопки задней двери входная дверь? задняя дверь высокая

Отпуская кнопку задней двери входная дверь? задняя дверь низкая

Вот код. Я надеюсь, что кто-то может увидеть то, что я не могу:

#include <Arduino.h>

#define FRONT_DOOR_SWITCH A2
#define BACK_DOOR_SWITCH A3

volatile int frontDoorStatus = 0;
volatile int backDoorStatus = 0;

void frontDoorIsr()
{
    static unsigned long last_interrupt_time = 0;
    unsigned long interrupt_time = millis();
    // If interrupts come faster than 200ms, assume it's a bounce and ignore
    if (interrupt_time - last_interrupt_time > 200) {
        if (digitalRead(FRONT_DOOR_SWITCH) == HIGH) {
            frontDoorStatus = 1;
        }
        else if (digitalRead(FRONT_DOOR_SWITCH) == LOW) {
            frontDoorStatus = 2;
        }
        else {
            frontDoorStatus = -1;
        }
    }
    last_interrupt_time = interrupt_time;
}

void backDoorIsr()
{
    static unsigned long last_interrupt_time = 0;
    unsigned long interrupt_time = millis();
    // If interrupts come faster than 200ms, assume it's a bounce and ignore
    if (interrupt_time - last_interrupt_time > 200) {
        if (digitalRead(BACK_DOOR_SWITCH) == HIGH) {
            backDoorStatus = 1;
        }
        else if (digitalRead(BACK_DOOR_SWITCH) == LOW) {
            backDoorStatus = 2;
        }
        else {
            backDoorStatus = -1;
        }
    }
    last_interrupt_time = interrupt_time;
}

void setup() {
    pinMode(FRONT_DOOR_SWITCH, INPUT_PULLDOWN);
    pinMode(BACK_DOOR_SWITCH, INPUT_PULLDOWN);
        
    attachInterrupt(digitalPinToInterrupt(FRONT_DOOR_SWITCH), frontDoorIsr, CHANGE);
    attachInterrupt(digitalPinToInterrupt(BACK_DOOR_SWITCH), backDoorIsr, CHANGE);
}

void loop()
{
    switch (frontDoorStatus) {
        case 0:
            break;
        case -1:
            Serial.println("front door WTF?");
            frontDoorStatus = 0;
            break;
        case 1:
            Serial.println("front door high");
            frontDoorStatus = 0;
            break;
        case 2:
            Serial.println("front door low");
            frontDoorStatus = 0;
            break;
    }

    switch (backDoorStatus) {
        case 0:
            break;
        case -1:
            Serial.println("back door WTF?");
            backDoorStatus = 0;
            break;
        case 1:
            Serial.println("back door high");
            backDoorStatus = 0;
            break;
        case 2:
            Serial.println("back door low");
            backDoorStatus = 0;
            break;
    }
}

Плата Adafruit Feather Express M4 (SAMD51)

, 👍2

Обсуждение

До сих пор единственный способ, который я могу придумать, чтобы объяснить прерывания, вызываемые так, как вы описываете, зависит от того, что кода больше, чем вы показываете. Действительно ли это весь тестируемый скетч?, @timemage

Да, именно так я сначала и подумал. Я уменьшил его, чтобы посмотреть, не могло ли что-то еще быть причиной этого, но то, что я опубликовал, работает и срабатывает от обоих ISR., @Baxter Tidwell

К сожалению, у меня нет устройств SAMD51 вообще. И я не уверен, что хочу поставить свою дикую догадку в качестве "ответа". В качестве диагностического теста я попробовал использовать тот же код с "#define FRONT_DOOR_SWITCH 21 " и "#define BACK_DOOR_SWITCH 22 "и подключить ваши сигналы к тем контактам, помеченным на плате как" SCL " и "SDA". Если это улучшит ситуацию, я дам вам ответ, почему я думаю, что это может быть так., @timemage

К сожалению, никаких улучшений с SCL и SDA не произошло. Оба ISR все еще вызываются. Какова ваша теория?, @Baxter Tidwell

Не уверен, что то, о чем я думал, поднимается до уровня "теории". И то, что вы сделали, отчасти опровергает это. Вывода не имеют специальных прерываний. Они разделяют одно и то же прерывание с несколькими другими контактами. В частности, ваш вывод Arduino A3 разделяет прерывание с одной из встроенных линий микросхемы SPI. SDA и SCL, похоже, не делятся ни с чем другим, по крайней мере, на плате больше ничего не сломалось, так что никакой другой вывод не может вызвать их внешнюю процедуру прерывания. Так что, как я уже сказал, это не так., @timemage

Я использую SPI для подключения ESP32. Интересно, это вызывает помехи? Чтобы исключить плохую плату, я поменялся с совершенно новым пером M4 Express. Та же самая проблема. Теперь я отключу SPI и посмотрю, что будет., @Baxter Tidwell


1 ответ


1

Оказывается, в процессорах Cortex Mx прерывания работают не так, как старые 8- и 16-битные чипы в стиле Arduino.

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

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

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

#include <Arduino.h>

#define FRONT_DOOR_SWITCH A2
#define BACK_DOOR_SWITCH A3

volatile bool frontDoor = LOW;
volatile bool backDoor = LOW;

void isr()
{
    static unsigned long last_interrupt_time = 0;
    unsigned long interrupt_time = millis();
    // Если прерывания приходят быстрее 200 мс, предположим, что это отскок, и проигнорируйте
    if (interrupt_time - last_interrupt_time > 50)
    {
        int fds = digitalRead(FRONT_DOOR_SWITCH);
        int bds = digitalRead(BACK_DOOR_SWITCH);
        if (fds == HIGH) frontDoor = HIGH;
        if (fds == LOW) frontDoor = LOW;
        if (bds == HIGH) backDoor = HIGH;
        if (bds == LOW) backDoor = LOW;
        last_interrupt_time = interrupt_time;
    }
}

void setup() 
{
    Serial.begin(115200);
    while (!Serial);
    Serial.println("in setup...");
    pinMode(FRONT_DOOR_SWITCH, INPUT_PULLDOWN);
    pinMode(BACK_DOOR_SWITCH, INPUT_PULLDOWN);
        
    attachInterrupt(digitalPinToInterrupt(FRONT_DOOR_SWITCH), isr, CHANGE);
    attachInterrupt(digitalPinToInterrupt(BACK_DOOR_SWITCH), isr, CHANGE);
}

bool lastFrontDoor = LOW;
bool lastBackDoor = LOW;

void loop()
{
    if ((frontDoor == HIGH) && (lastFrontDoor == LOW))
    {
        Serial.print("fd+");
        lastFrontDoor = HIGH;
    }
    if ((frontDoor == LOW) && (lastFrontDoor == HIGH))
    {
        Serial.println("fd-");
        lastFrontDoor = LOW;
    }
    if ((backDoor == HIGH) && (lastBackDoor == LOW))
    {
        Serial.print("bd+");
        lastBackDoor = HIGH;
    }
    if ((backDoor == LOW) && (lastBackDoor == HIGH))
    {
        Serial.println("bd-");
        lastBackDoor = LOW;
    }
}
,