Внешнее прерывание кнопки (цифровой вывод 3) Arduino не работает

Я пытаюсь выполнить прерывание внешней кнопки с помощью Arduino и буквенно-цифрового ЖК-дисплея 16*2. То, что я пытаюсь сделать, - это когда я нажимаю кнопку, экран должен переключаться между различными вариантами. Когда я нажал кнопку один раз, первая опция отображается на ЖК-дисплее, но при нажатии кнопки во второй раз экран остается прежним. Но код вводит другие условия, если, как я проверил с последовательным выходом, но он быстро переключается с нажатия 2 - 8 или около того за одно нажатие, а затем, когда я нажал снова, он переключается на 9, а при повторном нажатии переключается на 10. В чем может быть проблема? Код -

#include <LiquidCrystal.h>

const int rs = 12, en = 13, d4 = 8, d5 = 9, d6 = 10, d7 = 11;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

#define BUTTON              

unsigned long int timer = 0;
uint8_t buttonState = 0;
uint8_t buttonPressNo = 0;  // stores number of times button is pressed
uint8_t enter_flag = 1;

void setup()
{
    Serial.begin(9600);
    pinMode(BUTTON, INPUT);
    attachInterrupt(digitalPinToInterrupt(BUTTON), button, RISING); // use interrupt 1 (pin 3) and run function
                                      // wakeUpNow when pin 3 gets HIGH
    lcd.begin(16, 2);
    Serial.println(F("Setup done"));
}

void loop()
{
    buttonPressNo += buttonState;
    Serial.print(F("buttonState: "));Serial.println(buttonState);
    Serial.print(F("buttonPressNo: "));Serial.println(buttonPressNo);

    if(buttonPressNo == 1)
    {
        Serial.println(F("F1"));
        if(enter_flag == 1)
        {
            lcd.clear();
            lcd.setCursor(3, 0);
            lcd.print("T=");

            lcd.setCursor(3, 1);
            lcd.print("Calib");
            buttonState = 0;
            Serial.println(F("F1"));
            enter_flag = 0;
        }
        
        if(2-1 == 2)
        {
            Serial.println("1");
        }
        else
        {
            Serial.println("0");
        }
        
        if(millis() - timer >= 10000)
        {
            reset();
        }
            
    }
    
    else if(buttonPressNo == 2)
    {
        Serial.println(F("F2"));
        if(enter_flag == 1)
        {
            lcd.clear();
            lcd.setCursor(3, 0);
            lcd.print("T=");

            lcd.setCursor(3, 1);
            lcd.print("scale");
            buttonState = 0;
            Serial.println(F("F2"));
            enter_flag = 0;
        }

        if(2-1 == 2)
        {
            Serial.println("1");
        }
        else
        {
            Serial.println("0");
        }

        if(millis() - timer >= 10000)
        {
            reset();
        }
    }

    else if(buttonPressNo == 3)
    {
        Serial.println(F("F3"));
        if(enter_flag == 1)
        {
            lcd.clear();
            lcd.setCursor(3, 0);
            lcd.print("T=");

            lcd.setCursor(3, 1);
            lcd.print("memory");
            buttonState = 0;
            Serial.println(F("F3"));
            enter_flag = 0;
        }

        if(2-1 == 2)
        {
            Serial.println("1");
        }
        else
        {
            Serial.println("0");
        }

        if(millis() - timer >= 10000)
        {
            reset();
        }
    }

    else if(buttonPressNo == 4)
    {
        Serial.println(F("F4"));
        if(enter_flag == 1)
        {
            lcd.clear();
            lcd.setCursor(3, 0);
            lcd.print("T=");

            lcd.setCursor(3, 1);
            lcd.print("surface");
            buttonState = 0;
            Serial.println(F("F4"));
            enter_flag = 0;
        }

        if(2-1 == 2)
        {
            Serial.println("1");
        }
        else
        {
            Serial.println("0");
        }

        if(millis() - timer >= 10000)
        {
            reset();
        }
    }

    else if(buttonPressNo == 5)
    {
        Serial.println(F("F5"));
        if(enter_flag == 1)
        {
            lcd.clear();
            lcd.setCursor(3, 0);
            lcd.print("T=");

            lcd.setCursor(3, 1);
            lcd.print("buzzer");
            buttonState = 0;
            Serial.println(F("F5"));
            enter_flag = 0;
        }

        if(2-1 == 2)
        {
            Serial.println("1");
        }
        else
        {
            Serial.println("0");
        }

        if(millis() - timer >= 10000)
        {
            reset();
        }
    }

    else
    {
        //delay(1000);
        buttonState = 0;
        Serial.println(F("no function"));
        if(millis() - timer >= 10000)
        {
            reset();
        }
    }
}

void reset()
{
    buttonState = 0;
    buttonPressNo = 0;
    enter_flag = 1;
    Serial.println(F("reset done"));
    Serial.flush();
}

// debouncing
void button()
{
    buttonState = 0;
    timer = 0;

    while(1)
    {
        timer++;
        if(timer > 100)
        {
            buttonState++;
            timer = millis();
            break;
        }
    }
}

, 👍0

Обсуждение

у вас есть понижающий резистор на кнопке? что такое таймер? count или milliis()?, @Juraj

ISR никогда не должен иметь блокирующий цикл while. Прерывание на самом деле не подходит для чего-то столь медленного, как нажатие кнопки. Гораздо лучше было бы просто прочитать кнопку в цикле и сохранить код цикла неблокирующим., @Delta_G

Вы ведь знаете, что миллис не считается во время ИСР, верно? Когда ваш код входит в прерывание, он отключает все остальные прерывания. Так что там многое перестает работать. Вот почему прерывания не являются хорошим пластырем для блокировки кода., @Delta_G

`таймер " - это только счетчик. и у меня есть понижающий резистор, подключенный к кнопке. Как я уже сказал, прерывание работает в первый раз, а затем не работает, и я подозреваю, что цикл while во всех случаях if может вызвать некоторую неисправность, @Robot

Итак, вместо прерывания я должен использовать вложенный цикл if?, @Robot

Вместо прерывания вы должны просто прочитать кнопку в функции цикла. Похоже, что в вашем цикле нет кода блокировки, поэтому, если вы не можете нажать и отпустить кнопку за пару микросекунд (для сравнения, моргание глазом занимает около 200 000 микросекунд), у вас нет шансов пропустить нажатие., @Delta_G

Используя вложенный цикл if, я смог реализовать многократное нажатие кнопки и переключение между настройками нажатием кнопки. Но иногда нажатие кнопки вообще не работает. Я должен оказать дополнительное давление на кнопку или нажать кнопку несколько раз, чтобы зарегистрировать одно нажатие кнопки. И иногда он получает 2 или более нажатий кнопок, когда я просто нажал кнопку один раз. Есть идеи?, @Robot


2 ответа


0

Это не ответ, но он делает вашу программу намного меньше/удобнее в обслуживании.

  1. Сначала удалите 2-1 == 2 оператора if, которые всегда являются ложными (вероятно, это было для тестирования).
  2. Переместите все дублированное содержимое в отдельную функцию (используя параметры для переменных элементов).
  3. Используйте оператор switch вместо if/else.
  4. Используйте последовательные имена для переменных (изменен enter_flag на enterFlag);

Код:

void loop()
{
    buttonPressNo += buttonState;
    Serial.print(F("buttonState: "));Serial.println(buttonState);
    Serial.print(F("buttonPressNo: "));Serial.println(buttonPressNo);

    switch (buttonPressNo)
    {
    case 1:
        processMenu(F("F1"), F("Calib"));
        break;

    case 2:
        processMenu(F("F2", F("Scale"));
        break;

    case 3:
        processMenu(F("F3", F("Memory"));
        break;

    case 4:
        processMenu(F("F4"), F("Surface"));
        break;

    case 5:
        processMenu(F("F5"), F("Buzzer"));
        break;
   
    default:
    {
        //delay(1000);
        buttonState = 0;
        Serial.println(F("no function"));
        if(millis() - timer >= 10000)  // 2 mins timeout
        {
            reset();
        }
    }
 }


 void processMenu(char* button, char* title)
 {
    Serial.println(button);
    if(enterFlag == 1)
    {
        lcd.clear();
        lcd.setCursor(3, 0);
        lcd.print("T=");

        lcd.setCursor(3, 1);
        lcd.print(title);
        buttonState = 0;
        Serial.println(button);
        enterFlag = 0;
    }

    if(millis() - timer >= 10000)  // 2 mins timeout
    {
        reset();
    }
 }
,

0

Вы определили вывод кнопки как

#define BUTTON

Чего - то не хватает.




Просто заметка о написании кода.

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

Эти два фрагмента кода делают одно и то же.

if(buttonPressNo == 1) {
    if(enter_flag == 1) {
        Serial.println(F("F1"));
        enter_flag = 0;
    }
}

else if(buttonPressNo == 2) {
    if(enter_flag == 1) {
        Serial.println(F("F2"));
        enter_flag = 0;
    }
}

else if(buttonPressNo == 3) {
    if(enter_flag == 1) {
        Serial.println(F("F3"));
        enter_flag = 0;
    }
}

...

if(enter_flag == 1) {          //  это повторяется во всех блоках "если" 

    if(buttonPressNo == 1) {
        Serial.println(F("F1"));
    }
    
    else if(buttonPressNo == 2) {
        Serial.println(F("F2"));
    }
   
    else if(buttonPressNo == 3) {
        Serial.println(F("F3"));
    }

    enter_flag = 0;            // это повторяется во всех блоках "если"
}
,

Я действительно удивлен, что это компилируется тогда. Потому что при отсутствии половины define вызов pinMode становится pinMode( ,INPUT), и это должно привести к ошибке компиляции., @Delta_G