当前位置: 首页 > article >正文

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

使用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

相关文章:

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

使用STM32读写SD卡在低功耗存储中的应用是比较常见的,但是网上大多数资料都是基于标准库或者基于寄存器的开发。随着嵌入式设备越来越复杂,使用HAL库能够大大降低开发者的学习成本,从而提高开发效率。近年来,ST官方主推以STM32Cub…...

管道应力理论(应用)

本文仅对管道应力涉及的理论知识(偏向于应用)进行简单介绍。管道应力:对管道应力校核是为了防止管壁内应力过大对管道造成破坏,不同的荷载引起不同类型的应力,在实际工程应用中,一般分为三种:一…...

VMware 16 安装win,Win11推荐下载链接(不要选arm)

目录Win11下载链接待续、更新中......Win11下载链接 ed2k://|file|zh-cn_windows_11_consumer_editions_version_22h2_updated_sep_2022_x64_dvd_23d39103.iso|5579771904|33C7EC6485AD8C55ADFB550FA1A0F270|/ 待续、更新中… 1 顿号、: 先使用ctrl. ,再使用一遍切…...

Synthelix-Auto-Bot终极指南:10分钟掌握多钱包节点自动化管理

Synthelix-Auto-Bot终极指南:10分钟掌握多钱包节点自动化管理 【免费下载链接】Synthelix-Auto-Bot **Automated tool for managing Synthelix nodes across multiple wallets** 项目地址: https://gitcode.com/gh_mirrors/syn/Synthelix-Auto-Bot Synthelix…...

离线语音智能处理平台Buzz:本地化音频转文本全攻略

离线语音智能处理平台Buzz:本地化音频转文本全攻略 【免费下载链接】buzz Buzz transcribes and translates audio offline on your personal computer. Powered by OpenAIs Whisper. 项目地址: https://gitcode.com/GitHub_Trending/buz/buzz 在当今信息驱动…...

国产铷原子钟 快稳铷原子钟突破铷钟启动时长痛点 铷钟 特种铷原子钟

在数字化浪潮席卷全球的今天,时频同步已成为支撑通信、电力、国防、科研等关键领域稳定运行的核心基石。从6G基站的纳秒级协同,到智能电网的故障精准定位,再到北斗导航的车道级精度保障,每一个场景都对时间频率的准确度、稳定度提…...

【T型三电平仿真】SPWM调制中的单双极性载波特性对比

1. T型三电平逆变器基础认知 第一次接触T型三电平拓扑时,我被它精巧的结构设计惊艳到了。与传统的两电平逆变器相比,这种拓扑在每相桥臂上增加了两个钳位开关管,形成了独特的"T"字形结构。实际搭建电路时,你会发现它的输…...

Doris集群部署避坑指南:3FE+3BE配置全流程(含Java环境配置与常见问题解决)

Doris集群部署实战:3FE3BE高可用架构搭建与深度调优 在企业级数据分析场景中,Doris凭借其出色的实时分析性能和高并发处理能力,已成为众多企业的首选OLAP引擎。本文将基于3FE(Frontend)3BE(Backend&#xf…...

实战应用:基于快马平台开发完整权限监控应用,保障用户隐私

今天想和大家分享一个非常实用的安卓应用开发实战项目——相册权限监控工具。这个项目的灵感来源于日常生活中大家对隐私保护的关注,特别是最近关于某些应用可能滥用相册权限的讨论。通过InsCode(快马)平台,我们可以快速实现一个完整的解决方案。 项目背…...

Ollama实测:Yi-Coder-1.5B代码生成速度有多快?3秒搞定日常函数

Ollama实测:Yi-Coder-1.5B代码生成速度有多快?3秒搞定日常函数 1. 测试背景与目标 作为一名开发者,每天都要面对各种编码任务。从简单的工具函数到复杂的算法实现,代码生成速度直接影响着开发效率。Yi-Coder-1.5B作为一款开源的…...

BilibiliDown:如何高效批量下载B站视频并实现离线收藏管理?

BilibiliDown:如何高效批量下载B站视频并实现离线收藏管理? 【免费下载链接】BilibiliDown (GUI-多平台支持) B站 哔哩哔哩 视频下载器。支持稍后再看、收藏夹、UP主视频批量下载|Bilibili Video Downloader 😳 项目地址: https://gitcode.…...

新手程序员必备:收藏这份Prompt指南,轻松驾驭大模型创造业务价值!

本文系统介绍了大模型Prompt的概念、撰写框架及核心原则,深入剖析了构建高质量Prompt的实操方法。从RTF、思考链到RISEN等五大框架,再到提升Prompt效果的策略,如明确指令、结构化输出、赋予模型思考时间等,帮助读者高效驾驭大模型…...

算法对齐还是实战突围?解构GEO优化中方法论与实践的权重博弈

在生成式人工智能(AIGC)重塑全球信息检索范式的当下,生成式引擎优化(Generative Engine Optimization, GEO)已从一种前沿概念演变为品牌流量增长的底层操作系统。随着大语言模型(LLM)与检索增强…...

ProfControl V8的介绍 组合成为模板

作者:刘凌波链接:环野电子, profcontrolhttp://oa.profcontrol.cn/teaching_V8-7926f783c6.html来源:ProfControl组合为模版1、按下SHIFT键,在地图区域空白处按下鼠标左键不松开,移动鼠标则进入框选模式,让…...

VisualGDB跨平台调试避坑指南:用VS远程调试Linux程序(2023最新版配置)

VisualGDB跨平台调试实战:2023年VS远程开发Linux程序避坑指南 当Visual Studio开发者首次尝试在Linux环境下进行C开发时,往往会面临调试工具链断裂的困境。传统的gdb命令行调试方式与Windows开发者熟悉的图形化调试体验存在巨大鸿沟,而Visual…...

ProfControl V8的介绍 阵列生成

作者:刘凌波链接:环野电子, profcontrolhttp://oa.profcontrol.cn/teaching_V8-7926f783c6.html来源:ProfControl阵列生成ProfControl支持基于仿射变换的阵列快速生成方式,ProfControl支持对各种对象进行阵列生产(包括…...

小红书自动评论的‘伪需求’与真风险:聊聊RPA工具养号背后的封号逻辑与合规玩法

小红书自动化评论的合规边界:效率与账号安全的博弈术 凌晨三点,某MCN机构运营负责人李然被连续不断的手机提示音惊醒——团队管理的12个小红书达人账号同时收到平台封禁通知,而这一切都源于他们三天前部署的那套"高效互动系统"。这…...

AI报告文档审核赋能数据不出域:IACheck重构机械制造行业本地化质量管控体系

在机械制造行业不断推进数字化与智能化转型的过程中,“数据不出域”逐渐从合规要求演变为一种核心能力,即在保障数据安全的前提下,实现数据的高效利用与价值转化,而在这一背景下,检测报告作为连接生产过程与质量评估的…...

Windows环境下ODBC连接MySQL保姆级教程(含性能优化配置)

Windows环境下ODBC连接MySQL全流程实战指南 1. 环境准备与驱动安装 在Windows平台使用ODBC连接MySQL数据库,首先需要确保开发环境配置正确。与JDBC不同,ODBC作为跨语言的数据库连接标准,其驱动安装过程需要特别注意版本兼容性问题。以下是环境…...

终极指南:如何用Captum快速理解PyTorch模型的决策逻辑

终极指南:如何用Captum快速理解PyTorch模型的决策逻辑 【免费下载链接】captum Model interpretability and understanding for PyTorch 项目地址: https://gitcode.com/gh_mirrors/ca/captum 在当今人工智能快速发展的时代,PyTorch已成为深度学习…...

从零构建uWSGI-Nginx-Flask-Docker镜像的5个核心步骤

从零构建uWSGI-Nginx-Flask-Docker镜像的5个核心步骤 【免费下载链接】uwsgi-nginx-flask-docker Docker image with uWSGI and Nginx for Flask applications in Python running in a single container. Optionally with Alpine Linux. 项目地址: https://gitcode.com/gh_mi…...

揭秘Captum归因算法:5种NLP文本分类与情感分析的最佳实践

揭秘Captum归因算法:5种NLP文本分类与情感分析的最佳实践 【免费下载链接】captum Model interpretability and understanding for PyTorch 项目地址: https://gitcode.com/gh_mirrors/ca/captum 在当今人工智能快速发展的时代,模型可解释性已成为…...

XiaoMusic:让小爱音箱突破音乐限制的开源解决方案

XiaoMusic:让小爱音箱突破音乐限制的开源解决方案 【免费下载链接】xiaomusic 使用小爱音箱播放音乐,音乐使用 yt-dlp 下载。 项目地址: https://gitcode.com/GitHub_Trending/xia/xiaomusic 你是否遇到过这样的困扰:想听的歌曲在各大…...

cool-admin(midway版)数据库事务超时:超时设置与回滚机制终极指南

cool-admin(midway版)数据库事务超时:超时设置与回滚机制终极指南 【免费下载链接】cool-admin-midway 🔥 cool-admin(midway版)一个很酷的后台权限管理框架,模块化、插件化、CRUD极速开发,永久开源免费,基于midway.js…...

终极Cinder着色器编程指南:7个GLSL视觉效果开发技巧

终极Cinder着色器编程指南:7个GLSL视觉效果开发技巧 【免费下载链接】Cinder Cinder is a community-developed, free and open source library for professional-quality creative coding in C. 项目地址: https://gitcode.com/gh_mirrors/ci/Cinder Cinder…...

Topgrade性能优化技巧:提升大规模更新效率的5种方法

Topgrade性能优化技巧:提升大规模更新效率的5种方法 【免费下载链接】topgrade Upgrade all the things 项目地址: https://gitcode.com/gh_mirrors/top/topgrade Topgrade是一款强大的系统更新工具,它能自动检测并升级系统中的所有包管理器、编程…...

2025年9月中国电子学会青少年软件编程(图形化)等级考试试卷(一级)答案 + 解析

25年3月一级真题在线测评:http://jw.52coding.site/s/mwIJDR 青少年软件编程(图形化)等级考试试卷(一级) 一、单选题(共25题,共50分) 1.当前舞台背景为最后一个背景“背景3”,使用“下一个背景”…...

学术场景实战:DeepSeek-OCR-2驱动深求·墨鉴实现论文公式精准提取

学术场景实战:DeepSeek-OCR-2驱动深求墨鉴实现论文公式精准提取 1. 引言:学术研究中的公式提取痛点 如果你是一名理工科的研究生、科研工作者,或者经常需要阅读学术论文,你一定遇到过这样的场景:在PDF论文里看到一个…...

超分辨数据集全景图:从经典基准到实战选型指南

1. 超分辨数据集入门:为什么选择比努力更重要 刚接触超分辨率技术时,我和大多数新手一样,第一反应是赶紧找个开源模型跑起来。结果发现同样的代码,在Set5上PSNR能到40,换成自己的照片却糊成一团。后来才明白&#xff0…...

Qwen3.5-2B多场景教程:农业技术人员上传病虫害图→识别种类→推荐药剂

Qwen3.5-2B多场景教程:农业技术人员上传病虫害图→识别种类→推荐药剂 1. 引言:农业病虫害识别的技术痛点 在农业生产中,病虫害防治一直是困扰农户的核心问题。传统识别方式存在三大痛点: 识别门槛高:需要专业农技人…...