• 回复
  • 收藏
  • 点赞
  • 分享
  • 发新帖

小白菜发起讨论话题``````定时中断的按键检测程序



【作者简介】3htech

姓名:朱海生,生于山东德州。

2010年6月毕业于山东师范大学电子信息科学与技术专业

2010年1月至2012年3月,在济南某公司设计开发称重产品(模块,配料表,称重控制仪表等)

2012年5月至现在,在山东淄博某公司设计开发电力仪表。


下面是小白菜分享给各位网友的个人学习经历,以及一些个人心得:

话说大二开设的C语言,本人一不小心就挂了。现在回想起来,还要感谢这次挂科。

我好像是大二上学期开设的C语言,那个时候天气有点热,又是下午第一大节上课,我和我舍友经常性的不想去。躲在宿舍里睡个午觉,上个网,打个游戏,感觉不错;但是课程也落下了。最后期末考试了,发现自己什么都不会,考试时正执大热天,考完一场一身汗,但也没感觉自己能挂,等上网查成绩之后发现真的挂了。

怎么办?怎么办?听说C语言挂了之后一般补考也会挂,大部分都要重修。我的个神来……我不能重修,要不然太丢人了。于是自己在漫长的暑假里开始看《C语言程序设计》(谭老师编的)。一个字一个字的慢慢看,课后题一个不落的全做了,流程图,写代码,调试忙了整整一个暑假。等开学后补考,结果通过了。悬着的心放下了。在暑假里,我还发现了书上的几处错误之处,本想着开学后给寄出去,结果也经忘记了。

正是因为这一个月的突击,让我对C(尤其是指针)有了一个较为深刻的认识。在后来的《数据结构》学习中,我能很容易理解课本上所说的内容,并且可以把课后习题编写出来。

学习数据结构的时候,我不知怎么的写了一个大数阶乘的程序。已经记不得为什么要写这个了。后来我把自己写的这个大数阶乘给放到自己的求职简历里去了,结果很管事,只发了3份简历,便找到了我的第一份工作。

第一次调试单片机程序是在伟福实验箱,话说当时什么不都不知道,箱子据说也是坏的,上课就是为了去玩。后来自己买了一块开发板,开始用汇编编51上的程序(一开始我不会用51C,只会用汇编)。

第一个自己编写的程序是流水灯。我先把开发板上的代码看了一遍,了解了流水灯的原理(我是看代码会的),然后开始在原来代码基础上改写;比如,你左移,我改成右移,你移的快,我给你改成慢的;感觉自己差不多了,开始在一个空白文档上自己写流水灯的代码,并且成了。

第一次用的单片机并不是我现在最鄙视的STC系列,而是SST的,具体名字我忘记了,不过Keil里面有这一款单片机,通过串口可以仿真调试,非常方便。通过个单片机我学会了设置断点,看寄存器,看IO状态等等。

在工作前,我对硬件什么都不懂,连电阻上的色环代表什么意思都不知道。因为我不努力的原因吧。硬件方面,工作前不会画板(学的99se,不会用),不懂电阻电容,哎,总之就是一颗小白菜。

工作后,我接触到了硬件,感觉也就那些东西,学会了看手册,学会了画PCB,而且这些东西都是在很短的时间内就入门了。软件上,从汇编转向了51C。因为C语言自己学的还不错,所以转51C很快。

工作后,一开始,我还一个月买一本书。后来就没买了。看不完。买的书有《C陷阱与缺陷》、《C与指针》、《c primer plus》、《C专家编程》、《ARM嵌入式系统基础教程》、《EMC电磁兼容设计与测试案例分析》 《嵌入式实时操作系统μC\OS-Ⅱ》(邵贝贝教授译)……

电子书下了一堆,不过有一本不得不说到,《匠人手记》。以我的水平,其实不足以对这本书进行评论,但是我有权说一个字“好”!里面的内容对工作过一段时间的人来说就是至宝。

我最喜欢的是《C陷阱与缺陷》、《c primer plus》、《匠人手记》、《EMC电磁兼容设计与测试案例分析》,分析的很透彻。我也很尊敬《ARM嵌入式系统基础教程》的主要作者陈明计先生。陈先生以个人之力编写了一个在51上运行的RTOS,这点我很佩服,我买了他编写的《嵌入式实时操作系统Small RTOS51原理及应用》并试图移植,可是我太懒了,总是有各种理由不去碰它。

话说的有点多了,现在看看我的学习之路是很清晰。

一,先学好C,这样就不会为了程序上的一条语句在那里纠结半天。

二,开发板,对照着原程序,先改写,然后自己能全部写来了。

三,工作后,买书,看书,充电。

四,好好工作,照顾好自己的家人。

希望老鸟小鸟们开开心心学习,平平安安生活。That’s all.

By 3htech(小白菜)


讨论话题````````````定时中断的按键检测程序

零 该程序产生的背景

话说2012年,小白菜要做一个三相电压电流组合表,这个仪表需要进行数据输入(小白菜以前的项目也有输入,但是小白菜没有仔细的研究过),并且给出的时间很长,小白菜有时间来做一些“研究处理”。拿着以前写的按键检测程序,感觉漏洞百出,于是想着趁着有时间把这部分做出来,于是便用了一个星期(实际是5天,双休思密达)专门写了这部分程序。

一 小白菜的应用需求

小白菜的仪表仅需要单短击(简称单击)和单长击(简称长击),单短击要在按键松开后才进行识别,单长击要在达到设定的时间阀值时进行识别(这时按键未松开)。

不需要考虑的情况如下,不需要连击(可以做为多次短击)、不需要多键同时击、暂时不需要考虑输入数字时长按某键,数字快速自加或自减。

该检测程序要满目不依赖于任何一种单片机,也不依赖按键连接方式,如独立式,矩阵式(当然你要用按键扫描芯片那就……你要用AD式键盘,我……好吧,你赢了~),能够独立存在。

二 按键过程分析

1 按键小思考

正常的按键过程(不考虑非法的按键状态)如图2.1.1。


图2.1.1 正常按键状态示意图

单击和长击只是时间上的区分而已,但是其识别时稍有区别,单击是在按键松开时进行识别的,长击是在按键闭合时进行识别的。见图2.1.2。


图2.1.2 长短击按键状态示意图

2 各种可能出现的按键情况

合法情况不再赘述。下面把非法(仅在本应用中非法)的情况列一下。

(1) 人为或干扰引起的单击时间过短(主要为防干扰)。

(2) 单击时间过长(与(1)对应,凡事有短就有长,要有度嘛~)。

(3) 按下了多个键(与我的应用需要相悖,所以非法)。这里有可能是同时按的,也有可能是异步按下的。

(4) 快速多次按同一个键。这种情况可以归结到(1)。

(5) 我觉得没有了,元芳,你怎么看??

全部回复(67)
正序查看
倒序查看
cltwolf
LV.5
2
2014-02-13 17:19

您老是要讨论还有没有其他按按键的方式吗?

还是说解决这些非法操作的方式?

0
回复
2014-02-27 00:47

  3 小综合(理综还是文综??)

  综合1和2进行考虑,得出以下几条。

  (1) 正常的单击和长击示意图。

  图2.3.1短击识别示意图

  图2.3.2长击识别示意图

  (2) 按键时间过短,应丢弃。

  图2.3.3按键时间过短示意图

  (3) 多键同时按下时,不响应。

  图2.3.4多键同时按下示意图

  (4) 多键异步按下

  图2.3.5多键异步按下示意图

  这种情况要考虑一下了。如图2.3.5所示。这种情况不会影响到单击。看图2.3.1可知,单击是在按键松开后才进行识别的;但这种情况对长击有影响,如果t1大于长击的阀值,那么键1是否应该识别为一次有效长击呢?这里小白菜认为键1有效,后面的程序也是按键1有效进行的处理。

  既然认为有效,那么这个过程可以分解为两个部分,键1闭合到键2闭合这部分认为是一次合法的长击行为,键2闭合以后认为是“多键同时按下”,按出错处理。

  (5) 多键“前仆后继”地按下

  图2.3.6

  这里仍然存在(4)中存在的长击问题。这里小白菜依旧认为长击有效。并且不再响应后面的按键。

  (6) 按键因为受到干扰而出现了假松开。如图2.3.7中的尖峰所示。

  图2.3.7

  对付这种假松开,小白菜想到的是设定按键松开计时器,只有计时器到一定的值,并且在此期间,读取到的状态都是键松开,才真正认为是键松开。小白菜在最开始的设计时并未考虑到这种情况的出现,是在写本文的时候才考虑到的,所以第一版未对假松开做处理,但在测试时,无论多么复杂错误的按键,程序未出现误动作,也许只有强干扰才能检验了。在本次发布的代码中,小白菜对假松开作了处理,但由于周末,未能测试。请万能的网友们来进行吧,别忘记把结果(最主要的是程序中的BUG)告诉我一声。

  如果按键完全闭合后还口线电平还到达能够识别的高度,那么这个干扰也够强烈的,你说对么,元芳?

  (7) 还有其他错误情况吗,元芳??

0
回复
2014-02-27 00:58
@爱在春天
  3小综合(理综还是文综??)  综合1和2进行考虑,得出以下几条。  (1)正常的单击和长击示意图。[图片]  图2.3.1短击识别示意图[图片]  图2.3.2长击识别示意图  (2)按键时间过短,应丢弃。[图片]  图2.3.3按键时间过短示意图  (3)多键同时按下时,不响应。[图片]  图2.3.4多键同时按下示意图  (4)多键异步按下[图片]  图2.3.5多键异步按下示意图  这种情况要考虑一下了。如图2.3.5所示。这种情况不会影响到单击。看图2.3.1可知,单击是在按键松开后才进行识别的;但这种情况对长击有影响,如果t1大于长击的阀值,那么键1是否应该识别为一次有效长击呢?这里小白菜认为键1有效,后面的程序也是按键1有效进行的处理。  既然认为有效,那么这个过程可以分解为两个部分,键1闭合到键2闭合这部分认为是一次合法的长击行为,键2闭合以后认为是“多键同时按下”,按出错处理。  (5)多键“前仆后继”地按下[图片]  图2.3.6  这里仍然存在(4)中存在的长击问题。这里小白菜依旧认为长击有效。并且不再响应后面的按键。  (6)按键因为受到干扰而出现了假松开。如图2.3.7中的尖峰所示。[图片]  图2.3.7  对付这种假松开,小白菜想到的是设定按键松开计时器,只有计时器到一定的值,并且在此期间,读取到的状态都是键松开,才真正认为是键松开。小白菜在最开始的设计时并未考虑到这种情况的出现,是在写本文的时候才考虑到的,所以第一版未对假松开做处理,但在测试时,无论多么复杂错误的按键,程序未出现误动作,也许只有强干扰才能检验了。在本次发布的代码中,小白菜对假松开作了处理,但由于周末,未能测试。请万能的网友们来进行吧,别忘记把结果(最主要的是程序中的BUG)告诉我一声。  如果按键完全闭合后还口线电平还到达能够识别的高度,那么这个干扰也够强烈的,你说对么,元芳?  (7)还有其他错误情况吗,元芳??
三 “伪状态”的抽象
为什么要加上“伪”字呢,因为小白菜对于状态机这个东西,认识的不够清楚,为了避免老鸟们的拍砖,加上伪字。嘎嘎~
根据第二部分,小白菜大致能抽象出一些东西了。
1 确定所使用的变量
(1) 首先要有一个按键状态是否正确合法的标志 KeyErrFlg。
(2) 按键时长计时器KeyTimer。用这个变量来确定键按下了多长时间。
单击时间范围 TS < t < TL 。这里Ts代表短按最短时间,TL 代表长按最短时间。
长击时间范围 t ≥ TL 。
(3) 为了防止第二部分图2.3.6【多键“前仆后继”地按下】这种情况的出现,小白菜自以为是的增加了两个变量,当前按键值KeyCurrent和上次按键值KeyLast。通过两个变量的比较来防止二(5)的出现。
(4) 键松开计时器KeyOffTimer。
设置本变量主要是为了防止图2.3.7【假松开】情况的出现。KeyOffTimer必须大于某个值才算完全松开。
这里为何不不对键按下时做处理呢(出现假按下的情况)?因为假按下的延续时间比较短,达不到我们所设置的Ts时间,所以可以忽略。如果假按下的时间超过了Ts,让你来处理的话,你还认为是假按下?
(5) 键值寄存器 KeyValue和KeyReg,用户可以直接查询变量KeyValue,最重要的是不需要用户清零;而KeyReg则由检测程序和键值读取函数使用,用户不可使用。
2 所有可能的情况
KeyErrFlg :         TRUE = 无错误,ERR = 有错误。

KeyCurrent和KeyLast:NULL = 本次无键按下,OK = 键值合法,ERR = 键值非法。

3 “伪”状态转换图

0
回复
2014-02-27 01:01
@爱在春天
三 “伪状态”的抽象为什么要加上“伪”字呢,因为小白菜对于状态机这个东西,认识的不够清楚,为了避免老鸟们的拍砖,加上伪字。嘎嘎~根据第二部分,小白菜大致能抽象出一些东西了。1 确定所使用的变量(1)首先要有一个按键状态是否正确合法的标志KeyErrFlg。(2)按键时长计时器KeyTimer。用这个变量来确定键按下了多长时间。单击时间范围TS
4 程序传统流程图
这里面需要注意一点,在长按键的识别时,有两种方法,一是检查KeyTimer == TL 这样可以保证只识别一次,二是检查KeyTimer >= TL 然后把KeyErrFlg置为Err,这样可以转换其状态,另其不再识别按键。第一种方法的好处是清晰明了,但是却仅检查了TL 这一个点,小白菜很担心,也许你会说,正常运行时没问题,但是,我就怕不正常运行;基于安全的考虑,小白菜选用了方法二。
图3.4.1传统流程图
0
回复
2014-02-27 01:11
@爱在春天
4 程序传统流程图这里面需要注意一点,在长按键的识别时,有两种方法,一是检查KeyTimer==TL 这样可以保证只识别一次,二是检查KeyTimer>= TL 然后把KeyErrFlg置为Err,这样可以转换其状态,另其不再识别按键。第一种方法的好处是清晰明了,但是却仅检查了TL 这一个点,小白菜很担心,也许你会说,正常运行时没问题,但是,我就怕不正常运行;基于安全的考虑,小白菜选用了方法二。[图片]图3.4.1传统流程图
五应用说明
说了半天,还没有中断的事。下面说一下,同学们,注意听哈。
1 while(1)式检测和中断式检测的对比
(1) 在网上也看过一些人发的按键检测,自己以前也写过按键检测,但是无论这些检测程序多么美妙,绝大部分是在while(1)里执行按键检测程序(先管它叫while(1)式吧),这其实有一个很大的隐患。
当MCU负担不重时,while(1)式检测程序也许能好好的工作,这是由于while(1)的平均执行时间(不是最大执行时间)与按键检测程序的最长调用间隔时间相差无几或更小。但当MCU负担很重时,while(1)式反应就会明显变慢,甚至会丢失按键信息;这是由于while(1)的平均执行时间过大所造成的。举个极端例子,while(1)平均执行时间为1000ms,短按时间为300ms,显然按键检测程序会丢失按键信息。
一句话总结while(1)式的按键检测程序:时间不可控,耗时长,易丢失信息。
(2) 下面说一下在定时中断服务函数中进行按键检测(管它叫中断式吧)的优缺点。
首先说缺点:
由于是在中断中执行检测程序,增加的中断执行时间。但由于仅仅是几个【字节变量】的大小判断,所以耗时很短,小白菜曾在自己所用的平台上测试过,不超过50us,对,没错,就是50us不是50ms,而我的中断时间是10ms对其的影响小于5‰。
下面说一下优点。
优点一:中断式检测的调用时间是可预测,是可控的。中断的发生和while(1)的执行可以看成是并行的(老鸟不要拍砖啊),这显然为按键检测程序按我们设定的间隔执行。
优点二:几乎每个系统中都会出现定时中断(这里不用RTOS,因为小白菜不懂RTOS,只会裸了个奔),并且小白菜接触到的系统都是以10ms,20ms左右这样的定时中断。
优点三:一但把按键检测程序放在中断处理函数中执行,那我们就不需要再考虑之了,只需要在需要使用按键值的地方(比如菜单)调用一个获取键值函数就可以了,而获取键值函数也仅仅是把一个变量的值返回。
优点四:巧妙的躲过了抖动区。小白菜的系统中用的10ms中断。如图5.1.1所示。

图5.1.1 抖动区示意图
大家知道单片机IO对高低电平识别是有范围的,如图5.1.1中所示,所以,虽然aa’、dd’段存在抖动,但是这一段的电平仍然在“松开”范围内,同理,bb’、cc’段的电平仍在“闭合”的范围内。实际中肯定有ab段时长大于a’b’段时长。而我们真正要躲避的是a’b’和c’d’段,这才是真正的抖动区,因为其处于单片机不能识别的电平范围。
一般地,抖动区在10ms左右(我是听说的,这个我真没测试过),而我的中断时间是10ms,由图5.1.1可以,正好可以完美地跨过ab段和cd段,同要也跨过了a’b’和c’d’,从而实现了利用中断间隔达到普通延时去抖动的目的。
小白在实验定时间隔时发现,10-20ms的定时时间对按键响应比较好。中断时间过短,会因中断太频繁而降低系统性能;中断时间过长又不能及时地响应短按键。万能的亲们,你们自己试试吧。
0
回复
2014-02-27 01:12
@爱在春天
五应用说明说了半天,还没有中断的事。下面说一下,同学们,注意听哈。1while(1)式检测和中断式检测的对比(1)在网上也看过一些人发的按键检测,自己以前也写过按键检测,但是无论这些检测程序多么美妙,绝大部分是在while(1)里执行按键检测程序(先管它叫while(1)式吧),这其实有一个很大的隐患。当MCU负担不重时,while(1)式检测程序也许能好好的工作,这是由于while(1)的平均执行时间(不是最大执行时间)与按键检测程序的最长调用间隔时间相差无几或更小。但当MCU负担很重时,while(1)式反应就会明显变慢,甚至会丢失按键信息;这是由于while(1)的平均执行时间过大所造成的。举个极端例子,while(1)平均执行时间为1000ms,短按时间为300ms,显然按键检测程序会丢失按键信息。一句话总结while(1)式的按键检测程序:时间不可控,耗时长,易丢失信息。(2)下面说一下在定时中断服务函数中进行按键检测(管它叫中断式吧)的优缺点。首先说缺点:由于是在中断中执行检测程序,增加的中断执行时间。但由于仅仅是几个【字节变量】的大小判断,所以耗时很短,小白菜曾在自己所用的平台上测试过,不超过50us,对,没错,就是50us不是50ms,而我的中断时间是10ms对其的影响小于5‰。下面说一下优点。优点一:中断式检测的调用时间是可预测,是可控的。中断的发生和while(1)的执行可以看成是并行的(老鸟不要拍砖啊),这显然为按键检测程序按我们设定的间隔执行。优点二:几乎每个系统中都会出现定时中断(这里不用RTOS,因为小白菜不懂RTOS,只会裸了个奔),并且小白菜接触到的系统都是以10ms,20ms左右这样的定时中断。优点三:一但把按键检测程序放在中断处理函数中执行,那我们就不需要再考虑之了,只需要在需要使用按键值的地方(比如菜单)调用一个获取键值函数就可以了,而获取键值函数也仅仅是把一个变量的值返回。优点四:巧妙的躲过了抖动区。小白菜的系统中用的10ms中断。如图5.1.1所示。[图片]图5.1.1抖动区示意图大家知道单片机IO对高低电平识别是有范围的,如图5.1.1中所示,所以,虽然aa’、dd’段存在抖动,但是这一段的电平仍然在“松开”范围内,同理,bb’、cc’段的电平仍在“闭合”的范围内。实际中肯定有ab段时长大于a’b’段时长。而我们真正要躲避的是a’b’和c’d’段,这才是真正的抖动区,因为其处于单片机不能识别的电平范围。一般地,抖动区在10ms左右(我是听说的,这个我真没测试过),而我的中断时间是10ms,由图5.1.1可以,正好可以完美地跨过ab段和cd段,同要也跨过了a’b’和c’d’,从而实现了利用中断间隔达到普通延时去抖动的目的。小白在实验定时间隔时发现,10-20ms的定时时间对按键响应比较好。中断时间过短,会因中断太频繁而降低系统性能;中断时间过长又不能及时地响应短按键。万能的亲们,你们自己试试吧。
2 函数说明,首先申明,发布的代码未测试过,也未编译过!
(1)  externvoid App_Init_Key(void);按键检测初始化函数。必须在中断服务程序执行前调用,使变量一个合法的初始值开始运行,以防止因为变量随机值而出现误动作的情况!!!
(2)  externvoid App_Detect_Key(void); 核心函数,好吧,直接放到定时中断服务程序中就行了。不需要做什么工作。
(3)  externuint8 App_Get_Key_Value(void);读取按键函数。返回值是当前的键值,一定要在使用键值前先调用本函数,否则会出现严重的错误!在一个循环中本函数只能调用一次,请看一下本函数的设计,你会明白为什么。
3 简单的应用举例
主函数中
void main(void)
{
    uint8 u8Key;
App_Init_Key();    //初始化按键
xxx();//初始化定时器
while(1)
{
    u8Key = App_Get_Key_Value();  // 读取键值
    if(KEY_NULL == u8Key)
       {// 什么都不做
     }
   else if(xxxx1)
   { // 处理1
   }
}
}
中断ISR
Tn_ISR()
{
    App_Detect_Key();
}
0
回复
2amor
LV.2
8
2014-02-27 01:13
@爱在春天
2 函数说明,首先申明,发布的代码未测试过,也未编译过!(1)  externvoidApp_Init_Key(void);按键检测初始化函数。必须在中断服务程序执行前调用,使变量一个合法的初始值开始运行,以防止因为变量随机值而出现误动作的情况!!!(2)  externvoidApp_Detect_Key(void);核心函数,好吧,直接放到定时中断服务程序中就行了。不需要做什么工作。(3)  externuint8App_Get_Key_Value(void);读取按键函数。返回值是当前的键值,一定要在使用键值前先调用本函数,否则会出现严重的错误!在一个循环中本函数只能调用一次,请看一下本函数的设计,你会明白为什么。3 简单的应用举例主函数中voidmain(void){  uint8u8Key;App_Init_Key();  //初始化按键xxx();//初始化定时器while(1){  u8Key=App_Get_Key_Value();  //读取键值  if(KEY_NULL==u8Key)    {//什么都不做    }  elseif(xxxx1)  { //处理1  }}}中断ISRTn_ISR(){  App_Detect_Key();}
来看看 不错哦
0
回复
2014-02-27 01:15
@爱在春天
2 函数说明,首先申明,发布的代码未测试过,也未编译过!(1)  externvoidApp_Init_Key(void);按键检测初始化函数。必须在中断服务程序执行前调用,使变量一个合法的初始值开始运行,以防止因为变量随机值而出现误动作的情况!!!(2)  externvoidApp_Detect_Key(void);核心函数,好吧,直接放到定时中断服务程序中就行了。不需要做什么工作。(3)  externuint8App_Get_Key_Value(void);读取按键函数。返回值是当前的键值,一定要在使用键值前先调用本函数,否则会出现严重的错误!在一个循环中本函数只能调用一次,请看一下本函数的设计,你会明白为什么。3 简单的应用举例主函数中voidmain(void){  uint8u8Key;App_Init_Key();  //初始化按键xxx();//初始化定时器while(1){  u8Key=App_Get_Key_Value();  //读取键值  if(KEY_NULL==u8Key)    {//什么都不做    }  elseif(xxxx1)  { //处理1  }}}中断ISRTn_ISR(){  App_Detect_Key();}
写的很详细,想交流下用的是什么单片机呢,51么?这样子的话,是不好处理,要是换一种其他的单片机估计会容易写程序写。430 单片机的P1 和 P2端口均可配置成外部中断输入模式,这样子把按键配置成中断,并且一个8个引脚共用一个中断向量,这样子就很容易处理你所说按键按下时间很接近的情况,程序会简单些。stm32单片机所有的io端口均可以配置成外部中断输入(但总数不超过16个),并且还可以配置成双边沿触发。并且以上两种单片机的定时器时钟可配置,方便做ms级别的计数器...个人愚见。
0
回复
2014-02-27 01:15
@闪闪雪绒花
写的很详细,想交流下用的是什么单片机呢,51么?这样子的话,是不好处理,要是换一种其他的单片机估计会容易写程序写。430单片机的P1和P2端口均可配置成外部中断输入模式,这样子把按键配置成中断,并且一个8个引脚共用一个中断向量,这样子就很容易处理你所说按键按下时间很接近的情况,程序会简单些。stm32单片机所有的io端口均可以配置成外部中断输入(但总数不超过16个),并且还可以配置成双边沿触发。并且以上两种单片机的定时器时钟可配置,方便做ms级别的计数器...个人愚见。
还没用过430和stm32呢。
0
回复
2014-02-27 01:16
@爱在春天
2 函数说明,首先申明,发布的代码未测试过,也未编译过!(1)  externvoidApp_Init_Key(void);按键检测初始化函数。必须在中断服务程序执行前调用,使变量一个合法的初始值开始运行,以防止因为变量随机值而出现误动作的情况!!!(2)  externvoidApp_Detect_Key(void);核心函数,好吧,直接放到定时中断服务程序中就行了。不需要做什么工作。(3)  externuint8App_Get_Key_Value(void);读取按键函数。返回值是当前的键值,一定要在使用键值前先调用本函数,否则会出现严重的错误!在一个循环中本函数只能调用一次,请看一下本函数的设计,你会明白为什么。3 简单的应用举例主函数中voidmain(void){  uint8u8Key;App_Init_Key();  //初始化按键xxx();//初始化定时器while(1){  u8Key=App_Get_Key_Value();  //读取键值  if(KEY_NULL==u8Key)    {//什么都不做    }  elseif(xxxx1)  { //处理1  }}}中断ISRTn_ISR(){  App_Detect_Key();}
学习
0
回复
2014-02-27 01:17
@爱在春天
2 函数说明,首先申明,发布的代码未测试过,也未编译过!(1)  externvoidApp_Init_Key(void);按键检测初始化函数。必须在中断服务程序执行前调用,使变量一个合法的初始值开始运行,以防止因为变量随机值而出现误动作的情况!!!(2)  externvoidApp_Detect_Key(void);核心函数,好吧,直接放到定时中断服务程序中就行了。不需要做什么工作。(3)  externuint8App_Get_Key_Value(void);读取按键函数。返回值是当前的键值,一定要在使用键值前先调用本函数,否则会出现严重的错误!在一个循环中本函数只能调用一次,请看一下本函数的设计,你会明白为什么。3 简单的应用举例主函数中voidmain(void){  uint8u8Key;App_Init_Key();  //初始化按键xxx();//初始化定时器while(1){  u8Key=App_Get_Key_Value();  //读取键值  if(KEY_NULL==u8Key)    {//什么都不做    }  elseif(xxxx1)  { //处理1  }}}中断ISRTn_ISR(){  App_Detect_Key();}
基本的東西,很重要
0
回复
2014-02-27 01:18
@爱在春天
2 函数说明,首先申明,发布的代码未测试过,也未编译过!(1)  externvoidApp_Init_Key(void);按键检测初始化函数。必须在中断服务程序执行前调用,使变量一个合法的初始值开始运行,以防止因为变量随机值而出现误动作的情况!!!(2)  externvoidApp_Detect_Key(void);核心函数,好吧,直接放到定时中断服务程序中就行了。不需要做什么工作。(3)  externuint8App_Get_Key_Value(void);读取按键函数。返回值是当前的键值,一定要在使用键值前先调用本函数,否则会出现严重的错误!在一个循环中本函数只能调用一次,请看一下本函数的设计,你会明白为什么。3 简单的应用举例主函数中voidmain(void){  uint8u8Key;App_Init_Key();  //初始化按键xxx();//初始化定时器while(1){  u8Key=App_Get_Key_Value();  //读取键值  if(KEY_NULL==u8Key)    {//什么都不做    }  elseif(xxxx1)  { //处理1  }}}中断ISRTn_ISR(){  App_Detect_Key();}
感谢分享
0
回复
2014-02-27 01:20
@爱在春天
2 函数说明,首先申明,发布的代码未测试过,也未编译过!(1)  externvoidApp_Init_Key(void);按键检测初始化函数。必须在中断服务程序执行前调用,使变量一个合法的初始值开始运行,以防止因为变量随机值而出现误动作的情况!!!(2)  externvoidApp_Detect_Key(void);核心函数,好吧,直接放到定时中断服务程序中就行了。不需要做什么工作。(3)  externuint8App_Get_Key_Value(void);读取按键函数。返回值是当前的键值,一定要在使用键值前先调用本函数,否则会出现严重的错误!在一个循环中本函数只能调用一次,请看一下本函数的设计,你会明白为什么。3 简单的应用举例主函数中voidmain(void){  uint8u8Key;App_Init_Key();  //初始化按键xxx();//初始化定时器while(1){  u8Key=App_Get_Key_Value();  //读取键值  if(KEY_NULL==u8Key)    {//什么都不做    }  elseif(xxxx1)  { //处理1  }}}中断ISRTn_ISR(){  App_Detect_Key();}
实际项目就是要这种。
0
回复
2014-02-27 01:23
@爱在春天
2 函数说明,首先申明,发布的代码未测试过,也未编译过!(1)  externvoidApp_Init_Key(void);按键检测初始化函数。必须在中断服务程序执行前调用,使变量一个合法的初始值开始运行,以防止因为变量随机值而出现误动作的情况!!!(2)  externvoidApp_Detect_Key(void);核心函数,好吧,直接放到定时中断服务程序中就行了。不需要做什么工作。(3)  externuint8App_Get_Key_Value(void);读取按键函数。返回值是当前的键值,一定要在使用键值前先调用本函数,否则会出现严重的错误!在一个循环中本函数只能调用一次,请看一下本函数的设计,你会明白为什么。3 简单的应用举例主函数中voidmain(void){  uint8u8Key;App_Init_Key();  //初始化按键xxx();//初始化定时器while(1){  u8Key=App_Get_Key_Value();  //读取键值  if(KEY_NULL==u8Key)    {//什么都不做    }  elseif(xxxx1)  { //处理1  }}}中断ISRTn_ISR(){  App_Detect_Key();}
必须好文章,拜读一下
0
回复
My_sunshine
LV.2
16
2014-02-27 01:24
@爱在春天
2 函数说明,首先申明,发布的代码未测试过,也未编译过!(1)  externvoidApp_Init_Key(void);按键检测初始化函数。必须在中断服务程序执行前调用,使变量一个合法的初始值开始运行,以防止因为变量随机值而出现误动作的情况!!!(2)  externvoidApp_Detect_Key(void);核心函数,好吧,直接放到定时中断服务程序中就行了。不需要做什么工作。(3)  externuint8App_Get_Key_Value(void);读取按键函数。返回值是当前的键值,一定要在使用键值前先调用本函数,否则会出现严重的错误!在一个循环中本函数只能调用一次,请看一下本函数的设计,你会明白为什么。3 简单的应用举例主函数中voidmain(void){  uint8u8Key;App_Init_Key();  //初始化按键xxx();//初始化定时器while(1){  u8Key=App_Get_Key_Value();  //读取键值  if(KEY_NULL==u8Key)    {//什么都不做    }  elseif(xxxx1)  { //处理1  }}}中断ISRTn_ISR(){  App_Detect_Key();}
谢谢楼主分享
0
回复
2014-02-27 01:26
@爱在春天
2 函数说明,首先申明,发布的代码未测试过,也未编译过!(1)  externvoidApp_Init_Key(void);按键检测初始化函数。必须在中断服务程序执行前调用,使变量一个合法的初始值开始运行,以防止因为变量随机值而出现误动作的情况!!!(2)  externvoidApp_Detect_Key(void);核心函数,好吧,直接放到定时中断服务程序中就行了。不需要做什么工作。(3)  externuint8App_Get_Key_Value(void);读取按键函数。返回值是当前的键值,一定要在使用键值前先调用本函数,否则会出现严重的错误!在一个循环中本函数只能调用一次,请看一下本函数的设计,你会明白为什么。3 简单的应用举例主函数中voidmain(void){  uint8u8Key;App_Init_Key();  //初始化按键xxx();//初始化定时器while(1){  u8Key=App_Get_Key_Value();  //读取键值  if(KEY_NULL==u8Key)    {//什么都不做    }  elseif(xxxx1)  { //处理1  }}}中断ISRTn_ISR(){  App_Detect_Key();}
占位,收录
0
回复
2014-02-27 01:27
@爱在春天
2 函数说明,首先申明,发布的代码未测试过,也未编译过!(1)  externvoidApp_Init_Key(void);按键检测初始化函数。必须在中断服务程序执行前调用,使变量一个合法的初始值开始运行,以防止因为变量随机值而出现误动作的情况!!!(2)  externvoidApp_Detect_Key(void);核心函数,好吧,直接放到定时中断服务程序中就行了。不需要做什么工作。(3)  externuint8App_Get_Key_Value(void);读取按键函数。返回值是当前的键值,一定要在使用键值前先调用本函数,否则会出现严重的错误!在一个循环中本函数只能调用一次,请看一下本函数的设计,你会明白为什么。3 简单的应用举例主函数中voidmain(void){  uint8u8Key;App_Init_Key();  //初始化按键xxx();//初始化定时器while(1){  u8Key=App_Get_Key_Value();  //读取键值  if(KEY_NULL==u8Key)    {//什么都不做    }  elseif(xxxx1)  { //处理1  }}}中断ISRTn_ISR(){  App_Detect_Key();}
研究的很细,支持了
0
回复
2014-02-27 01:28
@爱在春天
2 函数说明,首先申明,发布的代码未测试过,也未编译过!(1)  externvoidApp_Init_Key(void);按键检测初始化函数。必须在中断服务程序执行前调用,使变量一个合法的初始值开始运行,以防止因为变量随机值而出现误动作的情况!!!(2)  externvoidApp_Detect_Key(void);核心函数,好吧,直接放到定时中断服务程序中就行了。不需要做什么工作。(3)  externuint8App_Get_Key_Value(void);读取按键函数。返回值是当前的键值,一定要在使用键值前先调用本函数,否则会出现严重的错误!在一个循环中本函数只能调用一次,请看一下本函数的设计,你会明白为什么。3 简单的应用举例主函数中voidmain(void){  uint8u8Key;App_Init_Key();  //初始化按键xxx();//初始化定时器while(1){  u8Key=App_Get_Key_Value();  //读取键值  if(KEY_NULL==u8Key)    {//什么都不做    }  elseif(xxxx1)  { //处理1  }}}中断ISRTn_ISR(){  App_Detect_Key();}
十分详细啊 一般在公司都要求这些东西的
0
回复
2014-02-27 01:28
@爱在春天
2 函数说明,首先申明,发布的代码未测试过,也未编译过!(1)  externvoidApp_Init_Key(void);按键检测初始化函数。必须在中断服务程序执行前调用,使变量一个合法的初始值开始运行,以防止因为变量随机值而出现误动作的情况!!!(2)  externvoidApp_Detect_Key(void);核心函数,好吧,直接放到定时中断服务程序中就行了。不需要做什么工作。(3)  externuint8App_Get_Key_Value(void);读取按键函数。返回值是当前的键值,一定要在使用键值前先调用本函数,否则会出现严重的错误!在一个循环中本函数只能调用一次,请看一下本函数的设计,你会明白为什么。3 简单的应用举例主函数中voidmain(void){  uint8u8Key;App_Init_Key();  //初始化按键xxx();//初始化定时器while(1){  u8Key=App_Get_Key_Value();  //读取键值  if(KEY_NULL==u8Key)    {//什么都不做    }  elseif(xxxx1)  { //处理1  }}}中断ISRTn_ISR(){  App_Detect_Key();}
学习了
0
回复
2014-02-27 01:29
@爱在春天
2 函数说明,首先申明,发布的代码未测试过,也未编译过!(1)  externvoidApp_Init_Key(void);按键检测初始化函数。必须在中断服务程序执行前调用,使变量一个合法的初始值开始运行,以防止因为变量随机值而出现误动作的情况!!!(2)  externvoidApp_Detect_Key(void);核心函数,好吧,直接放到定时中断服务程序中就行了。不需要做什么工作。(3)  externuint8App_Get_Key_Value(void);读取按键函数。返回值是当前的键值,一定要在使用键值前先调用本函数,否则会出现严重的错误!在一个循环中本函数只能调用一次,请看一下本函数的设计,你会明白为什么。3 简单的应用举例主函数中voidmain(void){  uint8u8Key;App_Init_Key();  //初始化按键xxx();//初始化定时器while(1){  u8Key=App_Get_Key_Value();  //读取键值  if(KEY_NULL==u8Key)    {//什么都不做    }  elseif(xxxx1)  { //处理1  }}}中断ISRTn_ISR(){  App_Detect_Key();}
支持研究
0
回复
2014-02-27 01:30
@爱在春天
2 函数说明,首先申明,发布的代码未测试过,也未编译过!(1)  externvoidApp_Init_Key(void);按键检测初始化函数。必须在中断服务程序执行前调用,使变量一个合法的初始值开始运行,以防止因为变量随机值而出现误动作的情况!!!(2)  externvoidApp_Detect_Key(void);核心函数,好吧,直接放到定时中断服务程序中就行了。不需要做什么工作。(3)  externuint8App_Get_Key_Value(void);读取按键函数。返回值是当前的键值,一定要在使用键值前先调用本函数,否则会出现严重的错误!在一个循环中本函数只能调用一次,请看一下本函数的设计,你会明白为什么。3 简单的应用举例主函数中voidmain(void){  uint8u8Key;App_Init_Key();  //初始化按键xxx();//初始化定时器while(1){  u8Key=App_Get_Key_Value();  //读取键值  if(KEY_NULL==u8Key)    {//什么都不做    }  elseif(xxxx1)  { //处理1  }}}中断ISRTn_ISR(){  App_Detect_Key();}
刚好有需要,可以参考。
0
回复
2014-02-27 01:31
@爱在春天
2 函数说明,首先申明,发布的代码未测试过,也未编译过!(1)  externvoidApp_Init_Key(void);按键检测初始化函数。必须在中断服务程序执行前调用,使变量一个合法的初始值开始运行,以防止因为变量随机值而出现误动作的情况!!!(2)  externvoidApp_Detect_Key(void);核心函数,好吧,直接放到定时中断服务程序中就行了。不需要做什么工作。(3)  externuint8App_Get_Key_Value(void);读取按键函数。返回值是当前的键值,一定要在使用键值前先调用本函数,否则会出现严重的错误!在一个循环中本函数只能调用一次,请看一下本函数的设计,你会明白为什么。3 简单的应用举例主函数中voidmain(void){  uint8u8Key;App_Init_Key();  //初始化按键xxx();//初始化定时器while(1){  u8Key=App_Get_Key_Value();  //读取键值  if(KEY_NULL==u8Key)    {//什么都不做    }  elseif(xxxx1)  { //处理1  }}}中断ISRTn_ISR(){  App_Detect_Key();}
看看,学习
0
回复
2014-02-27 01:32
@爱在春天
2 函数说明,首先申明,发布的代码未测试过,也未编译过!(1)  externvoidApp_Init_Key(void);按键检测初始化函数。必须在中断服务程序执行前调用,使变量一个合法的初始值开始运行,以防止因为变量随机值而出现误动作的情况!!!(2)  externvoidApp_Detect_Key(void);核心函数,好吧,直接放到定时中断服务程序中就行了。不需要做什么工作。(3)  externuint8App_Get_Key_Value(void);读取按键函数。返回值是当前的键值,一定要在使用键值前先调用本函数,否则会出现严重的错误!在一个循环中本函数只能调用一次,请看一下本函数的设计,你会明白为什么。3 简单的应用举例主函数中voidmain(void){  uint8u8Key;App_Init_Key();  //初始化按键xxx();//初始化定时器while(1){  u8Key=App_Get_Key_Value();  //读取键值  if(KEY_NULL==u8Key)    {//什么都不做    }  elseif(xxxx1)  { //处理1  }}}中断ISRTn_ISR(){  App_Detect_Key();}

  我觉得要实现楼主的按键功能,只需一个定时器加按键扫描程序就OK啦!下面是定时器跟按键扫描程序.

  void _timer2Process(void)

  {

  if (TMR2IF)

  {

  TMR2IF=0;

  keyScanCnt++;

  }

  }

  void _keyScan(void)

  {

  if (!isKeyDelay) //按键状态变化该变量也变化,初始化设为0

  {

  if (pinKeyTest^isKeyHigh)

  {

  isKeyHigh=pinKeyTest;//isKeyHigh=0为按键按下的状态,初始化设为1

  isKeyDelay=Yes;

  keyScanCnt=0;

  }

  }

  else

  {

  if (pinKeyTest^isKeyHigh)//按键弹起后的去抖动

  {

  keyScanCnt=0;

  isKeyHigh=pinKeyTest;

  }//按键弹起后去抖动

  else

  {

  if (conKeyLongTime

  {

  isLongTest=Yes;

  isKeyDelay=No;

  }

  else

  {

  if (conKeyShortTime

  {

  if (!isKeyHigh)

  {

  isKeyPress=Yes;

  }

  else //key release

  {

  isKeyDelay=No;

  if (isKeyPress)

  {

  isKeyPressOK=Yes;

  isKeyPress=No;

  }

  }

  }

  }

  }

  }

  }

  然后在按键处理程序中判断变量isLongTest和isKeyPressOK,isLongTest=1为长按(达到阀值时就响应无需等待按键释放),

  isKeyPressOK=1为短按(按键释放才响应).

0
回复
2014-02-27 01:33
@浓妆淡抹总相宜
  我觉得要实现楼主的按键功能,只需一个定时器加按键扫描程序就OK啦!下面是定时器跟按键扫描程序.  void_timer2Process(void)  {  if(TMR2IF)  {  TMR2IF=0;  keyScanCnt++;  }  }  void_keyScan(void)  {  if(!isKeyDelay)//按键状态变化该变量也变化,初始化设为0  {  if(pinKeyTest^isKeyHigh)  {  isKeyHigh=pinKeyTest;//isKeyHigh=0为按键按下的状态,初始化设为1  isKeyDelay=Yes;  keyScanCnt=0;  }  }  else  {  if(pinKeyTest^isKeyHigh)//按键弹起后的去抖动  {  keyScanCnt=0;  isKeyHigh=pinKeyTest;  }//按键弹起后去抖动  else  {  if(conKeyLongTime
我只写了定时器处理程序跟按键扫描程序,还有定时器初始化跟按键处理程序还需读者去写这些都很简单..就当练手贝...MCU是PIC16F877.
0
回复
2014-02-27 01:33
@爱在春天
2 函数说明,首先申明,发布的代码未测试过,也未编译过!(1)  externvoidApp_Init_Key(void);按键检测初始化函数。必须在中断服务程序执行前调用,使变量一个合法的初始值开始运行,以防止因为变量随机值而出现误动作的情况!!!(2)  externvoidApp_Detect_Key(void);核心函数,好吧,直接放到定时中断服务程序中就行了。不需要做什么工作。(3)  externuint8App_Get_Key_Value(void);读取按键函数。返回值是当前的键值,一定要在使用键值前先调用本函数,否则会出现严重的错误!在一个循环中本函数只能调用一次,请看一下本函数的设计,你会明白为什么。3 简单的应用举例主函数中voidmain(void){  uint8u8Key;App_Init_Key();  //初始化按键xxx();//初始化定时器while(1){  u8Key=App_Get_Key_Value();  //读取键值  if(KEY_NULL==u8Key)    {//什么都不做    }  elseif(xxxx1)  { //处理1  }}}中断ISRTn_ISR(){  App_Detect_Key();}
强烈支持
0
回复
2014-02-27 01:34
@爱在春天
2 函数说明,首先申明,发布的代码未测试过,也未编译过!(1)  externvoidApp_Init_Key(void);按键检测初始化函数。必须在中断服务程序执行前调用,使变量一个合法的初始值开始运行,以防止因为变量随机值而出现误动作的情况!!!(2)  externvoidApp_Detect_Key(void);核心函数,好吧,直接放到定时中断服务程序中就行了。不需要做什么工作。(3)  externuint8App_Get_Key_Value(void);读取按键函数。返回值是当前的键值,一定要在使用键值前先调用本函数,否则会出现严重的错误!在一个循环中本函数只能调用一次,请看一下本函数的设计,你会明白为什么。3 简单的应用举例主函数中voidmain(void){  uint8u8Key;App_Init_Key();  //初始化按键xxx();//初始化定时器while(1){  u8Key=App_Get_Key_Value();  //读取键值  if(KEY_NULL==u8Key)    {//什么都不做    }  elseif(xxxx1)  { //处理1  }}}中断ISRTn_ISR(){  App_Detect_Key();}
很有高手风范
0
回复
2014-02-27 01:35
@爱在春天
2 函数说明,首先申明,发布的代码未测试过,也未编译过!(1)  externvoidApp_Init_Key(void);按键检测初始化函数。必须在中断服务程序执行前调用,使变量一个合法的初始值开始运行,以防止因为变量随机值而出现误动作的情况!!!(2)  externvoidApp_Detect_Key(void);核心函数,好吧,直接放到定时中断服务程序中就行了。不需要做什么工作。(3)  externuint8App_Get_Key_Value(void);读取按键函数。返回值是当前的键值,一定要在使用键值前先调用本函数,否则会出现严重的错误!在一个循环中本函数只能调用一次,请看一下本函数的设计,你会明白为什么。3 简单的应用举例主函数中voidmain(void){  uint8u8Key;App_Init_Key();  //初始化按键xxx();//初始化定时器while(1){  u8Key=App_Get_Key_Value();  //读取键值  if(KEY_NULL==u8Key)    {//什么都不做    }  elseif(xxxx1)  { //处理1  }}}中断ISRTn_ISR(){  App_Detect_Key();}
谢谢分享,学到了很多,很有启发,真的很不错!
0
回复
2014-02-27 01:37
@爱在春天
2 函数说明,首先申明,发布的代码未测试过,也未编译过!(1)  externvoidApp_Init_Key(void);按键检测初始化函数。必须在中断服务程序执行前调用,使变量一个合法的初始值开始运行,以防止因为变量随机值而出现误动作的情况!!!(2)  externvoidApp_Detect_Key(void);核心函数,好吧,直接放到定时中断服务程序中就行了。不需要做什么工作。(3)  externuint8App_Get_Key_Value(void);读取按键函数。返回值是当前的键值,一定要在使用键值前先调用本函数,否则会出现严重的错误!在一个循环中本函数只能调用一次,请看一下本函数的设计,你会明白为什么。3 简单的应用举例主函数中voidmain(void){  uint8u8Key;App_Init_Key();  //初始化按键xxx();//初始化定时器while(1){  u8Key=App_Get_Key_Value();  //读取键值  if(KEY_NULL==u8Key)    {//什么都不做    }  elseif(xxxx1)  { //处理1  }}}中断ISRTn_ISR(){  App_Detect_Key();}
看一下
0
回复
2014-02-27 01:37
学习一下!
0
回复
2014-02-27 01:38
看看
0
回复