1. 系统初始化
从main函数开始(ZMain.c)
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 board I/O
InitBoard( OB_COLD ); //初始化I/O ,LED 、Timer 等
// Initialze HAL drivers
HalDriverInit(); //初始化芯片各硬件模块
// Initialize NV System
osal_nv_init( NULL ); //初始化Flash 存储器
// Initialize the MAC
ZMacInit(); //初始化MAC 层
// Determine the extended address
zmain_ext_addr(); //确定IEEE 64位地址
// 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
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(); //初始化LCD
#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()
重点关注osal_init_system()和osal_start_system(),一个是操作系统初始化(定义任务),一个是启动操作系统。
2. osal_init_sytem()解析
uint8 osal_init_system( void )
{
// Initialize the Memory Allocation System
osal_mem_init();
// Initialize the message queue
osal_qHead = NULL;
// 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()任务初始化
void osalInitTasks( void )
{
uint8 taskID = 0;
// 分配内存,返回指向缓冲区的指针
tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
// 设置所分配的内存空间单元值为0
osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));
// 任务优先级由高向低依次排列,高优先级对应taskID 的值反而小
macTaskInit( taskID++ ); //macTaskInit(0) ,用户不需考虑
nwk_init( taskID++ ); //nwk_init(1),用户不需考虑
Hal_Init( taskID++ ); //Hal_Init(2) ,用户需考虑
#if defined( MT_TASK )
MT_TaskInit( taskID++ );
#endif
APS_Init( taskID++ ); //APS_Init(3) ,用户不需考虑
#if defined ( ZIGBEE_FRAGMENTATION )
APSF_Init( taskID++ );
#endif
ZDApp_Init( taskID++ ); //ZDApp_Init(4) ,用户需考虑
#if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )
ZDNwkMgr_Init( taskID++ );
#endif
//用户创建的任务
SampleApp_Init( taskID ); // SampleApp_Init _Init(5) ,用户需考虑
}
其中关注点:
1)Hal_Init() ,查看hal_drivers.c
只有一行,why?
void Hal_Init( uint8 task_id )
{
/* Register task ID */
Hal_TaskID = task_id;
}
只定义赋值了Hal_TaskID, 在哪里调用呢?留待后续研究
其中hal_drivers.c 文件定义了HalDevierInit(), 上面在main.c里调用了这个初始化函数
两者之间的关系?
2)MT_TASK MT按API定义应该是串口直接访问各层的接口,可以对比后续的串口应用,看如何运用
3)ZDApp_Init(); 这是网络设备启动的函数。
void ZDApp_Init( uint8 task_id )
{
// Save the task ID
ZDAppTaskID = task_id;
// Initialize the ZDO global device short address storage
ZDAppNwkAddr.addrMode = Addr16Bit;
ZDAppNwkAddr.addr.shortAddr = INVALID_NODE_ADDR;
(void)NLME_GetExtAddr(); // Load the saveExtAddr pointer.
// Check for manual "Hold Auto Start"
ZDAppCheckForHoldKey();
// Initialize ZDO items and setup the device - type of device to create.
ZDO_Init();
// Register the endpoint description with the AF
// This task doesn't have a Simple description, but we still need
// to register the endpoint.
afRegister( (endPointDesc_t *)&ZDApp_epDesc );
#if defined( ZDO_USERDESC_RESPONSE )
ZDApp_InitUserDesc();
#endif // ZDO_USERDESC_RESPONSE
// Start the device?
if ( devState != DEV_HOLD )
{
ZDOInitDevice( 0 );
}
else
{
// Blink LED to indicate HOLD_START
HalLedBlink ( HAL_LED_4, 0, 50, 500 );
}
ZDApp_RegisterCBs();
} /* ZDApp_Init() */
其中HOLD_START宏定义决定了是否跳过启动过程,转由应用启动。
研究协议栈时,还需要深入研究
4)最后一行定义了用户创建的任务的初始化,及分配taskID
SampleApp_Init( taskID );
至此,初始化函数osal_init_system()完成
3. osal_start_system()解析
实质是任务轮询的操作系统执行流程
void osal_start_system( void )
{
#if !defined ( ZBIT ) && !defined ( UBIT )
for(;;) // Forever Loop
#endif
{
uint8 idx = 0;
osalTimeUpdate(); //扫描哪个事件被触发了,然后置相应的标志位
Hal_ProcessPoll(); //轮询TIMER与UART This replaces MT_SerialPoll() and osal_check_timer().
do {
if (tasksEvents[idx]) // Task is highest priority that is ready.
{
break; //得到待处理的最高优先级任务索引号 idx
}
} while (++idx < tasksCnt);
if (idx < tasksCnt)
{
uint16 events;
halIntState_t intState;
HAL_ENTER_CRITICAL_SECTION(intState);// 进入临界区,保护
events = tasksEvents[idx]; //提取需要处理的任务中的事件
tasksEvents[idx] = 0; //清除本次任务的事件
HAL_EXIT_CRITICAL_SECTION(intState); // 退出临界区
events = (tasksArr[idx])( idx, events );//通过指针调用任务处理函数,关键
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
}
}
4. 用户任务初始化SampleApp_Init()分析
void SampleApp_Init( uint8 task_id )
{
SampleApp_TaskID = task_id; //osal分配的任务ID随着用户添加任务的增多而改变
SampleApp_NwkState = DEV_INIT;//设备状态设定为ZDO层中定义的初始化状态
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
//该段的意思是,如果设置了HOLD_AUTO_START宏定义,将会在启动芯片的时候会暂停启动
//流程,只有外部触发以后才会启动芯片。其实就是需要一个按钮触发它的启动流程。
#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.
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;//组号0x0001
// Fill out the endpoint description. 定义本设备用来通信的APS层端点描述符
SampleApp_epDesc.endPoint = SAMPLEAPP_ENDPOINT; //指定端点号
SampleApp_epDesc.task_id = &SampleApp_TaskID; //SampleApp 描述符的任务ID
SampleApp_epDesc.simpleDesc
= (SimpleDescriptionFormat_t *)&SampleApp_SimpleDesc;//SampleApp简单描述符
SampleApp_epDesc.latencyReq = noLatencyReqs; //延时策略
// Register the endpoint description with the AF
afRegister( &SampleApp_epDesc ); //向AF层登记描述符,告知应用层已经有这个EP开通使用
// 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 );//把该组登记添加到APS中
#if defined ( LCD_SUPPORTED )
HalLcdWriteString( "SampleApp", HAL_LCD_LINE_1 ); //如果支持LCD,显示提示信息
#endif
}
关键操作:
1)定义网络设备状态
SampleApp_NwkState = DEV_INIT;
2)描述添加APS端点EP
查看SampleApp_epDesc定义
endPointDesc_t SampleApp_epDesc;
再追溯,发现EP的数据结构在AF.H中定义
// Endpoint Table - this table is the device description
// or application registration.
// There will be one entry in this table for every
// endpoint defined.
typedef struct
{
byte endPoint;
byte *task_id; // Pointer to location of the Application task ID.
SimpleDescriptionFormat_t *simpleDesc;
afNetworkLatencyReq_t latencyReq;
} endPointDesc_t;
3)设置数据发送方式:
广播,组播,组号0001,组名Group1,组登记到APS中
4)其中对按键处理的定义,是把所有的按键响应都登记到我们定义的用户程序中,查看相关函数(OnBoard.c)
/*********************************************************************
* Keyboard Register function
*
* The keyboard handler is setup to send all keyboard changes to
* one task (if a task is registered).
*
* If a task registers, it will get all the keys. You can change this
* to register for individual keys.
*********************************************************************/
uint8 RegisterForKeys( uint8 task_id )
{
// Allow only the first task
if ( registeredKeysTaskID == NO_TASK_ID )
{
registeredKeysTaskID = task_id;
return ( true );
}
else
return ( false );
}
至此,初始化分析完成,下一步要分析用户处理程序,怎么追溯用户程序处理入口呢?
先看osal_start_system中的轮询表(OSAL.C):
events = (tasksArr[idx])( idx, events );
追溯任务入口定义OSAL_SampleApp.c
/*********************************************************************
* GLOBAL VARIABLES
*/
// The order in this table must be identical to the task initialization calls below in osalInitTask.
const pTaskEventHandlerFn tasksArr[] = {
macEventLoop,
nwk_event_loop,
Hal_ProcessEvent,
#if defined( MT_TASK )
MT_ProcessEvent,
#endif
APS_event_loop,
#if defined ( ZIGBEE_FRAGMENTATION )
APSF_ProcessEvent,
#endif
ZDApp_event_loop,
#if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )
ZDNwkMgr_event_loop,
#endif
SampleApp_ProcessEvent
};
追溯pTaskEventHandlerFn函数原型定义在OSAL_Tasks.h
/*********************************************************************
* TYPEDEFS
*/
/*
* Event handler function prototype
*/
typedef unsigned short (*pTaskEventHandlerFn)( unsigned char task_id, unsigned short event );
/*********************************************************************
* GLOBAL VARIABLES
*/
extern const pTaskEventHandlerFn tasksArr[];
extern const uint8 tasksCnt;
extern uint16 *tasksEvents;
至此,找到应用事件入口SampleApp_ProcessEvent
下一节分析SampleApp_ProcessEvent的执行流程