STM32 FATFS - 在SDIO的SD卡中运行fatfs

参考文章

STM32CubeMX | SD Card FATFS - 知乎

[STM32F4]基于F407的硬件移植Free RTOS+FATFS(SDIO)_freertos+fatfs-CSDN博客

例程地址:STM32FatFS: 基于stm32的fatfs例程,配合博客文章

基于梁山派天空星开发板,STM32F407VET6

开发板引脚

STM32配置

系统模式配置

输出串口配置

系统时钟配置

这里有个48M的时钟,需要把这里配置成48M,SDIO使用的是系统时钟HCLK,也就是96M,这个需要记住,后面配置SDIO的时候会用到。

SDIO配置

选择4位总线,分频选择4分频,原因点击选项会看到。

SDIO_CK = SDIOCLK / [CLKDIV + 2]. The output clock frequency can vary between 187 KHz and 24 MHz. It is advised to keep the default ClockDiv value (0) to have a maximum SDIO_CK frequency of 24 MHz.

要求SDIO_CK在187 KHz 和 24 MHz之间,我们的系统时钟是96M,分频4,SDIO_CK计算后16M,符合要求。如果有的卡不能识别可以尝试降低频率。

需要开启DMA,因为在FATFS下的SDIO设置中,由于使用了Free RTOS,所以只能选择DMA的方式进行数据处理,且DMA的中断等级要低于SDIO的中断等级,但是在中断系统中0-4的优先级被RTOS占用。

打开SDIO中断

中断优先级调整,将DMA中断调整为7.

配置检测引脚

配置FATFS

使能SD卡,配置简体中文编码,因为资源足够多,可以选择长文件名支持,最大扇区尺寸选择4k。

配置检测引脚

这个引脚会在mount的时候进行判断,如果没有插卡会直接返回错误。

FREERTOS配置

选择V2版本,STM32F103可能需要选择V1版本,任务堆栈使用2048,避免栈溢出导致程序运行错误

长文件名支持,需要增大堆

配置独立c和h文件

代码

CUBEIDE自动生成的代码基本把驱动都实现了,只需要修改一部分内容,我是参考了

[STM32F4]基于F407的硬件移植Free RTOS+FATFS(SDIO)_freertos+fatfs-CSDN博客

文章修改的生成代码

在main.c中添加printf实现的支持函数

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */int __io_putchar(int ch)
{/* Implementation of __io_putchar *//* e.g. write a character to the UART1 and Loop until the end of transmission */HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFFFFFF);return ch;
}
int __io_getchar(void)
{/* Implementation of __io_getchar */char rxChar;// This loops in case of HAL timeout, but if an ok or error occurs, we continuewhile (HAL_UART_Receive(&huart1, (uint8_t *)&rxChar, 1, 0xFFFFFFFF) == HAL_TIMEOUT);return rxChar;
}
/* USER CODE END 0 */

还需要修改sdio.c源码,当我们挂载磁盘时,一次进入:

f_mount

----->find_volume

----->disk_initialize

----->disk.drv[pdrv]->disk_initialize(disk.lun[pdrv])

----->BSP_SD_Init;

SD卡在刚上电寻卡获取卡参数等一系列的初始化过程中,是采用单总线模式(SDIO_BUS_WIDE_1B)且时钟频率不超过400K的模式下进行的;但是问题在于初始化完成之后,要调用 HAL_SD_ConfigWideBusOperation 设置为4总线,在这个过程中要获取 SD卡 scr下的内容,此时采用4总线的方式获取不到数据,所以会导致最终的初始化失败!!!

解决方法:所以我们要将初始化配置里的总线模式从 SDIO_BUS_WIDE_4B 改为 SDIO_BUS_WIDE_1B,然后后面在使用 HAL_SD_ConfigWideBusOperation 使能设置为4总线时就没有问题了;

代码修改如下

void MX_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.Instance = SDIO;hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;hsd.Init.BusWide = SDIO_BUS_WIDE_4B;hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;hsd.Init.ClockDiv = 4;/* USER CODE BEGIN SDIO_Init 2 */hsd.Init.BusWide = SDIO_BUS_WIDE_1B;/* USER CODE END SDIO_Init 2 */}

增加了 hsd.Init.BusWide = SDIO_BUS_WIDE_1B;,也就是在初始化之后将SDIO_BUS_WIDE_4B改为了SDIO_BUS_WIDE_1B

下面是编写freertos的逻辑部分

在freertos.c文件中

添加头文件

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "fatfs.h"
#include "sdio.h"
#include <stdio.h>
/* USER CODE END Includes */

在默认任务中实现挂载、写入的实现。

/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{/* USER CODE BEGIN StartDefaultTask *//*测试SD card FATFS */// printf("》SD card FATFS BSP_SD_Init %ld.\r\n", BSP_SD_Init());// printf("》SD card FATFS HAL_SD_GetCardState %ld.\r\n", HAL_SD_GetCardState(&hsd));printf("Compilation Date: %s %s\n", __DATE__, __TIME__);// mount SD cardint retSD = f_mount(&SDFatFS, (TCHAR const *)SDPath, 1);if (retSD == FR_OK) {printf("》Filesystem mount ok, now you can read/write files.\r\n");// 创建或者打开文件 SD_Card_test.txtretSD = f_open(&SDFile, "SD_Card_test.txt", FA_OPEN_ALWAYS | FA_WRITE);if (retSD == FR_OK) {printf("》open/create SD_Card_test.txt OK, write data to it.\r\n");// Move to end of the file to append dataretSD = f_lseek(&SDFile, f_size(&SDFile));if (retSD == FR_OK) {f_printf(&SDFile, "SD card FATFS test.\r\n");printf("》write data to file OK, write bytes: SD card FATFS test.\r\n");} else {printf("!! File Write error: (%d)\n", retSD);}/* close file */f_close(&SDFile);} else {printf("!! open/Create file SD_Card_test.txt Fail (%d).\r\n", retSD);}} else {printf("!! SDcard mount filesystem error。(%d)\r\n", retSD);}// 不带fatfs调试函数SDCard_ShowInfo();// SDCard_ShowStatus();/* Infinite loop */for(;;){osDelay(1);}/* USER CODE END StartDefaultTask */
}

两个SDIO的测试函数

/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */
/*显示SD卡的信息*/
void SDCard_ShowInfo(void)
{// SD卡信息结构体变量HAL_SD_CardInfoTypeDef cardInfo;HAL_StatusTypeDef res = HAL_SD_GetCardInfo(&hsd, &cardInfo);if (res != HAL_OK) {printf("HAL_SD_GetCardInfo() error\r\n");return;}printf("\r\n*** HAL_SD_GetCardInfo() info ***\r\n");printf("Card Type= %ld\r\n", cardInfo.CardType);printf("Card Version= %ld\r\n", cardInfo.CardVersion);printf("Card Class= %ld\r\n", cardInfo.Class);printf("Relative Card Address= %ld\r\n", cardInfo.RelCardAdd);printf("Block Count= %ld\r\n", cardInfo.BlockNbr);printf("Block Size(Bytes)= %ld\r\n", cardInfo.BlockSize);printf("LogiBlockCount= %ld\r\n", cardInfo.LogBlockNbr);printf("LogiBlockSize(Bytes)= %ld\r\n", cardInfo.LogBlockSize);printf("SD Card Capacity(MB)= %ld\r\n", cardInfo.BlockNbr >> 1 >> 10);
}
// 获取SD卡当前状态
void SDCard_ShowStatus(void)
{// SD卡状态结构体变量HAL_SD_CardStatusTypeDef cardStatus;HAL_StatusTypeDef res = HAL_SD_GetCardStatus(&hsd, &cardStatus);if (res != HAL_OK) {printf("HAL_SD_GetCardStatus() error\r\n");return;}printf("\r\n*** HAL_SD_GetCardStatus() info ***\r\n");printf("DataBusWidth= %d\r\n", cardStatus.DataBusWidth);printf("CardType= %d\r\n", cardStatus.CardType);printf("SpeedClass= %d\r\n", cardStatus.SpeedClass);printf("AllocationUnitSize= %d\r\n", cardStatus.AllocationUnitSize);printf("EraseSize= %d\r\n", cardStatus.EraseSize);printf("EraseTimeout= %d\r\n", cardStatus.EraseTimeout);
}
/* USER CODE END Application */

需要自行添加函数文件声明。

运行效果

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

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

相关文章

Java 进化之路:从 Java 8 到 Java 21 的重要新特性

Java 进化之路&#xff1a;从 Java 8 到 Java 21 的重要新特性 开篇介绍 在软件开发领域&#xff0c;Java 作为一门历史悠久且广泛应用的编程语言&#xff0c;始终保持着其核心竞争力和持续创新能力。自 Java 8 发布以来&#xff0c;Java 经历了一系列重要版本更新&#xff0…

Reactor 事件流 vs. Spring 事件 (ApplicationEvent)

Reactor 事件流 vs. Spring 事件 ApplicationEvent Reactor 事件流 vs. Spring 事件 (ApplicationEvent)1️⃣ 核心区别2️⃣ Spring 事件 (ApplicationEvent)✅ 示例&#xff1a;Spring 事件发布 & 监听1️⃣ 定义事件2️⃣ 发布事件3️⃣ 监听事件&#x1f539; 进阶&…

JVM生产环境问题定位与解决实战(六):总结篇——问题定位思路与工具选择策略

本文已收录于《JVM生产环境问题定位与解决实战》专栏&#xff0c;完整系列见文末目录 引言 在前五篇文章中&#xff0c;我们深入探讨了JVM生产环境问题定位与解决的实战技巧&#xff0c;从基础的jps、jmap、jstat、jstack、jcmd等工具&#xff0c;到JConsole、VisualVM、MAT的…

【5090d】配置运行和微调大模型所需基础环境【一】

RuntimeError: Failed to import transformers.integrations.bitsandbytes because of the following error (look up to see its traceback): No module named triton.ops 原因&#xff1a;是因为在导入 transformers.integrations.bitsandbytes 时缺少必要的依赖项 triton.op…

华为交换综合实验——VRRP、MSTP、Eth-trunk、NAT、DHCP等技术应用

一、实验拓扑 二、实验需求 1,内网Ip地址使用172.16.0.0/16分配 2,sw1和SW2之间互为备份 3, VRRP/STP/VLAN/Eth-trunk均使用 4,所有Pc均通过DHCP获取IP地址 5,ISP只能配置IP地址 6,所有电脑可以正常访问IsP路由器环回 三、需求分析 1、设备连接需求 二层交换机&#xff08;LS…

DeepSeek 开源的 3FS 如何?

DeepSeek 3FS&#xff08;Fire-Flyer File System&#xff09;是一款由深度求索&#xff08;DeepSeek&#xff09;于2025年2月28日开源的高性能并行文件系统&#xff0c;专为人工智能训练和推理任务设计。以下从多个维度详细解析其核心特性、技术架构、应用场景及行业影响&…

Qt实现HTTP GET/POST/PUT/DELETE请求

引言 在现代应用程序开发中&#xff0c;HTTP请求是与服务器交互的核心方式。Qt作为跨平台的C框架&#xff0c;提供了强大的网络模块&#xff08;QNetworkAccessManager&#xff09;&#xff0c;支持GET、POST、PUT、DELETE等HTTP方法。本文将手把手教你如何用Qt实现这些请求&a…

echarts+HTML 绘制3d地图,加载散点+散点点击事件

首先&#xff0c;确保了解如何本地引入ECharts库。 html 文件中引入本地 echarts.min.js 和 echarts-gl.min.js。 可以通过官网下载或npm安装&#xff0c;但这里直接下载JS文件更简单。需要引入 echarts.js 和 echarts-gl.js&#xff0c;因为3D地图需要GL模块。 接下来是HTM…

深度剖析 MySQL 与 Redis 缓存一致性:理论、方案与实战

在当今的互联网应用开发中&#xff0c;MySQL 作为可靠的关系型数据库&#xff0c;与 Redis 这一高性能的缓存系统常常协同工作。然而&#xff0c;如何确保它们之间的数据一致性&#xff0c;成为了开发者们面临的重要挑战。本文将深入探讨 MySQL 与 Redis 缓存一致性的相关问题&…

DAO 类的职责与设计原则

1. DAO 的核心职责 DAO&#xff08;Data Access Object&#xff0c;数据访问对象&#xff09;的主要职责是封装对数据的访问逻辑&#xff0c;但它与纯粹的数据实体类&#xff08;如 DTO、POJO&#xff09;不同&#xff0c;也与 Service 业务逻辑层不同。 DAO 应该做什么&…

【Kubernetes】如何使用 kubeadm 搭建 Kubernetes 集群?还有哪些部署工具?

使用 kubeadm 搭建 Kubernetes 集群是一个比较常见的方式。kubeadm 是 Kubernetes 提供的一个命令行工具&#xff0c;它可以简化 Kubernetes 集群的初始化和管理。下面是使用 kubeadm 搭建 Kubernetes 集群的基本步骤&#xff1a; 1. 准备工作 确保你的环境中有两台或更多的机…

Pycharm(十二)列表练习题

一、门和钥匙 小X在一片大陆上探险&#xff0c;有一天他发现了一个洞穴&#xff0c;洞穴里面有n道门&#xff0c; 打开每道门都需要对应的钥匙&#xff0c;编号为i的钥匙能用于打开第i道门&#xff0c; 而且只有在打开了第i(i>1)道门之后&#xff0c;才能打开第i1道门&#…

在未归一化的线性回归模型中,特征的尺度差异可能导致模型对特征重要性的误判

通过数学公式来更清晰地说明归一化对模型的影响&#xff0c;以及它如何改变特征的重要性评估。 1. 未归一化的情况 假设我们有一个线性回归模型&#xff1a; y β 0 β 1 x 1 β 2 x 2 ϵ y \beta_0 \beta_1 x_1 \beta_2 x_2 \epsilon yβ0​β1​x1​β2​x2​ϵ 其…

JS—页面渲染:1分钟掌握页面渲染过程

个人博客&#xff1a;haichenyi.com。感谢关注 一. 目录 一–目录二–页面渲染过程三–DOM树和渲染树 二. 页面渲染过程 浏览器的渲染过程可以分解为以下几个关键步骤 2.1 解析HTML&#xff0c;形成DOM树 浏览器从上往下解析HTML文档&#xff0c;将标签转成DOM节点&#…

niuhe插件, 在 go 中渲染网页内容

思路 niuhe 插件生成的 go 代码是基于 github.com/ma-guo/niuhe 库进行组织管理的, niuhe 库 是对 go gin 库的一个封装&#xff0c;因此要显示网页, 可通过给 gin.Engine 指定 HTMLRender 来实现。 实现 HTMLRender 我们使用 gitee.com/cnmade/pongo2gin 实现 1. main.go …

openEuler24.03 LTS下安装HBase集群

前提条件 安装好Hadoop完全分布式集群&#xff0c;可参考&#xff1a;openEuler24.03 LTS下安装Hadoop3完全分布式 安装好ZooKeeper集群&#xff0c;可参考&#xff1a;openEuler24.03 LTS下安装ZooKeeper集群 HBase集群规划 node2node3node4MasterBackup MasterRegionServ…

LVGL移植说明

https://www.cnblogs.com/FlurryHeart/p/18104596 参考&#xff0c;里面说明了裸机移植以及freeRTOS系统移植。 移植到linux https://blog.csdn.net/sunchao124/article/details/144952514

ubuntu虚拟机裁剪img文件系统

1. 定制文件系统前期准备 将rootfs.img文件准备好&#xff0c;并创建target文件夹2. 挂载文件系统 sudo mount rootfs.img target #挂载文件系统 sudo chroot target #进入chroot环境3. 内裁剪文件系统 增删裁剪文件系统 exit #退出chroot环境 sudo umount target…

esp826601s固件烧录方法(ch340+面包板)

esp826601s固件烧录方法&#xff08;ch340面包板&#xff09; 硬件 stm32f10c8t6&#xff0c;esp826601s,面包板&#xff0c;ch340(usb转ttl),st_link&#xff08;供电&#xff09; 接线 烧录时&#xff1a; stm32f10c8t6&#xff1a;gnd->负极&#xff0c; 3.3->正极…

Servlet 点击计数器

Servlet 点击计数器 引言 Servlet 是 Java 企业版&#xff08;Java EE&#xff09;技术中的一种服务器端组件&#xff0c;用于处理客户端请求并生成动态内容。本文将详细介绍如何使用 Servlet 实现一个简单的点击计数器&#xff0c;帮助读者了解 Servlet 的基本用法和原理。 …