ZigBee开发(14)--组网实验按键

时间:2024-02-16 11:49:18

通过节点 1 的按键 S1 中断配置,检测按键的按下情况。整个过程在协议栈 Z-STACK SampleApp.eww 上完成。

协议栈已经自带了按键的驱动和使用函数。所以将按键改到任 IO 口也不是问题了。 

首先我们需要做的是花精力将官方自带的按键 IO 改到我的板子的 IO口上。 官方电路的按键 S1 连接的是 P0.1 引脚,我的按键 S1连接的是 P0.1 口,按键 S2 连接的是 P2.0 口。

首先就是要了解协议栈中按键的检测与按键事件的传递过程;

按键流程分析 :
  在上一节工程的基础上我们来进行分析修改,(上一节的工程链接为:https://pan.baidu.com/s/10eRDKbndrYMTkm-sgpGd_w

打开 SampleApp.eww 工程,在 ZMain.c main 主函数中跟按键相关的有:

HalDriverInit();
InitBoard( OB_READY );

进入 HalDriverInit()中的 HalKeyInit()

void HalKeyInit( void )
{
  /* Initialize previous key to 0 */
  halKeySavedKeys = 0;

  HAL_KEY_SW_6_SEL &= ~(HAL_KEY_SW_6_BIT);    /* Set pin function to GPIO */
  HAL_KEY_SW_6_DIR &= ~(HAL_KEY_SW_6_BIT);    /* Set pin direction to Input */

  HAL_KEY_JOY_MOVE_SEL &= ~(HAL_KEY_JOY_MOVE_BIT); /* Set pin function to GPIO */
  HAL_KEY_JOY_MOVE_DIR &= ~(HAL_KEY_JOY_MOVE_BIT); /* Set pin direction to Input */


  /* Initialize callback function */
  pHalKeyProcessFunction  = NULL;

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

这里主要是初始化按键相关的引脚, HAL_KEY_SW_6 就是对应 P01,而HAL_KEY_JOY 就是 TI 子上的 J-STICK 摇杆,

我们的板子上大部分都没有,直接注释或删掉。

 接着分析 InitBoard( OB_READY )

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 */
    HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE, OnBoard_KeyCallback);
  }
}

对按键的具体配置(重点): 配置按键的检测方式和按键的回调函数

HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE,OnBoard_KeyCallback); 

进入到此函数中,先配置非中断检测的,先看代码:

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_KEY_EVENT 事件 , 找到此事件的处理函数。
hal_drivers.c 中的 Hal_ProcessEvent()函数:

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)
    {
      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;

}

接下来就是 HalKeyPoll()了, 

void HalKeyPoll (void)
{
  uint8 keys = 0;

/* if ((HAL_KEY_JOY_MOVE_PORT & HAL_KEY_JOY_MOVE_BIT))  // Key is active HIGH 
  {
    keys = halGetJoyKeyInput();
  }
*/
  if (!HAL_PUSH_BUTTON2())//S0
  {
    keys |= HAL_KEY_SW_1; 
  }
  if (!HAL_PUSH_BUTTON1())//S1 
  {
    keys |= HAL_KEY_SW_6; 
  }
  
  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);
  }
}

当按键按下时就传递给上面注册过的回调函数 OnBoard_KeyCallback,传 递 进 去 的 就 是 相 应  键 值 keys ; 找 到 之 前 注 册 的 回 调 函 数OnBoard_KeyCallback(在 OnBoard.c 中)

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

  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 )在发送系统信息给用户的应用任务。

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 );
}

到这里我们就不知道 msg 发送到哪里了? registeredKeysTaskID 任务?这个就 是 我 们 用 户 根 据 自 己 的 需 要 选 择 按 键 的 传 递 的 任 务 号 , 可 通 过RegisterForKeys( xxx_TaskID )注册,此工程中在 SampleApp_Init()中已经调用此函数注册到 SampleApp_TaskID 中了,就是说按键信息会传递到此任务中。
接下来就是终点站了: 

void SampleApp_HandleKeys( uint8 shift, uint8 keys ) 
{
  (void)shift;  // Intentionally unreferenced parameter
  
  if ( keys & HAL_KEY_SW_1 )
  {
    HalUARTWrite(0,"KEY1\n",4);
    /* 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 );
  }

  if ( keys & HAL_KEY_SW_2 )
  {
    HalUARTWrite(0,"KEY2\n",4);
    /* 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.
     */
    aps_Group_t *grp;
    grp = aps_FindGroup( SAMPLEAPP_ENDPOINT, SAMPLEAPP_FLASH_GROUP );
    if ( grp )
    {
      // Remove from the group
      aps_RemoveGroup( SAMPLEAPP_ENDPOINT, SAMPLEAPP_FLASH_GROUP );
    }
    else
    {
      // Add to the flash group
      aps_AddGroup( SAMPLEAPP_ENDPOINT, &SampleApp_Group );
    }
  }
}

根据具体的键值做相应的处理,这里利用串口打印提示按键按下。 

轮训按键链接https://pan.baidu.com/s/1zNySZRpc83p9g0-h71jhzg

 

第二中按键检测方式:中断检测

 在  HalKeyConfig(HAL_KEY_INTERRUPT_ENABLE, OnBoard_KeyCallback);中 设置成中断检测方式,这样会更节省系统资源,所以一般都是使用中断方式来检测按键

当按键按下时进入P0口中断服务函数

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;
}

 

 并调用 halProcessKeyInterrupt()函数处理按键中断事件:

void halProcessKeyInterrupt (void)
{
  bool valid=FALSE;

  if (HAL_KEY_SW_6_PXIFG & HAL_KEY_SW_6_BIT)  /* Interrupt Flag has been set */
  {
    HAL_KEY_SW_6_PXIFG = ~(HAL_KEY_SW_6_BIT); /* Clear Interrupt Flag */
    valid = TRUE;
  }

  if (HAL_KEY_JOY_MOVE_PXIFG & HAL_KEY_JOY_MOVE_BIT)  /* Interrupt Flag has been set */
  {
    HAL_KEY_JOY_MOVE_PXIFG = ~(HAL_KEY_JOY_MOVE_BIT); /* Clear Interrupt Flag */
    valid = TRUE;
  }

  if (valid)
  {
    osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_DEBOUNCE_VALUE);
  }
}

这里又跑去启动 HAL_KEY_EVENT 事件了,然后就把按键发送个系统上层任务,后面 就跟轮询方式一样了

中断方式链接:https://pan.baidu.com/s/1oKAOByy6PdIaiiKieIoXMA

 

 下面是修改IO口和中断标志的位置

修改 hal_key.C 文件.

  1、 修改 SW_6 所在 IO
2

/* SW_6 is at P0.4 */
#define HAL_KEY_SW_6_PORT P0

#define HAL_KEY_SW_6_BIT BV(4) //BV(1) 改到 P0.4

#define HAL_KEY_SW_6_SEL P0SEL
#define HAL_KEY_SW_6_DIR P0DIR

3、 边缘触发方式

/* edge interrupt */
#define HAL_KEY_SW_6_EDGEBIT BV(0)
#define
HAL_KEY_SW_6_EDGE
HAL_KEY_RISING_EDGE//HAL_KEY_FALLING_EDGE 改成上升缘触发

4、 中断一些相关标志位

/* SW_6 interrupts */
#define HAL_KEY_SW_6_IEN IEN1 /* CPU interrupt mask
register */
#define HAL_KEY_SW_6_IENBIT BV(5) /* Mask bit for all of Port_0 */
#define HAL_KEY_SW_6_ICTL P0IEN /* Port Interrupt Control
register */
#define HAL_KEY_SW_6_ICTLBIT BV(4) //BV(1) /* P0IEN – P0.1
enable/disable bit 改到 P0.4*/
#define HAL_KEY_SW_6_PXIFG P0IFG /* Interrupt flag at source */

不需要用到 TI 的摇杆 J-STICK, 所以把代码注释掉。

void HalKeyPoll (void)
{
uint8 keys = 0;
if ((HAL_KEY_JOY_MOVE_PORT & HAL_KEY_JOY_MOVE_BIT)) 

/* Key is active HIGH */
{
//keys = halGetJoyKeyInput();
} 

修改 hal_board_cfg.h 文件。

  1、 修改 SW_6 所在 IO 口
/* S1 */
#define PUSH1_BV BV(4) //BV(1)
#define PUSH1_SBIT P0_4 //P0_1 

修改 OnBoard.C 文件。在 ZMain.C 目录树下, 

   2、 使能中断
HalKeyConfig(HAL_KEY_INTERRUPT_ENABLE,OnBoard_KeyCallback); 

 

下面是协议栈是检测到按键按下时候是如何处理的, 16 位必须只占 1 位,所以只能 16个任务。
回到 SampleApp.c 文件,找到按键时间处理 KEY_CHANGE 事件的函数:

当按键按下时,就会进入上面事件,我们加入串口提示:

// Received when a key is pressed
        case KEY_CHANGE://按键事件
          HalUARTWrite(0,"KEY ",4);
          SampleApp_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys );
          break;

进入 SampleApp_HandleKeys()函数,加入我们的按键处理函数。这里是SW_6,也即是我们刚定义好的开发板上的 S1

if ( keys & HAL_KEY_SW_6 )
  {
    HalUARTWrite(0,"KEY1\n",4);
    /* 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 );
  }



 

 

这个实验花了一个下午才弄明白,不过这一个下午的改错,对这个操作系统工作流程的理解确实比原来理解的深了一点,不过要暂时先放下ZigBee了,继续着手STM32,开始学STM32的时候忘记写一写博客了,等把这个工训大赛结束之后一起写吧!