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