zigbee2007中工程执行流程(初始化部分) 小记

废话什么的就不罗嗦了,直接开说 
首先找到工程的主文件,即整个工程的入口 
ZMain\ZMain.c 
进入主函数 int main() 如下

 1 int main( void )
 2 {
 3   // Turn off interrupts
 4   osal_int_disable( INTS_ALL );
 5 
 6   // Initialization for board related stuff such as LEDs
 7   HAL_BOARD_INIT();
 8 
 9   // Make sure supply voltage is high enough to run
10   zmain_vdd_check();
11 
12   // Initialize stack memory
13   zmain_ram_init();
14 
15   // Initialize board I/O
16   InitBoard( OB_COLD );
17 
18   // Initialze HAL drivers
19   HalDriverInit();
20 
21   // Initialize NV System
22   osal_nv_init( NULL );
23 
24   // Initialize basic NV items
25   zgInit();
26 
27   // Initialize the MAC
28   ZMacInit();
29 
30   // Determine the extended address
31   zmain_ext_addr();
32 
33 #ifndef NONWK
34   // Since the AF isn't a task, call it's initialization routine
35   afInit();
36 #endif
37 
38   // Initialize the operating system
39   osal_init_system();
40 
41   // Allow interrupts
42   osal_int_enable( INTS_ALL );
43 
44   // Final board initialization
45   InitBoard( OB_READY );
46 
47   // Display information about this device
48   zmain_dev_info();
49 
50   /* Display the device info on the LCD */
51 #ifdef LCD_SUPPORTED
52   zmain_lcd_init();
53 #endif
54 
55 #ifdef WDT_IN_PM1
56   /* If WDT is used, this is a good place to enable it. */
57   WatchDogEnable( WDTIMX );
58 #endif
59 
60   osal_start_system(); // No Return from here
61 
62   // Shouldn't get here
63   return ( 0 );
64 }  

整理上面的程序,容易知道,进入main后,首先是各种检测和初始化,之后是 这里我们比较感兴趣的 
osal_init_system(); 以及最后的 osal_start_system()

先说系统初试化 
(附 osalInitTasks()位于 OSAL.c 中)

uint8 osal_init_system( void )
{
  // Initialize the Memory Allocation System
  osal_mem_init();

  // Initialize the message queue
  osal_qHead = NULL;

#if defined( OSAL_TOTAL_MEM )
  osal_msg_cnt = 0;
#endif

  // Initialize the timers
  osalTimerInit();

  // Initialize the Power Management System
  osal_pwrmgr_init();

  // Initialize the system tasks.
  osalInitTasks();

  // Setup efficient search for the first free block of heap.
  osal_mem_kick();

  return ( SUCCESS );
}

照例,先是系统级的初始化,这里我们直接看工程对应的 
osalInitTasks(); (位于 OSAL_SerialApp.c) 
跟踪并打开如下

 1 void osalInitTasks( void )
 2 {
 3   uint8 taskID = 0;
 4 
 5   tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
 6   //osal_mem_alloc()为当前OSAL中的各任务分配存储空间(实际上是一个任务数组),函数返回指向任务缓冲
 7   //区的指针,因此tasksEvents指向该任务数组(任务队列).注意tasksEvents和后面谈到的tasksArr[]里的顺
 8   //序是一一对应的, tasksArr[ ]中的第i个事件处理函数对应于tasksEvents中的第i个任务的事件.
 9 
10   osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));
11   //申请内存空间,大小为 sizeof( uint16 )乘tasksCnt
12   //sizeof( uint16 )是4个字节,即一个任务的长度(同样是uint16定义)
13   //乘以任务数量tasksCnt
14 
15   macTaskInit( taskID++ );
16   nwk_init( taskID++ );
17   Hal_Init( taskID++ );
18 #if defined( MT_TASK )
19   MT_TaskInit( taskID++ );
20 #endif
21   APS_Init( taskID++ );
22 #if defined ( ZIGBEE_FRAGMENTATION )
23   APSF_Init( taskID++ );
24 #endif
25   ZDApp_Init( taskID++ );
26 #if defined ( ZIGBEE_FREQ_AGILITY ) ||  defined(ZIGBEE_PANID_CONFLICT )
27   ZDNwkMgr_Init( taskID++ );
28 #endif
29   SerialApp_Init( taskID );
30 }

在这部分中 
1、主要任务就是对各个系统内的层次进行初始化,其中核心的 SerialApp_Init( taskID ) 处在最后 
2、注意此处的taskID,表示任务的优先级,在 ZDProfile.c 中有其定义,如下

1 typedef struct
2 {
3   void *next;
4   uint8 taskID;
5   uint16 clusterID;
6 } ZDO_MsgCB_t;

注意结构体 ZDO_MsgCB_t 后面还会提到,可见这个结构体中包含了 任务ID 和 簇ID

3、现在回头看第一句

1   uint8 taskID = 0;
2   tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);//此步骤的关键作用在于
3   osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));
    • 首先令id=0
    • (uint16 *) 是强制转换,不说,关键在于后面 
      osal_mem_alloc( sizeof( uint16 ) * tasksCnt),来看函数的原文描述,如下
    • 1    @fn      osal_mem_alloc
      2       @brief   Implementation of the allocator functionality.
      3       @param   size - number of bytes to allocate from the heap.
      4       @return  void * - pointer to the heap allocation; NULL if error or failure.

      也就是说,该函数负责 执行分配功能 ,osal_mem_alloc( uint16 size ),其中size就是要分配的字节大小,sizeof( uint16 )是4个字节,即一个任务的长度(同样是uint16定义),乘以任务数量tasksCnt,即全部内存空间。 
      这里看一下 tsaksCnt,跟踪可知

            const uint8 tasksCnt = sizeof( tasksArr ) / sizeof( tasksArr[0] );
              uint16 *tasksEvents;
      sizeof 表示返回一个对象或者类型所占的内存字节数。
            也就是,通过这样  tasksCnt 就可以得到总共的任务个数,tasksArr是任务列表,稍后说。
      

      接上面说,osal_mem_alloc 的返回 :如果成功执行则返回指向一个缓存的指针,一个无类型指针指向被分配的新的缓存区。

      • 之后将上面返回的空指针(指向被分配的缓存区)赋给tasksEvents
      • 之后第三句 osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt))
         1  * @fn      osal_memset
         2  * @brief   Set memory buffer to value.
         3  * 
         4  * @param   dest - pointer to buffer
         5  * @param   value - what to set each uint8 of the message
         6  * @param   size - how big
         7 
         8  * @return  value of next widget, 0 if no widget found
         9 
        10  void *osal_memset( void *dest, uint8 value, int len )
        11   {
        12       return memset( dest, value, len );
        13   }

        该函数的作用是给内存设定指定地址、指定内容、指定大小的,话说怎么感觉那么像三归 哈~ 
        osal_memset()把开辟的内存全部设置为0;sizeof( uint16 )是4个字节,即一个任务的长度(同样是uint16定义),乘以任务数量tasksCnt,即全部内存空间

        以上,总结一下第3点: 
        首先令taskID=0,期间通过 tasksCnt的定义 顺带计算osal中总的任务数量 (任务队列数组的总字节数 除以 每个任务的字节数 unit16都是4) 
        然后tasksEvents = (uint16 )osal_mem_alloc( sizeof( uint16 ) tasksCnt)进行内存空间的分配,成功后将结果 也就是指向 heap allocation 的空指针 赋给 tasksEvents 
        然后通过 osal_memset()把开辟的内存(tasksEvents指向的区域)全部设置对应的值value为0,大小就是 
        sizeof( uint16 )是4个字节,即一个任务的长度(同样是uint16定义),乘以任务数量tasksCnt, 即全部内存空间。

        =========================================================================================
        4、下面开始说 osalInitTasks( void ) 中的 SerialApp_Init( taskID ),跟踪打开如下

         1 void SerialApp_Init( uint8 task_id )
         2 {
         3         halUARTCfg_t uartConfig;
         4 
         5         P0SEL &= 0xDf;                  //设置P0.5口为普通IO
         6         P0DIR |= 0x20;                  //设置P0.5为输出
         7         LAMP_PIN = 1;                   //高电平继电器断开;低电平继电器吸合
         8         P0SEL &= ~0x40;                 //设置P0.6为普通IO口
         9         P0DIR &= ~0x40;                 //设置P0.6为输入口
        10         P0SEL &= 0x7f;                  //P0_7配置成通用io
        11 
        12     SerialApp_TaskID = task_id;
        13     //SerialApp_RxSeq = 0xC3;
        14 
        15     afRegister( (endPointDesc_t *)&SerialApp_epDesc );
        16 
        17     RegisterForKeys( task_id );
        18 
        19     uartConfig.configured           = TRUE;              // 2x30 don't care - see uart driver.
        20     uartConfig.baudRate             = SERIAL_APP_BAUD;
        21     uartConfig.flowControl          = FALSE;
        22     uartConfig.flowControlThreshold = SERIAL_APP_THRESH; // 2x30 don't care - see uart driver.
        23     uartConfig.rx.maxBufSize        = SERIAL_APP_RX_SZ;  // 2x30 don't care - see uart driver.
        24     uartConfig.tx.maxBufSize        = SERIAL_APP_TX_SZ;  // 2x30 don't care - see uart driver.
        25     uartConfig.idleTimeout          = SERIAL_APP_IDLE;   // 2x30 don't care - see uart driver.
        26     uartConfig.intEnable            = TRUE;              // 2x30 don't care - see uart driver.
        27     uartConfig.callBackFunc         = SerialApp_CallBack;
        28     HalUARTOpen (UART0, &uartConfig);
        29 
        30 #if defined ( LCD_SUPPORTED )
        31     HalLcdWriteString( "SerialApp", HAL_LCD_LINE_2 );
        32 #endif
        33     //HalUARTWrite(UART0, "Init", 4);
        34     //ZDO_RegisterForZDOMsg( SerialApp_TaskID, End_Device_Bind_rsp );
        35     //ZDO_RegisterForZDOMsg( SerialApp_TaskID, Match_Desc_rsp );
        36 }

        此为 串口app任务 的初始化 ,下面进行相关语句分析,没提到的不另做说明 
        4.1 afRegister( (endPointDesc_t *)&SerialApp_epDesc ); 
        作用:Register an Application’s EndPoint description ,注册一个应用的端点描述符

         1 * @fn      afRegister
         2 
         3 * @brief   Register an Application's EndPoint description.
         4 
         5 * @param   epDesc - pointer to the Application's endpoint descriptor.
         6 
         7 * @return  afStatus_SUCCESS - Registered
         8 *          afStatus_MEM_FAIL - not enough memory to add descriptor
         9 *          afStatus_INVALID_PARAMETER - duplicate endpoint
        10 
        11 afStatus_t afRegister( endPointDesc_t *epDesc )
        12 {
        13   epList_t *ep;
        14 
        15   // Look for duplicate endpoint
        16   if ( afFindEndPointDescList( epDesc->endPoint ) )
        17     return ( afStatus_INVALID_PARAMETER );
        18 
        19   ep = afRegisterExtended( epDesc, NULL );
        20 
        21   return ((ep == NULL) ? afStatus_MEM_FAIL : afStatus_SUCCESS);
        22 }

        函数类型为 afStatus_t , 跟踪可知 typedef ZStatus_t afStatus_t;又 typedef Status_t ZStatus_t;又 typedef uint8 Status_t; 
        函数参数为 epDesc - pointer to the Application’s endpoint descriptor,即一个 endPointDesc_t 类型的指针

        在 afRegister( (endPointDesc_t *)&SerialApp_epDesc ) 中 可见,该注册函数指向了 SerialApp_epDesc 对应的指针 
        下面具体看一下这个指针类型

        1 typedef struct
        2 {
        3   byte endPoint;
        4   byte *task_id;  // Pointer to location of the Application task ID.
        5   SimpleDescriptionFormat_t *simpleDesc;
        6   afNetworkLatencyReq_t latencyReq;
        7 } endPointDesc_t;

        这个也就是 端点描述符的定义,这里展看结构体中的 SimpleDescriptionFormat_t 看一下

         1 typedef struct
         2 {                                  //关于此冒号的用法,见
         3   byte          EndPoint;
         4   uint16        AppProfId;
         5   uint16        AppDeviceId;       //app设备ID
         6   byte          AppDevVer:4;       //app版本号,冒号4,此处冒号用法为 “定义变量:占位符”表明定义的变量的需要位数
         7   byte          Reserved:4;        //保留,具体是什么可以自己看着办,比如下面的实例中用来表示 程序的版本标识           
         8                                    // AF_V1_SUPPORT uses for AppFlags:4.
         9   byte          AppNumInClusters;
        10   cId_t         *pAppInClusterList;
        11   byte          AppNumOutClusters;
        12   cId_t         *pAppOutClusterList;
        13 } SimpleDescriptionFormat_t;

        附:变量声明加冒号 C语言变量声明加冒号的用法(占位符) 
        上面这两个定义,均在 AF.h 中 
        下面 对照程序中的 SerialApp_epDesc 看一下,下面的函数定位于 SerialApp.c 中

        1 const endPointDesc_t SerialApp_epDesc =
        2 {
        3     SERIALAPP_ENDPOINT,
        4     &SerialApp_TaskID,
        5     (SimpleDescriptionFormat_t *)&SerialApp_SimpleDesc,
        6     noLatencyReqs
        7 };

        又其中 SerialApp_SimpleDesc 可得

         1 const SimpleDescriptionFormat_t SerialApp_SimpleDesc =
         2 {
         3     SERIALAPP_ENDPOINT,              //  int   Endpoint;
         4     SERIALAPP_PROFID,                //  uint16 AppProfId[2];
         5     SERIALAPP_DEVICEID,              //  uint16 AppDeviceId[2];
         6     SERIALAPP_DEVICE_VERSION,        //  int   AppDevVer:4;
         7     SERIALAPP_FLAGS,                 //  int   AppFlags:4;
         8     SERIALAPP_MAX_CLUSTERS,          //  byte  AppNumInClusters;
         9     (cId_t *)SerialApp_ClusterList,  //  byte *pAppInClusterList;
        10     SERIALAPP_MAX_CLUSTERS,          //  byte  AppNumOutClusters;
        11     (cId_t *)SerialApp_ClusterList   //  byte *pAppOutClusterList;
        12 };

        具体来说,看一下这几个参数的情况,在 SerialApp.h 中

        #define SERIALAPP_ENDPOINT               11
        #define SERIALAPP_PROFID                 0x0F05
        #define SERIALAPP_DEVICEID               0x0001
        #define SERIALAPP_DEVICE_VERSION         0
        #define SERIALAPP_FLAGS                  0

        `故可知,相关参数,在头文件中定义

        4.2 下面看一下 RegisterForKeys( task_id ) 
        它完成任务对按键事件的注册工作,这里如果不注册的话在后来的程序里是不会产生KEY_CHANG这个事件。

        byte RegisterForKeys( byte task_id ) 

        // Allow only the first task 
        if ( registeredKeysTaskID == NO_TASK_ID ) 

        registeredKeysTaskID = task_id;//注意这句话,就是给任务注册。和任务的ID号联系起来 
        return ( true ); 

        else 
        return ( false ); 
        }

        这样就把任务和事件联系在了一起

posted @ 2015-04-18 22:25  云何  阅读(910)  评论(0编辑  收藏  举报