AVR240: клавиатура 4x4, пробуждение AVR от нажатия
Добавил(а) microsin
В AVR240 [1] приведен пример организации клавиатурной матрицы 4x4, подключенной к микроконтроллеру AVR. Есть возможность введения энергосбережения в систему, когда микроконтроллер выводится из состояния сна при нажатии на кнопку клавиатуры. Основные возможности:
• Клавиатура из 16 кнопок на замыкание, организованных в матрицу 4x4 • Очень маленькое общее энергопотребление системы • Микроконтроллер AVR уходит в режим сна (Sleep Mode) и просыпается при нажатии кнопки • Минимальное количество внешних компонентов • Если это необходимо, можно добавить защиту от статического электричества (ESD) • Эффективный код • Прилагается пример кода для AT90S1200 • Описанный принцип легко применить для любого микроконтроллера AVR
[Общее описание]
В этом апноуте показан простой пример интерфейса с клавиатурой 4x4, разработанный для применения в переносных устройствах с батарейным питанием. Микроконтроллер AVR почти все свое время проводит в режиме экономии энергии (Power-down mode, или Sleep mode), просыпаясь при нажатии на кнопку клавиатуры. Для демонстрации факт пробуждения и детектирование нажатой клавиши отображается простой тест-программой, мигающей двумя светодиодами. Если нажата кнопка "0", то красный светодиод (RED LED) мигнет 10 раз. Все другие клавиши вызовут мигание зеленого светодиода (GREEN LED) столько раз, сколько написано на клавише (например, если нажата кнопка "C", то это соответствует шестнадцатеричному коду 0x0c или в десятичном виде 12, и GREEN LED мигнет 12 раз).
Рис. 1-1. Подключение к AVR клавиатурной матрицы и светодиодов.
[Немного теории - как это работает]
Столбцы матрицы клавиатуры подключены к старшему нибблу порта B (PB7..PB4). Строки матрицы клавиатуры подключены к младшему нибблу порта B (PB3..PB0). Резисторы R1 .. R8 (см. рис. 1-1) ограничивают входной ток и напряжение до безопасного уровня, что защищает AVR от действия статического электричества, которое могло бы быть случайно приложено к клавиатуре. Они могут быть выброшены в большинстве приложений.
В состоянии ожидания старший ниббл порта B (PB7..PB4) сконфигурирован как выходы, и на них выведен уровень лог. 0. Младший ниббл порта B (PB3..PB0) сконфигурирован как входы с разрешенными внутренними pull-up резисторами (что такое pull-up, как конфигурировать входы и выходы AVR см. [2, 3]). После завершения инициализации AVR переводится в режим сна. Когда нажата любая кнопка на клавиатуре, один из диодов D1..D4 откроется, и притянет к лог. 0 уровень входа внешнего прерывания PD2, на котором также разрешен внутренний резистор pull-up. Это событие разбудит AVR, и заставит его выполнить обработчик внешнего прерывания (interrupt service routine, ISR), которое просканирует клавиатуру и вычислит, какая клавиша была нажата.
После этого ISR вернет управление в основную программу, которая будет управлять светодиодом в соответствии нажатой клавише, и затем снова переведет AVR обратно в режим сна.
Резисторы R9 и R10 это традиционные токоограничивающие резисторы, чтобы установить для светодиодов номинальный ток. В этой схеме применены светодиоды 330?, рассчитанные на источник питания 5V (можно с успехом применить любые резисторы в диапазоне от 330? до 1k?). Светодиоды LEDx управляются втекающим током около 10mA (они загораются, когда на выходе PD0 или PD1 выведен лог. 0).
[Реализация]
Программное обеспечение микроконтроллера (firmware) состоит из 3 основных частей: код сброса (метка reset, см. листинг программы во врезке "Код программы на языке ассемблера"), тест-программа (метка flash) и обработчик прерывания (метка scan), которые настраивают порты, режим сна, энергосберегающий режим и прерывания. Тест-программа мигает светодиодами при пробуждении, и обработчик прерывания отвечает на клавиатурные нажатия.
Код сброса. Алгоритм программы сброса показан на рис. 3-1. В момент сброса (это происходит при включении питания) порты инициализируются своим начальным направлением (какие работают на вход, какие на выход). Направление фиксировано для порта D, у которого все разряды настроены как выход, кроме PD2, который должен быть входом для того, чтобы он фиксировал внешнее прерывание от нажатия на клавиатуре. На разряде PD2 также разрешен pull-up (внутренний верхний подтягивающий резистор) путем установки бита 2 регистра PORTD. Не используемые выводы сконфигурированы как выходы, чтобы избежать повышенного энергопотребления из-за шумовых входных сигналов, что было бы возможно, если оставить входы плавающими. У порта B старший ниббл настраивается как выходы в состоянии лог. 0, и младший ниббл как входы с разрешенными на входе резисторами pull-up.
Поскольку используется минимальное количество внешних компонентов, нужно гарантировать, что pull-up включены на всех настроенных входах. Направление работы порта настраивается через регистры DDRB и DDRD, и для разрядов выхода нужно записать в них лог. 1, а для разрядов входа лог. 0. Для входных разрядов нужно записать лог. 1 в соответствующие разряды регистров PORTB и PORTD. Входы можно опрашивать программно, читая содержимое регистров PINB и PIND. Эта программа ждет на входах появления лог. 0, и использует инструкцию SBIS, чтобы пропустить разряды при опросе клавиатуры, которые не находятся в состоянии лог. 0. Подробнее про работу с портами GPIO см. [2, 3].
Режим выключения (Power-down) выбирается установкой бит SE и SM в регистре MCUCR. В то же самое время внешнее прерывание конфигурируется записью нулей в биты ISC00 и ISC01. Это установит внешнее прерывание (external interrupt) INT0 для срабатывания на уровень лог. 0. Когда используется режим "Powerdown", AVR можно разбудить только по перепаду в лог. 0 на входе INT0 (разряд порта PD2).
Выключение аналогового компаратора (Analog Comparator) дополнительно снижает энергопотребление в режиме сна. Это осуществляется установкой бита ACD в регистре ACSR, что должно быть осуществлено с осторожностью; иначе возможна генерация нежелательного прерывания. Для этой цели в программе запрещаются глобальные прерывания, пока не будет полной готовности к обработке прерываний. Если Вы хотите использовать Analog Comparator, то код его запрещения можно удалить, но необходимо изменить ножки портов GPIO, используемые для подключения клавиатуры, поскольку два разряда порта B аппаратно используются как входы компаратора.
После всех действий по настройке, AVR вводится в режим сна (sleep mode). Это действие помещено в главный цикл программы (метка main), чтобы гарантировать, что микроконтроллер снова войдет в сон после завершения работы ISR и подпрограммы теста, которая мигает светодиодами. Когда AVR проснется от нажатия на клавиатуре, будет вызвана подпрограмма мигания (Flash) после завершения работы ISR. После завершения подпрограммы Flash снова разрешаются внешние прерывания от вывода PD2, так что может снова произойти другое прерывание.
Рис. 3-1. Алгоритм работы кода сброса и основной программы.
Функция тестирования Flash. Её алгоритм показан на рис. 3-2. Эта функция написана только для демонстрации, и может быть заменена на Ваше приложение, выполняющее нужные действия при выходе из режима Powerdown. It serves to demonstrate that the key scan routine is working correctly. Значение нажатой кнопки можно получить из переменной key, и использовать как указатель для доступа к 16-байтной таблице перекодирования, сохраненной в EEPROM. Таблица содержит коды нажатых кнопок.
Таблица использовалась по двум причинам: это позволяет сделать программу короче, и позволяет проще реализовать полное ASCII-кодирование нажатых кнопок. Для более мощных AVR таблицу можно хранить в памяти программ, и получать к ней доступ с помощью инструкции LPM.
Значение из таблицы, соответствующее нажатой кнопки, в данном примере используется как начальное значение переменной обратного отсчета в цикле включения/выключения выхода управления светодиодом. Если значение 0, то красный светодиод (RED LED) мигнет 10 раз. Если значение не 0, то зеленый светодиод (GREEN LED) мигнет столько раз, сколько будет прочитано из таблицы перекодирования в EEPROM. Например, 3 раза мигнет для кнопки 3, и 15 раз для кнопки F и т. п. Затем AVR повторит цикл и снова попадет в режим сна.
Подпрограмма мигания светодиодами может быть просто изменена для нужд Вашего приложения, можно заменить функцию Flash другим кодом. Основное внимание нужно уделить времени выполнения кода. Поскольку тест-программа тратит определенное время на задержки в формировании импульсов мигания, не требуются дополнительные усилия по подавлению дребезга контактов клавиатуры. Если же Ваш код работает очень быстро, то может потребоваться вставить небольшую задержку, чтобы дать время затихнуть импульсам дребезга контактов. Время пробуждения составляет обычно около 16 миллисекунд, хотя это время может быть уменьшено для новых моделей микроконтроллеров.
Рис. 3-2. Алгоритм работы функции мигания.
Подпрограмма формирования длинной задержки (delay). Чтобы мигания светодиода были хорошо заметны, нужна задержка как минимум 0.25 секунды. Это достигается подходящим циклом FOR, чтобы таймер/счетчик мог выполнять другую работу. Чтобы получилась задержка свыше 0.25 при тактовой частоте 4 МГц, требуется 3 вложенных цикла. Три локальные переменные содержатся в регистрах fine, medium и coarse, которые используются в цикле задержки. Счетчики fine (точный счетчик) и medium (средний по точности счетчик) могут содержать максимальное значение 255 при счетчике coarse (грубый счетчик), установленном в значение 5, что дает задержку около 0.25 секунды. Алгоритм формирования задержки показан на рис. 3-3.
Рис. 3-3. Алгоритм работы подпрограммы длинной задержки.
Обработчик прерывания (Interrupt Service Routine, ISR). Обычно состояние регистра статуса (Status Register) содержит контекст выполнения основной программы, поэтому этот регистр сохраняется на входе и восстанавливается на выходе из ISR, чтобы гарантировать надежную работу кода основной программы (main). В данном примере программы это может быть по желанию опущено. Алгоритм показан на рисунке 3-4.
Рис. 3-4. Алгоритм работы ISR.
Сначала детектируется строка клавиатуры, путем опроса на лог. 0 состояния каждого входа строки. Затем базовое число 0, 4, 8 или 12 присваивается переменной key. Затем порты переинициализируются, и направление работы GPIO порта B переключается.
Короткая задержка "settle" используется, чтобы дать время выводам установить свое состояние. Это делается как обычно, с помощью подходящего цикла FOR.
Столбец нажатой кнопки детектируется и записывается в переменную temp в виде одного из чисел 0, 1, 2 или 3. Конечное нажатие вычисляется путем сложения key и temp, с помещением результата в key, после чего можно выполнять функцию Flash.
Конфигурация входов/выходов порта B восстанавливается обратно перед восстановлением значения Status Register. Это экономит повторное использование задержки для установки уровней сигналов портов GPIO для строк и столбцов.
И наконец, запрещается внешнее прерывание. Это делается для того, чтобы избежать повторного срабатывания прерывания сразу после выхода из ISR.
Подпрограмма короткой задержки. Эта короткая задержка нужна, когда меняется конфигурация GPIO порта B, чтобы дать время на установление уровня ножек микроконтроллера. Подпрограмма использует общий временных регистр temp как одиночный счетчик для цикла FOR, и устанавливается на максимальное количество повторений цикла 255. Это дает задержку около 0.192 миллисекунд при использовании тактовой частоты 4 МГц. Значение задержки можно уменьшить экспериментально, если это имеет принципиальное значение для скорости работы Вашей программы. Возможно даже в некоторых случаях полностью убрать эту задержку.
[Ресурсы, используемые программой]
Таблица 3-1. Использование CPU и памяти AVR.
Функция
Размер кода
Количество циклов
Использование регистров
Прерывание
Описание
Main
24 слова
19
R16
-
Инициализация
Flash
20 слов
-
R16
-
Тестовый код - только для примера
Scan
31 слово
обычно 47
R16, R17, R21
INT0
Сканирование матрицы клавиатуры 4x4
Delay
10 слов
1000000
R18, R19, R20
-
Задержка на 0.25 сек
Settle
4 слова
764
R16
-
Задержка для установки значений уровней порта. Используется при сканировании матрицы клавиатуры.
ВСЕГО
87 слов
-
R16, R17, R18, R19, R20, R21
-
Примечание: одно слово команды AVR имеет размер 2 байта.
Таблица 3-2. Использование периферийных устройств AVR.
;***** Регистры, используемые всеми подпрограммами
.deftemp=r16;общий временный регистр
;Выводы порта B
.equROW1 =3;входы для строк клавиатуры
.equROW2=2
.equROW3=1
.equROW4=0
.equCOL1=7;выходы для столбцов клавиатуры
.equCOL2=6
.equCOL3=5
.equCOL4=4
;Выводы порта D
.equGREEN=0;зеленый светодиод (GREEN LED)
.equRED=1;красный светодиод (RED LED)
.equINTR=2;вход внешнего прерывания
.include"1200def.inc"
;***** Регистры, используемые ISR
.defkey=r17;указатель кнопки для таблицы в EEPROM
.defstatus=r21;в этом регистре сохраняется SREG
;***** Регистры, используемые как локальные переменные
;***** в подпрограмме формирования длинной задержки
.deffine=r18;счетчики задержки для циклов
.defmedium=r19
.defcoarse=r20
;***** Таблица для перекодирования кнопок *************************
.eseg;Сегмент EEPROM
.org0.db1,2,3,15,4,5,6,14,7,8,9,13,10,0,11,12
;**** Исходный код ************************************************
.cseg;Сегмент CODE.org0rjmpreset;Обработчик сбросаrjmpscan;ISRreti;обработчик таймера не используетсяreti;обработчик аналогового компаратора не используется
reset:lditemp,0xFB;инициализация порта D как GPIOoutDDRD,temp;все ножки будут выходами,; кроме PD2 для внешнего прерывания INT0lditemp,0x30;задать режим сна (sleep mode) и выключение (poweroutMCUCR,temp; down), и прерывание по низкому уровню.lditemp,0x40;разрешить внешние прерыванияoutGIMSK,tempsbiACSR,ACD;выключить компаратор для экономии энергии
main:cli;глобальный запрет прерыванийlditemp,0xF0;инициализация порта B как GPIOoutDDRB,temp; 4 выхода и 4 входаlditemp,0x0F;все выходы столбцов в лог. 0,outPORTB,temp;активировать pull up на строкахlditemp,0x07;разрешить pull up на PD2 иoutPORTD,temp;выключить светодиодыsei;разрешить прерыванияsleep;вход в режим снаrcallflash;запустить подпрограмму мигания светодиодамиlditemp,0x40outGIMSK,temp;разрешить внешние прерыванияrjmpmain;снова войти в сон после сканирования кнопок
scan:instatus,SREG;сохранить регистр статусаsbisPINB,ROW1;найти строку клавиатуры с нажатиемldikey,0;и установить указатель на строку ROWsbisPINB,ROW2ldikey,4sbisPINB,ROW3ldikey,8sbisPINB,ROW4ldikey,12lditemp,0x0F;поменять направление работы порта B,outDDRB,temp;чтобы найти столбец с нажатиемlditemp,0xF0;разрешить pull up иoutPORTB,temp;записать нули в строкиrcallsettle;дать время для установки сигналовsbisPINB,COL1;найти столбец с нажатиемlditemp,0;и установить указатель на столбец COLsbisPINB,COL2lditemp,1sbisPINB,COL3lditemp,2sbisPINB,COL4lditemp,3addkey,temp;сложить ROW и COL, чтобы получить полный указательlditemp,0xF0;переинициализировать обратно GPIO порта BoutDDRB,temp; 4 выхода, 4 входаlditemp,0x0F;все столбцы в лог. 0 и разрешить на строкахoutPORTB,temp;резисторы pull upoutSREG,status;восстановить регистр статусаlditemp,0x00outGIMSK,temp;запретить внешнее прерывание
;это необходимо, потому что здесь используется прерывание,
;срабатывающее не по перепаду сигнала, а по его уровнюreti;возврат обратно в main
;*** Пример тест-программы, которая мигает светодиодами **************
flash:outEEAR,key;адрес EEPROMsbiEECR,EERE;строб EEPROMintemp,EEDR;установить количество вспышекtsttemp;в ячейке 0?breqzero;если да, то мигаем красным светодиодом