[zigbee][z-Stack]协议栈简介及工作流程
文章目錄
- 什么是zigbee協(xié)議棧?
- 如何使用zigbee協(xié)議棧?
- z-Stack工作流程
什么是zigbee協(xié)議棧?
協(xié)議棧是協(xié)議的具體實(shí)現(xiàn)形式,通俗點(diǎn)來理解就是協(xié)議棧是協(xié)議和用戶之間的一個(gè)缺口,開發(fā)人員通過使用協(xié)議棧來使用這個(gè)協(xié)議的,進(jìn)而實(shí)現(xiàn)無線數(shù)據(jù)收發(fā)。
ZigBee的協(xié)議分為兩部分,IEEE 802.15.4定義了PHY(物理層)和MAC(介質(zhì)訪問層)技術(shù)規(guī)范;ZigBee聯(lián)盟定義了NWK(網(wǎng)絡(luò)層) APS(應(yīng)用程序支持子層) APL(應(yīng)用層)技術(shù)規(guī)范。
ZigBee協(xié)議棧就是將各個(gè)層定義的協(xié)議都集合在一起,以函數(shù)的形式實(shí)現(xiàn),并給用戶提供API(應(yīng)用層),用戶可以直接調(diào)用。
無線網(wǎng)絡(luò)協(xié)議層👇
如何使用zigbee協(xié)議棧?
協(xié)議棧是協(xié)議的實(shí)現(xiàn),可以理解為代碼,函數(shù)庫,供上層應(yīng)用調(diào)用,協(xié)議棧底下的層與應(yīng)用是相互獨(dú)立的。商業(yè)的協(xié)議棧就是給你寫好了底層的代碼,符合協(xié)議標(biāo)準(zhǔn),提過給你一個(gè)功能模塊給你調(diào)用。
我們需要關(guān)心的是我們的應(yīng)用邏輯,數(shù)據(jù)從哪里到哪里,怎么存儲(chǔ),處理;還有系統(tǒng)里的設(shè)備之間的聽信順序什么的,當(dāng)你的應(yīng)用需要數(shù)據(jù)通信時(shí),調(diào)用組網(wǎng)函數(shù)給你組建你想要的網(wǎng)絡(luò);當(dāng)你想從一個(gè)設(shè)備發(fā)數(shù)據(jù)到另一個(gè)設(shè)備時(shí),調(diào)用無線數(shù)據(jù)發(fā)送函數(shù);當(dāng)然,接收端就調(diào)用接收函數(shù);當(dāng)你的設(shè)備沒事干的時(shí)候你就調(diào)用睡眠函數(shù);要干活的時(shí)候就調(diào)用喚醒函數(shù)。
所以當(dāng)你做具體應(yīng)用時(shí),不需要關(guān)心協(xié)議棧是怎么寫的,里面的每條代碼是什么意思。除非你要做協(xié)議研究。每個(gè)廠商的協(xié)議棧有區(qū)別,也就是函數(shù)名稱和參數(shù)可能有區(qū)別,這個(gè)要看具體的例子、說明文檔。
舉個(gè)例子,用戶實(shí)現(xiàn)一個(gè)簡單的無線數(shù)據(jù)通信時(shí)的一般步驟:
1、組網(wǎng):調(diào)用協(xié)議棧的組網(wǎng)函數(shù)、加入網(wǎng)絡(luò)函數(shù),實(shí)現(xiàn)網(wǎng)絡(luò)的建立與節(jié)點(diǎn)的加入。2、發(fā)送:發(fā)送節(jié)點(diǎn)調(diào)用協(xié)議棧的無線數(shù)據(jù)發(fā)送函數(shù),實(shí)現(xiàn)無線數(shù)據(jù)發(fā)送。3、接收:接收節(jié)點(diǎn)調(diào)用協(xié)議棧的無線數(shù)據(jù)接收函數(shù),實(shí)現(xiàn)無線數(shù)據(jù)接收。是不是看上去很簡單啊,其實(shí)協(xié)議棧很多都封裝好了,下面我們大概看看無線發(fā)送函數(shù):
afStatus_t AF_DataRequest(afAddrType_t *dstAddr,endPointDesc_t *srcEP,uint16 cID,uint16 len, //發(fā)送數(shù)據(jù)的長度uint8 *buf, //指向存放發(fā)送數(shù)據(jù)的緩沖區(qū)的指針uint8 *transID,uint8 options,uint8 radiuis)用戶調(diào)用該函數(shù)即可實(shí)現(xiàn)數(shù)據(jù)的無線數(shù)據(jù)的發(fā)送,此函數(shù)中有8個(gè)參數(shù),用戶需要將每個(gè)參數(shù)的含義理解以后,才能熟練使用該函數(shù)進(jìn)行無線數(shù)據(jù)通信的目的?,F(xiàn)在只講其中最重要的兩個(gè)參數(shù),其它參數(shù)不需要死記硬背,以后用多了自然就記住了。
至于調(diào)用該函數(shù)后,如何初始化硬件進(jìn)行數(shù)據(jù)發(fā)送等工作,用戶不需要關(guān)心, ZigBee協(xié)議棧己經(jīng)將所需要的工作做好了,我們只需要調(diào)用相應(yīng)的API函數(shù)即可,而不必關(guān)心具體實(shí)現(xiàn)細(xì)節(jié)。
z-Stack工作流程
下載z-Stack 提取碼:qta9
打開工程:
ZStack2.5.1a\Projects\zstack\Samples\SampleApp\CC2530DB\SampleApp.eww建議大家復(fù)制工程到非中文目錄,因?yàn)橛行╅_發(fā)環(huán)境對(duì)中文路徑的支持不友好;還有就是不要把文件放的太深層目錄下,目錄太長,打開工程是IAR會(huì)關(guān)閉;(如果使用 IAR 打開工程停止響應(yīng)或關(guān)閉,說明你路徑太長, IAR 不識(shí)別,把路徑改短或移上幾層目錄即可解決)
App:應(yīng)用層目錄,這是用戶創(chuàng)建各種不同工程的區(qū)域,在這個(gè)目錄中包含了應(yīng)用層的內(nèi)容和這個(gè)項(xiàng)目的主要內(nèi)容。
看到工程中有這么多的文件夾和文件,先不要害怕,帶著疑問做實(shí)驗(yàn)就行,做的多了就明白了。
用戶自己添加的應(yīng)用任務(wù)程序在 Zstack 中的調(diào)用過程:
main()---> osal_init_system()---> osalInitTasks()---> SampleApp_Init()打開ZMain.c找到main函數(shù)
int main( void ) {osal_int_disable( INTS_ALL ); //關(guān)閉所有中斷HAL_BOARD_INIT(); //初始化系統(tǒng)時(shí)鐘zmain_vdd_check(); //檢查芯片電壓是否正常InitBoard( OB_COLD ); //初始化 I/O , LED 、 Timer 等HalDriverInit(); //初始化芯片各硬件模塊osal_nv_init( NULL ); //初始化 Flash 存儲(chǔ)器ZMacInit(); //初始化 MAC 層zmain_ext_addr(); //確定 IEEE 64 位地址zgInit(); //初始化非易失變量#ifndef NONWK// Since the AF isn't a task, call it's initialization routineafInit();#endifosal_init_system(); //初始化操作系統(tǒng)osal_int_enable( INTS_ALL ); //使能全部中斷InitBoard( OB_READY ); //最終板載初始化zmain_dev_info(); //顯示設(shè)備信息#ifdef LCD_SUPPORTEDzmain_lcd_init(); //初始化 LCD#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 執(zhí)行操作系統(tǒng),進(jìn)去后不會(huì)返回return 0; // Shouldn't get here. } // main()看了上面的代碼后,可能感覺很多函數(shù)不認(rèn)識(shí)。沒關(guān)系剛開始大概了解流程即可, main 函數(shù)先執(zhí)行初始化工作,包括硬件、網(wǎng)絡(luò)層、任務(wù)等的初始化。然后執(zhí)行 osal_start_system(); 操作系統(tǒng)。進(jìn)去后可不會(huì)回來了。
在這里,我們重點(diǎn)了解 2 個(gè)函數(shù):
初始化操作系統(tǒng) osal_init_system();運(yùn)行操作系統(tǒng) osal_start_system();先來看osal_init_system();系統(tǒng)初始化函數(shù),進(jìn)入函數(shù)。如果用IAR看代碼可在函數(shù)名上單擊右鍵——go to definition of…,便可以進(jìn)入函數(shù)。發(fā)現(xiàn)里面有6個(gè)初始化函數(shù),這里我們只關(guān)心 osalInitTasks();任務(wù)初始化函數(shù),繼續(xù)由該函數(shù)進(jìn)入。
函數(shù)對(duì) taskID 進(jìn)行初始化,每初始化一個(gè), taskID++。大家看到了注釋后面有些寫著用戶需要考慮,有些則寫著用戶不需考慮。沒錯(cuò),需要考慮的用戶可以根據(jù)自己的硬件平臺(tái)或者其他設(shè)置,而寫著不需考慮的也是不能修改的。TI 公司協(xié)議棧已完成。SampleApp_Init()是我們 應(yīng) 用 協(xié) 議 棧 例 程 的 必 要 函 數(shù) , 用 戶 通 常 在 這 里 初 始 化 自 己 的 東 西 。至 此 ,osal_init_system();大概了解完畢。
接下來看第二個(gè)函數(shù) osal_start_system();運(yùn)行操作系統(tǒng)。同樣用go to definition 的方法進(jìn)入該函數(shù)。
void osal_start_system( void ) {#if !defined ( ZBIT ) && !defined ( UBIT )for(;;) // Forever Loop#endif{uint8 idx = 0;osalTimeUpdate(); //掃描哪個(gè)事件被觸發(fā)了,然后置相應(yīng)的標(biāo)志位Hal_ProcessPoll(); //輪詢 TIMER 與 UARTdo {if (tasksEvents[idx]) // Task is highest priority that is ready.{break; //得到待處理的最高優(yōu)先級(jí)任務(wù)索引號(hào) idx}} while (++idx < tasksCnt);if (idx < tasksCnt){uint16 events;halIntState_t intState;HAL_ENTER_CRITICAL_SECTION(intState);// 進(jìn)入臨界區(qū),保護(hù)events = tasksEvents[idx]; //提取需要處理的任務(wù)中的事件tasksEvents[idx] = 0; //清除本次任務(wù)的事件HAL_EXIT_CRITICAL_SECTION(intState); // 退出臨界區(qū)events = (tasksArr[idx])( idx, events );//通過指針調(diào)用任務(wù)處理函數(shù),關(guān)鍵HAL_ENTER_CRITICAL_SECTION(intState); //進(jìn)入臨界區(qū)tasksEvents[idx] |= events; // 保存未處理的事件 Add back unprocessed eventsto the current task.HAL_EXIT_CRITICAL_SECTION(intState); // 退出臨界區(qū)}#if defined( POWER_SAVING )else // Complete pass through all task events with no activity?{osal_pwrmgr_powerconserve(); // Put the processor/system into sleep}#endif} }看一下 events = tasksEvents[idx]; 進(jìn)入 tasksEvents[idx]數(shù)組定義,發(fā)現(xiàn)恰好是osalInitTasks()函數(shù)里面分配空間初始化過的 tasksEvents。而且 taskID 一一對(duì)應(yīng)。這就是初始化與調(diào)用的關(guān)系。taskID 把任務(wù)聯(lián)系起來了。
SampleApp_Init()用戶應(yīng)用任務(wù)初始化函數(shù)
void SampleApp_Init( uint8 task_id ) {SampleApp_TaskID = task_id;//osal 分配的任務(wù) ID 隨著用戶添加任務(wù)的增多而改變SampleApp_NwkState = DEV_INIT; //設(shè)備狀態(tài)設(shè)定為 ZDO 層中定義的初始化狀態(tài)/*初始化應(yīng)用設(shè)備的網(wǎng)絡(luò)類型,設(shè)備類型的改變都要產(chǎn)生一個(gè)事件—ZDO_STATE_CHANGE,從字面理解為 ZDO 狀態(tài)發(fā)生了改變。所以在設(shè)備初始化的時(shí)候一定要把它初始化為什么狀態(tài)都沒有。那么它就要去檢測(cè)整個(gè)環(huán)境,看是否能重新建立或者加入存在的網(wǎng)絡(luò)。但是有一種情況例外,就是當(dāng) NV_RESTORE 被設(shè)置的候( NV_RESTORE 是把信息保存在非易失存儲(chǔ)器中),那么當(dāng)設(shè)備斷電或者某種意外重啟時(shí),由于網(wǎng)絡(luò)狀態(tài)存儲(chǔ)在非易失存儲(chǔ)器中,那么此時(shí)就只需要恢復(fù)其網(wǎng)絡(luò)狀態(tài),而不需要重新建立或者加入網(wǎng)絡(luò)了.這里需要設(shè)置 NV_RESTORE 宏定義。*/SampleApp_TransID = 0; //消息發(fā)送 ID(多消息時(shí)有順序之分)#if defined ( BUILD_ALL_DEVICES )if ( readCoordinatorJumper() )zgDeviceLogicalType = ZG_DEVICETYPE_COORDINATOR;elsezgDeviceLogicalType = ZG_DEVICETYPE_ROUTER;#endif // BUILD_ALL_DEVICES//該段的意思是,如果設(shè)置了 HOLD_AUTO_START 宏定義,將會(huì)在啟動(dòng)芯片的時(shí)候會(huì)暫停啟動(dòng)流程,只有外部觸發(fā)以后才會(huì)啟動(dòng)芯片。其實(shí)就是需要一個(gè)按鈕觸發(fā)它的啟動(dòng)流程。#if defined ( HOLD_AUTO_START )ZDOInitDevice(0);#endif//設(shè)置發(fā)送數(shù)據(jù)的方式和目的地址尋址模式//發(fā)送模式:廣播發(fā)送SampleApp_Periodic_DstAddr.addrMode = (afAddrMode_t)AddrBroadcast; //廣播SampleApp_Periodic_DstAddr.endPoint = SAMPLEAPP_ENDPOINT; //指定端點(diǎn)號(hào)SampleApp_Periodic_DstAddr.addr.shortAddr = 0xFFFF;//指定目的網(wǎng)絡(luò)地址為廣播地址//發(fā)送模式:組播發(fā)送 Setup for the flash command's destination address - Group 1SampleApp_Flash_DstAddr.addrMode = (afAddrMode_t)afAddrGroup; //組尋址SampleApp_Flash_DstAddr.endPoint = SAMPLEAPP_ENDPOINT; //指定端點(diǎn)號(hào)SampleApp_Flash_DstAddr.addr.shortAddr = SAMPLEAPP_FLASH_GROUP; //組號(hào) 0x0001//定義本設(shè)備用來通信的 APS 層端點(diǎn)描述符SampleApp_epDesc.endPoint = SAMPLEAPP_ENDPOINT;SampleApp_epDesc.task_id = &SampleApp_TaskID; //SampleApp 描述符的任務(wù) IDSampleApp_epDesc.simpleDesc //SampleApp 簡單描述符= (SimpleDescriptionFormat_t *)&SampleApp_SimpleDesc;SampleApp_epDesc.latencyReq = noLatencyReqs; //延時(shí)策略//向 AF 層登記描述符, 登記 endpoint description 到 AF,要對(duì)該應(yīng)用進(jìn)行初始化并在 AF/*進(jìn)行登記,告訴應(yīng)用層有這么一個(gè) EP 已經(jīng)開通可以使用,那么下層要是有關(guān)于該應(yīng)用的信息或者應(yīng)用要對(duì)下層做哪些操作,就自動(dòng)得到下層的配合*/afRegister( &SampleApp_epDesc );// 登記所有的按鍵事件RegisterForKeys( SampleApp_TaskID );// By default, all devices start out in Group 1SampleApp_Group.ID = 0x0001; //組號(hào)osal_memcpy( SampleApp_Group.name, "Group 1", 7 ); //設(shè)定組名aps_AddGroup( SAMPLEAPP_ENDPOINT, &SampleApp_Group ); //把該組登記添加到 APS 中#if defined ( LCD_SUPPORTED )HalLcdWriteString( "SampleApp", HAL_LCD_LINE_1 ); //如果支持 LCD,顯示提示信息#endif }SampleApp_ProcessEvent() 用戶應(yīng)用任務(wù)的事件處理函數(shù)
uint16 SampleApp_ProcessEvent( uint8 task_id, uint16 events ) {afIncomingMSGPacket_t *MSGpkt;(void)task_id; // Intentionally unreferenced parameterif ( events & SYS_EVENT_MSG ) //接收系統(tǒng)消息再進(jìn)行判斷{//接收屬于本應(yīng)用任務(wù)SampleApp的消息,以SampleApp_TaskID標(biāo)記MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID );while ( MSGpkt ){switch ( MSGpkt->hdr.event ){// Received when a key is pressedcase KEY_CHANGE://按鍵事件SampleApp_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t*)MSGpkt)->keys );break;// Received when a messages is received (OTA) for this endpointcase AF_INCOMING_MSG_CMD: //接收數(shù)據(jù)事件,調(diào)用函數(shù) AF_DataRequest()接收數(shù)據(jù)SampleApp_MessageMSGCB( MSGpkt ); //調(diào)用回調(diào)函數(shù)對(duì)收到的數(shù)據(jù)進(jìn)行處理break;// Received whenever the device changes state in the networkcase ZDO_STATE_CHANGE: //只要網(wǎng)絡(luò)狀態(tài)發(fā)生改變,就通過ZDO_STATE_CHANGE事件通知所有的任務(wù)。同時(shí)完成對(duì)協(xié)調(diào)器,路由器,終端的設(shè)置SampleApp_NwkState = (devStates_t)(MSGpkt->hdr.status);//if ( (SampleApp_NwkState == DEV_ZB_COORD) //實(shí)驗(yàn)中協(xié)調(diào)器只接收數(shù)據(jù)所以取消發(fā)送事件if ( (SampleApp_NwkState == DEV_ROUTER) || (SampleApp_NwkState ==DEV_END_DEVICE) ){//這個(gè)定時(shí)器只是為發(fā)送周期信息開啟的,設(shè)備啟動(dòng)初始化后從這里開始觸發(fā)第一個(gè)周期信息的發(fā)送,然后周而復(fù)始下去。osal_start_timerEx( SampleApp_TaskID,SAMPLEAPP_SEND_PERIODIC_MSG_EVT,SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT );}else{// Device is no longer in the network}break;default:break;}// Release the memory //事件處理完了,釋放消息占用的內(nèi)存osal_msg_deallocate( (uint8 *)MSGpkt );//指針指向下一個(gè)放在緩沖區(qū)的待處理的事件,返回 while ( MSGpkt )重新處理事件,直到緩沖區(qū)沒有等待處理事件為止MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID );}// return unprocessed events //返回未處理的事件return (events ^ SYS_EVENT_MSG);}// Send a message out - This event is generated by a timer// (setup in SampleApp_Init()).if ( events & SAMPLEAPP_SEND_PERIODIC_MSG_EVT ){/*處理周期性事件,利用 SampleApp_SendPeriodicMessage()處理完當(dāng)前的周期性事件,然后啟動(dòng)定時(shí)器開啟下一個(gè)周期性事情,這樣一種循環(huán)下去,也即是上面說的周期性事件了,可以做為傳感器定時(shí)采集、上傳任務(wù)*/SampleApp_SendPeriodicMessage();// Setup to send message again in normal period (+ a little jitter)osal_start_timerEx(SampleApp_TaskID,SAMPLEAPP_SEND_PERIODIC_MSG_EVT,(SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT + (osal_rand() & 0x00FF)) );// return unprocessed events 返回未處理的事件return (events ^ SAMPLEAPP_SEND_PERIODIC_MSG_EVT);}// Discard unknown eventsreturn 0; }分析接收數(shù)據(jù)函數(shù) SampleApp_MessageMSGCB
//接收數(shù)據(jù),參數(shù)為接收到的數(shù)據(jù) void SampleApp_MessageMSGCB( afIncomingMSGPacket_t *pkt ) {uint16 flashTime;byte buf[3];switch ( pkt->clusterId ) //判斷簇 ID{case SAMPLEAPP_PERIODIC_CLUSTERID: //收到廣播數(shù)據(jù)osal_memset(buf, 0 , 3);osal_memcpy(buf, pkt->cmd.Data, 2); //復(fù)制數(shù)據(jù)到緩沖區(qū)中if(buf[0]=='D' && buf[1]=='1') //判斷收到的數(shù)據(jù)是否為“ D1”{HalLedBlink(HAL_LED_1, 0, 50, 500); //如果是則 Led1 間隔 500ms 閃爍#if defined(ZDO_COORDINATOR) //協(xié)調(diào)器收到"D1"后,返回"D1"給終端,讓終端 Led1 也閃爍SampleApp_SendPeriodicMessage();#endif}else{HalLedSet(HAL_LED_1, HAL_LED_MODE_ON);}break;case SAMPLEAPP_FLASH_CLUSTERID: //收到組播數(shù)據(jù)flashTime = BUILD_UINT16(pkt->cmd.Data[1], pkt->cmd.Data[2] );HalLedBlink( HAL_LED_4, 4, 50, (flashTime / 4) );break;} }分析發(fā)送周期信息 SampleApp_SendPeriodicMessage()
void SampleApp_SendPeriodicMessage( void ) {byte SendData[3]="D1";// 調(diào)用 AF_DataRequest 將數(shù)據(jù)無線廣播出去if( AF_DataRequest( &SampleApp_Periodic_DstAddr, &SampleApp_epDesc,SAMPLEAPP_PERIODIC_CLUSTERID,2,SendData,&SampleApp_TransID,AF_DISCV_ROUTE,AF_DEFAULT_RADIUS ) == afStatus_SUCCESS ){}else{HalLedSet(HAL_LED_1, HAL_LED_MODE_ON);// Error occurred in request to send.} }AF_DataRequest 發(fā)送函數(shù)
AF_DataRequest( &SampleApp_Periodic_DstAddr, //發(fā)送目的地址+端點(diǎn)地址和傳送模式 &SampleApp_epDesc, //源(答復(fù)或確認(rèn))終端的描述(比如操作系統(tǒng)中任務(wù) ID 等)源 EP SAMPLEAPP_PERIODIC_CLUSTERID, //被 Profile 指定的有效的集群號(hào) 2, // 發(fā)送數(shù)據(jù)長度 SendData,// 發(fā)送數(shù)據(jù)緩沖區(qū) &SampleApp_TransID, // 任務(wù) ID 號(hào) AF_DISCV_ROUTE, // 有效位掩碼的發(fā)送選項(xiàng) AF_DEFAULT_RADIUS ) //傳送跳數(shù),通常設(shè)置為 AF_DEFAULT_RADIUS內(nèi)容很多但非常重要,最好盡力理解后再去做,會(huì)容易很多。
end.
總結(jié)
以上是生活随笔為你收集整理的[zigbee][z-Stack]协议栈简介及工作流程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 学习记录 | ZigBee协议栈工作流程
- 下一篇: exec函数详解