В этой статье мы рассмотрим подключение клавишной панели 4х4 (16 кнопок) к микроконтроллеру ATmega32 (семейство AVR). Клавишная панель (клавиатура, кейпад) является одним из важнейших устройств ввода в электронных схемах, она предоставляет возможности самого простого ввода различных команд и инструкций в электронные схемы.
Необходимые компоненты
Аппаратное обеспечение
- Микроконтроллер ATmega32 (купить на AliExpress).
- Программатор AVR-ISP (купить на AliExpress), USBASP (купить на AliExpress) или другой подобный.
- JHD_162ALCD (ЖК дисплей 16x2) (купить на AliExpress).
- Клавишная панель (клавиатура) 4х4 (купить на AliExpress).
- Конденсатор 100 мкФ (купить на AliExpress).
- Конденсатор 100 нФ (купить на AliExpress).
- Резистор 10 кОм (8 шт.) (купить на AliExpress).
- Источник питания с напряжением 5 Вольт.
Программное обеспечение
- Atmel Studio версии 6.1 (или выше).
- Progisp или flash magic (необязательно).
Работа схемы
Схема устройства приведена на следующем рисунке.
В представленной схеме 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 мы будем иметь на выходе C1 высокое напряжение. В этот момент мы собираемся переключить контакты, на которые подается постоянное напряжение, и контакты, соединенные со входами микроконтроллера, то есть как бы поменяем роль столбцов и строк. Эта ситуация показана на следующем рисунке.
Теперь мы на выводе R1 имеем высокое напряжение.
Таким образом, мы имеем высокое напряжение на C1 в первом случае и высокое напряжение на R1 во втором случае, поэтому мы можем однозначно идентифицировать позицию в рассматриваемой нами матрице соединений и определить что была нажата кнопка 1.
Если будет нажата кнопка 2, то мы будем иметь сигнал высокого уровня на выходе C1 в первом случае и сигнал высокого уровня на выходе R2 во втором случае. Сопоставляя эти факта (то есть наличие сигнала высокого уровня на C1 и R2), мы однозначно можем определить что была нажата кнопка 2.
Как видно из представленной схемы устройства, у нас 8 выходов (контактов) клавишной панели соединены с 8 входами (контактами) микроконтроллера. Вначале мы подадим высокое напряжение на контакты, соответствующие строкам клавишной панели, в это время столбцы клавишной панели будут рассматриваться как выходы. Во время нажатия кнопки соответствующий контакт в столбце будет иметь высокий логический уровень (напряжения) и на соответствующем ему входе микроконтроллера также будет высокий уровень напряжения, поэтому мы однозначно сможем определить тот факт, что была нажата какая-нибудь кнопка на панели. В этот момент мы можем переключить источник постоянного напряжения со строк на столбцы и в результате по изменению напряжению в соответствующей строке (а номер столбца, соответствующий нажатой кнопке, у нас уже есть) однозначно определить какая именно кнопка клавишной панели была нажата.
Номер нажатой кнопки высвечивается на экране ЖК дисплея. Далее мы рассмотрим программу, реализующую обработку нажатий кнопок клавишной панели.
Исходный код программы на языке C (Си) с пояснениями
Программа для рассматриваемой схемы представлена следующим фрагментом кода на языке C (Си). Комментарии к коду программу поясняют принцип работы отдельных команд.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
#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++); } } |
Во всех условиях где if (keypressed== 0bxxxxxxxx) нужно поменять нули на единицы, а единицы на нули. При моделировании Proteus видно что потенциалы снижаются при нажатии. И при реальной прошивке МК код приведённый выше не работает.
Получается опечатка что ли в код программы закралась?
Да, 16 раз.
А ещё функции непонятно откуда взятые:
void send_a_command(unsigned char command);
void send_a_character(unsigned char character);
void send_a_string(char *string_of_characters);
Да и на клавиатуру можно питание не тащить, она и без него отрабатывает.
Ну это функции для взаимодействия с ЖК дисплеем
Добрый день!
Попробовал сделать данную схему. Залил прошивку (скопировал ваш код), но в протеус при нажатии на кнопки ничего не происходит. На экране не выводится даже надпись "PRESS A KEY". Использовал LM016L. Скажите, пожалуйста, что необходимо добавить или поменять, чтобы все заработало?
Добрый вечер. К сожалению я сталкивался с моделированием в протеусе только схем обычной электроники, без использования микроконтроллеров, поэтому я не знаю как заставить в протеусе исполняться программе на микроконтроллере. Могу здесь только посоветовать отладку программы по частям. Например, удалите из программы все кроме вывода приветственного сообщения на экран дисплея. Если это заработает, тогда пробуйте добавлять в программу другие элементы из представленного в данной статье кода. По сути, программа достаточно проста и прямолинейна. Не вижу причин чтобы она не работала. Ну если только опечатка в нее маленькая где-нибудь закралась