STM32完全学习——FATFS0.15移植SD卡
一、下载FATFS源码
大家都知道使用CubMAX可以很快的将,FATFS文件管理系统移植到单片机上,但是别的芯片没有这么好用的工具,就需要自己从官网下载源码进行移植。我们首先解决SD卡的驱动问题,然后再移植FATFS文件管理系统。

二、SD卡的DMA读写实现

这个我们直接使用工具生成就可以了,需要注意上面的那几点,不然会工作不正常。为了让大家看得更加清楚,下面的几个函数下的都比较简单,没有过多的判断。尽可能地缩短代码。下面这几个函数的实现是必须的不能在减少了。
//获取卡的状态
uint8_t SD_GetCardState(void)
{HAL_SD_CardStatusTypeDef pStatus;if (HAL_SD_GetCardStatus(&hsd, &pStatus) != 0){return HAL_BUSY;}else{return HAL_OK;}}
void SD_ReadBlocks_DMA(uint8_t *buf, uint32_t sector, uint32_t cnt)
{HAL_SD_ReadBlocks_DMA(&hsd, buf, sector, cnt);//通过DMA读取SD卡n个扇区while(SD_GetCardState() != SD_TRANSFER_OK);//等待SD卡读完
}
uint8_t SD_WriteBlocks_DMA(uint8_t *buf, uint32_t sector, uint32_t cnt)
{HAL_SD_WriteBlocks_DMA(&hsd, (uint8_t*)buf, sector, cnt);//通过DMA写SD卡n个扇区//等待SD卡写完while(SD_GetCardState()!=SD_TRANSFER_OK)
}
三、FATFS中diskio.c里面相关函数的实现
#define DEV_FALSH 0
#define DEV_SD 1#define SECTOR_SIZE 4096 //定义扇区大小DSTATUS disk_status (BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{DSTATUS stat;switch (pdrv) {case DEV_FALSH :stat = EN25QXX_ReadSR(); //读取FLASH状态的函数return stat;case DEV_SD :stat = SD_GetCardState(); //读取SD卡状态的函数return stat; }return STA_NOINIT;
}
DSTATUS disk_initialize (BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{DSTATUS stat = 0;;switch (pdrv) {case DEV_FALSH : return stat; //如果你在外面已经做过了相关模块的初始化啥的这里直接返回接可以了case DEV_SD :return stat;}return STA_NOINIT;
}
DRESULT disk_read (BYTE pdrv, /* Physical drive nmuber to identify the drive */BYTE *buff, /* Data buffer to store read data */LBA_t sector, /* Start sector in LBA UINT count /* Number of sectors to read */
)
{uint32_t i = 0;uint8_t result = 0;switch (pdrv) {case DEV_FALSH :{uint32_t addr = sector*SECTOR_SIZE;for (i=0; i<count; i++){EN25QXX_Read((BYTE *)buff, addr, SECTOR_SIZE);addr += SECTOR_SIZE;buff += SECTOR_SIZE;}return RES_OK;}case DEV_SD :{SD_ReadBlocks_DMA(buff, sector, count);return RES_OK;}}return RES_PARERR;
}
这里需要注意的是FLASH和SD卡里面的两个函数是不太一样的,FLASH里面的函数只能写一个扇区,要想写多个扇区那你就得循环写,在FATFS文件系统里面,他的sector地址是从0-0xffffffff他是一个连续的地址,一个sector就是一个地址,但是在FLASH里面他的最小单位不是扇区,他有比扇区更小的操作也就是页,但是他最小的单位是可以按字节来读取的,也就是说他是一个字节一个地址,如果我们直接将FATFS里面的地址传过来,在FLASH里面他就会认为是字节地址,因此我们需要一个地址的偏移,也就是FLASH里面一个扇区的大小,也就是4096.理解这里你需要知道他们呢两个的基本操作的单元是不一样的。但是在SD卡里面就不需要这样的偏移,因为SD卡里面他就是以扇区为大小来进行读取的。也就是SD卡里面每个地址是512个字节。他的最小读写单元就是512个字节,也就是一个扇区。因此我们在传入参数的时候不需要将地址进行偏移。而且SD卡的读写函数是一次可以读多个扇区的,因此可以直接将count传入,他也是可以直接读的。其实说白了就是如果你的最小读写单元就是512个字节那么你就不需要地址的偏倚,否则你就要根据自己设置的扇区大小进行偏移。
DRESULT disk_write (BYTE pdrv, /* Physical drive nmuber to identify the drive */const BYTE *buff, /* Data to be written */LBA_t sector, /* Start sector in LBA */UINT count /* Number of sectors to write */
)
{uint32_t i = 0;uint8_t result = 0;switch (pdrv) {case DEV_FALSH :{uint32_t addr = sector*SECTOR_SIZE;for (i=0; i<count; i++){EN25QXX_Erase_Sector(addr);EN25QXX_Write_Sector((BYTE *)buff, addr, SECTOR_SIZE);addr += SECTOR_SIZE;buff += SECTOR_SIZE;}return RES_OK;}case DEV_SD :{SD_WriteBlocks_DMA((BYTE *)buff, sector, count);return RES_OK;}}return RES_PARERR;
}
DRESULT disk_ioctl (BYTE pdrv, /* Physical drive nmuber (0..) */BYTE cmd, /* Control code */void *buff /* Buffer to send/receive control data */
)
{
// DRESULT res = 0;
// int result;switch (pdrv) {case DEV_FALSH :{switch (cmd){case CTRL_SYNC:return RES_OK;case GET_SECTOR_COUNT:{*(DWORD *)buff = 4096; //表示扇区的个数return RES_OK;} case GET_SECTOR_SIZE:{*(WORD *)buff = SECTOR_SIZE; //表示每个扇区的大小return RES_OK;} }}case DEV_SD :{switch (cmd){case CTRL_SYNC:return RES_OK;case GET_SECTOR_COUNT:{*(DWORD *)buff = 31116288; //表示扇区的个数return RES_OK;} case GET_SECTOR_SIZE:{*(WORD *)buff = 512; //表示每个扇区的大小return RES_OK;} case GET_BLOCK_SIZE:{*(DWORD *)buff = 512; //表示每个扇区的大小return RES_OK;}default :return RES_PARERR;}}}return RES_PARERR;
}
上面这个函数的实现我是直接将数据写到里面的,这样操作如果换了别的SD卡,就完蛋了,严谨一点的操作是使用相关函数,读出来。我这里就比较粗糙的实现了,主打一个能用就行。
四、进行挂载
如果你的挂载直接就返回0,那么恭喜你直接就成功了,但是在大多数的情况下我们的挂载是不能成功的。下面就针对几个返回值进行故障的排除。
f_mount返回11 也就是你要使用几个外设,如果你就一个SD卡,那么问题,如果有2个你这里就得改成2,不然就会返回错误代码11

f_mount返回1,当这些函数没有正确的返回值就会报这个错误,我将哪里注释掉之后,就会产生这个错误。

还有一种就是返回值是13的错误,他的意思是卡上面没有文件系统导致的。但是我将卡格式化后发现,还是识别不上。还是返回13。经过一夜的查找发现是DMA设置那里没有设置地址的递增,就导致他一次只能读几个字节,后面的读写完全就是不正常的,内存里面是有一个缓冲区的,如果地址传完不发生偏移,那这个缓冲区就是没有意义的,因此只要你传入的DMA里面的buf[]数组里面的元素不止一个那么这里就得设置地址偏移。

res = f_mount(&fs, "1:/", 1);
一般来讲只要能挂载成功,其他的函数都是可以正常工作的,前提是你挂载的时候,后面选项里面一定要是 1,如果是0,就算有问题,他写会显示挂载成功的,这时候肯定是不行的。
相关文章:
STM32完全学习——FATFS0.15移植SD卡
一、下载FATFS源码 大家都知道使用CubMAX可以很快的将,FATFS文件管理系统移植到单片机上,但是别的芯片没有这么好用的工具,就需要自己从官网下载源码进行移植。我们首先解决SD卡的驱动问题,然后再移植FATFS文件管理系统。 二、SD…...
Linux -- 生产消费模型之环形队列、信号量
目录 前言 环形队列 如何用环形队列实现生产消费模型? 信号量 sem_t sem_init(初始化信号量) sem_destroy(销毁信号量) 什么是PV操作? sem_wait(P操作,减少信号量ÿ…...
Ashy的考研游记
文章目录 摘要12.1112.2012.21 DAY1(政治/英语)政治英语 12.22 DAY2(数学/专业课)数学专业课 结束估分 摘要 在24年的12月里,Ashy完成了他的考研冲刺,顺利的结束了他本年度的考研之旅。 在十二月里&#…...
MySQL线上事故:使用`WHERE`条件`!=xxx`无法查询到NULL数据
前言 在一次 MySQL 的线上查询操作中,因为 ! 的特性导致未能正确查询到为 NULL 的数据,险些引发严重后果。本文将详细解析 NULL 在 SQL 中的行为,如何避免类似问题,并提供实际操作建议。 1. 为什么NULL会查询不到? 在…...
vue3学习笔记(11)-组件通信
1.props 父传子 子传夫 父传子 接收用defineProps([]) 空字符串也是假 2.自定义事件 $event:事件对象 ref定义的数据在模板里面引用的时候可以不用.value 3.子传父 宏函数 触发事件 声明事件 defineEmits() 挂载之后3s钟触发 4.命名 肉串命名 5.任意组件通信 mitt pubs…...
【PDF物流单据提取明细】批量PDF提取多个区域内容导出表格或用区域内容对文件改名,批量提取PDF物流单据单号及明细导出表格并改名的技术难点及小节
相关阅读及下载: PDF电子物流单据: 批量PDF提取多个区域局部内容重命名PDF或者将PDF多个局部内容导出表格,具体使用步骤教程和实际应用场景的说明演示https://mp.weixin.qq.com/s/uCvqHAzKglfr40YPO_SyNg?token720634989&langzh_CN扫描…...
张量与数据类型
Pytorch最基本的操作对象——张量(tensor),张量是Pytorch中重要的数据结构,可认为是一个高维数组。一般的,标量(scalar)是只有大小没有方向的量,如1、2、3等;向量&#x…...
torchvision.utils.make_grid 解释下
torchvision.utils.make_grid 是 PyTorch 中 torchvision 库提供的一个实用函数,用于将多个图像拼接成一个网格,方便进行可视化。 主要功能 make_grid 将一批图片组织成一个网格形式,输出一个单一的张量,便于使用可视化工具(如 Matplotlib)查看图像。 参数解释 torchvi…...
Android原生Widget使用步骤
需要创建三个XML文件以及一个Class文件 三个XML文件分别是 Widget布局文件 <?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_p…...
实验八 指针2
7-1 利用指针返回多个函数值 分数 30 全屏浏览 切换布局 作者 陈晓梅 单位 广东外语外贸大学 读入n个整数,调用max_min()函数求这n个数中的最大值和最小值。 输入格式: 输入有两行: 第一行是n值; 第二行是n个数。 输出格式: 输出最大…...
1 数据库(下):多表设计 、多表查询 + SQL中的with查询语法(MySQL8.0以后版本才支持这种新语法)+ 数据库优化(索引优化)
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、多表设计1 多表设计-概述2 三种多表关系一对多(多对一)(1)无外键约束(逻辑外键)&…...
什么是.net framework,什么是.net core,什么是.net5~8,版本对应关系
我不知道有多少人和我一样,没学习过.netCore,想要学习,但是版本号太多就蒙了,不知道学什么了,这里解释下各个版本的关系 我们一般开始学习微软的时候,都是开始学习的.netframework,常用的就是4…...
vulhub-wordpress靶场
一.主题上传漏洞 来到靶场点击主题选择add new 这里有一个上传主题的地方 我们可以去网上找到wordpress主题下载一个 wordpress模板 网页设计模板 免费 免费下载 - 爱给网 下载完成后对我们有用的东西只有这一个目录,把它拖出来 点开moban目录后,创建…...
安装与配置
《PHP Libxml》是一个在PHP中处理XML和HTML文档的重要库。它提供了丰富的API,支持DOM、SimpleXML和XMLReader等多种解析方式,广泛应用于各种编程语言和项目中。 安装与配置 安装: 在PHP中,libxml扩展通常是默认启用的。如果你需要手动安装&…...
斗鱼Android面试题及参考答案
常用的图片框架有哪些? Glide:是一个快速高效的 Android 图片加载库,专注于平滑滚动。它支持多种图片格式,包括 GIF,具有高效的缓存策略,能自动管理图片的生命周期,避免内存泄漏和 OOM 错误。其 API 简洁易用,可轻松实现图片的加载、显示和缓存等功能,如一行代码即可实…...
Could not install Gradle distribution from 的解决办法
在安装完成AndroidStudio之后,运行工程出现如下错误 Could not install Gradle distribution from https://services.gradle.org/distributions/gradle-6.5-bin.zip. 错误原因是:对应版本的Gradle文件下载失败了,我这里是gradle-6.5-bin.zip,不同版本的android studio也可…...
基于 SensitiveWordBs 实现敏感词过滤功能
在现代的互联网应用中,敏感词过滤已成为一个必不可少的功能,尤其是在社交媒体、评论审核等需要保证内容健康的场景下。本文将基于开源库https://github.com/houbb/sensitive-word,详细讲解如何通过自定义敏感词库和工具类实现高效的敏感词过滤…...
网络安全威胁2024年中报告
下载地址: 网络安全威胁2024年中报告-奇安信...
批次特征组杂记
批次特征组杂记 运维的时候新增了一个批次特征,然后发现不能按照要求跑到之前已经分好的批次特征组。 研究了半天原来是通过布局实现的。 特此记录。...
【HarmonyOS】解决自定义弹框和键盘之间安全距离的问题
【HarmonyOS】解决自定义弹框和键盘之间安全距离的问题 一、问题背景 我们在应用开发评论输入框时,常规的需求样式是:输入框view和键盘贴近,上半部展示信息区的形式,这样的设计,方便用户不割裂的去评论发言。 但是在…...
19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...
Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...
UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...
苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...
CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云
目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...
