程序实现:
先说一下程序的结构,程序实现了一个循环队列,用来存放已按下的键值,可以保存最新的四个按键,可以防止按键丢失;程序使用的是中断的方式进行按键,每16ms(用的是看门狗的间隔中断)读一次按键,进行判断键值是否有效,有效则放入队列,等待读取。
循环队列的实现:用数组实现,为判断队满,数组的最后一个元素不用于存储键码值:
/**********************宏定义***********************/
#define KeySize 4 //键码值队列
#define Length KeySize+1 //队列数组元素个数
/***************************************************/
/**********************键值队列*********************/
//可KeySize(Length-1)个键码循环队列占用一个元素空间
char Key[Length];
入队函数:入队时,队满则出队一个,以保存最新的四个按键。
void AddKeyCode(char keyCode)
{
if((rear+1)%Length==front) //队满
{
front=(front+1)%Length; //出队一个
}
Key[rear] = keyCode;
rear=(rear+1)%Length;
}
出队函数:出队函数即是读取按键的函数,以供其他需要的地方调用。
char ReadKey()
{
char temp;
//if(rear==front) return '\0'; //无按键
while(rear==front);
temp = Key[front];
front=(front+1)%Length;
return temp;
}
KeyProcess:这个函数即是键盘处理函数,需要被每10ms-20ms的时间调用一次的函数,在这里把它放入了看门狗定时器16ms的中断中;函数流程图和函数内容如下:
void KeyProcess()
{
static char keyValue = 0xff; //按键标识,键值
static char addedFlag = 0; //加入队列标志
char keyVal = GetKey();
if(keyVal==0xff) //无按键
{
keyValue = 0xff;
addedFlag = 0;
return;
}
if(keyValue==0xff) //之前状态无按键
{
keyValue = keyVal;
return;
}
if(keyValue!=keyVal) //和前次按键不同
{
keyValue = keyVal; //保存新按键值
return;
}
if(addedFlag==1) //已加入队列
{
return;
}
addedFlag = 1;
AddKeyCode(KeyCode[keyVal]);
}
这个函数完成按键的判断,并和上次的比较,从而判断是否是有效按键,再根据是否已经入队保存,去判断是否要保存,入队列保存按键。
这个函数需要每10ms-20ms中断运行一次:
#pragma vector=WDT_VECTOR
__interrupt void WDT_ISR()
{
KeyProcess();
}
这是430看门狗的间隔定时中断,设置的是每16ms中断一次:
WDTCTL=WDT_ADLY_16; //看门狗内部定时器模式16ms
IE1 |= WDTIE; //允许看门狗中断
KeyProcess里调用了GetKey函数,这个函数需要用户提供,以满足特殊的按键需求,这里提供了两个实例:4个按键和4*4矩阵键盘。
4个按键的getkey函数:
char GetKey()
{
if((P1IN&0X0F)==0x0E)
{
return 0;
}
if((P1IN&0X0F)==0x0D)
{
return 1;
}
if((P1IN&0X0F)==0x0B)
{
return 2;
}
if((P1IN&0X0F)==0x07)
{
return 3;
}
return 0xff;
}
这里根据每个按键,输出按键原始键值,没有按键则输出0xff;当自己提供getkey函数时,也需要这样,无按键时返回0xff
把对应原始键值翻译成所需键码,用数组KeyCode:
char KeyCode[] = "0123"; /*4个按键时*/
这里把它转化成ASCII码输出,需要的话可以自行更改。
4*4矩阵键盘:getkey:
char GetKey()
{
P1DIR |= 0XF0; //高四位输出
for(int i=0;i<4;i++)
{
P1OUT = 0XEF << i;
for(int j=0;j<4;j++)
{
if((P1IN&(0x01<<j))==0)
{
return (i+4*j);
}
}
}
return 0xff;
}
这里是按列扫描,可以随意改成其他扫描方式,只要获取原始键值即可,无按键是须返回0xff。
KeyCode,翻译成ASCII码:
char KeyCode[] = "0123456789ABCDEF"
到这里,正常的键盘程序结束,调用时只需加入Key.c,包含Key.h即可使用,先调用KeyInit后,就可以正常的读键了。这里不再细说。
scanf移植:scanf移植时,需要的是ASCII码字符型设备,利用ASCII码输入数据还必须要有回车键,只有这样,才能用scanf输入数据,这里为了输入数据错误时,可以退格修改,按键还有一个退格键。
键盘结构:
1
|
2
|
3
|
退格
|
4
|
5
|
6
|
保留
|
7
|
8
|
9
|
保留
|
保留
|
0
|
保留
|
回车
|
保留键用字符’\0’,回车’\n’退格’\b’
所以:KeyCode:
char KeyCode[] = "123\b456\000789\0\0000\0\r"; /* 4*4,scanf移植*/
在字符串里,\0后面是数字时,必须用’\000’否则,c语言编译器认为\0和后面的数字组合为一个字符。
scanf的移植,需要实现getchar函数,这里和之前的getchar函数类似,把它放到了Getchar.c文件里,内容如下:
#include <stdio.h>
#include "Key.h"
#define LINE_LENGTH 20 //行缓冲区大小,决定每行最多输入的字符数
/*标准终端设备中,特殊ASCII码定义,请勿修改*/
#define InBACKSP 0x08 //ASCII <-- (退格键)
#define InDELETE 0x7F //ASCII <DEL> (DEL 键)
#define InEOL '\r' //ASCII <CR> (回车键)
#define InLF '\n' //ASCII <LF> (回车)
#define InSKIP '\3' //ASCII control-C
#define InEOF '\x1A' //ASCII control-Z
#define OutDELETE "\x8 \x8" //VT100 backspace and clear
#define OutSKIP "^C\n" //^C and new line
#define OutEOF "^Z" //^Z and return EOF
int getchar()
{
static char inBuffer[LINE_LENGTH + 2]; //Where to put chars
static char ptr; //Pointer in buffer
char c;
while(1)
{
if(inBuffer[ptr]) //如果缓冲区有字符
return (inBuffer[ptr++]); //则逐个返回字符
ptr = 0; //直到发送完毕,缓冲区指针归零
while(1) //缓冲区没有字符,则等待字符输入
{
c = ReadKey(); //等待接收一个字符==移植时关键
if(c == InEOF && !ptr) //==EOF== Ctrl+Z
{ //只有在未入其他字符时才有效
printf(OutEOF); //终端显示EOF符
return EOF; //返回 EOF(-1)
}
if(c==InDELETE || c==InBACKSP) //==退格或删除键==
{
if(ptr) //缓冲区有值
{
ptr--; //从缓冲区移除一个字符
printf(OutDELETE); //同时显示也删掉一个字符
}
}
else if(c == InSKIP) //==取消键 Ctrl+C ==
{
printf(OutSKIP); //终端显示跳至下一行
ptr = LINE_LENGTH + 1; //==0 结束符==
break;
}
else if(c == InEOL||c == InLF) //== '\r' 回车=='\n'回车
{
putchar(inBuffer[ptr++] = '\n');//终端换行
inBuffer[ptr] = 0; //末尾添加结束符(NULL)
ptr = 0; //指针清空
break;
}
else if(ptr < LINE_LENGTH) //== 正常字符 ==
{
if(c >= ' ') //删除 0x20以下字符
{
//存入缓冲区
putchar(inBuffer[ptr++] = c);
}
}
else //缓冲区已满
{
putchar('\7'); //== 0x07 蜂鸣符,PC回响一声
}
}
}
}
这里是支持退格等键的详细函数。
如果不需要支持退格,可以简化为:
int getchar()
{
return ReadKey();
}
要实现scanf调用,还需要设置,需要把库设置为CLIB;在Option-general option-library configuration里面。
这样,键盘的scanf移植完成,需要使用时,只需加入对stdio.h文件的包含,然后完成键盘的初始化即可。