☰
STM32 SPI模式SD卡+FATFS+USB模拟U盘数据记录仪方案详解
2026/10/5 4:01:02 网站建设 项目流程

这题目一看就是老嵌入式人会做的事:MCU端要存数据、要文件系统管理,还得随时能被电脑直接读走。把SPI模式的SD卡、FATFS文件系统和USB模拟U盘这三样东西在STM32 HAL库环境下串起来,用好了,就是一个非常实用的数据记录仪基础平台。

很多朋友第一次做这个组合,容易卡在各种莫名其妙的地方——SPI模式下SD卡初始化不过、FATFS挂载失败、USB枚举不识别,或者三者单独跑都正常、一连起来就互相干扰。这篇笔记我就围绕这个组合,把从CubeMX配置、底层驱动移植到U盘功能对接的完整过程写清楚,重点讲为什么这么做,以及哪些坑必须避开。

1. 整体设计思路与方案选型

1.1 为什么选择SPI模式而不是SDIO

做SD卡方案,第一件事就是选通信接口。STM32通常有两个选择:原生SDIO接口或者走SPI。

SDIO的优势是速度快,8位或4位总线并行传输,读卡能力可以到几十MB/s,适合需要高速连续存储的应用。但代价也很明显:引脚占用多(4条数据线加时钟命令线),CubeMX配置起来稍麻烦,而且PCB布线要求高,对于很多非专业布线的板子容易出现信号完整性问题。

SPI模式则相反。它走的是标准SPI协议,任何一颗MCU只要有SPI外设,就能跟SD卡通信。速度方面,SPI模式单数据线,一般实际跑个1~10MHz没问题,写文件每秒几百KB到1MB左右。做数据记录、配置存储、日志写入这类应用完全够用。

我实际项目里经常选SPI模式,最核心的理由有三个:CubeMX配置简单、引脚占用少(4根线加片选,共5根)、调试方便。SPI模式可以用逻辑分析仪直接看时序,也可以用软件模拟SPI来兜底,排查问题的路径比SDIO宽很多。

补充一句,如果你的设计确实需要高速连续写入(比如高清视频流采集),那老老实实用SDIO,这个方案不在本文范围内。

1.2 FATFS的作用

裸的SD卡存储是扇区级的,要读写数据,你得自己管理扇区分配、文件索引,数据分散了还容易出错。FATFS就是来解决这件事的——它是个开源文件系统组件,专门为嵌入式小型系统设计,支持FAT12/FAT16/FAT32/exFAT,占用资源少,移植简单。

有了FATFS,你就能在单片机里像在电脑上一样用文件函数:f_open建文件、f_write写数据、f_read读数据、f_mkdir建目录。记录的数据直接在电脑上插读卡器就能看到,不需要额外写上位机解析工具。

FATFS这种方案本身已经非常成熟,标准移植只需要你提供6个底层接口函数。它跟HAL库本身没有天上地下的耦合关系,只要把底层接口用HAL的SPI函数实现,其余部分完全不动,这也是这个方案调试起来相对可控的原因。

1.3 模拟U盘的扩展价值

模拟U盘,本质上是STM32内置USB外设运行MSC(Mass Storage Class)类,把PC端发来的SCSI命令翻译成对SD卡的扇区读写。

这带来的好处非常直观:设备通过USB线连电脑,电脑上直接出现一个可移动磁盘,用户不需要装任何驱动,也不需要拆开设备取卡,就能直接访问SD卡里的文件。这对产品形态来说,是体验上的一个跳跃——从"需要读卡器才能取数据"变成"插上USB就是U盘"。

一个典型的应用场景:设备离线采集数据,用户拿回办公室,插上USB线,所有日志文件直接拖出来分析,无比顺畅。再比如设备的配置文件,直接在电脑上编辑好,通过U盘模式拷进SD卡,设备启动时读取。

不过要注意,模拟U盘不是"独立"功能,它跟FATFS是共享同一个SD卡设备的。这里就涉及到一个很关键的设计问题:U盘模式和MCU本地访问,不能同时操作文件系统。通常做法是设计一个互斥逻辑——U盘连接时,MCU不访问SD卡;MCU访问完、释放SD卡后,才允许U盘模式激活。

2. 硬件连接与CubeMX工程配置

2.1 SD卡SPI模式下的硬件接线

SD卡在SPI模式下工作在3.3V电平,这点极其重要。如果你的MCU是3.3V供电(STM32绝大多数是),那就直接连接;如果板子上有5V部分,绝对不要直接把5V逻辑电平接到SD卡上。

标准的SPI模式接线:

SD卡引脚功能连接目标
CS片选(低有效)MCU GPIO输出
DI数据输入(主机->卡)MCU SPI MOSI
DO数据输出(卡->主机)MCU SPI MISO
SCLK时钟MCU SPI SCK
VDD3.3V电源3.3V供电
VSS地公共地

TF卡与SD卡引脚定义略有不同,但SPI模式下信号顺序一致,只需注意封装差异即可。

这里有个容易忽略的细节:SPI模式下,SD卡的DI(MOSI)和SCLK需要接上拉电阻,一般10kΩ左右,保证默认状态是确定的高电平。CS也是一样,建议加上拉。很多模块电路板上已经集成这些电阻,如果是裸卡座,一定要补上,否则初始化阶段极容易莫名其妙失败。

另一个细节是电源去耦:SD卡在擦写瞬间电流峰值可能到几十毫安,用万用表看不出瞬时压降,但用示波器能看到VDD上有毛刺。在卡座电源引脚旁边放一个10μF电解电容加一个0.1μF陶瓷电容,稳定性会好很多。我踩过一次坑,就是因为供电纹波太大,导致大文件写入时偶发中断。

2.2 CubeMX配置SPI外设

用STM32CubeMX配置SPI时,核心参数如下:

  • Mode:Transmit Only Master(或者Full-Duplex Master都行,我做的是Full-Duplex,方便读响应)
  • Hardware NSS Signal:Disable(片选用普通GPIO软件控制,灵活性更高)
  • Clock Polarity (CPOL):High
  • Clock Phase (CPHA):2 Edge
  • 数据传输顺序:MSB First
  • 预分频器:先设大分频(比如/64,得到约1MHz以下),初始化完成后再切高速

为什么CPOL=High、CPHA=2 Edge?这是SD卡SPI模式的既定要求,SD卡协议文档里明确规定,在SPI模式下,时钟空闲时是高电平,数据在第二个沿采样。很多人初始化不通,一查发现是CubeMX默认SPI模式跟SD卡要求不一致。

时钟频率的选择也要讲究。SD卡上电初始化阶段,要求SPI时钟不能超过400kHz。真正常见的做法是:CubeMX里把SPI时钟配置成低速(分频大),然后在SD卡初始化完成后,程序里再动态修改SPI预分频器,把时钟提上去。读CSD、写单块、读单块这些命令都可以在高速时钟下跑,但ACMD41初始化循环必须在低速下进行。

用STM32F103的典型时钟树举例:PCLK2=72MHz,SPI1挂在APB2上。你初始化阶段分频到/256,SCK大约281kHz,安全符合规范。初始化完成后,重新设置分频到/8,SCK=9MHz,读写速度就有了保障。F103的SPI最高36MHz,但实际上SD卡在SPI模式通常能跑到20~25MHz。我通常设到9~12MHz比较保守,稳定性优先。

2.3 CubeMX配置USB和FATFS组件

USB部分,在CubeMX里选择USB_DEVICE,然后选MSC(Mass Storage Class)。这里有几个关键配置:

  • USB时钟来源:必须选择PLL的48MHz输出(或者带内部专用PLL的型号),这是USB协议要求的,配置错的话枚举会失败或者不稳定
  • MSC类参数:一般默认即可,但如果你的系统内存紧张,注意调整缓冲区大小,不要贪大

FATFS组件在CubeMX里可以直接启用(Middleware and Software Packs -> FATFS)。这里有个人经验要分享:CubeMX自动生成的FATFS代码封装了底层接口,但它默认用的是USER驱动模式,你需要在user_diskio.c里实现对应的SPI读写函数。CubeMX生成代码的好处是省去很多宏定义和基础结构的搭建,坏处是它帮你包了一层,出了问题不好溯源。我平时喜欢用CubeMX生成工程框架,但FATFS的diskio接口会自己重写一遍,确保对底层行为完全可控。

3. 关键代码实现与移植细节

3.1 SD卡SPI模式初始化序列实操

这段代码是整个方案中一旦出错最容易让人崩溃的部分。SD卡上电后,状态机和初始化时序有严格步骤,很多人上来直接发CMD1或者ACMD41,死活过不去,就是因为顺序不对或者时钟太快。

标准的初始化流程:

// 1. 上电延时 + 至少74个SPI时钟 HAL_Delay(10); for (int i = 0; i < 10; i++) { HAL_SPI_Transmit(&hspi1, (uint8_t[]){0xFF}, 1, 100); } // 2. 进入SPI模式:发送CMD0 // 只有卡处于SPI模式后,才能使用SPI命令集 uint8_t cmd0[] = {0x40, 0x00, 0x00, 0x00, 0x00, 0x95}; // 发送CMD0,正确的响应是0x01(表示进入Idle状态) // 3. 发送CMD8,检查卡是否支持SDV2 // 响应0x01表示支持SDV2,0x05表示SDV1/MMC,注意区分 // 4. 循环发送CMD55 + ACMD41 // 注意:ACMD41不是独立命令,必须先发CMD55表示接下来是APP命令 // 响应中的busy位会从0x01慢慢变成0x00,表示初始化完成 uint32_t timeout = 10000; do { send_cmd(CMD55, 0); // 0x77 对应 CMD55 send_cmd(ACMD41, 0x40000000); // 0x69 对应 ACMD41,HCS=1 HAL_Delay(1); } while (response != 0x00 && timeout-- > 0);

这里有个非常关键的细节:每次发送命令之前,记得发送几个0xFF字节,因为SPI是全双工模式,发送的同时也在接收,需要这些"空字节"来给卡足够的处理时间并获取响应数据。CubeMX生成的HAL_SPI_Transmit只是发送,你可能感受不到,但在判断响应时,一定是在发送命令后再发送一个0xFF,然后在同一个SPI事务里把返回数据读出来。我当时卡了很久的地方就是在这里:读到的响应错位了一位或者两位,实际上就是因为缺少一个dummy clock。

初始化完成后,最好把CSD寄存器的内容读出来,解析出卡容量和块大小信息,然后设置FATFS的扇区大小。大多数SD卡块大小是512字节,这个可以直接写死,但如果你用的是老卡或者特殊卡,解析CSD是更稳妥的做法。读取CSD的代码不复杂:

uint8_t csd[16]; uint8_t cmd9[] = {0x49, 0x00, 0x00, 0x00, 0x00, 0x00}; // CMD9 // 发送后读取16字节CSD数据,最后还有2字节CRC

3.2 FATFS的diskio接口移植,必须手写一遍

CubeMX自动生成的user_diskio.c里,真正需要你动手的只有四个函数:disk_initialize、disk_read、disk_write、disk_ioctl。其余的状态查询、时间戳可以简单实现。

DRESULT disk_read(BYTE pdrv, BYTE* buff, LBA_t sector, UINT count) { // 发送CMD17读单块,CMD18读多块 for (UINT i = 0; i < count; i++) { send_cmd(CMD17, (sector + i) << 9); // 扇区号 -> 字节地址 // 读取数据令牌 0xFE,然后读取512字节 // 最后2字节CRC可忽略 read_data_token(buff + i * 512); } return RES_OK; }

disk_write则对应CMD24。这里有一个非常容易踩到的坑:FATFS在写数据时,有时候写入的地址不是扇区对齐的,如果你的底层不支持部分写,就必须先读出原始扇区,修改后再整体写回。多数SD卡在SPI模式下支持单块写(CMD24)和多块写(CMD25),它们对地址对齐的要求不同。我个人的做法是:直接用CMD24单块写,FATFS层面有512字节扇区缓冲,本身会处理对齐问题;如果在disk_write里发现接收到的地址不是512的整数倍,我再做读改写。实际中这种情况极少,但加了这段逻辑后,稳定性会显著提升。

disk_ioctl实现的关键命令包括:

  • GET_SECTOR_COUNT:返回总扇区数(解析CSD得到的)
  • GET_SECTOR_SIZE:返回512
  • CTRL_SYNC:等待SPI空闲,确保写完
  • GET_BLOCK_SIZE:返回1(表示每个块1个扇区,FATFS擦除单位)

有个细节:当SPI时钟从低速切换到高速后,你的disk_initialize里再次访问SD卡时,不需要重新执行CMD0~ACMD41的初始化序列,因为卡已经处于SPI模式且完成了上电初始化。但每次重新上电后,必须重新初始化。我在做低功耗休眠唤场景时,就踩过这种坑:唤醒后直接发CMD17读扇区,结果是超时,因为SD卡跟着系统一起掉电,重新上电后进入了MMC模式,必须重新做初始化流程。

3.3 USB MSC对接SD卡的实现思路

模拟U盘在STM32上的标准实现路径是:USB设备枚举为MSC设备,PC端通过SCSI命令(INQUIRY、READ CAPACITY、READ10、WRITE10等)访问存储介质。STM32的USB库已经帮我们把SCSI协议栈实现了,我们需要做的只是提供底层存储接口。

在CubeMX生成的代码里,这个接口是usbd_storage_if.c,里面有这么几个关键函数:

int8_t STORAGE_Init(uint8_t lun); int8_t STORAGE_GetCapacity(uint8_t lun, uint32_t* block_num, uint16_t* block_size); int8_t STORAGE_Read(uint8_t lun, uint8_t* buf, uint32_t blk_addr, uint16_t blk_len); int8_t STORAGE_Write(uint8_t lun, uint8_t* buf, uint32_t blk_addr, uint16_t blk_len);

这些函数的实现完全可以复用刚才移植的SD卡底层代码。注意以下几点:

  • block_size固定返回512
  • STORAGE_Read和STORAGE_Write的blk_len参数表示几个块,要循环调SD卡的读写函数,或者用CMD18/CMD25多块命令
  • 每次读写后要检查SD卡返回状态,失败时返回错误码,否则PC端会报I/O错误

还有一个比较棘手的问题:USB MSC和单片机本地文件系统共用SD卡,如何避免冲突?

我的做法是加一个"存储所有权"标志:

volatile uint8_t storage_owner = OWNER_MCU; // 或 OWNER_USB // USB枚举激活回调 void USB_MSC_Activate(void) { storage_owner = OWNER_USB; f_mount(NULL, "", 0); // 卸载FATFS } // USB断开回调 void USB_MSC_Deactivate(void) { storage_owner = OWNER_MCU; f_mount(&SDFatFS, "", 1); // 重新挂载 }

这个逻辑的优先级要理清楚:当U盘模式连接时,USB回调已经卸载FATFS,MCU侧的任务无论如何不能访问SD卡。反之MCU占用SD卡时,不能允许USB枚举为可用的MSC设备,要么返回STORAGE_BUSY错误,要么干脆不让USB枚举成功。

实际工程中,很多产品还会加一个机械开关或者GPIO来切换模式:拨到"U盘"档位时,MCU完全释放SD卡,USB硬件使能;拨到"运行"档位时,USB禁用,MCU用FATFS自由读写。这种物理隔离的做法虽然朴素,但在稳定性上是最省心的。我后来做的几款数据记录仪都是这种设计——上位机软件通过USB发命令让设备切换模式,本质上也是所有权标志的远程版本,不是物理开关但逻辑相同。

4. 常见问题与排查技巧实录

4.1 初始化卡死在ACMD41循环

这是最典型的问题,几乎每个做SPI SD卡的人都会遇到。现象是程序卡在do...while循环里一直出不来,或者等超时后报错。

我的排查步骤固定如下:

  1. 确认上电顺序和延时足够。SD卡上电后需要至少1ms的稳定时间,然后发74个以上SPI时钟周期。很多人上电马上初始化,就容易失败。我通常延时10ms起步,宁可慢一点。

  2. 确认时钟速度不大于400kHz。初始化阶段用低速,这点不能商量。如果你上来就是MHz级,卡根本不会理你。

  3. 检查响应数据是否错位。CMD0的响应应该是0x01,但很多人读到的是0x41或者0xC1。这是SPI模式下最常见的错位现象,原因就是发送命令时,发送时钟和数据采样时序不对齐。检查CPOL和CPHA配置,严格的CPOL=High、CPHA=2 Edge,一般能解决。

  4. 检查卡是否真的支持SPI协议。有些劣质扩容卡或特殊卡,SPI模式支持不完整。换一张电脑上能正常用的品牌卡,如果还是初始化失败,那就不是卡的问题,是你时序或者电平的问题。

每次处理好这几个环节,我都能把这个问题定位到具体某个原因。实际上,90%的ACMD41卡死,最后都是SPI时序参数配置错误。

4.2 FATFS挂载成功但文件读写异常

挂载(f_mount)能成功,说明底层的初始化、读扇区都是正常的,但读写文件时出问题,比如:

  • f_open返回FR_NOT_READY或者FR_NO_FILESYSTEM:检查disk_ioctl的GET_SECTOR_COUNT返回值是否正确。如果你返回的扇区数比实际大,FATFS会认为SD卡上有个巨大的分区,读取文件系统信息时就会出错。

  • 文件能创建,但写几KB后报错FR_INT_ERR:多半是写扇区时出错或者SPI速率太高导致数据不稳。尝试降低SPI时钟,比如从18MHz降到9MHz。很多时候SPI在低速下读写一切正常,高速下偶尔出错,这是电平上升沿裕量不足的表现,不是软件逻辑问题。

  • 文件系统被破坏:如果你之前的代码断电时没有正确关闭文件(f_close)或者没有执行f_mount后马上写,FATFS缓冲区里的数据没刷回SD卡,下次挂载时容易出现目录损坏。开发阶段常用的修复办法是插到电脑上让Windows或者chkdsk修复,但正式产品里一定要在关键位置上做好掉电保护。

还有一点经验:FATFS提供了f_printf、f_gets等高级接口,但底层写缓冲默认是512字节,如果你频繁写入少量数据(比如每秒写一行日志),性能会非常差。我一般会定义FATFS的配置宏FF_FS_TINY,开启tiny模式,减少内存占用;同时在应用层用一个大缓冲区,攒够一定数据量再一次性写入,只有调用flush时才真正落盘。这不仅是性能考虑,也是延长SD卡寿命的手段——频繁小幅写对Flash磨损更严重。

4.3 USB枚举成功但电脑提示需要格式化

U盘模式连电脑,能看到盘符,但双击打开提示"需要格式化",这种问题绝大部分出在SD卡上没有有效的FAT文件系统。

原因一般是:SD卡之前是MCU用FATFS初始化并格式化过,但格式化的类型和参数与Windows的兼容性不够好。或者SD卡分区表(MBR)和FAT引导扇区没写对。

排查思路分两步:

  1. 先确认SD卡单独插读卡器在电脑上是否正常。如果不正常,说明卡本身文件系统有问题。你可以先用读卡器在电脑上格式化一遍(选FAT32),再插回MCU验证是否能正常读写文件。

  2. 如果单独在电脑上正常,但在MCU+USB模式下提示格式化,那就是你的STORAGE_GetCapacity返回的参数跟实际SD卡参数不一致。Windows会先READ CAPACITY获取扇区总数和扇区大小,然后用这个参数去解析文件系统。如果你返回的容量小于FATFS格式化时的分区大小,Windows就解析不了。务必确保float32换算没有溢出。

我说个具体的场景:一张1GB SD卡,格式化后实际可用扇区数可能只有1953456个扇区(不是2000000)。如果你在GET_SECTOR_COUNT里返回2000000,Windows一看FAT表和根目录区域越界,就直接判断为未格式化。解决方式就是严格按照CSD寄存器里的C_SIZE参数计算,或者直接从FATFS的get_fattime和disk_ioctl接口顺着查,不能用拍脑袋的整数。

4.4 多文件连续写速度上不去

还有一类问题是速度:读文件很快,写文件慢得像蜗牛。排除SPI时钟因素后,最普遍的瓶颈在FATFS的分配策略上。FATFS默认是顺序分配簇,但如果你的文件不断删除再新建,FAT表里的空闲簇变得碎片化,写文件时频繁跨簇查找,速度立刻下降。

针对这种场景,我建议:

  • 尽量在采集数据前预先分配一个固定大小的文件,用f_lseek把文件指针移到最后再写入,这样FATFS会连续分配簇
  • 如果数据量固定(比如一条日志64字节,一天86400条),可以考虑直接用固定大小的大文件,记录位置指针写入头部作为索引,这样既有顺序写的优势,又方便PC端解析
  • 定期对SD卡做碎片整理——最方便的做法就是通过USB U盘模式连电脑,把文件拷贝出来格式化后再拷回去

尤其要注意f_write是带缓存的,写完记得f_sync,否则缓冲区里的数据可能还停留在内存中,直接掉电就丢了。f_close本身会sync,但你真的不必等到关闭才落盘,更多的做法是在关键节点主动fflush。

4.5 电源波动引起的偶发读写失败

这个比较隐蔽,你单测时一切正常,但在电机、继电器启动的瞬间,偶尔出现一次读写失败。用示波器一抓,VDD在电机启动瞬间被拉低了200~300mV,超出了SD卡的工作电压范围。

处理方式:

  • SD卡电源用单独的LDO供电,不要跟电机驱动器共用一个电源轨
  • SD卡VDD旁路电容加大,我用的是100μF电解再加0.1μF陶瓷
  • SPI信号线上串联22Ω的小电阻,可以抑制振铃,减少信号质量问题导致的偶发错误
  • 在低电压检测中断里加保护:VDD低于阈值时禁止对SD卡写入,防止写到一半掉电导致文件系统损坏

5. 从开发到产出的几个关键建议

5.1 做好调试阶段的"可视化"支撑

调试这个组合功能时,我强烈建议提前在板子上预留一个USB转串口或者SWO输出口。程序里加几个宏开关,打开后可以把SD卡初始化过程中的关键状态码、FATFS返回值、USB枚举状态实时打印出来。

这看着不起眼,实际排查问题时效率能翻好几倍。比如f_mount返回FR_NO_FILESYSTEM,你串口一眼就能看到,不用反复猜测到底是底层读错了还是文件系统本身坏了。

调试阶段可以做一个"忙时灯":SPI读写SD卡时,把GPIO拉高;空闲拉低。用示波器看这个GPIO的占空比,就能直观判断系统是在高速读写还是频繁等待——很多时候性能瓶颈一眼就能看穿。

5.2 掉电保护与文件系统健壮性设计

产品形态如果允许用户随时拔电,掉电保护这块必须做好。经验上,最有效的三个手段:

  1. 关键写操作后立即f_sync,把FAT表和目录项刷到SD卡
  2. 在SD卡硬件上加一个大电容,提供掉电后的"缓冲时间",让MCU有机会把最后一批数据写完并关闭文件
  3. SD卡写入期间,检测到掉电事件时立即停止写入,宁可丢最后几条数据,也不要去动FAT表结构

实践中,我会结合方案1和2:正常写数据时不做频繁sync(保证速度),当检测到掉电信号进入紧急中断后,把最后一段数据写进一个固定的"紧急存储区",然后马上sync一次。

5.3 U盘模式与MCU访问的互锁实现

回到开头的互斥问题,我的最终建议是做一个带超时的所有权切换机制:MCU试图访问SD卡时,如果发现当前所有者是USB,就等待一段时间,超时则报错;USB激活时的回调里设置一个"忙碌"标志,如果MCU正在写文件,USB枚举不响应或者直接拒绝激活。

代码结构上,可以用一个简单的状态机:

typedef enum { STORAGE_FREE, STORAGE_OWNED_MCU, STORAGE_OWNED_USB } storage_state_t; // 请求获取存储所有权 storage_err_t storage_acquire(storage_state_t owner, uint32_t timeout_ms); // 释放存储所有权 void storage_release(storage_state_t owner);

所有应用层读写文件之前先acquire,完成后release。这套机制看起来简单,但它能帮你挡住所有并发访问的坑,尤其是你在上了RTOS之后,多个任务同时调用FATFS函数,如果没有这个锁,文件系统损坏只是时间问题。

写在后面

这套SPI模式SD卡+FATFS+USB模拟U盘的组合,我前前后后在好几款数据记录仪和配置管理设备上跑过,整体方案稳定性和可维护性都不错。SPI模式牺牲了一点传输速度,换来了开发效率、稳定性和引脚资源上的巨大优势。

最后再分享一个小技巧:如果调试时遇到"SD卡能被电脑识别,但容量显示为0",先别急着怀疑代码,把SD卡放读卡器里在电脑上重新格式化一次(FAT32格式),很多时候是SD卡在MCU初始化时被写坏了引导扇区,重新格式化能解决90%的问题。如果重新格式化后依然显示0,再去查你的GET_SECTOR_COUNT返回值和底层读扇区有没有溢出。

这套方案后续还可以扩展的方向不少,比如通过SPI DMA提高读写吞吐量、加入TF卡热插拔检测(CD引脚)、把FATFS换成LittleFS以获得更强的掉电稳定性,这些都是在你跑通基础组合之后可以考虑的优化项。先把基础版本跑稳,再去谈优化,这是我一贯的做法。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询