51单片机驱动外设矩阵键盘
51单片机外接矩阵键盘的驱动程序,软件特性可以在proteus上仿真。单片机采用12MHz晶振,本设计采用P1口外接4x4矩阵键盘,按键具有松手检测。下面直接上代码:
键盘接口定义
定义矩阵键盘与51单片机之间的端口连接,罗列出矩阵键盘的16颗按键在系统中代表的功能。
#define KEYBOARD_PORT P1 // 矩阵接口
typedef enum { // 按键功能
KEY_0 = 0,
KEY_1 = 1,
KEY_2 = 2,
KEY_3 = 3,
KEY_4 = 4,
KEY_5 = 5,
KEY_6 = 6,
KEY_7 = 7,
KEY_8 = 8,
KEY_9 = 9,
KEY_ADD = 10,
KEY_SUB = 11,
KEY_ENTER = 12,
KEY_TRANS = 13,
KEY_FUN = 14,
KEY_BACK = 15,
KEY_BUTT
} key_t;
键盘扫描
按键扫描设计亮点在于算法精简、键盘扫描无延时两项。
算法精简主要体现在键盘行码、列码扫描上,通过两句switch语句获取按键值。扫描算法:将KEYBOARD_PORT高4位或低4位置1,其余置0,如果没有任何按键按下,读取KEYBOARD_PORT得到的应该是预置值,如果有按键按下,由于低电平会下拉高电平,使按键按下的行或列为0,读取KEYBOARD_PORT即可判断按键按下的行码和列码,将行码、列码组合得到唯一的按键值。
键盘扫描无延时主要体现在去掉了按键按下时的消抖延时和按键松手时的抖动延时,分别以内部静态变量标记状态,val = 0表示按键未被按下,val = 1表示按键已经按下,val = 2表示按键已经扫描完成等待松手。0 --> 1 --> 2,3种状态单向切换,将键盘扫描的延时消抖过程过渡到其他函数接口,可以更有效利用cpu。
/*******************************************************************************
* 函 数 名 : keyboard_scan
* 函数功能 : 矩阵键盘扫描
* 输 入 : void
* 输 出 : s8:获取键码值,返回-1无按键
* 说 名 : 矩阵键盘行线P1低四位,列线P1高四位
*******************************************************************************/
s8 keyboard_scan(void) // 矩阵键盘扫描
{
static s8 val = 0;
s8 key = -1;
KEYBOARD_PORT = 0x0f;
if ((val == 0) && (KEYBOARD_PORT != 0x0f)) { // 读取按键是否按下
val = 1;
} else if ((val == 1) && (KEYBOARD_PORT != 0x0f)) { // 再次检测键盘是否按下
val = 2;
KEYBOARD_PORT = 0x0f; // 确定行码0、4、8、12
switch (KEYBOARD_PORT) {
case 0x07: key = 0; break;
case 0x0b: key = 4; break;
case 0x0d: key = 8; break;
case 0x0e: key = 12; break;
default: break;
}
KEYBOARD_PORT = 0xf0; // 确定列码0、1、2、3
switch (KEYBOARD_PORT) {
case 0xe0: break;
case 0xd0: key += 1; break;
case 0xb0: key += 2; break;
case 0x70: key += 3; break;
default: break;
}
} else if ((val == 2) && (KEYBOARD_PORT == 0x0f)) { // 按键松手检测
val = 0;
}
return key;
}
矩阵键盘和51单片机连接在proteus仿真:
附录 – 矩阵键盘(keyboard.h)
#ifndef __KEYBOARD_H__
#define __KEYBOARD_H__
#include "include.h"
#define KEYBOARD_PORT P1 // 矩阵接口
typedef enum { // 按键功能
KEY_0 = 0,
KEY_1 = 1,
KEY_2 = 2,
KEY_3 = 3,
KEY_4 = 4,
KEY_5 = 5,
KEY_6 = 6,
KEY_7 = 7,
KEY_8 = 8,
KEY_9 = 9,
KEY_ADD = 10,
KEY_SUB = 11,
KEY_ENTER = 12,
KEY_TRANS = 13,
KEY_FUN = 14,
KEY_BACK = 15,
KEY_BUTT
} key_t;
s8 keyboard_scan(void); // 矩阵键盘扫描
key_t keyboard_map_to_key(s8 val); // 值映射到按键
void keyboard_delay_ms(u16 ms); // 52单片机12MHZ时钟,延时1ms函数
void test_keyboard(void); // 矩阵键盘测试代码
#endif
附录 – 矩阵键盘(keyboard.c)
#include "keyboard.h"
#include "lcd1602.h"
/*******************************************************************************
* 函 数 名 : keyboard_scan
* 函数功能 : 矩阵键盘扫描
* 输 入 : void
* 输 出 : s8:获取键码值,返回-1无按键
* 说 名 : 矩阵键盘行线P1低四位,列线P1高四位
*******************************************************************************/
s8 keyboard_scan(void) // 矩阵键盘扫描
{
static s8 val = 0;
s8 key = -1;
KEYBOARD_PORT = 0x0f;
if ((val == 0) && (KEYBOARD_PORT != 0x0f)) { // 读取按键是否按下
val = 1;
} else if ((val == 1) && (KEYBOARD_PORT != 0x0f)) { // 再次检测键盘是否按下
val = 2;
#ifdef ELECTRONIC_MARS
KEYBOARD_PORT = 0x0f; // 确定行码0、4、8、12
switch (KEYBOARD_PORT) {
case 0x07: key = 0; break;
case 0x0b: key = 4; break;
case 0x0d: key = 8; break;
case 0x0e: key = 12; break;
default: break;
}
KEYBOARD_PORT = 0xf0; // 确定列码0、1、2、3
switch (KEYBOARD_PORT) {
case 0xe0: break;
case 0xd0: key += 1; break;
case 0xb0: key += 2; break;
case 0x70: key += 3; break;
default: break;
}
#endif
#ifdef ELECTRONIC_MOON
KEYBOARD_PORT = 0x0f; // 确定列码0、1、2、3
switch (KEYBOARD_PORT) {
case 0x07: key=0; break;
case 0x0b: key=1; break;
case 0x0d: key=2; break;
case 0x0e: key=3; break;
default: break;
}
KEYBOARD_PORT = 0xf0; // 确定行码0、4、8、12
switch (KEYBOARD_PORT) {
case 0x70: break;
case 0xb0: key += 4; break;
case 0xd0: key += 8; break;
case 0xe0: key += 12; break;
default: break;
}
#endif
} else if ((val == 2) && (KEYBOARD_PORT == 0x0f)) { // 按键松手检测
val = 0;
}
return key;
}
/*******************************************************************************
* 函 数 名 : keyboard_map_to_key
* 函数功能 : 值映射到按键
* 输 入 : val:按键值
* 输 出 : void
* 说 名 : none
*******************************************************************************/
key_t keyboard_map_to_key(s8 val) // 值映射到按键
{
if ((val < KEY_0) || (val >= KEY_BUTT)) {
return KEY_BUTT;
}
return (key_t)val;
}
/*******************************************************************************
* 函 数 名 : keyboard_delay_ms
* 函数功能 : 52单片机12MHZ时钟,延时1ms函数
* 输 入 : ms:延时ms数
* 输 出 : void
* 说 名 : none
*******************************************************************************/
void keyboard_delay_ms(u16 ms) // 52单片机12MHZ时钟,延时1ms函数
{
u8 i;
for (; ms; ms--) {
for (i = 0; i < 123; i++);
}
}
/*******************************************************************************
* 函 数 名 : test_keyboard
* 函数功能 : 矩阵键盘测试代码
* 输 入 : void
* 输 出 : void
* 说 名 : none
*******************************************************************************/
void test_keyboard(void) // 矩阵键盘测试代码
{
s8 key;
lcd1602_init();
while (1) {
key = keyboard_scan();
lcd1602_set_pos(0, 0);
lcd1602_write_num(3, key);
lcd1602_set_pos(0, 1);
lcd1602_write_num(3, keyboard_map_to_key(key));
if (key != -1) {
keyboard_delay_ms(500);
}
}
}