我是靠谱客的博主 简单面包,最近开发中收集的这篇文章主要介绍Zigbee 按键机制,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

我做操作的芯片是TiCC2530。就不考虑Zstack协议栈而言,如果要用到按键,无非是中断或者查询两种方式。

查询方式是通过查询按键对应I/O口的状态来判断按键的状态,从而进行相应的处理。

外部中断的方式是使能按键对应得I/O口的中断,当I/O口的状态改变时触发中断,从而在相应的中断处理函数中进行处理。

万变不离其宗,Zigbee协议栈的按键操作本质上也是对于基本查询方式和终端方式的封装。对于查询方式我们关键要找到它是如何配置引脚的功能/输入输出/上拉下拉,以及是如何查询引脚状态的,对于中断方式关键是配置相应引脚的中断和相应的中断处理函数。

首先是ZMain.c文件中的两个函数涉及到了按键的配置,分别是InitBoard( )和 HalDriverInit()其中InitBoard()调用了两次

  // Initialize board I/O
  InitBoard( OB_COLD );

右键GO TO,其中参数OB_COLD是一个宏#define OB_COLD  0

void InitBoard( uint8 level )
{
  if ( level == OB_COLD )
  {
    // Interrupts off
    osal_int_disable( INTS_ALL );
    // Turn all LEDs off
    HalLedSet( HAL_LED_ALL, HAL_LED_MODE_OFF );
    // Check for Brown-Out reset
    ChkReset();
  }
  else  // !OB_COLD
  {
    /* Initialize Key stuff */
    OnboardKeyIntEnable = HAL_KEY_INTERRUPT_DISABLE;
    HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);
  }
}

第一个if是成立的,所以关闭所有的中断并关闭LED。我们回到main()函数

  // Initialze HAL drivers
  HalDriverInit();

右键GO TO,找到了按键的初始化函数HalKeyInit()

  /* KEY */
#if (defined HAL_KEY) && (HAL_KEY == TRUE)
  HalKeyInit();
#endif

右键GO TO ,这里主要是配置了P0SEL、P0DIR和P1SEL、P1DIR为通用和输入。

void HalKeyInit( void )
{
  /* Initialize previous key to 0 */
  halKeySavedKeys = 0;          //默认当前键值为0

  HAL_KEY_SW_6_SEL &= ~(HAL_KEY_SW_6_BIT);    /* Set pin function to GPIO */      //P0.1设置为通用
  HAL_KEY_SW_6_DIR &= ~(HAL_KEY_SW_6_BIT);    /* Set pin direction to Input */    //P0.1设置为输入

  HAL_KEY_JOY_MOVE_SEL &= ~(HAL_KEY_JOY_MOVE_BIT); /* Set pin function to GPIO */     //P2.0设置为通用
  HAL_KEY_JOY_MOVE_DIR &= ~(HAL_KEY_JOY_MOVE_BIT); /* Set pin direction to Input */   //P2.0设置为输入


  /* Initialize callback function */
  pHalKeyProcessFunction  = NULL;         //默认无回调函数

  /* Start with key is not configured */
  HalKeyConfigured = FALSE;           
}

GO TO HAL_KEY_SW_6_SEL发现就是P0SEL寄存器,到这里终于揭开了Zigbee包装的一角,其中寄存器的配置是也是以宏的方式给出的

#define HAL_KEY_SW_6_PORT   P0
#define HAL_KEY_SW_6_BIT    BV(1)
#define HAL_KEY_SW_6_SEL    P0SEL
#define HAL_KEY_SW_6_DIR    P0DIR
#ifndef BV
#define BV(n)      (1 << (n))
#endif
/* Joy stick move at P2.0 */
#define HAL_KEY_JOY_MOVE_PORT   P2
#define HAL_KEY_JOY_MOVE_BIT    BV(0)
#define HAL_KEY_JOY_MOVE_SEL    P2SEL
#define HAL_KEY_JOY_MOVE_DIR    P2DIR

特别的,1<<(n)就是将1左移n位,整数型立即数默认类型是int型,占有4个字节,所以1<<(1)等价于 0x 0000 0000 0000 0010。

  /* Initialize callback function */
  pHalKeyProcessFunction  = NULL;         

回调函数为空,说明中断方式的中断处理函数不是在这里进行设置的。我们回到main()函数,发现再次调用了InitBoard(),这次的参数为2 ,#define OB_READY 2

  // Final board initialization
  InitBoard( OB_READY );

右键GO TO,由于参数为2,这次进入了第一次调用InitBoard()时没有进入的另一个if分支

void InitBoard( uint8 level )
{
  if ( level == OB_COLD )
  {
    // Interrupts off
    osal_int_disable( INTS_ALL );
    // Turn all LEDs off
    HalLedSet( HAL_LED_ALL, HAL_LED_MODE_OFF );
    // Check for Brown-Out reset
    ChkReset();
  }
  else  // !OB_COLD
  {
    /* Initialize Key stuff */
    OnboardKeyIntEnable = HAL_KEY_INTERRUPT_DISABLE;
    HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);
  }
}

右键GO TO 按键配置函数HalKeyConfig(),发现这里配置了引脚的中断

void HalKeyConfig (bool interruptEnable, halKeyCBack_t cback)
{
  /* Enable/Disable Interrupt or */
  Hal_KeyIntEnable = interruptEnable;

  /* Register the callback fucntion */
  pHalKeyProcessFunction = cback;

  /* Determine if interrupt is enable or not */
  if (Hal_KeyIntEnable)
  {
    /* Rising/Falling edge configuratinn */

    PICTL &= ~(HAL_KEY_SW_6_EDGEBIT);    /* Clear the edge bit */
    /* For falling edge, the bit must be set. */
  #if (HAL_KEY_SW_6_EDGE == HAL_KEY_FALLING_EDGE)
    PICTL |= HAL_KEY_SW_6_EDGEBIT;
  #endif


    /* Interrupt configuration:
     * - Enable interrupt generation at the port
     * - Enable CPU interrupt
     * - Clear any pending interrupt
     */
    HAL_KEY_SW_6_ICTL |= HAL_KEY_SW_6_ICTLBIT;
    HAL_KEY_SW_6_IEN |= HAL_KEY_SW_6_IENBIT;
    HAL_KEY_SW_6_PXIFG = ~(HAL_KEY_SW_6_BIT);



    /* Rising/Falling edge configuratinn */

    HAL_KEY_JOY_MOVE_ICTL &= ~(HAL_KEY_JOY_MOVE_EDGEBIT);    /* Clear the edge bit */
    /* For falling edge, the bit must be set. */
  #if (HAL_KEY_JOY_MOVE_EDGE == HAL_KEY_FALLING_EDGE)
    HAL_KEY_JOY_MOVE_ICTL |= HAL_KEY_JOY_MOVE_EDGEBIT;
  #endif


    /* Interrupt configuration:
     * - Enable interrupt generation at the port
     * - Enable CPU interrupt
     * - Clear any pending interrupt
     */
    HAL_KEY_JOY_MOVE_ICTL |= HAL_KEY_JOY_MOVE_ICTLBIT;
    HAL_KEY_JOY_MOVE_IEN |= HAL_KEY_JOY_MOVE_IENBIT;
    HAL_KEY_JOY_MOVE_PXIFG = ~(HAL_KEY_JOY_MOVE_BIT);


    /* Do this only after the hal_key is configured - to work with sleep stuff */
    if (HalKeyConfigured == TRUE)
    {
      osal_stop_timerEx( Hal_TaskID, HAL_KEY_EVENT);  /* Cancel polling if active */
    }
  }
  else    /* Interrupts NOT enabled */
  {
    HAL_KEY_SW_6_ICTL &= ~(HAL_KEY_SW_6_ICTLBIT); /* don't generate interrupt */
    HAL_KEY_SW_6_IEN &= ~(HAL_KEY_SW_6_IENBIT);   /* Clear interrupt enable bit */

    osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_POLLING_VALUE);    /* Kick off polling */
  }

  /* Key now is configured */
  HalKeyConfigured = TRUE;
}

  Hal_KeyIntEnable = interruptEnable;而interruptEnable的值等于宏HAL_KEY_INTERRUPT_DISABLE,默认是不采用外部中断方式,那也就是查询方式,可以通过选择不同的宏来控制采用哪种方式。

/* Interrupt option - Enable or disable */
#define HAL_KEY_INTERRUPT_DISABLE    0x00
#define HAL_KEY_INTERRUPT_ENABLE     0x01
配置了回调函数,也就是原来的中断处理函数

  /* Register the callback fucntion */
  pHalKeyProcessFunction = cback;
如果Hal_KeyIntEnable为HAL_KEY_INTERRUPT_ENABLE的话就进行引脚的中断使能配置
if (Hal_KeyIntEnable)

否则的话就清除中断配置,并产生一个HAL_KEY_EVENT事件

  else    /* Interrupts NOT enabled */
  {
    HAL_KEY_SW_6_ICTL &= ~(HAL_KEY_SW_6_ICTLBIT); /* don't generate interrupt */
    HAL_KEY_SW_6_IEN &= ~(HAL_KEY_SW_6_IENBIT);   /* Clear interrupt enable bit */

    osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_POLLING_VALUE);    /* Kick off polling */
  }

有事件产生就有相应的事件处理函数,如果对Zigbee事件机制不太理解的可以参考点击打开链接。

这个事件在hal_drivers.c文件里的Hal_ProcessEvent函数被处理

  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)
    {
      osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100);
    }
#endif // HAL_KEY

    return events ^ HAL_KEY_EVENT;
  }

如果Hal_KeyIntEnable == 0 即采用轮询方式就周期性设置这一事件,从而达成了一个循环调用的效果,轮询函数为HalKeyPoll(),可以看到轮询的事件大约是100ms。HalKeyPoll()函数会检查按键的状态以确定是否要按键状态的改变。

void HalKeyPoll (void)
{
  uint8 keys = 0;

//  if ((HAL_KEY_JOY_MOVE_PORT & HAL_KEY_JOY_MOVE_BIT))  /* Key is active HIGH */   //如果P2.0是高位
//  {
//    keys = halGetJoyKeyInput();                         
//  }

  /* If interrupts are not enabled, previous key status and current key status
   * are compared to find out if a key has changed status.
   */
  
  
    if (!HAL_PUSH_BUTTON1())       //HAL_PUSH_BUTTON1() =  !!(P0_1)
  {
    keys |= HAL_KEY_SW_6;
  }
  
      if (!HAL_PUSH_BUTTON2())           //HAL_PUSH_BUTTON2() = !!(P2_0)
  {
    keys |= HAL_KEY_SW_1;
  }
  
  
  
  if (!Hal_KeyIntEnable)
  {
    if (keys == halKeySavedKeys)
    {
      /* Exit - since no keys have changed */
      return;
    }
    /* Store the current keys for comparation next time */
    halKeySavedKeys = keys;
  }
  else
  {
    /* Key interrupt handled here */
  }



  /* Invoke Callback if new keys were depressed */
  if (keys && (pHalKeyProcessFunction))
  {
    (pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);
  }
}
//  if ((HAL_KEY_JOY_MOVE_PORT & HAL_KEY_JOY_MOVE_BIT))  /* Key is active HIGH */   //如果P2.0是高位
//  {
//    keys = halGetJoyKeyInput();                         
//  }

  /* If interrupts are not enabled, previous key status and current key status
   * are compared to find out if a key has changed status.
   */
  
  
    if (!HAL_PUSH_BUTTON1())       //HAL_PUSH_BUTTON1() =  !!(P0_1)
  {
    keys |= HAL_KEY_SW_6;
  }
  
      if (!HAL_PUSH_BUTTON2())           //HAL_PUSH_BUTTON2() = !!(P2_0)
  {
    keys |= HAL_KEY_SW_1;
  }

hal_key.c中HalKeyPoll()中的这一段是我私自更改的地方,由于我用的开发板的P0_1和P2_0是上拉状态,所以未按下时引脚是高电平,而协议栈默认是以高电平作为按键按下的状态,所以我的板子初始化之后就会自动识别为有按键按下,因此我改成了低电平时才认为有按键按下。同时,由于我的开发板无操纵杆,所以注销了操作杆的状态识别。

这里的按键是以位区分的,这样做的好处是可以将多个按键的触发事件按位存储到keys中,并通过位运算识别和增删。

/* Switches (keys) */
#define HAL_KEY_SW_1 0x01  // Joystick up
#define HAL_KEY_SW_2 0x02  // Joystick right
#define HAL_KEY_SW_5 0x04  // Joystick center
#define HAL_KEY_SW_4 0x08  // Joystick left
#define HAL_KEY_SW_3 0x10  // Joystick down
#define HAL_KEY_SW_6 0x20  // Button S1 if available
#define HAL_KEY_SW_7 0x40  // Button S2 if available
在最后,如果有按键被按下且回调函数不为空时即调用回调函数。由于在HalKeyConfig()中已经注册了回调函数,所以这个回调函数是不为空的,所以就变成了只要有按键按下就调用回调函数。
  /* Invoke Callback if new keys were depressed */
  if (keys && (pHalKeyProcessFunction))
  {
    (pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);
  }

回调函数在当前文件中

void OnBoard_KeyCallback ( uint8 keys, uint8 state )
{
  uint8 shift;
  (void)state;

  /* Get shift key status */
  shift = ((keys & HAL_KEY_SW_6) ? true : false);

  if ( OnBoard_SendKeys( keys, shift ) != 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 5
    {
    }
    // Process SW6 here
    if ( keys & HAL_KEY_SW_6 )  // Switch 6
    {
    }
  }
}

其重点是调用了OnBoard_SendKeys( keys, shift ),右键GO TO这个函数

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 );
    }
    return ( ZSuccess );
  }
  else
    return ( ZFailure );
}

在这个函数中将按键触发状态key打包发送到应用层,并触发应用层事件处理函数

uint16 SampleApp_ProcessEvent( uint8 task_id, uint16 events )
{
  
  afIncomingMSGPacket_t *MSGpkt;
  (void)task_id;  

  if ( events & SYS_EVENT_MSG )
  {
    MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID );
    while ( MSGpkt )
    {
      switch ( MSGpkt->hdr.event )
      {
       
        case KEY_CHANGE:
          SampleApp_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys );
          break;

然后就可以在应用层调用相应的事件处理函数来处理这个按键事件,而到底是哪个按键被按下,可以通过'&'操作来检查keys的位状态,哪一位为1就表示那一位对应的按键发生了改变。

void SampleApp_HandleKeys( uint8 shift, uint8 keys )
{
  (void)shift; 
  
  if ( keys & HAL_KEY_SW_6 )
  {
    /* This key sends the Flash Command is sent to Group 1
     * This device will not receive the Flash Command from this
     * device (even if it belongs to group 1)
     */
//    SampleApp_SendFlashMessage( SAMPLEAPP_FLASH_DURATION );
    HalLedSet( HAL_LED_1 , HAL_LED_MODE_TOGGLE )  ;
    zgWriteStartupOptions(ZG_STARTUP_SET, ZCD_STARTOPT_DEFAULT_NETWORK_STATE);
    SystemReset();
    
  }

  if ( keys & HAL_KEY_SW_1 )
  {
    /* The Flashr Command is sent to Group 1.
     * This key toggles this device in and out of group 1.
     * If this device doesn't belong to group 1, this application
     * will not receive the Flash command sent to group 1.
     */

  }
}



水平有限,如有差错,请不吝赐教!








最后

以上就是简单面包为你收集整理的Zigbee 按键机制的全部内容,希望文章能够帮你解决Zigbee 按键机制所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(67)

评论列表共有 0 条评论

立即
投稿
返回
顶部