Skip to content

LV020-网络通信实验 ​

一、 实验准备 ​

1. 硬件连接 ​

  • 一根网线:板子与 PC 直连(现代网卡能自动翻转,不需要交叉线),或者两端都接在同一台交换机/路由器上。
  • 一根 USB 线:板载 CH340 提供串口终端,用来观察板子打印的日志、输入文本命令。

2. 网络参数 ​

参数写死在 atk-f103/Components/lwip-2.1.3/port/lwip_comm.h 里,改 IP 要重新编译:

项板子(默认)PC 侧要求
IP 地址192.168.16.30设成同网段的另一个地址,如 192.168.16.100
子网掩码255.255.255.0一致
网关192.168.16.1直连时用不到,可留空

同网段通信不走网关,所以 PC 这块网卡配不配网关都不影响 ping 板子。但如果随便填了一个实际上不存在的网关,PC 上其它跨网段的访问会莫名其妙失败,排查时容易误判——直连实验建议留空。

3. 上电自检输出 ​

DM9000 的初始化在 lwip_comm_init() 里,而它在 IAP 业务层被调用。上电后串口应当依次打出:

text
ATK-F103 USART1 ready, SystemCoreClock = 72000000 Hz, build <日期> <时间>
IAP: ready, waiting for app.img frame (magic IAPBIN01, max payload 131072 bytes, frame timeout 3000 ms)
USART1: commands: 'go' jump to APP, 'reboot' restart chip
DM9000: ID = 0x90000A46
lwIP: MAC 02-00-00-XX-XX-XX, IP 192.168.16.30
lwIP: RX mode = interrupt (EXTI6 falling edge)
lwIP: ready, ping 192.168.16.30 to test
IAP-TCP: listening on port 8888

逐行含义:

输出说明
DM9000: ID = 0x90000A46芯片认到了。不是这个值就停下,先查 FSMC 与片选
lwIP: MAC 02-00-00-XX-XX-XXMAC 由芯片 UID 生成:高三字节固定本地管理地址块,低三字节取自 0x1FFFF7E8
lwIP: RX mode = interrupt当前收包走 EXTI 中断;显示 polling 说明宏被改成了轮询
lwIP: ready, ping ... to testlwip_comm_init() 返回成功,可以开始 ping
IAP-TCP: listening on port 8888TCP 升级通道已监听

如果只看到 lwIP: DM9000 init failed,说明 DM9000_Init() 返回了非 0,也就是 ID 读错。这时不用怀疑协议栈,直接回到硬件层查 FSMC 与片选。

二、 实验一:ping 通 ​

1. 操作与预期 ​

这是最省事的验证手段:ping 只要用上 ARP、ICMP、IP 三层,能通就说明"DM9000 收发 + 协议栈 + 网络参数"整条链路都活着,而且不需要写任何上位机代码。

bash
ping 192.168.16.30

预期:

  • PC 侧先看到 ARP 请求、再收到 ICMP 应答,ping 正常返回;
  • 板子串口没有额外的报错输出(板子不打爆日志本身就是好消息)。

2. ping 不通时的排查顺序 ​

按下表的顺序查(按"从物理到协议"排,逐层砍):

现象先查什么
网口指示灯不亮网线、对端口,以及 PHY 是否上电(DM9000 内部 PHY 靠 GPCR / GPR 控制)
灯亮但 ping 无应答串口有没有打出 DM9000: ID = 0x90000A46;MAC / IP 打印是否正常
PC 收到 ARP 却没有应答掩码是否一致、PC 是否被配了网关
时通时不通收包模式(中断/轮询)是否与预期一致,见第四节

完整的排错决策树(含 arp -a 这一步)见 LV050-一次ping的完整旅程 第六节。

三、 实验二:TCP 服务器 ​

板子上跑的是一个单连接 TCP 服务器,监听 8888 端口,用途是把固件帧从 PC 灌进 Bootloader。它的实现见 port/iap_tcp.c,与帧协议的关系见 LV030-网络升级帧协议与三条通道。

1. 会话规则 ​

行为实现
并发连接只允许一个,忙时直接 tcp_abort 拒绝
建连时摘除串口字节流消费器(usart1_set_rx_sink(0)),由 TCP 独占喂帧状态机
断连时恢复串口消费器,两边都能继续用
文本命令TCP 字节在喂帧状态机前会先过一遍串口命令表,所以 go / reboot 走 TCP 同样有效

2. 先做一次不破坏数据的连通性测试 ​

不需要跑升级脚本,用任意 TCP 客户端连一下就能验证"DM9000 + lwIP + TCP"三层都通:

bash
nc 192.168.16.30 8888
  • 若 TCP 三次握手成功,板子会打印 IAP-TCP: client connected, waiting for IAPBIN01 frame;
  • 断开后打印 IAP-TCP: client disconnected, uart channel restored。

能看到这两行,就说明"连不上"这类问题已经排除,可以往下走真实升级。

3. 真实升级:把 app.img 发进去 ​

上位机脚本是 tools/send_iap_tcp.py,它依赖同目录的 pack_iap.py:给裸 bin 时会自动组帧,并先校验向量表。

bash
# 发送裸 bin(脚本自动打包成 IAPBIN01 帧)
python tools/send_iap_tcp.py -f flash-app.bin

# 指定板子 IP 与端口
python tools/send_iap_tcp.py -f flash-app.bin -i 192.168.16.30 -p 8888

# 只发 go 命令,让 Bootloader 跳转到 APP
python tools/send_iap_tcp.py -i 192.168.16.30 -g

脚本会打印栈顶(MSP)、复位向量与载荷 CRC32,然后每次 sendall() 发 1460 字节(脚本里 CHUNK = 1460 的注释写"对齐 TCP MSS"),最后等回执。

【注意】1460 字节只是脚本每次写入 socket 的批量大小,不是线上 TCP 段的实际大小。线上分段由握手时协商出的 MSS 决定:本工程 lwipopts.h 未定义 TCP_MSS,走 lwIP 默认值 536,所以 PC 的 TCP 栈会把每个 1460 字节的大块再切成 ≤ 536 字节的小段发送——这就是为什么大文件传输时板子看到的包会比预期碎得多(MSS 与窗口的来历见 LV015-lwIP配置与初始化 第二节,传输层基础见 LV035-传输层端口UDP与TCP)。

脚本最后的回执对照:

回执含义
RECV OK帧收齐且 CRC32 通过
FLASH OK写入 Flash 并回读校验成功
FLASH ERR写入失败
RECV ERR收帧失败(超时/格式错),串口有具体原因

板侧同一时刻也会打印对应日志。收到 FLASH OK 之后不会自动跳转,还要再发一次 go(或串口输入 go)——这是刻意留的确认环节,方便先回读确认写入结果。

四、 实验三:收包模式的切换 ​

1. 中断模式与轮询模式 ​

DM9000 的收包有两种驱动方式,由 dm9000.h 里的宏选择:

模式宏值工作机制代价
中断DM9000_RX_INT_MODE = 1PG6 下降沿 → EXTI6 → HAL_GPIO_EXTI_Callback() 置软件标志 → 主循环取包中断与主循环都要碰总线,需要临界区
轮询DM9000_RX_INT_MODE = 0主循环每拍无条件查 RX FIFO空转开销,但逻辑简单、不依赖中断
收包路径:中断模式与轮询模式

2. 中断模式里那个必须清的标志 ​

DM9000 的 INT 引脚是电平保持型:有包时拉低,且只有把 ISR 里对应的位写 1 清除之后才会回到高电平。所以回调里不能只置标志就返回:

c
do
{
    isr = DM9000_ReadReg(DM9000_ISR);
    if ((isr & ISR_PRS) != 0U) { s_rx_flag = 1; }
    DM9000_WriteReg(DM9000_ISR, isr);   /* 关键: 写 1 清中断源 */
} while (HAL_GPIO_ReadPin(DM9000_INT_GPIO_Port, DM9000_INT_Pin) == GPIO_PIN_RESET);

漏掉这一步的典型症状是:第一次收包正常,之后所有包全丢——因为下降沿只出现过一次,之后 INT 一直保持在低电平。中断路径的完整说明见 LV005-DM9000驱动实现 第五节。

3. 两种模式都保留的价值 ​

代码里两种模式都在,靠宏切换。这样做的价值在调试期:怀疑中断通路有问题时,把宏改成 0 就能立刻区分"是中断没进来"还是"网卡本身没在收"。

五、 诊断手段 ​

1. 串口日志与计数统计 ​

网络不通时最怕的是"完全没有观测点"。这个工程留了两层:

手段开关/入口内容
串口日志常开初始化结果、每帧收发的结论、错误原因
计数统计DM9000_NETDBG_ENEXTI 触发次数、成功取包数、因状态/内存丢弃的包数、发送次数
周期打印DM9000_DbgPoll()每拍由 lwip_comm_poll() 调用,每 2 秒打一行

计数统计是排查"丢包"的主力,三种典型读数:

(1)exti 远大于 rx_ok:标志置了却没被主循环及时取走(主循环被什么堵住了)。

(2)rx_drop 持续增长:多半是 pbuf_alloc 失败,即 PBUF_POOL_SIZE = 8 被用完。

(3)nsr 低位异常:PHY 链路状态没协商好,先查网线再看驱动。

2. 排错速查 ​

现象可能原因
DM9000: ID = 0x00000000 或 0xFFFFFFFFFSMC 未初始化、片选错、总线宽度/时序不对
串口完全没有 DM9000: 打印LWIP_APP_EN 被置 0,或 lwip_comm_init() 没被调用
能 ping 通但 TCP 连不上端口被别的会话占着(单连接策略);或 IAP_TCP_EN 为 0
第一次收包后就再也没包中断标志没清(第四节第 2 小节)
升级到一半板子卡死收帧超时(默认 3000 ms)后状态机重对齐,检查 PC 侧是否被防火墙/杀软拖慢
跳转 APP 后起步就卡死跳转前网络通道没关干净,见 LV025-基于TFTP的网络升级实验 第六节

六、 小结与参考资料 ​

1. 本篇小结 ​

  • 实验的三个层次:ping(验证 IP/ICMP/ARP)、TCP 连通性(nc 握手)、真实升级(发 IAPBIN01 帧)。
  • 上电日志的五行分别对应链路层、网络层、收包路径、协议栈就绪与 TCP 通道,任何一行不对都能立刻缩小排查范围。
  • TCP 通道是单连接的,建连时独占帧状态机、断连时归还串口;go / reboot 走 TCP 同样有效。
  • 收包有中断与轮询两种模式,靠宏切换;中断模式必须写 1 清 ISR,否则只有第一次能收到包。
  • 观测点分两层:常开的串口日志 + 可开关的驱动计数;计数用来区分"中断没来""主循环没取""内存不够"。
  • 开发顺序是"先硬件与协议栈、再通道与协议",这个顺序本身就是一套排错方法论。

2. 参考资料 ​


本文档由 markdowncli 技能辅助生成