STM32F4读写SD卡:填一填ST官方HAL库的坑

news2026/4/4 9:06:02
使用STM32读写SD卡在低功耗存储中的应用是比较常见的但是网上大多数资料都是基于标准库或者基于寄存器的开发。随着嵌入式设备越来越复杂使用HAL库能够大大降低开发者的学习成本从而提高开发效率。近年来ST官方主推以STM32CubeMx为核心代码初始化工具给开发者节省了配置硬件要花费的精力。然而由于HAL是一个硬件抽象层的库它将不同系列的芯片硬件封装成了统一的接口但是无法保证能够涵盖所有开发情况。在使用STM32F4开发SD卡读写功能的时候我发现ST官方提供的HAL存在一些严重Bug无法直接使用。本文就来填一填ST官方留下的坑。硬件准备1、STM32F407VET6开发板带SD卡槽2、1G逻辑分析仪软件准备STM32CubeMX (本项目使用6.12.1版本)IAR 9.50.2本项目主要使用IAR相比于Keil编译速度更快生成的文件体积更小若需要Keil版本的代码可通过STM32CubeMX生成对应版本操作步骤使用STM32CubeMx生成代码1、配置RCC2、配置调试器3、配置SDIO注意这里要配置DMA和SDIO全局中断其它默认4、添加一个串口用于调试5、配置时钟树6、生成代码修改代码1、重定向printf函数输出到串口用于调试#includestdio.hintfputc(intch,FILE*f){HAL_UART_Transmit(huart1,(uint8_t*)ch,1,HAL_MAX_DELAY);returnch;}2、主函数如下intmain(void){/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_DMA_Init();MX_SDIO_SD_Init();MX_USART1_UART_Init();/* USER CODE BEGIN 2 */setvbuf(stdout,NULL,_IONBF,0);printf(初始化完毕\n);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while(1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */}3、编译下载发现无法输出预期于是开始了Debug发现跳转到了Error_Handler4、打开Call Stack发现错误在MX_SDIO_SD_Init();这个函数5、于是继续跟踪发现这里出错了查找资料后发现这里生成的代码是有问题的ST官方代码的第一个大坑6、将代码改为如下后重新运行voidMX_SDIO_SD_Init(void){/* USER CODE BEGIN SDIO_Init 0 *//* USER CODE END SDIO_Init 0 *//* USER CODE BEGIN SDIO_Init 1 *//* USER CODE END SDIO_Init 1 */hsd.InstanceSDIO;hsd.Init.ClockEdgeSDIO_CLOCK_EDGE_RISING;hsd.Init.ClockBypassSDIO_CLOCK_BYPASS_DISABLE;hsd.Init.ClockPowerSaveSDIO_CLOCK_POWER_SAVE_DISABLE;hsd.Init.BusWideSDIO_BUS_WIDE_1B;// 这里只能是使用SDIO的1Bit总线模式进行初始化hsd.Init.HardwareFlowControlSDIO_HARDWARE_FLOW_CONTROL_DISABLE;hsd.Init.ClockDiv0;if(HAL_SD_Init(hsd)!HAL_OK){Error_Handler();}if(HAL_SD_ConfigWideBusOperation(hsd,SDIO_BUS_WIDE_4B)!HAL_OK){Error_Handler();}/* USER CODE BEGIN SDIO_Init 2 *//* USER CODE END SDIO_Init 2 */}可以看到输出说明初始化通过CubeMx1.16.1修复了这个bug7、使用DMA读写SD卡这里提一点由于DMA和SDIO模块是分开的因此当DMA写入完成之后SDIO的总线可能还处于正忙状态此时若强行写入SDIO只能导致失败。如果手动添加延时可以一定程度改善但是无法完全解决这个问题。使用逻辑分析仪调试之后发现只有当SDIO_CMD和SDIO_D0都空闲高电平的时候调用DMA写入才不会失败因此有了如下的补丁代码。// 将数据通过DMA写入if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_8)1)// 补丁只有当SDIO总线空闲的时候才能够发起写入否则出错{HAL_SD_WriteBlocks_DMA(hsd,buff_w,0,DMA_NUM_BLOCKS_TO_WRITE);}可通过逻辑分析仪观察波形可以看到只有当SDIO_D0引脚为高的时候才能够发起SDIO通信否则将不会触发DMA发送完成中断后续数据将无法继续传输这个坑目前还没在国内网站上看到有好的解决方法。。。测试SD卡读写/* USER CODE BEGIN Header *//** ****************************************************************************** * file : main.c * brief : Main program body ****************************************************************************** * attention * * Copyright (c) 2024 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** *//* USER CODE END Header *//* Includes ------------------------------------------------------------------*/#includemain.h#includedma.h#includesdio.h#includeusart.h#includegpio.h/* Private includes ----------------------------------------------------------*//* USER CODE BEGIN Includes */#includestdio.h#includestring.h/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*//* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*//* USER CODE BEGIN PD */#defineBLOCK_SIZE512// 一个块的字节数节#defineDMA_NUM_BLOCKS_TO_WRITE64// 每一次DMA写入块的数量#defineDMA_NUM_BLOCKS_TO_READ64// 每一次DMA读出块的数量#defineBUFFER_SIZE_WDMA_NUM_BLOCKS_TO_WRITE*BLOCK_SIZE// 写缓冲区大小#defineBUFFER_SIZE_RDMA_NUM_BLOCKS_TO_READ*BLOCK_SIZE// 读缓冲区大小/* USER CODE END PD *//* Private macro -------------------------------------------------------------*//* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV */uint8_tbuff_w[BUFFER_SIZE_W];uint8_tbuff_r[BUFFER_SIZE_R];uint8_tsdio_write_done0;uint8_tsdio_read_done0;/* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/voidSystemClock_Config(void);/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*//* USER CODE BEGIN 0 */// 重定向printf函数输出到串口intfputc(intch,FILE*f){HAL_UART_Transmit(huart1,(uint8_t*)ch,1,HAL_MAX_DELAY);returnch;}voidHAL_SD_TxCpltCallback(SD_HandleTypeDef*hsd){sdio_write_done1;}voidHAL_SD_RxCpltCallback(SD_HandleTypeDef*hsd){sdio_read_done1;}/* USER CODE END 0 *//** * brief The application entry point. * retval int */intmain(void){/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_DMA_Init();MX_SDIO_SD_Init();MX_USART1_UART_Init();/* USER CODE BEGIN 2 */setvbuf(stdout,NULL,_IONBF,0);printf(初始化完毕\n);// 生成测试数据printf(正在生成测试数据\n);for(uint32_ti0;isizeof(buff_w);i){buff_w[i]i;}// 写入SD卡printf(正在写入数据\n);// 将数据通过DMA写入if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_8)1)// 补丁只有当SDIO总线空闲的时候才能够发起写入否则出错{HAL_SD_WriteBlocks_DMA(hsd,buff_w,0,DMA_NUM_BLOCKS_TO_WRITE);}while(sdio_write_done0);printf(数据写入完成\n);// 读取SD卡数据并且通过串口输出printf(正在读取数据\n);// 将数据读出到buff_r中if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_8)1HAL_GPIO_ReadPin(GPIOD,GPIO_PIN_12)1){HAL_SD_ReadBlocks_DMA(hsd,buff_r,0,DMA_NUM_BLOCKS_TO_READ);}while(sdio_read_done0);if(0memcmp(buff_w,buff_r,sizeof(buff_r))){printf(数据是一致的\n);}/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while(1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */}/** * brief System Clock Configuration * retval None */voidSystemClock_Config(void){RCC_OscInitTypeDef RCC_OscInitStruct{0};RCC_ClkInitTypeDef RCC_ClkInitStruct{0};/** Configure the main internal regulator output voltage */__HAL_RCC_PWR_CLK_ENABLE();__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);/** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */RCC_OscInitStruct.OscillatorTypeRCC_OSCILLATORTYPE_HSI;RCC_OscInitStruct.HSIStateRCC_HSI_ON;RCC_OscInitStruct.HSICalibrationValueRCC_HSICALIBRATION_DEFAULT;RCC_OscInitStruct.PLL.PLLStateRCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSourceRCC_PLLSOURCE_HSI;RCC_OscInitStruct.PLL.PLLM8;RCC_OscInitStruct.PLL.PLLN168;RCC_OscInitStruct.PLL.PLLPRCC_PLLP_DIV2;RCC_OscInitStruct.PLL.PLLQ7;if(HAL_RCC_OscConfig(RCC_OscInitStruct)!HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses clocks */RCC_ClkInitStruct.ClockTypeRCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSourceRCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDividerRCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDividerRCC_HCLK_DIV4;RCC_ClkInitStruct.APB2CLKDividerRCC_HCLK_DIV2;if(HAL_RCC_ClockConfig(RCC_ClkInitStruct,FLASH_LATENCY_5)!HAL_OK){Error_Handler();}}/* USER CODE BEGIN 4 *//* USER CODE END 4 *//** * brief This function is executed in case of error occurrence. * retval None */voidError_Handler(void){/* USER CODE BEGIN Error_Handler_Debug *//* User can add his own implementation to report the HAL error return state */__disable_irq();while(1){}/* USER CODE END Error_Handler_Debug */}#ifdefUSE_FULL_ASSERT/** * brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * param file: pointer to the source file name * param line: assert_param error line source number * retval None */voidassert_failed(uint8_t*file,uint32_tline){/* USER CODE BEGIN 6 *//* User can add his own implementation to report the file name and line number, ex: printf(Wrong parameters value: file %s on line %d\r\n, file, line) *//* USER CODE END 6 */}#endif/* USE_FULL_ASSERT */结果如下初始化完毕 正在生成测试数据 正在写入数据 数据写入完成 正在读取数据 数据是一致的 写入的数据如下 读出的数据如下总结ST官方的代码有3大坑1、SDIO初始化的坑必须要使用1bit总线的SDIO来初始化SD卡否则会导致初始化失败2、采用轮询方式或者中断方式读写SDIO有问题这里建议采用DMA进行读写3、使用DMA读写SD卡的时候需要实现检查当前SDIO是空闲的否则会出错代码https://github.com/dwgan/STM32F407_SDIO

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2472948.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…