查看: 5783|回复: 3

zigbee按键流程之代码跟踪

[复制链接]
huruitao 发表于 2013-12-22 12:34:58 | 显示全部楼层 |阅读模式

自己一直游荡于各位大神的帖子之中汲取养分,学到了不少东西!怀着缅怀大神和激动的心情,我把自己分析的按键事件逐句的记录下来,说实话这种幼儿园的水平不值得粘出来,不过事事都有第一次嘛,饮水思源,把大神们的无私精神继续下去很重要!

zigbee的硬件也是单片机所以按键也无非两种方式:轮询和中断!

中断法:当有按键按下时,首先进入按键中断处理程序,里面有osal_start_timerEx()这个函数,用于设置按键处理程序,即调用Hal_ProcessEvent(),得到按键的值,并通过一系列调用传到应用层中。后面的步骤和上面一样我就不说了。该方法只调用osal_start_timerEx()一次,没有按键按下的时候,不会调用Hal_ProcessEvent()函数。也不存在循环的概念。

查询法:  通过该函数HalKeyConfig(),调用osal_start_timerEx(),再执行Hal_ProcessEvent(),再调用osal_start_timerEx()一次(该函数包含在hal_processEvent()中,如果选择的查询法,这该函数执行,如果选择的是中断方式则不执行)。从而实现循环,及每隔1毫秒执行一次Hal_ProcessEvent()函数。后面过程与上面一样。该方法调用osal_start_timerEx()二次(得到一次键值用的次数),目的就是为了实现轮询。

两方法的共同点:除了调用osal_start_timerEx()的方法和次数不同外,其余都一样。方法1利用中断方法调用,只使用一次。方法2程序执行时就调用,使用两次。归根结底,要得到按键值,必须设置事件发生标志,操作系统查询到有按键事件发生标志,调用hal层事件处理函数,得到按键的值。
============================================
具体分析如下:
在main主函数中初始化调用了void InitBoard( uint8 level )函数
void InitBoard( uint8 level )
{
  if ( level == OB_COLD )
  {
    // IAR does not zero-out this byte below the XSTACK.
    *(uint8 *)0x0 = 0;
    // Interrupts off
    osal_int_disable( INTS_ALL );
    // Check for Brown-Out reset
    ChkReset();
  }
  else  // !OB_COLD
  {
    /* Initialize Key stuff */
    #ifndef ENDDEVICE
    HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE, OnBoard_KeyCallback);//协调、路由的按键采用轮询,回调函数设为OnBoard_KeyCallback
    #else
    HalKeyConfig(HAL_KEY_INTERRUPT_ENABLE, OnBoard_KeyCallback);//终端采用中断,‍回调函数设为OnBoard_KeyCallback
‍    #endif
  }
}
在HalKeyConfig函数中若是中断则配置中断方式,若是轮询则置事件发送标识 HAL_KEY_EVENT
void HalKeyConfig(bool interruptEnable, halKeyCBack_t cback)
{
  if ((Hal_KeyIntEnable = interruptEnable))
  {
    HAL_KEY_CLR_INT();             // Clear spurious ints.
    PICTL |= 0x01;                 // P1ICONL: Falling edge ints on pins 0-3.
    P1IEN |= PUSH1_BV | PUSH2_BV;  // Enable specific P1 bits for ints by bit mask.
    IEN2  |= 0x10;                 // Enable general P1 interrupts.
  }
  else
  {
    (void)osal_set_event(Hal_TaskID, HAL_KEY_EVENT);
  }
  pHalKeyProcessFunction = cback;
}
随后osal系统发现了tasksEvents[Hal_TaskID]项不为0,就调用其对应的任务处理函数 Hal_ProcessEvent
在Hal_ProcessEvent 函数中查询事件标识HAL_KEY_EVENT
uint16 Hal_ProcessEvent( uint8 task_id, uint16 events )
{
  uint8 *msgPtr;
  
  (void)task_id;  // Intentionally unreferenced parameter
  if ( events & SYS_EVENT_MSG )
  {
    msgPtr = osal_msg_receive(Hal_TaskID);
    while (msgPtr)
    {
      /* Do something here - for now, just deallocate the msg and move on */
      /* De-allocate */
      osal_msg_deallocate( msgPtr );
      /* Next */
      msgPtr = osal_msg_receive( Hal_TaskID );
    }
    return events ^ SYS_EVENT_MSG;
  }
  if ( events & HAL_LED_BLINK_EVENT )
  {
#if (defined (BLINK_LEDS)) && (HAL_LED == TRUE)
    HalLedUpdate();
#endif /* BLINK_LEDS && HAL_LED */
    return events ^ HAL_LED_BLINK_EVENT;
  }
  if (events & HAL_KEY_EVENT)
  {
#if (defined HAL_KEY) && (HAL_KEY == TRUE)
    /* Check for keys */
    HalKeyPoll();
    /* if interrupt disabled, do next polling */
    if (!Hal_KeyIntEnable)//轮询模式就100ms后会在进入这个函数的
    {
      osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100);
    }
#endif // HAL_KEY
    return events ^ HAL_KEY_EVENT;
  }
#ifdef POWER_SAVING
  if ( events & HAL_SLEEP_TIMER_EVENT )
  {
    halRestoreSleepLevel();
    return events ^ HAL_SLEEP_TIMER_EVENT;
  }
#endif
#ifdef CC2591_COMPRESSION_WORKAROUND
  if ( events & PERIOD_RSSI_RESET_EVT )
  {
    macRxResetRssi();
    return (events ^ PERIOD_RSSI_RESET_EVT);
  }
#endif  
  
  /* Nothing interested, discard the message */
  return 0;
}
在上面Hal_ProcessEvent 函数中根据事件标识HAL_KEY_EVENT 执行HalKeyPoll();函数
void HalKeyPoll(void)
{
  uint8 newKeys;
  if (Hal_KeyIntEnable)//中断模式入口
  {
    halIntState_t intState;
    HAL_ENTER_CRITICAL_SECTION(intState);
    newKeys = isrKeys;
    isrKeys = 0;
    HAL_EXIT_CRITICAL_SECTION(intState);
  }
  else//轮询模式的入口
  {
    uint8 keys = HalKeyRead();//获得S1按键值0x20
    newKeys = (halKeys ^ keys) & keys;
    halKeys = keys;
  }
  if (newKeys && pHalKeyProcessFunction)
  {
    (pHalKeyProcessFunction)(newKeys, HAL_KEY_STATE_NORMAL);
  }
}
在HalKeyPoll函数中因为轮询模式则函数会调用HalKeyRead函数读取按键值
uint8 HalKeyRead ( void )
{
  uint8 keys = 0;
#ifndef A8GATEWAY
  if (HAL_PUSH_BUTTON1())//若是低电平则if条件成立
  {
    keys |= HAL_KEY_SW_6;
  }
#endif
#if (PROJECT == 3) && (SENSOR_TYPE == 1)
  if (HAL_PUSH_BUTTON2())
  {
    keys |= HAL_KEY_CURTAIN1;
  }
  if (HAL_PUSH_BUTTON3())
  {
    keys |= HAL_KEY_CURTAIN2;
  }
  if (HAL_PUSH_BUTTON4())
  {
    keys |= HAL_KEY_CURTAIN3;
  }
#endif
#if (HAL_JOYSTICK == TRUE)
  if ((HAL_KEY_JOY_MOVE_PORT & HAL_KEY_JOY_MOVE_BIT))  /* Key is active low */
  {
    keys |= halGetJoyKeyInput();
  }
#endif
  return keys;
}
在HalKeyRead把按键值给到keys之后,在根据函数调用关系一步一步的往回看,HalKeyRead 在HalKeyPoll 函数中把键值给到newKeys 后继续执行下面的语句:
if (newKeys && pHalKeyProcessFunction)//若键值不为0并且回调函数不为空那就执行回调函数
  {
    (pHalKeyProcessFunction)(newKeys, HAL_KEY_STATE_NORMAL);
  }
至于回调函数,程序在InitBoard 初始化过程中通过HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE, OnBoard_KeyCallback)函数配置为OnBoard_KeyCallback 函数了!
void OnBoard_KeyCallback ( uint8 keys, uint8 state )
{
#ifndef A8GATEWAY
  uint8 shift;
  (void)state;
  shift = (keys & HAL_KEY_SW_6) ? true : false;
  if ( OnBoard_SendKeys( keys, shift ) != ZSuccess )//成功将按键消息送到任务消息表并置上了一级事件标识SYS_EVENT_MSG 和二级事件标识KEY_CHANGE 后这里返回ZSuccess  
{
    // Process SW1 here
    if ( keys & HAL_KEY_SW_1 )  // Switch 1
    {
    }
    // Process SW2 here
    if ( keys & HAL_KEY_SW_2 )  // Switch 2
    {
    }
    // Process SW3 here
    if ( keys & HAL_KEY_SW_3 )  // Switch 3
    {
    }
    // Process SW4 here
    if ( keys & HAL_KEY_SW_4 )  // Switch 4
    {
    }
    // Process SW5 here
    if ( keys & HAL_KEY_SW_5 )  // Switch
    {
    }
    // Process SW6 here
    if ( keys & HAL_KEY_SW_6 )  // Switch 6
   {
    }
  }
#endif
}
上面回调函数中调用了OnBoard_SendKeys 函数:
uint8 OnBoard_SendKeys( uint8 keys, uint8 state )
{
  keyChange_t *msgPtr;
  if ( registeredKeysTaskID != NO_TASK_ID )//注册过按键事件
  {
    // Send the address to the task
    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 );//把按键事件送到OSAL任务事件表中
    }
    return ( ZSuccess );
  }
  else
    return ( ZFailure );
}
在用osal_msg_send 函数将按键消息送入OSAL任务事件表时,其本身又设置了SYS_EVENT_MSG 事件标识
uint8 osal_msg_send( uint8 destination_task, uint8 *msg_ptr )
{
  if ( msg_ptr == NULL )
    return ( INVALID_MSG_POINTER );
  if ( destination_task >= tasksCnt )
  {
    osal_msg_deallocate( msg_ptr );
    return ( INVALID_TASK );
  }
  // Check the message header
  if ( OSAL_MSG_NEXT( msg_ptr ) != NULL ||
       OSAL_MSG_ID( msg_ptr ) != TASK_NO_TASK )
  {
    osal_msg_deallocate( msg_ptr );
    return ( INVALID_MSG_POINTER );
  }
  OSAL_MSG_ID( msg_ptr ) = destination_task;
  // queue message
  osal_msg_enqueue( &osal_qHead, msg_ptr );
  // Signal the task that a message is waiting
  osal_set_event( destination_task, SYS_EVENT_MSG );
  return ( SUCCESS );
}
完成事件标记最后一步的是osal_set_event ,它的任务就是把对应的tasksEvents[task_id]其中的某一位 置为非0
uint8 osal_set_event( uint8 task_id, uint16 event_flag )
{
  if ( task_id < tasksCnt )
  {
    halIntState_t   intState;
    HAL_ENTER_CRITICAL_SECTION(intState);    // Hold off interrupts
    tasksEvents[task_id] |= event_flag;  // Stuff the event bit(s)
    HAL_EXIT_CRITICAL_SECTION(intState);     // Release interrupts
    return ( SUCCESS );
  }
   else
  {
    return ( INVALID_TASK );
  }
}
经过层层调用然后返回到函数的调用处继续执行下面的语句,最后以一句
return events ^ HAL_KEY_EVENT结束按键事件的触发流程

然后OSAL系统再次轮询到被osal_set_event 置为非0的那个tasksEvents[Hal_TaskID],和按键触发流程一样去调用它对应相同顺序的那个处理函数, 在这个函数中对消息包层层剥离,根据剥出来的事件标识:SYS_EVENT_MSG 、KEY_CHANGE 找到正确的处理函数,再根据具体消息内容,比如keys=0x20找到具体的处理,至此系统对于你没事按按键的事就此罢休了!

飞比管家 发表于 2014-1-7 09:55:18 | 显示全部楼层
感谢楼主的分享!{:soso_e113:}
astudo 发表于 2014-1-11 21:57:16 | 显示全部楼层
多谢LZ分析,还是有些不懂, 比如if (events & HAL_KEY_EVENT),if ( events & SYS_EVENT_MSG )
HAL_KEY_EVENT,SYS_EVENT_MSG的值是固定的吗,events的不同值代表什么含义?
Hugo801122 发表于 2014-2-20 00:40:10 | 显示全部楼层
好详细,多学学先。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表