Wiznet W5500 MAC RAW模式的操作

使用MAC RAW功能对于W5500来说有点浪费,不过如果想要在W5500基础上移植LwIP之类的协议栈的话就需要MAC RAW功能了,这就使得W5500和一片ENC28J60网卡芯片的功能一样了,屏蔽掉了内部的硬件TCP/IP协议栈。但是我使用MAC RAW的功能不是为了移植LwIP协议栈,而是作为一种局域组网的高速数据传输的验证,场景如下:

现在有一个设备需要8个控制器控制,8个控制器由一个主控器统一协调控制,主控器会向控制器发送数据,并且数据量很大,每个控制器需要33.75KBps的带宽(1152Bytes * 30,每秒30帧数据,一帧数据1152个字节),8个控制器需要的总带宽为270KBps,如果使用115200bps的RS485总线传输数据的话只能实现1.25FPS的效果,是不够的。使用500Kbps的CAN总线的话,虽然速率提高的了,但是CAN总线中一帧数据中的有效数据(Payload)占比太小,导致实际数据传输速率可能还低于RS485总线。最后想到的是网络了,但是每个控制器有自己活要干,分配太多的资源给网络LwIP类的协议栈的话会导致控制效果不好,当然直接使用W5500的硬件TCP/IP协议栈也是没有问题的,不过这里还是验证一下基于MAC RAW的数据传输。W5500拥有10/100M 以太网数据链路层(MAC)及物理层(PHY),SPI接口的最高速率为80Mhz,实际使用速率为36Mhz,实现270KBps的带宽是绰绰有余的。

下面可以开始进行试验,由于W5500的MAC RAW模式只能在socket 0上使用,所以我们将socket 0设置为MACRAW模式,开启MAC地址过滤、IPV6帧过滤,源端口、目的IP、目的端口等参数无需设置,初始化完成之后执行CR_OPEN命令打开socket(可不执行CR_CONNECT命令),等待socket的状态变为SOCK_MACRAW即表示初始化完成:

    struct Common_InitStruct Common_InitStructure = 
	{
		.gateway = {192,168,2,1},
		.subnet_mask = {255,255,255,0},
		.hardware_addr = {0x12,0x12,0x12,0x12,0x12,0x12},
		.ipaddr = {192,168,2,66},
		.int_mask = 0xFF,
		.sock_int_mask = 0xFF,
		.phy_config = 0xF8,
	};
	struct Socket_InitStruct Socket_InitStructure = 
	{
		.Sn = 0,
		.SnMR = Sn_MR_MFEN | Sn_MR_MIP6N | Sn_MR_MACRAW,
		.SnSrcPort = 8888,
		.SnDIP = {192,168,2,1},
		.SnDstPort = 6666,
	};
	
	w5500_init();
	common_init(&Common_InitStructure);
	socket_init(&Socket_InitStructure);
	
	u8 sock_sr;
	printf("%.2X ",sock_sr = getSnSR(0));
	setSnCR(0,Sn_CR_OPEN);
	setSnCR(0,Sn_CR_CONNECT);
	while(1)
	{
		printf("%.2X ",sock_sr = getSnSR(0));
		if(sock_sr == SOCK_MACRAW)
		{
			printf("Socket 0 MACRAW mdoe established\r\n");
			break;
		}
		vTaskDelay(500);
	}
	
	printf("%s\r\n",__FUNCTION__);
	u8 temp[] = "0123456789\r\n";
	static u8 temp2[2048];
	while(1)
	{
// 		writeSnTxBuff(0,temp,strlen(temp));
		int len = readSnRxBuff(0,temp2);
		int i;
		if(len)
		{
			printf("%.4X--",len);
			for(i=0;i<len;i++)
				printf("%.2X ",temp2[i]);
			printf("\r\n\r\n");
		}
		vTaskDelay(1);
	}

程序中把数据发送部分注释了,先进行接收试验,结果如下:

Wiznet W5500 MAC RAW模式的操作

结果发现收到的MAC帧和我们记忆中的MAC帧格式不大一样:https://blog.csdn.net/tq384998430/article/details/53391818。仔细查看这里的MAC帧前面多了两个字节的数据,如果把这两个字节去掉,后面的数据的结构和MAC帧格式是一样的,54 E1 AD 4F 78 5E是我电脑的MAC地址,我使用网线直接将W5500和电脑的网口连接,所以收到的帧中的源MAC地址应该是我的电脑的MAC地址,没有错,但是目的地址却不是W5500的MAC地址或者是广播地址,不知道为什么没有过滤MAC帧,这个问题先放这。看看前两个字节的数据是什么。仔细观察发现有的帧中前面两个字节和帧长度是一样的,说明这两个字节可能是帧长度,但是有帧的前两个字节又和帧长度不一样,再用心一点就发现了其实我们从W5500中读取的数据可能是多帧数据,也就是在我们读取的时候W5500中已经有多帧数据了,这时候我们读取的数据长度是多帧数据长度的总和,所以出现了实际帧长度和前两个字节不一样的现象,例如:

Wiznet W5500 MAC RAW模式的操作

Wiznet W5500 MAC RAW模式的操作

第一张图画红圈的地方是我电脑的MAC地址,那么说明读出来的额数据中有两个完整的帧,两个帧的长度都为0x0070,长度总和就是0x00E0,正好是我读取的数据的长度,注意这里读取的数据的长度包含了前两个字节,并且前两个字节表示的长度又包含了自己,也就是实际的MAC帧是0x6E个字节,加上2个字节的长度之后变成了0x70了。为什么W5500要加两个字节的数据作为帧长度呢?因为MAC帧是没有帧界定的,虽然数据链路层的MAC帧是有前导域和和校验的,但是存放在内存中的是不包含前导域和和校验的(校验是硬件计算并发送以及校验和剥离的),所以两帧数据如果同时放在缓存区中的话会导致无法确定帧的分界线,这才导致了两个字节的MAC帧长度数据的出现。

现在我们可以读取原始的MAC帧数据了,但是MAC地址的过滤没有有效,不知道是哪个细节没有注意到,不过使用软件进行MAC地址过滤不会占用很多CPU。下面进行MAC RAW的数据发送测试,直接取消上面代码的发送部分的注释,然后将循环延时调整到1000,打开网络抓包工具进行抓包,我们需要指定网卡,不然会导致WIFI网络的数据都会被抓取到,数据量非常大不方便分析,下面是实际的抓包结果(发送的数据是12字节的字符串“0123456789\r\n”):

Wiznet W5500 MAC RAW模式的操作

发送了12个字节的数据,但是接收到了60个字节,这可能是电脑上MAC帧的最小长度,或者是抓包工具每个帧都分配了至少60字节的长度,如果收到的帧没有达到60个字节也会将60个字节全部输出,如果使用两个W5500进行数据互发我猜测应该没有这种情况。

MAC RAW的试验没有问题,我们的方案可以使用一个拥有9端口以上的交换机然后将8台控制器和主控器连接到交换机上,控制器和主控器分配不同的MAC地址,以构成一个局域网,主控器可以向控制器定点发送数据或者广播发送数据。为了让交换机内部可以正常地建立MAC地址表,控制器和主控器需要发送一帧数据出去,不管目的地址,这样交换机在收到数据的时候就能知道端口号对应的设备的MAC地址了。

接下来就可以使用MAC RAW进行自定义协议并通讯了。

 

续。

 

前面发现W5500的MAC RAW模式下无法过滤MAC非本机MAC地址或广播MAC地址的帧,后面我做了一些实验,将Sn_MR_MFEN位去掉,也就是屏蔽MAC过滤功能,发送相比打开MAC过滤功能时多接受了很多帧,这说明MAC过滤功能时有作用的,那么为什么还有会接收到非本机MAC地址的帧呢?然后又做了一个实验,将MR寄存器中的Sn_MR_MMB位置位,他可以屏蔽多播MAC 地址的包传输,然后发现就没有再收到非本机MAC或者广播地址的帧了,由于没人知道我的MAC地址,所有收到的都是广播地址FF FF FF FF FF FF,那么这个多播MAC 地址的包是什么呢?参考文章:http://blog.chinaunix.net/uid-30343738-id-5748915.html,下图是打开MAC多播时接收到的多播帧:

Wiznet W5500 MAC RAW模式的操作

就不深入探讨这个问题了,我们这里屏蔽掉多播地址即可,然后就能收到只属于我们的帧数据了。

另外再说一下,使用UDP进行数据传输也可以实现上诉场景,我也进行了UDP传输的测试,发现和MAC RAW一样,在原始数据的前面会有数据长度,而且还有源IP地址和源端口号,这样我们就能知道发送过来数据的人是谁了,使用UDP的优点是可以和现成的网络设备例如PC电脑、Linux主板等直接通讯,因为这些设备进行MAC RAW传输不方便,而使用UDP是非常方便的。全速发送UDP数据测试了一下W5500的UDP数据传输速率:

	while(1)
	{
		writeSnTxBuff(0,temp,strlen(temp));
	}

writeSnTxBuff函数中会判断缓冲区是否有空闲,没有的话会直接返回。电脑上打开一个网络调试助手,本地端口6666,对方端口8888,运行程序打开任务管理器查看网络调试助手的网速:

Wiznet W5500 MAC RAW模式的操作

可以看到网络调试助手的网速为10.5Mbps,已经满足我们的需求了。