Друзья, Доделываю один проект и дополировываю последние шероховатости. Вкратце, плата на Arduino отлавливает импульсы с "кнопки" и отправляет их в интернет на сервер. Импульсы с кнопки ловятся через Bounce2 (продолжение популярного дебаунсера). Все работает шикарно, импульсы ловятся, помехи нет. Но есть проблема с отловлей импульсов нажатия при передаче наловленного в сеть. Сам процесс передачи (используется Ethernet шилд) занимает секунды полторы две. Чего вполне достаточно для пропуска целого импульса, а то и двух. Происходит сие по той причине, что Bounce2 не использует прерывание, а подразумевает постоянный прогон метода Update в цикле loop. Соответственно пока микроконтроллер занят обработкой передачи сведений в интернеты, никакие импульсы с кнопки я поймать не могу. Подумалось, что можно обойтись малой кровью и задействовать прерывание с пина кнопки, а в прерывании вызвать обработчик Bounce2.Update. Но не тут-то было. Просто так не работает. Прерывание срабатывает, но импульс не ловится. Сдается мне, что проблемы может быть две: 1. Не Volatile переменные в Bounce2. 2. Неработающие функции millis в обработчике прерывания (а все известные мне дебаунсеры работают как раз на этих функциях). Собственно теперь думу крепку думаю, как проще поступить... В теории, можно объявить объект Bounce как volatile, дабы не ковырять все переменные внутри самой библиотеки и собственно не создавать новую библиотеку. Либо вообще использовать не библиотеку, а например использовать код Калинина отсюда http://mypractic.ru/urok-8-cifrovaya-filtraciya-signalov-v-programmax-dlya-arduino.html и внедрить его прямо в тело программы позаботившись о volatile.... Буду рад свежим мыслям. PS. Еще полагаю, что во избежание ложных срабатываний, в код обработчика хорошо бы вставлять запрет на обработку прерывания в самом начале и разрешение в самом конце. Но практического подтверждения своим догадкам пока не нашел.
Говоря о кнопках забывают маленькую вещь. Это медленые устройства по отношению к процессору. Ну не может человек , сидящий на кнопке , стучать на ней со скоростью процессора. Опять же даже короткий импульс на кнопке (нажатие- отжатие) это ну очень большое время (ну очень много операций может сделать процессор) для процессора. Вот и получается, что "быстрый" процессор работая с "медленной" кнопкой отлавливает никому не нужный дребезг, кторый и устраняют . Говоря в итоге к сказаному. Если есть какой-то механизм, который опрашивает кнопку с частотой 0,2 сек, то мы всегда можем определить была ли кнопка нажата в это время или нет , был переход от ненажатая кнопка в нажатое состояние. Опять же кнопки инструмент , который позволяет человеку отдавать команды процесс И человек это тоже медленное устройство по отношению к процессору. Если вам нужна многозадачность и не хочется дергать прерывание воспользуйтесь этим Код (C++): uint8_t non_stop_program1(uint16_t span) { static uint32_t future = 0; if (millis()<future) return 0; future += span; return 1; } uint8_t non_stop_program2(uint16_t span) { static uint32_t future = 0; if (millis()<future) return 0; future += span; return 1; } uint8_t non_stop_program3(uint16_t span) { static uint32_t future = 0; if (millis()<future) return 0; future += span; return 1; } void setup() { Serial.begin(9600); } void loop() { if (non_stop_program1(500)) { // Здесь вы организовываете вывод на экран. Вызывается раз в 0.5 сек Serial.println("knok"); } if (non_stop_program2(200)) { // Здесь вы организовываете ввод с клавиатуры. Вызывается раз в 0.2 сек } if (non_stop_program3(100)) { // Здесь сама работа устройства не зависимая от вашего ввода и показа на экран. Вызывается раз в 0.1 сек(может быть по желанию другим) } }
Подход интересный, но он не решает саму проблему. Когда система будет отправлять сведения в интернеты, я не смогу поймать импульс. Интернет еще медленнее, чем нажатие кнопки человеком. Я вот думаю, а может быть в обработчике прерывания флаг какой поднять, мол был импульс. Конечно есть риск того, что кнопка нажата, началась отправка в интернет, дебаунсер фильтрует дребезг, а флаг поднимается. Но такой риск заметно меньше, чем не поймать импульс в середине отправки. Кстати этот же вариант можно сделать еще красивее - обработчик прерывания включать только в момент отправки в Интернет, а в остальное время пускай обычным способом работает.
Если и есть красивое решение, то скорее всего вы его не оцените. Красота это вещь сугубо индивидуальная. На вкус и цвет карандаши разные.
Ну да поднять флаг и тогда другой вычислительный поток будет долбить интернет. Вот посмотрите структуру программы Код (C++): /* Схема подключения I2C LCD1602 GND -> GND +5V -> +5V SDA -> A4(SDA) SCL -> A5(SCL) * Ардуино энкодер CLK -> A2 CLK_Pin 0 нажата 1 нет DT -> A1 DT_Pin 0 нажата 1 нет SW -> A0 SW_Pin 0 нажата 1 нет GND -> GND +5V -> +5V */ // ============= Переменные ==================================== // системные переменные uint8_t mode_viev ; // 0 режим показа экранов / 1 режим редактирования переменной на экране uint8_t stat_blink_mode_viev ; // мигалка mode_viev uint8_t screen_number ; // номер текущего демонстрируемого окна const int number_of_screen = 3 ; // количество окон в системе // технологические переменные // ============= Подключение LCD1602 по I2C ===================== #include <Wire.h> #include <LiquidCrystal_I2C.h> LiquidCrystal_I2C lcd(0x27,16,2); // Устанавливаем дисплей void LCD_ini(){ // инициализация дисплея mode_viev = 0 ; // установить режим редактирования stat_blink_mode_viev=0 ; screen_number = 1 ; // показать экран 0 lcd.init() ; lcd.backlight() ; // Включаем подсветку дисплея } // --- Экран 1 --- void Screen1_viev() { // показать экран 1 lcd.print("***") ; } // --- Экран 2 --- void Screen2_viev() { // показать экран 2 lcd.print("***") ; } // --- Экран 3 --- void Screen3_viev() { // показать экран 3 lcd.print("***") ; } // -- Панель -- void Panel_Viev() { // показать на экране режим и номер окна lcd.setCursor(15, 0) ; if (stat_blink_mode_viev&&mode_viev) lcd.print("E"); // показ значка обычный режим else lcd.print(" "); // показ значка режим редактирования lcd.setCursor(15, 1) ; lcd.print(screen_number) ; } // -- Общий вывод на дисплей-- void Viev(){ lcd.clear(); switch (screen_number) { case 1:Screen1_viev();break; // показать экран устройствa 1 case 2:Screen2_viev();break; // показать экран устройствa 2 case 3:Screen3_viev();break; // показать экран устройствa 3 } stat_blink_mode_viev = !stat_blink_mode_viev ; Panel_Viev(); } // ========== обработчик энкодера =========================== const int CLK_Pin=A2 ; // нога CLK кнопки 0 нажата 1 нет const int DT_Pin=A1 ; // нога DT кнопки 0 нажата 1 нет const int SW_Pin=A0 ; // нога кнопки 0 нажата 1 нет uint8_t statCLK,statCLK_old ; // переменные: состояние ноги CLK сейчас и раньше uint8_t SW ,SW_old ; // переменные: состояние ноги SW сейчас и раньше void Encoder_ini() { // инициализация энкодера и кпопки в нем pinMode(SW_Pin, INPUT_PULLUP) ; // вывод на ввод с подтягивающим резистром pinMode(CLK_Pin, INPUT); pinMode(DT_Pin, INPUT); statCLK_old = digitalRead(CLK_Pin); SW = digitalRead(SW_Pin); } uint8_t Encoder(){ statCLK = digitalRead(CLK_Pin); if ((statCLK == 0)&&(statCLK_old ==1 )){ // счет по фронту statCLK_old=statCLK; return (digitalRead(DT_Pin)+1); // 1 по часовой 2 против часовой }; statCLK_old=statCLK; return 0; // изменений не произошло } uint8_t SW_botton() { SW_old = SW; SW = digitalRead(SW_Pin); if (!SW &&SW_old) return 1; // срабатывание по нажатию кнопки else return 0; } // ==== ядро системы ====== uint8_t non_stop_program1(uint16_t span) { static uint32_t future = 0; if (millis()<future) return 0; future += span; return 1; } uint8_t non_stop_program2(uint16_t span) { static uint32_t future = 0; if (millis()<future) return 0; future += span; return 1; } uint8_t non_stop_program3(uint16_t span) { static uint32_t future = 0; if (millis()<future) return 0; future += span; return 1; } // ==== главная часть ===== void setup() { LCD_ini(); // инициализация дисплея Encoder_ini(); // инициализация энкодера и кпопки в нем } void loop() { if (non_stop_program1(500)) { // обновление раз в 0.5 сек информации на дисплее Viev(); } if (non_stop_program2(200)) { // Ввод с клавиатуры,анализ и выработка действия. Вызывается раз в 0.2 сек if (SW_botton()) { // если произошло нажатие клавиши // вот сюда и ставьте свою отправку в интернет } } if (non_stop_program3(100)) { // Здесь сама работа устройства не зависимая от вашего ввода и показа на экран. Вызывается раз в 0.1 сек(может быть по желанию другим) } }
В общем решил сделать примерно следующим образом: Код (C++): volatile boolean interruptImpulse = false; // Catches impulse when Sketch is busy with Internet publishing void inter() { interruptImpulse = true; } void loop() { deBouncer.update(); // check for the impulse if (deBouncer.rose()) { addGASImpulse(""); } delay(10); publisher(); // Update ThingSpeak } void publisher() { // uploads data to ThingSpeak attachInterrupt(digitalPinToInterrupt(GASMeterPin), inter, FALLING); //use interrupt in order not to oversee an impulse // тут какой-то код прет со страшной силой detachInterrupt(digitalPinToInterrupt(GASMeterPin)); // stop to use interrupts if (interruptImpulse) { // Try to catch GAS meter impulse via interrupt interruptImpulse = false; addGASImpulse(F("(i)")); } } void addGASImpulse(String sVal){ garageGASMeter++; Serial.print(F("Pressed ")); Serial.print(sVal); } Проверил - ловит импульсы как от нажатия кнопки во время отправки в интернеты, так и вне ее. Конечно, это некий костыль, но вроде бы все остальные функции у меня выполняются более-менее быстро. Исключением можно назвать обработка запроса встроенным веб-сервером, там есть Delay(1) после чтения каждого из символов обращения к веб-серверу, но операция сия настолько редкая, что потребности ее обходить как-то нету совсем.