使用PIC16F877A连接4x4矩阵键盘的教程

描述

键盘是广泛用于各种电子和嵌入式项目的输入设备。它们用于以数字和字母的形式获取输入,并将其输入系统以进行进一步处理。在本教程中,我们将使用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 矩阵键盘的工作原理:

PIC16F877A

在上图中,矩阵键盘模块显示在左侧。右侧显示了内部连接以及端口连接。如果我们看到端口有 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 适配器

电路图:

PIC16F877A

 

PIC16F877A

我们将连接相关引脚中的晶体和电阻器。此外,我们将通过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();

}

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分