前景描述
我司的以太网实现是LAN8720搭配片上MAC层做的,协议栈用LWIP
这种做法应该还是蛮常见的,反正不是LAN8720就是DP83864,再不然就是硬件协议栈的W5500了
想想还是有MAC层过滤比较好,随手翻了翻它的手册,W5500好像是没得MAC层过滤的
假如数据速率比较高,数据量大,而且外面还有乱七八糟无关的数据往板卡送,我也不知道用这个片子到底好不好
LAN8720也不好改了,就继续用吧
好好在,发给板卡的数据的MAC还是有特征的,我只需要过滤发送方MAC地址的前3个字节,也就是特定的厂家那段
MAC地址的科普知识这里就不过多描述了,我们直奔主题
怎么用STM32 F7的片上MAC做MAC层过滤
写这个的目的,就是我搜了搜,没看见有人写这个MAC层过滤功能,具体代码怎么写才能用
知识科普-STM32的片上MAC
STM32 F7的MAC层过滤方式有三种(说人话的直白解释,中文手册其实也翻译的不好)
精确过滤(手册上叫单播目标/源地址过滤)
粗糙过滤(手册上叫Hash表过滤)
特殊过滤(广播,组播过滤等等特殊的过滤)
我这里着重说第1种和第3种
STM32 F7可以存4个MAC地址
其中MAC地址0是自己的MAC,其他三个可以用来做过滤
网络数据包通过PHY进入STM32的MAC,MAC层是可以对目的地址/源地址检查并过滤掉没得用的数据的,默认的HAL库库函数HAL_ETH_Init中
有一个static的函数
static void ETH_MACDMAConfig(ETH_HandleTypeDef *heth, uint32_t err)
会帮你在初始化网络功能时初始化默认的MAC层功能:
关闭源地址过滤
不接收所有数据
接收广播帧
打开目的地地址过滤
单播数据精确过滤
组播数据精确过滤
Hash表过滤关闭
其他的功能自行研究,我这里捡重要的说
这里我就要稍微吐槽一下HAL库了
MAC层过滤的设置没有预留接口出来,有设置MAC地址的函数
static void ETH_MACAddressConfig(ETH_HandleTypeDef *heth, uint32_t MacAddr, uint8_t *Addr)
就不多做一个设置MAC层过滤的函数嘛,连后面用到的宏定义都有~~
木有办法,自己编一个初始化MAC层过滤的函数吧
其实也就是把上面这个ETH_MACDMAConfig函数分离成两个单独的设置函数,再编一个设置MAC层过滤的函数
开启MAC层过滤的初始化代码
- uint8_t ETH_MAC_Setting(ETH_HandleTypeDef *heth)
- {
- ETH_MACInitTypeDef macinit;
- /* Ethernet MAC default initialization **************************************/
- macinit.Watchdog = ETH_WATCHDOG_ENABLE;
- macinit.Jabber = ETH_JABBER_ENABLE;
- macinit.InterFrameGap = ETH_INTERFRAMEGAP_96BIT;
- macinit.CarrierSense = ETH_CARRIERSENCE_ENABLE;
- macinit.ReceiveOwn = ETH_RECEIVEOWN_ENABLE;
- macinit.LoopbackMode = ETH_LOOPBACKMODE_DISABLE;
- if (heth->Init.ChecksumMode == ETH_CHECKSUM_BY_HARDWARE)
- {
- macinit.ChecksumOffload = ETH_CHECKSUMOFFLAOD_ENABLE;
- }
- else
- {
- macinit.ChecksumOffload = ETH_CHECKSUMOFFLAOD_DISABLE;
- }
- macinit.RetryTransmission = ETH_RETRYTRANSMISSION_ENABLE; //使能重传
- macinit.AutomaticPadCRCStrip = ETH_AUTOMATICPADCRCSTRIP_ENABLE; //使能自动去除
- macinit.BackOffLimit = ETH_BACKOFFLIMIT_10;
- macinit.DeferralCheck = ETH_DEFFERRALCHECK_DISABLE;
- macinit.ReceiveAll = ETH_RECEIVEAll_DISABLE; ///不接收所有包
- macinit.SourceAddrFilter = ETH_SOURCEADDRFILTER_NORMAL_ENABLE; //源地址过滤
- macinit.PassControlFrames = ETH_PASSCONTROLFRAMES_BLOCKALL;
- macinit.BroadcastFramesReception = ETH_BROADCASTFRAMESRECEPTION_DISABLE; //广播不接收
- macinit.DestinationAddrFilter = ETH_DESTINATIONADDRFILTER_NORMAL; //目的地址过滤
- macinit.PromiscuousMode = ETH_PROMISCUOUS_MODE_DISABLE; //混乱模式
- macinit.MulticastFramesFilter = ETH_MULTICASTFRAMESFILTER_PERFECT; //多播过滤
- macinit.UnicastFramesFilter = ETH_UNICASTFRAMESFILTER_PERFECT; //单播过滤
- macinit.HashTableHigh = 0x0; //Hash表高位
- macinit.HashTableLow = 0x0; //Hash表低位
- macinit.PauseTime = 0x0;
- macinit.ZeroQuantaPause = ETH_ZEROQUANTAPAUSE_DISABLE;
- macinit.PauseLowThreshold = ETH_PAUSELOWTHRESHOLD_MINUS4;
- macinit.UnicastPauseFrameDetect = ETH_UNICASTPAUSEFRAMEDETECT_ENABLE;
- macinit.ReceiveFlowControl = ETH_RECEIVEFLOWCONTROL_DISABLE; //接收流控
- macinit.TransmitFlowControl = ETH_TRANSMITFLOWCONTROL_DISABLE; //发送流控
- macinit.VLANTagComparison = ETH_VLANTAGCOMPARISON_16BIT;
- macinit.VLANTagIdentifier = 0x0;
- return HAL_ETH_ConfigMAC(heth, &macinit);
- }
复制代码
默认的MAC层 DMA初始化函数
- uint8_t ETH_DMA_Setting(ETH_HandleTypeDef *heth)
- {
- ETH_DMAInitTypeDef dmainit;
- /* Ethernet DMA default initialization ************************************/
- dmainit.DropTCPIPChecksumErrorFrame = ETH_DROPTCPIPCHECKSUMERRORFRAME_ENABLE;
- dmainit.ReceiveStoreForward = ETH_RECEIVESTOREFORWARD_ENABLE;
- dmainit.FlushReceivedFrame = ETH_FLUSHRECEIVEDFRAME_ENABLE;
- dmainit.TransmitStoreForward = ETH_TRANSMITSTOREFORWARD_ENABLE;
- dmainit.TransmitThresholdControl = ETH_TRANSMITTHRESHOLDCONTROL_64BYTES;
- dmainit.ForwardErrorFrames = ETH_FORWARDERRORFRAMES_DISABLE;
- dmainit.ForwardUndersizedGoodFrames = ETH_FORWARDUNDERSIZEDGOODFRAMES_DISABLE;
- dmainit.ReceiveThresholdControl = ETH_RECEIVEDTHRESHOLDCONTROL_64BYTES;
- dmainit.SecondFrameOperate = ETH_SECONDFRAMEOPERARTE_ENABLE;
- dmainit.AddressAlignedBeats = ETH_ADDRESSALIGNEDBEATS_ENABLE;
- dmainit.FixedBurst = ETH_FIXEDBURST_ENABLE;
- dmainit.RxDMABurstLength = ETH_RXDMABURSTLENGTH_32BEAT;
- dmainit.TxDMABurstLength = ETH_TXDMABURSTLENGTH_32BEAT;
- dmainit.EnhancedDescriptorFormat = ETH_DMAENHANCEDDESCRIPTOR_ENABLE;
- dmainit.DescriptorSkipLength = 0x0;
- dmainit.DMAArbitration = ETH_DMAARBITRATION_ROUNDROBIN_RXTX_1_1;
- uint8_t res = HAL_ETH_ConfigDMA(heth, &dmainit);
- if ((heth->Init).RxMode == ETH_RXINTERRUPT_MODE)
- {
- /* Enable the Ethernet Rx Interrupt */
- __HAL_ETH_DMA_ENABLE_IT((heth), ETH_DMA_IT_NIS | ETH_DMA_IT_R);
- }
- return res;
- }
复制代码
用来设置MAC地址的函数(其实就是把库函数的static函数取出来直接用了)- <blockquote>//heth: HAL句柄
复制代码
然后到最最重要的MAC层过滤设置
以MAC1为例
注意看红框的描述
功能很好用,可以过滤源地址/目的地址,还可以做掩码功能
设置MAC层过滤的代码
- static void ETH_MAC_Filter_Setting(ETH_HandleTypeDef *heth,ST_ETH_PAR * para)
- {
- //设置本机MAC地址
- ETH_MAC_ADDR_Config(heth, ETH_MAC_ADDRESS0, heth->Init.MACAddr);
- //设置UDP-A的对方MAC地址
- ETH_MAC_ADDR_Config(heth, ETH_MAC_ADDRESS1,para->UDP_A_MAC);
- (heth->Instance)->MACA1HR|=0xC0000000;//需要完全匹配UDP-A的对方MAC才接收
- //设置UDP-B的对方MAC地址
- ETH_MAC_ADDR_Config(heth, ETH_MAC_ADDRESS2,para->UDP_B_MAC);
- (heth->Instance)->MACA2HR|=0xC0000000;//需要完全匹配UDP-A的对方MAC才接收
- }
复制代码
我这里是希望为我的两个UDP精确过滤接收到的源地址
所以AE位设置为1,SA设置为1,MBC全部为0(MAC地址全部检查)
假如我希望过滤MAC中特定的厂家字段
例如某个MAC地址
01-02-03-04-05-06
其中01-02-03就是厂家字段
那么代码要这样修改(MBC的第29位,28位,27位置1)
这样MAC层就不会检查MAC地址里后3个字节(04-05-06)
但是会检查前3个字节,也就是厂家字段
这里需要注意MAC地址的字节序
- static void ETH_MAC_Filter_Setting(ETH_HandleTypeDef *heth,ST_ETH_PAR * para)
- {
- //设置本机MAC地址
- ETH_MAC_ADDR_Config(heth, ETH_MAC_ADDRESS0, heth->Init.MACAddr);
- //设置UDP-A的对方MAC地址
- ETH_MAC_ADDR_Config(heth, ETH_MAC_ADDRESS1,para->UDP_A_MAC);
- (heth->Instance)->MACA1HR|=0xF8000000;//需要匹配厂家字段
- //设置UDP-B的对方MAC地址
- ETH_MAC_ADDR_Config(heth, ETH_MAC_ADDRESS2,para->UDP_B_MAC);
- (heth->Instance)->MACA2HR|=0xF8000000;//需要匹配厂家字段
- }
复制代码
然后这些函数的调用顺序按:
1. HAL_ETH_Init
2. ETH_MAC_Setting
3. ETH_DMA_Setting
4. ETH_MAC_Filter_Setting
用你希望过滤的MAC传递到一个Hash计算函数里,得到一个值,通过这个值产生“映射”关系来过滤MAC地址,所以这个并不是精确的,因为可能会重复~~
HASH函数
函数
- / STM32 MACHASH - Copyright (C) 2014-2018 Clive Turvey (<a href="mailto:sourcer32@gmail.com">sourcer32@gmail.com</a>)
- // All Rights Reserved
- #include <windows.h>
- #include <stdio.h>
- #include <stdlib.h>
- typedef unsigned char uint8_t;
- typedef unsigned long uint32_t;
- uint32_t Rev32(uint32_t x)
- {
- uint32_t y;
- int i;
- y = 0;
- for(i=0; i<32; i++)
- if (x & (1 << i))
- y |= 1 << (31 - i);
- return(y);
- }
- uint32_t MacHash(const uint8_t *Mac) // <a href="mailto:sourcer32@gmail.com">sourcer32@gmail.com</a>
- {
- int i, j;
- uint32_t Crc;
- Crc = 0xFFFFFFFF;
- for(j=0; j<6; j++)
- {
- Crc = Crc ^ (uint32_t)Mac[j];
- for(i=0; i<8; i++)
- if (Crc & 1)
- Crc = (Crc >> 1) ^ 0xEDB88320; // Reversed 0x04C11DB7
- else
- Crc = (Crc >> 1);
- }
- return(Rev32(~Crc) >> 26); // Get High order 6-bit in reversed/inverted CRC
- }
- uint32_t MacHashFast(const uint8_t *Mac) // <a href="mailto:sourcer32@gmail.com">sourcer32@gmail.com</a>
- {
- static const uint32_t Rev6Tbl[] = {
- 0x00,0x20,0x10,0x30,0x08,0x28,0x18,0x38,
- 0x04,0x24,0x14,0x34,0x0C,0x2C,0x1C,0x3C,
- 0x02,0x22,0x12,0x32,0x0A,0x2A,0x1A,0x3A,
- 0x06,0x26,0x16,0x36,0x0E,0x2E,0x1E,0x3E,
- 0x01,0x21,0x11,0x31,0x09,0x29,0x19,0x39,
- 0x05,0x25,0x15,0x35,0x0D,0x2D,0x1D,0x3D,
- 0x03,0x23,0x13,0x33,0x0B,0x2B,0x1B,0x3B,
- 0x07,0x27,0x17,0x37,0x0F,0x2F,0x1F,0x3F };
- static const uint32_t Crc32Tbl[] = {
- 0x4DBDF21C, 0x500AE278, 0x76D3D2D4, 0x6B64C2B0,
- 0x3B61B38C, 0x26D6A3E8, 0x000F9344, 0x1DB88320,
- 0xA005713C, 0xBDB26158, 0x9B6B51F4, 0x86DC4190,
- 0xD6D930AC, 0xCB6E20C8, 0xEDB71064, 0xF0000000 };
- int i;
- uint32_t Crc;
- Crc = 0;
- for(i=0; i<6; i++)
- {
- Crc = Crc ^ (uint32_t)Mac<i>;
- </i> Crc = (Crc >> 4) ^ Crc32Tbl[Crc & 0x0F]; /* lower nibble */
- Crc = (Crc >> 4) ^ Crc32Tbl[Crc & 0x0F]; /* upper nibble */
- }
- return(Rev6Tbl[Crc & 0x3F]);
- }
- int main(int argc, char **argv)
- {
- static const uint8_t Test1[] = { 0x1F, 0x52, 0x41, 0x9C, 0xB6, 0xAF }; // 0x2C
- static const uint8_t Test2[] = { 0xA0, 0x0A, 0x98, 0x00, 0x00, 0x45 }; // 0x07
- static const uint8_t Test3[] = { 0x01, 0x00, 0x5e, 0x00, 0x00, 0x21 }; // 0x24
- printf("MacHash %02X\n", MacHash(Test1));
- printf("MacHash %02X\n", MacHash(Test2));
- printf("MacHash %02X\n", MacHash(Test3));
- printf("MacHashFast %02X\n", MacHashFast(Test1));
- printf("MacHashFast %02X\n", MacHashFast(Test2));
- printf("MacHashFast %02X\n", MacHashFast(Test3));
- return(1);
- }
复制代码
回到手册
上面的函数算出来一个数,假如是手册上的那个0x2c,二进制为101100B
换句看得懂的人话···就是:
算出来的数先看第6位
是1的话说明用到的是HASH表高位寄存器
否则用到的是HASH表低位寄存器
然后剩下的5位,转成十进制不是12嘛
如果你希望过滤这个MAC
那么你就把对应的HASH表高/低位寄存器的对应位置1,否则置0
这里的0x2c就是要把
HASH表高位寄存器的第12位置1
就可以过滤这条MAC了
然后把你希望的MAC地址都按照上面说的方法弄完
回到这个函数稍微改一下
- uint8_t ETH_MAC_Setting(ETH_HandleTypeDef *heth)
- {
- ETH_MACInitTypeDef macinit;
- /* Ethernet MAC default initialization **************************************/
- macinit.Watchdog = ETH_WATCHDOG_ENABLE;
- macinit.Jabber = ETH_JABBER_ENABLE;
- macinit.InterFrameGap = ETH_INTERFRAMEGAP_96BIT;
- macinit.CarrierSense = ETH_CARRIERSENCE_ENABLE;
- macinit.ReceiveOwn = ETH_RECEIVEOWN_ENABLE;
- macinit.LoopbackMode = ETH_LOOPBACKMODE_DISABLE;
- if (heth->Init.ChecksumMode == ETH_CHECKSUM_BY_HARDWARE)
- {
- macinit.ChecksumOffload = ETH_CHECKSUMOFFLAOD_ENABLE;
- }
- else
- {
- macinit.ChecksumOffload = ETH_CHECKSUMOFFLAOD_DISABLE;
- }
- macinit.RetryTransmission = ETH_RETRYTRANSMISSION_ENABLE; //使能重传
- macinit.AutomaticPadCRCStrip = ETH_AUTOMATICPADCRCSTRIP_ENABLE; //使能自动去除
- macinit.BackOffLimit = ETH_BACKOFFLIMIT_10;
- macinit.DeferralCheck = ETH_DEFFERRALCHECK_DISABLE;
- macinit.ReceiveAll = ETH_RECEIVEAll_DISABLE; ///不接收所有包
- macinit.SourceAddrFilter = ETH_SOURCEADDRFILTER_NORMAL_ENABLE; //源地址过滤
- macinit.PassControlFrames = ETH_PASSCONTROLFRAMES_BLOCKALL;
- macinit.BroadcastFramesReception = ETH_BROADCASTFRAMESRECEPTION_DISABLE; //广播不接收
- macinit.DestinationAddrFilter = ETH_DESTINATIONADDRFILTER_NORMAL; //目的地址过滤
- macinit.PromiscuousMode = ETH_PROMISCUOUS_MODE_DISABLE; //混乱模式
- macinit.MulticastFramesFilter = ETH_MULTICASTFRAMESFILTER_PERFECTHASHTABLE; //!!多播HASH完美过滤
- macinit.UnicastFramesFilter = ETH_UNICASTFRAMESFILTER_PERFECTHASHTABLE; //!!单播HASH完美过滤
- macinit.HashTableHigh = 0x0; //!!填你最后算完的Hash表高位
- macinit.HashTableLow = 0x0; //!!填你最后算完的Hash表低位
- macinit.PauseTime = 0x0;
- macinit.ZeroQuantaPause = ETH_ZEROQUANTAPAUSE_DISABLE;
- macinit.PauseLowThreshold = ETH_PAUSELOWTHRESHOLD_MINUS4;
- macinit.UnicastPauseFrameDetect = ETH_UNICASTPAUSEFRAMEDETECT_ENABLE;
- macinit.ReceiveFlowControl = ETH_RECEIVEFLOWCONTROL_DISABLE; //接收流控
- macinit.TransmitFlowControl = ETH_TRANSMITFLOWCONTROL_DISABLE; //发送流控
- macinit.VLANTagComparison = ETH_VLANTAGCOMPARISON_16BIT;
- macinit.VLANTagIdentifier = 0x0;
- return HAL_ETH_ConfigMAC(heth, &macinit);
- }
复制代码
大概MAC层过滤就这些东西了
如果有条件弄MAC层过滤真的强烈建议开启,可以大幅减轻协议栈的处理负担,板卡不会因为处理大量无用的数据而白白浪费处理性能。
|