简介面向嵌入式开发者的STM32F103ZE FATFS实验资源包围绕在STM32上基于HAL库配置FATFS文件系统、生成并写入CSV文件的完整实现展开适合需要做SD卡数据记录或日志存储的物联网、工业控制初学者。资源共138个文件、压缩包1.79MB以C源文件与头文件为主辅以PNG操作截图、TXT说明、CSS以及Keil工程配置uvprojx/uvoptx便于使用者快速定位核心代码、实验现象和工程环境。已有1451人学习下载。包内不仅包含FATFS模块还整合了SDIO驱动、LCD显示、定时器、编码转换表等文件并附带keilkilll等辅助脚本实验说明给出了CubeMX下GPIO、SDIO/SPI及时钟的配置思路以及f_open、f_write、f_close写入CSV数据的示例流程。利用开发板配合工程目录可对照源码理解SD卡访问、文件系统挂载和CSV格式化写入的完整链条对数据采集、存储类项目有直接借鉴价值。 上个月做批量测试设备的数据记录功能需要在STM32F103ZE上跑一套文件系统把传感器采集到的数据写到SD卡里最后一查文件一堆乱码来回折腾了两三天才把CSV文件搞利索。总有人私信问我FATFS怎么配合CSV用干脆把整个实验从方案设计到代码实现再到排查过的所有坑完整写出来给正准备做数据记录功能的兄弟一个可以直接抄的参考。这套方案说简单也简单STM32F103ZE通过SPI接口驱动SD卡模块板子上跑FATFS文件系统把采集到的数据按CSV格式组织成文本文件写入。做完之后把SD卡拔下来插电脑Excel或者WPS直接打开一分钟都不用数据就能看能分析。适合做设备日志、传感器记录、工控参数回传这类场景也适合刚接触文件系统移植的嵌入式开发者作为练手项目。1. 实验定位与整体方案设计1.1 为什么选STM32F103ZE和FATFS做数据记录先说芯片。STM32F103ZE是经典F1系列里的高配型号叫ZET6也行512KB Flash加64KB SRAMFlash空间对FATFS这种体积轻薄的文件系统来说宽裕得很。RAM方面FATFS内核加上文件缓存实测跑起来算上其他业务代码SRAM占用也就十几KB完全不用担心资源不够。真正让我反复推荐F1系列的原因是外设稳定。SPI、SDIO这些接口在F1上被验证了十几年时序稳定资料铺天盖地遇到问题查得到人问得到路。你要是拿个新出的芯片折腾FATFS底层驱动就得先啃半天而F1的SPI驱动代码到处都有现成的踩坑成本低很多。再说文件系统。FATFS是ChaN老师写的开源FAT文件系统模块轻量到只有几个C文件专门为嵌入式环境设计。它支持FAT12、FAT16、FAT32API函数和PC上操作文件的习惯几乎一一对应f_open、f_read、f_write、f_close不用重新学一套思维模型。CubeMX自带FATFS中间件勾选一下配置就能用省掉了手写底层的麻烦。为什么不直接用SD卡裸读写呢因为数据管理的需求是长期可持续的。裸读写你得自己规划扇区地址、记录数据长度、处理覆盖策略换一台设备再读数据就是噩梦。FATFS做完之后SD卡就是一个标准U盘任何读卡器都能读这体验是裸读写给不了的。1.2 CSV格式为什么是日志数据的最佳载体CSV的全称是Comma-Separated Values逗号分隔值本质上就是一个纯文本文件每一行是一条记录逗号分隔字段换行符结尾。它的好处太明显了第一生成简单单片机上用f_printf格式化输出几行文本就行不涉及任何数据库或者二进制结构第二通用性极强Excel、WPS、Python的pandas、R、MATLAB全都认识它导出即可分析不需要专门的解析工具第三可读性好记事本打开也能看到数据出了问题人工排查也方便。有人可能会说用二进制格式存储不是更节省空间吗对比如一个float温度值转成4字节二进制一个文件只有几十KB好像比CSV文本省很多。但代价是读取必须配套解析程序否则就得手写脚本对二进制结构做解码。而且长时间运行的数据记录文本格式可追溯性更强出问题快速定位是靠文本不是靠二进制流。对于数据量不大的设备日志场景CSV的存储开销完全可以接受。方案选型总结成一张表对比就很直观方案生成难度电脑端读取可靠性适用场景SD卡裸写低需写解析工具一般临时数据暂存二进制文件FATFS中需配套解析程序较高大规模、高频率采样CSVFATFS低Excel/WPS/Python直接打开高日志记录、参数回传2. 软硬件准备与CubeMX工程搭建2.1 硬件连接清单硬件部分非常常规核心就三样一块STM32F103ZET6开发板、一个SPI接口的SD卡模块、一张Class10的TF卡用卡套转成大卡也行。注意SD卡模块的长得五花八门但原理都一样买的时候认准带电平转换方案的模块以免5V的逻辑电平把SD卡打死。SPI模式接线相当固定模块上六个引脚对应关系如下模块引脚STM32引脚说明CSPB12SPI2片选软件控制SCKPB13SPI2时钟MOSIPB15SPI2主发从收MISOPB14SPI2主收从发VCC3.3V必须3.3V供电GNDGND共地千万不要给模块供5V电这个坑我亲眼见过好几个刚开始工作正常跑了几分钟就初始化失败测了下VCC引脚上的电压纹波大得吓人。SD卡工作在3.3V逻辑下5V供电轻则发热重则直接烧毁芯片。TF卡建议在电脑上先把文件系统格式化好选FAT32格式分配单元大小默认就行。FATFS其实支持FAT16和FAT12但FAT32兼容性最好。卡速倒不用太纠结Class4以上的都能跑不过实测Class10在高SPI时钟下出错率更低。卡容量建议8GB以内SDSC标准的老卡在FATFS下兼容性反而不如SDHC卡。2.2 CubeMX关键配置工程用STM32CubeMX生成选择芯片STM32F103ZETx然后按下面步骤操作。RCC设置HSE选择Crystal/Ceramic Resonator启用外部高速晶振系统时钟的精确性对SD卡通信很重要。SPI2配置选Full-Duplex Master参数上先保守一点把时钟极性设置为Low相位设置为1 Edge速率分频设置到Prescaler 16。为什么要低速起步SPI模式跑SD卡初始化阶段SCK必须低于400kHz这是SD卡协议手册规定的硬指标。CubeMX里SPI2接的APB1总线时钟是36MHzPrescaler 16对应2.25MHz初始化阶段偏高了一般情况下能跑但某些卡会不认。我的做法是初始化阶段单独把SPI速率降到低速初始化完成后再切高速代码里两个GPIO配置切换后面代码部分会讲。FATFS配置Middleware分组下勾选FATFSMode选择SD Card。点开配置有个Use Long Filename选项建议勾选Enable并设置Code Page为936 (Simplified Chinese)。这个涉及长文件名和中文文件名支持后面单独讲。串口配置USART1打开用于printf调试输出。调试输出在排错阶段帮了大忙别省这一步。NVIC配置不需要特殊处理。时钟树里确保SDIO或SPI的时钟正确即可。生成工程之后切换到MDK-ARM模式在Options里勾选Use MicroLIB否则重定向printf会出问题。2.3 FATFS中间件需要理解的核心配置项CubeMX生成的FATFS会把ffconf.h暴露出来很多配置项已经在可视化界面里做了映射但几个关键参数还是值得手动确认。_USE_STRFUNC要设为1或2。这个宏控制是否启用f_printf、f_gets这类字符串操作函数。如果不开启后面用f_printf写CSV内容的时候会直接编译报错。设为2可以在换行时自动做\r\n转换但CSV文件本身要求的就是\r\n作为行结束符用方法1然后手动写\r\n更直观可控性也更好。_USE_LFN设为2启用长文件名并采用静态缓冲区。长文件名功能会消耗一部分RAM静态缓冲区方式比动态分配更稳定裸机环境跑错也少。代码页设936之后文件名里能写中文但某些系统上中文编码容易出岔子所以我实际还是建议文件名全用ASCII只有文件内容里可以带中文具体原因在踩坑部分详述。_MAX_SS建议保持默认512。这个参数定义了扇区缓冲区大小SD卡HC以上的扇区是512字节不用改成4096改大了纯浪费RAM并且有些SD卡不识别非512字节的扇区访问。3. 核心代码设计与实现3.1 文件系统挂载与首次格式化流程FATFS的使用要遵循固定套路先f_mount挂载文件系统再f_open打开文件f_write/f_printf写入内容f_sync同步f_close关闭。下面是挂载阶段的完整实现static FATFS fs; static DIR dir; uint8_t mount_sd_card(void) { FRESULT res; // 挂载文件系统到逻辑驱动器0 res f_mount(fs, 0:, 1); if (res FR_OK) { printf([INFO] mount ok\r\n); return 0; } else if (res FR_NO_FILESYSTEM) { // 首次使用或文件系统损坏需要格式化 printf([WARN] no filesystem, format...\r\n); f_mount(NULL, 0:, 0); // 先卸载当前挂载 static uint8_t work[4096]; res f_mkfs(0:, FM_FAT32, 0, work, sizeof(work)); if (res ! FR_OK) { printf([ERROR] mkfs failed: %d\r\n, res); return 1; } // 格式化完成后重新挂载 res f_mount(fs, 0:, 1); if (res ! FR_OK) { printf([ERROR] mount after mkfs failed: %d\r\n, res); return 1; } printf([INFO] format and mount ok\r\n); return 0; } else { printf([ERROR] mount failed: %d\r\n, res); return 1; } }f_mount的第三个参数1表示立即挂载。这里必须注意f_mount执行时并不真正访问磁盘介质只是注册一个FATFS对象真正的挂载动作在后续第一次f_open访问文件时才触发。因此f_mount返回FR_OK不代表SD卡一定正常只能说明API调用参数合法。要验证卡是不是真的初始化了得靠第一次文件访问的结果来判断。如果返回FR_NO_FILESYSTEM说明卡上确实没有合法引导扇区这时候可以调用f_mkfs。f_mkfs第二个参数FM_FAT32指定文件系统类型第三个参数0表示按卷大小自动决定分配单元大小这样最省心。工作缓冲区work至少512字节我给到4KB让它一次处理更多扇区。强烈建议第一次使用就用读卡器在电脑上格式化为FAT32而不是指望单片机去格式化。单片机端f_mkfs只能做基础格式化电脑上格式化出来的卡兼容性和容错都更好而且能排除文件系统本身的问题嫌疑。3.2 SPI速率切换与SD卡初始化联动SPI模式下SD卡正确工作的前提是初始化阶段低速通信。我的做法是用一个函数控制SPI分频系数挂在SD卡硬件初始化之前void sd_spi_set_speed(uint16_t prescaler) { SPI_InitTypeDef spi_init; spi_init.Mode SPI_MODE_MASTER; spi_init.Direction SPI_DIRECTION_2LINES; spi_init.DataSize SPI_DATASIZE_8BIT; spi_init.CLKPolarity SPI_POLARITY_LOW; spi_init.CLKPhase SPI_PHASE_1EDGE; spi_init.NSS SPI_NSS_SOFT; spi_init.BaudRatePrescaler prescaler; spi_init.FirstBit SPI_FIRSTBIT_MSB; HAL_SPI_Init(hspi2); } void sd_card_init_slow(void) { // PCLK1 36MHz, 分频128 - 281.25kHz sd_spi_set_speed(SPI_BAUDRATEPRESCALER_128); // 执行SD卡初始化序列发送CMD0、CMD8、CMD55ACMD41... // 初始化完成后提升速率 sd_spi_set_speed(SPI_BAUDRATEPRESCALER_4); // 9MHz }初始化SD卡的具体命令序列CMD0进入SPI模式、CMD8、CMD55ACMD41、CMD2、CMD3等在STM32官方例程和大量开源库中都有现成实现CubeMX生成的FATFS的SD卡底层驱动其实已经封装好了我们只需要在初始化阶段用低速让驱动框架去跑这个我会在FATFS中间件的底层配置里调整。用cube生成FATFS后SD卡底层使用的是平台无关的diskio接口文件叫sd_diskio.c里面调用了HAL_SPI_Transmit/Receive。如果初始化失败优先在初始化之前把SPI切到低速。FATFS有挂载超时机制只要SPI通信成功挂载一定能过。3.3 CSV文件创建、表头写入与BOM处理挂载完成后就可以正式生成CSV文件。完整代码如下FIL file; FRESULT res; // 创建一个新文件如果已存在则直接覆盖 res f_open(file, LOG_20250101.csv, FA_CREATE_ALWAYS | FA_WRITE); if (res ! FR_OK) { printf([ERROR] create file failed: %d\r\n, res); return; } // 写入UTF-8 BOM防止Excel中文乱码 uint8_t bom[] {0xEF, 0xBB, 0xBF}; UINT bw; f_write(file, bom, 3, bw); // 写入表头 f_printf(file, Time,Temperature,Humidity,Pressure\r\n); // 写入正式数据 f_printf(file, 2025-01-01 10:00:00,25.6,60.3,101.2\r\n); f_printf(file, 2025-01-01 10:00:01,25.7,60.1,101.1\r\n); // 同步数据到物理介质 f_sync(file); // 关闭文件 f_close(file);这段代码的核心在两个地方。一个是f_open的打开模式FA_CREATE_ALWAYS意味着不管之前有没有这个文件都会重建。第一次实验这么做没问题但如果是长期运行的设备每次开机都重写一个同名文件那之前的数据就全丢了。正确的做法是每次开机用当前时间拼文件名让每个文件只存一段时间的记录下面会讲。另一个是UTF-8 BOM。BOM是Byte Order Mark的三个字节EF BB BF加在文件最开头。如果文件里只有ASCII字符不加BOM没事一旦有中文内容Excel默认用ANSI编码打开UTF-8文件就会乱成一片。加上BOM之后Excel会正确识别UTF-8编码中文不乱码。这是CSV生成里最大的坑也是搜索热度能排到热搜词的好几个相关关键词的原因。f_printf使用的前提是ffconf.h中的_USE_STRFUNC已经开启设为1或2它的用法和标准库printf几乎一样支持%d、%f、%s等格式符。但注意嵌入式环境下的printf族浮点支持依赖编译器MDK下要勾选Use MicroLIB并且把浮点打印选项打开否则%f会输出一个空字符串。如果不想依赖浮点库可以把浮点数拆成整数和小数分别打印比如25.6拆成25和6然后f_printf(%d.%d\r\n, int_part, frac_part)。这个方法不依赖编译器配置稳定可靠。3.4 追加写入与f_sync数据保护设备运行过程中的数据是源源不断产生的不可能只在初始化时写一次所以纯FA_CREATE_ALWAYS思路行不通。正确的追加流程是FIL file; FRESULT res; // 打开已存在的文件不存在则创建 res f_open(file, LOG_20250101.csv, FA_OPEN_ALWAYS | FA_WRITE); if (res ! FR_OK) return; // 定位到文件末尾实现追加 res f_lseek(file, f_size(file)); if (res ! FR_OK) return; // 写一条采集数据 f_printf(file, 2025-01-01 10:00:02,25.8,59.9,101.0\r\n); // 同步数据到物理介质 f_sync(file); // 关闭文件 f_close(file);FA_OPEN_ALWAYS是打开或创建语义文件不存在就新建存在就打开。关键在f_lseek这步f_size返回当前文件字节数lseek到文件末尾就实现了追加效果。如果不做lseek默认文件指针在文件头再写入就覆盖旧数据了。f_sync这个函数的调用频率要重点说。它负责把FATFS内部缓冲区内容刷新到SD卡物理扇区防止数据停留在RAM缓存里丢失。如果设备在写完数据后马上断电没有调f_sync的话文件内容可能不完整甚至变成空文件。我的经验是数据采集频率固定的话每隔5秒或者10秒调一次f_sync具体取决于你的数据重要性和写卡性能。每条数据都调f_sync性能损失大实测发现FATFS在每512字节扇区边界处经常会产生额外写操作高频f_sync会让SD卡频繁擦写反而加速老化。低频业务比如一分钟一条的那每次写入后f_sync一次也不算浪费。另外写完数据记得f_close。f_close内部也会触发一次缓冲刷新和文件目录项更新不关闭就拔卡极大概率丢数据。有的程序跑着跑着发现CSV里最后一条记录缺失检查下来就是f_close没做或f_sync没调。3.5 日志文件自动命名策略用一个带时间的文件名可以在重启后不覆盖历史数据管理起来也方便。如果没有RTC芯片可以用启动后的运行时长做标识或者维护一个文件计数器每启动一次自动加一char filename[16]; uint16_t file_index 0; // 从EEPROM/Flash中读取上次的索引 file_index read_last_index(); // 生成文件名LOG_0001.CSV这类 sprintf(filename, LOG_%04d.CSV, file_index); res f_open(file, filename, FA_CREATE_NEW | FA_WRITE); if (res FR_EXIST) { // 如果同索引文件已存在索引加一再试 file_index; sprintf(filename, LOG_%04d.CSV, file_index); res f_open(file, filename, FA_CREATE_NEW | FA_WRITE); }FA_CREATE_NEW模式要求文件必须不存在否则返回FR_EXIST。这种按序号递增的方式比时间戳定义文件名简单直接不需要RTC芯片适合纯裸机环境。有RTC或从网络校时的当然用LOG_20250101_100203.CSV这种带日期时间的命名更直观。文件名长度有讲究。FATFS默认SFN短文件名只支持8个字符的主文件名加3个字符的扩展名。启用了长文件名功能可以支持更长的名字但为了保护兼容性一般控制在12个字符以内最稳妥。我见过有人把文件名写成一长串中文结果SD卡插到某些车载设备上认不出来最后还是改回英文短文件名正常了。4. 踩坑记录与常见问题排查4.1 SD卡挂载失败排查清单SD卡挂载失败是FATFS实验里最恶心的一个问题因为它错误信息往往模糊网上搜一圈答案都是模棱两可的。我整理了一个排查顺序按这个顺序查绝大部分问题都能定位错误码可能原因排查手段FR_DISK_ERRSPI时序不稳、电源纹波大检查供电、降低SPI速率、换根杜邦线FR_NO_FILESYSTEM卡未格式化FAT32、卡损坏读卡器连电脑重新格式化FR_NOT_ENABLEDf_mount未先调用检查挂载顺序FR_NOT_READYSD卡初始化失败、CMD0没响应确认CS引脚电平、检查VCC电压、换张卡重点排查一下供电。SD卡在SPI模式下瞬时电流能到100mA以上如果板子用的是稳压芯片输出带负载能力不足SD卡初始化过程中电压跌落就会导致CMD8响应超时。我用示波器测过最稳的做法是在SD卡模块的VCC和GND之间并一个10uF电解电容或钽电容实测很多问题电容一加上就消失了。杜邦线连接导致的问题也很隐蔽。SPI时钟在10MHz级别时杜邦线之间的寄生电容和串扰会让SD卡触发误码。如果初始化阶段没问题、高SPI速率下读写随机出错大概率就是线太长或者线材质量太差。要么把线剪短到10cm以内要么干脆画板做用FPC或PCB直接焊引。4.2 Excel打开CSV乱码问题详解CSV乱码是让无数人抓狂的问题网上相关热搜词能排一大串。乱码的根源只有两个编码不统一或者缺少BOM标记。场景一文件内容全是ASCII字符英文字母、数字、英文逗号、连字符这种情况下任何编码都不会乱UTF-8和ANSI内容完全一致。这也是我推荐日志内容全用英文表头的原因省掉编码烦恼。场景二文件里有中文内容比如表头写了“时间,温度,湿度”。这时候如果文件是UTF-8编码但没有BOMExcel在简体中文系统上默认按ANSI(GBK)解码3字节的汉字变成1-2字节的乱码。解决办法就是在文件头加EF BB BF三个字节前面代码里的bom数组干的就是这个事。加了BOM之后Excel识别为UTF-8正常显示。场景三代码内核里写的字符串编码是GBK文件却被识别为UTF-8也会乱。MDK默认源文件编码是ANSI/GBK如果你把源文件保存成了UTF-8但内容中文字符串是按照GBK的字节序列写的那么写入文件后的确是GBK字节加了BOM反而会让Excel按UTF-8解析出现双重乱码。处理思路是源文件编码和写入文件的编码必须统一要么全走UTF-8加BOM要么全走GBK不加BOM混用必炸。我的最终方案是文件内容全部用英文数字不写中文。读取方的脚本Python等自己做字段映射表格标题通常用英文或拼音。真要有中文就严格在文件头写入UTF-8 BOM并且保证MDK源文件编码为UTF-8。4.3 文件写入性能与SD卡寿命优化长期运行后文件写入越来越慢或者SD卡出现坏块这类问题和数据写入方式有关。FATFS对FAT32的处理思路是把FAT表放在靠近文件系统的区域每次新建文件都要更新FAT表来分配簇块。如果频繁创建小文件、频繁删除再创建FAT表会被频繁擦写SD卡的平均寿命随之下降。优化手段有三种第一种是增大写入粒度。不要每一条数据单独f_write一次而是攒够一个扇区或多个簇再写降低写入次数。比如温度1秒采集一次攒到16条64字节才触发一次f_write这样写入次数减少16倍。第二种是在文件内批量追加文件创建后一直保持打开状态不频繁f_open/f_close。每产生一批数据写入后调f_sync但文件句柄不释放等数据满了再关闭。FATFS的f_sync做的就是刷新扇区缓存的活相比f_open/f_close整个流程开销小得多。第三种是定期创建新文件不要让单文件体积无限增长。单片机上FAT的遍历和文件大小记录是线性的文件太大时f_lseek到末尾的时间会拉长。我一般按天或按小时切换日志文件既好排查也好管理。4.4 文件名中文乱码与长文件名兼容性前面提过长文件名的坑这里展开说一下。启用_USE_LFN之后FATFS可以读写字数超过8.3格式的文件名但电脑端嵌入式设备是否兼容是另一回事。如果文件名叫“温湿度记录_20250101.csv”写到了SD卡里在Windows电脑上看效果取决于代码页配置。CubeMX里Code Page设932就是日文936才是简体中文这个必须对应上否则中文文件名称在电脑上是乱码。另一个更隐蔽的问题是LFN会占用额外RAM。FATFS每次open文件都要准备一个特定大小的缓冲区缓冲区地址如果没对齐或者大小不足在某些编译器优化下可能莫名返回FR_INT_ERR。这个错误码排起来费劲因为表面看什么都是对的实际缓冲不够。_USE_LFN设为2是静态缓冲区只要RAM够就稳定动态分配模式_USE_LFN3在某些malloc碎片化场景下会失败。所以裸机工程首选静态方式安全第一。4.5 调试过程必开的三个日志开关排查FATFS问题时没有调试输出会非常被动。我在工程里开了三个日志SPI通信日志、FATFS返回值日志、文件命中和长度变化日志。SPI通信日志在调试SD卡初始化阶段用每次CMD发送完打印返回的R1响应码。SD卡SPI模式一切通信都是字节流对命令有固定响应格式R1响应0x00表示命令被接受正常0x01表示进入空闲状态其他值基本都是错误。打印了响应码之后哪个命令挂了一目了然。FATFS返回值日志是最重要的。FRESULT枚举从FR_OK到FR_INT_ERR、FR_DENIED、FR_WRITE_PROTECTED每个返回码对应不同问题。写文件时遇到FR_DENIED基本是文件属性只读或者扇区写保护FR_WRITE_PROTECTED查卡侧面的物理锁或SPI命令响应FR_INT_ERR多和缓冲区配置有关。把每一步调用的返回值打出来定位会比闷头试快十倍。文件状态日志记录f_open前后的文件名、文件大小、写入字节数防止f_open用了同样的文件名把旧数据覆盖了。我最早遇到这个问题时折腾了半小时才意识到FA_CREATE_ALWAYS会覆盖同名文件而日志文件名在同一天是固定的。所以文件名一定要带时间或序号这也呼应了前面的命名策略。5. 从实验到落地的一点建议这套方案在我实际项目里已经稳定跑了大半年帮设备厂家记录了上万小时的历史数据。最后给几个实用建议。如果你只是验证功能建议先不要用自己手头的杂牌SD卡找一张正经品牌的Class10卡排除卡的个体差异。等你确信代码没问题了再拿杂牌卡测试兼容性这会显著减少排查问题的难度。写数据一定记得定期执行f_sync频率要结合设备掉电场景来定。我见过太多人数据写到一半直接拔电然后文件就残缺不全。数据采集不到不致命但数据写了一半文件系统的结构被破坏丢了整个文件才是真亏。扩展方向上如果需求从日志记录变成数据采集平台可以加上RTC做时间戳用SPIFFS跑内部Flash存储关键参数再挂一个ESP8266做无线回传CSV文件就成了本地兜底存储云端成了实时分析的主战场。这套架构在工业数据采集领域很常见而本地CSV日志永远是最后一层保底防线。这个实验本身难度不高但涉及的SPI配置、文件系统原理、存储卡规范和编码处理这些都是嵌入式开发绕不开的基础值得花时间吃透。测试过程中我实际体会最深的还是那句话“问题不可怕可怕的是没有任何输出信息”。把日志打印做好排错的时间能省掉一大半。本文还有配套的精品资源点击获取