请马老师指教:有关按键问题----建议学电子技术的大学生尝试思考和回答__马老师摆擂台
http://cache.amobbs.com/bbs_upload782111/files_11/ourdev_439830.jpg(原文件名:无标题.jpg)
马老师:
在上图中,如果要检测按键的按下,如果要求在按键按下直至按键松开之后才定义一次按键完成,那么程序上如何检测P3。7的这种变化呢?(因P3。7这时的电平变化是一系列脉冲的)。请马老师指教,不胜感激。
=======================================================================================
首先谢谢捧场的朋友们。
我把题目总结归纳,给出更具体的需求,便于大家出招打擂。
1。原理图以楼主位的为基础,可以做稍微的改动,但要保持I/O复用的基本原则,也就是说,不能增加太多的I/O。最好还是原图的数目。
2。要保证6个LED数码管显示正常,按键检测过程中不能出现闪烁、抖动、或破坏显示的现象
3。系统需要使用组合键。即要能“同时”检测出多个键按下
4。要考虑按键按下和释放过程中的消抖,消抖时间在10ms-20ms之间
5。LZ位的要求是“在按键按下直至按键松开之后才定义一次按键完成”。这就是说不具备按键的“连_发”功能。我把要求改动为:按键不具备“连_发”功能。这样的话,按键稳定按下后就可以执行按键操作,但下一次按键的检测必需等待本次按键完全释放。这不违背LZ的“在按键按下直至按键松开之后才定义一次按键完成”原则,但在实际操作中用户使用方便。否则用户按下键后,没有见到相应的反映,会一直按着不放,这样这个按键过程会“永远”完不成了。
6。给出相应的C代码(主要是显示和读键部分,以及上层的调用关系等),和电路图(如果改动的话)。但显示扫描和消抖过程禁止使用软件延时,必须考虑使用定时器中断。
擂主:本专栏斑竹
在71楼我已经出了试探招数,亮了点底牌,请后来者看过后斟酌仔细,然后再杀上前来也。
=====================================================================================
擂主本人在93楼使出了绝招,剑法如下:
一、硬件原理图。就是LZ的图:
http://cache.amobbs.com/bbs_upload782111/files_11/ourdev_439830.jpg
(原LZ位图)
只是里面有点BUG,不能多键按下,按50楼的图改动,增加5个二极管。
http://cache.amobbs.com/bbs_upload782111/files_11/ourdev_443092.jpg
(原50楼硬件修改图)
至于改动原因,就不说了,前面已经有点评了。
另外,电路中的LS47可以省掉,直接用P1输出8位段码(89C2051的驱动能力10mA,点1个LED没问题),反正其它的口也没用吗。
二、具体功能,也是按LZ位制定的原则
1。要保证6个LED数码管显示正常,按键检测过程中不能出现闪烁、抖动、或破坏显示的现象
2。系统需要使用组合键。即要能“同时”检测出多个键按下
3。要考虑按键按下和释放过程中的消抖,消抖时间在10ms-20ms之间
4。LZ位的要求是“在按键按下直至按键松开之后才定义一次按键完成”。这就是说不具备按键的“连_发”功能。我把要求改动为:按键不具备“连_发”功能。这样的话,按键稳定按下后就可以执行按键操作,但下一次按键的检测必需等待本次按键完全释放。这不违背LZ的“在按键按下直至按键松开之后才定义一次按键完成”原则,但在实际操作中用户使用方便。否则用户按下键后,没有见到相应的反映,会一直按着不放,这样这个按键过程会“永远”完不成了。
5。给出相应的C代码(主要是显示和读键部分,以及上层的调用关系等),和电路图(如果改动的话)。但显示扫描和消抖过程禁止使用软件延时,必须考虑使用定时器中断。
三、为了使学51的和AVR的都能使用,代码采用C编写。程序中采用P1、P3表示两个8位的I/O口,AVR可类似对应的使用PORTA,PORTC。键输入为P3.7,对应AVR,可理解为PINC.7。系统6个LED数码管采用动态扫描显示,有5个按键,有一个位选是不复用的。
四、显示扫描时间的计算与确定。每个LED点亮时间为2ms,6个LED循环扫描一遍为12ms。1秒内共循环1000/12=83次,超过25次,保证显示不出现闪烁、抖动等情况。按键读取插在LED的扫描过程中,不另外使用单独的时间。
五、代码框架
//=============定义全局变量
char dis_buff; // 这里存放每个LED显示段码值(BCD格式的)
char key_tmp,key_value = 0x00; // 键值,8位,每位对应一个键,为0表示该位按键,为1表示无。高3位永远为0,
// 因此。0b00011111表示无键按下;0b00011110表示0号键按下,0b00011100则表示0、1号键同时按下的组合键。
//==============主程序
main()
{
while(1)
{
key_tmp = key_value; // 读键值,放在key_tmp中,因key_value会在中断中变化的
if (key_tmp < 128) // 有一次按键过程
{
key_value = 128; // 防止多2次处理
switch(key_tmp) //按键处理过程
{
case 0b00011110:
// 0号键处理.......
break;
case 0b00011101:
// 1号键处理..............
break;
............
case 0b00011100:
// 2键组合键处理(0、1号键按下).....................
break;
............
case 0b00011000
// 3键组合处理(0、1、2号键按下).....................
break;
}
.............
}
}
//--------------------------------------------
主程序就写这么多了,当然初始化部分都剩掉了,其它的系统功能我也不知道,这里重要的是键处理(千万不要在主程序的大循环中使用大量的软件延时,影响switch的执行,这样键的响应就会受到影响了)。另外重要的是我的代码中需要一个2ms的定时中断,不要忘记初始化一个定时器。这个不用写了。
//================================================================
//===================2ms定时中断服务(完成LED扫描显示和读键,消抖,确认的功能)=======
//====注意,此服务2ms执行一次,但执行时间很短======================
//=====此外,为了容易理解,下面是以138的ABC接2051的p3.0/p3.1/p3.2处理,那个JP1的作用就不考虑了,我也不知道他准备做什么===
timer_2ms_int()
{
static char posit = 0;
static char read_key,pre_key,delay_time;
static char key_state = 0;
P3 = 0x87; //关显示,消影(138的ABC = 7,y7输出0,其它为1)
P1 = dis_buff; //送出BCD格式的段码值
P3 = 0x80 | posit; //开相应posit位的LED显示。
if (posit < 5)
{
read_key &= ~(1<<posit);
if (P3.7) read_key |= (1<<posir)); // 5次中断,读到5个键值
}
elas
{ //这段12ms执行一次,消抖,判断都有了
switch(key_state)
{
case 0:
if (pre_key == read_key) // 不管是单键还是多键,必须稳定的按下60ms后才算正式确认一次按键。
{ if (++delay_time>= 5) key_state = 1;} // 调节delay_time的比较值,可以改变稳定确认的时间,如果实际按键
else // 过程中感到120ms比较好,那么(++delay_time>= 10)。
{ delay_time = 0; } // 这里实际已经包含了按键按下和释放的消抖处理。
break;
case 1:
key_value = pre_key; // 判断出一次有效的按键,注意:pre_key的高3位总是“0”
key_state = 2;
break;
case 2:
if (read_key == 0b00011111) // 必须5个键全部释放才能进入下次按键操作
{ // 只要开始发现释放了,就转到状态0,释放消抖在状态0中处理了。
delay_time = 0;
key_state = 0;
}
break;
}
pre_key = read_key;
}
if ( ++posit> 5 ) posit = 0;
}
=====================================================================
在此基础上做修改,可以非常容易的把“连_发”功能加上,不管是单键,还是多键,都能“连_发”。这个还是留给各位去实现吧。
剑法如何,欢迎各位后生评判。
本贴被 machao 编辑过,最后修改时间:2008-10-09,20:31:09. 留做练习题吧,看其它的人能给出解答吗? 在脉冲时检测电平,检测到了,计数器++,到一个数字时,按键按下。检测不到,计数器--,到0时,按键松开。 还有其它办法吗? 这是一个复用I/O口的例子,首先要把硬件电路看明白,先不考虑按键,分析出基本的工作方式,然后看采用什么方法和途径能够非常方便的读键。 难道访问这个专栏的朋友水平都那么的“低”,回答不出?还是水平都非常“高”,不值得回答?
这是一个非常好的综合性的分析题目,尽管没有用AVR,使用的2051,但思路是相同的。实现的方法肯定有不同,大家可以尝试和讨论,看那种最好,最方便。
本站有如此之多的高校版块,应该有很多的学习电子技术的大学生吧,不要老是贴上个什么代码,这个才是真正的学习和思考方法,锻炼一下你们的能力吧。
这个也送给正在听我课的华东师范大学的同学,如果要真正学到点什么,这就是很好的锻炼和学习。
2楼的帖子,并没有清楚的说明问题和给出方法,比如有6个键,如何区别?怎样才能实现“按键按下直至按键松开之后才定义一次按键完成”?如果按键过程中有抖动如何处理?
本贴被 machao 编辑过,最后修改时间:2008-10-06,16:43:50. 通过138位选按键和LED数码管。
1 138【位】选输出高电平,P3.7读,(1)当前【位】选按钮没按下,则P3.7被接地置0,读值0(2)按键按下,则P3.7读高电平(由于下拉电阻10K,并不会把高电平拉到地)。
==========================================
第一点的分析已经错了,后面免谈。:) _________马
==========================================
2 三极管作为LED【位】选驱动,所以138【位】选输出高电平时,三极管导通,【位】选LED数码管亮。由1;按下按键,不拉低三极管基极。
3 同上1,2;当138【位】选LED数码管的同时也【位】选了一个按键。所以可以P3.7可以扫描按键是否按下。
————————————————————————————————————————
程序流程
1 输出138位选信号
2 输出138位选对应的LED数码管段数据,读P3.7值,计入按键状态机变量。
3 延时
4 循环至1
注意:LED扫描周期小于按键件扫描周期。
-/////////////////////
罪过。138位选是低电平,P3.7是上拉VCC,三极管是PNP的,基极接0导通;
P3.7外接电阻向下,就一股脑看成接地了,其它就顺势看反了。
不仔细,就完全反了。罪过罪过。
通过138位选按键和LED数码管。
1 138【位】选输出低电平,P3.7读,(1)当前【位】选按钮没按下,则P3.7被接VCC置1,读值0;(2)按键按下,则P3.7被拉低电平,读0。(由于上拉电阻10K,并不会把低电平拉到VCC)。
2 三极管PNP作为LED【位】选驱动,所以138【位】选输出低电平时,三极管导通,【位】选LED数码管亮。由1;按下按键,不拉高三极管基极。
3 同上1,2;当138【位】选LED数码管的同时也【位】选了一个按键。所以可以P3.7可以扫描按键是否按下。
本贴被 mljda 编辑过,最后修改时间:2008-10-07,08:19:53. p->000 //==》 这时显示的是什么东西?
if(p37==0)s5按下
p->001
if(p37==0)s4按下
...
p->101
if(p37==0)s1按下
重新扫描
本贴被 machao 编辑过,最后修改时间:2008-10-06,23:57:59. 可以把扫描时P3.7电平变化的一系列脉冲依次左移或右移读到一个8bit的变量里,然后比较每次扫描得到的变量和上一次的不同之处就可以了,也就是说可以把这个变量当成PIN寄存器使用(51没用过,不知道有没有这个寄存器,反正就是类似的东西)。
=======================================
这个与51和AVR无关,你可以把控制器看成是AVR的I/O。
“扫描时P3.7电平变化的一系列脉冲”。。。到底是什么脉冲?如果你根本不知道应该是何种脉冲,那么又如何去检测和判断?
本贴被 machao 编辑过,最后修改时间:2008-10-07,00:01:17. 用四个位变量去保存四个按键,在每次扫描时去判断是否升起来了。MS很浪费时间的说。
只不过有一个好处:四个按键不管同时按下几只都可以准确判断哟。 以上各位,首先是你们的硬件水平就太差了,电路如何才能正确工作都分析不正确,后面怎么会继续下去? 阿莫:能否对能比较好的对这个问题给出解答的朋友给予支持:邮购部免费提供100元内的电子元器件。
提高大家的学习兴趣,提倡和支持踏踏实实的学习态度和学习方法。 图太小 上边的网络标号都看不清楚 啊
本贴被 hexixiaomao 编辑过,最后修改时间:2008-10-07,01:29:39. 嘿嘿 用PHOTO SHOP放大了看看
本贴被 hexixiaomao 编辑过,最后修改时间:2008-10-07,01:30:33. 楼上的,丢人丢大了也。
4、5接地没问题的,到是怎么会得出“38译码器任何时候输出都只有一位是1,也就是任何时候都有5个数码管同时被选中啊”的分析?数字逻辑电路没学过138?
138译码器任何时候输出都只有一位是“0”!在图中只能有1个数码管被选中,或一个也不被选中(138的Y6、Y7输出0时)。
逃的还真快:),不用放大,以上的基本东西掌握不好,放大了也没用。
本贴被 machao 编辑过,最后修改时间:2008-10-07,01:42:42. http://cache.amobbs.com/bbs_upload782111/files_11/ourdev_441951.jpg
(原文件名:未标题-1.jpg)
画了个时序图,方便分析。
基本原理:1数码管动态扫描 略
2 关于按键的检测,
在每次扫描的中间时刻插入按键检测子程序,如果在某一次检测中,P3.7为低电平,那么就把
这时被点亮的数码管的序号记录下来。然后就只在这一位数码管被点亮的时候检测P3.7是否为
低电平,如果是计数器num加1,如果没有继续检测到低电平那么就说明按键抬起来了,这时对
num判断如果小于1(或者设定一个最小值)那么就认为按键无效,如果大于 那么就认为按键有
效,将序号送出。
大概具体软件流程
keynum 表示按下的按键序号 0表示无任何按键按下
lednum 被点亮的led的序号
temp
testkey 0,表示未检测到按键,1表示检测到一次按键,程序在计数中。
num 计数器
1初始化
所有状态变量置零。
2开始循环
a 检测keynum ,如果不为零进入按键响应,执行完之后keynum 置零。如果为0继续。
b 点亮一位LED,lednum=此时led的序号。
c延时
d按键检测程序
(1)P3.7是否为0,
如果P3.7不为零那么 检测num是否大于2(或者设定一个最小值),
如果小于 那么就说明按无按键按下或者无效。num=0;temp=0; testkey=0;程序进入下一步e
如果大于2 并且lednum=temp那么就说明这个按键有效,并且已经抬起。testkey=0;keynum=temp;temp=0;num=0;程序进入下一步e
如果P3.7为0 那么检测testkey,
如果为0那么就说明这是第一次检测到低电平,temp=lednum。num++。testkey=1.程序进入下一步e。
如果testkey为1,那么就说明程序在计数中,
如果lednum=temp;num++
e延时
d lednum++
回到2
本贴被 hexixiaomao 编辑过,最后修改时间:2008-10-07,11:05:14. 上边的 程序 不能检测组合键 如果有两个按键同时按下 就只能检测到一个 要想检测组合键 就得为每一个按键分别设置一个变量
分别对其计数 在对应某个的时刻 连续检测到2个(或一个合适的阈值)以上低电平,和一个高电平之后 就认为这个键有效。
本贴被 hexixiaomao 编辑过,最后修改时间:2008-10-07,04:19:04. 不蒸馒头争口气 不能让这的老大们看不起我们新来的菜鸟们。非得整明白不可 。诶 搞得头都大了 先去睡觉咯 。
明天继续!!! 哈哈,马老师上课了,学生也凑个热闹...
用到74HC138的六种状态:
http://cache.amobbs.com/bbs_upload782111/files_11/ourdev_441970.jpg(原文件名:CAP27.jpg)
先设置,使Y0-Y4输出都为高,(例如74HC138的ABC输入分别为:HLH时),扫描P3.7。当有按键时,P3.7读出H状态,说明为有某一个按键按下;
然后改变p3.3~p3.5,动态扫描每一按键。例如:
74HC138的ABC输入分别为:LLL时,74HC138只有Y0输出为零,扫描P3.7。如果读出为L(低),则表示按键S5被按下;
...
该电路缺点:
一次只能够有一个按键按下。如果同时按下多个按键的情况,因为74HC138输出端都连在一起,很可能造成电路短接。
==================================================================
点评:
找到了硬件设计中不合理的地方,值得表扬一下,要加分的。
该电路的毛病就是不能同时按下多个键,这种情况后,轻者是影响到显示,按下键几个对应的LED数码管都亮显示同一个数。重者造成138输出脚烧掉,或系统电源出问题。
正确的应该串入5个二极管。具体可以参考我编写《AVR单片机嵌入式系统原理与应用实践》一书,487页,图16-16。
本贴被 machao 编辑过,最后修改时间:2008-10-07,13:12:42. 数码管在工作时,单片机要不停地对其进行扫描;以楼主的图为例,也就是138的Y0-Y5要不停地依次输出低电平(通过P3.3-P3.5输出译码得到)。为读取按键状况,可以在每扫描一位数码管时(相应的Yx为低电平),读取P3.7的电平;这样对所有数码管扫描一遍之后将得到6个电平值,分别对应六个按键。如果没有键按下,则6个电平量都为高;如果有,则相应的电平值为低。这样既可以检测到按键按下(1->0),也可以检测到按键松开(0->1)。按键的去抖可以把通常方法中的延时换成数个数码管扫描周期即可。 看看,学习一下.
本贴被 AVR51 编辑过,最后修改时间:2008-10-07,14:20:35. 每扫描一位数码管检测一次P3.7的值,如果为低则有按键按下,记录此键值(根据当前显示的位).
如果为高则检查上次记录按键值是否此位,如果是则说明按键松开. 显示和按键程序分开写,可能结构上更合理.
显示时不检测按键,当检测按键时将显示关断(共阳的管,将字段IO口线送高电平),通过138将按键逐位拉低.读P3.7,如果为低则有按键按下,P3.3P3.4P3.5的值就是其键值 马老师上课,我也来丢脸啦!BS上面逃跑的那位!
===>他没有逃,是很快发现自己分析不对,删掉了。但他是第一个比较详细给出按键检测的基本思路的,一直工作到凌晨4点才上床的(我在他后面休息的)。这样的精神值得表扬。
---------------------------------------------------
看了看大家的帖子,其实电路的基本原理都分析出来了,但是马老师的第二个问题还没有解决
“然后看采用什么方法和途径能够非常方便的读键。”
很多方法都可以读取键值,关键在于“非常方便”。即在不会对程序周期造成很大的影响下实现按键判定。
先上硬件电路分析:
此电路用74hc138实现了数码管动态扫描和键盘电路。
显示部分:74HC138将P3.3~P3.5的输出译为选通信号,使相应位的8550导通。从而驱动相应位的数码管共阳极。数码管数据由
74LS47和P1.3送出。
键盘部分:74HC138送出低电平扫描信号。P3.7作为输入端会在得到一个脉冲序列。文字太繁琐,上图:
扫描: 1.__---------- 2.--__-------- 3.----__------ 4.------__---- 5.--------__-- 6.----------__
无按键:1.------------ 2.------------ 3.------------ 4.------------ 5.------------ 6.------------
按键s1:1.------------ 2.------------ 3.------------ 4.------------ 5.--------__-- 6.------------
按键s2:1.------------ 2.------------ 3.------------ 4.------__---- 5.------------ 6.------------
.......
硬件电路的问题:因为显示和键盘扫描公用驱动信号,如果在扫描键盘只是是简单的关显示会造成显示闪烁。
解决方案:
显示和键盘扫描共用一个扫描周期。具体实现方法用程序来解释。
usinged char keyvalue; //取得的键值
usinged char keydownflag;//键盘按下标志
usinged char scaner; //扫描变量
显示延时函数(){
if(P3.7==0)
{
去抖动延时();
if(P3.7==0)keyvalue==scaner; //取键值
}
正常显示延时...
}
键值处理函数(){
if(keyvalue!=0)
{
if(keydownflag==0)
keydownflag=keyvalue; //保存键值
else
continue;
if(keydownflag!=keyvalue)
return error; //重键处理(此程序选取后扫描的键位,相当与可以自由设置按键优先级)
else
continue;
}
else //if(keyvalue==0);
{
if(keydownflag!=0)
return keydownflag; //按键有效,返回键值
else
return 0;
}
return 0;
}
程序通过返回值就可以判定是那位键按下又释放了。
请马老师点评
本贴被 machao 编辑过,最后修改时间:2008-10-07,14:18:26. 首先谢谢捧场的朋友们。
我看了上面的一些回答,有2个帖子非常有价值:16楼和21楼。一个指出了基本的检测思路,一个找到了硬件电路的BUG。但真正比较好的检测方法还是没有出现。
我把题目总结归纳,给出更具体的需求,便于大家讨论。
1。原理图以楼主位的为基础,可以做稍微的改动,但要保持I/O复用的基本原则,也就是说,不能增加太多的I/O。最好还是原图的数目。
2。要保证6个LED数码管显示正常,按键检测过程中不能出现闪烁、抖动、或破坏显示的现象
3。系统需要使用组合键。即要能“同时”检测出多个键按下
4。要考虑按键按下和释放过程中的消抖,消抖时间在10ms-20ms之间
5。LZ位的要求是“在按键按下直至按键松开之后才定义一次按键完成”。这就是说不具备按键的“连_发”功能。我把要求改动为:按键不具备“连_发”功能。这样的话,按键稳定按下后就可以执行按键操作,但下一次按键的检测必需等待本次按键完全释放。这不违背LZ的“在按键按下直至按键松开之后才定义一次按键完成”原则,但在实际操作中用户使用方便。否则用户按下键后,没有见到相应的反映,会一直按着不放,这样这个按键过程会“永远”完不成了。
6。给出相应的代码,和电路图(如果改动的话)。但显示扫描和消抖过程禁止使用软件延时,必须考虑使用定时器中断。
---------------------------------------------------------------------------------
如果有能力,可考虑实现按键“连_发”功能,但与“组合键”功能会有冲突,需要进一步的定义一个组合先导键,该键不能连_发,用于与其它4个键构成组合键。而只其它4个键能连_发。
====================================================================
该题目非常好,如果作为研究生考题的话,我们系刚确定的直升学生中,会有50%的人不及格的。
本贴被 machao 编辑过,最后修改时间:2008-10-07,19:05:26. //多键组合先串连二极管
//不检测按键释放
//显示延时后调用
//返回0xff没有键按下,其它为键值
uchar aaa()
{
static uchar flag; //按键有效
static uchar i; //有键按下计数器
static uchar p; //位显示计数器
static uchar k=0xff;//按键值暂存
static uchar m=0xff;//按键位暂存
//显示()
if(p==7)
{
p=0;
if(m==0xff)
{
i=0;
k=0xff;
}
else {
if(k==m)
{
i++;
m=0xff;
}
else
{
flag=0;
i=1;
k=m;
m=0xff;
}
}
}
p++;
m=m<<1;
m=m+(P3&0x01);
if((flag==0)&&(k!=0xff)&&(i>?))//?需要按键延时时间(即显示循环次数)
{
flag=1;
return(k);
}
else return(0xff);
}
请马老师指正
本贴被 hhjhhj 编辑过,最后修改时间:2008-10-07,15:03:27. 马老师分析的非常中肯!!此题和我的毕业设计——多功能时钟定时器,有异曲同工之处(我的那功能更强大,有复合键和连_发功能),贴上来让大家欣赏一下!!
原理图ourdev_442392.pdf(文件大小:17K) (原文件名:Timer_sch.pdf)
源程序ourdev_442393.txt(文件大小:11K) (原文件名:timer.txt)
================================================================
硬件设计不错,估计你硬件功底比较扎实,蜂鸣器和掉电处理有创意。不过系统软件的设计不是最好,还是比较乱,许多功能混在一起,如果改动的话,就比较麻烦了。
另外,能介绍你的复合键和连_发功能的具体是如何定义的?比如当我按下A键1秒后又按下B键,那么是A键连_发,还是AB复合?
本贴被 machao 编辑过,最后修改时间:2008-10-07,20:54:57. 我也凑个热闹,写个思路。
这个电路利用138做输出,做为数码管的位选,同时做按键检测。(这里面有个硬件设计的问题,如果同时按下几个按键硬件就出现问题了!不考虑这种情况,否则会出现输出短路的情况,在这种前提下每次只会有一个按键按下)
以循环周期来算,可以利用一个字节来保存外部按键状态,利用移位方式,当检测到外部P3.7为1的时候就移进1,如果为0就移进0.
如果字节全部是1,那么表示所有按键输入为全放开的状态。只要不出现全1就表示有按键按下。
对于消抖动部分,因为外部接的是动态扫描显示数码管,所以一般情况下扫描一遍的时间就是10到20毫秒之间吧,这样只要在每次得到的外部按键字节都与上次的字节(扫描后会保留上一次的按键状态)是否相同,如果不同,则表示按键未稳定,保存一下这次的状态,直接返回。如果相同了,那么就表示按键稳定了,执行一次按键相应的功能。
针对楼主的问题,在这种情况下也很容易了,因为一旦键盘扫描字节变成了0XFF就表示按键放开了(消抖动后的状态),然后处理就行了。(为了判断在放开按键之间的按键,还要增加一个单元用来保存松开前的键值) 建议各位:
本贴的目的是学习和提高技术。能到这里贴上自己的见解朋友,通常都“算”是比较优秀的学生,具备一定的能力和自信(这里用“算”是指与你周围的同学或同事比较,甚至是比教你的老师都“强”)。但山外有山,请大家能先仔细看看别人的帖子,从中找到有价值的东西,然后综合考虑。不要死抱着自己的想法。
因为我知道目前大多数教课书所介绍的内容,都是最基本和简单,或仅局限于某单一功能实现的例子。用在实际的系统中远远不够,BUG多多。这些通常的思路和方法需要在实际应用中更加灵活的使用和变换。 看看再说 到目前为止,在一天多的时间里本贴点击次数为678,但帖子数只有34,粗略评估回贴数还不到5%。
这到是一个非常好的证明,从某个侧面说明了我们现在学电子的学生的水平。
在我的专栏中,我曾经转贴一个帖子《转帖网友yanwang对电子专业毕业生的评价》(见http://www.ouravr.com/bbs/bbs_content_all.jsp?bbs_sn=797384)引起一些朋友的不满,说我看低了现在的电子专业的大学生。
那么这里到是一个机会,请那些不赞成我的说法或不服气的朋友,到这里晒晒你的本事。
老夫已经横刀立马在在此把关,后生们有本事就拍马冲上前来。如果已经被吓跑了,你们如何面对下一步激烈的社会竞争。支持20楼的说法:“不蒸馒头争口气 不能让这的老大们看不起我们新来的菜鸟们”。
./emotion/em062.gif./emotion/em050.gif./emotion/em049.gif 感谢马老师与各位的回复,我很喜欢这里讨论问题的氛围。
在各位的回复中,对同一问题有不同的看法,这拓宽了我的思路。
感谢各位! 楼上,不用客气。我借你的问题设下擂台,挑战一批学生,树立我的威信,我还要感谢你提供了非常好的机会。
不过,到目前为止,尽管你认为“拓宽了我的思路”,但我认为好的,比较完整的回答还没有。
再等上几天看看吧。到时侯我会晒上我的方法提供参考,或是大家排砖的目标。 假如为了某种需要(比如抗干扰),在P3。7对地接上一个104的话,程序与没接电容是可能不同的。
====================================================================
104没变化,若10u的话,就不一样了。
你是想通过电容来消抖?可以试试。不过通常不这样用。
本贴被 machao 编辑过,最后修改时间:2008-10-07,23:34:55. 以前的一个程序因为显示的扫描频率较高(整个程序差不多就是显示与键盘扫描了),只能用软件廷时外加104解决。但加上104后,按键廷时就不能用调用显示程序来实现了,否则将检测不到按键。 楼上,放弃你以前的程序吧。我已经讲了,不准使用软件延时。
ms级以上的延时采用软件方法,是小儿科的水平,不上台面。效率都给软件延时吃掉了,系统设计问题多多。要采用定时中断和状态机。
建议你先在本拦置顶贴中,下在我编写书的电子版,从第9章看,看看我如何教读键,做LED动态扫描的。 不过电路与LZ不同。http://cache.amobbs.com/bbs_upload782111/files_11/ourdev_442970.jpg
(原文件名:未标题-1.jpg) 呵呵,我本来就是菜鸟一个,只能按N年前的方法扫描。 总不能停止不前吧。为什么不能放弃不好的方法,学习好的思路? 谢谢。我现在只能用汇编,还学不会C语言呢。那个程序也是一边查指令表,一边写出来的,N年前学过51,用AVR也是今年才开始的。
===========================================================
不管是用汇编,还是用C,思想方法是一样的。用汇编也不提倡使用软件延时,除非是几个us的延时。
本贴被 machao 编辑过,最后修改时间:2008-10-08,00:36:53. 见笑了。 hexixiaomao说今天继续的,看样子今天等不到了。 呵,这个帖子讨论得很好。COOL ! 请马老师点评
1。电路图这样改
http://cache.amobbs.com/bbs_upload782111/files_11/ourdev_443092.jpg
(原文件名:MACHAO TEA1.jpg)
2。流程图这样画
http://cache.amobbs.com/bbs_upload782111/files_11/ourdev_443171.jpg
(原文件名:MACHAO TEA2.jpg)
本贴被 mashan75 编辑过,最后修改时间:2008-10-08,11:02:05. 楼上的电路图二极管接反了...
其实答案马老师已经都点到过了~~~
晕,怎么成负分了...
本贴被 luoyanfang 编辑过,最后修改时间:2008-10-08,12:28:56. to 51楼:图中二极管加的很正确吗。你再把它反过来,是否有新招?
我还没出手,各位自己不要沉不住气,不自信哈。
=================================================================
电脑一直放在床边上,通过无线挂在站上。终于看到的阿莫49楼的关注。
我请求阿莫帮助,能否将本贴作为【总论坛置顶】的帖子,挂上几天,我想把擂台摆到总站上,向所有访问本站的武林高手挑战。
建议阿莫在本站建立“华山论剑”技术专栏,把象类似这样的题目拿来开设擂台,并设立一定的获胜奖励。可以聘请几位裁判,或通过网友投票来决定获胜者。
在“华山论剑”中,那些是“真刀真枪”的实工夫,那些是“花拳绣腿”的三脚猫就一目了然了。同时大家也可以切磋武功,旁观者也能学上那么几招真正的功夫。另外也可以提高本站的人气和知名度。
“如此不起眼的一个小网站”没有关系,慢慢让它成长为“起眼的大网站”,阿莫有这个梦想吧。
我会尽力支持的。
下午有事要外出(是去学招数的,自己也要修_炼),晚上再来过招。
本贴被 machao 编辑过,最后修改时间:2008-10-08,13:23:48. 惭愧,我只能做看客,帮马老师把贴顶起来。。。 昨天 来了 改了下自己帖子的一个小错误 就去忙着应聘去咯 笔试过了 第一轮面试过了 等下还要去第二轮。。。。
晚上回来继续,(面试时已经想好一个点子咯) 51楼何必加5个二极管,需要吗?理由是什么?
如果真的需要,加一个和加5个有什么区别?
我 以前玩过这个电路
当时的思路是:
在定时器中断中搞的显示
以显示扫描周期为计时周期消抖
每显示一位 判断一下P3.7
如果扫描到某位时P3.7为低,计数
如果连续多个扫描周期内为低就认为有键按下,计数清零
如果不到规定周期 P3.7为高 计数清零
伪代码
Disp_Key()
{
......
P3.7=1;
LED_ADDR_Port=LED_ADDR;
Dip_Port=Disp_Buff;
if(P3.7==0)
{
KeyPressFlag++;
KeyTmp=LED_ADDR;
}
else
KeyPressFlag=0;
if(KeyPressFlag>KeyTime)
myKey=KeyTmp;
......
}
===========================================================
再练练吧,功夫不到。前面已经有说明了,或许你有新的解释和设计?
本贴被 machao 编辑过,最后修改时间:2008-10-08,17:00:04. 支持一下...
回楼上的...不加二极管的话..在几个按键同时按下时...138 很可能出现短路现象.
本贴被 ivws 编辑过,最后修改时间:2008-10-08,16:30:19. 我也来谈一下自己的思路。这个是典型的 按键/显示 IO复用的情况。
显示/按键 处理相对来说占用MUC时间较小,因而应该用定时中断扫描方式处理比较合适。
中断子程序处理应越快越好,以免和其它实性要求高的事件冲突。
中断处理程序:
MCU通过P3.3~P3.5控制138的Y0~Y5依次输出低电平,一方面选通数码管的位,另一方面对各按键进行扫描。
每次选通一位,都对P3.7检测一次,并用一变量通过移位记录下来。
一次循环下来,数码管完成一帧显示,刚才的记录变量也完成记录按键的状态。
按下不同的按键(包括组合键),记录变量有不同的值,这个值可作为键码。
得到键码后,不必在中断里进行处理,只需激活键码有效标志,按键处理过程应在程序主循环中。
主程序中的按键处理方法:
这一部分根据键码和按下时长,进行按键识别、消抖、处理长按、处理连_发、处理复合键。
设立一变量记录前一次键码,设立另一变量对按键时长进行计数。
当键码全为1(即没键按下)或者新键码与旧键码不同,清零计数器,其它不作处理。
当新旧键码相同,计数码加一,直到达到消抖时间要求,此时识别为某键按下。
新旧键码持续相同,计数器递增,达到一定值可视为长按(或连_发)。
==========================================================
很多人都会这样讲,把基本原理罗列一遍。但这是典型的“花拳绣腿”,属于应试教育的回答。
请用示例代码来说明,看你如何实现他们的组合,是否能正确完成功能,代码已经运行效率如何。这才是真本事。
本贴被 machao 编辑过,最后修改时间:2008-10-08,17:09:27. 以前做过类似的,程序流程的话:是否需要更新显示-》(需要)先关闭显示,然后读键值(读键就不用详细说了吧),再更新显示,再处理按键值,再回到开始
按键的处理是利用更新显示的时间即每一个COM的时间来消抖和计时。
图上不能有二个按键同时按下
我以前用的是没有中断的单片机做的万年历,只有个定时器,要显示年月日(农历公历)时分秒星期==。
本贴被 lusson 编辑过,最后修改时间:2008-10-08,16:51:16. 设计产品,你可以在说明书中说明只有单键功能,但你不能说禁止按下2个键。
用户在使用中可能会误操作,或不会操作,甚至是有意的按下了2个键,结果你的系统显示不正常了,甚至坏了,烧掉了,这样的产品能进入市场吗?
“华山论剑”是比真工夫,要按合格产品的要求进行设计。 如果要一个按键按下,起来后才算一次按键,那么可以设定一个按键信息变量,key,在有按键按下时记录key直到按键宋爱才执行按键处理程序就可以
另外,想要按键过程中数码管显示不闪烁,我的方法是,在案件获取函数内,放入显示扫描函数做延时,和等待按键循环,前提是你的显示函数时间不能太大了,
还可以定义显示刷新频率,用定时器定时显示函数,每次中断刷新一次显示,应该不会闪烁了
======================================================================
兜了怎么一大弯,还是不能根本的解决问题的。要采用好是设计方法根本就不存在这样的问题。
建议你在本栏置顶的帖子中,下载我编写书的前2篇,看看我编写的按键和键盘扫描函数的处理思想。上面的问题都不会发生的。 学习了 楼上的和我一样迟到了 唉 把这篇帖子 拿下去打印 再阅读了一番
很有进益 没想到许多东西可以这样啊
谢谢 马潮老师和各位顶帖的
希望 经常可以读到这种贴 然后自己慢慢的进入 讨论 的圈子 学习了 怎么只有显示60个回复??马潮老师不是都出了93楼了 我想看看老师的解答 老师给的这个问题太有意思了 难道要顶到71楼才能看到答案?? 要是这样的话我一天来顶一次 mark 马老师现在是不是不在这里了 新年 就来顶啦 新年快乐大家 继续 还要一个 最后一个了?? 到71楼看看 mark mark 我来接一招吧。用proteus仿真,能实验单击和连_发功能,但在仿真时会出现按键按下,对应数码管灭,用示波器观察对应管脚波形正常,不知是不是软件原故,过两天做硬件测试。
我的想法是这样的,采用状态机检测键盘,放在显示程序中,键盘0态时检测P3.7口。键盘1态时,检测时间(说明这个时间一定要是显示数的整数倍,否则检测不到键盘),确认键盘后转2态。键盘2态, 同样先检测时间(这个时间也一样),确认后键盘跳转到3态.
键盘3态,也同样检测键盘。键盘子程序如下:主程序中通过key_return值来确认键盘,0为不响应,1为单击,2为连击。响应后清些值.
显示程序为1ms显示一位。
//说明:key=P3.7
disp();
key_return=0;
key_press=key;
switch(key_state) //key_time一定要是显示位数的整数倍
{
case 0:
if(key_press==0)
{
key_state=1;
key_old=dat_tt;//保留键值
key_time=0;
}
break;
case 1:
if(++key_time==20)
{
key_time=0;
if(key_press==0)
{
if(key_old==dat_tt)
{ key_state=2;
key_return=1; //单击
key_time=0;
key_data=dat_tt;
}
else
{
key_state=0;
key_time=0;
key_data=0xff;
}
}
else
{
key_state=0;
key_time=0;
key_data=0xff;//键盘
}
}
break;
case 2:
if(++key_time>=1000)
{
if(key_press==0)
{
key_state=3;
key_time=0;
key_return=2;//进入连击
}
else
{
key_state=0;
key_time=0;
key_data=0xff;//键盘
}
}
break;
case 3:
if(++key_time>=200)
{
if(key_press==0)
{
key_state=3;
key_time=0;
key_return=2;//进入连击
}
else
{
key_state=0;
key_time=0;
key_data=0xff;//键盘
}
}
break;
default :
break;
} 上面是自己个人的想法,还清马潮老师批评指正。 受教! GZ mark mark 感觉按键反应不灵敏,再改delay_time都不行,请马老师指点! 从手机上看的帖子,但看起来不方便。等电脑在身边时一定要好好钻研。好帖呀! 如果每星期能出一个这样的贴子,那中国的电子技术短时间内必定会有质的飞跃! 頂一下,有空好好看看! 頂一下,有空好好看看! mark mark mark mark,有空仔细看看, mark 说说我的,意见,,,,,
二极管的方向就别说了 。高手看了下面的就知道了。
数码管的位选做按键的扫描脉冲,,,当一个位为高电平,同时按键按下,那么公共端就会出现高电平,
按键按下是从出现高电平开始,当扫描到某个按键没有出现高电平,则这个按键没有按下,,,
为每个按键设置低电平计数单元,以及设置 上次/当前 按键的状态标志位,
至于按键消抖,我想数码管的位扫描脉冲,不是最好的消抖时序嘛? mark 本贴没有93楼啊?
本题解决思路的关键在于有限状态机的运用。 标记,顶!!! 哇~~~ 来的稍稍晚了些 本帖到此结束了吗?如果马老师没有给出最终答案的话,我想出招试一试! cool 做个标记, 其实马老师书中有关于定时中断扫描显示,扫描键盘,再利用状态机来判断按键,我想这也正是是处理这种电路的方法吧! MARK 回家看看 回复一下......都08的贴了 mark
页:
[1]
2