基于STM32F407的SDCard读写操作及USB挂载(HAL库)

2023-10-26

基于STM32F407的SDCard读写操作及USB挂载(HAL库)

        本来在上一篇SD卡读写也都OK了,后来想着挂载SD卡做U盘,就去查了下资料……结果基本全是HAL库的,原来没用过HAL库,于是本着好奇的心态去下了,说实话,确实看起来简单多了,不过还是有点不习惯,所以在折腾完以后,又回去把标准库的SD卡挂载也做了。不过正常情况下,HAL库上手肯定比标准库会快一点啦。

        关于HAL库的SD卡,一搜一大把,其实没啥特别要说的,非要说只有一些细节,一开始参考的几篇总有或多或少一些小错误,导致前面调试总是不顺,尴尬。

        不多废话了,下面是正题:

目录

基于STM32F407的SDCard读写操作及USB挂载(HAL库)

硬件电路

管脚连接(这个和上一篇一样,就不多写了)

STM32CubeMX Untitled图形化配置

1.新建项目(New Project)

2.设置时钟

3.设置SDIO

4.配置文件系统

5.设置USB

6.配置中断优先级

7.配置系统时钟树

8.保存项目文件

9.进行项目生成

软件修改

1.SDCard测试

2.USD挂载U盘

相关下载



硬件电路

管脚连接(这个和上一篇一样,就不多写了)

STM32-GPIO

STM32-GPIO 复用功能

SDCard管脚

其他

PC8

SDIO-D0

DATA0

PC9

SDIO-D1

DATA1

PC10

SDIO-D2

DATA2

PC11

SDIO-D3

CD/DATA3

PC12

SDIO-CLK

CLK

PD2

SDIO-CMD

CMD

STM32CubeMX Untitled图形化配置

        HAL库可以采用STM32CubeMX进行自动生成(官网有下载),方法简单,但需要注意配置细节!!

1.新建项目(New Project)

        根据自己的方案选择MCU,我用的STM32F407VET6。(可以提前下载,也可以直接在软件里下载,介于我提前下载了,但怎么也导不进去,推荐直接在软件下吧,反正也挺快的!)

 

2.设置时钟

         红色是因为我的截图是配置完成再去的,因为配置SDIO的原因,可以忽略(等配置完SDIO再去看就能看到这个红色了),是提示部分已经被使用。 

3.设置SDIO

        选择4bit Wide,打开中断,增加SDIO_TX和SDIO_RX DMA,DMA数据类型改为Byte。

 

         切记!GPIO需要改成上拉模式(在左侧菜单栏GPIO配置里SDIO修改,在这个页面改不了?不知道是不是我的软件问题?!)。

        如果不修改,SD卡初始化可能可以过,但读写可能卡死,囧,当时卡了好一会,也可能是因为我硬件是偷懒直接飞线没有上拉的原因。

         时钟倍数按照自己的时钟来计算,如果初始化有数据,读写无数据,将数值修改(默认是0)。

        如果不修改,SD卡初始化可以读出数据,但读、写操作可能出错,我出现的情况的是写操作可以但读操作异常。在后期挂载USB做U盘时,有一次忘记修改,出现过读取乱码或无法打开的情况,更改时钟分频后恢复正常。

        如果只是SD卡读写,到这里基本就差不多了(就缺一个中断优先级的配置,即6)。

4.配置文件系统

         设置路径中文可识别

      SDIO的 DMA

        如果只需要文件系统,不需要挂载电脑端读取U盘,到这里基本就差不多了(就缺一个中断优先级的配置,即6)。

5.设置USB

        使能中断

        设置USBdevice

6.配置中断优先级

        前面提到过的优先级,优先级不当会导致设备读写冲突而卡死

        中断优先级的大小关系是:

        SDIO global interrup >SDIO DMA > USB

        数字越小优先级越高(数字随便,满足上面说的就可以)

7.配置系统时钟树

        我的板子是外部8M,实际配置按照电路板设计来选择。按照手册SDIO时钟48M

8.保存项目文件

        初次创建时,项目文件名称和路径不能有中文(已创建好的project再次打开修改时,可以有中文路径),否则在生成项目时会出错而导致无法生成.s。堆栈适当加大(做文件系统和U盘尽量大一点,只是单纯读写则无所谓)。

        Code Generator下的Generated files第一项进行勾选,可以将.c、.h文件单独生成,否则将会全部堆积在main.c文件中。

        如果需要挂载做U盘,除了上面说的堆栈需要增加(我最后改成了0x8000)还有USB的MSC_MEDIA_PACKET(默认512,我改成了32768),否则传输文件时速度慢得令人发指,不过在FS模式下,我自己做下来基本存入文件速度在800-900K,最快1M+,导出文件会快点。就只是传输TXT文本数据什么的10~100M+的文件基本上还算可以接受,如果是G以上的还是用HS吧。这几个参数可以在代码修改但建议在配置时修改,不然重新生产配置时候一个刷新,忘了自己没改就尴尬了(别问,问就是这种蠢事不小心发生过了)。

9.进行项目生成

        点击GENERATE CODE,忽视WARNINGS,生成后打开Project即可。

         生成后打开project

        至此,项目的工程文件初步生成结束,HAL库在使用上比较便捷,大多数的初始化已经由系统完成,按照功能在需要的地方加上用户代码即可,注意,用户代码添加在/* USER CODE BEGIN */及/* USER CODE END */之间,更改图形化配置时,重新生成项目文件代码不会被删除,否则将会被删除覆盖。

软件修改

1.SDCard测试

        注释MX_USB_DEVICE_Init();先不使用USB,如果没有配置则不用        管。(其实不注释也没事,只是调试一个功能,我就习惯性一步一步来免得万一相互有影响呢,毕竟HAL库我第一次用)

        添加SDCard初始化,SD_Driver.disk_initialize(0);  初始化成功后可以读取到SDCard信息,可以在Watch窗口添加hsd.SdCard查看或者使用串口打印输出到电脑查看信息。不同的卡信息不一样,以自己用的卡为准!

         测试读写操作(偷懒我就直接测试文件系统读写了,如果一步一步熟悉,可以先试着块读写,然后再看文件系统)

        在main()中添加初始化

    /* USER CODE BEGIN 0 */
    uint8_t  readBuf[512];
    uint8_t  writeBuf[512];
    BYTE work[_MAX_SS];
    char *fileName = "test.csv";
    uint32_t writeLen;
    uint32_t readLen;
    /* USER CODE END 0 */

        加文件读、写操作

        可以通过观察readBuf来查看读取是否正确。

        文件读、写可以参考上一篇。

        举个例子:挂载、文件写、文件读、卸载(用法和上一篇基本一样)

	retSD = f_mount(&SDFatFS, SDPath, 0);
	
	retSD = f_open(&SDFile,fileName,FA_OPEN_APPEND|FA_WRITE);
	sprintf((char *)writeBuf,"%s","11,44,33\n");
	retSD = f_write(&SDFile,writeBuf,strlen((const char *)writeBuf),&writeLen);
    retSD = f_close(&SDFile);
	
	retSD = f_open(&SDFile,fileName,FA_OPEN_EXISTING|FA_READ);
	retSD = f_read(&SDFile,readBuf,512,&readLen);
	f_close(&SDFile);
	
    f_mount(NULL,SDPath,1);

        Fatfs文件系统下的读写也就这样了,用HAL库真的方便好多!

2.USD挂载U盘

        只是挂载文件,以上设置直接就可以,如果要进行传输,建议增大堆栈、修改USB_Device缓存(前面提过了),可以在STM32Cube配置(前面提过了),也可以在代码中修改(不太推荐,毕竟重新生成配置时候万一忘了改就尴尬了,但是为了测试速度,测试参数时候可以这么干)

//#define MSC_MEDIA_PACKET     512U
#define MSC_MEDIA_PACKET     32768U
Stack_Size		EQU     0x8000
Heap_Size      EQU     0x8000

        STM32Cube在生产项目文件时,已经写好了函数接口,在usbd_storage_if.c中需要的函数添加自己的代码即可。一般来说主要要加东西的是STORAGE_Init_FS, STORAGE_GetCapacity_FS, STORAGE_Read_FS, STORAGE_Write_FS。

USBD_StorageTypeDef USBD_Storage_Interface_fops_FS =
{
  STORAGE_Init_FS,
  STORAGE_GetCapacity_FS,
  STORAGE_IsReady_FS,
  STORAGE_IsWriteProtected_FS,
  STORAGE_Read_FS,
  STORAGE_Write_FS,
  STORAGE_GetMaxLun_FS,
  (int8_t *)STORAGE_Inquirydata_FS
};

        STORAGE_Init_FS我没加代码,可以把SD卡初始化放在里面,不过由于我SD卡会需要单独使用,所以就单独在main()里面初始化了。

        STORAGE_GetCapacity_FS 

int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size)
{
  /* USER CODE BEGIN 3 */
	HAL_SD_CardInfoTypeDef info;
	if(HAL_SD_GetCardState(&hsd) ==  HAL_SD_CARD_TRANSFER)
	{
		HAL_SD_GetCardInfo(&hsd, &info);
		*block_num = info.LogBlockNbr;
		*block_size = info.LogBlockSize;
 
		return  USBD_OK;
	}
	return  USBD_FAIL;
  /* USER CODE END 3 */
}

        STORAGE_Read_FS,注意,如果前面配置用了DMA这里调用的函数就是HAL_SD_ReadBlocks_DMA(),否则调用HAL_SD_ReadBlocks()。

        之前调试一直读不到数据,参考的资料配置都用的DMA,但是这里的函数用的HAL_SD_ReadBlocks(),囧,也不知道是不是别的地方改了代码,尽信书不如无书啊!后来直接一条一条跟踪着查……说到底还是第一次用对HAL库不太了解,跟着别人的资料走,然后走到岔路去了。

int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
  /* USER CODE BEGIN 6 */
 
  int8_t ret = USBD_FAIL;  
  if( HAL_SD_ReadBlocks_DMA(&hsd, buf, blk_addr, blk_len) == HAL_OK )
  //if( HAL_SD_ReadBlocks(&hsd, buf, blk_addr,  blk_len, HAL_MAX_DELAY) == HAL_OK )
  {
		ret = USBD_OK;
		while(HAL_SD_GetState(&hsd) == HAL_SD_STATE_BUSY){};
		while( HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER ){};
  }    
  return ret;
  /* USER CODE END 6 */
}

        STORAGE_Write_FS,注意点和上面一样……不多说了,一把泪!

int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
  /* USER CODE BEGIN 7 */

  int8_t ret = USBD_FAIL; 
  if( HAL_SD_WriteBlocks_DMA(&hsd, buf, blk_addr, blk_len) == HAL_OK )
  //if( HAL_SD_WriteBlocks(&hsd, buf, blk_addr, blk_len, HAL_MAX_DELAY) == HAL_OK )
  {
		ret = USBD_OK;
		while(HAL_SD_GetState(&hsd) == HAL_SD_STATE_BUSY){};
		while( HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER ){};
  }    
  return ret;
  /* USER CODE END 7 */
}

        上面是非常粗略的,简单的,就把读写调用了下 ,再加上初始化,基本上用是可以用了。其他函数按照自己的需求添加自己需要的功能即可。

        注意,初始化SDCard后,才能在电脑看到U盘显示。我是把初始化放在main(),自己可以根据需要自己放。

/* USER CODE BEGIN 2 */
	SD_Driver.disk_initialize(0);  
/* USER CODE END 2 */

        STM32读、写文件时,不要去操作电脑端的U盘。另外写入后,千万、一定、绝对不要忘记close,不然全部白写!

         顺便提一句不是吐槽的吐槽,HAL库调来调去的真的好烦啊,跟着他跑可以对着N个文件跑一大圈。其实如果偷懒直接调用BSP_SD_Init();初始化也是可以的。读、写也是一样。

        HAL库也有方便的地方,例如不管你用那块芯片,配置完了,上面的函数调用基本就一致了,不用再去纠结寄存器这些了。

——————————————————————————

新增:

相关下载

1.工程文件源码

https://download.csdn.net/download/a390478024/86742278

2.STM32CubeMX安装包

https://download.csdn.net/download/a390478024/86742647

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

基于STM32F407的SDCard读写操作及USB挂载(HAL库) 的相关文章

随机推荐

  • 2020年09月 C/C++(三级)真题解析#中国电子学会#全国青少年软件编程等级考试

    C C 编程 1 8级 全部真题 点这里 第1题 铺砖 对于一个2行N列的走道 现在用12 22的砖去铺满 问有多少种不同的方式 时间限制 3000 内存限制 131072 输入 整个测试有多组数据 请做到文件底结束 每行给出一个数字N 0
  • 常见三方结算周期都是有哪些?

    T1 为工作日次日 就是一个工作日结算 如遇到节假日 则延迟到节假日结束后第一个工作日结算 T0 为工作日当天结算 当然如遇节假日 节假日中的交易不结算 延迟到节假日结束后的第一个工作日统一结算 D1 为交易后自然日次日结算 包含节假日内
  • Streamlit 讲解专栏(三):两种方案构建多页面

    文章目录 1 前言 2 第一种方案 使用Session State实现多页面交互 2 1 Session State简介 2 2 多页面应用的基本结构 2 3 实现多页面交互的代码示例 2 4 Session State机制的优缺点 3 第
  • 如何实现 Array 和 List 之间的转换?

    在 Java 中 我们可以通过以下方法实现 Array 和 List 之间的转换 数组转 List String arr apple banana orange List
  • 2023最新宝塔面板8.0.1企业版开心版

    宝塔面板是目前一个非常好用的可视化面板 这几天我自己搭建了一个宝塔云端 不经过宝塔官方接口 无需绑定手机号 安装之后直接显示企业版 所有插件全部免费使用 付费的也能使用 脚本如下 centos安装脚本 yum install y wget
  • 【蓝牙开发】Andorid蓝牙绝对音量

    蓝牙绝对音量相关基础知识 1 什么是绝对音量 Android 蓝牙部分的官方文档有如下描述 Androud Bluetooth Service 在 Android 6 0 及更高版本中 Android 蓝牙堆栈允许音频源设置绝对音量 以便用
  • 【毕业设计】深度学习行人重识别算法研究与实现 - python opencv yolo Reid

    文章目录 0 前言 1 课题背景 2 效果展示 3 行人检测 4 行人重识别 5 其他工具 6 最后 0 前言 Hi 大家好 这里是丹成学长的毕设系列文章 对毕设有任何疑问都可以问学长哦 这两年开始 各个学校对毕设的要求越来越高 难度也越来
  • WIN10的传递优化文件是个什么鬼?能删除吗?

    Win10正式版系统使用时间长了以后 电脑磁盘总会产生一些冗余的文件和磁盘碎片 我们可以用一些工具软件对硬盘进行优化和处理 其实利用系统自带的工具也可以清理磁盘的 但在操作过程中 我们常常会看到一个名叫 传递优化文件 的选项 而且多数情况下
  • UA到底是什么

    欢迎关注勤于奋 每天12点准时更新国外LEAD相关技术 今天这篇文章也是比较基础的文章 其实有很多新手还是搞不懂 所以我特意来写篇文章聊聊这个UA 到底是什么 在我们国外LEAD当中起到的作用 如果搞不懂 很多东西你都没有办法灵活运用 首先
  • 19款资源整合类网站推荐:每一个网站都堪称以一敌百

    强烈推荐这19个资源聚合网站 每一个网站都足以堪称 以一敌百 因为每一个网站都聚合了相当多不同类型 不同领域的网站资源 并且做了分类导航方便大家查找使用 所以 与其收藏那么多零碎的网址 不如收藏下面这些网站资源 比格张 发现更好的资源 一个
  • java8的日期工具类(获取当前时间 相隔天数 小时 分钟 秒等处理)

    package com example list test import java text ParseException import java text SimpleDateFormat import java time import
  • tvm 入门(二)

    代码是一个tvm入门的例子 以向量相加为例 使用tvm的流程是 1 描述串行的向量相加是怎么做的 2 描述并行的时候 怎么对计算单元做划分 3 编译目标函数 本文所示代码可以看到用tvm生成的cuda代码 4 把编译生成的内容保存成文件 加
  • VMware Workstation 未能启动 VMware Authorization Service。您可以尝试手动启动 VMware Authorization Service。如果此问题仍然存

    报错界面 解决方法一 管理员身份运行即可 解决方法二 打开服务 找到VMware Authorization Service右键选择然后点启动 然后发现又报了下面的错误 不慌这时候打开属性把启动类型改成手动即可
  • String、StringBuffer和StringBuilder的异同点

    String StringBuffer StringBuilder三者的异同 String 不可变的字符序列 底层使用char 存储 StringBuffer 可变的字符序列 线程安全的 效率低 底层使用char 存储 StringBuil
  • OpenAI的人工智能语音识别模型Whisper详解及使用

    1 whisper介绍 拥有ChatGPT语言模型的OpenAI公司 开源了 Whisper 自动语音识别系统 OpenAI 强调 Whisper 的语音识别能力已达到人类水准 Whisper是一个通用的语音识别模型 它使用了大量的多语言和
  • 安装mingw出现download failed和unable to continue

    利用mingw get setup安装mingw总是出现download failed和unable to continue错误 截图如下 错误原因 因为服务器在外网 可能是对方服务器不稳定 连接出错等问题导致 解决办法 下载对应等离线安装
  • 11信号学习之sigaction函数及使用其实现信号捕捉案例(信号最重要的一节)

    概述 注意 在我关于信号的文章中 我所说的系统的mask的意思实际上是进程的mask 每个进程的mask都是唯一的 所以我就将其称为系统的 但不能理解为每个进程的mask都是共用的 1 sigaction函数 1 上一篇我们说的signal
  • 基于STM32的DMX512开发

    首先基本了解一下DMX512的基本协议 一 DMX512协议 DMX 是Digital MultipleX 的缩写 意为多路数字传输 DMX512控制协议是美国舞台灯光协会 usITT 于1990年发布的灯光控制器与灯具设备进行数据传输的工
  • 复习使用git(二)

    删除远程分支 git push origin delete 分支名 撤销修改 撤销工作区的修改 已修改 但尚未添加 add 使用 git restore 文件名 撤销工作区的修改 Note git checkout 文件名 checkout
  • 基于STM32F407的SDCard读写操作及USB挂载(HAL库)

    基于STM32F407的SDCard读写操作及USB挂载 HAL库 本来在上一篇SD卡读写也都OK了 后来想着挂载SD卡做U盘 就去查了下资料 结果基本全是HAL库的 原来没用过HAL库 于是本着好奇的心态去下了 说实话 确实看起来简单多了