分别对SUN节点和PLANET节点进行说明。SUN节点流程图如图12.2
所示,节点上电是首先进行硬件及相应变量的初始化,然后创建网络,循环检测是否有数据包。如果接到数据包,对其进行解析,根据不同类型的数据包执行不同的操作。于此同时读取串口信息,如果串口有输入命令,对命令进行解析,执行不同的操作。
PLANET节点流程图如图12.3所示,PLANET节点首先进行一些初始化工作,然后申请加入网络,加网成功后,循环监测是否有数据包及按键是否被按下,如果有数据包对其进行解析,执行相应的操作,如果按键被按下,则向SUN节点发送数据包,并让LED3闪烁一次。
程序的设计基于SimpleMac协议栈进行,以下给出部分主要相关代码。该实例中的部分代码与第11章中的两节点通信实例代码相同,本章不再重复说明。
文件solar-system.c部分内容:
部分变量全局变量定义:
//负载类型定义 #define PT_SLEEPING (0x08) #define PT_LED (0x09) #define PT_TRSEND (0x0A) //数据包类型定义 #define SYN_SLEEPING_WAITTIME ((FT_DATA <<4) | (PT_SLEEPING <<0)) #define SYN_LED_WAITTIME ((FT_DATA <<4) | (PT_LED <<0)) #define TRSEND_PACKET ((FT_DATA <<4) | (PT_TRSEND <<0)) |
函数processRxPacket():
/************************************************************************** 功能描述:对接收的数据包进行解析,并执行相应的操作 输入参数:无 输出参数:无 *************************************************************************/ void processRxPacket(void) { ...... ...... …… //不同类型数据包进行不同处理 switch(packetType) { case (GENERIC_DATA_PACKET): //普通类型数据包 RX_DETAILS(printf("GENERIC_DATA_PACKET\r\n");) #ifdef SUN_ROLE halToggleLed(LED_D1); //让LED1闪烁 halCommonDelayMilliseconds(500);//延迟500ms halClearLed(LED_D1); //关闭LED1 #endif #ifdef PLANET_ROLE halToggleLed(LED_D3); //让LED3闪烁 halCommonDelayMilliseconds(500); //延迟500ms halClearLed(LED_D3); //关闭LED3 #endif rxData.lqi = calculateLqi(rxData.errors, (rxData.packet[0]+3)); //计算通信链路质量 printf("RX: Addr=0x%04X, VDD=%dmV, RxSFD=0x%05X, ", shortSrcAddr, ((rxData.packet[payloadStart+1]<<0)|(rxData.packet[payloadStart+2]<<8)), rxData.time); if(rxData.packet[payloadStart+5]&0x80) //判断数据包是否包含SFD { //获取TX SFD数据,并输出 rxData.packet[payloadStart+5] &= ~0x80; printf("TxSFD=0x%05X, ", ((rxData.packet[payloadStart+3]<< 0)| (rxData.packet[payloadStart+4]<< 8)| (rxData.packet[payloadStart+5]<<16))); } else { printf("TxSFD=-------, "); } printf("RSSI=%ddBm, LQI=0x%02X\r\n", rxData.rssi, rxData.lqi); break; #ifdef PLANET_ROLE case (SYN_SLEEPING_WAITTIME): //PT_SLEEPING类型数据包 printf("SYN_SLEEPING_WAITTIME\r\n"); halToggleLed(LED_D3); //闪烁LED3 halCommonDelayMilliseconds(1000); //延迟1000ms halClearLed(LED_D3); //关闭LED3 break; case (SYN_LED_WAITTIME): //PT_LED类型数据包 printf("SYN_LED_WAITTIME\r\n"); halToggleLed(LED_D3); //闪烁LED3 halCommonDelayMilliseconds(500); //延迟500ms halClearLed(LED_D3); //关闭LED3 halCommonDelayMilliseconds(500); halToggleLed(LED_D3); halCommonDelayMilliseconds(500); halClearLed(LED_D3); halCommonDelayMilliseconds(500); halToggleLed(LED_D3); halCommonDelayMilliseconds(500); halClearLed(LED_D3); break; #endif case (TRSEND_PACKET): //PT_TRSEND类型数据包 sendVddDataPacket(0x0000,0x0001,TRUE); //向节点1发送数据包 printf("trsend success\r\n"); break; …… …… …… default: RX_DETAILS(printf("Unknown payload type\r\n");) goto stopProcessing; } stopProcessing: rxData.packetBeingProcessed = FALSE; } |
函数sendVddDataPacket():
/************************************************************************** 功能描述:向参数中传入的地址发送类型负载类型为PT_GENERIC_DATA的数据包 输入参数:vddMillivolts为发送的16位数据,dstShortAddr为目的地址,sendDirectly为调用不同发送函数的表示符。 输出参数:无 *************************************************************************/ void sendVddDataPacket(u16 vddMillivolts,u16 dstShortAddr, boolean sendDirectly) { u8 packet[128]; //数据包封装 packet[0] = (15+2); //数据包长度 packet[1] = FCF_DATA + FCF_ACKREQ + FCF_INTRAPAN; //帧类型 packet[2] = FCF_SHORTDST + FCF_SHORTSRC; //地址类型 currSeqNum++; //数据包序列号 packet[3] = currSeqNum; packet[4] = (ST_RadioGetPanId()>>0)&0xFF; //目标PAN ID packet[5] = (ST_RadioGetPanId()>>8)&0xFF; packet[6] = (dstShortAddr>>0)&0xFF; //目标短地址 packet[7] = (dstShortAddr>>8)&0xFF; packet[8] = (ST_RadioGetNodeId()>>0)&0xFF; //源短地址 packet[9] = (ST_RadioGetNodeId()>>8)&0xFF; packet[10] = PT_GENERIC_DATA; //负载类型 packet[11] = (vddMillivolts>>0)&0xFF; //发送的16位数据 packet[12] = (vddMillivolts>>8)&0xFF; //归零Tx SFD有效负载,MSB用于指示SFD有效 packet[13] = 0; packet[14] = 0; packet[15] = 0; enqueueTxPacket(sendDirectly, dstShortAddr, packet, 15); //发送数据包 printf("send already!\r\n"); } |
函数sendVddDataPacket1():
/************************************************************************** 功能描述:向参数中传入的地址发送类型负载类型为PT_SLEEPING的数据包 输入参数:vddMillivolts为发送的16位数据,dstShortAddr为目的地址,sendDirectly为调用不同发送函数的表示符。 输出参数:无 *************************************************************************/ void sendVddDataPacket1(u16 vddMillivolts,u16 dstShortAddr,boolean sendDirectly) { u8 packet[128]; //数据包封装 packet[0] = (15+2); //数据包长度 packet[1] = FCF_DATA + FCF_ACKREQ + FCF_INTRAPAN; //帧类型 packet[2] = FCF_SHORTDST + FCF_SHORTSRC; //地址类型 currSeqNum++; //数据包序列号 packet[3] = currSeqNum; packet[4] = (ST_RadioGetPanId()>>0)&0xFF; //目标PAN ID packet[5] = (ST_RadioGetPanId()>>8)&0xFF; packet[6] = (dstShortAddr>>0)&0xFF; //目标短地址 packet[7] = (dstShortAddr>>8)&0xFF; packet[8] = (ST_RadioGetNodeId()>>0)&0xFF; //源短地址 packet[9] = (ST_RadioGetNodeId()>>8)&0xFF; packet[10] = PT_SLEEPING; //负载类型 packet[11] = (vddMillivolts>>0)&0xFF; //发送的16位数据 packet[12] = (vddMillivolts>>8)&0xFF; //归零Tx SFD有效负载,MSB用于指示SFD有效 packet[13] = 0; packet[14] = 0; packet[15] = 0; enqueueTxPacket(sendDirectly, dstShortAddr, packet, 15); //发送数据包 #ifdef SUN_ROLE halToggleLed(LED_D2); //LED2闪烁 halCommonDelayMilliseconds(500); //延时500ms halClearLed(LED_D2); #endif printf("send sleeping packet to every planet!\r\n"); } |
函数sendVddDataPacket2():
/************************************************************************** 功能描述:向参数中传入的地址发送类型负载类型为PT_LED的数据包 输入参数:vddMillivolts为发送的16位数据,dstShortAddr为目的地址,sendDirectly为调用不同发送函数的表示符。 输出参数:无 *************************************************************************/ void sendVddDataPacket2(u16 vddMillivolts,u16 dstShortAddr,boolean sendDirectly) { u8 packet[128]; //数据包封装 packet[0] = (15+2); //数据包长度 packet[1] = FCF_DATA + FCF_ACKREQ + FCF_INTRAPAN; //帧类型 packet[2] = FCF_SHORTDST + FCF_SHORTSRC; //地址类型 currSeqNum++; //数据包序列号 packet[3] = currSeqNum; packet[4] = (ST_RadioGetPanId()>>0)&0xFF; //目标PAN ID packet[5] = (ST_RadioGetPanId()>>8)&0xFF; packet[6] = (dstShortAddr>>0)&0xFF; //目标短地址 packet[7] = (dstShortAddr>>8)&0xFF; packet[8] = (ST_RadioGetNodeId()>>0)&0xFF; //源短地址 packet[9] = (ST_RadioGetNodeId()>>8)&0xFF; packet[10] = PT_LED; //负载类型 packet[11] = (vddMillivolts>>0)&0xFF; //发送的16位数据 packet[12] = (vddMillivolts>>8)&0xFF; //归零Tx SFD有效负载,MSB用于指示SFD有效 packet[13] = 0; packet[14] = 0; packet[15] = 0; enqueueTxPacket(sendDirectly, dstShortAddr, packet, 15); //发送数据包 #ifdef SUN_ROLE halToggleLed(LED_D1); //LED1闪烁 halCommonDelayMilliseconds(500); //延时500ms halClearLed(LED_D1); #endif printf("send message to every planet!\r\n"); } |
函数sendVddDataPacket3():
/************************************************************************** 功能描述:向参数中传入的地址发送类型负载类型为PT_TRSEND的数据包 输入参数:vddMillivolts为发送的16位数据,dstShortAddr为目的地址,sendDirectly为调用不同发送函数的表示符。 输出参数:无 *************************************************************************/ void sendVddDataPacket3(u16 vddMillivolts,u16 dstShortAddr,boolean sendDirectly) { u8 packet[128]; //数据包封装 packet[0] = (15+2); //数据包长度 packet[1] = FCF_DATA + FCF_ACKREQ + FCF_INTRAPAN; //帧类型 packet[2] = FCF_SHORTDST + FCF_SHORTSRC; //地址类型 currSeqNum++; //数据包序列号 packet[3] = currSeqNum; packet[4] = (ST_RadioGetPanId()>>0)&0xFF; //目标PAN ID packet[5] = (ST_RadioGetPanId()>>8)&0xFF; packet[6] = (dstShortAddr>>0)&0xFF; //目标短地址 packet[7] = (dstShortAddr>>8)&0xFF; packet[8] = (ST_RadioGetNodeId()>>0)&0xFF; //源短地址 packet[9] = (ST_RadioGetNodeId()>>8)&0xFF; packet[10] = PT_TRSEND; //负载类型 packet[11] = (vddMillivolts>>0)&0xFF; //发送的16位数据 packet[12] = (vddMillivolts>>8)&0xFF; //归零Tx SFD有效负载,MSB用于指示SFD有效 packet[13] = 0; packet[14] = 0; packet[15] = 0; enqueueTxPacket(sendDirectly, dstShortAddr, packet, 15); //发送数据包 #ifdef PLANET_ROLE halToggleLed(LED_D3); //LED3闪烁 halCommonDelayMilliseconds(500); //延迟500ms halToggleLed(LED_D3); halCommonDelayMilliseconds(500); #endif printf("trsend already!\r\n"); } |
函数planetTableCmd():
/************************************************************************** 功能描述:实现列出SUN节点的子节点信息,当SUN节点收到命令’t’后执行此函数 输入参数:无 输出参数:无 *************************************************************************/ void planetTableCmd(void) { u8 i,k; printf("\r\n"); if(!activeInNetwork) { printf("Not active in a network\r\n"); return; } if(ST_RadioGetNodeId() != 0x0000) { printf("Not a sun\r\n"); return; } printf("Planet Table\r\n"); printf("Active | DataPending | Short Address | Long Address\r\n"); for(i=0;i<PLANET_TABLE_SIZE;i++) //输出子节点信息 { printf(" %d | ", planetTable[i].active); printf(" %d | ", isDataPendingForShortId(planetTable[i].shortAddr)); printf(" 0x%04X | 0x", planetTable[i].shortAddr); k=8; while(k--) { printf("%02X", planetTable[i].longAddr[k]); } printf("\r\n"); } } |
函数main():
/************************************************************************** 功能描述:主函数,实现节点硬件初始化,及节点功能的实现 输入参数:无 输出参数:无 *************************************************************************/ int main(void) { u32 seed; StStatus status = ST_SUCCESS; halInit(); //初始化硬件 uartInit(115200, 8, PARITY_NONE, 1); //初始化UART //配置PA4和PA4引脚为复用功能,用于数据包的跟踪 halGpioConfig(PORTA_PIN(4),GPIOCFG_OUT_ALT); halGpioConfig(PORTA_PIN(5),GPIOCFG_OUT_ALT); GPIO_IRQDSEL = PORTB_PIN(2); //将IRQD连接到PB2/SC1RXD //允许IRQD标志位激活任何的IRQD GPIO_INTCFGD = (3<<GPIO_INTMOD_BIT); INT_GPIOFLAG = INT_IRQDFLAG; INT_PENDCLR = INT_IRQD; INTERRUPTS_ON(); #ifdef PLANET_ROLE halInitLed(); //初始化LED灯 halInitButton(); //初始化按键 printf("\r\nSimpleMAC (%s) Sample Application: ‘Planet role‘!\r\n", SIMPLEMAC_VERSION_STRING); #endif #ifdef SUN_ROLE halInitLed(); //初始化LED灯 halInitButton(); //初始化按键 printf("\r\nSimpleMAC (%s) Sample Application: ‘Sun role‘!\r\n", SIMPLEMAC_VERSION_STRING); #endif //生成随机种子 ST_RadioGetRandomNumbers((u16 *)&seed, 2); halCommonSeedRandom(seed); //初始化无线射频模块 ST_RadioEnableOverflowNotification(TRUE); status = ST_RadioInit(radioPowerState); assert(status==ST_SUCCESS); printf("Enter ? for list of commands\r\n"); printf("\r\n> "); //SUN节点操作部分 #ifdef SUN_ROLE u8 ch; formCmd(); //SUN节点首先创建网络 while(TRUE) { do { processRxPacket();//检测收到数据包并处理 halCommonDelayMilliseconds(10);//延时10毫秒 }while ((!__io_getcharNonBlocking(&ch))); //获取串口发送的命令 if(ch==‘s‘) //命令s,向第一个子节点发送数据包 { printf("s command is running!\n"); u16 dstShortAddr=planetTable[0].shortAddr;//第一个子节点地址 sendVddDataPacket(0x0000,dstShortAddr,TRUE);//向第一个子节点发送数据包 } else if(ch==‘b‘) //命令b,发送广播数据 { printf("b command is running!\n"); u16 dstShortAddr=0xffff;//将此处的地址改为0xffff,即广播地址 sendVddDataPacket(0x0000,dstShortAddr,TRUE); } else if(ch==‘d‘) //命令d,依次向所有节点发送LED闪烁数据包 { printf("d command is running!\n"); u8 cl1=PLANET_TABLE_SIZE;//定义节点总数 u8 m1=0;//定义临时计数变量m1 u16 base1=10000;//定义等待基时间 //定义存储每个节点等待时间的数组 u16 led_time[PLANET_TABLE_SIZE]; for(m1=0;m1<PLANET_TABLE_SIZE;m1++) led_time[m1]= (cl1-m1)*1000+base1; //给每个节点等待时间赋值 u8 i=0;//定义临时计数变量i for(i=0;i<PLANET_TABLE_SIZE;i++) { //遍历planetTabl中的每个PLANET节点并发送信息 if(planetTable[i].active) { sendVddDataPacket2(led_time[i],planetTable[i].shortAddr,TRUE); halCommonDelayMilliseconds(500);//延迟500ms } } } else if(ch==‘l‘) //命令l,依次向所有节点发送睡眠数据包 { u8 cl=PLANET_TABLE_SIZE; u8 m=0; u16 base=10000; u16 sleep_time[PLANET_TABLE_SIZE]; for(m=0;m<PLANET_TABLE_SIZE;m++) sleep_time[m]= (cl-m)*1000+base; //睡眠时间 u8 i=0; for(i=0;i<PLANET_TABLE_SIZE;i++) { if(planetTable[i].active) { //向Planet节点发送睡眠数据包 sendVddDataPacket1(sleep_time[i],planetTable[i].shortAddr,TRUE); } } } else if(ch==‘t‘) //命令t { planetTableCmd(); //列出子节点信息 } else if(ch==‘?‘) //命令?,列出当前支持命令 { printf("s send message to the first PLANET\n"); printf("d send message to every PLANET one by one\n"); printf("b broadcast\n"); printf("l syn sleep\n"); printf("t PlanetTable\n"); printf("? help command\n"); } else printf("Unknown Commamd\r\n"); INT_GPIOFLAG = INT_IRQDFLAG; INT_PENDCLR = INT_IRQD; } #endif //PLANET节点操作部分 #ifdef PLANET_ROLE activeInNetwork = FALSE; do { joinCmd(); //请求加入网络 }while(!activeInNetwork); while(TRUE) { processRxPacket(); //处理接收数据包 halCommonDelayMilliseconds(10); //延时10ms //如果S3被按下,向Sun节点发送信息 if(halGetButtonStatus(BUTTON_S3) == BUTTON_PRESSED) { sendVddDataPacket3(0x0001, 0x0000, TRUE); //向SUN节点发送数据包 halCommonDelayMilliseconds(400); //延迟400ms } INT_GPIOFLAG = INT_IRQDFLAG; INT_PENDCLR = INT_IRQD; } #endif } 本文参考《STM32W108嵌入式无线传感器网络》邱铁,夏锋,周玉编著.清华大学出版社,2014年5月 |