Почему мои прерывания mcp23s17 больше не работают?
Я использую teensy 4.0 с двумя расширителями gpio mcp23s17, где к первому подключено 16 кнопок, а к второму — 4 кнопки и шесть поворотных энкодеров. Для запуска всего я использую библиотеку majenkos mcp23s17. Недавно я провел рефакторинг своего кода и вдруг не могу настроить mcp для корректной работы прерываний.
Я попытался сузить проблему, закомментировав код, и оказалось, что как только я вызываю gpioExpander.enableInterrupt(pin, CHANGE);
в gpioSetup()
возникает проблема. В частности, loop()
работает нормально, но как только я нажимаю кнопку, тинси зависает. Кроме того, чтобы восстановить тинси, мне нужно загрузить код с вызовом gpioExpander.enableInterrupt(pin, CHANGE);
, закомментированным, чтобы тинси снова заработала.
Я смотрел на код вслепую и подозреваю, что упускаю что-то очевидное. Мы ценим вашу помощь.
//main.cpp
#include <Arduino.h>
#include "GPIO.h"
#include "MCP23S17.h"
extern MCP23S17 gpioExpander1;
extern MCP23S17 gpioExpander2;
void setup() {
Serial.begin(115200);
gpioSetup();
}
void loop() {
GPIOtest(gpioExpander1, gpioExpander2);
}
//GPIO.h
#ifndef GPIO_H
#define GPIO_H
#include <Arduino.h>
#include "MCP23S17.h"
constexpr uint8_t address = 0;
constexpr uint8_t interruptPin1 = 8;
constexpr uint8_t interruptPin2 = 22;
constexpr uint8_t chipSelectPin1 = 7;
constexpr uint8_t chipSelectPin2 = 21;
void setGPIO1Pins(MCP23S17 &gpioExpander);
void setGPIO2Pins(MCP23S17 &gpioExpander);
void gpioSetup();
void GPIO1interrupt();
void GPIO2interrupt();
void GPIOtest(MCP23S17 &gpioExpander1, MCP23S17 &gpioExpander2);
#endif
//GPIO.cpp
#include "GPIO.h"
#include <Arduino.h>
#include <MCP23S17.h>
volatile bool interruptFlag1 = false;
volatile bool interruptFlag2 = false;
MCP23S17 gpioExpander1(&SPI, chipSelectPin1, address);
MCP23S17 gpioExpander2(&SPI, chipSelectPin2, address);
// Установить все выводы расширителя портов в желаемый режим INPUT_PULLUP
// Установить прерывания всех входных контактов расширителя портов
void setGPIOPins(MCP23S17& gpioExpander) {
for (uint8_t pin = 0; pin <= 15; ++pin) {
gpioExpander.pinMode(pin, INPUT_PULLUP);
gpioExpander.enableInterrupt(pin, CHANGE);
}
// установка конфигурации прерывания расширителя портов
gpioExpander.setMirror(true);
gpioExpander.setInterruptOD(false);
gpioExpander.setInterruptLevel(LOW);
}
void gpioSetup() {
gpioExpander1.begin();
gpioExpander2.begin();
digitalWrite(chipSelectPin1, HIGH);
digitalWrite(chipSelectPin2, HIGH);
pinMode(interruptPin1, INPUT_PULLUP);
pinMode(interruptPin2, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(interruptPin1), GPIO1interrupt, LOW);
attachInterrupt(digitalPinToInterrupt(interruptPin2), GPIO2interrupt, LOW);
setGPIOPins(gpioExpander1);
setGPIOPins(gpioExpander2);
// регистры GPIO считываются для очистки
gpioExpander1.getInterruptValue();
gpioExpander2.getInterruptValue();
}
void GPIO1interrupt() {
interruptFlag1 = true;
}
void GPIO2interrupt() {
interruptFlag2 = true;
}
void GPIOtest(MCP23S17& gpioExpander1, MCP23S17& gpioExpander2) {
if (interruptFlag1 == true) {
Serial.println("interrupt 1 = true");
interruptFlag1 = false;
}
if (interruptFlag2 == true) {
Serial.println("interrupt 2 = true");
interruptFlag2 = false;
}
}
@Erik, 👍0
1 ответ
Лучший ответ:
Короткая версия, попробуйте:
attachInterrupt(digitalPinToInterrupt(interruptPin1), GPIO1interrupt, FALLING);
attachInterrupt(digitalPinToInterrupt(interruptPin2), GPIO2interrupt, FALLING);
FALLING
(вместо LOW
) является активный ингредиент. Я предполагаю, что FALLING
поддерживается. Если нет, вы можете попробовать синтезировать это с помощью CHANGE
и прочитать порт, чтобы увидеть, является ли он в настоящее время LOW
.
Затем вызовите .getInterruptValue();
, чтобы сбросить условие прерывания MCP23S17, где вы сбрасываете глобальные изменчивые флаги:
void GPIOtest(MCP23S17& gpioExpander1, MCP23S17& gpioExpander2) {
if (interruptFlag1 == true) {
Serial.println("interrupt 1 = true");
interruptFlag1 = false;
gpioExpander1.getInterruptValue(); // <------
}
if (interruptFlag2 == true) {
Serial.println("interrupt 2 = true");
interruptFlag2 = false;
gpioExpander2.getInterruptValue(); // <------
}
}
Полная версия:
Это предположение, так как я не могу проверить. Но это, возможно, неплохое предположение:
Вы настроили устройство MCP23S17 на установку активного низкого уровня при/во время состояния прерывания MCP23S17:
gpioExpander.setMirror(true);
gpioExpander.setInterruptOD(false);
gpioExpander.setInterruptLevel(LOW);
Итак, при возникновении прерывания MCP23S17 (gpioExpander.enableInterrupt(pin, CHANGE);
) будет установлен флаг прерывания внутри MCP23S17, и INTA/INTB MCP23S17 станет активным. низкий.
В техническом описании MCP23S17 говорится:
Условие прерывания сбрасывается после того, как младший разряд данных синхронизация во время команды чтения GPIO или INTCAP
Вы настроили контакты прерывания для прерывания НИЗКОГО уровня прерывания:
attachInterrupt(digitalPinToInterrupt(interruptPin1), GPIO1interrupt, LOW);
Итак, если я правильно понимаю, ваш Teensy постоянно повторно входит в функции GPIO#interrupt()
до тех пор, пока что-то прочитанное не приведет к сбросу условия прерывания. Вы не очищаете условие прерывания внутри ISR, вы устанавливаете флаг, чтобы сделать это позже в основной строке выполнения, что само по себе разумно. Я могу предположить, что если бы раньше у вас было что-то, чтобы сбросить условие прерывания внутри прерывания, то теперь оно вне прерывания, а именно ваш вызов .getInterruptValue()
, который считывает регистр INTCAP, упомянутый выше. Или, возможно, вы будете использовать одну из функций, которая читает контакт/порт, который очистил бы условие прерывания. Очистка глобальной переменной сама по себе не очистит условие прерывания, поэтому вы сразу же вернетесь в ISR, который переназначит глобальную переменную.
AVR выполнит одну инструкцию за пределами ISR перед повторным входом в ISR. Таким образом, пребывание в состоянии прерывания приводит к тому, что ваш код становится медленным, потенциально очень медленным. Я не знаю навскидку, что в этом случае будет делать ядро Teensy 4.x. В любом случае он будет заблокирован или, возможно, выглядит заблокированным.
Если бы вы читали/получали значение прерывания внутри ISR, оно бы очистило его и поэтому повторно не входило бы в ISR. Вернее, он не стал бы этого делать определенно (или просто надолго). Возможно, Teensy настолько быстрее, что может повторно войти несколько раз, пока MCP23S17 не подтвердит, что условие прерывания было устранено. Я не пропагандирую это, поэтому на этом остановлюсь.
Я считаю, что в конечном итоге вам нужно установить прерывание FALLING
от фронта:
attachInterrupt(digitalPinToInterrupt(interruptPin1), GPIO1interrupt, FALLING);
В любом случае, с FALLING
начальный задний фронт линии INTA/INTB устанавливает условие прерывания в Teensy (не MCP23S17), но оно сбрасывается при выполнении вашего ISR. Таким образом, вы не будете продолжать повторно входить в ISR. Таким образом, ваш ISR введет один раз, чтобы установить глобальный флаг volatile
. Позже, когда вы получите установленное значение вашего глобального флага(ов) volatile
, вам нужно будет вызвать .getInterruptValue()
(или иным образом прочитать вывод), чтобы очистить условие прерывания в вашем MCP23S17 (не Teensy), которое снимет/поднимет INTA/INTB, и в этот момент ISR в Teensy будет готов к срабатыванию другого спадающего фронта INTA/INTB.
Вероятно, это также возможно сделать с помощью прерывания уровня LOW
, но вам придется продолжать отменять их настройку в ISR и перенастраивать их после сброса состояния MCP23S17. Не уверен, что это будет рекомендовано. Но я думаю, что это возможно.
- Несколько кнопок на одном прерывании, как устранить дребезг?
- Как использовать attachInterrupt() в ATtiny85 с Arduino IDE?
- Невозможно установить низкий уровень на выводе MOSI даже после завершения SPI
- Как контролировать состояние 50 цифровых входов
- Проверить возможности PIN во время компиляции
- NodeMCU - Vin контакт как выход 5V?
- Использовать все контакты как цифровые входы/выходы
- Что такое ICSP pin (разъём)?
Вы точно указали, где я ошибся. Ваш острый глаз помог мне в правильном направлении. Очень ценю!, @Erik