转自小峰博客协议栈按键流程.docx

上传人:b****4 文档编号:24649152 上传时间:2023-05-29 格式:DOCX 页数:14 大小:21.34KB
下载 相关 举报
转自小峰博客协议栈按键流程.docx_第1页
第1页 / 共14页
转自小峰博客协议栈按键流程.docx_第2页
第2页 / 共14页
转自小峰博客协议栈按键流程.docx_第3页
第3页 / 共14页
转自小峰博客协议栈按键流程.docx_第4页
第4页 / 共14页
转自小峰博客协议栈按键流程.docx_第5页
第5页 / 共14页
点击查看更多>>
下载资源
资源描述

转自小峰博客协议栈按键流程.docx

《转自小峰博客协议栈按键流程.docx》由会员分享,可在线阅读,更多相关《转自小峰博客协议栈按键流程.docx(14页珍藏版)》请在冰豆网上搜索。

转自小峰博客协议栈按键流程.docx

转自小峰博客协议栈按键流程

最近看到小峰的笔记感觉不错,这里和大家分享一下,感谢小峰为我们初学者提供这么多笔记,真的感谢他,看了他的笔记使我少走了不少弯路。

我使用的协议栈版本及例子信息:

ZigBee2006\TexasInstruments\ZStack-1.4.3-1.2.1\Projects\zstack\Samples\SampleApp

记录下个人对按键流程的理解

在hal_key.c中有一段说明:

/*********************************************************************

NOTE:

Ifpollingisused,thehal_drivertaskschedulestheKeyRead()

    tooccurevery100ms.  Thisshouldbelongenoughtonaturally

    debounce(去抖动)thekeys.  TheKeyRead()functionremembersthekey

    stateofthepreviouspollandwillonlyreturnanon-zero

    valueifthekeystatechanges.

NOTE:

Ifinterruptsareused,theKeyRead()functionisscheduled

    25msaftertheinterruptoccursbytheISR.  Thisdelayisused

    forkeydebouncing.  TheISRdisablesanyfurtherKeyinterrupt

    untilKeyRead()isexecuted.  KeyRead()willre-enableKey

    interruptsafterexecuting.  Unlikepolling,wheninterrupts

    areenabled,thepreviouskeystateisnotremembered.  This

    meansthatKeyRead()willreturnthecurrentstateofthekeys

    (notachangeinstateofthekeys).

NOTE:

Ifinterruptsareused,theKeyRead()fucntionisscheduledby

    theISR.  Therefore,thejoystickmovementswillonlybedetected

    duringapushbuttoninterruptcausedbyS1orthecenterjoystick

    pushbutton.

NOTE:

WhenaswitchlikeS1ispushed,theS1signalgoesfromanormally

    highstatetoalowstate.  Thistransitionistypicallyclean.  The

    durationofthelowstateisaround200ms.  Whenthesignalreturns

    tothehighstate,thereisahighlikelihoodofsignalbounce,which

    causesaunwantedinterrupts.  Normally,wewouldsettheinterrupt

    edgetofallingedgetogenerateaninterruptwhenS1ispushed,but

    becauseofthesignalbounce,itisbettertosettheedgetorising

    edgetogenerateaninterruptwhenS1isreleased.  Thedebouncelogic

    canthenfilteroutthesignalbounce.  Theresultisthatwetypically

    getonly1interruptperbuttonpush.  Thismechanismisnottotally

    foolproofbecauseoccasionally,signalboundoccursduringthefalling

    edgeaswell.  Asimilarmechanismisusedtohandlethejoystick

    pushbuttonontheDB.  FortheEB,wedonothaveindependentcontrol

    oftheinterruptedgefortheS1andcenterjoystickpushbutton.  As

    aresult,onlyoneortheotherpushbuttonsworkreasonablywellwith

    interrupts.  ThedefaultisthemaketheS1switchontheEBworkmore

    reliably.

*********************************************************************/

对本协议中,对按键有两种处理方式:

1、中断法:

有按键按下,则进入中断,开启一软定时器25ms后,读取键值进行相应处理

2、查询法:

开启一软定时器,系统每隔100ms进行轮询,如有按键按下读取键值进行相应处理

            (软定时器即软件定时器,参见“系统时钟定时器”说明)            

首先看下协议栈对KEY的初始化,在InitBoard()函数中:

    /*InitializeKeystuff*/

  OnboardKeyIntEnable=HAL_KEY_INTERRUPT_DISABLE;

  HalKeyConfig(OnboardKeyIntEnable,OnBoard_KeyCallback);

从而知道协议栈默认的按键处理机制是查询法.就先来看下查询法的流程:

1、查询法(或者轮询法)

首先看下HalKeyConfig()

这个函数用于把按键/开关/操纵杆服务配置为轮询或中断驱动。

它还为服务配置一个回调函数。

如果不使用中断,轮询在100ms后自动开始。

按键/开关/操纵杆将每100ms被轮询一次。

如果使用中断,就使用一个ISR来处理这种情况.在中断发生后,有一个25ms的延时以消除回跳.

/***************************************************************************

voidHalKeyConfig(boolinterruptEnable,halKeyCBack_tcback)

{

  ……………………

  if(Hal_KeyIntEnable)

  {

    …………(清除中断标志,设置上升沿下降沿触发外部中断等)

  /*Dothisonlyafterthehal_keyisconfigured-toworkwithsleepstuff*/

  if(HalKeyConfigured==TRUE)

  {

    osal_stop_timerEx(Hal_TaskID,HAL_KEY_EVENT);  /*Cancelpollingifactive*/

  /*如果采用中断方式则不用定时器来查询事件,关闭以前为这个事件开启的系统时钟*/

  }

  }

  else  /*InterruptsNOTenabled*/

  {

  …………(清除中断标志等)

  osal_start_timerEx(Hal_TaskID,HAL_KEY_EVENT,HAL_KEY_POLLING_VALUE);/*Kickoffpolling*/

  }

}

/***************************************************************************

可以看到查询法中,开启了一个系统软定时器,任务是HAL层任务,事件是HAL_KEY_POLLING_VALUE,系统要定时轮询按键事件得到按键值再作相应处理。

轮询时间为#defineHAL_KEY_POLLING_VALUE  100,即100ms.跟前面的英文说明一致。

当这个软定时器溢出时,便设置事件发生标志调用HAL层任务事件处理函数,然后从系统时钟链表中删除这个软定时器设置(后面会看到当处理完按键事件后又会重新启动这个软定时器).

下面就来看下它所调用的HAL层任务事件处理函数:

/***************************************************************************

uint16Hal_ProcessEvent(uint8task_id,uint16events)

{

  uint8*msgPtr;

…………(其它事件处理)

  if(events&HAL_KEY_EVENT)

  {

#if(definedHAL_KEY)&&(HAL_KEY==TRUE)

  /*Checkforkeys*/

  HalKeyPoll();

  /*ifinterruptdisabled,donextpolling*/

  if(!

Hal_KeyIntEnable)

  {

    osal_start_timerEx(Hal_TaskID,HAL_KEY_EVENT,100);  //因为在HalKeyInit()时,如果采用

  }                //非中断方式,已经初始化设置过一个按键事件软定时器,当这个软定时器

                    //溢出时,便设置事件发生标志调用事件处理函数,然后从系统软定时器链表

                    //中删除这个定时器设置,因此这里调用HalKeyPoll()处理完后续工作后

                    //如果还是采用非中断方式,则要重新开启一个软定时器!

#endif  //HAL_KEY    

  returnevents^HAL_KEY_EVENT;

  }

…………  (其它事件处理)

}

/***************************************************************************

可以看到HAL层任务事件处理函数中对按键事件HAL_KEY_EVENT调用HalKeyPoll()进行下一步处理,在HalKeyPoll()处理完后跳出来,立马开启一个新的定时器,跟前面采用查询法(非中断:

!

Hal_KeyIntEnable)时开启的定时器一样,准备100ms后的下一次查询!

下面看下HalKeyPoll()

/***************************************************************************

voidHalKeyPoll(void)

{

…………(调用HalAdcRead(),检测模拟电压值最终得出键值keys)

  /*Exitifpollingandnokeyshavechanged*/

  if(!

Hal_KeyIntEnable)

  {

  if(keys==halKeySavedKeys)

  {

    return;

  }

  halKeySavedKeys=keys;    /*Storethecurrentkeysforcomparationnexttime*/

  }

  /*InvokeCallbackifnewkeysweredepressed*/

  if(keys&&(pHalKeyProcessFunction))

  {

  (pHalKeyProcessFunction)(keys,HAL_KEY_STATE_NORMAL);

  }

#endif

}

/***************************************************************************

可以看到这里通过一系列处理读取键值,最后调用按键的回调函数(pHalKeyProcessFunction)(keys,HAL_KEY_STATE_NORMAL);这之前有个存储键值为下一次比较做准备,这里是查询法与中断法之间键值读取的区别点,查询法需要存储键值,而中断法则直接读取当前键值就好,具体我不钻了。

回到回调函数,在InitBoard()里调用HalKeyConfig(OnboardKeyIntEnable,OnBoard_KeyCallback),即把回调函数初始化为OnBoard_KeyCallback(),下面来看下这个函数

/***************************************************************************

voidOnBoard_KeyCallback(uint8keys,uint8state)

{

  uint8shift;

  //shiftkey(S1)isusedtogeneratekeyinterrupt

  //applicationsshouldnotuseS1whenkeyinterruptisenabled

  shift=(OnboardKeyIntEnable==HAL_KEY_INTERRUPT_ENABLE)?

false:

((keys&HAL_KEY_SW_6)?

true:

false);

  if(OnBoard_SendKeys(keys,shift)!

=ZSuccess)

  {

…………  (如果不成功进行的一些处理)

  }

}

/***************************************************************************

可以看到得到一个参数shift,然后调用了OnBoard_SendKeys(keys,shift),来看下这个函数

/***************************************************************************

byteOnBoard_SendKeys(bytekeys,bytestate)

{

  keyChange_t*msgPtr;

  if(registeredKeysTaskID!

=NO_TASK_ID)  //按键事件被注册在sampleAPP应用registeredKeysTaskID=SampleApp_TaskID

  {

  //Sendtheaddresstothetask

  msgPtr=(keyChange_t*)osal_msg_allocate(sizeof(keyChange_t));

  if(msgPtr)

  {

    msgPtr->hdr.event=KEY_CHANGE;

    msgPtr->state=state;

    msgPtr->keys=keys;

    osal_msg_send(registeredKeysTaskID,(uint8*)msgPtr);//

  }

  return(ZSuccess);

  }

  else

  return(ZFailure);

}

/***************************************************************************

可以看到首先涉及到一个参数registeredKeysTaskID,在OnBoard.c中它被定义为:

staticbyteregisteredKeysTaskID=NO_TASK_ID;

如果是这样的话,那按键事件处理到这里就返回一个ZFailure,没有任何反应了,但在SampleApp这个例子中是按SW1:

发送闪烁消息到组1,按SW2:

进/退组…这是为何?

这里涉及到一个函数RegisterForKeys(bytetask_id),来看下:

/***************************************************************************

*KeyboardRegisterfunction

*

*Thekeyboardhandlerissetuptosendallkeyboardchangesto

*onetask(ifataskisregistered).

*

*Ifataskregisters,itwillgetallthekeys.Youcanchangethis

*toregisterforindividualkeys.

************************************

byteRegisterForKeys(bytetask_id)  //task_id=SampleApp_TaskID

{

  //Allowonlythefirsttask

  if(registeredKeysTaskID==NO_TASK_ID)

  {

  registeredKeysTaskID=task_id;

  return(true);

  }

  else

  return(false);

}

/***************************************************************************

tuzhuke

2010-11-0509:

39:

14

可以看到一个应用任务要得到按键值,那必须首先进行注册,这样一旦有按键发生,这个任务才能得到键值进行相应的处理,而协议栈中唯一一个用户应用任务SampleApp就对其进行了注册,具体在SampleApp_Init()函数中有这么一句:

//Registerforallkeyevents-Thisappwillhandleallkeyevents

RegisterForKeys(SampleApp_TaskID);

即可由这个任务SampleApp来处理按键事件。

通过函数参数传递易知最后registeredKeysTaskID=SampleApp_TaskID,然后再回到OnBoard_SendKeys()这个函数中,此时registeredKeysTaskID!

=NO_TASK_ID,所以调用osal_msg_send(registeredKeysTaskID,(uint8*)msgPtr),而这个函数里又调用osal_set_event(destination_task,SYS_EVENT_MSG)来触发事件发生标志,系统调用相应任务事件处理函数,任务ID为SampleApp_TaskID,即SampleApp_ProcessEvent(),通过参数SYS_EVENT_MSG和OnBoard_SendKeys()里的msgPtr->hdr.event=KEY_CHANGE可知任务事件处理函数SampleApp_ProcessEvent()最终调用SampleApp_HandleKeys(),SampleApp_HandleKeys()里面对按键进行了处理,包括按SW1:

发送闪烁消息到组1,按SW2:

进/退组。

  

至此,协议栈的按键查询法流程结束,一次下来两次触发系统事件(第一次通过osal_start_timerEx()启动一个软定时器触发,第二次通过osal_msg_send()发送系统消息触发;这俩的确是重点函数),分别调用相应任务事件处理函数,第一次是HAL层的Hal_ProcessEvent()来查询按键得到键值,一系列处理,第二次是APP层的SampleApp_ProcessEvent()把传送过来的按键事件进行最终处理.查询法函数调用流程如下:

HalKeyConfig()配置一定时器为轮询按键作准备——时间一到触发系统任务事件调用Hal_ProcessEvent()——调用HalKeyPoll()得到按键值——调用OnBoard_KeyCallback()进一步处理——调用OnBoard_SendKeys()构造消息包,准备触发应用按键事件【注意这个应用之前必须通过RegisterForKeys()注册接收按键事件的任务ID】——调用osal_msg_send()向系统发送消息——调用osal_set_event()设置事件发生标志——调用SampleApp_ProcessEvent()处理事件——最终调用SampleApp_HandleKeys()处理具体按键事件

2、中断法

按键引起的中断应属于外部中断.在HalKeyConfig()中有这样一段描述:

    WorkaroundforCC2430DBwheninterru

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > IT计算机 > 电脑基础知识

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1