【HAL库】STM32CubeMX开发----STM32F407----SD卡存储 SDIO基础通信

发布时间:2026/7/26 23:47:06
【HAL库】STM32CubeMX开发----STM32F407----SD卡存储 SDIO基础通信 STM32CubeMX 下载和安装 详细教程【HAL库】STM32CubeMX开发----STM32F407----目录STM32F407SD卡存储----SDIO基础通信和DMA读写----程序源码一、SD卡基础信息SD卡全称Secure Digital Memory Card安全数码卡是嵌入式设备上常用的一种存储介质。1. 物理尺寸1标准 SD 卡相机、记录仪常用尺寸 32×24×2.1mm。2Micro SDTF 卡单片机、STM32、摄像头、OTA 升级主流15×11×1mm。3Mini SD淘汰几乎不用。每种卡形状大小不一但功能一样遵循相同的 SD卡协议 、相同的命令集、相同的块大小(512)。只需确保SDIO引脚配置正确并且遵循SD卡协议发送正确的命令程序即可通用。目前STM32开发板、Linux开发板 等预留的卡座一般是TF卡座因为它占用空间最少。2. 容量等级类型容量范围文件系统块大小单片机适配SDSC标准容量≤2GBFAT12 / FAT16512 字节全兼容SDHC高容量4GB ~ 32GBFAT32512 字节STM32 SPI/SDIO 都支持SDXC超大容量64GB ~ 2TBexFAT512 字节FATFS 需开启 exFAT 配置SDUC2TB 以上exFAT512 字节极少单片机支持重点STM32 做 OTA / 日志存储优先 8GB/16GB/32GB SDHC FAT32兼容性最好。3. 速度等级SD卡是Flash存储读写速度特点是读快、写慢。SD卡的最低写入速度用Class等级来标识。在表面丝印上一般会有Class字样它后面的数表示最低写入速度单位是MB/s。或者会用一个外面带半圆的数字表示。等级最低持续写入适用场景C22MB/s低速音频、小日志现已淘汰C44MB/s简易录音、小文件存储C66MB/s720P 录像、普通数据记录C1010MB/s1080P 录像、单片机日志、OTA 升级嵌入式最低推荐4. 存储结构存储层级1扇区 Sector512Byte软件操作最小单元2簇 Cluster多个扇区组成文件系统分配空间单位FAT32 常见 4/8/16 扇区一簇3块 Block硬件擦写单元若干扇区标准 SD1 扇区 512 字节FATFS 固定操作单位所有读写、擦除必须按 512 字节对齐不能单字节随意写一次读写最小 512 字节读 / 写不足 512 字节也要填充凑整扇区最小擦除单元 N × 512Byte 数据块5. 擦写寿命SD 内部 NAND Flash 最小擦写单元为 Block几十 KB。颗粒类型单块擦写寿命适用场景SLC5 万10 万次工业级 SD 卡、长期循环日志MLC3000 次监控记录仪、工业设备首选TLC1000 次消费级手机、普通存储卡QLC300~500 次廉价大容量卡不适合循环写入SD 卡自带硬件 FTL 磨损均衡SD 卡内部集成主控 FTL 闪存转换层主机不用管磨损主机只操作 512B 逻辑扇区卡内部自动把逻辑扇区映射到不同物理擦除块均匀分散擦写压力不会某一块提前报废写入量不大时实际使用寿命会比裸 Flash 提升几十倍。二、SD卡接口SDIOSTM32的SDIO外设与SD卡通信通用接线如下图。注当使用弹簧式SD卡座会有第9个脚(CD)可不接。它用于判断SD卡是否插入当插入SD卡时此脚输出低电平。三、使用STM32CubeMX----新建工程步骤1打开 STM32CubeMX 软件具体STM32CubeMX界面如下步骤2选择 从MCU开始我的项目步骤3选择MCU芯片我们本次实验选择STM32F407VET6芯片做MCU。进入工程编辑具体界面如下步骤4设置烧写方式为 SWD模式这一步必须选择一种烧写方式否则烧写一次程序后芯片中就没法再次烧写程序了。需要硬件重启才可以重新烧写程序。STM32烧写程序连接方式详解步骤5设置时钟源设置外部晶振为芯片系统时钟源。步骤6设置SDIOMode选择SD的四线模式即 SD 4 bits Wide bus.参数部分F4系列不用修改配置默认即可。F103系列需把时钟分频系数修改为 6即SDIOCLK Clock divide factor这一项由默认0改为6, 不然会通信失败。步骤7时钟配置重点箭头所指的Q值它用于控制USB 、SDIO和随机数生成器的时钟必须为48M。步骤8项目编辑步骤9代码生成编辑步骤10点击 GENERATE CODE 生成代码新建的工程已经生成接下来就是对工程代码的编辑。四、使用keil5----编辑程序想要使用keil5打开工程需要电脑先安装 keil5编译环境具体操作流程。可以点击下方文章链接Keil5编译环境搭建流程----STM32和GD32步骤1使用keil5打开工程点击工程文件中的MDK-ARM文件打开keil5工程打开代码编译运行一下没有错误和告警为正常。步骤2用户代码 编辑位置用户代码要写在USER xxxx BEGIN xxx和USER xxxx END xxx之间。这样下次使用STM32CubeMX重新生成代码后自己写的代码不会被删除。步骤3修改代码CubeMX生成的SDIO初始化代码有一个bug需要手动修改操作如下编译后右击 main.c 文件中函数 MX_SDIO_SD_Init(),在弹出菜单中Go To Definition Of …; 将跳转到SD卡初始化函数;跳转到 sdio.c文件的 MX_SDIO_SD_Init()函数内部后把下图位置中的 4B改为 1B 重点每次重新生成后都要手动修改一次。如果不修改初始化过程会导致程序卡死。步骤4使用到的HAL库函数typedefstruct{uint32_tCardType;//SD卡类型uint32_tCardVersion;//SD卡版本uint32_tClass;//SD卡等级uint32_tRelCardAdd;//SD卡地址uint32_tBlockNbr;//SD卡容量(块)uint32_tBlockSize;//SD卡块大小(字节)}HAL_SD_CardInfoTypeDef;//SD卡信息结构体typedefuint32_tHAL_SD_CardStateTypeDef;//SD卡状态信息#defineHAL_SD_CARD_READY0x00000001U//SD卡状态准备就绪#defineHAL_SD_CARD_IDENTIFICATION0x00000002U//SD卡识别状态#defineHAL_SD_CARD_STANDBY0x00000003U//SD卡待机状态#defineHAL_SD_CARD_TRANSFER0x00000004U//SD卡传输状态#defineHAL_SD_CARD_SENDING0x00000005U//SD卡正在发送状态#defineHAL_SD_CARD_RECEIVING0x00000006U//SD卡正在接收状态#defineHAL_SD_CARD_PROGRAMMING0x00000007U//SD卡编程状态#defineHAL_SD_CARD_DISCONNECTED0x00000008U//SD卡断开连接状态#defineHAL_SD_CARD_ERROR0x000000FFU//SD卡响应状态// 1、获取 SD 卡的状态---参数SD卡的句柄HAL_SD_CardStateTypeDefHAL_SD_GetCardState(SD_HandleTypeDef*hsd);// 2、获取 SD 卡的信息---参数SD卡的句柄、SD卡信息HAL_StatusTypeDefHAL_SD_GetCardInfo(SD_HandleTypeDef*hsd,HAL_SD_CardInfoTypeDef*pCardInfo);//3、读数据---参数SD卡的句柄、数据、块地址、块数量、超时msHAL_StatusTypeDefHAL_SD_ReadBlocks(SD_HandleTypeDef*hsd,uint8_t*pData,uint32_tBlockAdd,uint32_tNumberOfBlocks,uint32_tTimeout);//4、写数据---参数SD卡的句柄、数据、块地址、块数量、超时msHAL_StatusTypeDefHAL_SD_WriteBlocks(SD_HandleTypeDef*hsd,uint8_t*pData,uint32_tBlockAdd,uint32_tNumberOfBlocks,uint32_tTimeout);//5、擦除数据---参数SD卡的句柄、块起始地址、块结束地址HAL_StatusTypeDefHAL_SD_Erase(SD_HandleTypeDef*hsd,uint32_tBlockStartAdd,uint32_tBlockEndAdd);//6、通过DMA读取数据---参数SD句柄、数据、块起始地址、需要读取的块数量;HAL_StatusTypeDefHAL_SD_ReadBlocks_DMA(SD_HandleTypeDef*hsd,uint8_t*pData,uint32_tBlockAdd,uint32_tNumberOfBlocks);//7、通过DMA写入数据---参数SD句柄、数据、块起始地址、需要写入的块数量;HAL_StatusTypeDefHAL_SD_WriteBlocks_DMA(SD_HandleTypeDef*hsd,uint8_t*pData,uint32_tBlockAdd,uint32_tNumberOfBlocks);五、使用keil5----调试和运行主要使用串口1通过printf函数打印运行数据这部分操作主要参考【HAL库】STM32CubeMX开发----STM32F407----Uart串口通信实验实验1获取SD卡信息代码在main函数中初始化位置调用以下函数。烧写代码到板子打印SD卡信息信息没有问题再做后面的实验。voidUser_SDCardInfo(void)// 获取SD卡信息{HAL_SD_CardInfoTypeDef pCardInfo{0};// SD卡信息结构体uint8_tstatusHAL_SD_GetCardState(hsd);// SD卡状态标志值if(statusHAL_SD_CARD_TRANSFER)//传输状态{HAL_SD_GetCardInfo(hsd,pCardInfo);// 获取 SD 卡的信息printf(\r\n*** 获取SD卡信息 *** \r\n);printf(卡类型%d \r\n,pCardInfo.CardType);// 类型返回0-SDSC、1-SDHC/SDXC、3-SECUREDprintf(卡版本%d \r\n,pCardInfo.CardVersion);// 版本返回0-CARD_V1、1-CARD_V2printf(块数量%d \r\n,pCardInfo.BlockNbr);// 可用的块数量printf(块大小%d 字节 \r\n,pCardInfo.BlockSize);// 每个块的大小; 单位字节printf(卡容量%llu G \r\n,((uint64_t)pCardInfo.BlockSize*pCardInfo.BlockNbr)/1024/1024/1024);// 计算卡的容量; 单位GB}}运行结果实验2SD卡-读写数据在初始化的时候调用SD卡-写数据写入数据到SD卡然后在运行时周期读取SD卡数据。SD卡-读数据voidUser_SD_ReadData(void){/*读测试读出测试位置原数据保存在ReadData[] */uint8_tReadData[1024]{0};printf(\r读取测试块的原数据\r\n);if(HAL_SD_ReadBlocks(hsd,ReadData,8,2,3000)HAL_OK)// 读SD卡数据块; 参数SD结构体、数据、块起始地址、读的块数量、超时时间;{while(HAL_SD_GetCardState(hsd)!HAL_SD_CARD_TRANSFER);// 等待卡的读写操作结束for(uint32_ti0;i1024;i)// 打印 原数据{printf(%X ,ReadData[i]);}printf(\r\n);}else{printf(SD卡 读测试 失败!\n);}}SD卡-写数据voidUser_SD_WriteData(void){/* 写测试在测试的块上写入数据 */uint8_tWriteData[1024]{0};for(uint32_ti0;i1024;i){WriteData[i]i;}printf(\rSD卡 写入测试\r\n);if(HAL_SD_WriteBlocks(hsd,WriteData,8,2,3000)HAL_OK)// 向SD卡写入数据块; 参数SD结构体、数据、块起始地址、写入的块数量、超时时间;{while(HAL_SD_GetCardState(hsd)!HAL_SD_CARD_TRANSFER);// 等待卡的读写操作结束printf(对指定块写入结束! \r写入的数据是\n);for(uint32_ti0;i1024;i)// 打印 写入的数据{printf(%X ,WriteData[i]);}printf(\r\n);}else{printf(SD卡 写测试 失败!\n);}}运行结果实验3SD卡-擦除数据初始化时调用SD卡擦除数据函数运行时周期读取SD卡数据。voidUser_SD_Erase(void){/*擦除测试擦除指定块上的数据 */printf(\r擦除块测试\r\n);if(HAL_SD_Erase(hsd,8,9)HAL_OK)// 擦除SD卡上的数据; 参数SD结构体、块的起始地址、块的结束地址{while(HAL_SD_GetCardState(hsd)!HAL_SD_CARD_TRANSFER);// 等待卡的读写操作结束printf(擦除 成功 \r\n);}else{printf(擦除 失败 \r\n);}}运行结果