Клавиатура через MCP23017 по прерываниям
- Войдите на сайт для отправки комментариев
Здравствуйте!
В 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 ФУНКЦИИ ======================================================================================
Работа по прерываниям не обязательно делает луп чище, но она делает его быстрее.
Вы подключали MCP через I2C - верно? Значит внутри библиотеки для опроса расширителя без использования прерывания (а точнее - флага, который выставляет прерывание) каждый луп будет использовано по нескольку десятков команд на языке Си и, возможно, несколько сотен - на языке Assembler.
С прерыванием же на каждом лупе будет проверяться только один байт (флаг keyPressed) - выполнением пары-тройки команд ассемблера. И только в случае необходимости начинается тяжелый обмен с расширителем по протоколу I2C.
В итоге, что эффективней - работа с прерыванием или без?
Засовывание же всего кода, что взаимодействует с расширителем, в обработчик прерывания, в данном случае достаточно бессмысленно по причине того, что в обработчике прерывания блокируется выполнение других прерываний, а стало быть - механизм работы микроконтроллера с шиной I2C.
sadman41
Спасибо, с Вами согласен. Но все же хотя бы из чисто академического интереса продолжает мучить вопрос - почему не работает 2-й вариант кода ?
Я же ответил на данный вопрос в последнем абзаце.
sadman41
Спасибо, понял. Получается, что в CallBack лучше ограничиться "чистой математикой", а работу с периферией делать в loop.
Лучше ограничиться только флагом, мол есть нажатие клавиши (это в обработчике прерывания) А уже в лупе неспешно обработать нажатую клавишу.
Вообще, прерывания вещь очень полезная, вот только подход к этим прерываниям должен быть продуманным до мелочей.