单片机C语言实现独立按键检测与矩阵键盘操作

来源:互联网 发布:尼赫鲁被气死 知乎 编辑:程序博客网 时间:2024/04/28 03:14

       所有的电子产品几乎到涉及到按键操作。所以微控制器是如何识别一个按键是否被按下,按下后又该如何做出反应,又如何防止按键抖动呢?更深入一点,微控制器又是如何识别矩阵键盘的?本文将详细阐述如何用C语言实现独立按键的检测和矩阵键盘操作。

完成本文所需硬件:基于C51系列单片机的开发板(本文是基于STC12C5A60S2处理器的一款开发板),带中文版windows操作系统的电脑。

完成本文所需软件:KEIL系列平台(本文选取Keil uVision4), STC烧写软件-ISP-V6.82E 。


一、独立按键检测

       这里我要实现用按键K1去控制发光二极管LD4。同时为了试验按键过程中与其他事件的冲突性,引入两个事件即LD1与LD8分别以不同频率闪烁。先上程序吧。另外利用keil软件新建工程和文件部分这里就略过了,总之所有的代码都放在main文件里执行。

代码:

-----------------------------------------------------------------------------------------------

#include <reg51.h>sbit K1=P2^4;       //定义按键K1的检测口sbit LD4=P1^3;    //定义控制LD4的输出口sbit LD1=P1^0;   //定义控制LD1的输出口sbit LD8=P1^7;   //定义控制LD8的输出口

void LD1_flash()  //LD1闪烁{static unsigned int a; //定义a为静态局部变量a = a + 1;if(a == 20000) //当a达到20000次CPU计数次数{a=0;  //a归零LD1 =! LD1; //LD1取反,如果先前是灭,则取反后亮,反之。}}void LD8_flash()//LD8 flash{static unsigned int b;b++;if(b==40000){b=0;LD8 =! LD8;}}

void delay(unsigned int x) {while(x){x = x - 1;}}

void Key1(){static char st;if(K1==0) //按键K1是否被按下{if(st==0) //按键K1是否是被刚刚按下,假设st为1,则按键还处于被按下的状态,则不用执行if里面语句。{delay(5000); //延时防抖动if(K1==0) //查看按键K1是否还处于被按下状态{LD4=!LD4;st=1;  //等待放手}}}else{st=0;}}

void main() //C语言执行入口{while(1){LD1_flash(); //ld1 flash LD8_flash(); //ld8 flashKey1();}}

要理解上面的代码,我们需要结合C语言的一些特性来分析。

1、static unsigned int a,为什么不把变量a与b定义成局部变量,却定义成静态局部变量?

     我们知道局部变量在函数执行完时候其值归零,而static关键字修饰的局部变量在函数结束时,其值会保留到下一次该函数被调用。

2、为什么a和b没被定义成全局变量?

    全局变量是指该变量可被任何函数使用,而局部变量只有定义该变量的函数可以使用。局部变量的优点是安全,但缺点是函数结束其值随之归零。与之相对,全局变量则不安全。

3、为什么a和b并未赋初始值?

    这是因为startup.A51程序执行时使其赋值为0。C语言执行是从函数开始的,但是真正的程序运行是从汇编语言开始,即startup.A51文件。因为C语言无法访问寄存器如R0, 所以只能由汇编程序来执行。下面截取一段startup.A51的程序进行分析:

IF IDATALEN <> 0                MOV     R0,#IDATALEN - 1                CLR     AIDATALOOP:      MOV     @R0,A                DJNZ    R0,IDATALOOPENDIF

    这段程序会将内存区域清零,其中A代表累加器ACC,即内存中224号地址。

     另外解释下赋初值的情况,例如unsigned int a = 8, 我们知道RAM在断电后数据丢失,因此单片机就利用ROM来保存这个初始值。在下次上电时,进入C语言之前,汇编程序将ROM中的a调到RAM中。这样也就保证了a的初值。

4、LD1与LD8的闪烁为什么不适用delay函数?

     如果使用delay函数,那么单片机在执行到LD1_flash函数时候,只能停滞在delay的这个时间段内,浪费时钟资源。而引入a和b后,程序进入LD1_flash函数时候,只需判断a的值,然后再加一或者清零,几乎不占用CPU时间。

5、key1函数中st的作用?

     首先,st可以判断程序在每次被调用的前后按键状态,这样,可以保证键被按住的时候,发光二极管不会闪烁。另外,也可以避免在操作K1键的时候影响LD1和LD8的运行。另外如果不想在key1函数中使用delay函数,可以采用如下代码:

void Key1(){static unsigned int a,b;if(K1==0){if(a==0) //检查按键是否被刚刚按下{b++;if(b >= 1000) //看看按键时间是否维持了1000次CPU执行周期{LD4=!LD4;//...a=1;}}}else{a=0;b=0;}}


6、如何将不同的函数独立到不同的文件?

    为了保证代码的可维护性,如果要将不同的代码区块分配到不同文件,可以使用extern,它的作用是实现不同文件间的函数调用。例如:

    本例中可将main函数精简成:

#include <reg51.h>extern void delay(unsigned int x);extern void LD1_flash();extern void LD8_flash();extern void Key1();void main(){while(1){LD1_flash(); //ld1 flash LD8_flash(); //ld8 flashKey1();}}

二、矩阵键盘



上图中,P1口初始值从高位到低位为11111110,当2和3键被按下,此时P1.0口连通P1.5和P1.6口,即P1口状态变为10011110。由此我们可以用P1口的高四位来判断有没有键被按下,如果没有那么其值为1111,也就是0x0F。那如果被按下了,具体是哪个键呢?这时候我们可以建立一个矩阵表,然后通过每次读取P1口的状态,与矩阵表相对应,便可以知道哪个键被按下。这个矩阵表见下(选用了P0口代替P1口):

原理就解释到这吧,直接上程序:

-----------------------------------------------------------------------------------------------------------------

#include "reg51.h"void delay(unsigned int x){while(x){x=x-1;}}void key_exc(unsigned char k)//unsigned 表示最高位不当做符号用{switch(k){case 0xee: P1=~P1; break; //1键按下case 0xed:  break;        //2键按下case 0xeb:  break;case 0xe7:  break;case 0xde:  break;case 0xdd:  break;case 0xdb:  break;case 0xd7:  break;case 0xbe:  break;case 0xbd:  break;case 0xbb:  break;case 0xb7:  break;case 0x7e:  break;case 0x7d:  break;case 0x7b:  break;case 0x77:  P1=~P1; break;}}void key_scan(){char i;char t; //用来存放读取P0口的状态//if((P0>>4) != 15)//{//return; //直接跳出函数//}for(i=0;i<4;i++) //for循环执行,首先i赋值,再判断i是否小于4,如果是的话,开始执行下面的程序,程序执行完加1,再判断i是否小于4,这样一直到结束{P0 = ~(1<<i); //扫描第i行。注意1<<i是一个数,这个数中只有一个1,这个1正好在第i位上。而~则将其取反。t=P0; //将P0的值赋给tif((t>>4)!=0x0F) //本行是否有按钮被按下{delay(5000);if(P0==t) //如果相等则表示延时前后的按键状态是一致的{key_exc(t);//将局部变量传给函数key-exc,并分析按下的按钮while((P0>>4)!=15)//等待放手{}}}}}void main(){while(1){key_scan();}}

-----------------------------------------------------------------------------------------------------------------------------------

利用上面的程序,可以判断哪个按钮被按下。例如当K1按下时候,P1口的发光二极管都被点亮。好了就讲到这里吧,如果有什么疑问,请留言,我会及时答复!

原创粉丝点击