zigbee按键分析

一、综述

上图从协议栈的 main() 函数开始分析,罗列出了 main() 函数里调用的函数。接下来我们将会对函数一个一个的分析,有关于 key 的函数我将会用黄色方框表示。

二、协议栈代码分析(按照上图逐步分析)

********************************************** 轮询 **********************************************
1、osal_int_disable( INTS_ALL ); //关闭总中断

2、HalDriverInit();

调用 HalKeyInit(); //按键初始化函数


[cpp] view plain copy
HalKeyInit()  
{  
  ......  
  /*Initialize callback function */  
 pHalKeyProcessFunction  = NULL;        //初始化按键回调函数为NULL  

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

3、osal_init_system(); //系统初始化函数

osal_init_system(); 里调用 osalInitTasks(); 初始化任务;

[cpp] view plain copy
osalInitTasks()  
{  
  ......  
  SampleApp_Init( taskID );            //用户在 应用层 自定义的任务初始化。  
}  
SampleApp_Init( taskID )  
{  
  ......  
  RegisterForKeys( SampleApp_TaskID );  
  ......  
}  

在自定义的任务初始化里面调用了 RegisterForKeys(SampleApp_TaskID ); ,里面的 registeredKeysTaskID = task_id; ,这个很关键,它将用户自定义的 SampleApp_Task 任务注册为按键任务。

4、osal_int_enable( INTS_ALL ); //使能了总中断

5、InitBoard( OB_READY );

调用了 HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE,OnBoard_KeyCallback); 来配置按键。

[cpp] view plain copy
HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE,OnBoard_KeyCallback)
{
… …
(void)osal_set_event(Hal_TaskID, HAL_KEY_EVENT); //用 tasksEvents[task_id] |= event_flag; 来通知Hal_Task任务有消息,使Hal_Task任务进入就绪态
… …
pHalKeyProcessFunction = cback; //将 OnBoard_KeyCallback 函数赋值给按键回调函数指针
}

6、最后就是进入我们的 osal_start_system(); 操作系统,无限循环啦。

首先是先执行:

[cpp] view plain copy
do{  
 if (tasksEvents[idx])      // Task is highest priority that is ready.  
  {  
   break;  
  }  
}while (++idx < tasksCnt);  

来查询是否有任务进入了就绪状态。我们回去看看第 5 步,在第 5 步的时候有调用到 osal_set_event(Hal_TaskID,HAL_KEY_EVENT); 设置了一个消息给 Hal_Task 任务使Hal_Task任务进入了就绪态。

所以接下来协议栈要执行:


[cpp] view plain copy
if(idx < tasksCnt)       //如果任务是有登记的  
 {  
  uint16 events;  
  halIntState_t intState;  
  HAL_ENTER_CRITICAL_SECTION(intState);  
  events = tasksEvents[idx];  
  tasksEvents[idx] = 0;  // Clearthe Events for this task.  
  HAL_EXIT_CRITICAL_SECTION(intState);  
  activeTaskID = idx;  
  events = (tasksArr[idx])( idx, events );       //这一句调用了按键的处理函数  
  activeTaskID = TASK_NO_TASK;  
  HAL_ENTER_CRITICAL_SECTION(intState);  
  tasksEvents[idx] |= events;  //Add back unprocessed events to the current task.  
  HAL_EXIT_CRITICAL_SECTION(intState);  
 }  
events = (tasksArr[idx])( idx, events ); 这一句调用了Hal_Task的处理函数。让我们进入 tasksArr[idx] 看看,有了,Hal_Task任务对应的Hal_ProcessEvent 硬件抽象层的事件处理函数,我们进去看看。

[cpp] view plain copy
uint16 Hal_ProcessEvent( uint8 task_id,uint16 events )  
{  
   ... ...  
   #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  
   ... ...  
}  

我们先看一下 HalKeyPoll();函数:

[cpp] view plain copy
HalKeyPoll()  
{  
  ......  
  if(newKeys && pHalKeyProcessFunction)  
  {  
   (pHalKeyProcessFunction)(newKeys, HAL_KEY_STATE_NORMAL);  //调用了按键处理回调函数  
  }  
  ......  
}  

按键轮询函数里调用了 (pHalKeyProcessFunction)(newKeys,HAL_KEY_STATE_NORMAL); 按键处理回调函数来处理按键事件,那么这个函数指针到底指向哪呢?让我们回去看看上面,有两个地方出现了回调函数指针的赋值。

(1)在第 2 步的时候 pHalKeyProcessFunction = NULL; 初始化按键回调函数指针为NULL;

(2)在第 5 步的时候调用了 HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE, OnBoard_KeyCallback); 函数将 OnBoard_KeyCallback函数赋值给按键回调函数指针。

所以现在我们就去找 OnBoard_KeyCallback()看看它是怎么处理按键事件的吧。

[cpp] view plain copy
OnBoard_KeyCallback()  
{  
   ... ...  
   if ( OnBoard_SendKeys( keys, shift ) != ZSuccess )  
   ... ...  
}  
OnBoard_SendKeys() 函数将发送一个消息给 registeredKeysTask 任务。

[cpp] view plain copy
OnBoard_SendKeys()  
{  
  ......  
  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 );  
  }  
  ......  
}  

看上面的代码,协议栈首先将消息打包,然后通过 osal_msg_send() 函数将消息发送给 registeredKeysTask 任务(也就是用户自定义的SampleApp_Task任务)。

我们进去 osal_msg_send()函数 里看看它是怎么发送消息的吧。

[cpp] view plain copy
osal_msg_send()  
{  
  ......  
  //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 );  
  ......  
}  

看看上面的代码,我把最重要的两句列了出来:

首先是osal_msg_enqueue( &osal_qHead, msg_ptr ); ,将消息放入消息队列中;

最后果然还是要用到我们的 osal_set_event( destination_task, SYS_EVENT_MSG ); 函数来通知任务有消息发过来了!那这次是通知那个任务呢,让我们回去看看destination_task指的是什么任务吧。原来是第 6 步的按键回调函数里一直往里面看可以看到osal_msg_send(registeredKeysTaskID, (uint8 *)msgPtr );,所以通知的是registeredKeysTask任务,也就是用户自定义的SampleApp_Task任务。

然后 osal_run_system()又进入

[cpp] view plain copy
do{  
 if (tasksEvents[idx])  // Task ishighest priority that is ready.  
  {  
   break;  
  }  
}while (++idx < tasksCnt);  

此时,托osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr);的福,我们的SampleApp_Task终于进入了就绪态。

那么接下来的 events = (tasksArr[idx])( idx, events );,我们进入的是我们用户自定义的 SampleApp_ProcessEvent 处理函数。

[cpp] view plain copy
SampleApp_ProcessEvent()  
{  
   if ( events & SYS_EVENT_MSG )  
  {  
    MSGpkt = (afIncomingMSGPacket_t*)osal_msg_receive( SampleApp_TaskID );  
    while ( MSGpkt )  
    {  
      switch ( MSGpkt->hdr.event )  
      {  
        // Received when a key is pressed  
        case KEY_CHANGE:  
          SampleApp_HandleKeys( ((keyChange_t*)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys );  
          break;  
        … …  
}  

然后就进入了SampleApp_HandleKeys按键处理函数了!

就这样,按键从协议栈系统一开始,到执行了一次用户自定义的按键处理函数的过程就全部结束啦!
然后是不是按键就这样结束了呢,那怎么叫轮询呢?!!!!

让我们看回到第 6 步的 Hal_ProcessEvent处理函数里面吧,它除了调用了按键轮询函数外,还调用了osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100);,这个函数的作用是 100ms 后再次让Hal_TaskID任务进入准备态,此时就会无限循环的重复第 6 步,从而达到轮询的功能!

********************************************** 中断 **********************************************
上面分析的是按键的轮询模式,那按键的中断模式协议栈又是怎么执行的呢,接下来我们一起来看一下吧~

在第 5 步的时候,也就是第 2 次板载初始化的时候,该函数 InitBoard( OB_READY ); 会调用 HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE, OnBoard_KeyCallback);,协议栈原设置的参数时 HAL_KEY_INTERRUPT_DISABLE,如果要使用按键的中断模式,我们要将 HAL_KEY_INTERRUPT_DISABLE 改为 HAL_KEY_INTERRUPT_ENABLE,接下来我们就进入 HalKeyConfig() 里面看看吧。

[cpp] view plain copy
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;  
}  

如果参数为 HAL_KEY_INTERRUPT_ENABLE,将执行 if 里面的语句初始化按键中断模式。也就是没有执行 osal_set_event(Hal_TaskID, HAL_KEY_EVENT); 了,也就不会进行按键轮询了,那么当我们按下按键的时候,协议栈又是怎么进行终端处理的呢?

接下来我们来看看 Hal_key.c 里面的 HAL_ISR_FUNCTION() 函数。

[cpp] view plain copy
HAL_ISR_FUNCTION( halKeyPort0Isr, P0INT_VECTOR )  
{  
  HAL_ENTER_ISR();  

  if (HAL_KEY_SW_6_PXIFG & HAL_KEY_SW_6_BIT)  
  {  
    halProcessKeyInterrupt();  
  }  

  /* 
    Clear the CPU interrupt flag for Port_0 
    PxIFG has to be cleared before PxIF 
  */  
  HAL_KEY_SW_6_PXIFG = 0;  
  HAL_KEY_CPU_PORT_0_IF = 0;  

  CLEAR_SLEEP_MODE();  
  HAL_EXIT_ISR();  
}  

当有按键按下的时候,则会进入这个外部中断服务函数,调用 halProcessKeyInterrupt(); 来处理按键事件。

[cpp] view plain copy
void halProcessKeyInterrupt(void)  
{  
    ... ...  
      if (valid)  
         {  
              osal_start_timerEx(Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_DEBOUNCE_VALUE);  
         }  
    ... ...  
}  

在这个按键中断处理函数里面我们又见到了非常熟悉的 osal_start_timerEx() 函数了,这个函数在这起什么作用呢?
作用有两个:
1、经过 25ms 之后才发送事件是为了延时去抖动。
2、发送一条按键事件消息给 Hal_Task 任务。
既然 Hal_Task 任务收到了按键事件消息,那接下来协议栈处理的流程也就和按键轮询后面的执行流程一样了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值