Chinaunix首页 | 论坛 | 博客
  • 博客访问: 31018397
  • 博文数量: 230
  • 博客积分: 2868
  • 博客等级: 少校
  • 技术积分: 2223
  • 用 户 组: 普通用户
  • 注册时间: 2009-10-08 21:48
个人简介

Live & Learn

文章分类

全部博文(230)

文章存档

2022年(2)

2019年(5)

2018年(15)

2017年(42)

2016年(24)

2015年(13)

2014年(1)

2012年(5)

2011年(58)

2010年(56)

2009年(9)

我的朋友

分类: 其他平台

2017-02-24 11:11:09



开发环境:
硬件板卡: 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文件的代码为:

[cpp] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. #ifndef __MULTICAST__H  
  2. #define __MULTICAST__H  
  3.   
  4. #include "stm324xg_eval_sdio_sd.h"  
  5.    
  6. #include "lwip/udp.h"  
  7. #include "lwip/pbuf.h"  
  8. #include "lwip/igmp.h"  
  9.   
  10. /*port */  
  11. #define UDP_MULTICASE_RECV_PORT 1178    // multicast port for recive  
  12. #define UDP_MULTICASE_SEND_PORT 1180   // multicast port for send  
  13.   
  14. #define  LWIP_DEMO_BUF     2048  
  15.   
  16. #endif  

其中 UDP_MULTICASE_RECV_PORT 1178   是板子接收组播的端口,UDP_MULTICASE_SEND_PORT 1180  是 板子对外发送组播的端口


multicast.c的代码为:

[cpp] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. #include "multicast.h"  
  2.   
  3. struct udp_pcb* udp_server_multi_pcb;//组播PCB控制块  
  4. struct ip_addr ipgroup_rev,ipgroup_send;  
  5.   
  6. u16 lwip_demo_buf_len = 0;  
  7. u8_t lwip_demo_buf[LWIP_DEMO_BUF];  
  8.   
  9. //UDP发送  
  10. void multicast_send_data(unsigned char * data,unsigned short len)  
  11. {  
  12.     struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT,len, PBUF_RAM);  
  13.   
  14.         memcpy(p->payload, data, len);  
  15.           
  16.         udp_sendto(udp_server_multi_pcb, p,(struct ip_addr *) (&ipgroup_send),UDP_MULTICASE_SEND_PORT);//1180  
  17.           
  18.         pbuf_free(p);  
  19.        
  20. }  
  21.   
  22. //组播接收,回调函数  
  23. void udp_server_rev(void* arg,struct udp_pcb* upcb,struct pbuf* p,struct ip_addr*addr ,u16_t port){  
  24.   
  25.     int i,j;  
  26.       
  27.     if(p!=NULL){  
  28.         if((p->tot_len)>=LWIP_DEMO_BUF){         //如果长度过长则额外处理          
  29.             memcpy(lwip_demo_buf,p->payload,LWIP_DEMO_BUF);  
  30.             lwip_demo_buf_len = LWIP_DEMO_BUF;  
  31.         }else{                
  32.             memcpy(lwip_demo_buf,p->payload,p->tot_len);  
  33.             lwip_demo_buf_len = p->tot_len;  
  34.         }  
  35.       
  36.      for(i=0;itot_len;i++)//测试组播时,有时候即使没发出去也可能显示收到,因此,我这里将收到的数据加2,以作区分  
  37.         {  
  38.             printf("%02x  ",lwip_demo_buf[i]);  
  39.           lwip_demo_buf[i]=lwip_demo_buf[i]+2;  
  40.         }  
  41.   
  42.         printf("\n");   
  43.   
  44.     }   
  45. }  
  46.    
  47. void Multicast_Config()   
  48. {  
  49.         int i;   
  50.     IP4_ADDR(&ipgroup_rev, 230,1,1,11);//用于接收组播的地址  
  51.     IP4_ADDR(&ipgroup_send, 230,12,2,22);//用于发送组播的地址  
  52.       
  53.     igmp_joingroup(IP_ADDR_ANY,(struct ip_addr *)(&ipgroup_rev));//只需要将接收地址放入igmp组,发送的不需要  
  54.       
  55.       udp_server_multi_pcb = udp_new();   
  56.       
  57.     if(udp_server_multi_pcb!=NULL){  
  58.                   
  59.         udp_bind(udp_server_multi_pcb,IP_ADDR_ANY,UDP_MULTICASE_RECV_PORT);//组播接收地址1178  
  60.           
  61.         udp_recv(udp_server_multi_pcb,udp_server_rev,NULL);//  
  62.    
  63.     }  
  64.   
  65. }  
  66. //测试发送的方法   
  67. void UDP_Send()  
  68. {   
  69.        multicast_send_data(lwip_demo_buf,lwip_demo_buf_len);  
  70. }  


Step3:调用

  在主函数main.c下添加#include "multicast.h"引用
并在main()函数中的while循环的前面添加组播配置方法
Multicast_Config();
添加成功后板子就能接收组播了。
如果想测试发送,则直接调用UDP_Send()方法即可。我为了进行长时间测试,直接在stm32f4xx_it.c文件的TIM2_IRQHandler中添加了调用,这样可以使得板子每隔一秒就发送一次。该函数的代码为:
[cpp] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. void TIM2_IRQHandler(void)  
  2. {  
  3.     if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)  
  4.     {  
  5.         TIM_ClearITPendingBit(TIM2, TIM_IT_Update);  
  6.                 if(flash==1)  
  7.                 {  
  8.                     STM_EVAL_LEDOff(LED1);  
  9.                     flash=0;  
  10.                 }  
  11.                 else  
  12.                 {  
  13.                     STM_EVAL_LEDOn(LED1);  
  14.                     flash=1;  
  15.                 }  
  16.     UDP_Send();  
  17.     }  
  18.   
  19.   
  20. }  

解释

接收的持续监听是如何进行

上述代码有两点让人不太明确,也是起初一直让我迷糊的地方:1组播到底是如何接收的?2如何实现收发同步。
在《LwIP协议深度剖析与实战演练》一书的16.3给了一个组播的例子。代码如下。如果了解高级编程语言,这个例子非常易懂,但是其前提是必须配置好一个操作系统,目前常用的是FreeOST和uosII,但是在板子上配置这个系统要做很多工作,我曾花了近半个月来配置,后来发现根本不需要这么做。
[cpp] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. void lwip_demo(void *pdata)  
  2. {  
  3.  struct netconn *conn;  
  4.  struct ip_addr local_addr,group_addr,remote_addr;  
  5.   
  6.  lwip_init_task();  
  7.  EnableMacInt();  
  8.    
  9.  IP4_ADDR(&local_addr,192,168,1,37);  
  10.  IP4_ADDR(&group_addr,233,0,0,6);  
  11.  IP4_ADDR(&remote_addr,192,168,1,78);  
  12.    
  13.  conn=netconn_new(NETCONN_UDP);  
  14.   
  15.  netconn_bind(conn,NULL,9090);  
  16.  netconn_join_leave_group(conn,&group_addr,&local_addr,NETCONN_JOIN);  
  17.    
  18.  while(1)  
  19.   {  
  20.      struct netbuf *inbuf;  
  21.      inbuf = netconn_recv(conn);  
  22.      if(inbuf!=NULL)  
  23.      {  
  24.        netconn_sendto(conn,inbuf,&remote_addr,8080);  
  25.        netbuf_delete(inbuf);  
  26.      }  
  27.   }  
  28.   netconn_delete(conn);  
  29. }  
在无操作系统的环境下使用组播的关键在于前面我们提到的添加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()用来发送,具体请参考代码。

测试

我的测试如下图所示:左侧是我发送数据,右侧为接收,我的设置是接收一次后每隔一秒发送一次,因此可以看到大量的接收信息。

 完善

上述代码还有一些不足的,一个是一次不能收发比较大的数据,测试发现,一次收发1KB的字节都不行,这个我调试完成后再补充。另一个是缺少业务控制逻辑,其实这个已经很简单了,只要添加一个bool变量,接收到新数据是置为1,发送了之后置为0即可灵活地控制数据收发,进而封装到你的业务逻辑中。

感谢

虽然STM32用的比较多,LWIP也用的比较多,但是如何实现组播,网络的材料比较杂乱,含金量实在很低,梳理出上面的内容花了我很长的时间。不过,在此特别感谢,我曾两次和他交流,他为了明确方向给了很大的帮助,并且无私地将自己的代码给我看,特此表示感谢。

//代码实现

点击(此处)折叠或打开

  1. #include "includes.h"

  2. struct udp_pcb* udp_server_multi_pcb;//组播PCB控制块
  3. struct ip_addr ipgroup_rev,ipgroup_send;
  4.   
  5. u16 lwip_demo_buf_len = 0;
  6. u8_t lwip_demo_buf[LWIP_DEMO_BUF];
  7.   
  8. //UDP发送
  9. void multicast_send_data(unsigned char * data,unsigned short len)
  10. {
  11.     struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT,len, PBUF_RAM);
  12.   
  13.      memcpy(p->payload, data, len);
  14.           
  15.      udp_sendto(udp_server_multi_pcb, p,(struct ip_addr *) (&ipgroup_send),UDP_MULTICASE_SEND_PORT);//1180
  16.           
  17.      pbuf_free(p);
  18.        
  19. }
  20. unsigned char DateRlease[15]="20170224121212";
  21. int ReadSysInfo(void)
  22. {
  23.      //Get
  24.         int i;
  25.     
  26.      //1.Get UUID
  27.         for(i=0;i<4;i++)
  28.             lwip_demo_buf[2+i] = g_ucUUID[i];
  29.      //Return cmd
  30.         lwip_demo_buf[6]= 0x32;
  31.     
  32.      //IndexSum =
  33.      lwip_demo_buf[7]= 8;

  34.      //IP地址
  35.      lwip_demo_buf[8]= 1; //IP
  36.      InFlashReadBytes(FLASH_ADDR_IP,4,&lwip_demo_buf[9]);

  37.         //Mask
  38.      lwip_demo_buf[13]= 2; //IP
  39.         InFlashReadBytes(FLASH_ADDR_IP+8,4,&lwip_demo_buf[14]);

  40.     //Host
  41.         lwip_demo_buf[18] = 3;
  42.     InFlashReadBytes(FLASH_ADDR_IP+4,4,&lwip_demo_buf[19]);
  43.  

  44.         //TCP PORT
  45.         lwip_demo_buf[23] = 3;
  46.         InFlashReadBytes(FLASH_ADDR_PORT,2,&lwip_demo_buf[24]);
  47.         
  48.         //DevType
  49.         lwip_demo_buf[28] = 14;
  50.         lwip_demo_buf[29] = 1;
  51.         
  52.         //DevStype
  53.         lwip_demo_buf[30] = 15;
  54.         lwip_demo_buf[31] = 0;
  55.         
  56.         //DevVer
  57.         lwip_demo_buf[30] = 16;
  58.         lwip_demo_buf[31] = 0x01;
  59.         lwip_demo_buf[32] = 0x02;
  60.         
  61.         //Date.
  62.         lwip_demo_buf[30] = 17;
  63.         for(i=0;i<14;i++)
  64.         lwip_demo_buf[31+i] = DateRlease[i];
  65.         lwip_demo_buf[45] = 0x55;
  66.         lwip_demo_buf[46] = 0x55;
  67.         
  68.      lwip_demo_buf_len = 47;
  69.         return 47;
  70. }

  71. int SetSysInfo(void)
  72. {
  73.         int i=0,j=0;
  74.         int Cnt=0;
  75.         if(lwip_demo_buf[7]>0)
  76.         {
  77.                     Cnt = 0;
  78.                     for(i=0;i<lwip_demo_buf[7];i++)
  79.              {
  80.                          Cnt+=1; //索引值大小
  81.                             switch(lwip_demo_buf[7+Cnt])
  82.                          {
  83.                                 case 1:
  84.                                     InFlashWriteByte(FLASH_ADDR_IP,4,&lwip_demo_buf[8+Cnt]);
  85.                                  Cnt+=4;
  86.                                     break;
  87.                                 case 2:
  88.                                     InFlashWriteByte(FLASH_ADDR_IP+8,4,&lwip_demo_buf[8+Cnt]);
  89.                                  Cnt+=4;
  90.                                     break;
  91.                                 case 3:
  92.                                     InFlashWriteByte(FLASH_ADDR_IP+4,4,&lwip_demo_buf[8+Cnt]);
  93.                                  Cnt+=4;
  94.                                     break;
  95.                                 case 4:
  96.                                     InFlashWriteByte(FLASH_ADDR_PORT,2,&lwip_demo_buf[8+Cnt]);
  97.                                  Cnt+=2;
  98.                                     break;
  99.               }
  100.                     }
  101.         }
  102.          //1.Get UUID
  103.         //for(i=0;i<4;i++)
  104.         //    lwip_demo_buf[2+i] = g_ucUUID[i];
  105.      //Return cmd
  106.         lwip_demo_buf[6]= 0x34;
  107.         lwip_demo_buf[7]= 0x00;    
  108.         lwip_demo_buf[8] = 0x55;
  109.         lwip_demo_buf[9] = 0x55;
  110.         lwip_demo_buf_len = 10;
  111.         return 10;
  112. }
  113.   
  114. //组播接收,回调函数
  115. void udp_server_rev(void* arg,struct udp_pcb* upcb,struct pbuf* p,struct ip_addr*addr ,u16_t port)
  116. {
  117.   
  118.     int i,j;
  119.       
  120.     if(p!=NULL)
  121.         {
  122.         if((p->tot_len)>=LWIP_DEMO_BUF)
  123.                 { //如果长度过长则额外处理
  124.             memcpy(lwip_demo_buf,p->payload,LWIP_DEMO_BUF);
  125.             lwip_demo_buf_len = LWIP_DEMO_BUF;
  126.         }else
  127.                 {
  128.             memcpy(lwip_demo_buf,p->payload,p->tot_len);
  129.             lwip_demo_buf_len = p->tot_len;
  130.         }
  131.                 
  132.                 if(lwip_demo_buf_len>8)
  133.                 {
  134.                         if(lwip_demo_buf[0]==0xAA && lwip_demo_buf[1]==0xAA)
  135.                         {
  136.                              if(lwip_demo_buf[6]==0x31) //GetCmd
  137.                                 {
  138.                                          ReadSysInfo();
  139.                                          multicast_send_data(lwip_demo_buf,lwip_demo_buf_len);
  140.                                 }
  141.                                 else if(lwip_demo_buf[6]==0x33) //Set Cmd
  142.                                 {
  143.                                     for(i=0;i<4;i++)
  144.                                      if(lwip_demo_buf[2+i]!=g_ucUUID[i])
  145.                                         break;
  146.                                      if(i==4)
  147.                                      {
  148.                                             SetSysInfo();
  149.                                          multicast_send_data(lwip_demo_buf,lwip_demo_buf_len);
  150.                                      }
  151.                                 }
  152.                                 else if(lwip_demo_buf[6]==0x35) //reboot device
  153.                                 {
  154.                                      for(i=0;i<4;i++)
  155.                                         if(lwip_demo_buf[2+i]!=g_ucUUID[i])
  156.                                                 break;
  157.                                      if(i==4)
  158.                                      {
  159.                                              NVIC_SystemReset();
  160.                                                 while(1);
  161.                                      }
  162.                                 }
  163.                         }
  164.                 }
  165.                 
  166.                 
  167.   
  168.        // printf("\n");
  169.   
  170.     }
  171. }
  172.    
  173. void Multicast_Config(void)
  174. {
  175.     int i;
  176.     IP4_ADDR(&ipgroup_rev, 230,1,11,111);//用于接收组播的地址
  177.     IP4_ADDR(&ipgroup_send, 230,2,22,222);//用于发送组播的地址
  178.       
  179.     igmp_joingroup(IP_ADDR_ANY,(struct ip_addr *)(&ipgroup_rev));//只需要将接收地址放入igmp组,发送的不需要
  180.       
  181.     udp_server_multi_pcb = udp_new();
  182.       
  183.     if(udp_server_multi_pcb!=NULL)
  184.         {
  185.                   
  186.         udp_bind(udp_server_multi_pcb,IP_ADDR_ANY,UDP_MULTICASE_RECV_PORT);
  187.              //udp_bind(udp_server_multi_pcb,(struct ip_addr *)&ipgroup_rev,UDP_MULTICASE_RECV_PORT);//组播接收地址1178
  188.              // udp_connect(udp_server_multi_pcb,&ipgroup_send , UDP_MULTICASE_SEND_PORT);
  189.         udp_recv(udp_server_multi_pcb,udp_server_rev,NULL);//
  190.     }
  191. }


  192. //测试发送的方法
  193. void UDP_Send(void)
  194. {
  195.              int i;
  196.              for(i=0;i<20;i++)
  197.                 lwip_demo_buf[i] = 0x30 +i;
  198.      lwip_demo_buf_len = 20;
  199.        multicast_send_data(lwip_demo_buf,lwip_demo_buf_len);
  200. }

阅读(6407) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~