Zigbee协议栈内核分析 - 按键分析

Zigbee协议栈内核分析 - 按键分析(轮询)

Jesse

协议栈按键代码分析 - 思维导图(放大可看)

一、综述

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

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

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

2、HalDriverInit();

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

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

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

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

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

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); 来配置按键。

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(); 操作系统,无限循环啦。

首先是先执行:

  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任务进入了准备态。

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

 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 硬件抽象层的事件处理函数,我们进去看看。

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();函数:

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()看看它是怎么处理按键事件的吧。

OnBoard_KeyCallback()
{
   ... ...
   if ( OnBoard_SendKeys( keys, shift ) != ZSuccess )
   ... ...
}

OnBoard_SendKeys() 函数将发送一个消息给 registeredKeysTask 任务。

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()函数 里看看它是怎么发送消息的吧。

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()又进入

  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 处理函数。

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 步,从而达到轮询的功能!

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-12 22:57:22

Zigbee协议栈内核分析 - 按键分析的相关文章

[ZigBee] 16、Zigbee协议栈应用(二)——基于OSAL的无线控制LED闪烁分析(下)

说在前面:上一篇介绍了无线LED闪烁实现的OSAL部分,本篇介绍如何实现无线数据收发及数据处理: 上一篇是用SI跟着流程查看源码,我个人认为以架构的思维去了解代码能让人更清晰 ::ZMain.c程序入口文件 这里chipcon_cstartup.s51是汇编的启动文件,ZMain.c相当于main文件,里面有main函数: 1 int main( void ) 2 { 3 osal_int_disable( INTS_ALL );// Turn off interrupts 关中断 4 HAL_

zigbee协议栈之按键处理流程

zigbee协议栈版本: z-stack home 1.2.1 1.按键的初始化:协议栈的按键初始化主要有两个地方,一个是初始化开始,一个是初始化结束. int main( void ) { .... InitBoard( OB_COLD ); //第一次初始化按键 .... InitBoard( OB_READY ); .... } 2.先看一下InitBoard函数的实现 void InitBoard( uint8 level ) { if ( level == OB_COLD ) { //

Linux tcp被动打开内核源码分析

[我是从2个角度来看,其实所谓2个角度,是发现我分析源码时,分析重复了,写了2个分析报告,所以现在都贴出来.] [如果你是想看看,了解一下内核tcp被动打开时如何实现的话,我觉得还是看看概念就可以了,因为即使看了源码,过一个个礼拜你就忘了,如果是你正在修改协议栈,为不知道流程而发愁,那么希望你能看看源码以及注释,希望你给你帮助.] 概念: tcp被动打开,前提是你listen,这个被动打开的前提.你listen过后,其实创建了一个监听套接字,专门负责监听,不会负责传输数据. 当第一个syn包到达

Linux内核源码分析--内核启动之(5)Image内核启动(rest_init函数)(Linux-3.0 ARMv7)【转】

原文地址:Linux内核源码分析--内核启动之(5)Image内核启动(rest_init函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.chinaunix.net/uid-25909619-id-4938395.html 前面粗略分析start_kernel函数,此函数中基本上是对内存管理和各子系统的数据结构初始化.在内核初始化函数start_kernel执行到最后,就是调用rest_init函数,这个函数的主要使命就是创建并启动内核线

Linux内核源码分析--内核启动之(6)Image内核启动(do_basic_setup函数)(Linux-3.0 ARMv7)【转】

原文地址:Linux内核源码分析--内核启动之(6)Image内核启动(do_basic_setup函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.chinaunix.net/uid-25909619-id-4938396.html 在基本分析完内核启动流程的之后,还有一个比较重要的初始化函数没有分析,那就是do_basic_setup.在内核init线程中调用了do_basic_setup,这个函数也做了很多内核和驱动的初始化工作,详解

Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7) 【转】

原文地址:Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.chinaunix.net/uid-25909619-id-4938390.html 在构架相关的汇编代码运行完之后,程序跳入了构架无关的内核C语言代码:init/main.c中的start_kernel函数,在这个函数中Linux内核开始真正进入初始化阶段, 下面我就顺这代码逐个函数的解释,但是这里并不会过于深入

Linux内核源码分析--内核启动之(4)Image内核启动(setup_arch函数)(Linux-3.0 ARMv7)【转】

原文地址:Linux内核源码分析--内核启动之(4)Image内核启动(setup_arch函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.chinaunix.net/uid-25909619-id-4938393.html 在分析start_kernel函数的时候,其中有构架相关的初始化函数setup_arch. 此函数根据构架而异,对于ARM构架的详细分析如下: void __init setup_arch(char **cmdlin

Linux内核源码分析方法

  一.内核源码之我见 Linux内核代码的庞大令不少人“望而生畏”,也正因为如此,使得人们对Linux的了解仅处于泛泛的层次.如果想透析Linux,深入操作系统的本质,阅读内核源码是最有效的途径.我们都知道,想成为优秀的程序员,需要大量的实践和代码的编写.编程固然重要,但是往往只编程的人很容易把自己局限在自己的知识领域内.如果要扩展自己知识的广度,我们需要多接触其他人编写的代码,尤其是水平比我们更高的人编写的代码.通过这种途径,我们可以跳出自己知识圈的束缚,进入他人的知识圈,了解更多甚至我们一

ARMv8 Linux内核源码分析:__flush_dcache_all()

1.1 /* *  __flush_dcache_all() *  Flush the wholeD-cache. * Corrupted registers: x0-x7, x9-x11 */ ENTRY(__flush_dcache_all) //保证之前的访存指令的顺序 dsb sy //读cache level id register mrs x0, clidr_el1           // read clidr //取bits[26:24](Level of Coherency f