stm32f207实现组播

背景

第一次使用lwip,在网上看了很多文章,一步步实现了freeRtos与lwip的移植,关于freeRtos与lwip的移植已经有很多可以参考的文章了,就不赘述了。这里主要讲讲我如何实现组播的,因为遇到一些坑,并且如何解决的。

实现组播的流程

第一步、配置LWIP

lwipopts.h中,将LWIP_IGMP改为1,使能组播代码的编译。

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

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

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

实现组播接收

#define MULTICAST_ADDR "239.2.1.22"
#define UDP_RCV_PORT 1234

typedef struct{
	int fd;
	struct sockaddr_in sockServer;
	struct sockaddr_in sockClient;
	uint8_t rcvData[1024];
	int rcvLen;
}SocketInfo_ts;

SocketInfo_ts udpClient;
//create a udp socket
int iCreatUdpClient(SocketInfo_ts* udpSock)
{
    struct ip_mreq group;
    
    if((udpSock->fd=socket(AF_INET,SOCK_DGRAM,0))<0)
    {
        return 0;
    }
    
    udpSock->sockClient.sin_family=AF_INET;
    udpSock->sockClient.sin_port=htons(UDP_RCV_PORT);
    udpSock->sockClient.sin_addr.s_addr=htonl(INADDR_ANY);
    if(bind(udpSock->fd,(struct sockaddr*)&udpSock->sockClient,sizeof(struct sockaddr_in))<0)
    {
        return 0;
    }
	
	group.imr_interface.s_addr=htons(INADDR_ANY);
	group.imr_multiaddr.s_addr=inet_addr(MULTICAST_ADDR);
	setsockopt(udpSock->fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,(char *)&group,sizeof(group));
	
	return 1;
}
void vUdpThread(void* argument)
{
    uint32_t fromSize;
    fd_set fdSockSet;
    struct timeval timeout;
    
    //init udpClient
    iCreatUdpClient(&udpClient);
    for(;;)
    {
		FD_ZERO(&fdSockSet);
		FD_SET(udpClient.fd,&fdSockSet);
		
		timeout.tv_sec=1;
		timeout.tv_usec=0;
		if(select(udpClient.fd+1,&fdSockSet,NULL,NULL,&timeout)>0)
		{
			if(FD_ISSET(udpClient.fd,&fdSockSet))
			{
				fromSize=sizeof(struct sockaddr_in);
				udpClient.rcvLen=recvfrom(udpClient.fd,udpClient.rcvData,1024,0,(struct sockaddr*)&udpClient.sockServer,&fromSize);
			}
		}
    }
}

/*-----------------------------------------------------------------------------------*/
void vUdpInit(void)
{
    sys_thread_new("udp_thread", vUdpThread, NULL, DEFAULT_THREAD_STACKSIZE,UDP_THREAD_PRIO );
}

实现组播发送

这里和普通发送差不多,只要将目的地址改为组播地址就行。

第三步、编译验证

编译没什么问题,有问题就改改,然后通过。网络调试助手用起来……
结果当然是失败了,看来没那么简单,啪啪打脸。
接着只能自己一步步查了,程序通过setsockopt(udpSock->fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,(char *)&group,sizeof(group))实现组播,先查查这个函数,一步步打断点。发现了一个异常igmp.c中,igmp_joingroup函数中igmp_joingroup_netif没有执行,查看了netif->flag这里NETIF_FLAG_IGMP标志没有设置。

程序修改点1

修改在ethernetif.clow_level_init函数添加NETIF_FLAG_IGMP

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

修改后,还是不能接收数据,看来还有问题啊。单播通信是可以的。发现网络调试助手发送组播后,网卡对应的中断都没有触发。
网卡初始化程序是抄的(参考)是st官方demo。查看了STM32技术手册,发现里面有组播/广播以及过滤器的说明。抱着疑惑的态度将网卡初始化代码看了。在结构体ETH_MACInitTypeDef中发现一个成员变量ReceiveAll设置是ETH_RECEIVEAll_DISABLE,将其改为ETH_RECEIVEALL_ENABLE试试。还有个成员变量MulticastFramesFilter注释说明该变量与组播有关,也可以改改。经测试,这里两种更改都可行。这里很多东西包括如何配置过滤器,都可以去看官方的文档。

程序更改点2

更改方式1

修改stm32f2xx_hal_eth.c中函数ETH_MACDMAConfig,接收所有数据包,如下

/*************************************************/
  macinit.ReceiveAll = ETH_RECEIVEALL_ENABLE;
/****************************************************/
更改方式2

也是函数ETH_MACDMAConfig中,取消针对组播的过滤

/********************************************************/
macinit.MulticastFramesFilter = ETH_MULTICASTFRAMESFILTER_NONE;
/*******************************************************/

可以接收组播数据了。
只是电脑和pcb板一对一连接也不需要什么组播。用路由器、交换机,多个网络主机、多个开发板组个局域网,也能收发,可惜好景不长,接收了没多久,就收不到数据。
网口接收中断也不触发,然后抓包,发现交换机没有给板子发数据了(而使用路由器可以)。当时想交换机有bug啊,都不发数据了。但是用其他板子,可以正常收到数据,就stm32的不行,打电话问过交换机厂家,也不清楚具体原因。
看了只能自己好好了解下组播的机制,继续看文档。发现组播协议有自己的membership querymembership reports

补充说明,市面上部分路由器、交换机并没有去实现组播,而是采用广播的方式。将组播数据直接转发出去,没有根据query与report建立对应的组播表,根据表实现转发。具体内容可以参考其他文章

抓包发现,板子就最初发送过membership reports,后面就没有发送了。查看了lwip里关于组播的api,这里有个igmp_report_groups,调用定时发送membership report。这个函数并不会report组播地址出来。调用igmp_lookfor_group,组播地址有加入netif里面。查看实现report代码。有如下语句,通过注释了解到是要跳过224.0.0.1这个地址。

	/* Do not send messages on the all systems group address! */
     /* Skip the first group in the list, it is always the allsystems group added in igmp_start() */
	if (groupref != NULL) {
		 groupref = groupref->next;
	}

每次会跳过第一个组播地址(实际该地址是通过socket的配置组播地址),又查看了igmp_lookup_group实现组播地址的添加,这里通过链表实现,224.0.0.1这个地址放在最后的,不应该跳过第一个地址。

程序更改点3

igmp.c中把如下代码注释掉就行。在igmp_delaying_member中判断地址不等于224.0.0.1

  /* Skip the first group in the list, it is always the allsystems group added in igmp_start() */
//  if (group != NULL) {
//    group = group->next;
//  }

本打算去lwip上提交更改意见,但查看了后面的版本,该问题已经修复了。这里建议使用较新的lwip,新版本修复了许多问题,也添加了如mqtt等新功能。

结束语

这里网口芯片最初使用的是LAN8742A,后续开发板使用了DP83848,也是可以使用的。希望的本文能给大家一点参考。

  • 1
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值