Подключение клавишной панели к микроконтроллеру AVR ATmega32

В этой статье мы рассмотрим подключение клавишной панели 4х4 (16 кнопок) к микроконтроллеру ATmega32 (семейство AVR). Клавишная панель (кейпад) является одним из важнейших устройств ввода в электронных схемах, она предоставляет возможности самого простого ввода различных команд и инструкций в электронные схемы.

Подключение клавишной панели к ATmega32: внешний вид

Необходимые компоненты

Аппаратное обеспечение

Микроконтроллер ATmega32
Источник питания с напряжением 5 Вольт
Программатор AVR-ISP, USBASP или другой подобный
JHD_162ALCD (ЖК дисплей 16×2)
Конденсатор 100 мкФ
Конденсатор 100 нФ
Резистор 10 кОм (8 шт.)
Клавишная панель 4х4

Программное обеспечение

Atmel Studio версии 6.1 (или выше)
Progisp или flash magic (необязательно)

Работа схемы

Схема устройства приведена на следующем рисунке.

Схема подключения клавишной панели к ATmega32

В представленной схеме PORTB микроконтроллера ATmega32 соединен с портом данным жидкокристаллического (ЖК) дисплея. При этом следует помнить о том, что необходимо деактивировать JTAG интерфейс микроконтроллера на порту PORTC при помощи изменения фьюзов (fuse bytes) если мы хотим использовать PORTC как обычный порт ввода/вывода. В ЖК дисплее (если мы не хотим использовать черный цвет) можно задействовать только 14 его контактов: 8 контактов для передачи данных (7-14 или D0-D7), 2 контакта для подачи питания (1&2 или VSS&VDD или gnd&+5v), 3-й контакт для управления контарстностью, 3 контакта для управления (RS&RW&E).

В представленной схеме мы использовали только 2 контакта управления ЖК дисплея для лучшего понимания работы схемы. Бит контраста и READ/WRITE используются нечасто, поэтому они могут быть замкнуты на землю. Это обеспечивает ЖК дисплею максимальную контрастность и переводит его в режим чтения. Теперь нам всего лишь нужно контролировать контакты ENABLE и RS чтобы передавать на ЖК дисплей символы и данные. Также на нашем сайте вы можете прочитать более подробную статью о подключении ЖК дисплея к микроконтроллеру ATmega32.

В схеме необходимо сделать следующие соединения с ЖК дисплеем:
PIN1 или VSS — земля
PIN2 или VDD или VCC — +5v питание
PIN3 или VEE — земля (обеспечивает максимальный контраст ЖК дисплею)
PIN4 или RS (Register Selection) – контакт PD6 микроконтроллера
PIN5 или RW (Read/Write) — земля (переводит ЖК дисплей в режим чтения что упрощает взаимодействие с ним для начинающих)
PIN6 или E (Enable) — контакт PD5 микроконтроллера
PIN7 или D0 — контакт PB0 микроконтроллера
PIN8 или D1 — контакт PB1 микроконтроллера
PIN9 или D2 — контакт PB2 микроконтроллера
PIN10 или D3 — контакт PB3 микроконтроллера
PIN11 или D4 — контакт PB4 микроконтроллера
PIN12 или D5 — контакт PB5 микроконтроллера
PIN13 или D6 — контакт PB6 микроконтроллера
PIN14 или D7 — контакт PB7 микроконтроллера

В схеме мы использовали 8-битную связь (D0-D7) ЖК дисплея с микроконтроллером, хотя можно было ограничиться и 4-битной – но в этом случае код программы стал бы немного сложнее. Таким образом, мы использовали 10 контактов ЖК дисплея, 8 из которых будут использоваться для передачи данных и 2 для управления.

Принципы работы клавишной панели

Клавишная панель, по сути, представляет собой мультиплексор («уплотнитель») клавиш. Кнопки на ней соединены в мультиплексированной форме чтобы уменьшить количество используемых контактов.

Если рассматривать случай без мультиплексирования (уплотнения), то для работы клавишной панели 4х4 (16 кнопок) нам бы понадобилось 16 контактов, но это не очень хорошо с точки зрения управления подобной системой. Уменьшение числа используемых контактов достигается соединением кнопок клавишной панели в мультиплексированной (объединенной) форме.

К примеру, мы имеем 16 кнопок и мы хотим присоединить к ним контроллер чтобы получить клавишную панель, пусть эти кнопки упорядочены следующим образом, показанным на рисунке.

Структура клавишной панели

Объединим эти кнопки по столбцам как показано на следующем рисунке.

Объединение кнопок в клавишной панел по столбцам

Как показано на рисунке, немаркированные концы всех четырех кнопок в столбце объединены в одно соединение, таким образом для 16 кнопок мы имеем 4 столбца.

Вместо объединения по столбцам можно использовать и объединение по строкам как показано на следующем рисунке.

Объединение кнопок в клавишной панел по строкам

Если мы используем объединение одновременно и по строкам, и по столбцам, то мы получим схему, представленную на следующем рисунке.

Объединение кнопок в клавишной панел по строкам и по столбцам

Здесь мы имеем 16 кнопок в мультиплексированной форме чтобы уменьшить количество контактов клавишной панели. Если бы мы использовали вариант без мультиплексирования, то нам бы понадобилось для соединения с 16 кнопками использовать 16 контактов микроконтроллера, здесь же нам будет достаточно всего лишь 8 контактов.

Внешний вид подобной клавишной панели со шлейфом показан на следующем рисунке.

Внешний вид клавишной панели со шлейфом

Таким образом, рассматриваемая нами клавишная панель содержит четыре строки и четыре столбца, поэтому для идентификации нажатой кнопки мы будем использовать метод перекрестных связей. Сначала соединим все строки или все столбцы к общему источнику постоянного напряжения (vcc), получим что если, к примеру, что если все строки подсоединены к общему источнику постоянного напряжения, мы столбцы можем подсоединить ко входам микроконтроллера. Теперь если кнопка 1 клавишной панели нажата, то мы получим следующую картинку.

На клавишной панели нажата кнопка 1

Постоянный ток в этом случае будет протекать по клавишной панели как показано на следующем рисунке.

Путь протекания тока при нажатой кнопке 1

Таким образом, при нажатой кнопке 1 мы будем иметь на выходе C1 высокое напряжение. В этот момент мы собираемся переключить контакты, на которые подается постоянное напряжение, и контакты, соединенные со входами микроконтроллера, то есть как бы поменяем роль столбцов и строк. Эта ситуация показана на следующем рисунке.

Другой путь протекания тока

Теперь мы на выводе R1 имеем высокое напряжение.
Таким образом, мы имеем высокое напряжение на C1 в первом случае и высокое напряжение на R1 во втором случае, поэтому мы можем однозначно идентифицировать позицию в рассматриваемой нами матрице соединений и определить что была нажата кнопка 1.

Если будет нажата кнопка 2, то мы будем иметь сигнал высокого уровня на выходе C1 в первом случае и сигнал высокого уровня на выходе R2 во втором случае. Сопоставляя эти факта (то есть наличие сигнала высокого уровня на C1 и R2), мы однозначно можем определить что была нажата кнопка 2.

Как видно из представленной схемы устройства, у нас 8 выходов (контактов) клавишной панели соединены с 8 входами (контактами) микроконтроллера. Вначале мы подадим высокое напряжение на контакты, соответствующие строкам клавишной панели, в это время столбцы клавишной панели будут рассматриваться как выходы. Во время нажатия кнопки соответствующий контакт в столбце будет иметь высокий логический уровень (напряжения) и на соответствующем ему входе микроконтроллера также будет высокий уровень напряжения, поэтому мы однозначно сможем определить тот факт, что была нажата какая-нибудь кнопка на панели. В этот момент мы можем переключить источник постоянного напряжения со строк на столбцы и в результате по изменению напряжению в соответствующей строке (а номер столбца, соответствующий нажатой кнопке, у нас уже есть) однозначно определить какая именно кнопка клавишной панели была нажата.

Номер нажатой кнопки высвечивается на экране ЖК дисплея. Далее мы рассмотрим программу, реализующую обработку нажатий кнопок клавишной панели.

Исходный код программы на языке C (Си) с пояснениями

Программа для рассматриваемой схемы представлена следующим фрагментом кода на языке C (Си). Комментарии к коду программу поясняют принцип работы отдельных команд.

#include <avr/io.h> // заголовок чтобы разрешить контроль данных на контактах
#define F_CPU 1000000 // задание тактовой частоты микроконтроллера
#include <util/delay.h> // заголовок чтобы задействовать функции задержки в программе
#define E 5 // задействуем 5-й контакт PORTD (“enable”), поскольку он соединен с контактом “enable” ЖК дисплея
#define RS 6 // задействуем выбор регистра (“registerselection”) на 6-м контакте PORTD, поскольку он соединен с контактом RS ЖК дисплея

void send_a_command(unsigned char command);
void send_a_character(unsigned char character);
void send_a_string(char *string_of_characters);
int main(void)
{
DDRB = 0xFF; // установка portB на вывод данных
DDRD = 0xFF; // установка portD на вывод данных
_delay_ms(50); //задержка 50ms
int key=0;// переменная чтобы осуществить сброс ЖК дисплея когда количество символов на его экране достигнет лимита
int keypressed=0;// переменная целого типа для хранения значения матрицы (нажатой кнопки)

send_a_command(0x01); // очистить экран 0x01 = 00000001
_delay_ms(50);
send_a_command(0x38);// сообщаем ЖК дисплею что мы будем использовать 8 битный режим передачи данных/команд
_delay_ms(50);
send_a_command(0b00001111); //включаем курсор и мигание курсора на ЖК дисплее
send_a_string("PRESS A KEY"); //отображение строки "PRESS A KEY" (нажмите клавишу)
send_a_command(0x80 + 0x40 +0);// перемещение курсора на вторую строку ЖК дисплея
DDRA=0xF0;// назначаем контакты столбцов входными, а контакты строк выходными
_delay_ms(1);
PORTA=0x0F;// подаем напряжение на строки, то есть переключаем напряжение со столбцов на строки клавишной панели
_delay_ms(1);

while(1)
{
if (PINA!=0b11110000) // если в течение цикла на каком-нибудь контакте столбца появится высокое напряжение
{
_delay_ms(5);
keypressed = PINA; // номер столбца записываем в переменную
DDRA ^=0b11111111;// делаем строки входными контактами, а столбцы - выходными
_delay_ms(1);
PORTA ^= 0b11111111;// подаем напряжение на столбцы
_delay_ms(1);
keypressed |=PINA; // применяем операцию "или" к полученным значениям строки и столбца

if (keypressed==0b00010001)
{
send_a_string("1"); //если строка 1 и столбец 1 высокого напряжения, то показать “1”
key++;
}
if (keypressed==0b00010010)
{
send_a_string("4"); //если строка 1 и столбец 2 высокого напряжения, то показать “4”
key++;
}
if (keypressed==0b00010100)
{
send_a_string("7"); //если строка 1 и столбец 3 высокого напряжения, то показать “7”
key++;
}
if (keypressed==0b00011000)
{
send_a_string("*");//если строка 1 и столбец 4 высокого напряжения, то показать “*”
key++;
}

if (keypressed==0b00100001)
{
send_a_string("2"); //если строка 2 и столбец 1 высокого напряжения, то показать “2”
key++;
}
if (keypressed==0b00100010)
{
send_a_string("5"); //если строка 2 и столбец 2 высокого напряжения, то показать “5”
key++;
}
if (keypressed==0b00100100)
{
send_a_string("8"); //если строка 2 и столбец 3 высокого напряжения, то показать “8”
key++;
}
if (keypressed==0b00101000)
{
send_a_string("0"); //если строка 2 и столбец 1 высокого напряжения, то показать “0”
key++;
}

if (keypressed==0b01000001)
{
send_a_string("3");
key++;
}
if (keypressed==0b01000010)
{
send_a_string("6");
key++;
}
if (keypressed==0b01000100)
{
send_a_string("9");
key++;
}
if (keypressed==0b01001000)
{
send_a_string("#");
key++;
}

if (keypressed==0b10000001)
{
send_a_string("A");
key++;
}
if (keypressed==0b10000010)
{
send_a_string("B");
key++;
}
if (keypressed==0b10000100)
{
send_a_string("C");
key++;
}
if (keypressed==0b10001000)
{
send_a_string("D");
key++;
}
keypressed=0;// после того как показали нажатую клавишу на экране очистим переменную, хранящую информацию о номере строки и номере столбца
DDRA ^=0b11111111;// переключение входных и выходных портов
_delay_ms(1);
PORTA ^= 0b11111111;// подача питания на строки клавишной панели
_delay_ms(220);
}
if (key==16) // если 16 символов показаны на ЖК дисплее
{
send_a_command(0x01);// очистить экран ЖК дисплея
send_a_string("PRESS A KEY"); //отображение строки "PRESS A KEY" (нажмите клавишу)
send_a_command(0x80 + 0x40 +0); // перемещение курсора на вторую строку ЖК дисплея
key=0;
}
}
}

void send_a_command(unsigned char command)
{
PORTA = command;
PORTD &= ~ (1<<RS); // устанавливаем RS в 0 чтобы сообщить ЖК дисплею что мы будем передавать команду
PORTD |= 1<<E; // сообщаем ЖК дисплею чтобы он принял команду/данные
_delay_ms(50);
PORTD &= ~1<<E; // сообщаем ЖК дисплею что мы закончили передачу данных
PORTA= 0;
}

void send_a_character(unsigned char character)
{
PORTA= character;
PORTD |= 1<<RS; // сообщаем ЖК дисплею что мы будем передавать данные (не команду)
PORTD |= 1<<E; // сообщаем ЖК дисплею чтобы он начал прием данных
_delay_ms(50);
PORTD &= ~1<<E;// сообщаем ЖК дисплею что мы закончили передачу данных
PORTA = 0;
}
void send_a_string(char *string_of_characters)
{
while(*string_of_characters > 0)
{
send_a_character(*string_of_characters++);
}
}

Видео, демонстрирующее работу схемы


(1 голосов, оценка: 5,00 из 5)
Загрузка...


Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *