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

stm32 CubeMx 实现SD卡/sd nand FATFS读写测试

文章目录

stm32 CubeMx 实现SD卡/SD nand FATFS读写测试

1. 前言

2. 环境介绍

2.1 软硬件说明

2.2 外设原理图

3. 工程搭建

3.1 CubeMx 配置

3.2 SDIO时钟配置说明

3.2 读写测试

3.2.1 添加读写测试代码

3.3 FATFS文件操作

3.3.1 修改读写测试代码

3.4 配置问题记录

3.4.1 CubeMx生成代码bug

3.4.2 SD插入检测引脚配置

4. 结束语

1. 前言

SD卡/SD nand是嵌入式开发中常为使用的大容量存储设备,SD nand虽然当前价格比SD卡高,但胜在价格、封装以及稳定性上有优势,实际操作和SD卡没什么区别。

关于 SD卡/SDnand 的驱动,有了CubeMx之后其实基本上都自动生成了对应的驱动了,基本上把驱动配置一下之后,自己写一些应用就可以完成基本的读写了,同时关于FATFS文件系统,也可以直接采用CubeMx配置,也不用自己移植,因此使用STM32开发这些还是比较爽的!不过使用过程中也有一些坑,自动生成的驱动有时候也还是有一些bug,因此还是需要大家对对应驱动有一定的了解。

本文将主要分享关于使用 CubeMx 配置 stm32 的工程,通过SDIO总线完成 SD卡/SD nand 的读写,并配置FATFS,采用文件操作实现对 SD卡/SD nand 的读写操作;此外还将分享博主在调试过程中遇到的一些问题,比如CubeMx自动生成的驱动存在的bug等,以及分享关于驱动部分的代码分析!

2. 环境介绍

2.1 软硬件说明

硬件环境:

主控:stm32f103vet6

SD nand: CSNPGCR01-AOW

软件环境:

CubeMx版本:Version 6.6.1

注意:当前最新版本 V6.8.0,生成的工程配置存在bug,具体细节在后文描述

2.2 外设原理图

SD卡槽原理图部分如下:

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

3. 工程搭建

3.1 CubeMx 配置

  1. 1.选择芯片,ACCESS TO MCU SELECTOR 

  2. SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

  3. 2.搜索对应的芯片型号,在对应列表下方选择对应芯片

  4. SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

  5. 3.配置时钟方案,采用外部高速时钟,无源晶振方案

  6. SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

  7. 4.配置调试器,由于我采用SWD调试接口,因此选择 Serial Wrie 串行总线

  8. SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

  9. 5.配置SDIO外设,由于我们所使用的SD nand支持4线传输,因此此处选择4线宽度;如果你所使用的SD nand或SD卡不支持4线传输,此处应选择1线宽度;支持4线宽度的SD卡肯定可以使用1线宽度,因此如果你实在不知道你的SD卡支持几线宽度,你可以直接选择1线宽度!4线和1线宽度的差别也就在于速度上相差了4倍!

  10. (注意这里暂时不需要对SDIO的参数进行配置,后面我们再回来配置!)

  11. SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

  12. 6.完成时钟树配置:

  • 配置外部晶振频率

  • 调整时钟选择,SYSCLK由PLL产生,PLL由外部时钟倍频产生

  • 配置SDIO外设时钟,注意此处SDIO外设比较特殊,有两个时钟!具体原因见后文!

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

  1. 7.

  2. 修改SDIO参数配置,主要是修改SDIOCLK的分频
  • 由于我们上述配置的SDIO时钟为 72M,而SD卡支持的通讯速率在0MHz至25MHz之间,因此我们需要分频,配置 SDIO Clock divider bypass 为 Disable

  • 此处设置 SDIOCLK clock divide factor CLKDIV分频系数为 8,这个受限于具体的SD卡支持的最大速度。如果设置值较小,可能由于SDIO_CK速度过高,SD卡/SDnand不支持,导致通讯失败,因此建议先将此值设大点(或查看SD卡/SDnand手册,或先设一个较大值,软件完成SD信息读取后再配置)

  • 注意这个配置的时钟是用于SD读写通讯时候的时钟,而不是SD卡信息识别过程时的速度!

    SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

    SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

8.勾选 FATFS 配置,选择 SD Card

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

9.配置SD卡检测引脚,有以下两种方案

  • 方案一:选择一个输入IO,作为触发引脚

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

  • 方案二:不配置输入IO,最后生成代码的时候无视警报即可,生成的代码会自动取消输入检测判断

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

10.配置调试串口,用来打印信息,此处我选择USART1,大家可根据自己硬件环境自行选择

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

11.配置工程信息

  • 配置工程名

  • 选择工程路径

  • 配置应用程序结构,我习惯选择 Basic 结构

  • 选择IDE工具及版本

  • 修改堆栈大小,适当改大一点,怕不够用

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

12.勾选将外设初始化放置在独立的.c.h文件,这样每个外设的初始化是独立的,方便阅读移植!

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

13.生成代码

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

3.2 SDIO时钟配置说明

在上述CubeMx时钟配置中,外设的时钟一般都是只有一路过去,但是在此处我们会发现SDIO的时钟在时钟树中有两个!没弄清楚还会以为这是CubeMx出现bug了!

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

其实这是SDIO外设的特殊点,我们查看数据手册上的时钟树,便可以发现,实际上是真的有两路时钟,分别是:1)SDIOCLK;2)至SDIO的AHB接口;

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

之后,我们看到数据手册的SDIO章节,我们可以看到SDIO外设分为:1)AHB总线接口 和 2)SDIO适配器两大块,且使用不同的时钟,这也就是我们在时钟树配置中可以看到有两路时钟配置的原因了!

从下图我们可以知道,SDIO外设不同于其他外设,其外设模块部分与中断、DMA是分开的,并采用不同的时钟!

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

关于AHB总线接口及SDIO适配器更多细节,大家可自行阅读参考手册部分章节内容,此处不做赘述。

此外,关于时钟配置有一个特别需要注意的,也就是SDIO_CK时钟信号。SDIO_CK时钟,也就是我们SDIO外设与SD卡/SD nand通讯的CLK时钟,从上图我们可知,SDIO_CK时钟来自SDIO适配器,也就是来自SDIOCLK,对应CubeMX时钟配置中的:

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

3.2 读写测试

3.2.1 添加读写测试代码
  1. 1.使能 MicroLIB 微库,否则调用 printf 函数会卡住

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

2.修改编码规则为 UTF-8,这是由于我们CubeMx中配置的FATFS的编码格式为 UTF-8导致,如果不修改为 UTF-8 则部分中文会乱码! //TODO:确认是由FATFS配置导致

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

3.添加 printf 重映射 (位置可根据自行决定)

  1. #include <stdio.h>

  2. int fputc(int ch, FILE *f)

  3. {

  4. HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1,0xffff);

  5. return (ch);

  6. }

4.添加 sdcard 信息打印函数,查看卡片信息

  1. HAL_SD_CardInfoTypeDef  SDCardInfo;    

  2. void printf_sdcard_info(void)

  3. {

  4. uint64_t CardCap;                       //SD卡容量

  5. HAL_SD_CardCIDTypeDef SDCard_CID; 

  6. HAL_SD_GetCardCID(&hsd,&SDCard_CID); //获取CID

  7. HAL_SD_GetCardInfo(&hsd,&SDCardInfo);   //获取SD卡信息

  8. CardCap=(uint64_t)(SDCardInfo.LogBlockNbr)*(uint64_t)(SDCardInfo.LogBlockSize); //计算SD卡容量

  9. switch(SDCardInfo.CardType)

  10. {

  11. case CARD_SDSC:

  12. {

  13. if(SDCardInfo.CardVersion == CARD_V1_X)

  14. printf("Card Type:SDSC V1\r\n");

  15. else if(SDCardInfo.CardVersion == CARD_V2_X)

  16. printf("Card Type:SDSC V2\r\n");

  17. }

  18. break;

  19. case CARD_SDHC_SDXC:printf("Card Type:SDHC\r\n");break;

  20. default:break;

  21. }

  22.     printf("Card ManufacturerID: %d \r\n",SDCard_CID.ManufacturerID); //制造商ID

  23.   printf("CardVersion:         %d \r\n",(uint32_t)(SDCardInfo.CardVersion)); //卡版本号

  24. printf("Class:               %d \r\n",(uint32_t)(SDCardInfo.Class));     //SD卡类别

  25.   printf("Card RCA(RelCardAdd):%d \r\n",SDCardInfo.RelCardAdd); //卡相对地址

  26. printf("Card BlockNbr:       %d \r\n",SDCardInfo.BlockNbr); //块数量

  27.   printf("Card BlockSize:      %d \r\n",SDCardInfo.BlockSize); //块大小

  28. printf("LogBlockNbr:         %d \r\n",(uint32_t)(SDCardInfo.LogBlockNbr)); //逻辑块数量

  29. printf("LogBlockSize:        %d \r\n",(uint32_t)(SDCardInfo.LogBlockSize)); //逻辑块大小

  30. printf("Card Capacity:       %d MB\r\n",(uint32_t)(CardCap>>20)); //卡容量

  31. }

5.添加初始化及读写测试代码,注意此处我们没有直接使用FATFS的读写接口,我们先测试生成的SD驱动函数接口

  1. int main(void)

  2. {

  3.   /* USER CODE BEGIN 1 */

  4.   BYTE send_buf[512];

  5.   DRESULT ret;

  6.   /* USER CODE END 1 */

  7.   /* ...省略若干自动生成代码... */

  8.   

  9.   /* USER CODE BEGIN 2 */

  10.   SD_Driver.disk_initialize(0);

  11.   printf_sdcard_info();

  12.   printf("\r\n\r\n********** 英文读写测试 **********\r\n");

  13.   ret = SD_Driver.disk_write(0,

  14. (BYTE *)"Life is too short to spend time with people who suck the happiness out of you. \

  15. If someone wants you in their life, they’ll make room for you. You shouldn’t have to fight for a spot. Never, ever\

  16.  insist yourself to someone who continuously overlooks your worth. And remember, it’s not the people that stand by \

  17. your side when you’re at your best, but the ones who stand beside you when you’re at your worst that are your true\

  18.  friends",20,2);

  19.   printf("sd write result:%d\r\n", ret);

  20.   ret = SD_Driver.disk_read(0, send_buf, 20, 2);

  21.   printf("sd reak result:%d\r\n", ret);

  22.   printf("sd read content:\r\n%s\r\n", send_buf);

  23.   

  24.   printf("\r\n\r\n********** 中文读写测试 **********\r\n");

  25.   ret = SD_Driver.disk_write(0,

  26. (BYTE *)"开发者社区的明天需要大家一同开源共创,期待下一次你的分享,让我们一同携手共进,推动人类科技的发展!!!\r\n\

  27. 创作不易,转载请注明出处~\r\n\

  28. 更多文章敬请关注:爱出名的狗腿子\r\n", 22, 2);

  29.   printf("sd write result:%d\r\n", ret);

  30.   ret = SD_Driver.disk_read(0, send_buf, 22, 2);

  31.   printf("sd reak result:%d\r\n", ret);

  32.   printf("sd read content:\r\n%s\r\n", send_buf);

  33.   /* USER CODE END 2 */

  34.   while (1)

  35.   {

  36.     /* USER CODE END WHILE */

  37.     /* USER CODE BEGIN 3 */

  38.   }

  39.   /* USER CODE END 3 */

  40. }

6.修改烧录器配置,配置为烧录后自动运行

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

7.下载测试,这里由于我们采用UTF-8编码,所以使用的串口上位机也需要支持UTF-8解析,我们这里使用Mobaxterm上位机,测试结果如下:

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

8.main.c 文件全部代码如下,供大家参考:

  1. /* USER CODE BEGIN Header */

  2. /**

  3.   ******************************************************************************

  4.   * @file           : main.c

  5.   * @brief          : Main program body

  6.   ******************************************************************************

  7.   * @attention

  8.   *

  9.   * Copyright (c) 2023 STMicroelectronics.

  10.   * All rights reserved.

  11.   *

  12.   * This software is licensed under terms that can be found in the LICENSE file

  13.   * in the root directory of this software component.

  14.   * If no LICENSE file comes with this software, it is provided AS-IS.

  15.   *

  16.   ******************************************************************************

  17.   */

  18. /* USER CODE END Header */

  19. /* Includes ------------------------------------------------------------------*/

  20. #include "main.h"

  21. #include "fatfs.h"

  22. #include "sdio.h"

  23. #include "usart.h"

  24. #include "gpio.h"

  25. /* Private includes ----------------------------------------------------------*/

  26. /* USER CODE BEGIN Includes */

  27. #include <stdio.h>

  28. /* USER CODE END Includes */

  29. /* Private typedef -----------------------------------------------------------*/

  30. /* USER CODE BEGIN PTD */

  31. /* USER CODE END PTD */

  32. /* Private define ------------------------------------------------------------*/

  33. /* USER CODE BEGIN PD */

  34. /* USER CODE END PD */

  35. /* Private macro -------------------------------------------------------------*/

  36. /* USER CODE BEGIN PM */

  37. /* USER CODE END PM */

  38. /* Private variables ---------------------------------------------------------*/

  39. /* USER CODE BEGIN PV */

  40. /* USER CODE END PV */

  41. /* Private function prototypes -----------------------------------------------*/

  42. void SystemClock_Config(void);

  43. /* USER CODE BEGIN PFP */

  44. /* USER CODE END PFP */

  45. /* Private user code ---------------------------------------------------------*/

  46. /* USER CODE BEGIN 0 */

  47. HAL_SD_CardInfoTypeDef  SDCardInfo;    

  48. void printf_sdcard_info(void)

  49. {

  50. uint64_t CardCap;                       //SD卡容量

  51. HAL_SD_CardCIDTypeDef SDCard_CID; 

  52. HAL_SD_GetCardCID(&hsd,&SDCard_CID); //获取CID

  53. HAL_SD_GetCardInfo(&hsd,&SDCardInfo);   //获取SD卡信息

  54. CardCap=(uint64_t)(SDCardInfo.LogBlockNbr)*(uint64_t)(SDCardInfo.LogBlockSize); //计算SD卡容量

  55. switch(SDCardInfo.CardType)

  56. {

  57. case CARD_SDSC:

  58. {

  59. if(SDCardInfo.CardVersion == CARD_V1_X)

  60. printf("Card Type:SDSC V1\r\n");

  61. else if(SDCardInfo.CardVersion == CARD_V2_X)

  62. printf("Card Type:SDSC V2\r\n");

  63. }

  64. break;

  65. case CARD_SDHC_SDXC:printf("Card Type:SDHC\r\n");break;

  66. default:break;

  67. }

  68.     printf("Card ManufacturerID: %d \r\n",SDCard_CID.ManufacturerID); //制造商ID

  69.   printf("CardVersion:         %d \r\n",(uint32_t)(SDCardInfo.CardVersion)); //卡版本号

  70. printf("Class:               %d \r\n",(uint32_t)(SDCardInfo.Class));     //SD卡类别

  71.   printf("Card RCA(RelCardAdd):%d \r\n",SDCardInfo.RelCardAdd); //卡相对地址

  72. printf("Card BlockNbr:       %d \r\n",SDCardInfo.BlockNbr); //块数量

  73.   printf("Card BlockSize:      %d \r\n",SDCardInfo.BlockSize); //块大小

  74. printf("LogBlockNbr:         %d \r\n",(uint32_t)(SDCardInfo.LogBlockNbr)); //逻辑块数量

  75. printf("LogBlockSize:        %d \r\n",(uint32_t)(SDCardInfo.LogBlockSize)); //逻辑块大小

  76. printf("Card Capacity:       %d MB\r\n",(uint32_t)(CardCap>>20)); //卡容量

  77. }

  78. int fputc(int ch, FILE *f)

  79. {

  80. HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1,0xffff);

  81. return (ch);

  82. }

  83. /* USER CODE END 0 */

  84. /**

  85.   * @brief  The application entry point.

  86.   * @retval int

  87.   */

  88. int main(void)

  89. {

  90.   /* USER CODE BEGIN 1 */

  91.   BYTE send_buf[512];

  92.   DRESULT ret;

  93.   /* USER CODE END 1 */

  94.   /* MCU Configuration--------------------------------------------------------*/

  95.   /* Reset of all peripherals, Initializes the Flash interface and the Systick. */

  96.   HAL_Init();

  97.   /* USER CODE BEGIN Init */

  98.   /* USER CODE END Init */

  99.   /* Configure the system clock */

  100.   SystemClock_Config();

  101.   /* USER CODE BEGIN SysInit */

  102.   /* USER CODE END SysInit */

  103.   /* Initialize all configured peripherals */

  104.   MX_GPIO_Init();

  105.   MX_SDIO_SD_Init();

  106.   MX_USART1_UART_Init();

  107.   MX_FATFS_Init();

  108.   /* USER CODE BEGIN 2 */

  109.   

  110.   SD_Driver.disk_initialize(0);

  111.   printf_sdcard_info();

  112.   printf("\r\n\r\n********** 英文读写测试 **********\r\n");

  113.   ret = SD_Driver.disk_write(0,

  114. (BYTE *)"Life is too short to spend time with people who suck the happiness out of you. \

  115. If someone wants you in their life, they’ll make room for you. You shouldn’t have to fight for a spot. Never, ever\

  116.  insist yourself to someone who continuously overlooks your worth. And remember, it’s not the people that stand by \

  117. your side when you’re at your best, but the ones who stand beside you when you’re at your worst that are your true\

  118.  friends",20,2);

  119.   printf("sd write result:%d\r\n", ret);

  120.   ret = SD_Driver.disk_read(0, send_buf, 20, 2);

  121.   printf("sd reak result:%d\r\n", ret);

  122.   printf("sd read content:\r\n%s\r\n", send_buf);

  123.   

  124.   printf("\r\n\r\n********** 中文读写测试 **********\r\n");

  125.   ret = SD_Driver.disk_write(0,

  126. (BYTE *)"开发者社区的明天需要大家一同开源共创,期待下一次你的分享,让我们一同携手共进,推动人类科技的发展!!!\r\n\

  127. 创作不易,转载请注明出处~\r\n\

  128. 更多文章敬请关注:爱出名的狗腿子\r\n", 22, 2);

  129.   printf("sd write result:%d\r\n", ret);

  130.   ret = SD_Driver.disk_read(0, send_buf, 22, 2);

  131.   printf("sd reak result:%d\r\n", ret);

  132.   printf("sd read content:\r\n%s\r\n", send_buf);

  133.   

  134.   /* USER CODE END 2 */

  135.   /* Infinite loop */

  136.   /* USER CODE BEGIN WHILE */

  137.   while (1)

  138.   {

  139.     /* USER CODE END WHILE */

  140.     /* USER CODE BEGIN 3 */

  141.   }

  142.   /* USER CODE END 3 */

  143. }

  144. /**

  145.   * @brief System Clock Configuration

  146.   * @retval None

  147.   */

  148. void SystemClock_Config(void)

  149. {

  150.   RCC_OscInitTypeDef RCC_OscInitStruct = {0};

  151.   RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  152.   /** Initializes the RCC Oscillators according to the specified parameters

  153.   * in the RCC_OscInitTypeDef structure.

  154.   */

  155.   RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;

  156.   RCC_OscInitStruct.HSEState = RCC_HSE_ON;

  157.   RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;

  158.   RCC_OscInitStruct.HSIState = RCC_HSI_ON;

  159.   RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;

  160.   RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;

  161.   RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;

  162.   if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)

  163.   {

  164.     Error_Handler();

  165.   }

  166.   /** Initializes the CPU, AHB and APB buses clocks

  167.   */

  168.   RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK

  169.                               |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;

  170.   RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;

  171.   RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;

  172.   RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;

  173.   RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  174.   if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)

  175.   {

  176.     Error_Handler();

  177.   }

  178. }

  179. /* USER CODE BEGIN 4 */

  180. /* USER CODE END 4 */

  181. /**

  182.   * @brief  This function is executed in case of error occurrence.

  183.   * @retval None

  184.   */

  185. void Error_Handler(void)

  186. {

  187.   /* USER CODE BEGIN Error_Handler_Debug */

  188.   /* User can add his own implementation to report the HAL error return state */

  189.   __disable_irq();

  190.   while (1)

  191.   {

  192.   }

  193.   /* USER CODE END Error_Handler_Debug */

  194. }

  195. #ifdef  USE_FULL_ASSERT

  196. /**

  197.   * @brief  Reports the name of the source file and the source line number

  198.   *         where the assert_param error has occurred.

  199.   * @param  file: pointer to the source file name

  200.   * @param  line: assert_param error line source number

  201.   * @retval None

  202.   */

  203. void assert_failed(uint8_t *file, uint32_t line)

  204. {

  205.   /* USER CODE BEGIN 6 */

  206.   /* User can add his own implementation to report the file name and line number,

  207.      ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

  208.   /* USER CODE END 6 */

  209. }

  210. #endif /* USE_FULL_ASSERT */

3.3 FATFS文件操作

移植了FATFS,当然也就可以只用通用的文件系统操作函数完成文件的读写,通用的文件系统操作API 在 ff.c 文件内,声明在 ff.h 文件内,主要使用的API接口如下:

  1. FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */

  2. FRESULT f_close (FIL* fp); /* Close an open file object */

  3. FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from a file */

  4. FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to a file */

  5. FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */

  6. FRESULT f_lseek (FIL* fp, DWORD ofs); /* Move file pointer of a file object */

  7. FRESULT f_truncate (FIL* fp); /* Truncate file */

  8. FRESULT f_sync (FIL* fp); /* Flush cached data of a writing file */

  9. FRESULT f_opendir (DIR* dp, const TCHAR* path); /* Open a directory */

  10. FRESULT f_closedir (DIR* dp); /* Close an open directory */

  11. FRESULT f_readdir (DIR* dp, FILINFO* fno); /* Read a directory item */

  12. FRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */

  13. FRESULT f_findnext (DIR* dp, FILINFO* fno); /* Find next file */

  14. FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */

  15. FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */

  16. FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */

  17. FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */

  18. FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of the file/dir */

  19. FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change times-tamp of the file/dir */

  20. FRESULT f_chdir (const TCHAR* path); /* Change current directory */

  21. FRESULT f_chdrive (const TCHAR* path); /* Change current drive */

  22. FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */

  23. FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */

  24. FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */

  25. FRESULT f_setlabel (const TCHAR* label); /* Set volume label */

  26. FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */

  27. FRESULT f_mkfs (const TCHAR* path, BYTE sfd, UINT au); /* Create a file system on the volume */

  28. FRESULT f_fdisk (BYTE pdrv, const DWORD szt[], void* work); /* Divide a physical drive into some partitions */

  29. int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */

  30. int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */

  31. int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */

  32. TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */

关于API的使用此处不做过多赘述,大家可以自行上官网查阅 FATFS官网,或者网上搜索,或直接看下述示例亦可。

3.3.1 修改读写测试代码

修改3.2.1章节所使用的读写测试代码,此处我们直接使用FATFS文件系统的读写函数接口,修改主函数如下,注意需要包含fatfs.h头文件!

  1. #include "fatfs.h"

  2. int main()

  3. {

  4.   /* USER CODE BEGIN 1 */

  5. #define USERPath "0:/"

  6.   

  7.   BYTE write_buf[] = "\r\n\r\n\

  8. hello world!\r\n\

  9. 开发者社区的明天需要大家一同开源共创,期待下一次你的分享,让我们一同携手共进,推动人类科技的发展!!!\r\n\

  10. 创作不易,转载请注明出处~\r\n\

  11. 更多文章敬请关注:爱出名的狗腿子\r\n\r\n\

  12. ";

  13.   BYTE read_buf[1024] = {0};

  14.   UINT num;

  15.   FRESULT ret;

  16.   /* USER CODE END 1 */

  17.   

  18.   /* ... 省略初始化代码... */

  19.   

  20.   /* USER CODE BEGIN 2 */

  21.   

  22.   /* 挂载文件系统,挂载的时候会完成对应硬件设备(SD卡/SDnand)初始化 */

  23.   ret = f_mount(&SDFatFS, USERPath, 1);

  24.   if (ret != FR_OK) {

  25.     printf("f_mount error!\r\n");

  26.     goto mount_error;

  27.   } else if(ret == FR_NO_FILESYSTEM) {         /* 检测是否存在文件系统,如果没有则进行格式化 */

  28.     printf("未检测到FATFS文件系统,执行格式化...\r\n");

  29.     ret = f_mkfs(USERPath, 0, 0);

  30.     if(ret == FR_OK) {

  31.       printf("格式化成功!\r\n");

  32.       f_mount(NULL, USERPath, 1); /* 先取消挂载,后重新挂载 */

  33.       ret = f_mount(&SDFatFS, USERPath, 1);

  34.     } else {

  35.       printf("格式化失败!\r\n");

  36.       goto mount_error;

  37.     }

  38.   } else {

  39.     printf("f_mount success!\r\n");

  40.   }

  41.   

  42.   /* 读写测试 */

  43.   printf("\r\n ========== write test ==========\r\n");

  44.   ret = f_open(&SDFile, "hello.txt", FA_CREATE_ALWAYS | FA_WRITE);

  45.   if(ret == FR_OK) {

  46.     printf("open file sucess!\r\n");

  47.     ret = f_write(&SDFile, write_buf, sizeof(write_buf), &num);

  48.     if(ret == FR_OK) {

  49.       printf("write \"%s\" success!\r\nwrite len:%d\r\n", write_buf, num);

  50.     } else {

  51.       printf("write error! ret:%d \r\n", ret);

  52.       goto rw_error;

  53.     }

  54.     f_close(&SDFile);

  55.   } else {

  56.     printf("open file error!\r\n");

  57.     goto rw_error;

  58.   }

  59.   printf("\r\n ========== read test ==========\r\n");

  60.   ret = f_open(&SDFile, "hello.txt",FA_OPEN_EXISTING | FA_READ);   

  61.   if(ret == FR_OK) {

  62.     printf("open file sucess!\r\n");

  63. ret = f_read(&SDFile, read_buf, sizeof(read_buf), &num); 

  64.     if(ret == FR_OK) {

  65.       printf("read data:\"%s\"!\r\nread len:%d\r\n", read_buf, num);

  66.     } else {

  67.       printf("read error! ret:%d \r\n", ret);

  68.       goto rw_error;

  69.   }

  70. } else {

  71.    printf("open file error!\r\n");

  72.    goto rw_error;

  73. }

  74. rw_error:

  75.   f_close(&SDFile);

  76.   

  77. mount_error:

  78.   f_mount(NULL, USERPath, 1);

  79.   /* USER CODE END 2 */

  80.   while (1) {

  81.   

  82.   }

  83. }

#define USERPath "0:/" 表示挂载的位置,这是由于FATFS初始化的时候链接的根目录为 0:/ ,所以挂载的文件系统需要在此目录下,当然也可以是此目录下的路径,如0:/hello,但不能是其他目录,如 1:/

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

测试结果如下:

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

main.c完整内容如下:

  1. /* USER CODE BEGIN Header */

  2. /**

  3.   ******************************************************************************

  4.   * @file           : main.c

  5.   * @brief          : Main program body

  6.   ******************************************************************************

  7.   * @attention

  8.   *

  9.   * Copyright (c) 2023 STMicroelectronics.

  10.   * All rights reserved.

  11.   *

  12.   * This software is licensed under terms that can be found in the LICENSE file

  13.   * in the root directory of this software component.

  14.   * If no LICENSE file comes with this software, it is provided AS-IS.

  15.   *

  16.   ******************************************************************************

  17.   */

  18. /* USER CODE END Header */

  19. /* Includes ------------------------------------------------------------------*/

  20. #include "main.h"

  21. #include "fatfs.h"

  22. #include "sdio.h"

  23. #include "usart.h"

  24. #include "gpio.h"

  25. /* Private includes ----------------------------------------------------------*/

  26. /* USER CODE BEGIN Includes */

  27. #include <stdio.h>

  28. #include "fatfs.h"

  29. /* USER CODE END Includes */

  30. /* Private typedef -----------------------------------------------------------*/

  31. /* USER CODE BEGIN PTD */

  32. /* USER CODE END PTD */

  33. /* Private define ------------------------------------------------------------*/

  34. /* USER CODE BEGIN PD */

  35. /* USER CODE END PD */

  36. /* Private macro -------------------------------------------------------------*/

  37. /* USER CODE BEGIN PM */

  38. /* USER CODE END PM */

  39. /* Private variables ---------------------------------------------------------*/

  40. /* USER CODE BEGIN PV */

  41. /* USER CODE END PV */

  42. /* Private function prototypes -----------------------------------------------*/

  43. void SystemClock_Config(void);

  44. /* USER CODE BEGIN PFP */

  45. /* USER CODE END PFP */

  46. /* Private user code ---------------------------------------------------------*/

  47. /* USER CODE BEGIN 0 */

  48. HAL_SD_CardInfoTypeDef  SDCardInfo;    

  49. void printf_sdcard_info(void)

  50. {

  51. uint64_t CardCap;                       //SD卡容釿

  52. HAL_SD_CardCIDTypeDef SDCard_CID; 

  53. HAL_SD_GetCardCID(&hsd,&SDCard_CID); //获取CID

  54. HAL_SD_GetCardInfo(&hsd,&SDCardInfo);   //获取SD卡信恿

  55. CardCap=(uint64_t)(SDCardInfo.LogBlockNbr)*(uint64_t)(SDCardInfo.LogBlockSize); //计算SD卡容釿

  56. switch(SDCardInfo.CardType)

  57. {

  58. case CARD_SDSC:

  59. {

  60. if(SDCardInfo.CardVersion == CARD_V1_X)

  61. printf("Card Type:SDSC V1\r\n");

  62. else if(SDCardInfo.CardVersion == CARD_V2_X)

  63. printf("Card Type:SDSC V2\r\n");

  64. }

  65. break;

  66. case CARD_SDHC_SDXC:printf("Card Type:SDHC\r\n");break;

  67. default:break;

  68. }

  69.     printf("Card ManufacturerID: %d \r\n",SDCard_CID.ManufacturerID); //制鿠商ID

  70.   printf("CardVersion:         %d \r\n",(uint32_t)(SDCardInfo.CardVersion)); //卡版本号

  71. printf("Class:               %d \r\n",(uint32_t)(SDCardInfo.Class));     //SD卡类刿

  72.   printf("Card RCA(RelCardAdd):%d \r\n",SDCardInfo.RelCardAdd); //卡相对地坿

  73. printf("Card BlockNbr:       %d \r\n",SDCardInfo.BlockNbr); //块数釿

  74.   printf("Card BlockSize:      %d \r\n",SDCardInfo.BlockSize); //块大尿

  75. printf("LogBlockNbr:         %d \r\n",(uint32_t)(SDCardInfo.LogBlockNbr)); //逻辑块数釿

  76. printf("LogBlockSize:        %d \r\n",(uint32_t)(SDCardInfo.LogBlockSize)); //逻辑块大尿

  77. printf("Card Capacity:       %d MB\r\n",(uint32_t)(CardCap>>20)); //卡容釿

  78. }

  79. int fputc(int ch, FILE *f)

  80. {

  81. HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1,0xffff);

  82. return (ch);

  83. }

  84. /* USER CODE END 0 */

  85. /**

  86.   * @brief  The application entry point.

  87.   * @retval int

  88.   */

  89. int main(void)

  90. {

  91.   /* USER CODE BEGIN 1 */

  92. #define USERPath "0:/"

  93.   

  94.   BYTE write_buf[] = "\r\n\r\n\

  95. hello world!\r\n\

  96. 开发者社区的明天需要大家一同开源共创,期待下一次你的分享,让我们一同携手共进,推动人类科技的发展!!!\r\n\

  97. 创作不易,转载请注明出处~\r\n\

  98. 更多文章敬请关注:爱出名的狗腿子\r\n\r\n\

  99. ";

  100.   BYTE read_buf[1024] = {0};

  101.   UINT num;

  102.   FRESULT ret;

  103.   /* USER CODE END 1 */

  104.   /* MCU Configuration--------------------------------------------------------*/

  105.   /* Reset of all peripherals, Initializes the Flash interface and the Systick. */

  106.   HAL_Init();

  107.   /* USER CODE BEGIN Init */

  108.   /* USER CODE END Init */

  109.   /* Configure the system clock */

  110.   SystemClock_Config();

  111.   /* USER CODE BEGIN SysInit */

  112.   /* USER CODE END SysInit */

  113.   /* Initialize all configured peripherals */

  114.   MX_GPIO_Init();

  115.   MX_SDIO_SD_Init();

  116.   MX_USART1_UART_Init();

  117.   MX_FATFS_Init();

  118.   /* USER CODE BEGIN 2 */

  119.   

  120.   /* 挂载文件系统,挂载的时候会完成对应硬件设备(SD卡/SDnand)初始化 */

  121.   ret = f_mount(&SDFatFS, USERPath, 1);

  122.   if (ret != FR_OK) {

  123.     printf("f_mount error!\r\n");

  124.     goto mount_error;

  125.   } else if(ret == FR_NO_FILESYSTEM) {         /* 检测是否存在文件系统,如果没有则进行格式化 */

  126.     printf("未检测到FATFS文件系统,执行格式化...\r\n");

  127.     ret = f_mkfs(USERPath, 0, 0);

  128.     if(ret == FR_OK) {

  129.       printf("格式化成功!\r\n");

  130.       f_mount(NULL, USERPath, 1); /* 先取消挂载,后重新挂载 */

  131.       ret = f_mount(&SDFatFS, USERPath, 1);

  132.     } else {

  133.       printf("格式化失败!\r\n");

  134.       goto mount_error;

  135.     }

  136.   } else {

  137.     printf("f_mount success!\r\n");

  138.   }

  139.   

  140.   /* 读写测试 */

  141.   printf("\r\n ========== write test ==========\r\n");

  142.   ret = f_open(&SDFile, "hello.txt", FA_CREATE_ALWAYS | FA_WRITE);

  143.   if(ret == FR_OK) {

  144.     printf("open file sucess!\r\n");

  145.     ret = f_write(&SDFile, write_buf, sizeof(write_buf), &num);

  146.     if(ret == FR_OK) {

  147.       printf("write \"%s\" success!\r\nwrite len:%d\r\n", write_buf, num);

  148.     } else {

  149.       printf("write error! ret:%d \r\n", ret);

  150.       goto rw_error;

  151.     }

  152.     f_close(&SDFile);

  153.   } else {

  154.     printf("open file error!\r\n");

  155.     goto rw_error;

  156.   }

  157.   printf("\r\n ========== read test ==========\r\n");

  158. ret = f_open(&SDFile, "hello.txt",FA_OPEN_EXISTING | FA_READ);   

  159. if(ret == FR_OK) {

  160.     printf("open file sucess!\r\n");

  161. ret = f_read(&SDFile, read_buf, sizeof(read_buf), &num); 

  162.     if(ret == FR_OK) {

  163.       printf("read data:\"%s\"!\r\nread len:%d\r\n", read_buf, num);

  164.     } else {

  165.       printf("read error! ret:%d \r\n", ret);

  166.       goto rw_error;

  167.     }

  168. } else {

  169.     printf("open file error!\r\n");

  170.     goto rw_error;

  171. }

  172. rw_error:

  173. f_close(&SDFile);

  174.   

  175. mount_error:

  176. f_mount(NULL, USERPath, 1);

  177.   /* USER CODE END 2 */

  178.   /* Infinite loop */

  179.   /* USER CODE BEGIN WHILE */

  180.   while (1)

  181.   {

  182.     /* USER CODE END WHILE */

  183.     /* USER CODE BEGIN 3 */

  184.   }

  185.   /* USER CODE END 3 */

  186. }

  187. /**

  188.   * @brief System Clock Configuration

  189.   * @retval None

  190.   */

  191. void SystemClock_Config(void)

  192. {

  193.   RCC_OscInitTypeDef RCC_OscInitStruct = {0};

  194.   RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  195.   /** Initializes the RCC Oscillators according to the specified parameters

  196.   * in the RCC_OscInitTypeDef structure.

  197.   */

  198.   RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;

  199.   RCC_OscInitStruct.HSEState = RCC_HSE_ON;

  200.   RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;

  201.   RCC_OscInitStruct.HSIState = RCC_HSI_ON;

  202.   RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;

  203.   RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;

  204.   RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;

  205.   if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)

  206.   {

  207.     Error_Handler();

  208.   }

  209.   /** Initializes the CPU, AHB and APB buses clocks

  210.   */

  211.   RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK

  212.                               |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;

  213.   RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;

  214.   RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;

  215.   RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;

  216.   RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  217.   if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)

  218.   {

  219.     Error_Handler();

  220.   }

  221. }

  222. /* USER CODE BEGIN 4 */

  223. /* USER CODE END 4 */

  224. /**

  225.   * @brief  This function is executed in case of error occurrence.

  226.   * @retval None

  227.   */

  228. void Error_Handler(void)

  229. {

  230.   /* USER CODE BEGIN Error_Handler_Debug */

  231.   /* User can add his own implementation to report the HAL error return state */

  232.   __disable_irq();

  233.   while (1)

  234.   {

  235.   }

  236.   /* USER CODE END Error_Handler_Debug */

  237. }

  238. #ifdef  USE_FULL_ASSERT

  239. /**

  240.   * @brief  Reports the name of the source file and the source line number

  241.   *         where the assert_param error has occurred.

  242.   * @param  file: pointer to the source file name

  243.   * @param  line: assert_param error line source number

  244.   * @retval None

  245.   */

  246. void assert_failed(uint8_t *file, uint32_t line)

  247. {

  248.   /* USER CODE BEGIN 6 */

  249.   /* User can add his own implementation to report the file name and line number,

  250.      ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

  251.   /* USER CODE END 6 */

  252. }

  253. #endif /* USE_FULL_ASSERT */


 

3.4 配置问题记录

3.4.1 CubeMx生成代码bug

测试发现,使用CubeMx当前最新版本:V6.8.0版本,生成代码会存在以下问题:

  • SD卡/SDnand 卡片信息读取成功,但是读写测试失败

经过仔细分析代码后发现,出现的问题在 MX_SDIO_SD_Init() 此初始化函数内的配置项错误导致,具体分析如下:

  1. 我们在CubeMx里面配置的时候选择的是4线宽度模式 SD 4bit Wide bus

  2. v6.8.0版本CubeMx生成的 MX_SDIO_SD_Init() SD初始化函数内,hsd.Init.BusWide = SDIO_BUS_WIDE_4B;

  3. 看上去没有什么问题,配置4线模式,对应的初始化项也使用4线模式,但是不然,我们继续分析 MX_SDIO_SD_Init() 此初始配置的调用

  4. MX_SDIO_SD_Init() 此函数在main函数内初始化的时候调用,此函数只配置了 hsd 结构体,并未配置给SDIO硬件寄存器

  5. 之后调用 SD_Driver.disk_initialize(0); 函数的时候才真正开始进行SDIO外设配置

BSP_SD_Init()

  ->HAL_SD_Init()

    ->HAL_SD_InitCard()

在 HAL_SD_InitCard() 函数内使用Init结构体配置SDIO外设,总线宽度1bit,时钟速度<400k,以进行卡片的初始化识别。

      -> SD_InitCard()

        -> SDIO_Init(hsd->Instance, hsd->Init)

      -> SDMMC_CmdBlockLength(hsd->Instance, BLOCKSIZE)

· 在 SD_InitCard() 函数内实现SD卡的初始化识别,之后调用 SDIO_Init() 将 MX_SDIO_SD_Init() 内对 hsd 的配置配置给SDIO外设,此处的作用主要是提升SDIO外设时钟速率为我们配置的速率;

· v6.8.0版本的代码此时hsd.Init.BusWide = SDIO_BUS_WIDE_4B; ,因此v6.8.0版本代码后续SDIO外设使用4线通讯;

· 之后调用 SDMMC_CmdBlockLength() 设置块大小,由于SDIO外设已切换到4线模式,而SD卡/SDnand此时仍然处于1线模式,因此配置会出错

   -> HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B)

根据前面获取到的SD卡SCR寄存器值,判断是否支持4线模式,如果支持则发送配置命令通知SD卡/SDnand进入4线模式,之后修改SDIO外设总线宽度为4线模式

      6.通过以上分析可知,MX_SDIO_SD_Init() 函数内对 hsd.Init.BusWide = SDIO_BUS_WIDE_4B; 的配置会导致对SD卡块大小的配置失败,从而导致后续读写时失败,报错为块大小设置失败!

      7.综上,针对当前最新版本 V6.8.0 版本CubeMx的处理方法是:手动修改此 hsd.Init.BusWide 配置为 SDIO_BUS_WIDE_1B 或更换低版本CubeMx,本人更换V6.6.1版本后无此bug。

3.4.2 SD插入检测引脚配置

使用CubeMx配置FATFS 选择 SD Card 之后,有一个配置参数,用来配置SD Card的输入检测引脚。如果我们在硬件上有设计SD卡的卡槽插入检测引脚插入连接到了MCU的IO,则可配置对应IO为输入模式,并设置对应IO为输入检测引脚,比如,我们设置PD12为输入检测引脚,则配置如下:

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

对应代码如下,输入检测 IO 低电平有效!

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

如果硬件上,没有此插入检测引脚,则可以在CubeMx内不进行配置,只是在生成代码的时候会提示警报而已,可以不用关心,生成的代码项会自动屏蔽插入检测!

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

SD NAND,贴片式TF卡,贴片式SD卡,北京君正,nor flash,存储,芯片,主控,小尺寸emmc,大容量SLC Nand,语音芯片,语音识别,语音控制,语音模块,离线语音

4. 结束语

  • 以上便是本文的全部内容了,欢迎大家评论区留言讨论!

  • 使用CubeMx虽然能帮助我们快速生成驱动,但是对于SD卡/SD nand的驱动流程,我们还是需要有清晰的认识,推荐阅读: SD Nand 与 SD卡 SDIO模式应用流程

相关文章:

stm32 CubeMx 实现SD卡/sd nand FATFS读写测试

文章目录 stm32 CubeMx 实现SD卡/SD nand FATFS读写测试 1. 前言 2. 环境介绍 2.1 软硬件说明 2.2 外设原理图 3. 工程搭建 3.1 CubeMx 配置 3.2 SDIO时钟配置说明 3.2 读写测试 3.2.1 添加读写测试代码 3.3 FATFS文件操作 3.3.1 修改读写测试代码 3.4 配置问题记…...

【Unity】 HTFramework框架(六十)Assistant助手(在Unity中接入DeepSeek等AI语言大模型)

更新日期&#xff1a;2025年2月14日。 Github源码&#xff1a;[点我获取源码] Gitee源码&#xff1a;[点我获取源码] 索引 Assistant助手安装Ollama使用Assistant&#xff08;在编辑器中&#xff09;打开Assistant配置Assistant使用Assistant处理Assistant回复的内容使用推理大…...

web自动化笔记(二)

文章目录 一、参数化测试1.pytest命令2.实现参数化测试3.填写地址测试4.生成Allure测试报告5.关键字驱动 二、案例1.实现后台登录1.1登录1.2.处理验证码1.3.封装识别验证码函数 2.通过cookie保持登录2.1给页面添加cookie2.2获取页面的cookie2.3自动化获取cookie 三、excel进行数…...

IIS部署netcore程序后,出现500.30错误解决方案之一

netcore程序部署到IIS后一直出现错误&#xff0c;访问首页后会跳转到登录页地址&#xff0c;然后看到如下错误 HTTP Error 500.30 - ANCM In-Process Start Failure Common solutions to this issue: The application failed to start The application started but then stopp…...

spring 学习(spring-Dl补充(注入不同类型的数据))

前言 在之前的案例&#xff0c;列举的最多的是注入 对象。本篇博客则是补充说我们不仅可以注入对象 还可以注入其他的数据类型包括基本数据类型&#xff0c;引用数据类型。 注入基本数据类型 常见的基本数据类型有&#xff1a;short char int long float double boolean …...

Docker Desktop之Nginx

安装Nginx 把这个复制 到docker 中执行 即可...

利用ffplay播放udp组播视频流

ffplay -fs -fflags nobuffer -flags low_delay -analyzeduration 0 -probesize 32 -framedrop -sync ext -strict experimental udp://224.1.1.1:5001 -fs : 全屏显示 -fflags nobuffer &#xff1a; 禁用输入缓冲&#xff08;减少100-200ms缓冲延迟&#xff09; -an…...

【教程】MySQL数据库学习笔记(七)——多表操作(持续更新)

写在前面&#xff1a; 如果文章对你有帮助&#xff0c;记得点赞关注加收藏一波&#xff0c;利于以后需要的时候复习&#xff0c;多谢支持&#xff01; 【MySQL数据库学习】系列文章 第一章 《认识与环境搭建》 第二章 《数据类型》 第三章 《数据定义语言DDL》 第四章 《数据操…...

2025.2.14——1400

2025.2.14——1400 A 1400 B 1400 C 1400 D 1400 E 1400 F 1400 G 1400 H 1400 ------------------------------------------------ 思维排序/双指针/二分/队列匹配思维二分/位运算思维数学思维 A 一眼想到的是维护信息计数。维护两个信息同时用长的一半去找短的一半…...

DeepSeek教unity------MessagePack-04

Union 联合 MessagePack for C# 支持序列化接口类型和抽象类类型的对象。它的行为类似于 XmlInclude 或 ProtoInclude。在 MessagePack for C# 中&#xff0c;这些被称为Union。只有接口和抽象类可以被 Union 属性注解。需要唯一的联合键。 /******************************…...

Java异常体系深度解析:从Exception到Error

文章目录 前言一、Java异常体系概览ExceptionError 二、受检异常与非受检异常受检异常&#xff08;Checked Exception&#xff09;非受检异常&#xff08;Unchecked Exception&#xff09; 三、常见的Error类型四、异常处理机制try-catch-finally结构Throws关键字 五、自定义异…...

【linux】文件与目录命令 - ln

文章目录 1. 基本用法2. 常用参数3. 用法举例4. 注意事项 ln 命令用于在文件系统中创建硬链接或符号链接&#xff08;软链接&#xff09;&#xff0c;是文件共享和路径引用的常用工具。 1. 基本用法 语法&#xff1a; ln [选项] 源文件 [目标文件/目标目录]功能&#xff1a; 创…...

Xilinx kintex-7系列 FPGA支持PCIe 3.0 吗?

Xilinx kintex-7系列资源如下图 Xilinx各系列的GT资源类型和性能 PCIe Gen1/2/3的传输速率对比 K7上面使用的高速收发器GTX最高速率为12.5GT/s&#xff0c; PCIe Gen2 每个通道的传输速率为 5 GT/s。 PCIe Gen3 每个通道的传输速率为 8 GT/s。 所以理论上硬件支持PCIe3.0&#…...

无人机遥感技术在农业中的具体应用:株数和株高、冠层覆盖度、作物倒伏检测、叶面积指数、病虫害监测、产量估算、空间数据综合制图

近年来&#xff0c;随着无人机技术的飞速发展&#xff0c;其在智慧农业领域的应用越来越广泛。无人机遥感作为一种高效的空间大数据获取手段&#xff0c;能够为农业生产提供多时相、多维度、大面积的农情信息&#xff0c;为实现精准农业和智慧农业提供了有力支持。今天&#xf…...

前端框架React知识回顾

首先&#xff0c;得确定用户的需求&#xff0c;可能是一个准备面试的前端开发者&#xff0c;想要系统复习React相关知识点。接下来要考虑React的核心概念&#xff0c;比如组件、生命周期、Hooks这些肯定是必须的。然后&#xff0c;面试中常问的问题&#xff0c;比如虚拟DOM、状…...

坑多多之ac8257 i2c1 rtc-pcf8563

pcf85163 ordering information Ordering information Package Description Version Marking code PCF85163T/1 SO8 ① SOT96-1 PF85163 PCF85163TS/1 TSSOP8 ② SOT505-1 85163 ①plastic small outline package; 8 leads;body width 3.9 mm ②plastic thin…...

webpack构建流程

文章目录 [TOC](文章目录) 运行流程初始化流程编译构建流程compile编译make 编译模块build module 完成模块编译 输出流程seal输出资源emit输出完成 小结 运行流程 是一个串行的过程&#xff0c;它的工作流程就是将各个插件串联起来 在运行过程中会广播事件&#xff0c;插件只…...

React - 组件之props属性

在 React 中&#xff0c;props&#xff08;即属性&#xff09;是组件之间传递数据的一种方式。它是 React 组件的基础&#xff0c;用于将数据从父组件传递到子组件。 一、类组件中 1. props 的作用 数据传递: props 允许父组件向子组件传递数据。子组件可以使用这些数据来渲…...

PMTUD By UDP

通过UDP探测MTU&#xff0c;并实现udp echo server // Description: UDP echo server. // g udp_echo_server.cc -o udp_echo_server #include <iostream> #include <cstring> #include <arpa/inet.h> #include <unistd.h>#define PORT …...

Hutool - BloomFilter:便捷的布隆过滤器实现

1. 布隆过滤器简介 布隆过滤器&#xff08;Bloom Filter&#xff09;是一种空间效率极高的概率型数据结构&#xff0c;用于判断一个元素是否存在于一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法&#xff0c;但缺点是有一定的误判率&#xff0c;即判断元素存在…...

华为云AI开发平台ModelArts

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

【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表

1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...

push [特殊字符] present

push &#x1f19a; present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中&#xff0c;push 和 present 是两种不同的视图控制器切换方式&#xff0c;它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...

逻辑回归暴力训练预测金融欺诈

简述 「使用逻辑回归暴力预测金融欺诈&#xff0c;并不断增加特征维度持续测试」的做法&#xff0c;体现了一种逐步建模与迭代验证的实验思路&#xff0c;在金融欺诈检测中非常有价值&#xff0c;本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…...

TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?

在工业自动化持续演进的今天&#xff0c;通信网络的角色正变得愈发关键。 2025年6月6日&#xff0c;为期三天的华南国际工业博览会在深圳国际会展中心&#xff08;宝安&#xff09;圆满落幕。作为国内工业通信领域的技术型企业&#xff0c;光路科技&#xff08;Fiberroad&…...

MFE(微前端) Module Federation:Webpack.config.js文件中每个属性的含义解释

以Module Federation 插件详为例&#xff0c;Webpack.config.js它可能的配置和含义如下&#xff1a; 前言 Module Federation 的Webpack.config.js核心配置包括&#xff1a; name filename&#xff08;定义应用标识&#xff09; remotes&#xff08;引用远程模块&#xff0…...

Modbus RTU与Modbus TCP详解指南

目录 1. Modbus协议基础 1.1 什么是Modbus? 1.2 Modbus协议历史 1.3 Modbus协议族 1.4 Modbus通信模型 🎭 主从架构 🔄 请求响应模式 2. Modbus RTU详解 2.1 RTU是什么? 2.2 RTU物理层 🔌 连接方式 ⚡ 通信参数 2.3 RTU数据帧格式 📦 帧结构详解 🔍…...

VisualXML全新升级 | 新增数据库编辑功能

VisualXML是一个功能强大的网络总线设计工具&#xff0c;专注于简化汽车电子系统中复杂的网络数据设计操作。它支持多种主流总线网络格式的数据编辑&#xff08;如DBC、LDF、ARXML、HEX等&#xff09;&#xff0c;并能够基于Excel表格的方式生成和转换多种数据库文件。由此&…...

Redis上篇--知识点总结

Redis上篇–解析 本文大部分知识整理自网上&#xff0c;在正文结束后都会附上参考地址。如果想要深入或者详细学习可以通过文末链接跳转学习。 1. 基本介绍 Redis 是一个开源的、高性能的 内存键值数据库&#xff0c;Redis 的键值对中的 key 就是字符串对象&#xff0c;而 val…...

6.计算机网络核心知识点精要手册

计算机网络核心知识点精要手册 1.协议基础篇 网络协议三要素 语法&#xff1a;数据与控制信息的结构或格式&#xff0c;如同语言中的语法规则语义&#xff1a;控制信息的具体含义和响应方式&#xff0c;规定通信双方"说什么"同步&#xff1a;事件执行的顺序与时序…...