Подключение матричной мембранной клавиатуры к Arduino
Подключение матричной мембранной клавиатуры к Arduino
В этой статье расскажу как работает матричная мембранная клавиатуру и приведу простой пример подключения клавиатуры к Arduino с отображением нажатий клавиш на последовательном мониторе.
Технические параметры:
► Разъём: PLS-8;
► Тип шлейфа: плоский, гибкий, 8 pin;
► Длина шлейфа: 75 мм;
► Размер: 77×69×2 мм;
► Масса: 7 г.
Обзор матричной мембранной клавиатуры
В статье пойдет речь о матричной мембранной клавиатуре на 4×4. Так же существуют клавиатуры 4 × 3, 4 × 1 и другие, в не зависимости от размера, все они работают одинаково. Как видно из фото, на передней части располагается сами кнопки, а на обратной стороне приклеена липкая основа, которая позволяет приклеить клавиатуру к любой поверхности.
Принцип работы матричная клавиатура
В матричной мембранной клавиатуре 4 x 4 (4 столбца и 4 строки) содержится 16 кнопок, под каждой кнопкой находится мембранная переключатель. Все эти кнопки соединены с друг другом, образуя матрицу 4 × 4, это позволило уменьшит количество используемых выводов для подключения к микроконтроллеры (Arduino) с 17 контактов до 8 контактов.
Принцип работы очень простой, при нажатие кнопки замыкается контакт между столбцом и строкой, между ними начинает течь ток. Например, при нажатии клавиши «4» происходит короткое замыкание столбца 1 и строки 2. Схема клавиатуры 4 x 4 показана на рисунке ниже.
Подключение матричной мембранной клавиатура к Arduino
Необходимые детали:
► Arduino UNO R3 x 1 шт.
► Матричная мембранная клавиатура 4х4, 16 кнопок x 1 шт.
► Провод DuPont, 2,54 мм, 20 см, F-F x 1 шт.
► Кабель USB 2.0 A-B x 1 шт.
Подключение:
Теперь приступим к сборке схемы, подключим контакта 1 клавиатуры к цифровому контакту 9 на Arduino. Далее необходимо подключить все остальные контакты 2 с 8 и так далее.
Установка библиотек:
В данном примере используем одну библиотеку «Keypad.h» скачать ее можно с «Менеджер библиотек» или в конце статьи.
В строке поиска вводим «Keypad» находим библиотеку «Keypad by Mark Stanley» и устанавливаем ее.
Программа:
Пример простой, копируем или скачиваем и загружаем в Arduno.
Прерывания и многозадачность в Arduino
Отсутствие на Arduino операционной системы совершенно не означает, что невозможно решить проблему многозадачности для этого контроллера. Для этого просто нужна своеобразная методика, частью которой является использование прерываний.
Это могут быть прерывания по таймеру или внешние прерывания, уведомляющие о событиях, воздействующих на систему извне.
Прерывания и их источники
В процессе работы управляющий процессор выполняет определенные операции, а прерывание вызывает их остановку и в соответствие с кодом заставляет выполнить операции с более высоким приоритетом.
Проще говоря, прерывания – это набор приоритетов для тех или иных процессов, исполняемых контроллером.
Этот процесс имеет название обработчик прерываний и дает возможность присоединить к себе определенную функцию. Она будет вызываться при поступлении сигнала прерывания. Процессор, вернувшись из обработчика, приступит к выполнению тех операций, в процессе исполнения которых поступил сигнал прерывания.
Источником сигнала прерывания может стать таймер Arduino или процесс изменения состояния одного из контактов (пинов). Еще одним источником может стать какой-либо вход внешних прерываний: нужный сигнал появится при изменении его состояния.
Прерывание можно заставить работать с помощью кода, который поможет отвечать на прерывание. При этом нет необходимости писать код в loop (), который используют для систематической проверки приоритета прерывания.
В момент получения сигнала процессор автоматически остановится, вызвав обработчик. При этом нет нужды беспокоиться о времени отклика на нажатие кнопки или продолжительной реакции на выполняемую программу.
Особенности прерывания по таймеру
Как же управлять временем и запускать процессы в нужном вам порядке? При работе с микросхемой Arduino для этого можно использовать millis(), ее эффективность зависит от постоянного обращения к ней. Тогда, в случае вызова этой функции, можно будет понять – наступило время определенной операции или нет.
Добиться эффективности можно при вызове millis() несколько раз в миллисекунду, но это довольно расточительно. Для вызова функции раз в миллисекунду (что является оптимальным вариантом) необходимо использовать таймер. Его можно установить на интервал в миллисекунду и добиться желаемого результата.
Микроконтроллер Arduino Uno укомплектован тремя таймерами: из них timer0 предназначен для генерации прерываний с интервалом в одну миллисекунду. При этом будет постоянно обновляться счетчик, передающий информацию функции millis(). Вести точный подсчет таймеру позволяет определенная частота, получаемая из 16 МГц процессора.
Arduino, при необходимости, позволяет произвести конфигурацию делителя частоты с подбором оптимального режима счета.
Функция timer0 оперирует тактовым делителем на 64 и изменять это значение не стоит. Оно позволяет получить частоту прерывания, близкую к 1 кГц, оптимальную для целей большинства схемотехников. Если попытаться изменить данный параметр, то можно нарушить работу функции millis().
Регистры сравнения и их роль в генерации прерывания
Регистры сравнения выполняют функцию анализа хранимых данных с текущим состоянием счетчика прерывания. Например, регистр сравнения (OCR0A) эффективно применяется для прерывания в середине счета.
Программный код, пример которого приведен ниже, позволит генерировать функцию TIMER0_COMPA при прохождении счетчика xAF:
Для оптимизации работы кода и микроконтроллера следует полностью отказаться от loop(). Для этого необходимо определение по вектору обработчика прерывания с помощью функции TIMER0_COMPA_vect. Благодаря ей обработчик и будет выполнять все те операции, что раньше делались в loop().
Данный код позволит вернуться к использованию функции delay(), благодаря чему все классические мерцающие светодиоды или работающие сервоприводы будут функционировать без проблем, но при этом мы получим удобный и практичный таймер.
Какие внешние воздействия вызывают прерывание
Вызвать внешние прерывания могут определенные действия из внешней среды. Это может быть простым нажатием на кнопку или срабатыванием используемого датчика. При этом нет необходимости вести постоянный опрос вывода GPIO о происходящих изменениях.
Микроконтроллер Arduino может иметь несколько пинов, способных обрабатывать внешние прерывания. Так, на плате Arduino Uno их два, а на Arduino Mega 2560 – 6. Продемонстрировать их функционал можно с помощью кнопки сброса сервопривода. Для этого при написании кода в класс Sweeper необходимо добавит функцию reset(). Она способна установить нулевое положение и перетаскивать в нее сервопривод.
Соединить обработчик с внешним прерыванием поможет другая функция – attachInterrupt(). На приведенных в качестве примеров микроконтроллерах Interrupt0 реализована на втором контакте. Она сообщает микроконтроллеру о том, что на данном входе ожидается спад сигнала.
Достаточно нажать кнопку и сигнал действительно падает до минимального уровня, вызывая обработчик Reset.
В результате, при нажатии кнопки – сервоприводы сбрасываются, возвращаясь в нулевое положение. Схема внешних прерываний простая, и с помощью приведенных в примере скетчей, можно будет получить довольно легко желаемый результат.
Полный код программы с таймерами и внешними прерываниями:
Библиотеки прерываний
У схемотехников, благодаря Всемирной паутине есть доступ к широкому кругу библиотек, которые существенно облегчат работу с таймерами. Большинство из них предназначены для тех, кто применяет функцию millis(), но есть и такие, которые позволяют произвести желаемую настройку таймеров и сгенерировать прерывания. Оптимальным вариантом для этого являются библиотеки TimerThree и TimerOne, разработанные Paul Stoffregan.
Благодаря им, схемотехники получают широкий выбор возможностей, с помощью которых можно сконфигурировать прерывания с помощью таймера. Первая из этих библиотек не работает с Adruino Uno, но прекрасно зарекомендовала себя с платами Teensy, микроконтроллерами Adruino Mega2560 и Adruino Leonardo.
Недостатком Adruino Uno является наличие всего двух ходов, предназначенных для работы с внешними прерываниями. Если требуется большее количество подобных пинов, то отчаиваться не стоит, ведь этот микроконтроллер поддерживает pin-change – прерывания по изменению входа и работает это на всех восьми входах.
Их отличие от простых внешних прерываний – в сложности обработки, так как схемотехнику требуется отслеживать последнее из известных состояний пинов. Только в этом случае можно будет понять, какой из них вызвал прерывание.
Наиболее информативное и практичной библиотекой для прерываний по изменению входа является PinChangeInt.
Прерывания: основные правила работы
В ходе реализации проекта может потребоваться несколько прерываний, но если каждое из них будет иметь максимальный приоритет, то фактически его не будет ни у одной из функций. По этой же причине не рекомендуется использовать более десятка прерываний.
Обработчики должны применяться только к тем процессам, которые имеют максимальную чувствительность ко временным интервалам. Не стоит забывать, что пока программа находится в обработчике прерывания – все другие прерывания отключены. Большое количество прерываний ведет к ухудшению их ответа.
В момент, когда действует одно прерывание, а остальные отключаются, возникает два важных нюанса, которые должен учитывать схемотехник. Во-первых, время прерывание должно быть максимально коротким.
Это позволит не пропустить все остальные запланированные прерывания. Во-вторых, при обработке прерывания программный код не должен требовать активности от других прерываний. Если этого не предотвратить, то программа просто зависнет.
Не стоит использовать длительную обработку в loop(), лучше разработать код для обработчика прерывания с установкой переменной volatile. Она подскажет программе, что дальнейшая обработка не нужна.
Если вызов функции Update() все же необходим, то предварительно необходимо будет проверить переменную состояния. Это позволит выяснить, необходима ли последующая обработка.
Перед тем, как заняться конфигурацией таймера, следует произвести проверку кода. Таймеры Anduino стоит отнести к ограниченным ресурсам, ведь их всего три, а применяются они для выполнения самых разных функций. Если запутаться с использованием таймеров, то ряд операций может просто перестать работать.
Какими функциями оперирует тот или иной таймер?
Для микроконтроллера Arduino Uno у каждого из трех таймеров свои операции.
Так Timer0 отвечает за ШИМ на пятом и шестом пине, функции millis(), micros(), delay().
Другой таймер – Timer1, используется с ШИМ на девятом и десятом пине, с библиотеками WaveHC и Servo.
Timer2 работает с ШИМ на 11 и 13 пинах, а также с Tone.
Схемотехник должен позаботиться о безопасном использовании обрабатываемых совместно данных. Ведь прерывание останавливает на миллисекунду все операции процессора, а обмен данных между loop() и обработчиками прерываний должен быть постоянным. Может возникнуть ситуация, когда компилятор ради достижения своей максимальной производительности начнет оптимизацию кода.
Результатом этого процесса будет сохранение в регистре копии основных переменных кода, что позволит обеспечить максимальную скорость доступа к ним.
Недостатком этого процесса может стать подмена реальных значений сохраненными копиями, что может привести к потере функциональности.
Чтобы этого не произошло нужно использовать переменную voltatile, которая поможет предотвратить ненужные оптимизации. При использовании больших массивов, которым требуются циклы для обновлений, нужно отключить прерывания на момент этих обновлений.
Варианты частот для timer_init_ISR_XYHz
(вызов timer_init_ISR_1MHz тоже есть, но он не даёт рабочий результат ни на одном из тестовых контроллеров)
Код прерывания, очевидно, должен выполняться достаточно быстро для того, чтобы успеть завершиться до следующего вызова прерывания и, желательно, еще оставить немного процессорного времени для выполнения главного цикла.
Полагаю, излишне пояснять, что чем выше частота таймера, тем меньше период вызова прерываний, тем быстрее должен выполняться код обработчика. Я бы не рекомендовал помещать в него вызовы блокирующих задержек delay, циклы с неизвестным заранее количеством итераций, любые другие вызовы с плохо предсказуемым временем выполнения (в том числе Serial.print).
Создание счётчика кликов
Сначала создал в HTML-коде три кнопки с разным текстом, на которых и проверяется работа кода счётчика.
После подключил библиотеку jQuery отдельным файлом, и прописал JavaScript-код внутри кода этой страницы, который и считает сделанные клики.
Prim* Этот, написанный, JavaScript-код в отдельный файл НЕ ВЫРЕЗАТЬ! Перестанет работать!
Настройка размера, жирности и цвета цифр и рамок у полей вывода результатов производится атрибутом «style» в блоке вывода у каждого из тэгов «span» HTML-кода этой страницы.
Если в управляющем коде вместо строки: $(‘.counter’).eq($(this).index()).text($(this).data(‘counter’)); которая прописана сейчас, прописать другую, попроще, то результат кликов будет выводится уже по-другому!
Если, вместо показанной выше строчки кода, написать такую: alert($(this).data(‘counter’)); тогда результат кликов будет выводиться во всплывающем окне. Тоже неплохо!
Однако, пользователю будет не особо ясно, результат кликов по какой именно(?!) кнопке оно показывает.
* Обе строчки скрипта срабатывают ПРАВИЛЬНО! *
Но всё же вариант, использованный в управляющем коде сейчас, гораздо лучше. Понятнее!
Естественно, что таким способом можно создать счётчики кликов для любых веб-элементов, а не только для тестовых кнопок, как я сделал на этой странице тестового сайта.
Программа для работы с кнопкой на Ардуино
Наконец, мы разобрались с нюансами нашей схемы, и готовы к написанию программы. В уроке по зажиганию светодиода мы познакомились с функциями настройки выводов pinMode и функцией вывода в цифровой порт digitalWrite. На этот раз нам понадобится ещё одна важная функция, которая обеспечивает ввод информации в микроконтроллер:
Эта функция возвращает логическое значение, которое Ардуино считала с заданного контакта. Это означает, что если на контакт подать напряжение +5В, то функция вернет истину*. Если контакт соединить с землей, то получим значение ложь. В языке C++, истина и ложь эквивалентны числам 1 и 0 соответственно.
Для того, чтобы интересующий нас контакт заработал в режиме ввода информации, нам нужно будет установить его в определенный режим:
Наконец, соберем всё вместе, и напишем программу.
Загружаем программу на Ардуино Уно, и проверяем работу программы. Если всё сделано правильно, должно получиться как на картинке:
Ну вот и всё. Теперь мы можем управлять нашими устройствами при помощи кнопок. Если вы уже прошли урок по подключению ЖК дисплея, то мы вполне сможем сделать часы с будильником!
Как пользоваться прибором начинающим
Для использования осциллографа на базе Ардуино потребуется собрать прибор, подключить его и загрузить необходимое программное обеспечение. Затем загрузить код. После этого нужно пустить сигнал от источника. В программе необходимо выбрать нужный порт и открыть каналы. Сигнал появится на экране устройства.
Осциллограф на Ардуино довольно простой прибор, но дающие необходимые показатели
Переключение режимов с помощью кнопки
Для того, чтобы определить, была ли нажата кнопка, надо просто зафиксировать факт ее нажатия и сохранить признак в специальной переменной.
Факт нажатия мы определяем с помощью функции digitalRead(). В результате мы получим HIGH (1, TRUE) или LOW(0, FALSE), в зависимости от того, как подключили кнопку. Если мы подключаем кнопку с помощью внутреннего подтягивающего резистора, то нажатие кнопки приведет к появлению на входе уровня 0 (FALSE).
Для хранения информации о нажатии на кнопку можно использовать переменную типа boolean:
boolean keyPressed = digitalRead(PIN_BUTTON)==LOW;
Почему мы используем такую конструкцию, а не сделали так:
boolean keyPressed = digitalRead(PIN_BUTTON);
Все дело в том, что digitalRead() может вернуть HIGH, но оно не будет означать нажатие кнопки. В случае использования схемы с подтягивающим резистором HIGH будет означать, что кнопка, наоборот, не нажата. В первом варианте (digitalRead(PIN_BUTTON)==LOW ) мы сразу сравнили вход с нужным нам значением и определили, что кнопка нажата, хотя и на входе сейчас низкий уровень сигнала. И сохранили в переменную статус кнопки. Старайтесь явно указывать все выполняемые вами логические операции, чтобы делать свой код более прозрачным и избежать лишних глупых ошибок.
Как переключать режимы работы после нажатия кнопки?
Часто возникает ситуация, когда мы с помощью кнопок должны учитывать факт не только нажатия, но и отпускания кнопки. Например, нажав и отпустив кнопку, мы можем включить свет или переключить режим работы схемы. Другими словами, нам нужно как-то зафиксировать в коде факт нажатия на кнопку и использовать информацию в дальнейшем, даже если кнопка уже не нажата. Давайте посмотрим, как это можно сделать.
Логика работы программы очень проста:
- Запоминаем факт нажатия в служебной переменной.
- Ожидаем, пока не пройдут явления, связанные с дребезгом.
- Ожидаем факта отпускания кнопки.
- Запоминаем факт отпускания и устанавливаем в отдельной переменной признак того, что кнопка была полноценно нажата.
- Очищаем служебную переменную.
Как определить нажатие нескольких кнопок?
Нужно просто запомнить состояние каждой из кнопок в соответствующей переменной или в массиве ардуино. Здесь главное понимать, что каждая новая кнопка – это занятый пин. Поэтому если количество кнопок у вас будет большим, то возможно возникновение дефицита свободных контактов. Альтернативным вариантом является использование подключения кнопок на один аналоговый пин по схеме с резистивным делителем. Об этом мы поговорим в следующих статьях.