键盘是广泛用于各种电子和嵌入式项目的输入设备。它们用于以数字和字母的形式获取输入,并将其输入系统以进行进一步处理。在本教程中,我们将使用PIC16F877A 连接 4x4 矩阵键盘。
为什么我们需要 4x4 键盘:
通常,我们使用微控制器单元的单个I / O引脚来读取数字信号,例如开关输入。在少数需要 9、12、16 个键进行输入的应用中,如果我们在微控制器端口中添加每个键,我们最终将使用 16 个 I/O 端口。这 16 个 I/O 端口不仅用于读取 I/O 信号,还可以用作外设连接,如 ADC 支持、I2C、SPI 连接也由这些 I/O 引脚支持。由于这些引脚与开关/键连接,因此我们不能将它们用作I / O端口。这完全没有意义。那么,如何减少引脚数呢?答案是,使用十六进制键盘或矩阵键盘;我们可以减少引脚数量,这些引脚数与4x4矩阵键相关联。它将使用 8 个引脚,其中 4 个成行连接,4 个以列连接,因此节省了微控制器的 8 个引脚。
4x4 矩阵键盘的工作原理:
在上图中,矩阵键盘模块显示在左侧。右侧显示了内部连接以及端口连接。如果我们看到端口有 8 个引脚,从左到右的前 4 个是 X1、X2、X3 和 X4 是行,从左到右的最后 4 个是Y1、Y2、Y3、Y4是四列。如果我们制作 4 行或 X 侧作为输出并使它们逻辑低或0,并将 4列作为输入并读取键,我们将在对应Y 得到 0时读取开关按下。
同样的事情也会发生在 nxn 矩阵中,其中 n是数字。可以是 3x3、6x6 等。
现在只要认为1 被按下了。然后1 位于 X1 行和 Y1 列。如果 X1 为 0,则 Y1 将为 0。以同样的方式,我们可以通过感应列 Y1、Y2、Y3 和 Y4 来感知 X1 行中的每个键。每个开关都会发生这种情况,我们将读取矩阵中开关的位置。
每个绿色圆圈都是开关,它们都以相同的方式连接在一起。
在本教程中,我们将使用以下规格连接键盘-
我们将使用内部上拉
我们将添加密钥去抖动选项
但是当开关没有被按下时,我们需要使Y1、Y2、Y3 和 Y4达到高或 1。否则,我们无法检测到按下开关时的逻辑变化。但是我们无法通过代码或程序来实现它,因为这些引脚用作输入,而不是输出。因此,我们将在微控制器中使用内部操作寄存器,并将这些引脚作为弱上拉使能模式运行。通过使用此功能,当它处于默认状态时,将有一个逻辑高使能模式。
此外,当我们按键时,开关触点会产生尖峰或噪音,因此会发生多次开关按下,这是意料之外的。因此,我们将首先检测开关按下,等待几毫秒,再次检查开关是否仍然按下,如果开关仍然按下,我们将接受开关按下最终,否则不会。这称为开关的去抖动。
我们将在代码中实现这一切,并在面包板上建立连接。
所需材料:
面包板
PC中的Pic-kit 3和开发环境,即MPLABX
电线和连接器
字符液晶屏 16x2
20兆赫晶体
2 个 33pF 陶瓷盘盖。
4.7k 电阻
10k 预设(可变电阻)
4x4 矩阵键盘
一个 5 V 适配器
电路图:
我们将连接相关引脚中的晶体和电阻器。此外,我们将通过PORTD 以 4 位模式连接LCD。我们将六角键盘或矩阵键盘连接到端口RB4。
编程说明:
最后给出了矩阵键盘与PIC微控制器接口的完整代码。代码简单易懂。键盘库只是在代码中要理解的东西。在这里,我们使用 keyboard.h 和 lcd.h 库来连接键盘和 16x2 LCD。让我们看看里面发生了什么。
在keypad.h内部,我们将看到我们使用了默认寄存器库的xc.h标头,晶体频率是为使用kepad.c文件中使用的延迟而定义的。我们在PORTRB寄存器上定义了键盘端口,并将各个引脚定义为行 (X)和列(Y)。
我们还使用了两个函数,一个用于键盘初始化,它将端口重定向为输出和输入,另一个是开关按下扫描,它将在调用时返回开关按下状态。
#include
#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);
在keypad.c中,我们将看到当键盘扫描仪函数未返回“n”时,下面的函数将返回按键。
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
}
下面是键盘读取功能。在每个步骤中,我们将行 X1、X2、X3 和 X4 设为 0,并读取 Y1、Y2、Y3 和 Y4 状态。延迟用于去抖动效果,当仍然按下开关时,我们将返回与之关联的值。当没有按下开关时,我们将返回'n'。
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 个作为输出。 OPTION_REG &= 0x7F;用于在最后一个引脚上设置弱上拉模式。
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;
}
在主PIC程序中(如下所示),我们首先设置了配置位,并包含了一些需要的库。然后在无效的system_init功能中,我们初始化键盘和LCD。最后在主函数中,我们通过调用switch_press_scan()函数并将值返回给 lcd 来读取键盘。
/*
* 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
#include
#include
#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(); // This will initialise the lcd
InitKeypad();
}
全部0条评论
快来发表一下你的评论吧 !