天天看點

STM32F4闆子使用LWIP進行多點傳播收發資料的完整過程,附代碼Step1.首先配置LWIP,啟動多點傳播功能Step2:編寫多點傳播實作代碼Step3:調用解釋測試 完善感謝

開發環境:

硬體闆卡: STM32F427ZITx

網卡:DP83848

LWIP版本:V3.2

最近要使用LWIP協定實作多點傳播,由于此前并不知道這個闆卡,對嵌入式也不算熟悉,平時還有其他的任務,前前後後忙了近兩個月,後來發現實作過程非常簡單,下面介紹我的實作過程。

首先,使用STM32F4時不用完全自己搭建環境的,到官網下載下傳類似于stsw-stm32070.zip的檔案包,裡面包含了多個典型的Keil uVision工程,配置一下就能用。這個過程改天再寫個文檔。

在Kei工程中配置多點傳播的過程可以分為三步:配置LWIP,啟動多點傳播;編寫多點傳播實作代碼;調用。

Step1.首先配置LWIP,啟動多點傳播功能

修改lwipopts.h 文

#define LWIP_IGMP                       1

在lwip初始化後調用

2.配置多點傳播計時器

在 netconf.c檔案靠前的位置添加變量,大約在該檔案的第70行左右,代碼如下所示。

#ifdef LWIP_IGMP

uint32_t  IGMPTimer=0;

#endif

然後在該檔案的#ifdef USE_DHCP前面,大約在第180行的位置添加igmp計時器調用代碼:

 #if LWIP_IGMP

if(localtime-IGMPTimer>=IGMP_TMR_INTERVAL)

  {

IGMPTimer=localtime;

    igmp_tmr();

  }

 #endif

編譯,如果能正确編譯,并且在編譯過程中看到編譯igmp.c檔案,說明配置基本成功了。

Step2:編寫多點傳播實作代碼

添加兩個檔案,multicast.h和multicast.c檔案。廢話少說,先上代碼:

multicast.h檔案的代碼為:

#ifndef __MULTICAST__H
#define __MULTICAST__H

#include "stm324xg_eval_sdio_sd.h"
 
#include "lwip/udp.h"
#include "lwip/pbuf.h"
#include "lwip/igmp.h"

/*port */
#define UDP_MULTICASE_RECV_PORT 1178    // multicast port for recive
#define UDP_MULTICASE_SEND_PORT 1180   // multicast port for send

#define  LWIP_DEMO_BUF     2048

#endif
           

其中  UDP_MULTICASE_RECV_PORT 1178   是闆子接收多點傳播的端口, UDP_MULTICASE_SEND_PORT 1180  是 闆子對外發送多點傳播的端口

multicast.c的代碼為:

#include "multicast.h"

struct udp_pcb* udp_server_multi_pcb;//多點傳播PCB控制塊
struct ip_addr ipgroup_rev,ipgroup_send;

u16 lwip_demo_buf_len = 0;
u8_t lwip_demo_buf[LWIP_DEMO_BUF];

//UDP發送
void multicast_send_data(unsigned char * data,unsigned short len)
{
  	struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT,len, PBUF_RAM);

		memcpy(p->payload, data, len);
		
		udp_sendto(udp_server_multi_pcb, p,(struct ip_addr *) (&ipgroup_send),UDP_MULTICASE_SEND_PORT);//1180
		
		pbuf_free(p);
	 
}

//多點傳播接收,回調函數
void udp_server_rev(void* arg,struct udp_pcb* upcb,struct pbuf* p,struct ip_addr*addr ,u16_t port){

	int i,j;
	
	if(p!=NULL){
		if((p->tot_len)>=LWIP_DEMO_BUF){         //如果長度過長則額外處理        
			memcpy(lwip_demo_buf,p->payload,LWIP_DEMO_BUF);
			lwip_demo_buf_len = LWIP_DEMO_BUF;
		}else{				
			memcpy(lwip_demo_buf,p->payload,p->tot_len);
			lwip_demo_buf_len = p->tot_len;
		}
  	
	 for(i=0;i<p->tot_len;i++)//測試多點傳播時,有時候即使沒發出去也可能顯示收到,是以,我這裡将收到的資料加2,以作區分
		{
			printf("%02x  ",lwip_demo_buf[i]);
		  lwip_demo_buf[i]=lwip_demo_buf[i]+2;
		}

		printf("\n"); 

	} 
}
 
void Multicast_Config()	
{
		int i; 
   	IP4_ADDR(&ipgroup_rev, 230,1,1,11);//用于接收多點傳播的位址
    IP4_ADDR(&ipgroup_send, 230,12,2,22);//用于發送多點傳播的位址
	
    igmp_joingroup(IP_ADDR_ANY,(struct ip_addr *)(&ipgroup_rev));//隻需要将接收位址放入igmp組,發送的不需要
	
	  udp_server_multi_pcb = udp_new();	
	
	if(udp_server_multi_pcb!=NULL){
				
		udp_bind(udp_server_multi_pcb,IP_ADDR_ANY,UDP_MULTICASE_RECV_PORT);//多點傳播接收位址1178
		
		udp_recv(udp_server_multi_pcb,udp_server_rev,NULL);//
 
	}

}
//測試發送的方法 
void UDP_Send()
{ 
 	   multicast_send_data(lwip_demo_buf,lwip_demo_buf_len);
}
           

Step3:調用

  在主函數main.c下添加#include "multicast.h"引用

并在main()函數中的while循環的前面添加多點傳播配置方法

Multicast_Config();

添加成功後闆子就能接收多點傳播了。

如果想測試發送,則直接調用UDP_Send()方法即可。我為了進行長時間測試,直接在stm32f4xx_it.c檔案的TIM2_IRQHandler中添加了調用,這樣可以使得闆子每隔一秒就發送一次。該函數的代碼為:

void TIM2_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
    {
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
				if(flash==1)
				{
					STM_EVAL_LEDOff(LED1);
					flash=0;
				}
				else
				{
					STM_EVAL_LEDOn(LED1);
					flash=1;
				}
    UDP_Send();
    }


}
           

解釋

接收的持續監聽是如何進行

上述代碼有兩點讓人不太明确,也是起初一直讓我迷糊的地方:1多點傳播到底是如何接收的?2如何實作收發同步。 在《LwIP協定深度剖析與實戰演練》一書的16.3給了一個多點傳播的例子。代碼如下。如果了解進階程式設計語言,這個例子非常易懂,但是其前提是必須配置好一個作業系統,目前常用的是FreeOST和uosII,但是在闆子上配置這個系統要做很多工作,我曾花了近半個月來配置,後來發現根本不需要這麼做。

void lwip_demo(void *pdata)
{
 struct netconn *conn;
 struct ip_addr local_addr,group_addr,remote_addr;

 lwip_init_task();
 EnableMacInt();
 
 IP4_ADDR(&local_addr,192,168,1,37);
 IP4_ADDR(&group_addr,233,0,0,6);
 IP4_ADDR(&remote_addr,192,168,1,78);
 
 conn=netconn_new(NETCONN_UDP);

 netconn_bind(conn,NULL,9090);
 netconn_join_leave_group(conn,&group_addr,&local_addr,NETCONN_JOIN);
 
 while(1)
  {
	 struct netbuf *inbuf;
	 inbuf = netconn_recv(conn);
	 if(inbuf!=NULL)
	 {
       netconn_sendto(conn,inbuf,&remote_addr,8080);
       netbuf_delete(inbuf);
     }
  }
  netconn_delete(conn);
}
           

在無作業系統的環境下使用多點傳播的關鍵在于前面我們提到的 添加 igmp_tmr()的調用,它能保證闆子可以在一定的周期内監聽網絡上發來的多點傳播流。如果你對嵌入式程式設計很熟悉,這一點不難了解,如果像我一樣習慣了進階程式設計語言的風格,會一直想不明白。

 如何實作收發同步進行

上面解決了接收的問題,那如何發送呢?并且如何保證收發能同步進行呢?其實發送更為簡單,隻要你配置好UDP環境,将單點傳播位址換成多點傳播位址就能發送,這裡的關鍵在于Multicast_Config()中的配置。

開始我一直在想是否需要寫兩個配置函數,一個同僚堅持認為應該建立兩個pcb,一個用于收,一個用于發,但是測試發現根本不需要,隻要為pcb添加兩個多點傳播位址就行了。 IP4_ADDR(&ipgroup_rev, 230,1,1,11);用來配置接收多點傳播的位址,為了能收到資料,還需要将其添加到igmp_joingroup組中。然後将其與udp_server_multi_pcb綁定,最後在回調函數中執行。這幾個函數包涵了大量的底層操作,感興趣的話可以看看LWIP的源代碼。 IP4_ADDR(&ipgroup_send, 230,12,2,22)用來配置發送多點傳播的位址,multicast_send_data()用來發送,具體請參考代碼。

測試

我的測試如下圖所示:左側是我發送資料,右側為接收,我的設定是接收一次後每隔一秒發送一次,是以可以看到大量的接收資訊。

STM32F4闆子使用LWIP進行多點傳播收發資料的完整過程,附代碼Step1.首先配置LWIP,啟動多點傳播功能Step2:編寫多點傳播實作代碼Step3:調用解釋測試 完善感謝

 完善

上述代碼還有一些不足的,一個是一次不能收發比較大的資料,測試發現,一次收發1KB的位元組都不行,這個我調試完成後再補充。另一個是缺少業務控制邏輯,其實這個已經很簡單了,隻要添加一個bool變量,接收到新資料是置為1,發送了之後置為0即可靈活地控制資料收發,進而封裝到你的業務邏輯中。

感謝

雖然STM32用的比較多,LWIP也用的比較多,但是如何實作多點傳播,網絡的材料比較雜亂,含金量實在很低,梳理出上面的内容花了我很長的時間。不過,在此特别感謝lutao614http://bbs.csdn.net/topics/391894501,我曾兩次和他交流,他為了明确方向給了很大的幫助,并且無私地将自己的代碼給我看,特此表示感謝。

繼續閱讀