TI中文支持网
TI专业的中文技术问题搜集分享网站

MSP430 单片机矩阵键盘程序

基于io中断实现的矩阵键盘程序

#include <MSP430X44X.H>
unsigned char Key_Num=0 ;
void time_delay(void) ;
void Display_Num(void) ;
void Key_Process(void) ;
void main()
{
WDTCTL = WDTPW + WDTHOLD ; //停止看门狗
P4OUT = 0X00 ; P4DIR |= 0X08 ;
P1DIR = 0X00 ; //将P1定义为输入
P1DIR |= 0X0E ; //将P1.1~P1.3定义为输出
P1OUT = 0X00 ; //P1.1~P1.3输出为0
P1IE = 0X00 ; P1IES = 0X00 ; P1IFG = 0X00 ; P1IE |= 0X0E ; //P1允许中断
P1IES |= 0X0E ; _EINT() ; //系统中断允许
while(1) ;
}
#pragma vector = PORT1_VECTOR
__interrupt void port1(void)
{
time_delay () ; //软件消抖
if((P1IN&0XF0) != 0XF0 ) //有键按下
{
Key_Process () ;
P1IFG = 0X00 ;
P1OUT = 0X0E ;
Display_Num () ;
}
}

void time_delay (void){
unsigned int i =1 ;
while (i–) ;
}
void Display_Num(void)
{
P4OUT = 0X08 ;
}

void Key_Process(void)
{
P1OUT=0XEF; //P1.4 OUT 0
if((P1IN & 0X02) == 0X00) Key_Num = 1 ;
if((P1IN & 0X04) == 0X00) Key_Num = 2 ;
if((P1IN & 0X08) == 0X00) Key_Num = 3 ;
P1OUT=0XDF; //P1.5 OUT 0
if((P1IN & 0X02) == 0X00) Key_Num = 4 ;
if((P1IN & 0X04) == 0X00) Key_Num = 5 ;
if((P1IN & 0X08) == 0X00) Key_Num = 6 ;
P1OUT=0XBF; //P1.6 OUT 0
if((P1IN & 0X02) == 0X00) Key_Num = 7 ;
if((P1IN & 0X04) == 0X00) Key_Num = 8 ;
if((P1IN & 0X08) == 0X00) Key_Num = 9 ;
P1OUT=0X7F; //P1.7 OUT 0
if((P1IN & 0X02) == 0X00) Key_Num = 10 ;
if((P1IN & 0X04) == 0X00) Key_Num = 11 ;
if((P1IN & 0X08) == 0X00) Key_Num = 12 ;
}

gaoyang9992006:

程序简洁不错,如果把端口寄存器都用宏的形式,移植程序就非常方便了。可以单独建立个key.c  key.h

gaoyang9992006:

我也发一个另外一种形式的。

按键

         按键是单片机系统最常用的输入设备之一;几乎是只要需要交互输入,就必须有键盘。这篇博客实现了一个通用的键盘程序,只要提供一个读取键值的函数(底层键值),程序将完成消抖、存入队列等一些列处理。同时本程序提供最常用的4*4矩阵键盘的程序,和4个按键的程序。

硬件介绍:

本文主要实现了一个键盘的通用框架,可以很方便的改为不同的键盘函数,这里实现了两种按键4个单独按键和4*4行列扫描的键盘。

4个按键的是这样的:四个按键分别一端接地,另一端接上拉电阻后输入单片机的P1.0-P1.3口;这样,按键按下时,单片机接到低电平,松开时单片机输入信号有上拉电阻固定为高电平。

4*4的按键:行输入信号配有桑拉电阻,无按键时默认电平高电平;列扫描信号线直接接到按键列线;读键时,列扫描信号由单片机给出低电平信号(按列逐列扫描),读取行信号,从而判断具体是哪个按键;电路图大概如下:

 

图中,IN是键盘的列扫描线,OUT是键盘的输出的行信号线。扫描是也可以按行扫描,这时IN是行扫描线,OUT的按键输出的列信号线。我的程序是按列扫描的(行列扫描原理一样,只是行列进行了交换)。

这里,同时实现了4*4按键的scanf函数的移植,同时,加入了之前实现的液晶的printf函数的移植,搭建了一个可以交互输入输出的完整的一个系统;液晶的printf又加入了函数,实现了退格;可以在输入错误数字的时候退格重新输入。

程序实现:

先说一下程序的结构,程序实现了一个循环队列,用来存放已按下的键值,可以保存最新的四个按键,可以防止按键丢失;程序使用的是中断的方式进行按键,每16ms(用的是看门狗的间隔中断)读一次按键,进行判断键值是否有效,有效则放入队列,等待读取。

循环队列的实现:用数组实现,为判断队满,数组的最后一个元素不用于存储键码值:

/**********************宏定义***********************/ #define KeySize4//键码值队列 #define LengthKeySize+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调用,还需要设置,详细设置参考:MSP430程序库<四>printf和scanf函数移植;需要把库设置为CLIB;在Option-general option-library configuration里面。

这样,键盘的scanf移植完成,需要使用时,只需加入对stdio.h文件的包含,然后完成键盘的初始化即可。

使用示例:

这里,示例实现的是键盘和液晶的简单交互;键盘输入数据,液晶正常显示;就像c语言调试时键盘和屏幕一样;当然没有那个丰富啦。

液晶的部分,用的是原来实现的程序,在这里,为了支持输入错误时退格,对原来的printf函数加入了退格支持。具体参考:MSP430程序库<四>printf和scanf函数移植(已经更新)。

项目中接入液晶的c程序文件和printf的程序文件(Lcd12864.c、Printf.c),加入Lcd12864.h的文件包含;初始化液晶后,就可用printf向液晶输出要显示的内容了。

键盘:加入Key.c,包含Key.h,加入Getchar.c,程序中初始化键盘;然后设置所用的lib为CLIB,具体设置见:MSP430程序库<四>printf和scanf函数移植。之后就可以用键盘和液晶完成和430单片机简单的交互了。

详细参考示例工程和main.c。

#include <msp430x16x.h> #include <stdio.h> #include "Lcd12864.h" #include "Key.h" long a;
void main( void )
{// Stop watchdog timer to prevent time out reset WDTCTL = WDTPW + WDTHOLD;ClkInit();LcdInit();KeyInit();_EINT();while(1){printf("请输入数字:");scanf("%ld",&a);printf("输入的数字是:%ld",a);_NOP();}
}

这样,就可以用键盘向单片机输入数据,同时利用液晶可以很容易的知道数据输入的是否有问题。

键盘的程序库就到这里,有什么不足,欢迎讨论。

赞(0)
未经允许不得转载:TI中文支持网 » MSP430 单片机矩阵键盘程序
分享到: 更多 (0)