协议是一系列的通信标准,通信双方需要共同按照这一标准进行正常的数据发射和接收。协议栈是协议的具体实现形式,通俗点来理解就是协议栈是协议和用户之间的一个接口,开发人员通过使用协议栈来使用这个协议的,进而实现无线数据收发。
ZigBee 无线网络协议层的架构图:PHY(物理层)、MAC(介质访问层)技术规范、NWK (网络层)、 APS (应用程序支持子层)、APL (应用层)技术规范。
我们在开发一个应用时,我们只需在应用层进行改动。我们只需的掌握 10 个函数就能使用 ZigBee 通讯。
实现一个简单的无线数据通信时的一般步骤:
1、组网:调用协议栈的组网函数、加入网络函数,实现网络的建立与节点的加入。
2、发送:发送节点调用协议栈的无线数据发送函数,实现无线数据发送。
3、接收:接收节点调用协议栈的无线数据接收函数,实现无线数据接收。
Samples 文件夹里面有三个例子: GenericApp、SampleApp、SimpleApp 在这
里们选择 SampleApp 对协议栈的工作流程进行讲解。打开\SampleApp\CC2530DB
下工程文件 SampleApp.eww。找到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 board I/O
InitBoard( OB_COLD );//
// Initialze HAL drivers
HalDriverInit();//初始化驱动led、key、lcd、uart
// 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
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 */ //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 );
#endifosal_start_system(); // No Return from here
return 0; // Shouldn't get here.
}
我们重点看两个函数:
osal_init_system();
osal_start_system();
首先看
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) ,用户需考虑
}
在看osal_start_system()函数中的 osal_run_system()函数:
void osal_run_system( void )
{
uint8 idx = 0;osalTimeUpdate();
Hal_ProcessPoll(); // This replaces MT_SerialPoll() and osal_check_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 );
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/* Yield in case cooperative scheduling is being used. */
#if defined (configUSE_PREEMPTION) && (configUSE_PREEMPTION == 0)
{
osal_task_yield();
}
#endif
}
进入 tasksEvents[idx]数组定义,发现恰好在刚刚 osalInitTasks( void )函数上面。而且 taskID 一一对应。
这就是初始化与调用的关系。taskID 把任务联系起来了。