尧图精选

STM32嵌入式FTP服务器实战:整合FreeRTOS、FATFS和LWIP

🕒 发布时间:2026/9/10 0:14:31 📁 来源:尧图网络
简介STM32F4平台上集成FreeRTOS、FATFS与LWIP的完整工程面向需要实现嵌入式设备与Linux/Windows主机双机通信的开发者主要解决通过FTP协议在板端与上位机之间收发文件的问题。资源以RAR格式打包共322个文件、约1.87MB源码以C语言为主包含130个.c与150个.h文件覆盖FATFS文件系统、LWIP网络协议栈、STM32F4以太网与SDIO驱动、TCP socket通信及FTP服务端逻辑另有启动汇编文件、IAR工程配置文件ewp/ewd/eww和说明文档等目录结构清晰便于直接导入编译。包内还包含中文编码转换、mib2网络管理信息库等模块可为实际产品中的中文文件名和网络管理提供参考。使用这套代码可以较快搭建起基于FreeRTOS多任务环境的FTP服务框架节省系统移植和协议调试时间同时也能从任务调度、文件读写与网络收发的联动实现中理解嵌入式综合应用的完整链路。目前已有3178人学习下载适合有一定单片机基础、想深入掌握嵌入式网络与文件系统综合应用的开发者学习参考。 做过带网口的嵌入式项目迟早都会遇到一个需求让设备能够像一台小型服务器一样被上位机或者同事的电脑直接访问文件。我这次在STM32F407上完成了一个整合FreeRTOS、FATFS和LWIP的工程目标是让开发板通过网线接入局域网后可以像普通FTP服务器一样被访问、上传和下载文件。这个项目不算特别复杂但把实时操作系统、文件系统和网络协议栈三样东西拼到一起再把FTP服务跑通中间有不少细节值得记录下来。如果你是正在做类似嵌入式网络应用、想给设备加文件远程管理功能的开发者这篇文章应该能帮你少走不少弯路。1. 整体架构设计为什么把这三样东西放在一起1.1 这个工程解决了什么问题很多MCU应用跑着跑着会积累出日志文件、配置文件、采集数据这些数据存在SD卡或者Flash里。在没有网络功能的时候想要把数据取出来只能通过串口、USB或者干脆拔卡。这在产品原型调试阶段特别痛苦——我调试设备日志时经常要蹲在设备旁边插拔SD卡效率太低。FTP服务器功能的引入本质上是给设备加了一个远程文件窗口。PC上的FTP客户端比如FileZilla、Windows资源管理器通过局域网就能直接浏览、下载和上传文件。STM32F407在主频168MHz、带FPU和以太网MAC的情况下跑一个轻量级FTP服务完全够用。搭配FreeRTOS是为了让网络协议栈、文件系统访问和FTP逻辑各跑各的互不阻塞搭配FATFS是为了让文件存取有标准接口同时也让PC端FTP工具能直接识别文件系统结构。1.2 系统角色划分与数据流我先说清楚这套系统里每个模块的职责避免后面讲实现的时候一头雾水。STM32F4以F407为例硬件平台。内置MAC控制器外接PHY芯片我用的是LAN8720ARMII接口负责以太网数据链路。FreeRTOS调度核心。提供任务、信号量、互斥锁、队列让网络收包、协议处理、FTP命令响应、文件IO能在同一个CPU上分时复用。LWIP网络协议栈。提供TCP/IP协议实现FTP底层依赖TCP传输。LWIP还抽象了网卡接口我需要提供底层数据收发函数。FATFS文件系统层。SD卡通过SDIO接口挂载为FAT32分区FATFS负责文件读写、目录枚举等操作。FTP服务逻辑应用层。监听TCP 21端口解析FTP命令USER、PASS、LIST、RETR、STOR、PWD、CWD等并根据命令调用FATFS接口读写文件。数据流大概是这样的PC FTP客户端发送命令 - TCP传输 - LWIP协议栈 - FTP应用任务解析 - 调用FATFS操作SD卡文件 - 结果返回 - 数据连接传文件内容。整个过程里FTP控制连接和数据连接是分开的这在后面实现时要特别注意。1.3 为什么不用现成的FTP服务器库网上确实有开源的嵌入式FTP服务器实现有些也宣称直接移植就行。但我个人建议不要直接抄。原因有几个第一很多库是裸机思路写的没有和RTOS配合做好任务阻塞和互斥保护第二大多数库对FATFS的封装是能用就行没有考虑多客户端并发访问、长文件名、中文文件名这些实际问题第三FTP协议本身不复杂自己写一遍顺手还能按需裁剪——比如我不想开放匿名访问那就可以在代码里写死账号校验。当然完全不看现成实现也不现实。我的做法是参考lwip contrib里的示例代码理清控制连接和数据连接的状态转换再按FreeRTOS的思路重新组织任务和信号量。2. 关键技术点任务划分、内存规划与文件系统保护2.1 FreeRTOS任务怎么划分才合理先看整体任务列表这个是很典型的嵌入式网络应用划分方式任务名称优先级栈大小职责ethernetif_input高4512轮询网卡驱动将收到的网络包投递到LWIP协议栈tcpip_thread中31024LWIP自带处理TCP/IP协议栈核心逻辑ftp_server中21024FTP控制连接处理命令解析与响应ftp_data中21024FTP数据连接处理文件发送与接收sd_card_task低1512挂载检查、FATFS维护、热插拔检测可选你可能会问为什么tcpip_thread和ftp_server不是同一个任务我最初也试图在收到TCP数据后直接解析FTP命令、读写文件但很快发现这样会把LWIP的回调上下文拖慢——LWIP的tcpip_thread主要靠邮箱和信号量驱动一旦在里面做阻塞式文件读写SD卡有时候会被占用整个网络协议栈都会被卡住导致其他协议比如ping、后续的HTTP服务全部超时。所以正确做法是LWIP的tcpip_thread只管网络协议处理FTP应用逻辑单独一个任务通过LWIP提供的tcp_recv回调函数把收到的数据放入队列或直接写入一个buffer然后由ftp_server任务唤醒处理。这样文件操作的耗时就不会阻塞协议栈这是整个架构设计上最值得注意的一个点。2.2 内存池与堆栈分配经验单片机上跑网络协议栈文件系统最怕的就是内存不够。STM32F407内部RAM有192KB我开启了外部SDRAM1MBFMC接口作为大块缓冲。但FreeRTOS和lwIP的堆我建议放在内部RAM因为访问速度快且外部SDRAM的时序在某些低功耗模式下可能有问题。关于LWIP的内存配置我会重点检查这几个宏#define MEM_SIZE (32 * 1024) #define MEMP_NUM_PBUF 16 #define MEMP_NUM_TCP_SEG 32 #define PBUF_POOL_SIZE 32 #define TCP_MSS 1460 #define TCP_WND (4 * TCP_MSS) #define TCP_SND_BUF (4 * TCP_MSS)TCP_MSS是1460这是以太网MTU1500字节减去IP头20字节和TCP头20字节后的值。数值太大网络分片太小传输效率低。TCP_WND是接收窗口我设成4倍MSS也就是约5.8KB用FTP传文件时吞吐量基本能跑满10MbpsF407LAN8720在RMII下典型百兆以太网。MEM_SIZE是LWIP内部堆大小给到32KB比较稳妥如果同时开了多个TCP连接FTP控制连接数据连接再加上调试用的telnet这个值不能太小。FreeRTOS方面各任务的栈大小我上面表格里写了。要特别提醒一点ftp_server任务栈至少要1KB以上因为FTP命令解析里如果用了scanf、sprintf这类格式化函数栈消耗会很大。我实测过用标准库的vsnprintf时1024字节栈会比较紧张换成轻量化的snprintf实现后会好很多。建议在调试阶段开启堆栈水印检查// 在任务创建后用这个API打印剩余最小栈空间 UBaseType_t minFreeStack uxTaskGetStackHighWaterMark(ftpServerTaskHandle); printf(ftp_server task min free stack: %d\r\n, minFreeStack * 4);这个数字比printf直接看栈顶更准确。我调试时发现FTP任务在长时间连续LIST大目录时栈占用会显著上升就是因为路径拼接和文件名缓存导致的。2.3 FATFS并发访问与互斥保护FATFS本身不是线程安全的。如果FTP服务器同时开两个会话一个在上传文件另一个在读取目录两个任务同时调用f_open、f_read极有可能造成文件系统错乱。FATFS官方其实提供了_FS_REENTRANT配置项打开后需要提供ff_req_grant和ff_rel_grant两个函数可以在里面加FreeRTOS互斥锁。#define _FS_REENTRANT 1 #define _FS_TIMEOUT 1000 // 单位是OS tick // 在ffconf.h或者系统接口文件中实现这两个函数 int ff_req_grant(FATFS *fs) { // 用互斥锁保护FATFS的可重入访问等待上限1000 tick return xSemaphoreTakeRecursive(fsMutex, _FS_TIMEOUT) pdTRUE; } void ff_rel_grant(FATFS *fs) { xSemaphoreGiveRecursive(fsMutex); }这里有个坑很多人只加普通互斥锁但在高优先级任务占用互斥锁时低优先级任务就拿不到锁FTP服务器端的ftp_data任务和ftp_server任务如果优先级相同还可能出现互相等待的情况。我建议用xSemaphoreCreateRecursiveMutex递归互斥锁配合优先级继承机制能有效避免优先级反转。什么场景会发生优先级反转如果FTP数据任务拿着文件系统锁做慢速SD卡写操作此时FTP控制任务想访问文件系统但调度器因为同优先级不会让控制任务抢占数据任务这时候低优先级的其他任务反而可能先运行——处理不好就会出现数据任务卡死、控制任务无响应。另外记得在初始化时挂载文件系统FRESULT res f_mount(sd_fs, 0:, 1); // 立即挂载 if (res ! FR_OK) { // 检查SD卡是否插入、文件系统是否为FAT32 }3. LWIP移植与网卡驱动配置细节3.1 CubeMX生成工程还是手写移植两种方式我都尝试过。CubeMXSTM32CubeMX的优点是快、配置可视化几步就能生成带ETHLWIPFreeRTOS的工程骨架。缺点是生成的代码层次多、有较多不需要的中间层对内存紧张的项目不太友好。我本次的工程核心逻辑比较多所以我用CubeMX生成基础配置但禁用了它的LWIP应用层示例只保留驱动和协议栈底层然后自己在应用层写FTP服务。这样既省时间又不会受示例代码干扰。如果你完全不用CubeMX手写移植的话需要重点关注这几处网卡驱动low_level_output函数负责把LWIP的pbuf链转成DMA描述符能处理的格式并触发发送。low_level_input函数从DMA接收描述符中提取数据构造成pbuf交给ethernetif_input任务。PHY地址检查。LAN8720A的地址一般通过PHYAD0引脚配置我这块板子设成0所以eth_phy地址填0。地址不对的话Link状态检测永远失败。3.2 RMII接口与PHY芯片初始化要点F407的以太网MAC支持MII和RMII两种接口。RMII只需要2根数据线RXD[1:0]、TXD[1:0]、一根时钟线REF_CLK50MHz和MDIO/MDC管理接口引脚占用少是目前小板子的主流方案。我用的是LAN8720A注意它要求REF_CLK由外部有源晶振或MCU的MCO引脚提供50MHz时钟这里不要接错否则PHY芯片内部时钟不对网口完全起不来。初始化PHY时我习惯用MDIO读取PHY的ID寄存器来做自检再配置自动协商// 软件复位PHY phy_write(0, 0x8000); // 等待复位完成 while ((phy_read(0) 0x8000) ! 0); // 配置自动协商开启全双工100M phy_write(0, 0x1200); // BMCR: 自动协商使能重启自动协商连上之后可以用HAL_ETH_GetLinkState或者自己通过PHY的BSR寄存器检查Link状态。我调试时踩过一个坑CubeMX生成的LWIP示例里会有一个MX_LWIP_Process函数需要周期调用如果用了RTOS应该放到tcpip_thread里由平台自动调用而不是自己再开一个while循环去调用否则会造成重复处理网络包。3.3 网络参数静态配置FTP服务器需要固定IP地址才方便PC端连接。我在lwipopts.h里禁用DHCP直接静态配置IP4_ADDR(ipaddr, 192, 168, 1, 100); IP4_ADDR(netmask, 255, 255, 255, 0); IP4_ADDR(gw, 192, 168, 1, 1);需要注意的是如果PC是无线网卡Windows防火墙可能会拦截入站TCP连接此时要放行21端口和主动模式数据端口20或者干脆用被动模式让服务器自己开放一个高端口比如5000-5009做数据连接。我后面会详细说这个坑。4. FTP服务器实现控制连接、数据连接与常见模式4.1 FTP协议基本交互流程FTP协议表面上看着简单但实际实现时状态机很多。核心是两条TCP连接控制连接客户端连接到服务器21端口发送明文命令服务器返回响应码如220欢迎、331需要密码、230登录成功、221退出。数据连接在有LIST、RETR、STOR、CWD等需要传数据的命令时建立另一条TCP连接来传数据。数据连接有两种模式主动模式PORT客户端告诉服务器你来连我某个端口服务器主动去连接客户端的端口。容易受客户端防火墙限制。被动模式PASV服务器开放一个端口并告诉客户端你来连我客户端主动连服务器的这个端口。对客户端更友好能穿透不少NAT。我工程里两个模式都实现了但默认用PASV因为FileZilla这类客户端默认也是PASV。关于数据连接的建立我初始化了一个监听socket并绑定到随机端口或者固定范围端口在LIST等命令响应时把它返回给客户端。下面是我简化后的FTP命令响应实现思路void ftp_handle_command(char *cmd, int sock) { if (strncmp(cmd, USER, 4) 0) { send_response(sock, 331 Password required\r\n); } else if (strncmp(cmd, PASS, 4) 0) { // 校验密码示例只做固定账号 if (check_account(cmd[5]) 0) send_response(sock, 230 Logged in\r\n); else send_response(sock, 530 Login incorrect\r\n); } else if (strncmp(cmd, PWD, 3) 0) { char path[128]; f_getcwd(path, sizeof(path)); char resp[160]; snprintf(resp, sizeof(resp), 257 \%s\ is current directory\r\n, path); send_response(sock, resp); } else if (strncmp(cmd, TYPE, 4) 0) { // 支持ASCII和二进制实际传输时我一直以二进制方式处理 send_response(sock, 200 Type set\r\n); } else if (strncmp(cmd, PASV, 4) 0) { setup_pasv_mode(sock); } else if (strncmp(cmd, LIST, 4) 0 || strncmp(cmd, NLST, 4) 0) { handle_list(sock); } else if (strncmp(cmd, RETR, 4) 0) { handle_retr(cmd[5], sock); } else if (strncmp(cmd, STOR, 4) 0) { handle_stor(cmd[5], sock); } else if (strncmp(cmd, CWD, 3) 0) { handle_cwd(cmd[4], sock); } }4.2 LIST和文件枚举实现LIST命令的响应格式要符合FTP标准FileZilla才能正确显示文件名、大小、日期。最简单的格式是Unix风格-rw-r--r-- 1 owner group 12345 Jan 18 10:30 test.txt在嵌入式环境里月份和日期我是直接用FATFS的文件时间结构体直接转的。这里要注意FATFS默认没有开启长文件名的话f_readdir返回的文件名是8.3短格式中文名会乱码。所以一定要在ffconf.h里配置#define _USE_LFN 2 // 使用堆栈动态分配长文件名缓冲区 #define _MAX_LFN 255 // 长文件名最大长度配置完之后FILINFO结构体里有个fname是短文件名lfname指向你提供的缓冲区用来接收长文件名。我踩过坑是忘了给lfname分配缓冲区导致读取的文件名全是空的。正确做法static FILINFO fileInfo; static char lfname[_MAX_LFN 1]; fileInfo.lfname lfname; fileInfo.lfsize sizeof(lfname); while (f_readdir(dir, fileInfo) FR_OK fileInfo.fname[0] ! 0) { // fileInfo.fname 是短文件名lfname 是长文件名如果存在 }4.3 RETR和STOR的文件传输细节RETR下载的关键步骤解析文件名和路径调用f_open。开启数据连接PASV模式时等待客户端连接。循环调用f_read读出一块数据比如4KB再用LWIP的netconn_write或lwip_send发送。传输完成关闭文件、关闭数据连接返回226响应。STOR上传则是反过来从数据连接读数据、写SD卡。这个过程中最需要注意的是超时处理。如果客户端连上了数据连接但迟迟不发数据或者传一半断网了你的recv函数会一直阻塞导致FTP任务卡死。解决办法是在LWIP的socket层设置接收超时struct timeval timeout; timeout.tv_sec 10; timeout.tv_usec 0; setsockopt(dataSock, SOL_SOCKET, SO_RCVTIMEO, timeout, sizeof(timeout));每次读写都检查超时返回超时就主动关闭连接释放任务这样单个客户端异常不会拖垮整个服务器。还有一个细节每块读写大小要匹配SD卡扇区。SD卡扇区一般是512字节我读文件用4KB缓冲写文件也是4KB缓冲能减少FATFS的底层IO次数提升吞吐量。实测同样一个文件1KB缓冲写入比4KB缓冲慢了将近4倍。5. 联调过程、常见问题与排查技巧实录5.1 文件传输卡死、FileZilla提示无法与服务器建立连接这个报错基本可以按顺序排查先ping通设备IP。ping不通就先解决网络链路重点查PHY初始化、网线、IP配置。telnet测试21端口。在PC上用telnet 192.168.1.100 21如果能看到220响应说明控制连接正常。检查防火墙。Windows自带防火墙经常会拦截入站21端口解决方法是添加入站规则或者在FTP客户端里设置为主动模式。FileZilla还经常提示不支持的FTP over TLS警告这只是客户端提示不代表连接失败可以忽略或关掉加密要求。确保局域网内没有其他设备占用21端口。比如有些路由器管理界面本身开的就是21端口如果你测试设备IP和路由器IP冲突就会出现奇怪的连接失败。5.2 FTP复制文件出错或传输一段时间后断开这种问题很典型是嵌入式网络服务器最容易遇到的。我排查结束后发现两种原因一是LWIP内存池不够。传输大文件时接收窗口和发送缓冲需要较多PBUF如果PBUF_POOL_SIZE和MEMP_NUM_TCP_SEG太小高负载下协议栈会丢包、重传最终客户端报错。调大这两个值后问题明显改善。二是FATFS的文件系统锁冲突。我一开始测试时开着两个FTP会话一个在下文件另一个在浏览目录结果偶尔出现FR_DENIED错误。这就是前面说的并发访问问题互斥锁必须加在FATFS的访问层而不是只在单个FTP命令里加锁。另外注意文件名大小写。Windows资源管理器访问FTP时默认可能把文件名转换成小写但FATFS对大小写敏感。虽然FAT32在存储上不区分大小写但是FATFS会按创建时的大小写返回如果用户在SD卡里存的是Test.TXT用Windows资源管理器输入test.txt去下载可能会报文件未找到。5.3 资源管理器直接访问FTP与双机互传Windows资源管理器访问FTP非常简单地址栏输入ftp://192.168.1.100它会弹出登录框输入用户名密码登录后就能像本地文件夹一样浏览和拖拽文件。这个功能在开发调试时特别好用实测下来Windows自带的FTP客户端走的是标准FTP协议兼容性比第三方客户端差点——它在LIST命令解析上比较严格如果格式不对目录列表可能会空白。所以如果资源管理器显示不出文件先用FileZilla测确认服务器LIST输出符合标准再回到资源管理器。如果你只是想在两台PC之间传文件其实不需要专门的FTP服务器软件Windows自带的Internet Information Services (IIS)里就带了FTP服务装好之后启个站点即可。个人使用的话也可以选择FileZilla Server这类轻量方案。但如果是嵌入式设备要实现远程取数那还是老老实实把FTP服务器做进固件里。5.4 调试技巧LWIP日志、Wireshark抓包与任务栈水位网络调试离不开抓包。我强烈建议在PC端用Wireshark抓包配合设备端打印LWIP调试信息来定位问题。Wireshark抓包时直接抓设备的IP即可重点看TCP握手有没有完成、FTP命令有没有被正确响应。一个非常实用的判断技巧如果FTP登录成功但LIST不出目录十有八九是数据连接没有建立成功。这时候抓包能看到客户端发PASV服务器返回227 Entering Passive Mode (x,x,x,x,p1,p2)然后客户端去连接p1*256p2端口。如果你看到客户端发了TCP SYN但服务器没有任何响应那就是服务器端监听数据端口的socket没有正确listen或者绑定的IP不对。任务栈水位检查前面说过了另外一个经验是不要在所有地方都用printf打日志。我在FTP数据收发循环里加过printf做调试结果吞吐量掉到几乎不可用——printf在串口上输出的耗时比网络传输大得多。正确的做法是开一个环形日志缓冲区只记录关键状态等系统空闲时再输出。5.5 SD卡热插拔与掉电保护嵌入式FTP服务器如果把SD卡当作存储介质一定要考虑热插拔场景。如果客户端正在下载文件时把SD卡拔了FATFS的底层读函数会返回错误但你可能在已经打开的FIL结构体上继续操作轻则文件损坏重则挂掉系统。我的处理方式是在sd_card_task里周期检测SD卡是否在位用GPIO检测卡检测引脚如果发现卡被拔出就全局置一个标志位FTP的每次文件操作前检查这个标志并且在文件操作中如果检测到FR_NOT_ENABLED等错误码立刻关闭连接、释放资源。这个逻辑虽然简单但能有效避免因为用户误操作带来的死机和数据损坏。再一个建议是确保掉电时FATFS的写入缓冲区及时落盘。可以用f_sync在文件关闭或定期调用把脏数据刷到SD卡上。不要依赖关闭文件时才刷盘因为FTP连接如果异常断开客户端已经传完的数据可能还在FATFS缓存里直接掉电会导致文件不完整。6. 实测数据与性能优化建议6.1 我实测的吞吐量和CPU占用在STM32F407168MHzSDIODMA方式访问SD卡FTP走百兆以太网的环境下我测了一组数据操作实测平均速度说明下载大文件10MB约850KB/s瓶颈主要在SD卡读取速度上传大文件10MB约700KB/sSD卡写入速度略慢于读取列出1000个文件的目录1.2秒左右主要耗时在FATFS遍历CPU占用持续传输约60%-70%存储和网络DMA占用不少CPU带宽百兆以太网理论上限是12.5MB/s但F407的CPU性能和SD卡IO能力决定了不可能跑满。实测850KB/s对绝大多数嵌入式日志采集、配置管理场景已经够用了。如果想进一步提升可以考虑把FATFS的扇区大小调成4096字节如果SD卡是4KB扇区减少底层IO次数。6.2 如何进一步优化性能启用DMA传输无论是SDIO还是以太网DMA都要确保开启DMA避免CPU逐字节搬运数据。CubeMX默认会开启但手动移植时要格外留意。调整TCP窗口和缓冲区上面讲过的TCP_WND、TCP_SND_BUF可以适当增加到8倍MSS对大文件传输有正收益但内存占用也会上升。考虑双缓冲文件读写设置两个4KB缓冲一个在读/写数据、一个在DMA搬运能够把IO延迟隐藏起来。实现上稍微复杂但对吞吐量提升明显。避免在传输循环中做耗时操作比如不要在每个数据块后printf、不要频繁开关LED、不要动不动就查询传感器。6.3 功能扩展FTP之外还能做什么这套架构的价值远不止FTP。因为你已经打通了RTOS 文件系统 网络协议栈这组基础设施后续至少可以很自然地扩展HTTP服务器通过网页浏览下载SD卡里的日志文件。固件OTA升级接收固件包写入Flash或SD卡配合Bootloader实现远程升级。MQTT或Modbus TCP把采集数据上报到云端或者SCADA系统。NFS/CIFS如果你有足够RAM直接把SD卡共享成网络驱动器PC端映射为本地磁盘。我自己后续就在这个工程基础上加了HTTP上传固件的功能底层全是复用已有的TCP socket和FATFS接口开发效率非常高。7. 踩坑记录与经验总结做一个整合多个中间件的嵌入式工程最大的敌人不是单个模块而是模块之间的交互。我在这套FTP服务器工程里踩过的坑值得记录下来的主要有这几个。坑一LWIP内存不足导致的假死现象。现象是刚开始FTP能连上传输几个文件后整个网络无响应。排查到最后是MEMP_NUM_TCP_SEG太小TCP重传时无法分配新段协议栈内部死锁。把MEMP_NUM_TCP_SEG从16改成32就好了。这提醒我做网络应用时内存配置不是拍脑袋给的要根据最大并发连接数和传输窗口算一下。坑二FATFS长文件名缓冲区必须提供且足够大。最初我省事没开_USE_LFN结果PC端创建的中文文件名在设备上显示为乱码。打开长文件名支持后注意FILINFO里的lfname指针必须指向有效缓冲区否则f_readdir返回的文件名是空的这个错很容易被忽略。坑三别在LWIP进程回调里做阻塞操作。我最初尝试在tcp_recv回调里直接处理FTP命令结果只要SD卡忙整个TCP/IP协议栈就卡住ping设备都不通。把它们拆成独立任务后问题消失。这是嵌入式网络编程的重要原则协议栈回调要快速返回耗时操作交给任务上下文处理。坑四Windows资源管理器连接FTP时兼容性问题。如果你的LIST输出少了一个\r\n或者格式不对资源管理器就显示不出目录但FileZilla可能能用。调试时建议两种客户端各测一遍优先保证标准FTP格式正确。最后分享一个我后来一直保留的小习惯把LWIP、FreeRTOS、FATFS的版本号和关键配置宏都打印在启动日志里。这样在维护老设备时一个人能立刻知道固件用的哪套配置排查问题省非常多时间。比如某次客户报障说上传大文件会失败我一看日志发现他用的固件PBUF_POOL_SIZE是老版本配置的16立刻知道是内存池问题让他升级了新固件就好了。这个小习惯在嵌入式网络项目里特别值得推广。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →