Клавиатура через MCP23017 по прерываниям

s96serg
Offline
Зарегистрирован: 16.12.2019

Здравствуйте!

В Arduino новичок.

Случилась необходимость работать с клавиатурой 4*4  через расширитель портов MCP23017 по прерываниям. Использовал библитотеку Adafruit_MCP23017. Ну без прерываний все работает замечательно, а вот с прерываниями никак.

Для тестирования написал скетч, который переключает светодиод при нажатии кнопки.

Так вот - вариант, который переработал с примера к библиотеке, работает. И напряжение на контакте mcp INTA как положено при нажатии кнопки падает к LOW, а после отжатия возвращается к HIGH.

Рабочий код:

//
// Клавиатура 4X4 через mcp23017
//
//

#include <Wire.h>
#include "Adafruit_MCP23017.h"
#include "kbd4x4mcp.h"

volatile boolean KeyPressed = false;
byte LedPin = 13;
volatile boolean LedState = false;

Adafruit_MCP23017 mcp00;                //объект расширителя портов
kbd4x4mcp kbd(mcp00,0,1,2,3,4,5,6,7);   //объект клавиатуры        
byte arduinoResetMCPpin = 10;           //пин Ардуино для сброса MCP

byte arduinoIntPin=3;                   //пин прерывания Arduino
byte arduinoInterrupt=1;                //номер прерывания Arduino

//=== ИНИЦИАЛИЗАЦИЯ ====================================================================================
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial.println("Test");

  mcp00.begin();
  pinMode(arduinoResetMCPpin, OUTPUT); //конфигурировать пин аппаратного сброса MCP
  ResetMCP();                          //аппаратный сброс расширителя портов (без него с прерываниями вообще глюки)
  kbd.Reset();                         //конфигурировать MCP для клавиатуры

  pinMode(LedPin, OUTPUT); //мигательная лампочка
  digitalWrite(LedPin, LedState);

  //подготовить MCP для прерываний клавиатуры
  mcp00.setupInterrupts(false,false,LOW); //mirror, INTA/B will not be Floating, INTs will be signaled with a LOW
  for(byte row = 0; row < 4; row++) mcp00.setupInterruptPin(kbd.RowsPinsIN[row], FALLING);
  
  //подготовить Arduino для прерываний клавиатуры  
  pinMode(arduinoIntPin,INPUT);
  attachInterrupt(arduinoInterrupt,kbdIntCallBack,FALLING);

  KeyPressed = false;
}//setup()
//======================================================================================================


void loop() {

if(KeyPressed)
{

 detachInterrupt(arduinoInterrupt);

 //сменить состояние мигательной лампочки
 
 LedState = !LedState;
 digitalWrite(LedPin, LedState);

 //подождать отжатия клавиши
 while( !(mcp00.digitalRead(kbd.RowsPinsIN[0]) && 
          mcp00.digitalRead(kbd.RowsPinsIN[1]) &&
          mcp00.digitalRead(kbd.RowsPinsIN[2]) &&
          mcp00.digitalRead(kbd.RowsPinsIN[3])) );


 attachInterrupt(arduinoInterrupt,kbdIntCallBack,FALLING);
 KeyPressed = false;
}
 
} //loop  



//=== ФУНКЦИИ ==========================================================================================

//--- CallBack прерывания клавиатуры -------------------------------------------------------------------
void kbdIntCallBack()
{
 KeyPressed = true;
}//kbdIntCallBack()
//------------------------------------------------------------------------------------------------------


//--- аппаратный сброс расширителя портов --------------------------------------------------------------
void ResetMCP()
{
 digitalWrite(arduinoResetMCPpin, LOW);
 delay(500);
 digitalWrite(arduinoResetMCPpin, HIGH);
}
//------------------------------------------------------------------------------------------------------


//=== END ФУНКЦИИ ======================================================================================

 

Только смысла в нем не вижу, ибо сказано - с прерываниями loop должен быть пуст. А если в loop все делать, то и без прерываний можно. 

А вот если обработку перенести все в CallBack, то срабатывает только 1-й раз, и напряжение на mcp INTA падает в LOW и не возвращается в HIGH даже после отжатия кнопки. А нужно вместить в CallBack довольно большой обработчик с многими ветвлениями.

 

НЕ рабочий код:

//
// Клавиатура 4X4 через mcp23017
//
//

#include <Wire.h>
#include "Adafruit_MCP23017.h"
#include "kbd4x4mcp.h"

//volatile boolean KeyPressed = false;
byte LedPin = 13;
volatile boolean LedState = false;

Adafruit_MCP23017 mcp00;                //объект расширителя портов
kbd4x4mcp kbd(mcp00,0,1,2,3,4,5,6,7);   //объект клавиатуры        
byte arduinoResetMCPpin = 10;           //пин Ардуино для сброса MCP

byte arduinoIntPin=3;                   //пин прерывания Arduino
byte arduinoInterrupt=1;                //номер прерывания Arduino

//=== ИНИЦИАЛИЗАЦИЯ ====================================================================================
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial.println("Test");

  mcp00.begin();
  pinMode(arduinoResetMCPpin, OUTPUT); //конфигурировать пин аппаратного сброса MCP
  ResetMCP();                          //аппаратный сброс расширителя портов (без него с прерываниями вообще глюки)
  kbd.Reset();                         //конфигурировать MCP для клавиатуры

  pinMode(LedPin, OUTPUT); //мигательная лампочка
  digitalWrite(LedPin, LedState);

  //подготовить MCP для прерываний клавиатуры
  mcp00.setupInterrupts(false,false,LOW); //mirror, INTA/B will not be Floating, INTs will be signaled with a LOW
  for(byte row = 0; row < 4; row++) mcp00.setupInterruptPin(kbd.RowsPinsIN[row], FALLING);
  
  //подготовить Arduino для прерываний клавиатуры  
  pinMode(arduinoIntPin,INPUT);
  attachInterrupt(arduinoInterrupt,kbdIntCallBack,FALLING);

  //KeyPressed = false;
}//setup()
//======================================================================================================


void loop() {

 
} //loop  



//=== ФУНКЦИИ ==========================================================================================
//--- CallBack прерывания клавиатуры -------------------------------------------------------------------
void kbdIntCallBack()
{
 //KeyPressed = true;
  detachInterrupt(arduinoInterrupt);

 //сменить состояние мигательной лампочки
 LedState = !LedState;
 digitalWrite(LedPin, LedState);

 //Serial.println("123456789"); //выводит только "12"
 
 //подождать отжатия клавиши
 while( !(mcp00.digitalRead(kbd.RowsPinsIN[0]) && 
          mcp00.digitalRead(kbd.RowsPinsIN[1]) &&
          mcp00.digitalRead(kbd.RowsPinsIN[2]) &&
          mcp00.digitalRead(kbd.RowsPinsIN[3])) );


 attachInterrupt(arduinoInterrupt,kbdIntCallBack,FALLING);
 
}//kbdIntCallBack()
//------------------------------------------------------------------------------------------------------


//--- аппаратный сброс расширителя портов --------------------------------------------------------------
void ResetMCP()
{
 digitalWrite(arduinoResetMCPpin, LOW);
 delay(500);
 digitalWrite(arduinoResetMCPpin, HIGH);
}
//------------------------------------------------------------------------------------------------------


//=== END ФУНКЦИИ ======================================================================================

 

 

 

 

 

 
sadman41
Offline
Зарегистрирован: 19.10.2016

Работа по прерываниям не обязательно делает луп чище, но она делает его быстрее. 

Вы подключали MCP через I2C - верно? Значит внутри библиотеки для опроса расширителя без использования прерывания (а точнее - флага, который выставляет прерывание) каждый луп будет использовано по нескольку десятков команд на языке Си и, возможно, несколько сотен - на языке Assembler.

С прерыванием же на каждом лупе будет проверяться только один байт (флаг keyPressed) - выполнением пары-тройки команд ассемблера. И только в случае необходимости начинается тяжелый обмен с расширителем по протоколу I2C.

В итоге, что эффективней - работа с прерыванием или без?

Засовывание же всего кода, что взаимодействует с расширителем, в обработчик прерывания, в данном случае достаточно бессмысленно по причине того, что в обработчике прерывания блокируется выполнение других прерываний, а стало быть - механизм работы микроконтроллера с шиной I2C.

s96serg
Offline
Зарегистрирован: 16.12.2019

 

sadman41

 

Спасибо, с Вами согласен. Но все же хотя бы из чисто академического интереса продолжает мучить вопрос - почему не работает 2-й вариант кода ?

 

sadman41
Offline
Зарегистрирован: 19.10.2016

Я же ответил на данный вопрос в последнем абзаце.

s96serg
Offline
Зарегистрирован: 16.12.2019

sadman41

Спасибо, понял. Получается, что в CallBack лучше ограничиться "чистой математикой", а работу с периферией делать в loop.

-NMi-
Offline
Зарегистрирован: 20.08.2018

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