Мой способ обработки нажатия кнопок (2 вывода контроллера, сколько угодно кнопок)
- Войдите на сайт для отправки комментариев
Покритикуйте мой способ обработки нажатия кнопок по прерыванию. Может прерывание тут вообще лишнее? Или как-то можно оптимизировать? Не реализована обработка одновременного нажатия двух и более кнопок.
Задача вообще стоит такая: выполнить действие по быстрому нажатию кнопки (менее 500 ms) или по длительному нажатию кнопки (более 500 ms)
Подключение: каждая кнопка через свой резистор к любому аналоговому входу и одновременно к прерыванию INT0 или INT1 (см. рисунок). В итоге занято всего два выхода. В программе по прерыванию считываем состояние аналогового входа, тем самым проверяем, какая кнопка нажата.
Алгоритм.
Прерывание настраивается по переднему фронту. При нажатии на кнопку срабатывает пррерывание, в обработчике запоминаем время нажатия, переключаем прерывание на срабатывание по заднему фронту (отпускание кнопки). При отпускании кнопки считаем время, которое она была зажата, переключаем прерывание в срабатывание по переднему фронту. Устанавливаем нужные переменные, которые проверяются в основном цикле и в зависимости от результата запускается нужная функция. Ниже приведён пример кода.
Время нажатия я считал в основном цикле, чтобы при достижении нужной длительности сразу же выполнить действие (500мс). Но можно время считать и в прерывании и, проверив, как долго была нажата кнопка при отпускании, выполнить нужное действие (в этом случае время может быть какое угодно, всё зависит от того, столько я буду держать кнопку нажатой). Так же исключает дребезг контактов.
1. Обработка прерывания
// настройка INT1 EICRA |= (1 << ISC11) | (1 << ISC10); // настраиваем на срабатывание INT1 по переднему фронту EIMSK |= (1 << INT1); // разрешаем внешнее прерывание INT1 interrupts(); // глобально разрешаем прерывания
/*------- I N T 1 - нажатие кнопок. -----------------------------------*/ ISR(INT1_vect) { if (btn_down_mills == 0) { // если 0 - кнопка до этого не была нажата, а это момент нажатия кнопки btn_down_mills = millis(); // запоминаем время, когда её нажали EICRA &= ~(1 << ISC10); // переключаем прерывание по заднему фронту btn_code = analogRead(19); // запоминаем какую кнопку нажали digitalWrite(Led_Pin, HIGH); // включаем светодиод } else { // если не 0 - значит это момент отпускания кнопки int btn_pressed_time = millis() - btn_down_mills; // вычисляем как долго была нажата кнопка if (50<btn_pressed_time && btn_pressed_time<490) { // если кнопка была зажата более 50ms, но менее 500ms, то обрабатываем нажатие button_short_press = true; } btn_down_mills = 0; // обнуляем длительность нажатия кнопки EICRA |= (1 << ISC10); // переключаем прерывание по переднему фронту } }
2. Основной цикл (loop)
// если было короткое нажатие на кнопку if (button_short_press) { button_short_press = false; press_button(btn_code, SHORT_PRESS); // запускаем обработчик нажатой кнопки } // проверяем долгое нажатие кнопки if (btn_down_mills > 1) { // если кнопка нажата int btn_pressed_time = millis()-btn_down_mills; // считаем время удержания кнопки нажатой if (btn_pressed_time > 500) { // Если кнопка зажата уже более 500ms btn_down_mills = 0; // обнуляем переменную - бльше кнопка не нажата digitalWrite(Led_Pin, LOW); // гасим светодиод EICRA |= (1 << ISC10); // переключаем прерывание по переднему фронту button_short_press = false; press_button(btn_code, LONG_PRESS); // запускаем обработчик нажатой кнопки } }
Почему вы считаете , что прерывание будет четко фиксировать нажатие кнопки ? У вас же получается не логический уровень, а непонятное состояние входа.
Почему вы считаете , что прерывание будет четко фиксировать нажатие кнопки ? У вас же получается не логический уровень, а непонятное состояние входа.
Поясните - почему нет?
Вроде пояснил уже. Ладно давайте так. По даташиту прерывания осуществляется изменением логического уровня. Логический уровень это 0 или 1 которым соответствуют определенные диапазоны вольтажа, в даташите написано конкретнее, мне леньтуда лезть. Все что не попадает в диапазон ноля или единицы является неопределенным состоянием, на которое процессор не обязан реагировать.
Например, по схеме нажатие средней кнопки сформирует на ноге int1 уровень 3.3333 вольта. Как вы считаете это 0 или 1 ?
Если уж очень хочется работать такой схемой, то оторвите землю с r7 . На оторванную ногу переключите int1. Оторвите питание с ног резисторов, как раз туда воткните землю. При нажатии любой кнопки на int 1 сформируется 0. Сработает прерывание. Переключите ногу int1 на выход и выдайте на нее 1. Считайте аналоговое значение с adc5. Полученное значение будет соответствовать нажатой кнопке. Верните все в прежнее положение. Ожидайте отпускания кнопки . Итд
Свой косяк понял. Буду дорабатывать. brokly, спасибо!
Добавил транзистор. Вольтметр в протеусе показывает 5 вольт при отпущенной кнопке на входе прерывания. При нажатой кнопке 0 вольт. Поменялся фронт прерывания: при нажатии кнопки - по заднему, при отпускании - по переднему.
p.s. в транзисторах я почти ноль.
С моей точки зрения это лишнее усложнение. Не люблю дискретных элементов в цифровых схемах. Думаю r4 следует уменьшить, мегаом тут явный перебор. Будет нестабильно работать, возбуждаться и прочее. Да и вот еще транзистор - аналоговый элемент. У вас в настройках шпрота наверняка эмуляция установлена цифровая. Если измените на аналоговую получите большее приближение к жизни.
R2 можно убрать и включить подтяжку на ноге int1