在阅读了Zigbee及IEEE802.15.4协议的理论知识后,接下来看一下TI公司开发的基于Zigbee的协议实现ZStack。
我们仍然从TI提供的温度监测程序开始,首先查看一下,程序的主函数在ZMain.c文件中,从程序的说明看出,是ZStack的startup和shutdown代码.
以下是其中的main()函数,其实英文注释已经很清楚说明每一步,这里翻译一下:
int main( void )
{
// Turn off interrupts(关中断)
osal_int_disable( INTS_ALL );
// Initialization for board related stuff such as LEDs(初始化板上组件,如LED)
HAL_BOARD_INIT();
// Make sure supply voltage is high enough to run(电压检查)
zmain_vdd_check();
// Initialize board I/O(初始化I/O接口)
InitBoard( OB_COLD );
// Initialze HAL drivers(初始化HAL设备,在hal_drivers.c中实现)
HalDriverInit();
// Initialize NV System(初始化NV系统,即非易失设备,如Flash)
osal_nv_init( NULL );
// Initialize the MAC(初始化MAC)
ZMacInit();
// Determine the extended address(确定设备的长地址)
zmain_ext_addr();
// Initialize basic NV items(初始化ZStack的全局变量,如果在NV内存中不存在,则写入缺省值)
zgInit();
#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(显示设备信息,比如CollectorEB板的IEEE地址就是在这个函数里面显示出来的。)
zmain_dev_info();
/* Display the device info on the LCD */(显示LCD设备信息)
#ifdef LCD_SUPPORTED
zmain_lcd_init();
#endif
#ifdef WDT_IN_PM1
/* If WDT is used, this is a good place to enable it. */(开WatchDog)
WatchDogEnable( WDTIMX );
#endif
osal_start_system(); // No Return from here(启动操作系统,实际上进入一个死循环)
return 0; // Shouldn't get here.
} // main()
说明:以上初始化有的跟硬件相关,有的跟操作系统(以osal开头)相关,最后系统调用osal_start_system进入循环,然后在循环中处理消息,下面是这个函数的主体(见osal.c):
void osal_start_system( void )
{
#if !defined ( ZBIT ) && !defined ( UBIT )
for(;;) // Forever Loop (死循环)
#endif
{
uint8 idx = 0;
osalTimeUpdate(); //更新时间
Hal_ProcessPoll(); // This replaces MT_SerialPoll() and osal_check_timer().(轮询UART和Timer等设备消息)
do {
if (tasksEvents[idx]) // Task is highest priority that is ready.
{
break;
}
} while (++idx < tasksCnt);
if (idx < tasksCnt)
{
uint16 events;
halIntState_t intState;
HAL_ENTER_CRITICAL_SECTION(intState); //加锁防上访问事件队列充突
events = tasksEvents[idx];
tasksEvents[idx] = 0; // Clear the Events for this task.
HAL_EXIT_CRITICAL_SECTION(intState);
events = (tasksArr[idx])( idx, events ); //处理当前要处理的事件,并返回事件ID
HAL_ENTER_CRITICAL_SECTION(intState);
tasksEvents[idx] |= events; // Add back unprocessed events to the current task.(更新事件状态)
HAL_EXIT_CRITICAL_SECTION(intState);
}
#if defined( POWER_SAVING )
else // Complete pass through all task events with no activity?
{
osal_pwrmgr_powerconserve(); // Put the processor/system into sleep(没有事件处理,进入休眠状态)
}
#endif
}
}
需要特别解析的是上面红色标注的行,这个地方看起来象是一个函数指针的形式,查看taskArr定义(见SAPI.c)如下:
#if OSAL_SAPI
// The order in this table must be identical to the task initialization calls below inosalInitTask.
const pTaskEventHandlerFn tasksArr[] = {
macEventLoop,
nwk_event_loop,
Hal_ProcessEvent,
#if defined( MT_TASK )
MT_ProcessEvent,
#endif
APS_event_loop,
ZDApp_event_loop,
SAPI_ProcessEvent
};
上面基本上是***EventLoop,可以推测是处理不同层的信息,如mac, NWK, Hal, APS, ZDAPP的事件处理函数。以macEventLoop为例,可以在mac_api.h文件中找到这个函数的定义如下:
extern uint16 macEventLoop(uint8 taskId, uint16 events);
但是,没有跟踪到上述函数的实现在哪个文件中。但是对于温度监测应用DEMo来讲,上述的SAPI_ProcessEvent()函数在SAPI.c中实现如下:
UINT16 SAPI_ProcessEvent( byte task_id, UINT16 events )
{
osal_event_hdr_t *pMsg;
afIncomingMSGPacket_t *pMSGpkt;
afDataConfirm_t *pDataConfirm;
if ( events & SYS_EVENT_MSG ) //系统事件消息,只有定义了SAPI_CB_FUNC 的似乎才会被上层应用处理。
{
...
case KEY_CHANGE:
#if ( SAPI_CB_FUNC )
zb_HandleKeys( ((keyChange_t *)pMsg)->state, ((keyChange_t *)pMsg)->keys );
#endif
break;
...
// Return unprocessed events
return (events ^ SYS_EVENT_MSG);
}
if ( events & ZB_ALLOW_BIND_TIMER ) //ZB_ALLOW_BIND_TIMER
{
afSetMatch(sapi_epDesc.simpleDesc->EndPoint, FALSE);
return (events ^ ZB_ALLOW_BIND_TIMER);
}
if ( events & ZB_BIND_TIMER ) //ZB_BIND_TIMER
{
// Send bind confirm callback to application
SAPI_BindConfirm( sapi_bindInProgress, ZB_TIMEOUT );
sapi_bindInProgress = 0xffff;
return (events ^ ZB_BIND_TIMER);
}
if ( events & ZB_ENTRY_EVENT ) //ZB_ENTRY_EVENT
{
uint8 startOptions;
// Give indication to application of device startup
#if ( SAPI_CB_FUNC )
zb_HandleOsalEvent( ZB_ENTRY_EVENT );
#endif
// LED off cancels HOLD_AUTO_START blink set in the stack
HalLedSet (HAL_LED_4, HAL_LED_MODE_OFF);
zb_ReadConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions );
if ( startOptions & ZCD_STARTOPT_AUTO_START )
{
zb_StartRequest();
}
else
{
// blink leds and wait for external input to config and restart
HalLedBlink(HAL_LED_2, 0, 50, 500);
}
return (events ^ ZB_ENTRY_EVENT );
}
// This must be the last event to be processed
if ( events & ( ZB_USER_EVENTS ) )
{
// User events are passed to the application
#if ( SAPI_CB_FUNC )
zb_HandleOsalEvent( events );
#endif
// Do not return here, return 0 later
}
// Discard unknown events
return 0;
}
上述代码中,只有黑色标注的才被上传到应用层APL进行处理,而这些代码正好是用户需要实现的,分别在DemoSensor.c和DemoCollector.c中实现。以路由器的代码为例,显示如下,其作用主要是对按键进行处理:
void zb_HandleKeys( uint8 shift, uint8 keys )
{
static uint8 allowBind=FALSE;
static uint8 allowJoin=TRUE;
uint8 logicalType;
// Shift is used to make each button/switch dual purpose.
if ( shift )
{
if ( keys & HAL_KEY_SW_1 )
{
}
if ( keys & HAL_KEY_SW_2 )
{
}
if ( keys & HAL_KEY_SW_3 )
{
}
if ( keys & HAL_KEY_SW_4 )
{
}
}
else
{
if ( keys & HAL_KEY_SW_1 ) //Joystick Up key (Set as cordinator,设置为协调器)
{
if ( appState == APP_INIT )
{
// Key 1 starts device as a coordinator
logicalType = ZG_DEVICETYPE_COORDINATOR;
zb_WriteConfiguration(ZCD_NV_LOGICAL_TYPE, sizeof(uint8), &logicalType);
// Reset the device with new configuration
zb_SystemReset();
}
}
if ( keys & HAL_KEY_SW_2 ) //Joystick Right key (Allow bind,允许绑定,一个网络中只有一个节点,即协调器有此设置。)
{
allowBind ^= 1;
if (allowBind)
{
// Turn ON Allow Bind mode infinitly
zb_AllowBind( 0xFF );
HalLedSet( HAL_LED_2, HAL_LED_MODE_ON );
//This node is the gateway node
isGateWay = TRUE;
// Update the display
#if defined ( LCD_SUPPORTED )
HalLcdWriteString( "Gateway Mode", HAL_LCD_LINE_2 );
#endif
}
else
{
// Turn OFF Allow Bind mode infinitly
zb_AllowBind( 0x00 );
HalLedSet( HAL_LED_2, HAL_LED_MODE_OFF );
isGateWay = FALSE;
// Update the display
#if defined ( LCD_SUPPORTED )
HalLcdWriteString( "Collector", HAL_LCD_LINE_2 );
#endif
}
}
if ( keys & HAL_KEY_SW_3 ) //Joystick down key(路由器有效,用于定时向协调器报告事件,以生成拓扑图)
{
// Start reporting
osal_set_event( sapi_TaskID, MY_REPORT_EVT );
}
if ( keys & HAL_KEY_SW_4 ) //Joystick Left key(用于禁止/允许节点加入,此功能用于协调器设置禁止或允许节点加入,但是不知为何无效!!?)
{
// Key 4 is used to control which routers
// that can accept join requests
allowJoin ^= 1;
if(allowJoin)
{
NLME_PermitJoiningRequest(0xFF);
}
else {
NLME_PermitJoiningRequest(0);
}
}
}
}
对应的另一个处理函数如下:
void zb_HandleOsalEvent( uint16 event )
{
uint8 logicalType;
if(event & SYS_EVENT_MSG) //系统消息,多数在底层已经处理,因此此处为空。
{
}
if( event & ZB_ENTRY_EVENT ) //系统启动事件,需要做一些设置。
{
// Initialise UART
initUart(uartRxCB);
// blind LED 1 to indicate starting/joining a network
HalLedBlink ( HAL_LED_1, 0, 50, 500 );
HalLedSet( HAL_LED_2, HAL_LED_MODE_OFF );
// Read logical device type from NV
zb_ReadConfiguration(ZCD_NV_LOGICAL_TYPE, sizeof(uint8), &logicalType);
// Start the device
zb_StartRequest();
}
if ( event & MY_START_EVT )
{
zb_StartRequest();
}
if ( event & MY_REPORT_EVT )
{
if (isGateWay)
{
osal_start_timerEx( sapi_TaskID, MY_REPORT_EVT, myReportPeriod );
}
else if (appState == APP_BINDED)
{
sendDummyReport();
osal_start_timerEx( sapi_TaskID, MY_REPORT_EVT, myReportPeriod );
}
}
if ( event & MY_FIND_COLLECTOR_EVT )
{
// Find and bind to a gateway device (if this node is not gateway)
if (!isGateWay)
{
zb_BindDevice( TRUE, DUMMY_REPORT_CMD_ID, (uint8 *)NULL );
}
}
}