1. 项目概述
在嵌入式以太网开发里,MAC和PHY之间的“对话”是个基本功,而MII/RMII管理接口(MDC/MDIO)就是它们说悄悄话的专用通道。别看它只有两根线,PHY芯片的“性格”——比如跑100M还是1000M、是全双工还是半双工、甚至是否启用节能模式——全得靠读写它内部的寄存器来设定。很多新手觉得这部分配置是驱动库或者硬件抽象层(HAL)该管的事,直接调个初始化函数就完事了。但真到了调试的时候,比如链路死活不起来,或者网络性能不稳,你迟早得和这些底层的时序、配置选项打交道,否则连问题出在PHY还是软件配置上都搞不清楚。
这次我们聚焦瑞萨(Renesas)RX系列微控制器,它通过一个叫“以太网FIT模块”(Firmware Integration Technology)的软件层,把硬件操作封装成了相对友好的API。但官方手册里关于MII/RMII寄存器访问的细节,特别是阻塞与非阻塞两种模式的差异和实现,往往散落在各个章节和配置项里。我最近在几个工业网关项目上深度使用了RX65N和RX72N,把ETHER_CFG_NON_BLOCKING这个开关从0拨到1,再从1拨回0,来回折腾了好几遍,踩了不少坑,也对这两种访问模式的适用场景和调优方法有了更深的体会。这篇文章,我就结合实测数据和代码片段,把RX系列以太网模块的MII/RMII寄存器访问机制,以及非阻塞API的使用心法,给你彻底讲透。
2. MII/RMII管理接口核心原理与硬件时序
2.1 MDC/MDIO协议基础:PHY的“I2C”
你可以把MDC/MDIO理解成以太网PHY芯片专属的“I2C”总线。MDC是时钟线,由MAC(或MCU内部的以太网控制器)主导;MDIO是双向数据线,负责传输具体的命令和数据。每一次通信,都遵循一个固定的管理帧格式,这个帧包含了起始条件、操作码(读或写)、PHY地址、寄存器地址和要读写的数据。
在RX的以太网FIT模块中,访问PHY寄存器本质上就是由软件或硬件辅助模块,按照这个帧格式,在MDC时钟的节拍下,在MDIO线上生成或解析一串特定的高低电平序列。理解这一点至关重要,因为后续所有关于时序的配置和调试,都是为了让这一串电平信号能够被PHY芯片正确无误地识别。
2.2 阻塞模式(PIR寄存器模拟)的运作机制
当你在配置文件
r_ether_rx_config.h
中将
ETHER_CFG_NON_BLOCKING
设置为
0
时,就选择了阻塞访问模式。在这种模式下,FIT模块会使用一个叫做PIR(PHY Interface Register)的寄存器来模拟MDC/MDIO的时序。
它是怎么工作的? 软件需要直接操作PIR寄存器,手动控制MDC引脚的电平翻转,并按照位(bit)来拼装或解析整个管理帧。例如,要发起一个读操作,你的代码(或驱动底层)需要:
- 通过PIR寄存器将MDIO引脚设置为输出模式,并依次输出帧起始码、读操作码、PHY地址和寄存器地址。
- 切换MDIO为输入模式,然后在MDC的每个时钟边沿从MDIO引脚读取PHY返回的数据位。
- 在整个过程中,CPU需要循环等待,直到整个帧的32位或更多位全部发送/接收完成,函数才会返回。这就是“阻塞”的含义——在访问PHY寄存器的几十微秒内,CPU会被完全占用,无法执行其他任务。
官方手册中图5.3和表5.2给出了一个具体的阻塞模式时序案例。他们用的是R5F565N9ADFB芯片,主频120MHz,PHY是DP83620。表5.3的实测数据非常关键:
| 时序参数 | 符号 | PHY要求最小值 | PHY要求最大值 | 实测值(参考) | 单位 |
|---|---|---|---|---|---|
| MDC到MDIO(输出)延迟 | T1 | 0 | 20 | 8 | ns |
| MDIO(输入)到MDC建立时间 | T2 | 10 | - | 500 | ns |
| MDIO(输入)到MDC保持时间 | T3 | 10 | - | 2300 | ns |
| MDC时钟周期 | T4 | 40 | - | 2840 | ns |
关键解读与避坑点 :
- T1(8ns) :这个值远小于PHY的最大要求(20ns),说明MCU驱动MDIO引脚变化的速度足够快,是安全的。
- T2和T3(500ns & 2300ns) :这两个是 输入建立和保持时间 ,实测值远大于PHY要求的最小值(10ns)。这其实是 软件模拟时序的典型特征 。因为用循环和NOP指令来翻转MDC时钟,其精度和速度远不如硬件,导致时钟周期(T4)长达2840ns(约352kHz),从而使得T2和T3的时间窗口被拉得非常宽裕。虽然通信功能正常,但效率低下。
- 核心矛盾 :阻塞模式的缺点显而易见。首先, CPU占用率高 ,每次访问都要“忙等”。其次, 时序由软件循环控制 ,其精度受中断、任务调度影响,在高主频或复杂RTOS环境下可能变得不稳定。最后, MDC频率低 ,当需要频繁读取PHY状态(如链路状态轮询)时,会成为系统性能的瓶颈。
2.3 非阻塞模式(PMGI硬件模块)的运作机制
将
ETHER_CFG_NON_BLOCKING
设置为
1
,就启用了非阻塞模式。此时,一个专用的硬件模块——PMGI(PHY Management Interface)——会接管MDC/MDIO的控制。
硬件模块的优势 :PMGI是一个状态机,你只需要通过API向它下达“读PHY地址0x01的寄存器0x01”这样的命令,它就会自动在后台生成正确的MDC时钟和管理帧,完成整个通信过程。在此期间,CPU被完全解放,可以去处理其他任务。当PMGI完成操作后,会产生一个中断,并通过你预先注册的回调函数来通知你结果。
手册中的图5.4和表5.4展示了非阻塞模式的配置,用的是R5F572NNDDBD芯片,并引入了几个关键配置项:
-
ETHER_CFG_PMGI_CLOCK: 设置为2500000,即2.5MHz。这是你希望PMGI硬件产生的MDC时钟频率。 -
ETHER_CFG_PMGI_HOLD_TIME: 设置为7。 -
ETHER_CFG_PMGI_CAPTURE_TIME: 设置为0。
表5.5的实测数据发生了根本性变化:
| 时序参数 | 符号 | PHY要求最小值 | PHY要求最大值 | 实测值(参考) | 单位 |
|---|---|---|---|---|---|
| MDC到MDIO(输出)延迟 | T1 | - | - | 64 | ns |
| MDIO(输入)到MDC建立时间 | T2 | 10 | - | 332 | ns |
| MDIO(输入)到MDC保持时间 | T3 | 4 | - | 60 | ns |
| MDC时钟周期 | T4 | - | - | 399 | ns |
关键解读与优势分析 :
- T4(399ns) :对应的MDC频率约为2.5MHz,与配置值完全吻合。这比软件模拟的352kHz快了7倍多, 通信效率大幅提升 。
- T2和T3(332ns & 60ns) :虽然仍比最小值大很多,但这是由硬件时序逻辑保证的, 极其稳定和精确 ,不受软件负载影响。
- CPU零占用 :在PMGI工作期间,CPU完全自由。这对于实时性要求高的系统(如需要快速响应网络事件、执行运动控制循环)是至关重要的。
- 配置的艺术 :
PMGI_HOLD_TIME和PMGI_CAPTURE_TIME这两个参数是 确保时序符合PHY芯片AC规格的关键 。它们用来微调PMGI模块在MDC时钟沿前后,对MDIO信号进行采样和保持的时间点。如果实测时序不满足你所用PHY芯片的数据手册要求,就需要调整这两个参数,而不是去改软件延时。
3. 非阻塞API的使用流程与实战代码解析
非阻塞模式不仅仅改变了底层时序,更改变了整个软件驱动的工作方式。你需要从“顺序执行”的思维,切换到“事件驱动”或“状态机”的思维。
3.1 全局状态变量与初始化
非阻塞API的核心思想是“发起请求,等待回调”。因此,我们需要一些全局或模块内的状态变量来跟踪异步操作的进度。
/* 非阻塞操作状态标志 */
volatile bool g_pmgi_busy = false; // PMGI硬件忙标志
volatile bool g_link_status_check_done = false; // 链路检查完成标志
volatile bool g_link_up = false; // 链路最终状态标志
volatile ether_event_t g_last_ether_event; // 记录最后一次以太网事件
/* 回调函数参数结构体指针 */
pmgi_cb_arg_t *g_pmgi_cb_arg;
在
main
函数或网络初始化任务的开始,必须初始化这些变量,并将回调函数注册到系统中。
void ethernet_init(void) {
/* 1. 初始化全局状态变量 */
g_pmgi_busy = true; // 初始化为true,等待Open函数将其置为false
g_link_status_check_done = false;
g_link_up = false;
/* 2. 内存初始化、引脚设置等(根据BSP和硬件设计) */
R_BSP_Init();
ether_pin_config(); // 配置ETn_MDC, ETn_MDIO, ETn_TX, ETn_RX等引脚
/* 3. 注册PMGI中断回调函数 */
R_ETHER_Control(ETHER_CH0, CONTROL_SET_PMGI_CALLBACK, (void *)pmgi_interrupt_callback);
/* 4. 初始化ETHERC和EDMAC控制器 */
R_ETHER_Open_ZC2(); // 首次调用,非阻塞模式启动
}
3.2 主循环中的状态机逻辑
你的主循环或网络任务不再直接调用阻塞函数等待结果,而是不断地检查状态标志,并根据状态决定下一步动作。手册中的图5.5清晰地描绘了这个流程。
void main_loop(void) {
ethernet_init();
while (1) {
/* 状态机:等待PHY初始化完成 */
if (g_pmgi_busy == false) {
/* PMGI空闲,可以开始链路检查 */
if (g_link_status_check_done == false) {
R_ETHER_CheckLink_ZC(ETHER_CH0); // 非阻塞调用,检查链路
g_link_status_check_done = true; // 防止重复调用
}
/* 状态机:处理链路检查结果 */
if (g_link_status_check_done == true && g_link_up == false) {
/* 链路检查已完成,但链路未UP,执行链路处理(如重启自协商) */
R_ETHER_LinkProcess(ETHER_CH0);
} else if (g_link_up == true) {
/* 链路已UP,可以开始正常的网络数据收发 */
user_network_application();
}
}
/* 执行其他系统任务,CPU不会阻塞在PHY访问上 */
other_system_tasks();
R_BSP_SoftwareDelay(10, BSP_DELAY_MILLISECS); // 简单延时,实际项目中用RTOS延时或事件驱动
}
}
3.3 PMGI中断回调函数的实现
这是非阻塞模式的“心脏”。当PMGI硬件完成任何一个操作(初始化、读PHY、写PHY、链路检查等),都会触发中断并调用这个函数。手册图5.6是其逻辑流程图。
void pmgi_interrupt_callback(void *p_args) {
pmgi_cb_arg_t *p_decode = (pmgi_cb_arg_t *)p_args;
/* 根据回调参数中的`mode`判断是哪个操作完成了 */
switch (p_decode->mode) {
case OPEN_ZC2:
if (p_decode->event == PMGI_COMPLETE) {
g_pmgi_busy = false; // PHY初始化完成,PMGI空闲
LOG_INFO("PHY Init Complete via PMGI.");
} else if (p_decode->event == PMGI_ERROR) {
LOG_ERROR("PHY Init Failed!");
// 这里可以加入错误恢复机制,如重试或系统复位
}
break;
case CHECKLINK_ZC:
if (p_decode->event == PMGI_COMPLETE) {
// 链路检查完成,需要从回调参数或API返回值中解析出实际链路状态
// 假设通过某个函数或p_decode->data获取到link_stat
bool link_stat = get_link_status_from_cb_arg(p_decode);
g_link_status_check_done = true;
if (link_stat) {
g_link_up = true;
LOG_INFO("Link is UP.");
} else {
g_link_up = false;
LOG_INFO("Link is DOWN.");
}
}
break;
case LINKPROCESS:
if (p_decode->event == PMGI_COMPLETE) {
// 链路处理(如自协商)完成,可以再次触发链路检查
g_link_status_check_done = false; // 重置标志,让主循环重新检查
LOG_DEBUG("Link Process Completed.");
}
break;
case WRITEPHY:
case READPHY:
// 处理自定义的PHY寄存器读写完成事件
if (p_decode->event == PMGI_COMPLETE) {
uint16_t phy_data = p_decode->data; // 假设数据在此
handle_phy_reg_access_complete(p_decode->phy_reg_addr, phy_data);
}
break;
default:
LOG_WARN("Unknown PMGI callback mode: %d", p_decode->mode);
break;
}
/* 如果需要,可以在这里更新其他非阻塞处理相关的全局变量 */
}
实操心得与陷阱 :
- 回调函数执行上下文 :这个函数在 中断上下文 中被调用!这意味着你必须遵循中断服务例程(ISR)的所有规则: 快进快出 ,不能调用可能阻塞的函数(如某些RTOS的
vTaskDelay),对于复杂处理应通过标志位通知任务线程处理。- 共享变量访问 :
g_pmgi_busy、g_link_up等状态变量在中断和主循环中被共同访问,必须使用volatile关键字声明,并在必要时使用临界区保护(如开关全局中断)来确保操作的原子性,避免出现竞态条件。- 错误处理 :一定要处理
PMGI_ERROR事件。PHY访问可能因为硬件连接不良、PHY芯片未响应、时序不匹配等原因失败。完善的错误处理(如有限次重试、日志记录、系统降级运行)是产品稳定性的保障。
4. 配置选项深度解析与调优指南
r_ether_rx_config.h
这个配置文件是驱动行为的“总开关”。除了
ETHER_CFG_NON_BLOCKING
,还有几个与MII/RMII访问密切相关的配置项,理解它们才能玩转这个驱动。
4.1 ETHER_CFG_PHY_MII_WAIT(阻塞模式专用)
这个配置仅在阻塞模式下生效。它定义了软件在操作MDC引脚(如电平翻转)之间插入的延迟循环次数。 这个值直接决定了软件模拟出的MDC时钟频率和时序。
-
如何计算?
它的单位是CPU指令周期。例如,在表5.2的环境(ICLK=120MHz)下,设置值为4。假设一个NOP或简单循环消耗几个周期,那么一次“翻转-等待”的耗时决定了T4(时钟周期)。你需要根据你的CPU主频和所需MDC频率来调整它。公式可以粗略估算为:
MDC_Period ≈ (2 * ETHER_CFG_PHY_MII_WAIT * Cycles_per_Loop) / ICLK_Frequency。 - 调优方法 :如果PHY访问不稳定,可以尝试增大此值以降低MDC频率、延长建立保持时间。但过大会导致访问过慢。最佳方法是结合逻辑分析仪,测量实际的MDC、MDIO波形,确保T1, T2, T3满足你所用PHY芯片数据手册的AC特性要求。
4.2 PMGI相关配置(非阻塞模式专用)
-
ETHER_CFG_PMGI_CLOCK:这是你 期望 的MDC时钟频率(单位Hz)。驱动会基于PCLKA频率和这个值来计算内部分频器。 这个值不能超过PMGI硬件和PHY芯片支持的最大频率 。常见PHY支持最高2.5MHz或更高,但为了稳定,通常设置为1-2.5MHz。 -
ETHER_CFG_PMGI_HOLD_TIME和ETHER_CFG_PMGI_CAPTURE_TIME:这是 时序微调 的利器。它们控制PMGI模块在MDC时钟边沿前后,对MDIO数据线的采样和驱动保持的时间点。-
HOLD_TIME:在MDC下降沿(或上升沿,具体看实现) 之后 ,PMGI继续保持输出数据稳定的时间。如果PHY芯片读取MDIO数据需要较长的保持时间,可以适当增加此值。 -
CAPTURE_TIME:在MDC上升沿(或采样沿) 之前 ,PMGI提前采样MDIO输入数据的时间。如果MDIO信号到MCU有延迟,可以增加此值以确保在稳定时段采样。 -
如何设置?
最科学的方法是
查阅PHY芯片数据手册的MDC/MDIO时序图
,找到
tSU(建立时间)和tHD(保持时间)的要求,然后通过调整这两个配置项,使得PMGI产生的时序满足要求。手册表5.5的实测值就是一个参考起点。
-
4.3 模式选择建议:阻塞 vs 非阻塞
| 特性 |
阻塞模式 (
NON_BLOCKING = 0
)
|
非阻塞模式 (
NON_BLOCKING = 1
)
|
|---|---|---|
| 实现方式 | 软件循环操作PIR寄存器 | PMGI硬件状态机 |
| CPU占用 | 高,访问期间CPU忙等 | 极低,访问期间CPU自由 |
| 时序精度 | 低,受中断和软件负载影响 | 高,由硬件时钟精确控制 |
| MDC频率 | 低(通常几百kHz) | 高(可配置,通常1-2.5MHz) |
| 代码复杂度 | 低,流程直观(顺序执行) | 高,需状态机和回调函数 |
| 实时性影响 | 可能阻塞高优先级任务 | 对系统实时性影响小 |
| 适用场景 | 对实时性要求不高、初始化阶段、或资源极其有限的简单应用 | 对系统响应速度要求高、需要频繁监控PHY状态、或基于RTOS的复杂应用 |
我的经验
:在早期的简单设备上,我用阻塞模式,代码简单粗暴。但在现在的工业网关、需要快速网络故障切换的设备上,
一律使用非阻塞模式
。虽然前期状态机设计稍复杂,但它带来的系统响应能力提升是质的飞跃。特别是当网络链路抖动时,非阻塞的
R_ETHER_LinkProcess
和
R_ETHER_CheckLink_ZC
可以无缝融入RTOS的事件驱动框架,不会卡住整个系统。
5. 常见问题排查与调试技巧实录
5.1 PHY寄存器读写失败
-
症状
:
R_ETHER_WritePHY或R_ETHER_ReadPHY返回错误,或回调函数收到PMGI_ERROR,但硬件连接正常。 -
排查步骤
:
- 确认PHY地址 :这是最常出错的地方!不同PHY芯片、不同板卡设计,PHY的MDIO地址可能不同(通常由硬件引脚上下拉决定)。用万用表测量PHY芯片的配置引脚,或仔细阅读板卡原理图和数据手册。常见的地址是0或1,但也可能是其他值。
- 检查电源和复位 :确保PHY芯片的供电稳定,且复位引脚已经完成释放。有些PHY需要上电后等待几十毫秒才能响应MDIO命令。
-
测量MDC/MDIO波形
:
这是终极调试手段
。用逻辑分析仪或示波器抓取MDC和MDIO的波形。
- 看帧格式 :对照MII管理帧格式(前导码、起始位、操作码、地址、数据等),看发出的帧是否正确。
-
看时序
:测量T1, T2, T3, T4是否满足PHY芯片数据手册的
最小值/最大值
要求。如果不满足,调整
PHY_MII_WAIT或PMGI的HOLD_TIME/CAPTURE_TIME。
-
检查配置宏
:确认
ETHER_CFG_CH0_PHY_ADDRESS(或CH1)是否正确设置为你的PHY地址。
5.2 非阻塞模式下回调函数不执行
-
症状
:调用了
R_ETHER_Open_ZC2等函数,但注册的回调函数从未被调用,程序卡住。 -
排查步骤
:
-
中断未使能
:PMGI中断必须在MCU的中断控制器(ICU)中使能,并且优先级设置正确。检查
r_ether_rx模块的初始化代码或你的BSP配置,是否正确配置了PMGI中断向量和使能位。 -
回调函数注册失败
:确认
R_ETHER_Control(CONTROL_SET_PMGI_CALLBACK, ...)调用成功,且传入的函数指针正确。 -
全局中断未开启
:在
main函数初始化后期,确保调用了类似__enable_irq()的函数开启了全局中断。 -
状态机逻辑死锁
:检查你的全局状态变量(如
g_pmgi_busy)初始化是否正确。如果初始化时为true,但回调函数里忘记将其置为false,主循环就会一直等待。
-
中断未使能
:PMGI中断必须在MCU的中断控制器(ICU)中使能,并且优先级设置正确。检查
5.3 链路状态检测异常
-
症状
:网线已连接,但
R_ETHER_CheckLink_ZC总是返回链路断开,或者链路状态不稳定。 -
排查步骤
:
-
PHY自协商配置
:大多数PHY默认启用自协商。确保你的MAC端配置(通过
R_ETHER_Open_ZC2的参数或PHY寄存器配置)与对端设备(如交换机)兼容。有时需要强制指定速度和双工模式。 -
读取PHY状态寄存器
:不要只依赖
R_ETHER_CheckLink_ZC的返回。在非阻塞回调中,或使用R_ETHER_ReadPHY直接读取PHY的标准状态寄存器(如BMCR、BMSR)。查看具体的链接状态位、自协商完成位,这能提供更准确的诊断信息。 - 硬件连接 :检查RJ45接口、变压器(Magnetics)、以及PHY到RJ45之间的差分线是否焊接良好。差分对需要等长、紧耦合,阻抗匹配为100欧姆。
-
PHY自协商配置
:大多数PHY默认启用自协商。确保你的MAC端配置(通过
5.4 软件复位(EDMR.SWR)的致命陷阱
手册第8章“Usage Notes”里提到了一个 极其重要 的警告:在RX64M/RX71M/RX72M/RX72N/RX66N上,如果在EDMAC(以太网DMA控制器)正在传输数据时,将EDMR.SWR(软件复位位)置1, 可能会破坏地址0x00000000到0x0000001F的内存数据 。
血的教训 :我在一次调试中,因为网络异常试图在运行时调用复位函数重新初始化以太网模块,结果导致系统跑飞。最后排查发现就是触发了这个坑。这段内存通常是中断向量表或关键代码/数据所在区域,被破坏后系统行为完全不可预测。
安全操作指南 :
- 绝对不要在数据收发过程中进行软件复位 。如果需要复位,必须先通过API停止DMA收发,等待所有传输完成,再进行复位操作。
-
仔细阅读
R_ETHER_Close和R_ETHER_Open_ZC2的调用顺序。通常,关闭函数会处理DMA停止等清理工作。 - 如果可能,考虑使用硬件复位(通过MCU的复位引脚控制PHY)而非软件复位。
6. 与EPTPC Light FIT模块的协同工作
手册第6.1节提到了一个增强功能模块:EPTPC Light FIT。它主要提供两个实用功能:
- 简单交换 :对于具有双以太网通道(Channel 0和1)的RX型号(如RX72M),可以在硬件层面实现两个端口之间的数据帧转发,无需CPU干预,适合做简单的双端口交换机。
- 组播过滤 :在硬件层面过滤组播帧,只有目的地址匹配预设地址(最多两个)的组播帧才会被提交给CPU,极大减轻了CPU处理无关组播流量的负担,在工业协议(如EtherNet/IP)中非常有用。
关键限制 :需要注意的是, 以太网FIT模块不能同时与EPTPC Light FIT模块和完整的EPTPC FIT模块(支持IEEE 1588精确时间协议)一起使用 。你必须根据需求二选一:
-
需要简单交换或组播过滤,但不需要IEEE 1588:选择
EPTPC Light (
r_ptp_light_rx) 。 -
需要IEEE 1588时间同步功能:选择
完整的EPTPC模块 (
r_ptp_rx) ,但它可能不包含Light版本的交换和过滤功能。
7. 开发环境与版本兼容性要点
手册第6.2节罗列了庞大的已验证环境列表。这里提炼几个关键信息:
-
编译器兼容性
:CC-RX、GCC for Renesas RX、IAR for RX三大编译器都支持。但要注意,从Rev1.23开始,
编译器选项默认使用Smart Configurator的配置
。如果你手动修改了工程选项,需要确保与FIT模块兼容,特别是C语言标准(如
-lang=c99或-std=gnu99)。 -
版本迭代
:从Rev1.12到1.25,几乎每个版本都修复了重要的软件问题。例如:
-
Rev1.12:修复了
R_ETHER_LinkProcess在特定条件下链路处理失败的问题。 - Rev1.15:修复了在中断函数中调用读函数可能无法正常接收,以及链路处理在自协商未完成时卡住的问题。
- Rev1.17/1.20:修复了缓冲区释放后可能无法接收,或描述符设置错误导致收发异常的问题。
-
Rev1.12:修复了
-
实践建议
:
- 始终使用最新版本的FIT模块 。瑞萨官网会定期更新,修复已知问题。
- 在创建新工程时,使用e2 studio的Smart Configurator图形化工具来添加和配置FIT模块,这能最大程度避免手动配置错误(如头文件路径、链接顺序)。
- 如果遇到编译错误,如“Could not open source file ‘platform.h’”,首先检查BSP(板级支持包)FIT模块是否已正确添加到工程中,这是所有FIT模块依赖的基础。
调试RX以太网,逻辑分析仪是比仿真器更直观的工具。抓一抓MDC/MDIO的波形,抓一抓以太网帧的收发,很多问题都会一目了然。非阻塞模式初看复杂,但一旦理顺了状态机的逻辑,它带来的系统整体性能提升会让你觉得一切投入都是值得的。尤其是在复杂的网络应用中,让CPU从低速的PHY访问中解脱出来,去处理更重要的协议栈和应用逻辑,是整个系统设计走向成熟的关键一步。
1319

被折叠的 条评论
为什么被折叠?



