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

本文详细解读了Zigbee设备启动时的主函数逻辑,包括主函数内部的各种检测与初始化操作,重点分析了osal_init_system()与osal_start_system()函数的作用,以及任务初始化过程中的细节,如任务ID的分配与任务队列的创建。

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

int main( void )
{
  // Turn off interrupts
  osal_int_disable( INTS_ALL );

  // Initialization for board related stuff such as LEDs
  HAL_BOARD_INIT();

  // Make sure supply voltage is high enough to run
  zmain_vdd_check();

  // Initialize stack memory
  zmain_ram_init();

  // Initialize board I/O
  InitBoard( OB_COLD );

  // Initialze HAL drivers
  HalDriverInit();

  // Initialize NV System
  osal_nv_init( NULL );

  // Initialize basic NV items
  zgInit();

  // Initialize the MAC
  ZMacInit();

  // Determine the extended address
  zmain_ext_addr();

#ifndef NONWK
  // Since the AF isn't a task, call it's initialization routine
  afInit();
#endif

  // Initialize the operating system
  osal_init_system();

  // Allow interrupts
  osal_int_enable( INTS_ALL );

  // Final board initialization
  InitBoard( OB_READY );

  // Display information about this device
  zmain_dev_info();

  /* Display the device info on the LCD */
#ifdef LCD_SUPPORTED
  zmain_lcd_init();
#endif

#ifdef WDT_IN_PM1
  /* If WDT is used, this is a good place to enable it. */
  WatchDogEnable( WDTIMX );
#endif

  osal_start_system(); // No Return from here

  // Shouldn't get here
  return ( 0 );
}  

整理上面的程序,容易知道,进入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)
跟踪并打开如下

void osalInitTasks( void )
{
  uint8 taskID = 0;

  tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
  //osal_mem_alloc()为当前OSAL中的各任务分配存储空间(实际上是一个任务数组),函数返回指向任务缓冲
  //区的指针,因此tasksEvents指向该任务数组(任务队列).注意tasksEvents和后面谈到的tasksArr[]里的顺
  //序是一一对应的, tasksArr[ ]中的第i个事件处理函数对应于tasksEvents中的第i个任务的事件.

  osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));
  //申请内存空间,大小为 sizeof( uint16 )乘tasksCnt
  //sizeof( uint16 )是4个字节,即一个任务的长度(同样是uint16定义)
  //乘以任务数量tasksCnt

  macTaskInit( taskID++ );
  nwk_init( taskID++ );
  Hal_Init( taskID++ );
#if defined( MT_TASK )
  MT_TaskInit( taskID++ );
#endif
  APS_Init( taskID++ );
#if defined ( ZIGBEE_FRAGMENTATION )
  APSF_Init( taskID++ );
#endif
  ZDApp_Init( taskID++ );
#if defined ( ZIGBEE_FREQ_AGILITY ) ||  defined(ZIGBEE_PANID_CONFLICT )
  ZDNwkMgr_Init( taskID++ );
#endif
  SerialApp_Init( taskID );
}

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

typedef struct
{
  void *next;
  uint8 taskID;
  uint16 clusterID;
} ZDO_MsgCB_t;

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

3、现在回头看第一句

  uint8 taskID = 0;
  tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);//此步骤的关键作用在于
  osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));
  • 首先令id=0
  • (uint16 *) 是强制转换,不说,关键在于后面
    osal_mem_alloc( sizeof( uint16 ) * tasksCnt),来看函数的原文描述,如下
      @fn      osal_mem_alloc
      @brief   Implementation of the allocator functionality.
      @param   size - number of bytes to allocate from the heap.
      @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))
 * @fn      osal_memset
 * @brief   Set memory buffer to value.
 * 
 * @param   dest - pointer to buffer
 * @param   value - what to set each uint8 of the message
 * @param   size - how big

 * @return  value of next widget, 0 if no widget found

 void *osal_memset( void *dest, uint8 value, int len )
  {
      return memset( dest, value, len );
  }

该函数的作用是给内存设定指定地址、指定内容、指定大小的,话说怎么感觉那么像三归 哈~
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 ),跟踪打开如下

void SerialApp_Init( uint8 task_id )
{
        halUARTCfg_t uartConfig;

        P0SEL &= 0xDf;                  //设置P0.5口为普通IO
        P0DIR |= 0x20;                  //设置P0.5为输出
        LAMP_PIN = 1;                   //高电平继电器断开;低电平继电器吸合
        P0SEL &= ~0x40;                 //设置P0.6为普通IO口
        P0DIR &= ~0x40;                 //设置P0.6为输入口
        P0SEL &= 0x7f;                  //P0_7配置成通用io

    SerialApp_TaskID = task_id;
    //SerialApp_RxSeq = 0xC3;

    afRegister( (endPointDesc_t *)&SerialApp_epDesc );

    RegisterForKeys( task_id );

    uartConfig.configured           = TRUE;              // 2x30 don't care - see uart driver.
    uartConfig.baudRate             = SERIAL_APP_BAUD;
    uartConfig.flowControl          = FALSE;
    uartConfig.flowControlThreshold = SERIAL_APP_THRESH; // 2x30 don't care - see uart driver.
    uartConfig.rx.maxBufSize        = SERIAL_APP_RX_SZ;  // 2x30 don't care - see uart driver.
    uartConfig.tx.maxBufSize        = SERIAL_APP_TX_SZ;  // 2x30 don't care - see uart driver.
    uartConfig.idleTimeout          = SERIAL_APP_IDLE;   // 2x30 don't care - see uart driver.
    uartConfig.intEnable            = TRUE;              // 2x30 don't care - see uart driver.
    uartConfig.callBackFunc         = SerialApp_CallBack;
    HalUARTOpen (UART0, &uartConfig);

#if defined ( LCD_SUPPORTED )
    HalLcdWriteString( "SerialApp", HAL_LCD_LINE_2 );
#endif
    //HalUARTWrite(UART0, "Init", 4);
    //ZDO_RegisterForZDOMsg( SerialApp_TaskID, End_Device_Bind_rsp );
    //ZDO_RegisterForZDOMsg( SerialApp_TaskID, Match_Desc_rsp );
}

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

* @fn      afRegister

* @brief   Register an Application's EndPoint description.

* @param   epDesc - pointer to the Application's endpoint descriptor.

* @return  afStatus_SUCCESS - Registered
*          afStatus_MEM_FAIL - not enough memory to add descriptor
*          afStatus_INVALID_PARAMETER - duplicate endpoint

afStatus_t afRegister( endPointDesc_t *epDesc )
{
  epList_t *ep;

  // Look for duplicate endpoint
  if ( afFindEndPointDescList( epDesc->endPoint ) )
    return ( afStatus_INVALID_PARAMETER );

  ep = afRegisterExtended( epDesc, NULL );

  return ((ep == NULL) ? afStatus_MEM_FAIL : afStatus_SUCCESS);
}

函数类型为 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 对应的指针
下面具体看一下这个指针类型

typedef struct
{
  byte endPoint;
  byte *task_id;  // Pointer to location of the Application task ID.
  SimpleDescriptionFormat_t *simpleDesc;
  afNetworkLatencyReq_t latencyReq;
} endPointDesc_t;

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

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

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

const endPointDesc_t SerialApp_epDesc =
{
    SERIALAPP_ENDPOINT,
    &SerialApp_TaskID,
    (SimpleDescriptionFormat_t *)&SerialApp_SimpleDesc,
    noLatencyReqs
};

又其中 SerialApp_SimpleDesc 可得

const SimpleDescriptionFormat_t SerialApp_SimpleDesc =
{
    SERIALAPP_ENDPOINT,              //  int   Endpoint;
    SERIALAPP_PROFID,                //  uint16 AppProfId[2];
    SERIALAPP_DEVICEID,              //  uint16 AppDeviceId[2];
    SERIALAPP_DEVICE_VERSION,        //  int   AppDevVer:4;
    SERIALAPP_FLAGS,                 //  int   AppFlags:4;
    SERIALAPP_MAX_CLUSTERS,          //  byte  AppNumInClusters;
    (cId_t *)SerialApp_ClusterList,  //  byte *pAppInClusterList;
    SERIALAPP_MAX_CLUSTERS,          //  byte  AppNumOutClusters;
    (cId_t *)SerialApp_ClusterList   //  byte *pAppOutClusterList;
};

具体来说,看一下这几个参数的情况,在 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 );
}

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值