购物车0种商品
IC邮购网-IC电子元件采购商城
中断扫描键盘和主程序扫描键盘的优缺点
(2011/10/23 9:28:00)

网友评论:支持一下ayb_ice。

网友评论:“若键盘数量比较多,并且要读写EEPROM”

若键盘数量比较多,就更加应该放到主循环当中做,老占着中断不放,不是好事。

读EEPROM,一般不怎么耗时,
写EEPROM,采取中断方式,不能傻等。

一句话,程序当中,不要为一件事长时间傻等。

网友评论:不会为一件事长时间傻等,是OS的一个巨大的优点

网友评论:终于有人支持了

网友评论:通用的,都能插的上嘴

网友评论:写eep本身,就几条指令,不怎么耗时,
但是为了确信写完成,启动下一次写,需要等待可能若干ms。

对于确信写完成,简单的就是延时查询傻等,这样就比较耗时。

如果是芯片内部的eep,写完成,会有中断发生,置启动写标志,
主循环里,检测到标志,启动下一次写。

或者在主循环里查询,写完成中断标标志,启动下一次写

对于外部的eep,启用定时器延时,时间到设置一个写标志,
主循环里,检测到标志,启动下一次写。

这样处理的话,就不会有傻等的情况发生

网友评论:我喜欢放在主程序扫键盘,其它任务都用中断,置标志位,主程序处理,这样假设所有任务都发生主程序处理时间也不会太长(不到100ms,对我做过的来说),而人对键盘的反应比较迟钝

网友评论:和twz8858877思路完全一样。

而且,
在一个循环的,所有任务都发生的可能性,一般情况下非常小.

网友评论:即可所有任务同时发生也可以面对,因为不是所有任务必须及时处理,可以先处理最紧急的任务。

网友评论:放定时中断里绝对是很好的方法

网友评论:因为有个程序每31.25us中断一次,每次最少18us,平均25uS,最大30uS~~~

还要跑很多程序,特别还要检测频率,动态测试IO的好坏,芯片是否被振落,LED刷新等~~~

网友评论:hotpower的0耗时,没看过。
不知道是个啥概念。

但估计十之八九是在消抖延时上做文章,消抖延时在定时器里定时,完成后再进行键盘值的确定,除此外,想不出,还有什么地方可以接见时间。

如果是这种思路的话,主程序执行键盘扫描,一样可以0耗时.

网友评论:完全封装,做成标准库。
外部只提供和硬件有关的扫描标志数组。
可每个KEY单独识别PUSH、DOWN、DOUBLECLICK、LONGCLICK,强大啊。


网友评论:这样可以保证扫描的周期性,尤其是对数码管。

如果放在主程序中,很难保证其周期性,处理事务是还要瞻前顾后,有点麻烦。

放在定时器中断中,很容易做到扫描的周期性,同时也很容易实现所长所说的“PUSH、DOWN、DOUBLECLICK、LONGCLICK”,
以及自动连击等。甚至还可以用一个队列保存按键,这样在主程序任务时间长时,按键也能很好的响应。
如果扫描放在主程序中,做这些就有难度了。

网友评论:所长技痒难忍啊。

实现每个KEY单独识别PUSH、DOWN、DOUBLECLICK、LONGCLICK,
工作量很大啊。


网友评论:当然,这和最早的电路是使用键盘扫描IO和LED复用有关。

我是一直使用键盘0耗时,这和我笨有关,我最早的时候,对着书本就是没学会写去抖程序。

其实,0耗时是检测keydown事件,然后把去抖放在检测keyup事件,如此而已。没什么神秘

扫描程序和键盘处理程序分开,那扫描放中断还是放主程序的问题就基本可以终结。

网友评论:去抖也很方便的
任何按键都有三个动作,即三个属性:按下、按住、弹起
按下和弹起是瞬间的,按住可以任意时长...

网友评论:为每个键,设置一个队列,记录每个键的动作,和持续的时间,
对队列进行分析,
各种键的状态,应该在主程序里面实现PUSH、DOWN、DOUBLECLICK、LONGCLICK,
问题不大吧?

网友评论:具有按键按下、按住、弹起等功能。在这个代码的基础上,再增加几条语句,就可以实现长按、按键连发等功能。



/********************************************************************
函数功能:定时器0中断处理。
入口参数:约5ms中断一次。
返回:无。
备注:无。
********************************************************************/
voidTimer0Isr(void)interrupt1
{
uint8temp;

#ifndefLCD
staticuint8LedDigit=0;
#endif

TH0=(65536-Fclk/1000/12*5+22)/256;//定时器0重装
TL0=(65536-Fclk/1000/12*5+22)%256;

SystemTick++;

#ifndefLCD//没有定义LCD,则使用数码管显示
switch(LedDigit)
{
case0:
LED_3=1;
LED_SEG=Table[LedBuffer][0]];
LED_0=0;
LedDigit=1;
break;
case1:
LED_0=1;
LED_SEG=Table[LedBuffer][1]];
LED_1=0;
LedDigit=2;
break;
case2:
LED_1=1;
LED_SEG=Table[LedBuffer][2]];
LED_2=0;
LedDigit=3;
break;
case3:
LED_2=1;
LED_SEG=Table[LedBuffer][3]];
LED_3=0;
LedDigit=0;
break;
default:
LedDigit=0;
break;
}
#endif

if(!KeyCanChange)return;//如果正在处理按键,则不再扫描键盘

//开始键盘扫描
//保存按键状态到当前按键情况
//KeyCurrent总共有8个bit
//当某个开关按下时,对应的bit为1

P2_4=0;//扫描第一列
temp=~KeyIO;
P2_4=1;
temp&=0x0F;
P2_5=0;//扫描第二列
KeyCurrent=~KeyIO;
P2_5=1;

KeyCurrent<<=4;
KeyCurrent|=temp;

if(KeyCurrent!=KeyOld)//说明按键情况发生了改变
{
KeyNoChangedTime=0;//键盘按下时间为0
KeyOld=KeyCurrent;//保存当前按键情况
return;
}
else
{
KeyNoChangedTime++;//按下时间累计
if(KeyNoChangedTime>=1)//如果按下时间足够
{
KeyNoChangedTime=1;
KeyPress=KeyOld;//保存按键
KeyDown|=(~KeyLast)&(KeyPress);//求出新按下的键
KeyUp|=KeyLast&(~KeyPress);//求出新释放的键
KeyLast=KeyPress;//保存当前按键情况
if(KeyDown)BeepOn();//按键按下,蜂鸣器响
elseif(KeyUp)BeepOff();
}
}
}
/*******************************************************************/

网友评论:其实都是在消抖上做文章,不要在那里死等,可以间隔调用扫键程序,利用间隔来完成延时,加上们简单的状态机就搞定了,很容易实现短按,长按,连按,组组合等功能。。。

网友评论:voidScanKey()
{
unsignedchartempKeyValue;

if(keyScanState==0)//预先扫描键盘
{
if(RA0==0||RA1==0)
keyScanState=1;//启动消抖延时
}
elseif(keyScanState==2)//延时完毕,确认扫描键盘
{
tempKeyValue=0;
TRISA2=0;
TRISA3=1;
RA2=0;
asm("nop");asm("nop");asm("nop");

if(RA0==0)tempKeyValue=4;
elseif(RA1==0){tempKeyValue=1;}
else;

TRISA2=1;
TRISA3=0;
RA3=0;
asm("nop");asm("nop");asm("nop");

if(RA0==0)tempKeyValue=3;
elseif(RA1==0){tempKeyValue=2;}
else;

if(tempKeyValue!=0)//扫描到键盘值,存到缓冲队列
{
if(keyDataLen<MAN_KEYBUF_LEN)
{
keyBuf[keyBufHead]=tempKeyValue;
keyDataLen++;
if(keyBufHead==MAN_KEYBUF_LEN-1)
keyBufHead=0;
else
keyBufHead++;
}
}

TRISA2=0;
TRISA3=0;
RA2=0;
RA3=0;
keyScanState=0;//恢复预先扫描状态
}
else
;
}

voidinterruptISR(void)
{
if(T0IE&&T0IF){//判TMR0中断,8m

if(keyScanState==1)
{
if(KeyDelayTime<4)
KeyDelayTime++;
else
{
keyScanState=2;
KeyDelayTime=0;
}
}

TMR0=5;
T0IF=0;}
}

如果检测键弹起事件,存入缓冲队列,实现长击,连击,也非难事

网友评论:这个问题建议大家,到书店看看这本书《松翰SN8P2700系列单片机原理及应用技术》


网友评论:按键处理在300毫秒内处理完就可以,有必要放中断吗?
还有如果下降沿触发进入中断,硬件电路也要处理。
一般可以不放中断处理的程序,就不放中断。

网友评论:我也不赞成放在中断,按键的响应速度要求不高,即使DELAY个几十MS也没有问题,在主程序里做扫描,定时中断只是作为启动扫描的钥匙,扫描不存在等待延时,同样也是零耗时

网友评论://一个单片机系统的设计经常会用到多种不同目的和用图的定时,例如系统需要输出
//一个指示“心跳正常”的秒闪信号,间隔0.5s;按键检测时临时需要约20ms的消抖;
//蜂鸣器需要发声延时;用户菜单选择时可能需要对应的发光管或LCD点阵(字段)
//闪烁;通讯时需要设定应答超时判别,等等。是不是要抱怨一个单片机上的若干个
//定时器不够用了?其实,用一个定时器资源就可以搞定所有的这一切定时要求。

//1)首先,选定一个你喜欢的定时器,按所需应用的定时精度要求设定其定时中断频
//率。一般人机界面的定时精度为ms级就足够了,所以可以设定定时中断时间间隔为
//1ms,5ms或10ms;例如我的选择:

//==============================================================
//TPM2overflowinterruptserviceroutine
//Interruptatevery1ms
//==============================================================
voidinterrupt14TPM2_Overflow_ISR(void)
{
TPM2SC_TOF=0;//resetinterruptflag
msTimeoutCount++;//1msincrement
}

//变量msTimeoutCount是一个16位word型的静态变量,在中断服务程序中简单地对它
//递增,无需考虑溢出。如果你的中断时间间隔为Nms,则在中断中对其递增的方法为
//“msTimeoutCount+=N”。它在程序模块的前面被声明,为了提高中断服务程序的效
//率,其被定位在直接寻址区:

//==============================================================
//Followingdataaredeclaredinthedirectaddressingarea
//forfastaccess(address<0x100)
//==============================================================
#pragmaDATA_SEGSHORTMY_ZEROPAGE//directaddressingdatasegment
volatilewordmsTimeoutCount;

//然后写一段独立的定时判别函数。这个函数有两个入口参数:特定定时实例的一个
//定时变量指针和所需的定时时间长度。若定时时间长度为0,则定时过程被复位,实
//际上是当前的定时计数器值(msTimeoutCount)被复制到定时实例的一个定时变量
//中。返回值为0则表明定时时间未到,0xff则为一次定时时间到并自动开始下一次的
//定时过程。具体代码如下:

//==============================================================
//Checkfortimeoutoccurance
//Input*timer-pointeroftimercounter
//timeOutVal-timeoutvalue,0=forcedtimerupdate
//Return0-notimeoutyet
//0xff-timeoutoccuredandtimerupdated
//==============================================================
byteTimeOutChk(word*timer,wordtimeOutVal)
{
wordshadow,diff;

TPM2SC_TOIE=0;//针对8位机必须禁止定时中断,16位机以上则无需如此
shadow=msTimeoutCount;//将当前时间计数值复制一份以作后需
TPM2SC_TOIE=1;//对应上面的中断禁止,现在开放中断

if(timeOutVal==0)
{//复位定时过程
*timer=shadow;
return(0);
}
else
{
diff=shadow-*timer;//计算定时时间间隔
if(diff>=timeOutVal)
{//定时时间到
*timer+=timeOutVal;//更新定时变量,开始下一次定时过程
return(0xff);//返回时间到标志
}
else
{
return(0);//定时时间未到
}
}
}


//剩下的就看具体应用的需要而开辟特定的定时实例了。每一个实例必须有一个word
//型的变量作为定时跟踪变量。

//例如产生500ms的定时(msCount变量在模块前面已经定义):

voidmain(void)
{

...

TimeOutChk(&msCount,0);//复位初始化定时实例

...

while(1)
{

Clock();

KeyScan();

...

}

}

//==============================================================
//Keepthesystemclockrunning
//==============================================================
voidClock(void)
{
if(TimeOutChk(&msCount,500)==0)
return;//waitfor0.5secondtimeout

runFlag.halfSec=!runFlag.halfSec;
dispCodeBuff[2]^=0x80;
dispCodeBuff[3]^=0x80;

if(runFlag.halfSec)
{
return;
}

second++;
if(second==30)
{//syncsoftclockwithRTCvalue
RTC_Read();
}
if(second>59)
{
second=0;
minute++;
if(minute>59)
{
minute=0;
hour++;
if(hour>23)
hour=0;
}
}

runFlag.clkDisp=1;
}


//按键扫描时的消抖延时实现,keyDebounce在模块前面为局部静态变量定义

//==============================================================
//Scaningkeyinput
//==============================================================
voidKeyScan(void)
{
bytekeyInput;

keyInput=(PTFD^0xff)&0b00011111;

switch(keyState)
{
case0://idle
if(keyInput)
{//possiblekeyinputdetected
runFlag.keyCon=0;//continuouskeystrikenotallowedbydefault
TimeOutChk(&keyDebounce,0);//resetdebouncetimer
keyState=1;
}
break;
case1://downdebouncing
if(TimeOutChk(&keyDebounce,50))
{//50msdebouncetimeout
if(keyInput)
{
KeyFifoAdd(keyInput);
TimeOutChk(&keyDebounce,0);//!复位定时准备实现按键持续按下时的连续激发功能
keyState=2;//keyisholding
}
else
{
keyState=0;//debouncecheckfailed
}
}
break;
case2://hold
if(keyInput==0)
{//possiblekeyreleasedetected
TimeOutChk(&keyDebounce,0);
keyState=4;
}
else
{
if(runFlag.keyCon)
{//continuouskeystrikeallowed
if(TimeOutChk(&keyDebounce,500))
{//持续按下时间达0.5s
KeyFifoAdd(keyInput);
TimeOutChk(&keyDebounce,0);//准备后续每隔0.1s激发一个按键值
keyState=3;//invokekeycontinuousstrike
}
}
}
break;
case3://continuousstrike
if(keyInput==0)
{//possiblekeyreleasedetected
TimeOutChk(&keyDebounce,0);
keyState=4;
}
else
{
if(TimeOutChk(&keyDebounce,100))
{//每隔0.1s激发一个按键值
KeyFifoAdd(keyInput);
TimeOutChk(&keyDebounce,0);
}
}
break;
case4://updebouncing
if(TimeOutChk(&keyDebounce,50))
{//50msdebouncetimeout
if(keyInput)
{
keyState=2;//keyisstillholding
}
else
{
keyState=0;//confirmreleased
}
}
break;
default:
keyState=0;
}
}

//所以理论上只要你有足够多的内存作为定时跟踪变量,你就可以实现任意多个定时
//实例,无论什么时间和什么地点。当然上面的定时程序有一个局限,就是一次最大
//的定时时间为65535ms。如果要实现更长时间的定时,可以用一个实例产生1s
//(或更长)的定时基准,然后参照函数TimeOutChk另外写一个例如TimeOutChkSec,
//按1s的分辨率最多实现65535s的定时。


//采用状态机实现按键检测是最可靠最有效的方法。同时在单片机设计中实现多任务
//的并发和协调,状态机起着不可或缺的作用。对于按键处理,部分代码如下:
#defineKEY_FIFO_LEN4
bytekeyFifo[KEY_FIFO_LEN],keyPut,keyGet;
//==============================================================
//AddakeyintoFIFO
//==============================================================
voidKeyFifoAdd(bytecode)
{
keyFifo[keyPut++]=code;
keyPut&=(KEY_FIFO_LEN-1);
}

//==============================================================
//FetchakeyfromFIFO
//==============================================================
byteKeyFifoGet(void)
{
bytetmp;
tmp=keyFifo[keyGet++];
keyGet&=(KEY_FIFO_LEN-1);
return(tmp);
}

//==============================================================
//Dokeyfunctionforprimarytask
//==============================================================
voidKeyFuncMain(void)
{
bytekeyCode;
if(keyPut==keyGet)
return;
keyCode=KeyFifoGet();
switch(keyCode)
{
caseKEY_CH1_CTL:
RELAY1_CTL=!RELAY1_CTL;
if(RELAY1_CTL)
dispCodeBuff[4]|=0x10;
elsedispCodeBuff[4]&=(0x10^0xff);
SetBeep(200);
break;
caseKEY_CH2_CTL:
RELAY2_CTL=!RELAY2_CTL;
if(RELAY2_CTL)
dispCodeBuff[4]|=0x08;
elsedispCodeBuff[4]&=(0x08^0xff);
SetBeep(200);
break;
caseKEY_SET:
SetBeep(50);
TimeOutChk(&menuTimeout,0);
menuId=0;
MainTaskEntry=SetupEnable;
MenuTaskEntry=ClockSetup;
dispCodeBuff[4]=0x01;
break;
default:
return;
}
}

网友评论:以后尝试在中断中扫描

网友评论:脱离需求环境讨论解决方案是没有意义的!

网友评论:在中断函数中不要太多的执行代码

网友评论:要是放在中断里扫的话,万一不停的按着按键不就狂进中断了吗,那主程序就没法正常运行了啊!就死了!
不知大侠们怎么处理的!?

网友评论:关键是怎么设消抖动关键是怎么设消抖动关键是怎么设消抖动

网友评论:键盘都能把程序整死太危险了

网友评论:我没什么经验,不过干了20几年。我从来不在中断中干这些没有即时响应要求、没有流量的事情。可以分析各位的系统中按键操作占系统工作多少时间,扫描按键中断又占了系统多少时间?如果领导要求你每天、每小时、每分钟报到,想一下你的心情、怎么工作。

网友评论:键盘不是中断,而是定时器中断中扫描键盘
我是这样做的
定时器中检测到有键盘按下置一标志,再次检测到可以认为它就是真的按下了,如果很重要的功能键不妨就对扫描计数,达到一定的次数后才认为真的有效,每次扫描键盘所占的时间微不足道,几个us,几ms一次。至于那键功能的处理根据具体情况处理,如果可能涉及到耗时的计算就把键的功能放到主循环里,一般都可以及时处理的,哪怕使用者用手掌乱拍键盘也不会死机

网友评论:我明白了。其实如果主程序不是很大的话,键盘扫描放主程序里和用定时器感觉没什么不同的!

网友评论:中断里做的事情越多哦,全局变量冲突就越多
只有需要极其及其实时性要求的任务才放进去做。

网友评论:一个弱实时,一个强实时而已。
不应有太大争论。
只是把扫键放到主程序后,优先级降低,允许其他更需要实时的任务执行而已。

网友评论:中断的话就要多了一个SM口拉!检测到中断之后也要扫一下,看是哪个按键,呵呵,跟高手的意见不同,等着被人泼水

网友评论:用中断的话又要多一个单片机引脚,而且中断之后也一样要扫描,呵呵,跟楼下的高手意见不同,等着泼水

网友评论:我认为像数码管记数显示等,应该在定时器中段中处理比较好,可以不用在主函数中不停的扫描。

像扫描键盘,特别是矩阵键盘的检测,我认为不应在中断中处理,应该独立写成一个函数,然后在主函数中不停的调用,满足条件(按下键)的话就执行处理键。

理由,你用一个中断,相应的就少了一个中断可用;就算在主函数中不停的调用,没有键按下时,才几个指令,占用不了多少CPU的时间,而且在中断中,处理的时间不能超过定时的时间;中断中消抖也不方便。

有人说,在中断中设置标志位,这样的话跟在主函数中不停的判断也就没有多大的区别了。

这个是我写的,请指教
voidtrach(void)//键盘检测函数
{
if(temp!=0xf0)//有按键(先扫描第一行,主函数中有移位
{//下一行的)
delay();//消抖
temp=temp&0xf0;
if(temp!=0xf0)//重新检测
{
temp=P3;//确实有按键
switch(temp)//处理键,当然,硬件不同,方法是不
{
case0xee:num=0;break;
case0xde:num=1;break;
case0xbe:num=2;break;
case0x7e:num=3;break;
case0xed:num=4;break;
case0xdd:num=5;break;
case0xbd:num=6;break;
case0x7d:num=7;break;
case0xeb:num=8;break;
case0xdb:num=9;break;
case0xbb:num=10;break;
case0x7b:num=11;break;
case0xe7:num=12;break;
case0xd7:num=13;break;
case0xb7:num=14;break;
case0x77:num=15;break;
}
}
}
}
voiddelay(void)//延迟6ms,消抖
{
uchari,j;
for(i=6;i>0;i--)
for(j=100;j>0;j--);
}

网友评论:如果主循环的最长周期可以控制在一次按键扫描的循环时间内,则可放在主程序中做,否则放在中断中,并存储好按键值,以作处理。按键如果放在中断中扫描,程序段必须尽量的短,以减少中断时间,有时候,中断时间长是一件很恐怖的事情,非常容易使程序出现莫名其妙的问题。

网友评论:我觉得中断里做没什么问题.

一个好的逻辑结构,有确定性.相对多一点开销是值得的.这时调试什么都会方便点.



网友评论:根本就没入门,还敢充专家说什么“没有多大的区别”...

浏览:(11708)| 评论( 0 )
博文评论

  • 昵 称:
  • 内 容:10~250个字符
  • 验证码: 验证码看不清楚?请点击刷新验证码
  •                      
  • 博文分类

    热点博文

    最新博文

    最新评论

    IC电子元件查询
    IC邮购网电子元件品质保障