1. 项目概述:从硬件视角看嵌入式网络的核心引擎
在嵌入式系统开发中,实现稳定可靠的以太网通信是连接设备与数字世界的基石。无论是工业现场的PLC、智能家居的网关,还是车载信息娱乐系统,其网络功能的底层支撑都离不开两个核心硬件模块:以太网控制器(EMAC)和管理数据输入/输出接口(MDIO)。这不仅仅是两个技术名词,更是决定网络性能、稳定性和开发效率的关键。EMAC负责数据链路层的“重体力活”——帧的组装、发送、接收、校验和流量控制,而MDIO则扮演着“神经中枢”的角色,默默地配置和监控着物理层收发器(PHY)的健康状态。很多开发者初期只关注上层协议栈,直到遇到PHY无法识别、链路频繁闪断、吞吐量上不去等问题时,才回头审视这两个底层硬件模块。实际上,深入理解EMAC/MDIO的工作原理,是解决复杂网络问题、进行深度性能优化和实现可靠驱动的必经之路。本文将以德州仪器(TI)的嵌入式处理器为例,拆解EMAC/MDIO模块的内部架构、工作流程和关键配置,分享从寄存器配置到驱动实现的实战经验,帮助你在下一个嵌入式网络项目中,不仅能让设备“连上网”,更能让它“连得稳、传得快”。
2. EMAC模块深度解析:数据的高速公路与交通管制
以太网控制器(EMAC)是嵌入式系统的网络数据通路核心。你可以把它想象成一个高度自动化、多车道的智能收费站系统。数据包(车辆)从内存(城市)出发,经过这个收费站的处理和调度,才能有序、高效地驶上物理网络(高速公路),反之亦然。其设计目标非常明确:在保证符合IEEE 802.3标准的前提下,最大限度地卸载CPU负担,实现高吞吐、低延迟的数据交换。
2.1 EMAC核心组件与数据流剖析
一个典型的EMAC模块,其内部结构是高度流水线化和并行的。主要包含以下几个逻辑单元,它们协同工作,构成了完整的数据收发路径。
发送路径(Transmit Path):当CPU需要发送一个网络数据包时,它并不直接操作PHY。首先,CPU会在系统内存中准备好数据包,并填写一个称为“描述符(Descriptor)”的数据结构。这个描述符就像快递单,包含了数据包在内存中的地址、长度、以及一些控制信息(如是否由硬件添加CRC)。随后,CPU将这个描述符的地址写入EMAC的发送通道头描述符指针寄存器(TXnHDP)。这个动作相当于按下了“发货”按钮。
发送DMA引擎(Transmit DMA Engine)在检测到指针更新后,立刻开始工作。它根据描述符,从系统内存中读取数据包内容,并以64字节为“细胞(Cell)”单位,搬运到发送FIFO(Transmit FIFO)中。这里的设计很巧妙:FIFO通常只有3个细胞(192字节)的深度,远小于一个完整的数据包。它起到的是流量整形和速率匹配的作用,防止DMA和MAC发送器速度不匹配导致的数据丢失。当FIFO中的数据达到预设的阈值(由TXCELLTHRESH配置),或者一个完整的数据包已存入(以较小者为准),MAC发送器(MAC Transmitter)便被激活。
MAC发送器是协议的忠实执行者。它从FIFO中取出数据,为其添加前导码、帧起始定界符,并根据配置决定是否计算并附加32位的帧校验序列(CRC)。然后,它按照CSMA/CD(载波侦听多路访问/冲突检测)协议,通过MII/RMII接口将数据比特流发送给PHY芯片。整个发送过程,CPU仅在最初提交描述符时介入,后续的DMA搬运、帧组装、物理发送均由EMAC硬件独立完成,CPU得以解放去处理其他任务。
接收路径(Receive Path)是一个对称但方向相反的过程。PHY芯片将来自网络的模拟信号转换为数字比特流,通过MII/RMII接口传递给EMAC。MAC接收器(MAC Receiver)首先识别帧的起始,剥离前导码和定界符,然后进行地址匹配(判断这个帧是不是发给本机的)和初步的错误检查(如CRC错误、对齐错误)。只有通过检查的帧,才会被分段存入接收FIFO(Receive FIFO)。
与发送端类似,接收DMA引擎会监控接收FIFO。一旦有数据,DMA引擎便根据当前激活的接收通道的描述符链表,将数据搬运到事先由CPU分配好的系统内存缓冲区中。完成后,EMAC会更新对应的描述符状态(如数据长度、错误标志),并可选地产生一个接收完成中断通知CPU:“数据已到货,请处理”。CPU在中断服务例程中,只需读取描述符就能获取数据包信息,无需参与繁琐的数据搬运。
辅助模块:除了核心的数据通路,EMAC还包含几个关键辅助模块。统计逻辑(Statistics Logic)模块内置了一块RAM,实时记录着多达36种不同的网络统计信息,如接收/发送的字节数、帧数、各种错误计数等,这对于网络监控和故障诊断至关重要。状态RAM(State RAM)则存储着各通道的描述符链表头指针和完成指针,是DMA引擎能够自动遍历描述符链表的基础。中断控制器负责集中管理EMAC产生的20多种中断源,并可通过掩码寄存器灵活控制哪些中断能上报给CPU。
2.2 核心功能机制:流量控制、自适应优化与多通道管理
理解了数据流,我们再来看看EMAC如何保障这条“高速公路”不堵车、不出事故。这里有几个在实际驱动开发中必须关注的机制。
流量控制(Flow Control)是防止缓冲区溢出、避免丢包的关键。EMAC支持两种模式:
- 半双工碰撞流控:当EMAC工作在半双工模式(如传统的10BASE-T/100BASE-TX共享介质)时,如果接收缓冲区快满了(通过
RXnFREEBUFFER与RXnFLOWTHRESH比较判断),EMAC会主动对任何 incoming 帧产生“碰撞”信号。对方检测到碰撞后,会执行二进制指数退避算法,延迟重发,从而为本方赢得处理缓冲区数据的时间。 - 全双工暂停帧流控(IEEE 802.3x):在全双工模式下(现代网络的主流),流控通过发送特殊的“暂停帧(Pause Frame)”实现。当本地接收缓冲区紧张时,EMAC会向一个特定的组播地址(01-80-C2-00-00-01)发送暂停帧,其中包含一个“暂停时间”值(以512位时间为单位)。对端设备收到后,会在指定时间内停止发送数据。EMAC也能接收并响应对方发来的暂停帧,实现双向流控。启用此功能需要同时设置
MACCONTROL寄存器中的FULLDUPLEX和RXBUFFERFLOWEN/TXFLOWEN位。
注意:暂停帧的识别和处理是硬件自动完成的,但驱动程序必须正确配置相关寄存器。一个常见的坑是,在调试全双工模式下的吞吐量问题时,如果未禁用流控或阈值设置不当,可能会意外触发暂停帧,导致数据传输出现周期性卡顿。
自适应性能优化(APO)是一个用于提升半双工网络效率的智能特性。当网络繁忙、碰撞频繁时,APO通过动态增加帧之间的发送间隔(IPG),来降低后续碰撞的概率。其内部有一个“步调计数器(Pacing Counter)”。当发送一个帧遭遇延迟或碰撞时,计数器被重置为一个初始值(如31),此后每成功发送一帧,计数器减1。只有当计数器为0时,新帧才能立即发送;否则,会被延迟大约4个IPG的时间。这个机制能有效平抑网络突发流量,提升整体吞吐量。通过设置MACCONTROL寄存器的TXPACE位可以启用它。
多通道与虚拟MAC是EMAC一个非常强大的特性。它支持最多8个独立的发送和��收通道。在发送端,这8个通道可以配置为轮询(Round-Robin)调度或固定优先级调度,实现简单的服务质量(QoS)管理。在接收端,其能力更为突出:每个通道可以绑定一个独立的MAC地址。这意味着单个EMAC硬件可以虚拟出最多8个不同的网络接口。驱动程序可以为每个通道设置不同的MAC地址(通过MACADDRHI和MACADDRLO寄存器,注意高40位是共享的),并配置地址过滤规则。例如,可以将广播帧、组播帧、错误帧分别导向指定的通道进行处理,这对于实现协议分离、网络监控或虚拟化应用非常有价值。
3. MDIO模块详解:PHY设备的“体检医生”与“遥控器”
如果说EMAC是处理网络流量的“肌肉”,那么MDIO就是感知和配置物理层状态的“神经”。MDIO接口,有时也被称为MII管理接口(MIIM),是一个非常简单但至关重要的两线制串行总线(MDC时钟线和MDIO数据线),专门用于CPU通过EMAC访问外部PHY芯片的内部寄存器。
3.1 MDIO模块架构与自动化监控机制
MDIO模块的设计哲学是自动化和减轻CPU负担。它不仅仅是一个被动的读写接口,更集成了强大的PHY设备发现与链路状态监控功能。
MDIO时钟生成器(MDIO Clock Generator)是整个接口的节拍器。它通过对EMAC控制模块的外设时钟进行分频,产生MDC时钟信号。标准规定MDC最高频率为2.5MHz,典型应用为1.0MHz。由于不同芯片的主频不同,驱动程序必须在初始化时,根据数据手册提供的外设时钟频率,正确计算并设置CONTROL寄存器中的CLKDIV分频系数。例如,如果外设时钟为100MHz,要产生1MHz的MDC,则分频系数应设置为(100MHz / 1MHz / 2 - 1)= 49。设置错误的时钟频率会导致PHY通信失败。
全局PHY检测与链路状态监控是MDIO模块最实用的功能之一。上电初始化后,一旦MDIO模块被使能(CONTROL.ENABLE=1),其内部状态机就会自动、持续地轮询所有32个可能的PHY地址(0-31)。对于每个地址,它尝试读取PHY的标准状态寄存器。这个轮询过程完全由硬件完成,无需软件干预。轮询结果被实时更新在两个寄存器中:
- PHY存活状态寄存器(ALIVE):如果某个地址有PHY响应了读请求,则对应位置1。
- PHY链路状态寄存器(LINK):如果某个地址的PHY不仅存活,而且报告链路已建立(Link Up),则对应位置1。
这个机制极大地简化了驱动程序的编写。系统启动时,驱动不必去猜测PHY地址,只需读取ALIVE寄存器,就能知道系统里挂接了哪些PHY设备。之后,驱动程序可以选定一个PHY(例如地址最低的那个)进行详细配置。
选定PHY的主动监控更进一步。通过USERPHYSELn寄存器,我们可以告诉MDIO模块:“请重点监控地址为X的PHY”。此后,MDIO模块会透明地(在后台)定期读取该PHY的链路状态寄存器。一旦检测到链路状态发生变化(Link Up -> Down 或 Down -> Up),MDIO模块就会在LINKINTRAW寄存器中记录这个事件,并可配置产生中断(通过设置USERPHYSELn.LINKINTENB)通知CPU。这意味着,驱动程序无需定时轮询PHY来检查网线是否被拔掉,实现了事件驱动的链路状态管理,非常高效。
PHY寄存器用户访问是进行PHY具体配置(如设置自协商、速度、双工模式)的通道。CPU通过USERACCESSn寄存器(通常有USERACCESS0和USERACCESS1两个)发起读写请求。其工作流程是典型的“命令-完成”异步模式:
- CPU检查
USERACCESSn.GO位是否为0(空闲)。 - CPU向
USERACCESSn写入目标PHY地址(PHYADR)、寄存器地址(REGADR)、数据(DATA,写操作时)以及方向(WRITE位),最后置位GO位。 - MDIO模块接管,通过MDIO串行总线执行实际的读写操作。此时CPU可以去做其他事情,或者等待。
- 操作完成后,MDIO模块自动清零
GO位,并在USERINTRAW寄存器中置位相应的完成中断标志位。如果中断被使能(通过USERINTMASKSET),还会向CPU触发中断。 - CPU通过查询
GO位或中断,得知操作完成。对于读操作,结果数据可以从USERACCESSn.DATA字段中读取。
实操心得:
USERACCESS0和USERACCESS1支持简单的轮询仲裁,可以排队两个请求。但在实际驱动中,更常见的做法是使用其中一个寄存器,并采用“完成-等待”的同步模式,即发起请求后,循环查询GO位直到其变为0,然后再进行下一次操作。代码结构更清晰,且对于初始化阶段的配置序列来说,速度完全足够。避免在GO位为1时写入新的请求,否则会导致未定义行为。
3.2 MDIO驱动实战:初始化、读写与代码示例
理解了原理,我们来看如何用代码操作它。以下是一个基于TI芯片支持库(CSL)风格的简化驱动实现思路。
MDIO模块初始化是网络驱动启动的第一步,通常在EMAC初始化之前进行。
// 假设:peripheral_clock = 100 MHz, 目标 MDC = 1.0 MHz // CLKDIV = (peripheral_clock / (2 * MDC)) - 1 = (100 / 2) - 1 = 49 #define MDIO_CLKDIV_VAL 49 void MDIO_Init(void) { // 1. 配置控制寄存器:使能前导码(大多数PHY需要),设置时钟分频 MDIO_REGS->CONTROL = CSL_FMK(MDIO_CONTROL_PREAMBLE, 1) | CSL_FMK(MDIO_CONTROL_CLKDIV, MDIO_CLKDIV_VAL); // 2. 使能MDIO模块 MDIO_REGS->CONTROL |= CSL_FMK(MDIO_CONTROL_ENABLE, 1); // 3. (可选)等待并检测PHY // 硬件会自动开始轮询,我们可以稍等片刻后读取ALIVE寄存器 delay_us(1000); // 等待1ms,确保完成至少一轮轮询 uint32_t alive_status = MDIO_REGS->ALIVE; if (alive_status == 0) { // 错误:未检测到任何PHY return; } // 通常选择最低有效位的PHY地址,例如:phy_addr = __builtin_ffs(alive_status) - 1; g_phy_address = 0; // 假设我们使用地址0的PHY // 4. 配置用户PHY选择寄存器,监控该PHY的链路变化,并启用中断 MDIO_REGS->USERPHYSEL0 = CSL_FMK(MDIO_USERPHYSEL_PHYADR_MON, g_phy_address) | CSL_FMK(MDIO_USERPHYSEL_LINKINTENB, 1); // 启用链路变化中断 // 5. (可选)启用USERACCESS0操作完成中断 MDIO_REGS->USERINTMASKSET = CSL_FMK(MDIO_USERINTMASKSET_USERINT0MASK, 1); }PHY寄存器读写函数是驱动与PHY交互的基础。我们需要实现可靠、带错误处理的读写操作。
// 等待USERACCESS0空闲 static inline void MDIO_WaitIdle(void) { while (CSL_FEXT(MDIO_REGS->USERACCESS0, MDIO_USERACCESS0_GO)) { // 可以加入超时机制,防止死循环 } } // 读取PHY寄存器 int32_t PHY_Read(uint16_t phyAddr, uint16_t regAddr, uint16_t *data) { if (data == NULL) return -1; MDIO_WaitIdle(); // 确保硬件空闲 // 配置读请求:GO=1, WRITE=0, 设置PHY地址和寄存器地址 MDIO_REGS->USERACCESS0 = CSL_FMK(MDIO_USERACCESS0_GO, 1) | CSL_FMK(MDIO_USERACCESS0_REGADR, regAddr) | CSL_FMK(MDIO_USERACCESS0_PHYADR, phyAddr); // WRITE位默认为0,表示读操作 // 等待操作完成 MDIO_WaitIdle(); // 检查ACK位(可选,但推荐)。ACK=0表示PHY无响应(地址错��或PHY故障) if (CSL_FEXT(MDIO_REGS->USERACCESS0, MDIO_USERACCESS0_ACK) == 0) { return -2; // PHY未应答错误 } // 读取数据 *data = (uint16_t)CSL_FEXT(MDIO_REGS->USERACCESS0, MDIO_USERACCESS0_DATA); return 0; // 成功 } // 写入PHY寄存器 int32_t PHY_Write(uint16_t phyAddr, uint16_t regAddr, uint16_t data) { MDIO_WaitIdle(); // 确保硬件空闲 // 配置写请求:GO=1, WRITE=1, 设置PHY地址、寄存器地址和数据 MDIO_REGS->USERACCESS0 = CSL_FMK(MDIO_USERACCESS0_GO, 1) | CSL_FMK(MDIO_USERACCESS0_WRITE, 1) | CSL_FMK(MDIO_USERACCESS0_REGADR, regAddr) | CSL_FMK(MDIO_USERACCESS0_PHYADR, phyAddr) | CSL_FMK(MDIO_USERACCESS0_DATA, data); // 等待操作完成(写操作无需检查ACK) MDIO_WaitIdle(); return 0; // 通常认为写操作总是成功,除非总线故障 }链路状态中断处理是实现网络热插拔的基础。当MDIO模块检测到链路变化并产生中断时,CPU的中断服务程序需要快速响应。
void MDIO_LinkChange_ISR(void) { // 1. 读取原始中断状态,并清除中断标志(通常通过向RAW寄存器写1清除) uint32_t link_int_status = MDIO_REGS->LINKINTRAW; MDIO_REGS->LINKINTRAW = link_int_status; // 写1清除对应位 // 2. 检查是哪个PHY地址产生了中断(根据USERPHYSELn的设置) // 通常我们只监控一个PHY,所以直接处理即可 uint32_t current_link_status = MDIO_REGS->LINK; // 3. 判断链路是Up还是Down if (current_link_status & (1 << g_phy_address)) { // 链路已建立 (Link Up) // 通知网络栈:启动EMAC接收/发送,协商速度双工等 // 可以在这里读取PHY的特定状态寄存器(如BMCR/BMSR)获取具体速度(10/100M)和双工模式 uint16_t bmsr; PHY_Read(g_phy_address, PHY_BMSR_REG, &bmsr); // ... 根据bmsr解析速度双工信息,并配置EMAC的MACCONTROL寄存器(FULLDUPLEX位等) EMAC_Start(); } else { // 链路断开 (Link Down) // 通知网络栈:停止EMAC数据流 EMAC_Stop(); } // 4. (可选)重新使能链路变化中断(如果硬件清除中断时自动禁用了) }4. 常见问题排查与调试技巧实录
在实际的嵌入式网络驱动开发中,硬件初始化失败、链路不稳定、性能低下等问题层出不穷。以下是我在多个项目中总结的一些典型问题及其排查思路,很多都是数据手册里不会写的“坑”。
4.1 PHY无法识别或通信失败
这是最令人头疼的启动问题。现象是:MDIO模块使能后,读取ALIVE寄存器始终为0,或者PHY_Read函数总是返回ACK错误。
检查清单:
- 硬件连接:首先用万用表或示波器检查MDC和MDIO两根线是否物理连通,上拉电阻(通常需要4.7kΩ上拉)是否焊接正确。这两根线是开漏输出,必须上拉。
- 时钟配置:这是最高频的原因。确认
CONTROL.CLKDIV计算是否正确。MDC时钟必须在PHY支持的范围内(通常1-2.5MHz)。用示波器测量MDC引脚,看是否有波形,频率是否符合预期。一个快速验证方法是:将分频值设到最大,产生一个极低的时钟(如几十KHz),看通信是否恢复。 - 电源与复位:确认PHY芯片的电源电压稳定,复位引脚(如果有)的时序符合要求。有些PHY需要在上电后等待几十毫秒才能响应MDIO命令。
- PHY地址:虽然MDIO会轮询32个地址,但你的PHY硬件地址(通过PHY芯片的引脚上下拉配置)必须唯一且与软件预期一致。查阅PHY数据手册,确认其地址配置。
- 前导码:绝大多数PHY要求MDIO帧必须以32个“1”比特的前导码开始。确保
CONTROL.PREAMBLE位被设置为1(启用)。少数旧款或特殊PHY可能不需要,但启用是安全的选择。
调试技巧:如果条件允许,使用逻辑分析仪连接MDC和MDIO线,解码MDIO协议。你可以清晰地看到CPU发出的读/写帧格式、PHY地址、寄存器地址,以及PHY的回应。这是定位通信问题最直接的手段。对比解码出的波形与IEEE 802.3标准定义的MDIO帧结构,能立刻发现问题所在(如起始位、操作码、TA turnaround等)。
4.2 链路频繁Up/Down或性能不佳
网络能连通,但时不时断开,或者传输大文件时速度远低于理论值。
- 排查思路:
- 自动协商:确保EMAC和PHY的双工模式与速度设置匹配。最常见的问题是“双工不匹配”。最佳实践是在PHY和EMAC两端都启用自动协商。PHY侧通过配置其控制寄存器(如BMCR)实现。EMAC侧,则需要通过MDIO读取PHY协商完成后的结果(通常是状态寄存器BMSR或某个扩展寄存器),然后根据结果动态配置
MACCONTROL寄存器中的FULLDUPLEX位和速度选择位。切忌将一端强制设为全双工100M,另一端强制设为半双工10M。 - 流控配置:检查流控配置。如果网络两端都支持并启用了全双工流控(暂停帧),但阈值设置过于敏感(
RXnFLOWTHRESH值太小),可能会在网络稍有波动时就触发流控,导致吞吐量下降。在调试阶段,可以尝试暂时禁用流控(清除RXBUFFERFLOWEN和TXFLOWEN),看问题是否消失。 - 中断与轮询:如果采用中断模式处理接收数据,但中断服务程序处理时间过长,可能导致接收FIFO溢出丢包。检查是否因关中断时间太长,或者中断服务程序中做了耗时的操作(如打印日志)。对于高流量场景,考虑使用轮询模式,或者优化中断服务程序,仅做标记,将数据搬运等耗时任务放到主循环或任务中。
- 缓冲区与描述符:确保为EMAC分配的接收/发送描述符链表足够长,且每个描述符指向的缓冲区大小足够容纳一个最大传输单元(MTU,通常为1518字节)的帧。如果接收描述符用尽,EMAC将丢弃后续所有数据包,表现为“断流”。驱动程序必须及时回收处理完的描述符并重新挂接到链表中。
- 自动协商:确保EMAC和PHY的双工模式与速度设置匹配。最常见的问题是“双工不匹配”。最佳实践是在PHY和EMAC两端都启用自动协商。PHY侧通过配置其控制寄存器(如BMCR)实现。EMAC侧,则需要通过MDIO读取PHY协商完成后的结果(通常是状态寄存器BMSR或某个扩展寄存器),然后根据结果动态配置
4.3 数据收发异常:丢包、错包与CRC错误
当网络栈报告校验错误、丢包率高等问题时,需要从底层硬件数据流排查。
- 诊断步骤:
- 利用统计寄存器:EMAC的统计逻辑模块是你的第一助手。定期读取相关的统计寄存器,如接收/发送帧计数、CRC错误计数、对齐错误计数、FIFO溢出错误等。这些计数器能明确告诉你错误发生的类型和频率。例如,发送侧CRC错误激增,可能是
MACCONTROL中CRC生成配置(与描述符PASSCRC标志相关)有问题。 - 检查描述符字段:每个数据包描述符中都包含状态字段(如
SOP、EOP、OWNERSHIP位)和包长度字段。在驱动中,仔细检查回收的描述符状态。确认OWNERSHIP位已由硬件清零(表示DMA完成),检查是否有错误标志被置位。核对接收到的包长度是否合理。 - 内存一致性:确保描述符和数据缓冲区所在的内存区域,已经被正确配置为可被EMAC的DMA引擎访问。在带有缓存(Cache)的系统中,这是最大的陷阱。DMA直接读写物理内存,而CPU操作的是缓存中的数据副本。如果你在CPU填充发送数据后没有写回(Write-Back)缓存,或者CPU读取接收数据前没有无效(Invalidate)缓存,就会看到数据错误或丢失。务必在DMA操作涉及的内存区域使用非缓存(Non-cacheable)内存,或者严格进行缓存维护操作(Clean & Invalidate)。
- 时钟与信号完整性:对于RMII接口,确保提供给PHY和EMAC的50MHz参考时钟是干净、稳定的。用示波器检查MII/RMII数据线上的信号质量,是否存在过冲、振铃或电平不达标的情况。较长的PCB走线、不匹配的阻抗都可能引起信号完整性问题,导致间歇性的比特错误。
- 利用统计寄存器:EMAC的统计逻辑模块是你的第一助手。定期读取相关的统计寄存器,如接收/发送帧计数、CRC错误计数、对齐错误计数、FIFO溢出错误等。这些计数器能明确告诉你错误发生的类型和频率。例如,发送侧CRC错误激增,可能是
4.4 多通道与虚拟MAC配置陷阱
当尝试使用EMAC的多通道特性实现复杂功能时,容易配置出错。
- 关键点:
- MAC地址配置:8个通道的MAC地址高40位是共享的,只有低8位独立。这意味着你不能为每个虚拟接口设置完全随意的MAC地址。正确的流程是:先通过
MACINDEX选择通道号,然后写入MACADDRHI(高32位)和MACADDRLO(低16位)。对于第一个通道,需要写全48位。对于后续通道,通常只需更新MACADDRLO的低字节,因为高40位已经通过第一次写入MACADDRHI设置好了。 - 接收过滤使能:仅仅设置MAC地址还不够,必须通过
RXUNICASTSET寄存器使能对应的接收通道(RXCHnEN位),该通道才会接收目的地址匹配的单播帧。对于广播和组播帧,则需要额外配置RXMBPENABLE寄存器中的RXBROADEN、RXMULTEN等位,并指定它们被导向哪个通道。 - 中断分发:每个通道的接收/发送完成事件都会汇总到EMAC的中断系统。你需要仔细设计中断服务程序,通过查询
RXINTSTATRAW或TXINTSTATRAW等寄存器,来确定是哪个通道产生了中断,并进行相应的处理。为每个虚拟接口维护独立的描述符链表和状态机是必要的。
- MAC地址配置:8个通道的MAC地址高40位是共享的,只有低8位独立。这意味着你不能为每个虚拟接口设置完全随意的MAC地址。正确的流程是:先通过
调试这类问题,可以借助网络抓包工具(如Wireshark),配合设置不同的目的MAC地址发送测试包,观察哪个虚拟接口能收到,从而反向验证地址过滤和通道使能的配置是否正确。从底层硬件寄存器到上层网络协议栈的联合调试,是解决复杂网络问题的唯一途径。