Клавиатуры находят широкое применение в качестве устройств ввода в разнообразных электронных проектах. Они позволяют осуществлять ввод как цифр, так и букв. В данной статье мы рассмотрим подключение матричной клавиатуры (клавишной панели) 4x4 к микроконтроллеру PIC16F877A.
Ранее на нашем сайте мы рассматривали подключение матричной клавиатуры к другим микроконтроллерам (платам):
Принципы работы матричной клавиатуры 4x4
В большинстве электронных проектов встраиваемой электроники используются клавиатуры с 9, 12 или 16 клавишами. Но если мы подключим 16-клавишную клавиатуру к микроконтроллеру обычным способом, то ее подключение займет 16 контактов ввода/вывода микроконтроллера и эти задействованные контакты уже не смогут выполнять каких либо других функций. 16 контактов для подключения клавиатуры – это очень много для микроконтроллеров, не обладающих большим числом доступных контактов.
Каким образом можно уменьшить это число контактов? Для этой цели можно использовать матричную или, как ее еще называют, шестнадцатеричную клавиатуру. К примеру, подобная клавиатура 4x4 задействует всего 8 контактов, 4 из которых подключены к ее строкам, а 4 – к столбцам. Поэтому ее подключение задействует всего 8 контактов микроконтроллера.
Как работает подобная клавиатура
Внешний вид и распиновка матричной клавиатуры 4x4 представлены на следующем рисунке.
Как видно из представленного рисунка, контакты X1, X2, X3 и X4 обозначают строки клавиатуры, а контакты Y1, Y2, Y3, Y4 – столбцы. Если мы подадим на контакты X логический 0 (low), а контакты столбцов сконфигурируем в режим ввода данных, то мы сможем считывать нажатия клавиш, при нажатии клавиши на соответствующем Y контакте будет логический 0.
Аналогичные процессы происходят и в других матричных клавиатурах размером nxn, например, 3x3, 6x6.
Допустим, в используемой нами матричной клавиатуре нажата клавиша 1 – ей соответствует строка X1 и столбец Y1. Если на X1 логический 0, то и на Y1 будет логический 0. Этим же самым образом мы сможем определить нажатие любой клавиши в строке X1, соответствующей столбцам Y1, Y2, Y3 и Y4. Аналогичным образом мы можем определить нажатия и других клавиш клавиатуры.
На представленном рисунке соответствующие пересечения строк и столбцов, соответствующие определенным клавишам, обозначены кругами зеленого цвета.
В нашем проекте мы будем использовать следующие настройки для подключения клавиатуры:
- мы будем использовать внутренние подтягивающие резисторы микроконтроллера;
- мы будем использовать опцию устранения дребезга контактов у кнопок.
Когда клавиши клавиатуры не нажаты, нам необходимо подавать на контакты Y1, Y2, Y3 и Y4 уровень high. Иначе мы не сможем обнаружить изменения логических уровней при нажатии клавиш. Но мы не можем сделать это поскольку данные контакты у нас используются в режиме ввода, а не вывода. Поэтому мы будем использовать внутренние подтягивающие регистры микроконтроллера и использовать эти контакты в слабом "подтягивающем" режиме. В данном режиме на контактах будет логический уровень high когда они будут в состоянии по умолчанию (default state).
Также практически для всех кнопок, включая и клавиши используемой нами клавиатуры, характерен так называемый эффект дребезга контактов, при котором одиночное нажатие клавиши воспринимается как несколько нажатий. Для борьбы с данным эффектом мы будем сначала обнаруживать нажатие клавиши, затем ждать несколько миллисекунд, снова проверять все еще нажата ли клавиша и если она все еще нажата мы будем "окончательно" считать что клавиша нажата, иначе мы ее будем считать не нажатой. Описанный нами процесс и будет составлять суть нашей процедуры борьбы с эффектом дребезга контактов.
Необходимые компоненты
- Микроконтроллер PIC16F877A (купить на AliExpress).
- Матричная клавиатура 4x4 (купить на AliExpress).
- Программатор PICkit 3 (купить на AliExpress).
- ЖК дисплей 16х2 (купить на AliExpress).
- Кварцевый генератор 20 МГц (купить на AliExpress).
- Конденсаторы 33 пФ (2 шт.) (купить на AliExpress).
- Резистор 4,7 кОм (купить на AliExpress).
- Потенциометр 10 кОм (купить на AliExpress).
- Адаптер питания на 5 V.
- Макетная плата.
- Соединительные провода.
Схема проекта
Схема подключения матричной клавиатуры 4x4 к микроконтроллеру PIC представлена на следующем рисунке.
В представленной схеме ЖК дисплей подключен к PORTD микроконтроллера PIC в 4-битном режиме. Матричная клавиатура подключена к контактам с RB0 по RB7 микроконтроллера.
Внешний вид собранной конструкции проекта показан на следующем рисунке.
Объяснение программы для микроконтроллера PIC
Полный код программы приведен в конце статьи, здесь же мы кратко рассмотрим его основные фрагменты.
Код программы достаточно простой, необходимо всего лишь понять принцип работы библиотеки для взаимодействия с матричной клавиатурой. В нашем проекте мы будем использовать библиотеки keypad.h и lcd.h.
Внутри библиотеки keypad.h мы будем подключать заголовочный файл xc.h. Клавиатура у нас подключена к порту PORTRB, индивидуальные контакты данного порта мы обозначим как строки (X) и столбцы (Y).
Также мы будем использовать две функции, одну для инициализации клавиатуры, которая будет задавать режимы работы контактов с клавиатурой (на ввод или вывод данных) и вторую для определения нажатой клавиши.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include <xc.h> #define _XTAL_FREQ 200000000 //Crystal Frequency, used in delay #define X_1 RB0 #define X_2 RB1 #define X_3 RB2 #define X_4 RB3 #define Y_1 RB4 #define Y_2 RB5 #define Y_3 RB6 #define Y_4 RB7 #define Keypad_PORT PORTB #define Keypad_PORT_Direction TRISB void InitKeypad(void); char switch_press_scan(void); |
Код функции switch_press_scan(void), используемый для определения нажатой клавиши, приведен в файле keypad.c. Данная функция будет возвращать нажатую клавишу.
1 2 3 4 5 6 7 |
char switch_press_scan(void) // Get key from user { char key = 'n'; // Assume no key pressed while(key=='n') // Wait untill a key is pressed key = keypad_scanner(); // Scan the keys again and again return key; //when key pressed then return its value } |
Далее представлен код функции keypad_scanner(void), используемой для считывания клавиши. На каждом шаге в этой функции мы будем подавать на строки X1, X2, X3 и X4 логический 0 и считывать состояние контактов Y1, Y2, Y3 и Y4. Для устранения эффекта дребезга контактов нажатых клавиш используется задержка. Функция будет возвращать нажатую клавишу. Когда никакая клавиша не нажата функция будет возвращать символ ‘n‘.
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 |
char keypad_scanner(void) { X_1 = 0; X_2 = 1; X_3 = 1; X_4 = 1; if (Y_1 == 0) { __delay_ms(100); while (Y_1==0); return '1'; } if (Y_2 == 0) { __delay_ms(100); while (Y_2==0); return '2'; } if (Y_3 == 0) { __delay_ms(100); while (Y_3==0); return '3'; } if (Y_4 == 0) { __delay_ms(100); while (Y_4==0); return 'A'; } X_1 = 1; X_2 = 0; X_3 = 1; X_4 = 1; if (Y_1 == 0) { __delay_ms(100); while (Y_1==0); return '4'; } if (Y_2 == 0) { __delay_ms(100); while (Y_2==0); return '5'; } if (Y_3 == 0) { __delay_ms(100); while (Y_3==0); return '6'; } if (Y_4 == 0) { __delay_ms(100); while (Y_4==0); return 'B'; } X_1 = 1; X_2 = 1; X_3 = 0; X_4 = 1; if (Y_1 == 0) { __delay_ms(100); while (Y_1==0); return '7'; } if (Y_2 == 0) { __delay_ms(100); while (Y_2==0); return '8'; } if (Y_3 == 0) { __delay_ms(100); while (Y_3==0); return '9'; } if (Y_4 == 0) { __delay_ms(100); while (Y_4==0); return 'C'; } X_1 = 1; X_2 = 1; X_3 = 1; X_4 = 0; if (Y_1 == 0) { __delay_ms(100); while (Y_1==0); return '*'; } if (Y_2 == 0) { __delay_ms(100); while (Y_2==0); return '0'; } if (Y_3 == 0) { __delay_ms(100); while (Y_3==0); return '#'; } if (Y_4 == 0) { __delay_ms(100); while (Y_4==0); return 'D'; } return 'n'; } |
В функции инициализации клавиатуры мы подключим внутренние подтягивающие резисторы на контактах микроконтроллера, к которым подключена клавиатура и установим режимы работы контактов: первые 4 контакта – на вывод данных, а последние 4 контакта – на ввод данных.
1 2 3 4 5 6 |
void InitKeypad(void) { Keypad_PORT = 0x00; // Set Keypad port pin values zero Keypad_PORT_Direction = 0xF0; // Last 4 pins input, First 4 pins output OPTION_REG &= 0x7F; } |
В основном тексте программы, приведенном ниже, мы настроим биты конфигурации микроконтроллера и подключим необходимые библиотеки. Затем в функции void system_init мы инициализируем клавиатуру и ЖК дисплей. И далее в основной функции программы main мы будем считывать нажатые клавиши клавиатуры при помощи функции switch_press_scan() и отображать считанную клавишу на экране ЖК дисплея.
Все необходимые файлы проекта можно скачать по этой ссылке.
Исходный код основной программы
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 |
/* * File: main.c * Author: Sourav Gupta * By:- circuitdigest.com * Created on April 13, 2018, 2:26 PM */ // PIC16F877A Configuration Bit Settings // 'C' source line config statements // CONFIG #pragma config FOSC = HS // Oscillator Selection bits (HS oscillator) #pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled) #pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled) #pragma config BOREN = ON // Brown-out Reset Enable bit (BOR enabled) #pragma config LVP = OFF // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3/PGM pin has PGM function; low-voltage programming enabled) #pragma config CPD = OFF // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off) #pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control) #pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off) #include <xc.h> #include <stdio.h> #include <string.h> #include "supporing_cfile/lcd.h" #include "supporing_cfile/Keypad.h" /* Hardware related definition */ #define _XTAL_FREQ 200000000 //Crystal Frequency, used in delay /* Other Specific definition */ void system_init(void); void main(void){ system_init(); char Key = 'n'; lcd_com(0x80); lcd_puts("CircuitDigest"); lcd_com(0xC0); while(1){ Key = switch_press_scan(); lcd_data(Key); } } /* * System Init */ void system_init(void){ TRISD = 0x00; lcd_init(); // инициализируем ЖК дисплей InitKeypad(); } |