SM32W108無線射頻模塊多個節點之間通信實例中分別對SUN節點和PLANET節點進行說明。節點上電是首先進行硬件及相應變量的初始化,然後創建網絡,循環檢測是否有數據包。如果接到數據包,對其進行解析,根據不同類型的數據包執行不同的操作。於此同時讀取串口信息,如果串口有輸入命令,對命令進行解析,執行不同的操作。
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月 |