Попались мне крайне дешевые китайские кнопки. Взял на замену емкостным, т.к. у последних стали появляться ложные срабатывания.
Из чего и как сделаны кнопки, я не знаю, но дребезг у них занимает большую часть времени нажатия. Стандартный алгоритм с этими кнопками не справился, пришлось придумывать что-то иное, т.к. мне нужно обработку коротких и длинных нажатий.
Я не претендую на оригинальность, скорее всего подобный код есть и много лучше, однако поделюсь. Не судите строго.
Смысл в том, что мы не фиксируем нажатие и не считаем его длительность. Мы стробируем время и в заданные промежутки проверяем состояние линии за прошедший период.
//*********************************************
void IRAM_ATTR button_interr_i(){
if( !ft_i ){
ft_i = 1;
buttonTime_i = millis();
}
millis_prev_i = millis();
}
Выше код обработчика нажатия на кнопку. Мы его подключаем в setup().
attachInterrupt(digitalPinToInterrupt(PIN_BUTTON_I), button_interr_i, CHANGE);
Также в setup() мы подключаем обработчик прерывания таймера:
hw_timer_t *My_timer = NULL; //указатель на таймер
. . .
void setup() {
. . .
My_timer = timerBegin(0, 80, true);
timerAttachInterrupt(My_timer, &ft_interr, true);
timerAlarmWrite(My_timer, ALATM_DT * 1000, true);
timerAlarmEnable(My_timer);
. . .
}
Ниже код обработчика прерываний таймера:
//*********************************************
void IRAM_ATTR ft_interr(){ //обработка прерывания по таймеру
if( ft_i && ( millis_prev_i == 0 ) ) {
if( ft_i < EMPTY_LOOP ){
ft_i += !digitalRead(PIN_BUTTON_I);
} else {
buttonStatus_i = 1;
}
} else {
millis_prev_i = 0;
if(ft_i > 1){
ft_i = 1;
}
}
}
Немного подробнее: при первом нажатии на кнопку мы устанавливаем значение переменной ft_i в 1 и фиксируем время события в переменной buttonTime_i. При последующих прерываниях от кнопки (дребезга) мы фиксируем время данного события в переменной millis_prev_i.
Обработчик таймера проверяет состояние millis_prev_i и ft_i. Если нажатие состоялось, то при каждом вызове таймера мы проверяем, а не закончилось ли оно сбрасывая переменную millis_prev_i в 0 при каждом вызове. Т.е. если кнопу отпустили, то прерываний от таймера не будет и переменная millis_prev_i останется равной 0. Дешевым кнопкам доверять нельзя. Мы несколько раз (EMPTY_LOOP) убеждаемся в том, что импульсов от кнопки больше нет и устанавливаем признак buttonStatus_i в 1, который обрабатывается уже в loop().
void loop() {
//.................................
if(buttonStatus_i) {
unsigned long bT_i = millis() - buttonTime_i - EMPTY_LOOP * ALATM_DT;
if(bT_i > LONG_WAIT) //какие-то действия
light_2.toggleMax();
else
light_2.trigger();
ft_i = 0;
buttonStatus_i = 0;
buttonTime_i = 0;
}
. . .
}