Контакт клавиатуры как прерывание (проводка или программирование)

У меня есть проект "телефон". Он состоит из:

  • Arduino DUE,
  • Клавиатура 4 x 4,
  • Защита данных SD,
  • обнаружение телефонной трубки (вкл./выкл.)
  • усилитель и динамик.

Вначале, после выбора телефонной трубки, я проигрываю файл .wav с интро о том, как выбрать язык. И этот .wav должен прерываться, когда пользователь нажимает 4-й ряд клавиатуры (кнопки A/B/C/D). Насколько я знаю, мне нужен только один контакт, в этой настройке - контакт 38 (столбец 4 контакт). Насколько я понимаю, нет правильного способа объявить входной контакт клавиатуры прерыванием по DUE. Или это?

Итак, мой вопрос - как подключить входной контакт клавиатуры в качестве прерывания? Я читал про проводку с диодом. Но в большинстве тредов нет никакой конкретики.

Может ли кто-нибудь подтолкнуть меня в правильном направлении? Здоровья!

Код:

#include <DAC.h>
#include <SD.h>
#include <SPI.h>
#include <Audio.h>
#include <Key.h>
#include <Keypad.h>

const byte ROW_NUM = 4;
const byte COLUMN_NUM = 4;

int code1 = 12345;  // Код, который я использовал, вы можете изменить
int code2 = 45678;  // Код, который я использовал, вы можете изменить
int code3 = 78900;  // Код, который я использовал, вы можете изменить

int tot, i1, i2, i3, i4, i5;
char c1, c2, c3, c4;

char hexaKeys[ROW_NUM][COLUMN_NUM] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

byte hookPinA = 23;  // ваш контакт прерывания
byte interruptPin = 22;  // ваш контакт прерывания

byte pin_rows[ROW_NUM] = {52, 50, 48, 46}; //подключаемся к распиновке ряда клавиатуры
byte pin_column[COLUMN_NUM] = {44, 42, 40, 38}; //подключаем к колонке распиновку клавиатуры

int keyCounter = 0;

Keypad myKeypad = Keypad(makeKeymap(hexaKeys), pin_rows, pin_column, ROW_NUM, COLUMN_NUM);

char keypresses[5];
char language;
char key;

int goIntoKeys = 0;

volatile uint8_t interruptState = 0;
volatile byte hookOffState = 0;


void setup() {
  // отладочный вывод на скорости 9600 бод
  Serial.begin(9600);
  SD.begin(10);
  analogWriteResolution(12);

// for( int i=0; i < ROW_NUM; ++i) pinMode(pin_rows[i], INPUT_PULLUP);
// for( int i=0; i < COLUMN_NUM; ++i) pinMode(pin_column[i], INPUT_PULLUP);

 pinMode(hookPinA, INPUT_PULLUP);
  
 attachInterrupt(digitalPinToInterrupt(hookPinA), hookOffToggle, CHANGE);

// EIFR |= бит(INTF0); // очистить флаг прерывания INT0

 attachInterrupt(digitalPinToInterrupt(interruptPin), interruptToggle, RISING); //проверить прерывание обычной кнопкой

 pinMode(38, INPUT_PULLUP);
 attachInterrupt(digitalPinToInterrupt(38), interruptToggle, RISING);

  DACC->DACC_CHDR = DACC_CHDR_CH1; //отключаем ЦАП1


}


int state = 0;
const int STATE_OFF = 0;
const int STATE_IDLE = 1;
const int STATE_LISTEN_KEY = 2;
const int STATE_PLAYER = 3;

bool isLanguageKey(char customKey) {
  return ('A' == customKey || 'B' == customKey || 'C' == customKey || 'D' == customKey);
}

void loop() {
  switch (state) {
    case STATE_OFF:
    // ожидание удаления телефонной трубки
      if (hookOffState == 1) {
      Serial.print("HOOK OFF ");
      Serial.println(hookOffState);
        interruptState = 0;
        state = STATE_IDLE;
               }
               else if (hookOffState == 0) {
      Serial.print("HOOK ON ");
      Serial.println(hookOffState);
               }
        break;
    case STATE_IDLE:
    //прослушивание языкового ключа. Невозможно набрать номер раньше
      Serial.print("interrupt IDLE ");
      Serial.println(interruptState);
               if (interruptState == 1) {
         buttonBeep();
        processLanguage();
        state = STATE_LISTEN_KEY;
               }
               else if (hookOffState == 0){
                state = STATE_OFF;
               }
               else {
      playSound("intro.wav");
        }
      break;
    case STATE_LISTEN_KEY:
    //прослушивание любого ключа
      Serial.print("interrupt LISTEN KEY ");
      Serial.println(interruptState);
      if (isKeyPressed()) {
        interruptState = 0;
        if (isLanguageKey(key)) {
          buttonBeep();
          processLanguage();
        }
        else {
      Serial.print("interruptStateF ");
      Serial.println(interruptState);
               buttonBeep();
          keypresses[keyCounter] = key;
          keyDial();
          keyCounter++;
          if (keyCounter > 4) {
            state = STATE_PLAYER;
            //при нажатии 5 цифр - пробуем открыть .wav файл
          }
        }
      }
               else if (hookOffState == 0){
                state = STATE_OFF;
               }
      break;
    case STATE_PLAYER:
        //Воспроизвести аудио и вернуться в состояние LISTEN
      calculateAndPlay();
      keyCounter = 0;
      interruptState = 0;
      state = STATE_LISTEN_KEY;
      break;
    default:
      break;
  }
}

void hookOffToggle(){
 if (digitalRead(hookPinA) == 1) {
  hookOffState = 1;
 }
  else  {
      hookOffState = 0;
  }
}

void interruptToggle() {
    interruptState = 1;
}

void kalba() {
  Serial.println("Kalba? Language? Valoda? Yazik?");
}

bool isKeyPressed() {
  key = myKeypad.getKey();
  return key != NO_KEY;
}

bool langKeyPressed() {
  key = myKeypad.getKey();
  return key != NO_KEY && isLanguageKey(key);
}

void processLanguage() { //нажимайте клавиши A/B/C/D - выберите 1 из 4 языков
  //переход в ключи = 1;
  keyCounter = 0;
  if (key == 'A') {
    Serial.println("   LT    ");
  }
  if (key == 'B') {
    Serial.println("   EN    ");
  }
  if (key == 'C') {
    Serial.println("   LV    ");
  }
  if (key == 'D') {
    Serial.println("   RU    ");
  }
  language = key;
}


void keyDial() {    // ЖК-дисплей отображает номер, набранный в 3 слотах.
  keypresses[keyCounter] = 0x00;//нулевой байт
  keypresses[keyCounter] = key;

  if (key != NO_KEY)   {
    Serial.println(keypresses[keyCounter]);
  }
}

void buttonBeep(){
      for (int j = 0; j < 200; j++) {
        for (int i = 0; i < 200; i++)
          analogWrite(DAC0, i);
        for (int i = 200; i >= 0; i--)
          analogWrite(DAC0, i);
      }
}

void backgroundHumm() {
      for (int j = 0; j < 256; j++) {
        for (int i = 0; i < 256; i++)
          analogWrite(DAC0, j);
          for (int j = 256; j >= 0; j--) {
            for (int i = 256; i >= 0; i--)
          analogWrite(DAC0, j);
      }
  }
}
 
void calculateAndPlay() {
  //если у нас есть 4+1 числа - продолжаем
  // нажатые клавиши сохраняются в символы, я конвертирую их в int, затем я сделал некоторое умножение, чтобы получить код как int xxxx
  i1 = (keypresses[0] - 48) * 10000;
  i2 = (keypresses[1] - 48) * 1000;
  i3 = (keypresses[2] - 48) * 100;
  i4 = (keypresses[3] - 48) * 10;
  i5 = (keypresses[4] - 48) * 1;  

  tot = i1 + i2 + i3 + i4 + i5;
  if (language == 'A') {
    if (tot == code1) //если код правильный, вы запускаете воспроизведение .wav
    {
      playSound("ledaiLT.wav");
    }
    else if (tot == code2)
    {
      playSound("zuvisLT.wav");
    }
    else if (tot == code3)
    {
      playSound("draugLT.wav");
    }
    else // если код неверный, вы получите звуковой сигнал и звуковое сообщение
  {
      playSound("blogasLT.wav");
    }
  }
  if (language == 'B') {
    if (tot == code1) //если код правильный, вы запускаете воспроизведение .wav
    {
      playSound("ledaiEN.wav");
    }
    else if (tot == code2)
    {
      playSound("zuvisEN.wav");
    }
    else if (tot == code3)
    {
      playSound("draugEN.wav");
    }
    else // если код неверный, вы получите звуковой сигнал и звуковое сообщение
    {
      playSound("blogasEN.wav");
    }
  }
  if (language == 'C') {
    if (tot == code1) //если код правильный, вы запускаете воспроизведение .wav
    {
      playSound("ledaiLV.wav");
    }
    else if (tot == code2)
    {
      playSound("zuvisLV.wav");
    }
    else if (tot == code3)
    {
      playSound("draugLV.wav");
    }
    else // если код неверный, вы получите звуковой сигнал и звуковое сообщение
    {
      playSound("blogasLV.wav");
    }
  }
  if (language == 'D') {
    if (tot == code1) //если код правильный, вы запускаете воспроизведение .wav
    {
      playSound("ledaiRU.wav");
    }
    else if (tot == code2)
    {
      playSound("zuvisRU.wav");
    }
    else if (tot == code3)
    {
      playSound("draugRU.wav");
    }
    else // если код неверный, вы получите звуковой сигнал и звуковое сообщение
    {
      playSound("blogasRU.wav");
    }
  }
}

// Воспроизведение назначенного имени файла
void playSound(const char* cName) {  
  File myFile = SD.open(cName);
  const int S = 1024; // Количество выборок для чтения в блоке
  short buffer[S];
  // пока файл не будет закончен
  Serial.println("playing ");
  Serial.println(cName);


  Audio.begin(44100, 200);
  delay(200);
  while (myFile.available()) {
    myFile.read(buffer, sizeof(buffer));
    // Подготовить образцы
    int volume = 512;
    //максимальный объем 1024
    Audio.prepare(buffer, S, volume);
    Audio.write(buffer, S);
   if ( interruptState ) break;
     else if ( hookOffState == 0 ) break;
  }
  Audio.end();
  myFile.close();
  delay(50);
  DACC->DACC_CHDR = DACC_CHDR_CH1; //отключаем ЦАП1
}

Схемы (если это можно так назвать) ниже:

ИЗМЕНИТЬ: Решение @Damago работает! Я просто хотел поделиться здесь решением для будущих поисков. Что вам нужно сделать, так это заставить 4-й столбец клавиатуры 4x4 правильно работать как прерывание: Установите все выводы строк в качестве выходных, а выводы столбцов в качестве входных с помощью функции подтягивания.

for( int i=0; i< ROW_NUM; ++i) pinMode(pin_rows[i], OUTPUT);
for( int i=0; i< COLUMN_NUM; ++i) pinMode(pin_column[i], INPUT_PULLUP);

Установите вывод строки на низкий уровень:

for( int i=0; i< ROW_NUM; ++i) digitalWrite(pin_rows[i], LOW);

Прикрепите контакт 4-го столбца как прерывание:

attachInterrupt(digitalPinToInterrupt(pin_column[3]), interruptToggle, FALLING);

И, конечно же, исправьте синтаксис в целом (он был немного запутанным :) )

Полный скетч ниже:

#include <DAC.h>
#include <SD.h>
#include <SPI.h>
#include <Audio.h>
#include <Keypad.h>

const byte ROW_NUM = 4;
const byte COLUMN_NUM = 4;

int code1 = 12345;  // Код, который я использовал, вы можете изменить
int code2 = 45678;  // Код, который я использовал, вы можете изменить
int code3 = 78900;  // Код, который я использовал, вы можете изменить

int tot, i1, i2, i3, i4, i5;
char c1, c2, c3, c4;

char hexaKeys[ROW_NUM][COLUMN_NUM] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

byte hookPinA = 23;  // ваш контакт прерывания
byte pin_rows[ROW_NUM] = {52, 50, 48, 46}; //подключаемся к распиновке ряда клавиатуры
byte pin_column[COLUMN_NUM] = {44, 42, 40, 38}; //подключаем к колонке распиновку клавиатуры

Keypad myKeypad = Keypad(makeKeymap(hexaKeys), pin_rows, pin_column, ROW_NUM, COLUMN_NUM);

char keypresses[5];
char language;
char key;

volatile uint8_t interruptState = 0;
volatile byte hookOffState = 0;
int keyCounter = 0;

void setup() {
  // отладочный вывод на скорости 9600 бод
  Serial.begin(9600);
  SD.begin(10);
  analogWriteResolution(12);

for( int i=0; i< ROW_NUM; ++i) pinMode(pin_rows[i], OUTPUT);
for( int i=0; i< COLUMN_NUM; ++i) pinMode(pin_column[i], INPUT_PULLUP);

for( int i=0; i< ROW_NUM; ++i) digitalWrite(pin_rows[i], LOW); 
 pinMode(hookPinA, INPUT_PULLUP);
 attachInterrupt(digitalPinToInterrupt(hookPinA), hookOffToggle, CHANGE);
 attachInterrupt(digitalPinToInterrupt(pin_column[3]), interruptToggle, FALLING);

  DACC->DACC_CHDR = DACC_CHDR_CH1; //отключаем ЦАП1
}


int state = 0;
const int STATE_OFF = 0;
const int STATE_IDLE = 1;
const int STATE_LISTEN_KEY = 2;
const int STATE_PLAYER = 3;

bool isLanguageKey(char customKey) {
  return ('A' == customKey || 'B' == customKey || 'C' == customKey || 'D' == customKey);
}

void loop() {
  switch (state) {
    case STATE_OFF:
    // ожидание удаления телефонной трубки
      if (hookOffState == 1) {
        interruptState = 0;
        state = STATE_IDLE;
                      }
               else if (hookOffState == 0) {
               }
        break;
    case STATE_IDLE:
    //прослушивание языкового ключа. Невозможно набрать номер раньше
               if (interruptState == 1) {
         isKeyPressed(); // получаем ввод ключа
         buttonBeep();
        processLanguage();
        state = STATE_LISTEN_KEY;
               }
               else if (hookOffState == 0){
                state = STATE_OFF;
               }
               else {
      playSound("intro.wav");
        }
      break;
    case STATE_LISTEN_KEY:
    //прослушивание любого ключа
      if (isKeyPressed()) {
        interruptState = 0;
        if (isLanguageKey(key)) {
          buttonBeep();
          processLanguage();
        }
        else  {
          buttonBeep();
          keypresses[keyCounter] = key;
          keyCounter++;
          if (keyCounter > 4) {
            state = STATE_PLAYER;
            //при нажатии 5 цифр - пробуем открыть .wav файл
          }
        }
      }
               else if (hookOffState == 0){
                state = STATE_OFF;
               }
      break;
    case STATE_PLAYER:
        //Воспроизвести аудио и вернуться в состояние LISTEN
      calculateAndPlay();
      keyCounter = 0;
      interruptState = 0;
      state = STATE_LISTEN_KEY;
      break;
    default:
      break;
  }
}

void hookOffToggle() {     //обнаружение удаляемой телефонной трубки
hookOffState = digitalRead(hookPinA);
}

void interruptToggle() {
    interruptState = 1;
}

bool isKeyPressed() {
  key = myKeypad.getKey();
  return key != NO_KEY;
}

bool langKeyPressed() {
  key = myKeypad.getKey();
  return key != NO_KEY && isLanguageKey(key);
}

void processLanguage() { //нажимайте клавиши A/B/C/D - выберите 1 из 4 языков
  keyCounter = 0;
  if (key == 'A') {
    Serial.println("   LT    ");
  }
  if (key == 'B') {
    Serial.println("   EN    ");
  }
  if (key == 'C') {
    Serial.println("   LV    ");
  }
  if (key == 'D') {
    Serial.println("   RU    ");
  }
  language = key;
}


void buttonBeep(){
      for (int j = 0; j < 200; j++) {
        for (int i = 0; i < 200; i++)
          analogWrite(DAC0, i);
        for (int i = 200; i >= 0; i--)
          analogWrite(DAC0, i);
      }
}

void backgroundHumm() {
      for (int j = 0; j < 256; j++) {
        for (int i = 0; i < 256; i++)
          analogWrite(DAC0, j);
          for (int j = 256; j >= 0; j--) {
            for (int i = 256; i >= 0; i--)
          analogWrite(DAC0, j);
      }
  }
}
 
void calculateAndPlay() {
  //если у нас есть 4+1 числа - продолжаем
  // нажатые клавиши сохраняются в символы, я конвертирую их в int, затем я сделал некоторое умножение, чтобы получить код как int xxxx
  i1 = (keypresses[0] - 48) * 10000;
  i2 = (keypresses[1] - 48) * 1000;
  i3 = (keypresses[2] - 48) * 100;
  i4 = (keypresses[3] - 48) * 10;
  i5 = (keypresses[4] - 48) * 1;  

  tot = i1 + i2 + i3 + i4 + i5;
  if (language == 'A') {
    if (tot == code1) //если код правильный, вы запускаете воспроизведение .wav
    {
      playSound("ledaiLT.wav");
    }
    else if (tot == code2)
    {
      playSound("zuvisLT.wav");
    }
    else if (tot == code3)
    {
      playSound("draugLT.wav");
    }
    else // если код неверный, вы получите звуковой сигнал и звуковое сообщение
  {
      playSound("blogasLT.wav");
    }
  }
  if (language == 'B') {
    if (tot == code1) //если код правильный, вы запускаете воспроизведение .wav
    {
      playSound("ledaiEN.wav");
    }
    else if (tot == code2)
    {
      playSound("zuvisEN.wav");
    }
    else if (tot == code3)
    {
      playSound("draugEN.wav");
    }
    else // если код неверный, вы получите звуковой сигнал и звуковое сообщение
    {
      playSound("blogasEN.wav");
    }
  }
  if (language == 'C') {
    if (tot == code1) //если код правильный, вы запускаете воспроизведение .wav
    {
      playSound("ledaiLV.wav");
    }
    else if (tot == code2)
    {
      playSound("zuvisLV.wav");
    }
    else if (tot == code3)
    {
      playSound("draugLV.wav");
    }
    else // если код неверный, вы получите звуковой сигнал и звуковое сообщение
    {
      playSound("blogasLV.wav");
    }
  }
  if (language == 'D') {
    if (tot == code1) //если код правильный, вы запускаете воспроизведение .wav
    {
      playSound("ledaiRU.wav");
    }
    else if (tot == code2)
    {
      playSound("zuvisRU.wav");
    }
    else if (tot == code3)
    {
      playSound("draugRU.wav");
    }
    else // если код неверный, вы получите звуковой сигнал и звуковое сообщение
    {
      playSound("blogasRU.wav");
    }
  }
}

// Воспроизведение назначенного имени файла
void playSound(const char* cName) {  
  File myFile = SD.open(cName);
  const int S = 1024; // Количество выборок для чтения в блоке
  short buffer[S];
  // пока файл не будет закончен
  Serial.println("playing ");
  Serial.println(cName);


  Audio.begin(44100, 200);
  delay(200);
  while (myFile.available()) {
    myFile.read(buffer, sizeof(buffer));
    // Подготовить образцы
    int volume = 512;
    //максимальный объем 1024
    Audio.prepare(buffer, S, volume);
    Audio.write(buffer, S);
   if ( interruptState ) break;
   else if ( hookOffState == 0 ) break;
  }
  Audio.end();
  myFile.close();
  delay(50);
  DACC->DACC_CHDR = DACC_CHDR_CH1; //отключаем ЦАП1
}

, 👍2

Обсуждение

почему вам нужно использовать прерывание?, @jsotola

Это кнопка с резистором PULLDOWN, @SBF

Кнопка, показанная в правом верхнем углу чертежа, предназначена только для целей тестирования — ее не должно быть в окончательном проекте., @GoldenMoose

идеальным было бы воспроизведение звука без блокировки и проверка кнопки loop(), @Juraj


2 ответа


Лучший ответ:

3

Здесь есть небольшая проблема. Вы не можете просто использовать один диод. Напряжение присутствует на контактах клавиатуры только во время оценки функции getKey ().

Клавиатура работает таким образом: когда вы вызываете myKeypad.getKey(); тогда arduino делает следующее:

  • изменяет первый столбец с входного (высокий импеданс) на выходной
  • выводит напряжение 0В на первый столбец,
  • сканирует все строки, если они равны 0V (обычно они подтягиваются - вы знаете, что такое подтягивание).
  • если нажата какая-либо клавиша в этом столбце, то напряжение столбца 0В тянет вниз соответствующее напряжение строки. То есть между строкой и столбцом существует связь.

Вот точный код:

    pin_mode(columnPins[c],OUTPUT);
    pin_write(columnPins[c], LOW);  // Начало импульсного вывода столбца.
    for (byte r=0; r<sizeKpd.rows; r++) {
        bitWrite(bitMap[r], c, !pin_read(rowPins[r]));  // keypress is active low so invert to high.
    }
    // Установите PIN на вход с высоким импедансом. Эффективно завершает колонный импульс.
    pin_write(columnPins[c],HIGH);
    pin_mode(columnPins[c],INPUT);

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

Я бы сделал это по-другому: я бы полностью пропустил прерывание здесь. Вы проверяете наличие флага, установленного внутри прерывания:

if ( interruptState ) break;

Я бы непосредственно проверил здесь напряжение на определенном выводе. Вы должны (просто):

  • установите PIN строки (46, насколько я понимаю из вашего кода) для вывода и установите его на низкий уровень,
  • установите контакты столбца (все они) в положение input-pullup - как я понимаю {44, 42, 40, 38}
  • проверьте, не является ли какой - либо из этих штифтов "НИЗКИМ", если да- то сломанным;

Насколько я понимаю библиотеку, вам даже не нужно возвращать какие-либо номера пин обратно, библиотека правильно инициализирует выводы на каждом getKey ().

Если по какой-либо причине описанная выше операция занимает слишком много времени, вы можете сократить ее, используя прямое манипулирование портом, как в https://forum.arduino.cc/t/arduino-due-how-to-direrct-port-access/251656 но обратите внимание, что на Arduino Due это не так просто, как на AVR.

,

Спасибо! На мгновение мне показалось, что я полностью решил эту проблему! Но не совсем еще.. Я сделал описанные изменения, за исключением того, что мне нужно, чтобы вывод 38 был выходным и НИЗКИМ. И выводы строк как input_pullup. Прерывание работает! К сожалению, клавиатура больше не получает входы для нажатых клавиш таким образом., @GoldenMoose

"Насколько я понимаю библиотеку, вам даже не нужно возвращать какие-либо выводы обратно, библиотека правильно инициализирует выводы на каждом getKey ()". Наверное, мне нужно вернуть эти контакты обратно? Но тогда - как получать прерывания после возврата?, @GoldenMoose

Я думаю, что ваше решение наиболее близко к решению моей проблемы. Теперь я могу прервать его .wav-файл, но ввод не будет регистрировать, какую языковую клавишу я нажал... Поэтому в основном мне нужно нажать дважды, чтобы достичь цели. Отредактированный код в главном вопросе:), @GoldenMoose


1

Если вы хотите использовать прерывания, требуется отдельный вывод для обнаружения падающего края в столбце 4 (вывод 38). В следующем примере я использовал вывод 28 для прерывания столбца 4. Они могут быть соединены вместе через резистор 1K для защиты от случайного вывода низкого напряжения контактом 28, в то время как другой выводит высокое.

Вывод 38 опускается во время вызова функции myKeypad.getKey (), тем самым вызывая ISR.

Рабочие копии создаются из переменных, которые обновляются в ISR. Это потребует внесения изменений в конечный автомат для обработки column4Pressed, полученной из ISR.

const byte hookPinA = 23;  // ваш вывод прерывания
const byte interruptPin = 22;  // ваш вывод прерывания

const byte column4InterruptPin = 28;  // Вывод прерывания столбца 4 подключен к столбцу 4pin через проводную связь. Можно было бы добавить резистор серии 1K для защиты от короткого замыкания на землю, если бы он был низким на выходе, когда вывод 38 высокий.
const byte column4Pin = 38;  // Вывод столбца 4 подключен к столбцу 4interruptpin через проводную связь.

// обновленные ISR.
volatile byte isr_hookOffState = 0;
volatile byte isr_interruptState = 0;
volatile bool isr_column4Pressed = false;

// Рабочие копии данных ISR.
byte hookOffState = 0;
byte interruptState = 0;
bool column4Pressed = false;

// Используйте один байт для переменной состояния и инициализируйте ее в OFF.
enum class State : byte
{
    OFF = 0,
    IDLE = 1,
    LISTEN_KEY = 2,
    PLAYER = 3
} state = State::OFF;

void setup()
{
    pinMode(hookPinA, INPUT_PULLUP);
    attachInterrupt(digitalPinToInterrupt(hookPinA), hookOffToggle, CHANGE);

    pinMode(interruptPin, INPUT);
    attachInterrupt(digitalPinToInterrupt(interruptPin), interruptToggle, RISING); // тестовое прерывание с помощью обычной кнопки

    pinMode(column4InterruptPin, INPUT_PULLUP);
    attachInterrupt(digitalPinToInterrupt(column4InterruptPin), interruptColumn4, FALLING);  // Срабатывает при вытягивании столбца 4 low, например digitalWrite(column4Pin, LOW) или в myKeypad.getKey().
}

void loop()
{
    // Получить свежую копию данных ISR и атомарно сбросить флаг состояния прерывания
    // путем временного отключения прерываний.
    cli();
    hookOffState = isr_hookOffState;
    interruptState = isr_interruptState;
    column4Pressed = isr_column4Pressed;
    isr_interruptState = 0;  // Сброс для обнаружения следующего прерывания.
    isr_column4Pressed = false; // Сброс для обнаружения следующего прерывания.
    sei();

    // Альтернативный метод проверки того, нажата ли какая-либо кнопка в столбце 4.
    // Вот как это сделать без использования прерываний.
    /*
    pinMode(column4Pin, OUTPUT);
    digitalWrite(column4Pin, LOW);
    column4Pressed = !digitalRead(52) || !digitalRead(50) || !digitalRead(48) || !digitalRead(46);
    digitalWrite(column4Pin, HIGH);
    pinMode(column4Pin, INPUT);
    */

    // Обработать рабочие копии данных ISR.
    // Вам нужно будет изменить логику конечного автомата для обработки column4Pressed, полученной из ISR.

    switch (state)
    {
    case State::OFF:
        break;
    case State::IDLE:
        break;
    case State::LISTEN_KEY:
        break;
    case State::PLAYER:
        break;
    }
}

void hookOffToggle()
{
    isr_hookOffState = digitalRead(hookPinA);
}

void interruptToggle()
{
    isr_interruptState = 1;
}

void interruptColumn4()
{
    // Проверьте, нажата ли какая-либо кнопка в столбце 4.
    isr_column4Pressed = !digitalRead(52) || !digitalRead(50) || !digitalRead(48) || !digitalRead(46);
}

Вот моделирование Wokwi, которое использует Arduino Mega и pin 2 для прерывания столбца 4. Обратите внимание, что между контактами 2 и 38 нет защитного резистора. Симулятор Wokwi создаст файл дампа изменения значения (VCD) для просмотра в вашем любимом анализаторе трассировки:

Screenshot of Wokwi simulation of keypad interrupt

В качестве альтернативы, если вы не хотите использовать прерывания, вы можете опустить столбец 4 (вывод 38) и проверить выводы строки на наличие логического минимума. См.Прокомментированный код в loop ().

,

Что такое cli(); и sei();? Они не объявляются в этой области., @GoldenMoose

Они очищают и устанавливают флаг глобального прерывания для отключения и включения прерываний. Попробуйте вместо этого noInterrupts() и interrupts (). (перебивает.) (https://www.arduino.cc/reference/en/language/functions/interrupts/interrupts/), @tim

Спасибо, я реализовал все изменения, к сожалению, не повезло - ничего не получается через isr_column4Pressed. Как я подключил 38 и 28 контактов: https://ibb.co/RP65rzQ, @GoldenMoose

@GoldenMoose, какое значение резистора вы используете для подключения контакта 38 к контакту 28? Из [вашего изображения](https://ibb.co/RP65rzQ) это 560K, что слишком велико. Диапазон внутренних подтягивающих резисторов ввода-вывода составляет от 20К до 50К. Защитный резистор должен быть достаточно большим, чтобы ограничить ток менее 20 мА, если один вывод случайно высок, а другой низок. Он также должен быть достаточно низким, чтобы упасть небольшое напряжение по сравнению с напряжением на внутреннем подтягивающем резисторе, которое будет распознано как логический минимум, когда вывод 38 вытянут низко, то есть делитель напряжения., @tim

Я использовал 3.6 k reistor. Я также пробовал это с разными резисторами/без резистора. Результат тот же..., @GoldenMoose

Я вижу, вы отредактировали свой пост. Но в этом примере вы также используете ЗАЗЕМЛЯЮЩИЙ штифт для Column4pin. Нужно ли мне это?, @GoldenMoose