本文基於802.15.4/ZigBee的SimpleMac協議棧編寫程序,實現兩個STM32W108無線節點之間的通信。節點分爲SUN節點和PLANET節點,SUN節點使用STM32W108無線開發板,PLANET節點使用STM32W108無線節點,SUN節點可與PC機進行通信。
程序設計與實現
程序的設計基於SimpleMac協議棧進行,根據官方提供的MAC協議棧示例代碼進行的裁剪更改,第10章已對協議棧代碼進行了解析,在此就不詳細說明,以下只給出部分主要相關代碼。
文件solar-system.c部分內容:
部分全局變量定義:
//負載類型 #define PT_LED (0x09) #define PT_TRSEND (0x0A) #define PT_GENERIC_DATA8 (0x0B) #define PT_GENERIC_DATA32 (0x0C)
//數據包類型 #define GENERIC_DATA8_PACKET ((FT_DATA <<4) | (PT_GENERIC_DATA8 <<0)) #define GENERIC_DATA32_PACKET ((FT_DATA <<4) | (PT_GENERIC_DATA32 <<0)) #define LED_PACKET ((FT_DATA <<4) | (PT_LED <<0)) #define TRSEND_PACKET ((FT_DATA <<4) | (PT_TRSEND <<0)) |
函數processRxPacket():
/************************************************************************** 功能描述:對接收的數據包進行解析解碼處理,並根據不同類型的數據包執行不同的操作,數據包信息通過數據包回調函數保存在結構體變量rxData中 輸入參數:無 輸出參數:無 ***************************************************************************/ void processRxPacket(void) { …... //不同的數據包類型,不同的處理 switch(packetType) { case (GENERIC_DATA_PACKET): //普通類型數據包 RX_DETAILS(printf("GENERIC_DATA_PACKET\r\n");) #ifdef SUN_ROLE case (LED_PACKET): //PT_LED數據包 printf("Message from my PLANET\r\n"); halSetLed(LED_D1); //點亮LED halCommonDelayMilliseconds(500);//延時500ms halClearLed(LED_D1); break; #endif case (SUN_SEARCH_PACKET): //處理搜索父節點的數據包 RX_DETAILS(printf("SUN_SEARCH_PACKET\r\n");) for(i=0;i<PLANET_TABLE_SIZE;i++) //掃描子節點數組 { if(!planetTable[i].active) //判斷是否有有效空間 { packet[0] = (24+2); //數據包長度 packet[1] = FCF_DATA; //幀類型 packet[2] = FCF_LONGDST + FCF_LONGSRC; //地址類型 currSeqNum++; //數據包序列號 packet[3]=currSeqNum; packet[4] = (0xFFFF>>0)&0xFF; //16位短目標地址 packet[5] = (0xFFFF>>8)&0xFF; memcpy((packet+6), longSrcAddr, 8); //64位長目標地址 packet[14] = (ST_RadioGetPanId()>>0)&0xFF; //16位源PAN ID packet[15] = (ST_RadioGetPanId()>>8)&0xFF; memcpy((packet+16), ST_RadioGetEui64(), 8); //64位長源地址 packet[24] = PT_SUN_AVAILABLE; //負載類型 enqueueTxPacket(TRUE, 0xFFFF, packet, 0); //廣播 break; } } break; case (SUN_AVAILABLE_PACKET): //SUN節點發送的父節點可用數據包 RX_DETAILS(printf("SUN_AVAILABLE_PACKET\r\n");) if(availableSunFound) //如果已加入網絡,則停止父節點搜索 { return; } if(srcPanId!=MyPANID) //如果PAN ID不同,則不處理此數據包 { goto stopProcessing; } availableSunFound=TRUE; ST_RadioSetPanId(srcPanId); //設置節點PAN ID
packet[0] = (22+2); //數據包長度 packet[1] = FCF_DATA + FCF_ACKREQ + FCF_INTRAPAN; //幀類型 packet[2] = FCF_LONGDST + FCF_LONGSRC; //地址類型 currSeqNum++; //數據包序列號 packet[3]=currSeqNum; packet[4] = (ST_RadioGetPanId()>>0)&0xFF; //16位短目標地址 packet[5] = (ST_RadioGetPanId()>>8)&0xFF; memcpy((packet+6), longSrcAddr, 8); //64位長目標地址 memcpy((packet+14), ST_RadioGetEui64(), 8); //64位長源地址 packet[22] = PT_JOIN_REQUEST; //負載類型 enqueueTxPacket(TRUE, 0xFFFF, packet, 0); //單播發送請求加入網絡數據包 break;
case (JOIN_REQUEST_PACKET): //SUN節點收到請求加網的包 RX_DETAILS(printf("JOIN_REQUEST_PACKET\r\n");) //串口終端顯示 { u8 flag=0; u8 pt = PT_JOIN_DENIED; //負載類型 u8 assignedShortId[2] = {0xFE, 0xFF}; packet[0] = (24+2); //數據包長度 packet[1] = FCF_DATA + FCF_ACKREQ + FCF_INTRAPAN; //幀類型 packet[2] = FCF_LONGDST + FCF_LONGSRC; //地址類型 currSeqNum++; //數據包序列號 packet[3]=currSeqNum; packet[4] = (ST_RadioGetPanId()>>0)&0xFF; //16位短目標地址 packet[5] = (ST_RadioGetPanId()>>8)&0xFF; memcpy((packet+6), longSrcAddr, 8); //64位長目標地址 memcpy((packet+14), ST_RadioGetEui64(), 8); //64位長源地址
/*搜尋表中是否存在與加網節點相同的64位長地址,如果有則覆蓋,若沒有則繼續遍歷表*/ for(i=0;i<PLANET_TABLE_SIZE;i++) { u8 k=0; while(k<8) { if(planetTable[i].longAddr[k]!=rxData.packet[14+k]) break; k++; } if(k==8) { planetTable[i].active = TRUE; shortAddrCounter++; planetTable[i].shortAddr = shortAddrCounter; pt = PT_JOIN_ACCEPTED; //允許加入網絡負載類型 assignedShortId[0] = (shortAddrCounter>>0)&0xFF; assignedShortId[1] = (shortAddrCounter>>8)&0xFF; printf("Join: Planet 0x%04X (index %d) has joined the network\r\n", shortAddrCounter, i); flag=1; break; } }
if(flag==0) //如果沒有找到相同長地址,則查找空缺位置加進去 { for(i=0;i<PLANET_TABLE_SIZE;i++) if(!planetTable[i].active) { planetTable[i].active = TRUE; shortAddrCounter++; planetTable[i].shortAddr = shortAddrCounter; memcpy(planetTable[i].longAddr, longSrcAddr, 8); pt = PT_JOIN_ACCEPTED; //允許加入網絡負載類型 assignedShortId[0] = (shortAddrCounter>>0)&0xFF; assignedShortId[1] = (shortAddrCounter>>8)&0xFF; printf("Join: Planet 0x%04X (index %d) has joined the network\r\n", shortAddrCounter, i); break; } } packet[22] = pt; //負載類型 packet[23] = assignedShortId[0]; packet[24] = assignedShortId[1]; enqueueTxPacket(TRUE, 0xFFFF, packet, 0); //單播迴應 } break; case (JOIN_ACCEPTED_PACKET): //PLANET節點處理加入網絡允許數據包 RX_DETAILS(printf("JOIN_ACCEPTED_PACKET\r\n");) ST_RadioSetNodeId((rxData.packet[payloadStart+1]<<0)| (rxData.packet[payloadStart+2]<<8)); //設置NodeID networkJoinedStopSearching = TRUE; //加入網絡成功,停止搜索 break; case (JOIN_DENIED_PACKET): //PLANET節點處理加入網絡拒絕數據包 RX_DETAILS(printf("JOIN_DENIED_PACKET\r\n");) ST_RadioSetPanId(0xFFFF); //重設PAN ID break; …… …… default: RX_DETAILS(printf("Unknown payload type\r\n");) goto stopProcessing; } stopProcessing: rxData.packetBeingProcessed = FALSE; } |
函數joinCmd():
/************************************************************************** 功能描述:PLANET節點執行請求加入網絡的操作,循環11~26信道,分別發送PT_SUN_PACKET類型數據包 輸入參數:無 輸出參數:無 *************************************************************************/ void joinCmd(void) { u8 packet[128]; u8 searchChannel; u32 lastTime; StStatus status = ST_SUCCESS; printf("\r\n"); if(activeInNetwork) { printf("Already in network\r\n"); return; } printf("Inactive node joining network and becoming a planet\r\n"); initNetworkState(); //初始化網絡狀態 TURN_RADIO_ON(); //打開無線 activeInNetwork = TRUE; //加入網絡成功,如果加入網絡失敗,會重新設置爲FALSE
//構造發送請求加入網絡的數據包 packet[0] = (18+2); packet[1] = FCF_DATA; //幀類型 packet[2] = FCF_SHORTDST + FCF_LONGSRC; //地址類型 packet[4] = (0xFFFF>>0)&0xFF; //目標PAN ID packet[5] = (0xFFFF>>8)&0xFF; packet[6] = (0xFFFF>>0)&0xFF; //目標Node ID packet[7] = (0xFFFF>>8)&0xFF; packet[8] = (0xFFFF>>0)&0xFF; //源PAN ID packet[9] = (0xFFFF>>8)&0xFF; memcpy((packet+10), ST_RadioGetEui64(), 8); //64位長地址 packet[18] = PT_SUN_SEARCH; //負載類型
printf("Trying channel"); //循環搜索所有信道 for(searchChannel=ST_MIN_802_15_4_CHANNEL_NUMBER; searchChannel<=ST_MAX_802_15_4_CHANNEL_NUMBER; searchChannel++) { //信道搜索過程中會延遲200ms,所以每次都需要重置看門狗,防止觸發復位 halResetWatchdog(); printf(" %d", searchChannel); status = ST_RadioSetChannel(searchChannel); assert(status==ST_SUCCESS);
currSeqNum++; //數據包序列號 packet[3]=currSeqNum;
availableSunFound = FALSE; enqueueTxPacket(TRUE, 0xFFFF, packet, 0); //廣播搜索父節點
//延遲200ms等待回覆 lastTime = halCommonGetInt32uMillisecondTick(); do { processRxPacket(); //處理接收的數據包 txTick(); } while(elapsedTimeInt32u(lastTime, halCommonGetInt32uMillisecondTick())<200);
//判斷是否加入網絡成功 if(networkJoinedStopSearching) { printf("\r\n"); printf("Joined on channel %d with PAN ID 0x%04X. My ID is now 0x%04X.\r\n", ST_RadioGetChannel(), ST_RadioGetPanId(), ST_RadioGetNodeId()); activeInNetwork = TRUE; #ifdef PLANET_ROLE autoSendRate = 60; //設置子節點向父節點數據包發送週期 halSetLed(LED_D4); //加入網絡成功,點亮LED4 #endif return; } } printf("\r\n"); printf("Did not join. Returning to inactive state.\r\n"); activeInNetwork = FALSE; //加入網絡失敗 } |
本文出自《STM32W108嵌入式無線傳感器網絡》邱鐵,夏鋒,周玉編著.清華大學出版社,2014年5月