Как стабилизировать кнопку по прерыванию?
- Войдите на сайт для отправки комментариев
Чт, 28/02/2013 - 21:45
В своем скетче мне очень нужно использовать кнопку по прерыванию. Так как есть очень длинный и долгий цикл который нужно обрывать в любой момент по нажатию кнопки и запускать другую обработку. Как раз для этого идеально подходит прерывание, которое обрабатывает кнопку не зависимо от основной программы. Есть стандартный код обработки кнопки по прерыванию, но он работает с дребезгом.
//
// светодиод, подключённый к digital pin 13 будет изменять своё
// состояние при изменении напряжения на digital pin 2
//
int pin = 13;
volatile int state = LOW;
void setup()
{
pinMode(pin, OUTPUT); // порт как выход
attachInterrupt(0, blink, CHANGE); // привязываем 0-е прерывание к функции blink().
}
void loop()
{
digitalWrite(pin, state); // выводим state
}
void blink()
{
state = !state; // меняем значение на противоположное
}
Я знаю что можно внести в схему инвертирующий триггер шмидта, который будет стабилизировать сигнал получаемый с кнопки, но охота не услажнять схему дополнительными деталями. Может есть вариант сделать это програмно?
В обработчике прерываний запретить прерывания в начале и разрешить в конце.
Установить глобальный счетик в обработчике прерываний выполняющий полный код обработчика на 5-тый 10-тый или 20-тый вызов прерывания
Лучше всего читать в обработчике "системное" время ( millis() ) и проверять разницу. По достижении заданного интервала делать полную обработку и записывать актуальное время для последующего сравнения. Учесть преход счётчика millis() через ноль, если включаться будет дольше чем на 50 дней
А можно пример пожалуйста, а то у меня в голове каша после прочтенного.
Все решил проблему! А все очень просто. Добавил обычный керамический конденсатор на 1мф мжду контактами кнопки. И вуаля абсолютно никакого дребезга. Кнопку с большой частотой нажимал на сколько мог. Дребезга нет, удивительно)
int pin = 13; volatile int state = LOW; void setup() { pinMode(pin, OUTPUT); // порт как выход attachInterrupt(0, blink, LOW); // привязываем 0-е прерывание к функции blink(). digitalWrite(2, 1); } void loop() { digitalWrite(pin, state); // выводим state } void blink() { static unsigned long millis_prev; if(millis()-100 > millis_prev) state = !state; // меняем значение на противоположное millis_prev = millis(); }"Все решил проблему! А все очень просто. Добавил обычный керамический конденсатор на 1мф мжду контактами кнопки. И вуаля абсолютно никакого дребезга. Кнопку с большой частотой нажимал на сколько мог. Дребезга нет, удивительно)" - Вы просто ещё не попали в ситуацию когда дребезг даст о себе знать снова. Да, конденсатор значительно сглаживает сигнал дребезга, но не устраняет полностью.
Все таки добавить две строки куда проще чем впаивать конденсатор.
int pin = 13; volatile int state = LOW; void setup() { pinMode(pin, OUTPUT); // порт как выход attachInterrupt(0, blink, LOW); // привязываем 0-е прерывание к функции blink(). digitalWrite(2, 1); } void loop() { digitalWrite(pin, state); // выводим state } void blink() { static unsigned long millis_prev; if(millis()-100 > millis_prev) state = !state; // меняем значение на противоположное millis_prev = millis(); }отличное решение! А я пошел по другому пути. Установил глубину фильтра - от 8 до 16 единиц. И в том-же обработчике прерывания от кнопки просто считаю сколько подряд проскочило 1 или 0, смотря по какому уровню настроено прерывание. Когда необходимое количество 1 или 0 пришло, выставляю флаг что нажата кнопка.
Таким образом получается избавиться даже от очень сильного дребезга
int pin = 13; volatile int state = LOW; void setup() { pinMode(pin, OUTPUT); // порт как выход attachInterrupt(0, blink, LOW); // привязываем 0-е прерывание к функции blink(). digitalWrite(2, 1); } void loop() { digitalWrite(pin, state); // выводим state } void blink() { static unsigned long millis_prev; if(millis()-100 > millis_prev) state = !state; // меняем значение на противоположное millis_prev = millis(); }при объявлении переменная millis_prev инициализируется в ноль или ей просто выделяется неочищенная область памяти ?
-------
Таким образом получается избавиться даже от очень сильного дребезга
В обработчике прерываний запретить прерывания в начале и разрешить в конце.
Пустые заботы. Прерывания автоматически запрещаются на время выполнения кода обработчика прерываний
Я полагаю в техническом форуме не место для персональных оскорблений.
Если Вы хотите критиковать, критикуйте по существу, если не можете , ведите себя достойно
Оскорблений никаких не было.
А по существу пожалуйста:
Абсолютно никак не помогает от дребезга. По какой причине в коде выше стоит LOW уже не помню, с таким же успехом будет работать и RISING и FALLING.
#4
http://arduino.ru/forum/programmirovanie/vopros-po-preryvaniyam#comment-34127
Что зависит? Длительность дребезга? Без разницы какой длины дребезг, если он есть.
из этой душераздирающей ветки нужно сделать вывод, что если в проекте всё так критично с дребезгом - плюньте на дребезг, пусть он будет, но не влияет ни на что...
две кнопки или тумблер - одна кнопка(положение тумблера) что-то включает, вторая - выключает.
и никакого дребезга!
Я полагаю в техническом форуме не место для персональных оскорблений.
Я вас правильно понял?
Потому как прочитав ваш пост у меня сложилось впечатление что вы решили мол - "да хрен ли вы тут все чушь какую то несете, вот как надо делать, вот как должно быть", отсюда и была такая жесткая реакция с моей стороны на ваш пост.
мой вариант борьбы с дребезгом при использовании прерываний такой:
- на кнопку настраиваем прерывание (одно из INT0/INT1 или PCINT). Только для INT0/INT1 можно установить то на какое событие мы будем срабатывать (any, zero, high, rasing, faling), для PCINT можно выбрать только вариант any, но это не страшно - прочитав первой строчкой кода прерывания статус кнопки в порту мы сразу поймем из-за чего произошло прерывание и можем его проигнорировать, если оно нам не интересно.
- делаем прерывание на тамер. частоты срабатывания должна быть от десятых долей секунды и чаще. точное значение милисеунд не нужно, нужно просто иметь возможность отсчитывать короткие (в десять и более раз меньшие чем секунда) интервалы.
- объявляем две volatile переменные одна для работы таймера кнопки, вторая для фиксации факта успешного (уже после обработки дребезга) нажатия кнопки. По второй переменной мы будет делать что нам нужно в loop() и после обработки будет сбрасывать в ноль значение этой переменной. Для многих случаев полезно игнорировать новые нажатия кнопки до тех пор, пока предыдущее нажатие не обработано.
- первую переменную выставляем в прерывании от нажатия кнопки (не забываем проверять состояние порта для обработки PCINT). Выставляем ее в количество циклов срабатывания таймера которые мы считаем надежным нажатием. Подбирается экспериментально. Из моей практики время до истечения таймера должно быть между 400 и 100 мсек. Делим это время на период срабатывания обработчика таймера и полученно число после округдения записываем в нашу первую переменную
- как только значение первой переменной стало больше нуля мы начинаем считать и как только досчитаем до нуля снова смотрим состояние порта и если кнопка все еще нажата - ставим единицу во вторую переменную
все. пример есть в моем коде здесь http://arduino.ru/forum/proekty/igrushechnyi-svetofor
А что если в обработчике прерываний первой строкой вписать запрет обработки прерывания? А в конце, если надо, разрешить.
int pin = 13; volatile int state = LOW; void setup() { pinMode(pin, OUTPUT); // порт как выход attachInterrupt(0, blink, LOW); // привязываем 0-е прерывание к функции blink(). digitalWrite(2, 1); } void loop() { digitalWrite(pin, state); // выводим state } void blink() { static unsigned long millis_prev; if(millis()-100 > millis_prev) state = !state; // меняем значение на противоположное millis_prev = millis(); }Отлично работает! Перепробовал разные варианты, похоже этот пока самый простой и стабильный.
//В сетапе attachInterrupt(0, butt1, FALLING); // Кнопка // или rising void butt1() { detachInterrupt(0);//отключаем прерывание interrupts(); if(millis()- last_time >= 100 ) { // Чето делаем last_time = millis(); } attachInterrupt(0, butt1, FALLING);//активируем прерывание }как по мне самый простой вариант, можно еще тригер поставить для большей надежности, тот же шмитта. Ставил DS, то тоже норм сбивает дребезг
Все таки добавить две строки куда проще чем впаивать конденсатор.
Ваше программное решение не защищает от брезга при отпускании кнопки. Пользователь может нажать, подержать, а потом отпустить. И вот тут-то при дребезге будет лишнее срабатывание.
А обычно хочется отрабатывать именно нажатие на кнопку, а не отпускание. В том числе, продолжительное.
Все решил проблему! А все очень просто. Добавил обычный керамический конденсатор на 1мф мжду контактами кнопки. И вуаля абсолютно никакого дребезга. Кнопку с большой частотой нажимал на сколько мог. Дребезга нет, удивительно)
... и потеряли энергоэффективность, надо полагать. Кондей можно поставить и поменьше.
Зачетного мракобеса к нам занесло! Это ж надо было раскопать тему и отвечать на сообщения пятилетней давности. Какая нах энергоэффективность нажатия кнопки ))) Какое длительное отпускание?! Почти усцался от смеха))
ПС. примеры годные, но деатач/атач в обработчике лишние. Кондей, тригер и пр. лабудень на кнопке - показатель неумения человека писать код. Еще важно чтобы один месный не заметил что паузу на 100мсек в коде, он свято верит что умеет нажать и отпустить кнопку быстрей и воспринимает такое как личное унижение ;)
Какая нах энергоэффективность нажатия кнопки ))) Какое длительное отпускание?! Почти усцался от смеха))
;)
не нажатия кнопки, а зарядки/разрядки кондея. Разумеется, программно множно и нужно. Но приведенный здесь пример далек от совершенства.
По поводу длительно нажатия:
Кнопки нажимаются не только для того чтобы тут же отпустить. По-моему, это очевидно.
ПС. Написал в древнюю тему, потому что искал готовое решение. Его, к сожалению, годного, пока не нашлось.
Еще важно чтобы один месный не заметил...
один местный уже три года ждёт обещанного одним балаболом кода обработчика сотни кнопок для человека с одним пальцем... но, пока балабол тратит время на комменты некропостеров.
алё. когда код будет, пустобрёх?
А один местный заказ уже оплатил?
ПС. Я знал что он обязательно клюнет! )))
пример далек от совершенства.
а совершенство - штука скорей хилосовская;)
Кнопки нажимаются не только для того чтобы тут же отпустить. По-моему, это очевидно.
А вот тут по разному бывает. Кнопка лифта, кнопка мышки, кнопка бояна - все сильно специфично.
искал готовое решение. Его, к сожалению, годного, пока не нашлось.
разумеется. Нет проблемы - нет решения.
А один местный заказ уже оплатил?
ПС. Я знал что он обязательно клюнет! )))
тред был не в рубрике "Ищу исполнителя", и ты обещал без условий оплаты, а что бы доказать, как нужно делать - б-а-л-а-б-о-л
*я знал, что Логик - пустобрёх.
Ничего подобного, я профи, пишу код за деньги. И если кому надо коду от меня - так не за так. И об этом изначально я те писал, влом только искать. Но учитывая чтот ты настырно клянчиш уже который месяц и беря во внимание, что недавно был день дурака, а заслуженого подарка на форуме ты так и нифига не получил, то дарю. Но не все ;) Интересные методы классов я позатираю, поля данных оставлю для подсчета. Так до поступления финансов на счет.
#ifndef cbi #define cbi(sfr, bit) (sfr &= ~_BV(bit)) #endif #ifndef sbi #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) #endif enum KBD_EVENT { // физические события изменения состояния кнопок KBD_EVENT_DONE, KBD_EVENT_UP, //логические события KBD_EVENT_LONG, KBD_EVENT_SHORT, KBD_EVENT_DBL, KBD_EVENT_PRESS, KBD_EVENT_TIME, }; void setup(void) { Serial.begin(9600); PORTC=0x1f; DDRC=0x1f; PORTD=0xfc; PORTB=0x03; } #define MAX_STROBE 5 #define KEY_TIME_MAX 4 //столько кнопок одновременно можно нажать //Сканирование матрицы, поиск нажатий и отправка их в SelectPress. Генерация KBD_EVENT_TIME class PhisicalKBD { virtual void SetStrob(byte n, byte s)=0; virtual byte GetReturn(void)=0; //Ищем изменения в состоянии матрицы и уведомляем о них уровень выше //Про завершение сканирования уведомляем сообщением KBD_EVENT_TIME void Select(void) { byte r; byte rez; byte a; rez=0; for(byte i=0;i<MAX_STROBE;i++) { a=KbdScrean[i]; r=KbdOldScrean[i] ^ a; KbdOldScrean[i]=a; SelectPress(i, r, a); } SelectLongPress(KBD_EVENT_TIME, 0); } protected: byte Strob; byte OldTime; byte KbdScrean[MAX_STROBE]; byte KbdOldScrean[MAX_STROBE]; //сюда класс отправит все состояния возвратов для каждого строба //i-строб //r-маска кнопок изменивших состояние //ф-маска текущих состояний кнопок virtual void SelectPress(byte i, byte r, byte a) {}; //выделяем короткие и длинные нажатия, обработка комбинаций virtual void SelectLongPress(byte evnt, byte Key, byte FlCombi = false) =0; public: PhisicalKBD() { Strob=0xff; } //Выполняем очередную фазу сканирования и опроса кнопок //Если цикл сканирования завершился - анализируем кнопки в Select() void Scaning(void) { if(Strob==0xff) { for(byte i=0;i<MAX_STROBE;i++) { SetStrob(i, HIGH); } Strob=0; } else KbdScrean[Strob]=~GetReturn(); //храним с инверсией для удобства при старте SetStrob(Strob, HIGH); Strob++; if(Strob>MAX_STROBE) { Strob=0; Select(); } SetStrob(Strob, LOW); } // Процесс формирования временных интервалов сканирования и опроса кнопок // Достаточно часто вызываем из loop. byte Process(byte t) { if(byte(t-OldTime)<20) return 0; OldTime=t; Scaning(); } }; //Класс формирования физических сообщений - KBD_EVENT_DONE и KBD_EVENT_UP class BaseKBD : public PhisicalKBD { куку! ;) }; class LongPressKBD : public BaseKBD { struct KyeTime { byte Key; byte Time; }; KyeTime KT[KEY_TIME_MAX]; //храним нажатия кнопок //по содержимому KT выявляем код комбинации, если его нет возвращаем 0 куку! ;) protected: byte TimeKbd; //счетчик тактов сканирования кнопок }; class DblPressKBD : public LongPressKBD { byte Key_SH; byte Key_SH_Time; куку! ;) }; #define DBG #ifdef DBG byte Tic; byte Key=0xff; byte Key1=0xff; #endif class KBD : public DblPressKBD //class KBD : public BaseKBD { public: // KBD(byte s) : PhisicalKBD(s) { }; KBD(byte s) : DblPressKBD(s) { }; private: // работаем напрямую с портом void SetStrob(byte n, byte s) { #ifdef DBG if((n==3) && (s==LOW)) { Key1=Key; } else Key1=0xff; #else s?sbi(PORTC,n):cbi(PORTC,n); #endif } byte GetReturn(void) { #ifdef DBG byte r=Key1;Key1=0; return r; #else return (PIND & 0xfc) | (PINB & 0x3); #endif } byte SelectCombiKey(void){ return 0;} void KeyEvent(byte evnt, byte key) { #ifdef DBG if(evnt==KBD_EVENT_TIME) Tic++; if(!(Tic&63)) { Serial.println("Tic!!"); Key=0xf9; Tic++; } if((Tic&63)==3) Key=0xf8; if((Tic&63)==7) Key=0xf9; if((Tic&63)==9) Key=0xfd; if((Tic&63)==12) Key=0xff; if(evnt==KBD_EVENT_TIME) return; #endif Serial.print("Event="); Serial.print(evnt); Serial.print(" Key="); Serial.println(key, HEX); }; }; KBD kbd(MAX_STROBE); void loop(void) { static byte OldTime; // static byte KbdScrean[MAX_STROBE]; byte Time; Time=millis(); switch(kbd.Process(Time)) { case KBD_EVENT_DONE: break; case KBD_EVENT_UP: break; case KBD_EVENT_LONG: break; case KBD_EVENT_SHORT: break; case KBD_EVENT_DBL: break; } }Ну и вывод при полном наличии иерархии классов
Ничего подобного, я профи, пишу код за деньги. И если кому надо коду от меня - так не за так. И об этом изначально я те писал, влом только искать.
ты не профи - у тебя долгосрочной памяти как у канарейки, ты не помнишь, что начал мне рассказывать, как нужно... и, когда я тебе предложил показать, как нужно - ты сказал, что выкатишь код и я буду посрамлён.
когда я тебе напомнил о твоих обещаниях - ты-сука слился.
давай код, прохфесианал!
Заметь, кстати непосредственно стробирование и опрос реализуется в пользовательском классе KBD, функции SetStrob и GetReturn. А это означает что? Правильно, ты ниче не понял... Это означает что клаву легко и не принужденно можна вынести в расширители портов. И ничего от этого не поменяется в базовых классах. Обем памяти сам посчитаеш, там просто, структурка в массиве. А я - спать. Пока. С прошедшим праздником ;)
Ничего подобного, я профи, пишу код за деньги. И если кому надо коду от меня - так не за так. И об этом изначально я те писал, влом только искать.
ты не профи - у тебя долгосрочной памяти как у канарейки, ты не помнишь, что начал мне рассказывать, как нужно... и, когда я тебе предложил показать, как нужно - ты сказал, что выкатишь код и я буду посрамлён.
когда я тебе напомнил о твоих обещаниях - ты-сука слился.
давай код, прохфесианал!
Прикольно получилось))) А код то - вон он, выше! Но самое вкусное - за бабло. Увы это жизнь такая. Не злись. Думаю в коде ты всеравно нихера не поймеш.
ПС. еще раз тя с прошедшим праздником.
ты бы ещё ссылку на блинк без делая дал, а отсальное - за бабло попросил, клоун.
это не раздел Ищу исполнителя - публикация цитат из секретного кода здесь считается тупым балабольством.
в референс, Гаммон и другие умные дядьки говорят не использовать милис,сериал и прочее внутри ISR. а) почему вы всетаки использовались милис именно внутри ?
б) почему оно работает если умные дядьки говорят что внутри ISR это не будет работать ?
Надо понимать, что такое "не работает". В ISR millis() не изменяет своего значения, выдавая все время одно и то же. Но, при двух разнесенных во времени вызовах обработчика, величина millis() в нем будет отличаться. Данный факт применен в этом коде "антидребезга".
понял спасиб
Господа, ок а можно же просто отключить прерывание? (вопрос в целях самооброзования а не подвоха который почемуто все тут ждут)
setup{ flag = false } loop{ if (!flag) attachInterrupt if(flag) { interrupt handler flag = false } } ISR{ detach flag=true }Так отключайте, никто не запрешает (пока).
я спрашиваю так тоже будет правильным ? не с точки зрения валидности кода а с точки зрения логики, "хорошего" кода
Правильно "отключать" прерывания путем их запрещения. Не всех скопом, а конкретное.
так detach как раз и отключает конкретное а не все скопом...
(не осуждаю умения и навыки maksim, он знает что делает) но новичков хотел бы предостиречь от использования millis(), Serial внутри ISR (обработчика прерываний) во избежания непонятных глюков
delay (100);Serial.println ("ISR entered");)подробнее тут http://www.gammon.com.au/interrupts, http://arduino.ru/forum/programmirovanie/vopros-po-preryvaniyam#comment-34127, http://arduino.ru/forum/programmirovanie/vnutrennie-preryvaniya?page=1#comment-446383
В моём случае этот код нормально отрабатывает только с LOW. Видимо поэтому он и был здесь указан! ))
По непонятным причинам с RISING или FALLING прерывание часто срабатывает и на нажатие, и на отпускание кнопки. Т.е. 2 раза, а не один раз, как ожидается.
Причем с RISING "левых" сработок около 90%, а с FALLING - около 50%. И, к сожалению, не прослеживается никакой логики по срабатыванию четко на смену сигнала с LOW на HIGH либо наоборот.
С вариантом LOW все как часы. Нажал - получил.
мой вариант борьбы с дребезгом при использовании прерываний такой:
Дык это некропост ! Тьфу ты :)