SM32W108无线射频模块两节点之间通信实例
本文基于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; //加入网络失败 } |