Zigbee学习(三)之Zstack协议栈应用实践

之前Zstack协议栈的工作原理已经讲过了,这次来一发应用举例的帮助大家理解,实验内容很简单,就是按键控制LED小灯闪烁,用的是CC2530的电路板,如果是裸机控制,过程相当简单,只要查询是否有按键按下,然后执行小灯闪烁的操作就可以了,或者用中断也可以。但是如果放到了协议栈当中,过程会复杂些,也就是之前所说的工作原理。

在添加自己的函数或者命令行之前,我们要做的最最重要的事情就是宏定义,比如按键的端口,小灯的端口,用的板子的型号等等。诸如此类的宏定义在嵌入式开发中是家常便饭思密达,因为本人使用的板子是宅基地购买的,所以宏定义大多都是HoneyGeek_V2之类的。在修改代码之前,先要有个大概的思路,那些文件里的代码是要做修改的,比如这次是用到按键和小灯,那么hal_key.c和hal_key.h,hal_board_cfg.h等一些和按键、小灯有关的.c和.h文件都要看一下。我们先看hal_board_cfg.h文件,在LED Configuration下面看到#define HAL_NUM_LEDS这句定义,很明显要改,我用的HoneyGeek_V2的板子只有一个LED所以在后面添加|| defined (HoneyGeek_V2)。

#if defined (FS2530)|| defined (HAL_BOARD_CC2530EB_REV17) && !defined (HAL_PA_LNA) && !defined (HAL_PA_LNA_CC2590)
  #define HAL_NUM_LEDS            3
#elif defined (HAL_BOARD_CC2530EB_REV13) || defined (HAL_PA_LNA) || defined (HAL_PA_LNA_CC2590)|| defined (HoneyGeek_V2)
  #define HAL_NUM_LEDS            1
#else
  #error Unknown Board Indentifier
#endif

再往下看,看到对LED1的定义,也就是LED1的PIN和PORT的一些设置,我们可以这样改:

#ifdef HoneyGeek_V2
/* 1 - Green */
  #define LED1_BV           BV(3)
  #define LED1_SBIT         P0_3
  #define LED1_DDR          P0DIR
  #define LED1_POLARITY     ACTIVE_HIGH

#elif defined (HAL_BOARD_CC2530EB_REV17)

  #define LED1_BV           BV(0)
  #define LED1_SBIT         P1_0
  #define LED1_DDR          P1DIR
  #define LED1_POLARITY     ACTIVE_HIGH

#endif

再往下,看到一块Push Button Configuration的部分,显然是按键配置:

#ifdef HoneyGeek_V2
  #define PUSH1_BV          BV(2)
  #define PUSH1_SBIT        P1_2

#else
/* S1 */
  #define PUSH1_BV          BV(1)
  #define PUSH1_SBIT        P0_1
#endif

再往下看,找到Board Initialization我们也要修改:

#elif defined (HoneyGeek_V2)
#define HAL_BOARD_INIT()                                        
{                                                                
  ......
                                                                 
  HAL_TURN_OFF_LED1();                                           
  LED1_DDR |= LED1_BV;                                           
                                                                 
  /* configure tristates */                                      
  P1INP |= PUSH1_BV;                                            
}

这部分代码就是内部时钟的初始化,后面两行是小灯和按键的设置,要改成相应的端口数值。接下来看hal_key.c文件,对按键再配置一下:

#ifdef HoneyGeek_V2
/* SW_6 is at P1.2 */
#define HAL_KEY_SW_6_PORT   P1
#define HAL_KEY_SW_6_BIT    BV(2)
#define HAL_KEY_SW_6_SEL    P1SEL
#define HAL_KEY_SW_6_DIR    P1DIR

本例子中其他部分无需修改宏定义了。那么,完成前期的宏定义之后,就可以修改后面的命令行,来实现自己要的功能了。在osal_system_start(0函数里是没有任何与按键相关的信息的,这说明在之前的函数里就已经有相关的命令了。首先,看到HAL_BOARD_INIT()函数,GOTO一下发现是Board Initialization,上面已经说过了。然后,InitBoard( OB_COLD )函数点进去看发现HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE, OnBoard_KeyCallback)这个配置函数,明显这个函数是按键配置函数,输入参数一个是知否中断的宏定义值,还有一个是按键回调函数,那么这个回调函数又能实现什么功能呢?来看一下这个函数:

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

关键是看if ( OnBoard_SendKeys( keys, !shift ) != ZSuccess )这句话,因为他又嵌套了一个函数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 );
}

这个函数内部关键就是osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr ),这个函数就不展开了,先看registeredKeysTaskID,这个是注册的按键任务ID号,是多少呢?在应用层已经定义好了,RegisterForKeys( PuppyApp_TaskID )就是这个函数啦!把应用层的任务ID号给了按键的任务ID号,osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr )中有个osal_set_event( destination_task, SYS_EVENT_MSG ),就是设置应用层的任务事件,然后轮询的过程中就会通知应用层有事件要处理了,应用层就调用PuppyApp_ProcessEvent( uint8 task_id, UINT16 events )函数,然后因为是KEY_CHANGE事件,所以执行PuppyApp_HandleKeys()函数,在这个函数里会判断是哪个按键触发的,我的实验用的是按键6,所以:

if ( keys & HAL_KEY_SW_6 )
    {
      PuppyApp_SendTheMessage();
      HalLedBlink(HAL_LED_1,10,50,800);
    }

这个命令就是让小灯闪烁10次。说到这里,大家有没有疑问呢?我按键的时候系统怎么知道我按了?嘿嘿,在这个函数HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE, OnBoard_KeyCallback)中有个osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_POLLING_VALUE)函数,就是再过一段时间后触发HAL_KEY_EVENT事件,这次的事件是给硬件层的,所以我们找到硬件层处理函数Hal_ProcessEvent( uint8 task_id, uint16 events ):

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

这里执行了HalKeyPoll()然后又是一次osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100),也就是说每隔100毫秒系统都会接收到一个HAL_KEY_EVENT要处理,反复的来检查是否有按键被按下,如果有按键被按下,就会去执行回调函数,然后按照上面的顺序来点亮小灯。整个过程就是这样啦!可能说的不太清楚,不过大家推敲一下肯定OK的啦!

你可能感兴趣的:(PuppyZigbee)