看了一段时间的z-stack协议栈了,想自己写写对协议栈的理解,这几天花时间整理一下看的东西,顺带着自己梳理梳理。对SampleApp的例子出分析,从main函数开始,主要想的是按照代码运行的流程做出分析,中间穿插一些需要知道和了解的东西(啰啰嗦嗦),还望大家指正。
1、main函数分析简单介绍
int main( void )
{
// Turn off interrupts
osal_int_disable( INTS_ALL );//关闭所有中断
// Initialization for board related stuff such as LEDs
HAL_BOARD_INIT();//初始化时钟和LED
// Make sure supply voltage is high enough to run
zmain_vdd_check();//检测供电电压
// Initialize board I/O
InitBoard( OB_COLD );
// Initialze HAL drivers(介绍见1.1)
```c
HalDriverInit();/*在本例程中用到了按键,所以在该函数的初始化*/
/*包括AD的初始化HalAdcInit()和按键的初始化HalKeyInit()*/
```
// Initialize NV System
osal_nv_init( NULL );
// Initialize the MAC
ZMacInit();
// Determine the extended address
zmain_ext_addr();
#if defined ZCL_KEY_ESTABLISH
// Initialize the Certicom certificate information.
zmain_cert_init();
#endif
// Initialize basic NV items
zgInit();
#ifndef NONWK
// Since the AF isn't a task, call it's initialization routine
afInit();
#endif
// Initialize the operating system(见介绍1.2)
osal_init_system();/*在这个函数里包括对OSAL系统的初始化,*/
/*可以先理解osalInitTasks()函数,给每个Task分配任务ID,以及初始化任务*/
// Allow interrupts
osal_int_enable( INTS_ALL );
// Final board initialization(见介绍1.3)
InitBoard( OB_READY );/*在这个函数里调用*/
/*了HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE, OnBoard_KeyCallback)函数,*/
/* 是回调函数,下面会讲到*/
// 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
return 0; // Shouldn't get here.
} // main()
1.1、HalDriverInit()中的函数HalAdcInit()和HalKeyInit()
HalAdcInit()函数
void HalAdcInit (void)
{
#if (HAL_ADC == TRUE)
adcRef = HAL_ADC_REF_VOLT;//adcRef赋值为0x80
#endif
}
HalKeyInit()函数,在这个函数做的工作是对两个按键端口P0.1、P2.0进行初始化的工作
void HalKeyInit( void )
{
/* Initialize previous key to 0 */
halKeySavedKeys = 0;
HAL_KEY_SW_6_SEL &= ~(HAL_KEY_SW_6_BIT); /* Set pin function to GPIO *///P0.1
HAL_KEY_SW_6_DIR &= ~(HAL_KEY_SW_6_BIT); /* Set pin direction to Input */
HAL_KEY_JOY_MOVE_SEL &= ~(HAL_KEY_JOY_MOVE_BIT); /* Set pin function to GPIO *///P2.0
HAL_KEY_JOY_MOVE_DIR &= ~(HAL_KEY_JOY_MOVE_BIT); /* Set pin direction to Input */
/* Initialize callback function */
pHalKeyProcessFunction = NULL;//没有回调函数
/* Start with key is not configured */
HalKeyConfigured = FALSE;//按键配置为FALSE
}
1.2、osal_init_system()函数
在这里可以看到,osal_init_system()函数的作用就是初始化系统的各个任务,以及给每个任务分配任务ID
void osalInitTasks( void )
{
uint8 taskID = 0;
tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
osal_memset( tasksEvents, 0, (sizeof( 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
SampleApp_Init( taskID );
}
可以看到每一句都是taskID++,也就是给每个任务的ID都每次加一。关于tasksEvents 我们后边再做介绍。同时需要了解SampleApp_Init()函数。
void SampleApp_Init( uint8 task_id )
{
SampleApp_TaskID = task_id; //osal分配的任务ID,这里为6,随着用户添加任务的增多而改变
SampleApp_NwkState = DEV_INIT;//设备状态设定为ZDO层中定义的初始化状态(无连接)
/* 初始化应用设备的网络类型,设备类型的改变都要产生一个事件—ZDO_STATE_CHANGE, 从字面理解为ZDO状态发生了改变。所以在设备初始化的时候一定要把它初始化为什么 状态都没有。那么它就要去检测整个环境,看是否能重新建立或者加入存在的网络。 但是有一种情况例外,就是当NV_RESTORE被设置的候(NV_RESTORE是把信息保存在非 易失存储器中),那么当设备断电或者某种意外重启时,由于网络状态存储 在非易失存储器中,那么此时就只需要恢复其网络状态,而不需要重新建立或者加入 网络了.这里需要设置NV_RESTORE宏定义。 */
SampleApp_TransID = 0; //消息发送ID(多消息时有顺序之分)
// Device hardware initialization can be added here or in main() (Zmain.c).
// If the hardware is application specific - add it here.
// If the hardware is other parts of the device add it in main().
#if defined ( BUILD_ALL_DEVICES )
// The "Demo" target is setup to have BUILD_ALL_DEVICES and HOLD_AUTO_START
// We are looking at a jumper (defined in SampleAppHw.c) to be jumpered
// together - if they are - we will start up a coordinator. Otherwise,
// the device will start as a router.
if ( readCoordinatorJumper() )
zgDeviceLogicalType = ZG_DEVICETYPE_COORDINATOR;
else
zgDeviceLogicalType = ZG_DEVICETYPE_ROUTER;
#endif // BUILD_ALL_DEVICES
#if defined ( HOLD_AUTO_START )
// HOLD_AUTO_START is a compile option that will surpress ZDApp
// from starting the device and wait for the application to
// start the device.
/* 该段的意思是,如果设置了HOLD_AUTO_START宏定义,将会在启动芯片的时候 会暂停启动流程,只有外部触发以后才会启动芯片。其实就是需要一个按钮触 发它的启动流程。 */
ZDOInitDevice(0);
#endif
/* 设置发送数据的方式和目的地址寻址模式*/
// Setup for the periodic message's destination address
// Broadcast to everyone
/*广播到所有设备*/
SampleApp_Periodic_DstAddr.addrMode = (afAddrMode_t)AddrBroadcast;
SampleApp_Periodic_DstAddr.endPoint = SAMPLEAPP_ENDPOINT;
SampleApp_Periodic_DstAddr.addr.shortAddr = 0xFFFF;//指定目的网络地址为广播地址
//闪烁消息:发送到组
// Setup for the flash command's destination address - Group 1
SampleApp_Flash_DstAddr.addrMode = (afAddrMode_t)afAddrGroup; //(组寻址)
SampleApp_Flash_DstAddr.endPoint = SAMPLEAPP_ENDPOINT;
SampleApp_Flash_DstAddr.addr.shortAddr = SAMPLEAPP_FLASH_GROUP;
/*定义本设备用来通信的APS层端点描述符*/
// Fill out the endpoint description.
SampleApp_epDesc.endPoint = SAMPLEAPP_ENDPOINT;
SampleApp_epDesc.task_id = &SampleApp_TaskID;
SampleApp_epDesc.simpleDesc
= (SimpleDescriptionFormat_t *)&SampleApp_SimpleDesc;
SampleApp_epDesc.latencyReq = noLatencyReqs;
// Register the endpoint description with the AF
/*向AF层登记EP描述符*/
/*登记endpoint description 到AF,要对该应用进行初始化并在AF进行登记, 告诉应用层有这么一个EP已经开通可以使用,那么下层要是有关于该应用的 信息或者应用要对下层做哪些操作,就自动得到下层的配合。*/
afRegister( &SampleApp_epDesc );
// Register for all key events - This app will handle all key events
RegisterForKeys( SampleApp_TaskID );
// By default, all devices start out in Group 1
/*设定一个新的组*/
SampleApp_Group.ID = 0x0001;
osal_memcpy( SampleApp_Group.name, "Group 1", 7 );
aps_AddGroup( SAMPLEAPP_ENDPOINT, &SampleApp_Group );
#if defined ( LCD_SUPPORTED )
HalLcdWriteString( "SampleApp", HAL_LCD_LINE_1 );
#endif
}
除了上面注释中提到的东西,需要另外注意一段代码
RegisterForKeys( SampleApp_TaskID );
这段函数是在这么写的:
uint8 RegisterForKeys( uint8 task_id )
{
// Allow only the first task
if ( registeredKeysTaskID == NO_TASK_ID )
{
registeredKeysTaskID = task_id;
return ( true );
}
else
return ( false );
}
把SampleApp_TaskID的值赋值给registeredKeysTaskID,以便后边按键触发回调函数调用。
1.3 函数InitBoard( OB_READY )
这这个函数里会调用函数HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE, OnBoard_KeyCallback),这个地方需要的注意的就是OnBoard_KeyCallback函数,按键不使用中断,同时定义了按键的回调函数是OnBoard_KeyCallback
void HalKeyConfig (bool interruptEnable, halKeyCBack_t cback)
{
/* Enable/Disable Interrupt or */
Hal_KeyIntEnable = interruptEnable;
/* Register the callback fucntion */
pHalKeyProcessFunction = cback;
/* Determine if interrupt is enable or not */
if (Hal_KeyIntEnable)//如果使用中断的话
{
/* Rising/Falling edge configuratinn */
PICTL &= ~(HAL_KEY_SW_6_EDGEBIT); /* Clear the edge bit */
/* For falling edge, the bit must be set. */
#if (HAL_KEY_SW_6_EDGE == HAL_KEY_FALLING_EDGE)
PICTL |= HAL_KEY_SW_6_EDGEBIT;
#endif
/* Interrupt configuration: * - Enable interrupt generation at the port * - Enable CPU interrupt * - Clear any pending interrupt */
HAL_KEY_SW_6_ICTL |= HAL_KEY_SW_6_ICTLBIT;
HAL_KEY_SW_6_IEN |= HAL_KEY_SW_6_IENBIT;
HAL_KEY_SW_6_PXIFG = ~(HAL_KEY_SW_6_BIT);
/* Rising/Falling edge configuratinn */
HAL_KEY_JOY_MOVE_ICTL &= ~(HAL_KEY_JOY_MOVE_EDGEBIT); /* Clear the edge bit */
/* For falling edge, the bit must be set. */
#if (HAL_KEY_JOY_MOVE_EDGE == HAL_KEY_FALLING_EDGE)
HAL_KEY_JOY_MOVE_ICTL |= HAL_KEY_JOY_MOVE_EDGEBIT;
#endif
/* Interrupt configuration: * - Enable interrupt generation at the port * - Enable CPU interrupt * - Clear any pending interrupt */
HAL_KEY_JOY_MOVE_ICTL |= HAL_KEY_JOY_MOVE_ICTLBIT;
HAL_KEY_JOY_MOVE_IEN |= HAL_KEY_JOY_MOVE_IENBIT;
HAL_KEY_JOY_MOVE_PXIFG = ~(HAL_KEY_JOY_MOVE_BIT);
/* Do this only after the hal_key is configured - to work with sleep stuff */
if (HalKeyConfigured == TRUE)
{
osal_stop_timerEx(Hal_TaskID, HAL_KEY_EVENT); /* Cancel polling if active */
}
}
else /* Interrupts NOT enabled 不使用中断方式*/
{
HAL_KEY_SW_6_ICTL &= ~(HAL_KEY_SW_6_ICTLBIT); /* don't generate interrupt */
HAL_KEY_SW_6_IEN &= ~(HAL_KEY_SW_6_IENBIT); /* Clear interrupt enable bit */
osal_set_event(Hal_TaskID, HAL_KEY_EVENT);
}
/* Key now is configured */
HalKeyConfigured = TRUE;
}
在上面的函数中注意到osal_set_event函数,它会调用
tasksEvents[task_id] |= event_flag;
这句话个人感觉特别关键,就是给taskEvents[Hal_TaskID]置1,表示有按键事件,后边程序需要对按键做轮询操作。
先到这里吧,后边接着做介绍。