原贴:
http://www.ourdev.cn/bbs/bbs_content_all.jsp?bbs_sn=1567987
(原文件名:无标题.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的图:
(原LZ位图)
只是里面有点BUG,不能多键按下,按50楼的图改动,增加5个二极管。
(原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[6]; // 这里存放每个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[posit]; //送出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;
}
=====================================================================
在此基础上做修改,可以非常容易的把“连_发”功能加上,不管是单键,还是多键,都能“连_发”。这个还是留给各位去实现吧。