每次修改完内核代码都要重新烧写eMMC?调试设备树时频繁插拔SD卡?这种开发效率简直让人崩溃。我在做IMX6UL项目时就深有体会——平均每天要重复烧写30多次,直到发现了TFTP这个神器。
TFTP(Trivial File Transfer Protocol)就像嵌入式开发的"外卖小哥",它能通过网线把编译好的zImage内核镜像和设备树文件(dtb)直接从你的开发机送到板子内存里。配合U-Boot的灵活引导能力,可以实现"开发机编译→网线传输→内存运行"的极速调试闭环。实测下来,从代码修改到板子运行新内核,最快只需12秒。
这种方案特别适合以下场景:
在Ubuntu上搭建TFTP服务比想象中简单,我习惯用tftpd-hpa这个实现:
bash复制sudo apt install tftpd-hpa
sudo systemctl enable tftpd-hpa
关键配置在/etc/default/tftpd-hpa文件中:
bash复制TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/tftpboot" # 这是存放镜像的目录
TFTP_ADDRESS=":69"
TFTP_OPTIONS="--secure --create"
记得给目录赋权并重启服务:
bash复制sudo chmod 777 /tftpboot
sudo systemctl restart tftpd-hpa
测试时可以用本机自测:
bash复制echo "test" > /tftpboot/test.txt
tftp localhost -c get test.txt
如果用Windows开发,推荐这些实测可用的方案:
遇到过最坑的问题是Windows Defender防火墙拦截,解决方法是在高级安全设置里为TFTP添加入站规则,允许UDP 69端口。
在U-Boot命令行中,这几个环境变量最关键:
bash复制setenv serverip 192.168.1.100 # TFTP服务器IP
setenv ipaddr 192.168.1.101 # 开发板IP
setenv netmask 255.255.255.0 # 子网掩码
setenv gatewayip 192.168.1.1 # 网关(非必须)
setenv ethaddr 00:15:5d:4e:3f:01 # MAC地址(防冲突)
建议先用ping测试连通性:
bash复制ping 192.168.1.100
如果出现"host 192.168.1.100 is alive"就说明网络通了。遇到过最头疼的问题是PHY芯片初始化失败,这时需要检查:
bash复制mii device
mii info
默认的自动协商模式可能只有10Mbps,可以强制设为百兆:
bash复制setenv ethprime FEC0
setenv ethact FEC0
setenv eth1addr 00:15:5d:4e:3f:01
phyconfig FEC0 speed 100 duplex full autoneg off
对于IMX6系列,还可以调整时钟:
bash复制mw.l 0x020c8168 0x00000000
mw.l 0x020c816c 0x00000000
分步执行适合初次验证:
bash复制# 加载内核到内存0x80800000
tftp 0x80800000 zImage
# 加载设备树到0x83000000
tftp 0x83000000 imx6ul-14x14-evk.dtb
# 启动内核
bootz 0x80800000 - 0x83000000
常见问题排查:
终极方案是改造bootcmd实现全自动加载:
bash复制setenv mmcboot 'echo "TFTP Loading..."; \
tftp 0x80800000 zImage; \
tftp 0x83000000 ${fdt_file}; \
bootz 0x80800000 - 0x83000000'
setenv bootcmd 'run mmcboot'
saveenv
这个脚本实现了:
当开发板有多个网口时,需要指定活动网卡:
bash复制setenv ethact usb_ether # 使用USB网卡
setenv ethact FEC0 # 使用第一个以太网口
传输超过16MB的内核时,需要调整U-Boot参数:
bash复制setenv tftpblocksize 1468 # 默认512容易超时
setenv tftptimeout 5000 # 超时设为5秒
误操作导致无法启动时,可以通过串口中断U-Boot,然后:
bash复制env default -a # 恢复默认环境
saveenv # 保存
reset # 重启
对于NAND设备,可能需要擦除环境分区:
bash复制nand erase.part env
在智能家居网关项目中,我们通过这套方案实现了:
有个特别实用的技巧是使用版本号后缀:
bash复制setenv image_name zImage-$(git rev-parse --short HEAD)
tftp ${loadaddr} ${image_name}
这样每次烧录都能明确知道对应的代码版本,再也不会出现"这个内核是谁编译的"这种灵魂拷问。