[其他ST产品] stm32f207实现组播

[复制链接]
 楼主| SHOPQQ 发表于 2023-7-26 18:50 | 显示全部楼层 |阅读模式
背景
第一次使用lwip,在网上看了很多文章,一步步实现了freeRtos与lwip的移植,关于freeRtos与lwip的移植已经有很多可以参考的文章了,就不赘述了。这里主要讲讲我如何实现组播的,因为遇到一些坑,并且如何解决的。

实现组播的流程
第一步、配置LWIP
在lwipopts.h中,将LWIP_IGMP改为1,使能组播代码的编译。

  1. /* ---------- IGMP options ---------- */
  2. #define LWIP_IGMP                       1

第二步、编写组播实现代码

此前没有用过lwip,不会使用netconn。所幸lwip实现了socket接口。socket实现组播,网上有很多文章可以学习,这里和是否使用lwip没关系。…


 楼主| SHOPQQ 发表于 2023-7-26 18:50 | 显示全部楼层
实现组播接收
  1. #define MULTICAST_ADDR "239.2.1.22"
  2. #define UDP_RCV_PORT 1234

  3. typedef struct{
  4.         int fd;
  5.         struct sockaddr_in sockServer;
  6.         struct sockaddr_in sockClient;
  7.         uint8_t rcvData[1024];
  8.         int rcvLen;
  9. }SocketInfo_ts;

  10. SocketInfo_ts udpClient;
  11. //create a udp socket
  12. int iCreatUdpClient(SocketInfo_ts* udpSock)
  13. {
  14.     struct ip_mreq group;
  15.    
  16.     if((udpSock->fd=socket(AF_INET,SOCK_DGRAM,0))<0)
  17.     {
  18.         return 0;
  19.     }
  20.    
  21.     udpSock->sockClient.sin_family=AF_INET;
  22.     udpSock->sockClient.sin_port=htons(UDP_RCV_PORT);
  23.     udpSock->sockClient.sin_addr.s_addr=htonl(INADDR_ANY);
  24.     if(bind(udpSock->fd,(struct sockaddr*)&udpSock->sockClient,sizeof(struct sockaddr_in))<0)
  25.     {
  26.         return 0;
  27.     }
  28.        
  29.         group.imr_interface.s_addr=htons(INADDR_ANY);
  30.         group.imr_multiaddr.s_addr=inet_addr(MULTICAST_ADDR);
  31.         setsockopt(udpSock->fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,(char *)&group,sizeof(group));
  32.        
  33.         return 1;
  34. }
  35. void vUdpThread(void* argument)
  36. {
  37.     uint32_t fromSize;
  38.     fd_set fdSockSet;
  39.     struct timeval timeout;
  40.    
  41.     //init udpClient
  42.     iCreatUdpClient(&udpClient);
  43.     for(;;)
  44.     {
  45.                 FD_ZERO(&fdSockSet);
  46.                 FD_SET(udpClient.fd,&fdSockSet);
  47.                
  48.                 timeout.tv_sec=1;
  49.                 timeout.tv_usec=0;
  50.                 if(select(udpClient.fd+1,&fdSockSet,NULL,NULL,&timeout)>0)
  51.                 {
  52.                         if(FD_ISSET(udpClient.fd,&fdSockSet))
  53.                         {
  54.                                 fromSize=sizeof(struct sockaddr_in);
  55.                                 udpClient.rcvLen=recvfrom(udpClient.fd,udpClient.rcvData,1024,0,(struct sockaddr*)&udpClient.sockServer,&fromSize);
  56.                         }
  57.                 }
  58.     }
  59. }

  60. /*-----------------------------------------------------------------------------------*/
  61. void vUdpInit(void)
  62. {
  63.     sys_thread_new("udp_thread", vUdpThread, NULL, DEFAULT_THREAD_STACKSIZE,UDP_THREAD_PRIO );
  64. }
 楼主| SHOPQQ 发表于 2023-7-26 18:51 | 显示全部楼层
实现组播发送
这里和普通发送差不多,只要将目的地址改为组播地址就行。

第三步、编译验证
编译没什么问题,有问题就改改,然后通过。网络调试助手用起来……
结果当然是失败了,看来没那么简单,啪啪打脸。
接着只能自己一步步查了,程序通过setsockopt(udpSock->fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,(char *)&group,sizeof(group))实现组播,先查查这个函数,一步步打断点。发现了一个异常igmp.c中,igmp_joingroup函数中igmp_joingroup_netif没有执行,查看了netif->flag这里NETIF_FLAG_IGMP标志没有设置。
 楼主| SHOPQQ 发表于 2023-7-26 18:51 | 显示全部楼层
程序修改点1
修改在ethernetif.c中low_level_init函数添加NETIF_FLAG_IGMP

  1. netif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_IGMP | NETIF_FLAG_ETHARP;

修改后,还是不能接收数据,看来还有问题啊。单播通信是可以的。发现网络调试助手发送组播后,网卡对应的中断都没有触发。
网卡初始化程序是抄的(参考)是st官方demo。查看了STM32技术手册,发现里面有组播/广播以及过滤器的说明。抱着疑惑的态度将网卡初始化代码看了。在结构体ETH_MACInitTypeDef中发现一个成员变量ReceiveAll设置是ETH_RECEIVEAll_DISABLE,将其改为ETH_RECEIVEALL_ENABLE试试。还有个成员变量MulticastFramesFilter注释说明该变量与组播有关,也可以改改。经测试,这里两种更改都可行。这里很多东西包括如何配置过滤器,都可以去看官方的文档。
 楼主| SHOPQQ 发表于 2023-7-26 18:51 | 显示全部楼层
程序更改点2
更改方式1
修改stm32f2xx_hal_eth.c中函数ETH_MACDMAConfig,接收所有数据包,如下
  1. /*************************************************/
  2.   macinit.ReceiveAll = ETH_RECEIVEALL_ENABLE;
  3. /****************************************************/
 楼主| SHOPQQ 发表于 2023-7-26 18:51 | 显示全部楼层
更改方式2
也是函数ETH_MACDMAConfig中,取消针对组播的过滤
  1. /********************************************************/
  2. macinit.MulticastFramesFilter = ETH_MULTICASTFRAMESFILTER_NONE;
  3. /*******************************************************/
 楼主| SHOPQQ 发表于 2023-7-26 18:51 | 显示全部楼层
可以接收组播数据了。
只是电脑和pcb板一对一连接也不需要什么组播。用路由器、交换机,多个网络主机、多个开发板组个局域网,也能收发,可惜好景不长,接收了没多久,就收不到数据。
网口接收中断也不触发,然后抓包,发现交换机没有给板子发数据了(而使用路由器可以)。当时想交换机有bug啊,都不发数据了。但是用其他板子,可以正常收到数据,就stm32的不行,打电话问过交换机厂家,也不清楚具体原因。
看了只能自己好好了解下组播的机制,继续看文档。发现组播协议有自己的membership query与membership reports。
 楼主| SHOPQQ 发表于 2023-7-26 18:52 | 显示全部楼层
补充说明,市面上部分路由器、交换机并没有去实现组播,而是采用广播的方式。将组播数据直接转发出去,没有根据query与report建立对应的组播表,根据表实现转发。具体内容可以参考其他文章
 楼主| SHOPQQ 发表于 2023-7-26 18:52 | 显示全部楼层
抓包发现,板子就最初发送过membership reports,后面就没有发送了。查看了lwip里关于组播的api,这里有个igmp_report_groups,调用定时发送membership report。这个函数并不会report组播地址出来。调用igmp_lookfor_group,组播地址有加入netif里面。查看实现report代码。有如下语句,通过注释了解到是要跳过224.0.0.1这个地址。
 楼主| SHOPQQ 发表于 2023-7-26 18:52 | 显示全部楼层
  1.         /* Do not send messages on the all systems group address! */
  2.      /* Skip the first group in the list, it is always the allsystems group added in igmp_start() */
  3.         if (groupref != NULL) {
  4.                  groupref = groupref->next;
  5.         }
 楼主| SHOPQQ 发表于 2023-7-26 18:52 | 显示全部楼层
每次会跳过第一个组播地址(实际该地址是通过socket的配置组播地址),又查看了igmp_lookup_group实现组播地址的添加,这里通过链表实现,224.0.0.1这个地址放在最后的,不应该跳过第一个地址。
 楼主| SHOPQQ 发表于 2023-7-26 18:52 | 显示全部楼层
程序更改点3
igmp.c中把如下代码注释掉就行。在igmp_delaying_member中判断地址不等于224.0.0.1。
 楼主| SHOPQQ 发表于 2023-7-26 18:52 | 显示全部楼层
  1.   /* Skip the first group in the list, it is always the allsystems group added in igmp_start() */
  2. //  if (group != NULL) {
  3. //    group = group->next;
  4. //  }
 楼主| SHOPQQ 发表于 2023-7-26 18:52 | 显示全部楼层
每次会跳过第一个组播地址(实际该地址是通过socket的配置组播地址),又查看了igmp_lookup_group实现组播地址的添加,这里通过链表实现,224.0.0.1这个地址放在最后的,不应该跳过第一个地址。
 楼主| SHOPQQ 发表于 2023-7-26 18:53 | 显示全部楼层
程序更改点3
igmp.c中把如下代码注释掉就行。在igmp_delaying_member中判断地址不等于224.0.0.1。
  1.   /* Skip the first group in the list, it is always the allsystems group added in igmp_start() */
  2. //  if (group != NULL) {
  3. //    group = group->next;
  4. //  }
 楼主| SHOPQQ 发表于 2023-7-26 18:53 | 显示全部楼层
本打算去lwip上提交更改意见,但查看了后面的版本,该问题已经修复了。这里建议使用较新的lwip,新版本修复了许多问题,也添加了如mqtt等新功能。

结束语
这里网口芯片最初使用的是LAN8742A,后续开发板使用了DP83848,也是可以使用的。**的本文能给大家一点参考。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

9

主题

183

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部