|
文件keyboard.h:
/*
按键扫描程序工作原理说明:
按键扫描采取行线扫描输出高电平列线扫描接收的方式进行工作
首先在一根行线上输出一个高电平,然后列线扫描接收,如果其
中有按键按下则按键所在列线上接收到高电平信号否则为低,从
而根据接收到高电平时行列编号可确定按键编号。
*/
#ifndef _KEYBOARD_H_
#define _KEYBOARD_H_
#include <iom16v.h>
#include "define.h"
#define KEYSUM 9 ///按键数量
#define HANGSUM 3 ///按键行数
#define LIESUM 3 ///按键列数
#define HANGSTART 0 ///按键行起始端口号
#define LIEEND 6 ///按键列末位端口号
#define KEYOUT PORTB ///按键扫描高电平输出端口
#define KEYIN PINB ///按键扫描输入端口
struct key
{
uchar keynum; ///按键编号
uchar keyconter; ///按键灵敏度
uchar keyburstmode; ///按键触发方式0为延时电平触发1为按下触发2为抬起触发
}; ///按键键值缓冲区
extern volatile uchar nowkeynum; ///当前按键编号 无按键按下时为0
/*!
* \brief 按键扫描程序
*
*读入按键键值并写入全局变量nowkeynum
*
* \return 按键键值
*/
uchar keyscan(void);
#endif
**********************************************************************
文件keyboard.c:
#include "keyboard.h"
/*按键记录电平计数缓冲区
每次扫描有按键按下记录缓冲区加1,直到超过按键灵敏度值。如无按键按下则清零。
*/
uchar keytmp[KEYSUM + 1] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
/*按键记录缓冲区
当使用沿触发方式时记录按键状态
*/
uchar keytmpnow[KEYSUM + 1] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
/*按键记录历史缓冲区
当使用沿触发方式时记录上一次按键状态
*/
uchar keytmpold[KEYSUM + 1] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
/*按键声明区
定义各个按键属性
*/
const struct key keyunit[KEYSUM + 1] =
{
0, 1, 0, ///
1, 2, 1, ///
2, 5, 0, ///
3, 5, 0, ///
4, 5, 0, ///
5, 5, 0, ///
6, 5, 0, ///
7, 5, 0, ///
8, 5, 0, ///
9, 5, 0, ///
};
uchar keyscan(void)
{
uchar loop1 = 0; ///行扫描循环控制
uchar loop2 = 0; ///列扫描循环控制
uchar keynumtmp = 0; ///按键编号缓存
uchar keyloopmasklie = 0; ///列扫描掩码
///列扫描掩码设定
keyloopmasklie |= (1 << LIEEND);
KEYOUT = 0x00;
for (loop1 = 0; loop1 < HANGSUM; loop1++)
{
///置行线
KEYOUT |= (1 << (loop1 + HANGSTART));
for (loop2 = 0; loop2 < LIESUM; loop2++)
{
keynumtmp = loop1 * LIESUM + loop2 + 1; ///计算出当前正在处理的按键编号
///判断按键的触发方式然后执行相应的判断程序
switch (keyunit[keynumtmp].keyburstmode)
{
///电平触发
case 0:
{
///如果有键按下相应键值计数器加1
if ((KEYIN &keyloopmasklie) != 0)
{
keytmp[keynumtmp]++;
///如果计数器计数超出灵敏度设定值有效按键键值赋值
if (keytmp[keynumtmp] >= keyunit[keynumtmp].keyconter)
{
nowkeynum = keynumtmp;
///计数器计数保持最大值
keytmp[keynumtmp] = keyunit[keynumtmp].keyconter;
return nowkeynum;
}
}
///没有按键按下相应按键计数寄存器清零
else
{
keytmp[keynumtmp] = 0;
}
}
break;
///按下触发
case 1:
///抬起触发
case 2:
{
///如果有键按下相应键值计数器加1
if ((KEYIN &keyloopmasklie) != 0)
{
keytmp[keynumtmp]++;
}
///没有按键按下相应按键计数寄存器清零
else
{
keytmp[keynumtmp] = 0;
}
///如果计数器计数超出灵敏度设定值按键键值赋高
if (keytmp[keynumtmp] >= keyunit[keynumtmp].keyconter)
{
///置当前按键电平状态为高
keytmpnow[keynumtmp] = HIGH;
///计数器计数保持最大值
keytmp[keynumtmp] = keyunit[keynumtmp].keyconter;
///按下触发
if (keyunit[keynumtmp].keyburstmode == 1)
{
///上升沿
if (keytmpold[keynumtmp] == LOW && keytmpnow[keynumtmp] == HIGH)
{
nowkeynum = keynumtmp;
keytmpold[keynumtmp] = keytmpnow[keynumtmp];
return nowkeynum;
}
}
}
else
{
///置当前按键电平状态为低
keytmpnow[keynumtmp] = LOW;
///抬起触发
if (keyunit[keynumtmp].keyburstmode == 2)
{
///下降沿
if (keytmpold[keynumtmp] == HIGH && keytmpnow[keynumtmp] == LOW)
{
nowkeynum = keynumtmp;
keytmpold[keynumtmp] = keytmpnow[keynumtmp];
return nowkeynum;
}
}
}
///按键记录推移
keytmpold[keynumtmp] = keytmpnow[keynumtmp];
}
break;
}
keyloopmasklie >>= 1;
}
///列扫描掩码设定
keyloopmasklie = 0;
keyloopmasklie |= (1 << LIEEND);
///复位行线到KL1
KEYOUT = 0x00;
}
nowkeynum = 0;
return nowkeynum;
}
*********************************************************************************
主程序中定义
volatile uchar nowkeynum = 0; ///当前按键编号 无按键按下时为0
*********************************************************************************
程序说明:
本程序适用于3*3矩阵键盘。输入,输出均使用PB口,也可以改为其他口,和其他行列数,更改
keyboard.h中的声明部分即可。
本按键扫描程序可选择电平触发或者沿触发方式,最后的按键键值存在变量nowkeynum中。
使用时在各位的原文件中包含上面的keyboard.h和keyboard.c文件并在主程序中定义
volatile uchar nowkeynum = 0;
然后就可以调用keyscan();读出键值了。
推荐使用定时器定时调用的方式来完成按键扫描操作。
对程序如有疑问请在后面留言,欢迎各位大虾帮忙测试,大家共同提高。
***********************************************************************************
程序本人原创,版权没有,各位可以随意使用。
如需转载请注明本人ID和OURAVR即可。 |
|