单片机按键处理技巧及C语言编程方式

合集下载

c51单片机c语言教程

c51单片机c语言教程

51单片机C语言学习杂记学习单片机实在不是件易事,一来要购买高价格的编程器,仿真器,二来要学习编程语言,还有众多种类的单片机选择真是件让人头痛的事。

在众多单片机中51 架构的芯片风行很久,学习资料也相对很多,是初学的较好的选择之一。

51 的编程语言常用的有二种,一种是汇编语言,一种是 C 语言。

汇编语言的机器代码生成效率很高但可读性却并不强,复杂一点的程序就更是难读懂,而 C 语言在大多数情况下其机器代码生成效率和汇编语言相当,但可读性和可移植性却远远超过汇编语言,而且 C 语言还可以嵌入汇编来解决高时效性的代码编写问题。

对于开发周期来说,中大型的软件编写用 C 语言的开发周期通常要小于汇编语言很多。

综合以上 C 语言的优点,我在学习时选择了 C 语言。

以后的教程也只是我在学习过程中的一些学习笔记和随笔,在这里加以整理和修改,希望和大家一起分享,一起交流,一起学习,一起进步。

*注:可以肯定的说这个教程只是为初学或入门者准备的,笔者本人也只是菜鸟一只,有望各位大侠高手指点错误提出建议。

明浩2003-3-30mailto:pnzwzw@ wzw@第一课建立您的第一个C项目使用C 语言肯定要使用到C 编译器,以便把写好的C 程序编译为机器码,这样单片机才能执行编写好的程序。

KEIL uVISION2 是众多单片机应用开发软件中优秀的软件之一,它支持众多不同公司的MCS51 架构的芯片,它集编辑,编译,仿真等于一体,同时还支持,PLM,汇编和C 语言的程序设计,它的界面和常用的微软VC++的界面相似,界面友好,易学易用,在调试程序,软件仿真方面也有很强大的功能。

因此很多开发51 应用的工程师或普通的单片机爱好者,都对它十分喜欢。

以上简单介绍了KEIL51 软件,要使用KEIL51 软件,必需先要安装它。

KEIL51 是一个商业的软件,对于我们这些普通爱好者可以到KEIL 中国代理周立功公司的网站上下载一份能编译2K 的DEMO 版软件,基本可以满足一般的个人学习和小型应用的开发。

单片机按键程序设计

单片机按键程序设计

单片机按键程序设计单片机按键的基本原理其实并不复杂。

通常,按键就是一个简单的开关,当按键按下时,电路接通,对应的引脚电平发生变化;当按键松开时,电路断开,引脚电平恢复到初始状态。

在程序设计中,我们需要不断检测引脚的电平变化,从而判断按键是否被按下。

在实际的按键程序设计中,有多种方式可以实现按键检测。

其中一种常见的方法是查询法。

这种方法是通过不断地读取按键对应的引脚状态来判断按键是否被按下。

以下是一个简单的查询法示例代码:```cinclude <reg51h> //包含 51 单片机的头文件sbit key = P1^0; //定义按键连接的引脚void main(){while(1) //无限循环{if(key == 0) //如果按键按下,引脚为低电平{//执行按键按下的操作//比如点亮一个 LED 灯P2 = 0xfe;while(key == 0);//等待按键松开}}}```上述代码中,我们首先定义了按键连接的引脚`key`,然后在主函数的无限循环中不断检测按键引脚的状态。

当检测到按键按下时,执行相应的操作,并通过`while(key == 0)`等待按键松开。

除了查询法,还有中断法可以用于按键检测。

中断法的优点是能够及时响应按键动作,不会因为程序的其他操作而导致按键响应延迟。

```cinclude <reg51h> //包含 51 单片机的头文件sbit key = P1^0; //定义按键连接的引脚void int0_init()//中断初始化函数{IT0 = 1; //下降沿触发中断EX0 = 1; //使能外部中断 0EA = 1; //开总中断}void int0() interrupt 0 //外部中断 0 服务函数{//执行按键按下的操作//比如点亮一个 LED 灯P2 = 0xfe;}void main(){int0_init();//初始化中断while(1);//无限循环,保持程序运行}```在上述代码中,我们首先在`int0_init` 函数中对中断进行了初始化设置,然后在`int0` 函数中编写了按键按下时的处理代码。

按键处理程序C语言单片机

按键处理程序C语言单片机

按键处理程序C语言单片机分享一种按键处理程序(用C)//头文件定义:Ustruct KEY{Uchar Val;#define Key_Model_C 0 //按键1值#define Key_AddVal_C 1 //按键2值Uint ScanOnTime;Uchar LongKeyState;Uchar LongKeyRestState;Uchar SetInRn;Uchar Model; //按键状态(模式)#define Off_C 0 //之前未按下#define On_C 1 //现按下#define Delay_C 2 //按键处理后标志}Key;//----------------定义两个IO输入口为按键入口--------------------//#define KeyMo_Bin (GPIOB->IDR.Bit.B5)#define KeyAdd_Bin (GPIOB->IDR.Bit.B6)/*===================================== ==========================*/GPIO_Init(GPIO_Pin_5|GPIO_Pin_6,GPIO_Mode_In_PU_No_IT); //初始化为上拉输入无中断/*===================================== ==========================*///主程序大循环中每1毫秒扫描1次void KeyScan(void){if(Key.LongKeyRestState == 1) //长按键标志(复位处理长按键){if((KeyMo_Bin == 1) && (KeyAdd_Bin == 1)) //当两按键均抬起{if(++Key.ScanOnTime >= 130) //延时后复位{Key.LongKeyRestState=0;Key.Model=Delay_C;}}elseKey.ScanOnTime=0;return;}if(Key.Model == Off_C) //如果当前按键状态为未按下“Off_C”{if((KeyMo_Bin == 0) || (KeyAdd_Bin == 0))//按键1或按键2已按下(低有效){if(++Key.ScanOnTime >= 10) //当按下后自加1,加够10次即1ms*10=10ms去抖动{Key.ScanOnTime=0;if(KeyMo_Bin == 0) //如果按键1为0即按下{Key.Val=Key_Model_C; //付当键1值(看头文件定义)Key.Model=On_C; //置按键已按下标志}else if(KeyAdd_Bin == 0) //如果按键2为0即按下{Key.Val=Key_AddVal_C; //付当键2值(看头文件定义)Key.Model=On_C; //置按键已按下标志}BellOn(200); //蜂鸣器响}}elseKey.ScanOnTime=0; //清去抖延时计数值}else if(Key.Model == Delay_C) //如果当前按键状态为已按下且已处理“Delay_C”{if((KeyMo_Bin == 1) && (KeyAdd_Bin == 1))//如果两按键均抬起{if(++Key.ScanOnTime >= 100) //延时100ms后复位按键状态为“Off_C”{Key.SetInRn=0;Key.ScanOnTime=0;Key.Model=Off_C;}}else //如果按键没有被抬起,对应上面if{if(++Key.ScanOnTime >= 1000) //延时1000ms后再复位按键状态为“Off_C”(为长按处理){Key.ScanOnTime=0;Key.Model=Off_C;}}}}//===================================== ========================================= ========void LoadCheckKeyRest(void){Key.LongKeyRestState=1;Key.ScanOnTime=0;}//===================================== ========================================= ========//处理相应按键(可250ms才调用一次)长按if(Key.Model == On_C) //按键状态为已按下{if(Key.Val == Key_Model_C) //是键1按下(看头文件定义){if(++Key.SetInRn >= 3) //连计3次数3秒后为长按键(对应上面,如果按下未抬起的话会延时1000ms){Key.SetInRn=0;LoadCheckKeyRest(); //调清长按处理BellOn(600); //蜂鸣器响//-----------长按需处理的内容-----下-----------//WorkStateBit.Bit.SettingMo=1;SetOverTime=0;SetMoRn=0;SetBak[0]=Rtc_InitDate.RTC_Year;SetBak[1]=Rtc_InitDate.RTC_Month;SetBak[2]=Rtc_InitDate.RTC_Date;SetBak[3]=Rtc_InitDate.RTC_WeekDay;SetBak[4]=Rtc_InitTime.RTC_Hours;SetBak[5]=Rtc_InitTime.RTC_Minutes;//-----------长按需处理的内容------上----------//Key.Model=Delay_C; //置按键模式为:已处理按键(看头文件定义)return;}}elseKey.SetInRn=0;Key.Model=Delay_C;}。

STC单片机C语言程序设计STC单片机C语言编程入门

STC单片机C语言程序设计STC单片机C语言编程入门

STC单片机C语言程序设计STC单片机C语言编程入门STC单片机是一种广泛应用于嵌入式系统和物联网设备中的微控制器。

它具有体积小、功耗低、运算能力强等特点,被广泛应用于各种控制系统中。

本文将介绍STC单片机C语言程序设计的入门知识,以帮助初学者快速上手。

首先,我们需要了解一些基本的概念和术语。

1.单片机:单片机是一种集成电路芯片,其中包含了中央处理器、存储器、输入输出接口等功能模块。

它可以独立完成特定的任务,不需要额外的硬件设备与之配合。

2.C语言:C语言是一种高级编程语言,被广泛应用于嵌入式系统开发中。

它具有简洁、高效的特点,易于理解和学习。

了解了上述基本概念后,接下来我们将介绍一些STC单片机C语言程序设计的入门知识。

2. 程序结构:一个C语言程序通常由多个函数组成,其中一个函数名为main(。

程序从main(函数开始执行,执行完main(函数后程序结束。

3.数据类型:C语言中有多种数据类型,包括整型、浮点型、字符型等。

在使用数据类型时,需要根据需要选择合适的数据类型。

4.变量和常量:在C语言中,可以使用变量和常量来存储数据。

变量是可以改变值的,而常量是固定不变的值。

5. 输入和输出:C语言中使用标准库函数scanf(和printf(来实现输入和输出操作。

通过这两个函数可以从键盘获取输入数据,并将结果输出到屏幕上。

6. 控制语句:在C语言中,可以使用if语句、for循环和while循环等控制语句来控制程序的执行流程。

通过控制语句,可以实现条件判断、循环执行等功能。

7.函数:函数是C语言中的重要概念,它可以将一段代码封装成一个独立的模块,方便重复使用。

在编写程序时,可以自定义函数来实现特定的功能。

8.数组:数组是一种存储相同类型数据的连续内存区域。

在C语言中,可以使用数组来存储一组数据,并对数据进行操作。

9.文件操作:C语言提供了文件操作函数,可以对文件进行读写操作。

通过文件操作,可以实现数据的持久化存储。

编写单片机C语言代码的技巧和经验

编写单片机C语言代码的技巧和经验

编写单片机C语言代码的技巧和经验编写单片机C语言代码的技巧和经验C语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。

那么编写单片机C语言代码的技巧和经验都有哪些呢。

以下仅供参考!具体如下:1、如果可以的话少用库函数,便于不同的mcu和编译器间的移植2、选择合适的算法和数据结构应该熟悉算法语言,知道各种算法的优缺点,具体资料请参见相应的参考资料,有很多计算机书籍上都有介绍。

将比较慢的顺序查找法用较快的二分查找或乱序查找法代替,插入排序或冒泡排序法用快速排序、合并排序或根排序代替,都可以大大提高程序执行的效率。

选择一种合适的数据结构也很重要,比如你在一堆随机存放的数中使用了大量的插入和删除指令,那使用链表要快得多。

数组与指针语句具有十分密码的关系,一般来说,指针比较灵活简洁,而数组则比较直观,容易理解。

对于大部分的编译器,使用指针比使用数组生成的代码更短,执行效率更高。

但是在Keil中则相反,使用数组比使用的指针生成的代码更短。

3、使用尽量小的数据类型能够使用字符型(char)定义的变量,就不要使用整型(int)变量来定义;能够使用整型变量定义的变量就不要用长整型(long int),能不使用浮点型(float)变量就不要使用浮点型变量。

当然,在定义变量后不要超过变量的作用范围,如果超过变量的范围赋值,C编译器并不报错,但程序运行结果却错了,而且这样的错误很难发现。

在ICCAVR中,可以在Options中设定使用printf参数,尽量使用基本型参数(%c、%d、%x、%X、%u和%s格式说明符),少用长整型参数(%ld、%lu、%lx和%lX格式说明符),至于浮点型的参数(%f)则尽量不要使用,其它C编译器也一样。

在其它条件不变的情况下,使用%f 参数,会使生成的代码的数量增加很多,执行速度降低。

4、使用自加、自减指令通常使用自加、自减指令和复合赋值表达式(如a-=1及a+=1等)都能够生成高质量的程序代码,编译器通常都能够生成inc和dec之类的指令,而使用a=a+1或a=a-1之类的指令,有很多C编译器都会生成二到三个字节的指令。

单片机按键单击双击长按功能实现

单片机按键单击双击长按功能实现

单片机按键单击双击长按功能实现在很多嵌入式系统中,都需要对按键进行检测和处理,以实现不同的功能。

常见的按键功能包括单击、双击和长按。

在这篇文章中,我们将介绍如何使用单片机实现这些按键功能。

首先,我们需要连接一个按键到单片机的I/O口。

按键通常是一个开关,有两个接触点。

当按键按下时,两个接触点会闭合,导致I/O口的电平发生变化。

我们可以利用这个变化来检测按键的状态。

为了实现按键功能,我们需要编写一段程序来监测按键的状态。

以下是一个简单的流程:1.初始化I/O口:将按键连接到I/O口上,并将I/O口设置为输入模式。

2.监测按键状态:定时读取I/O口的电平状态,以检测按键是否按下。

如果I/O口电平发生变化,则按键被按下或松开。

3.单击功能:当按键被按下时,记录当前时间,并等待一段时间,如果超过这段时间,说明按键已松开,则触发单击功能。

4.双击功能:如果在两次按键之间的时间间隔内再次检测到按键按下,则触发双击功能。

5.长按功能:当按键被按下一段较长的时间后,触发长按功能。

下面我们来具体介绍如何实现这些功能。

首先,我们需要初始化单片机的I/O口。

这个过程可以通过配置相应的寄存器实现。

具体的方法和步骤依赖于你使用的单片机型号和开发环境。

在这里,我们不具体展开,而是假设我们已经成功初始化了I/O口。

接下来,我们需要设置一个计时器用于定时检测按键的状态。

计时器的定时周期决定了我们可以检测的最短按键时间间隔。

通常,我们使用一个定时器来实现单击和双击功能,使用另一个定时器来检测长按功能。

一旦我们完成了计时器的配置,我们就可以开启定时器中断,并启动计时器。

每当定时器溢出时,中断函数会被触发,我们可以在这个函数中检测按键的状态。

在中断函数中,我们读取I/O口的电平状态,并根据当前的按键状态做出相应的处理。

首先,我们需要检测按键是否按下。

为了防止按键的抖动和误触发,我们使用一个状态机来确定按键的状态。

状态机的状态可以分为按下和松开两种。

51单片机c语言教程

51单片机c语言教程

51单片机c语言教程在本教程中,我们将学习如何在51单片机上使用C语言进行编程。

无论您是初学者还是有一定经验的开发者,本教程都将对您有所帮助。

首先,我们需要了解一些基本概念。

51单片机是一种基于哈弗微电子公司的MCS-51架构的微控制器。

它采用了Harvard结构,即将程序存储器和数据存储器分开。

它具有各种功能和接口,可以满足不同的应用需求。

在使用C语言进行51单片机编程之前,必须安装相应的开发工具。

这里我们推荐使用Keil C51开发环境。

安装完成后,我们就可以开始编写第一个程序了。

#include <reg51.h>void main(){// 在这里编写您的代码}以上是一个简单的C语言程序模板。

我们使用了reg51.h头文件,该文件包含了与51单片机相关的寄存器定义和常量。

接下来,我们可以开始编写具体的功能代码了。

例如,如果我们想要在LED灯上闪烁一个简单的模式,可以使用以下代码:#include <reg51.h>sbit LED = P1^0;void main(){while(1){LED = 0; // 点亮LEDdelay(1000); // 延时1秒LED = 1; // 熄灭LEDdelay(1000); // 延时1秒}}在这个程序中,我们首先定义了一个LED的控制引脚,然后通过循环实现了闪烁的功能。

在每次循环中,我们先点亮LED,然后通过调用延时函数延时1秒,再将LED熄灭,再次延时1秒。

这样就形成了一个简单的LED闪烁效果。

除了控制IO口外,51单片机还可以实现其他各种功能,如定时器、串口通信等。

这些功能的实现也都可以通过C语言来完成。

希望通过本教程,您可以对51单片机的C语言编程有一个基本的了解。

在以后的学习中,您可以深入研究这些知识,并通过实践来提升自己的能力。

祝您学习愉快!。

MSP430单片机C语言编程

MSP430单片机C语言编程
CCR0 =32768; // 设置计数器CCR0初值
TACTL |= MC0; //设置定时器工作模式为加计数到CCR0初值
P3DIR = 0XFF; //P3口为输出
P4DIR = 0XFF; //P4口为输出
P5DIR = 0XFF; //P5口为输出
P3OUT = 0X7E; //P3口输出为0111 1110
void main (void)
{
WDTCTL= WDTPW + WDTTMSEL+WDTSSEL;
IE1|=WDTIE;
P3DIR |=BIT7;
_EINT();
while(1);
}
interrupt[WDT_VECTOR] void WDT_interrupt (void)
P3DIR |=BIT7; //将P3.7设置为输出
_EINT(); //调用C430编译器内部函数,使能中断
while(1); //无限次循环
}
interrupt[TIMERA0_VECTOR] void Timer_A (void) //定时器A中断函数
{
P3OUT ^= BIT7; //P3.7位取反
右侧数码管与P4口相连,a~g,h对应P4.0~P4.7
(2)发光二极管
8 个发光二极管与P3 口连接
(3)按钮:
左侧8个按钮与P2口相连,引脚号标在按钮上方
右侧8个按钮与P1口相连,引脚号标在按钮上方
(4)P2.3引脚还是模拟比较器输入
(5)P6.0,P6.1引脚连接模拟量电位器,用于模拟量实验
//定义七段译码的共阳数码管显示数组
// hgfg dcba
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

单片机按键处理技巧及C语言编程方式(共5页)--本页仅作为文档封面,使用时请直接删除即可----内页可以根据需求调整合适字体及大小--单片机按键处理技巧及编程方式在基于单片机为核心构成的应用系统中,用户输入是必不可少的一部分。

输入可以分很多种情况,譬如有的系统支持PS2键盘的接口,有的系统输入是基于编码器,有的系统输入是基于串口或者USB或者其它输入通道等等。

在各种输入途径中,更常见的是,基于单个按键或者由单个键盘按照一定排列构成的矩阵键盘(行列键盘)。

我们这一篇章主要讨论的对象就是基于单个按键的程序设计,以及矩阵键盘的程序编写。

按键检测的原理: 它们和我们的单片机系统的I/O口连接一般如下:对于单片机I/O内部有上拉电阻的微控制器而言,还可以省掉外部的那个上拉电阻。

简单分析一下按键检测的原理。

当按键没有按下的时候,单片机I/O通过上拉电阻R接到VCC,我们在程序中读取该I/O的电平的时候,其值为1(高电平); 当按键S按下的时候,该I/O被短接到GND,在程序中读取该I/O的电平的时候,其值为0(低电平) 。

这样,按键的按下与否,就和与该按键相连的I/O的电平的变化相对应起来。

结论:我们在程序中通过检测到该I/O口电平的变化与否,即可以知道按键是否被按下,从而做出相应的响应。

一切看起来很美好,是这样的吗?在我们通过上面的按键检测原理得出上述的结论的时候,那就是现实中按键按下时候的电平变化状态。

我们的结论是基于理想的情况得出来的,而实际中,由于按键的弹片接触的时候,并不是一接触就紧紧的闭合,它还存在一定的抖动,尽管这个时间非常的短暂,但是对于我们执行时间以us为计算单位的微控制器来说,它太漫长了。

因而,实际的波形图应该如下面这幅示意图一样。

这样便存在这样一个问题。

假设我们的系统有这样功能需求:在检测到按键按下的时候,将某个I/O的状态取反。

由于这种抖动的存在,使得我们的微控制器误以为是多次按键的按下,从而将某个I/O的状态不断取反,这并不是我们想要的效果,假如该I/O控制着系统中某个重要的执行的部件,那结果更不是我们所期待的。

于是乎有人便提出了软件消除抖动的思想,道理很简单:抖动的时间长度是一定的,只要我们避开这段抖动时期,检测稳定的时候的电平不就可以了吗?听起来确实不错,而且实际应用起来效果也还可以。

于是就像下面的伪代码所描述的一样。

(假设按键按下时候,低电平有效)If(0 ==io_KeyEnter)51单片机复位后,不需要进行此操作也可以进行读取外部引脚的操作。

因此,在按键的端口没有复用的情况下,可以省略此步骤。

而对于其它一些真正双向I/O口的单片机来说,将引脚设置成输入状态,是必不可少的一个步骤。

下面的程序代码初始化引脚为输入。

void KeyInit(void){io_key_1 = 1 ;io_key_2 = 1 ;io_key_3 = 1 ;io_key_4 = 1 ;}根据按键硬件连接定义按键键值#define KEY_VALUE_1 0x0e#define KEY_VALUE_2 0x0d#define KEY_VALUE_3 0x0b#define KEY_VALUE_4 0x07#define KEY_NULL 0x0f下面我们来编写按键的硬件驱动程序。

根据第一章所描述的按键检测原理,我们可以很容易的得出如下的代码:static uint8 KeyScan(void){if(io_key_1 == 0)return KEY_VALUE_1 ;if(io_key_2 == 0)return KEY_VALUE_2 ;if(io_key_3 == 0)return KEY_VALUE_3 ;if(io_key_4 == 0)return KEY_VALUE_4 ;return KEY_NULL ;}其中io_key_1等是我们按键端口的定义,如下所示:sbit io_key_1 = P3^0 ;sbit io_key_2 = P3^1 ;sbit io_key_3 = P3^2 ;sbit io_key_4 = P3^3 ;KeyScan()作为底层按键的驱动程序,为上层按键扫描提供一个接口,这样我们编写的上层按键扫描函数可以几乎不用修改就可以拿到我们的其它程序中去使用,使得程序复用性大大提高。

同时,通过有意识的将与底层硬件连接紧密的程序和与硬件无关的代码分开写,使得程序结构层次清晰,可移植性也更好。

对于单片机类的程序而言,能够做到函数级别的代码重用已经足够了。

在编写我们的上层按键扫描函数之前,需要先完成一些宏定义。

//定义长按键的TICK数,以及连_发间隔的TICK数#define KEY_LONG_PERIOD 100#define KEY_CONTINUE_PERIOD 25//定义按键返回值状态(按下,长按,连_发,释放)#define KEY_DOWN 0x80#define KEY_LONG 0x40#define KEY_CONTINUE 0x20#define KEY_UP 0x10 //定义按键状态#define KEY_STATE_INIT 0#define KEY_STATE_WOBBLE 1#define KEY_STATE_PRESS 2#define KEY_STATE_LONG 3#define KEY_STATE_CONTINUE 4#define KEY_STATE_RELEASE 5接着我们开始编写完整的上层按键扫描函数,按键的短按,长按,连按,释放等等状态的判断均是在此函数中完成。

对照状态流程转移图,然后再看下面的函数代码,可以更容易的去理解函数的执行流程。

完整的函数代码如下:void GetKey(uint8 *pKeyValue){static uint8 s_u8KeyState = KEY_STATE_INIT ;static uint8 s_u8KeyTimeCount = 0 ;static uint8 s_u8LastKey = KEY_NULL ; //保存按键释放时候的键值uint8 KeyTemp = KEY_NULL ;KeyTemp = KeyScan() ; //获取键值switch(s_u8KeyState){case KEY_STATE_INIT :{if(KEY_NULL != (KeyTemp)){s_u8KeyState = KEY_STATE_WOBBLE ;}}break ;case KEY_STATE_WOBBLE : //消抖{s_u8KeyState = KEY_STATE_PRESS ;}break ;case KEY_STATE_PRESS :{if(KEY_NULL != (KeyTemp)){s_u8LastKey = KeyTemp ; //保存键值,以便在释放按键状态返回键值 KeyTemp |= KEY_DOWN ; //按键按下s_u8KeyState = KEY_STATE_LONG ;}else{s_u8KeyState = KEY_STATE_INIT ;}}break ;case KEY_STATE_LONG :{if(KEY_NULL != (KeyTemp)){if(++s_u8KeyTimeCount > KEY_LONG_PERIOD){s_u8KeyTimeCount = 0 ;KeyTemp |= KEY_LONG ; //长按键事件发生s_u8KeyState = KEY_STATE_CONTINUE ;}}else{s_u8KeyState = KEY_STATE_RELEASE ;}}break ;case KEY_STATE_CONTINUE :{if(KEY_NULL != (KeyTemp)){if(++s_u8KeyTimeCount > KEY_CONTINUE_PERIOD){s_u8KeyTimeCount = 0 ;KeyTemp |= KEY_CONTINUE ;}}else{s_u8KeyState = KEY_STATE_RELEASE ;}}break ;case KEY_STATE_RELEASE :{s_u8LastKey |= KEY_UP ;KeyTemp = s_u8LastKey ;s_u8KeyState = KEY_STATE_INIT ;}break ;default : break ;}*pKeyValue = KeyTemp ; //返回键值}关于这个函数内部的细节我并不打算花过多笔墨去讲解。

对照着按键状态流程转移图,然后去看程序代码,你会发现其实思路非常清晰。

最能让人理解透彻的,莫非就是将整个程序自己看懂,然后想象为什么这个地方要这样写,抱着思考的态度去阅读程序,你会发现自己的程序水平会慢慢的提高。

不管怎么样,这样的一个程序已经完成了本章开始时候要求的功能:按下,长按,连按,释放。

事实上,如果掌握了这种基于状态转移的思想,你会发现要求实现其它按键功能,譬如,多键按下,功能键等等,亦相当简单,在下一章,我们就去实现它。

在主程序中我编写了这样的一段代码,来演示我实现的按键功能。

void main(void){uint8 KeyValue = KEY_NULL;uint8 temp = 0 ;LED_CS11 = 1 ; //流水灯输出允许LED_SEG = 0 ;LED_DIG = 0 ;Timer0Init() ;KeyInit() ;EA = 1 ;while(1){Timer0MainLoop() ;KeyMainLoop(&KeyValue) ;if(KeyValue == (KEY_VALUE_1 | KEY_DOWN)) P0 = ~1 ;if(KeyValue == (KEY_VALUE_1 | KEY_LONG)) P0 = ~2 ;if(KeyValue == (KEY_VALUE_1 | KEY_CONTINUE)) { P0 ^= 0xf0;}if(KeyValue == (KEY_VALUE_1 | KEY_UP)) P0 = 0xa5 ;}}按住第一个键,可以清晰的看到P0口所接的LED的状态的变化。

当按键按下时候,第一个LED灯亮,等待2 S后第二个LED亮,第一个熄灭,表示长按事件发生。

再过500 ms 第5~8个LED闪烁,表示连按事件发生。

当释放按键时候,P0口所接的LED的状态为:灭亮灭亮亮灭亮灭,这也正是P0 =0xa5这条语句的功能。

相关文档
最新文档