STM32F407 - SDIO | FATFS - 将文件保存到 SD 卡

Posted

技术标签:

【中文标题】STM32F407 - SDIO | FATFS - 将文件保存到 SD 卡【英文标题】:STM32F407 - SDIO | FATFS - save file to sd card 【发布时间】:2020-09-30 05:34:01 【问题描述】:

问题:

无论卡是否存在,SD 卡都已安装。 我无法将文件写入 sd 卡。

上下文:

我阅读了很多书籍、文档和教程,但我找不到让它发挥作用的方法。 在 STM IDE 调试器中,我无法像使用 Python 或 javascript 那样得到丰富的错误。这就是我寻求帮助的原因。除了 printf ,但这只是对我的终端的个人评论。

技术规格:

Mac 操作系统 - 10.15.4

CubeMX - 5.6.1

STM32 IDE - SDIO / FATFS

STM32F407VG - 探索板

SD卡板:

https://www.amazon.fr/Module-Lecteur-Carte-Double-Arduino/dp/B07MG4LZRW/ref=pd_rhf_se_p_img_12?_encoding=UTF8&psc=1&refRID=WJGA2Y1B8XK0H658VV8C

代码

Main.c


/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under Ultimate Liberty license
  * SLA0044, the "License"; You may not use this file except in compliance with
  * the License. You may obtain a copy of the License at:
  *                             www.st.com/SLA0044
  *
  ******************************************************************************
  */
/* USER CODE END Header */
 
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "fatfs.h"
 
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
 
/* USER CODE END Includes */
 
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
 
/* USER CODE END PTD */
 
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
 
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
 
/* USER CODE END PM */
 
/* Private variables ---------------------------------------------------------*/
SD_HandleTypeDef hsd;
 
/* USER CODE BEGIN PV */
 
/* USER CODE END PV */
 
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_SDIO_SD_Init(void);
/* USER CODE BEGIN PFP */
 
/* USER CODE END PFP */
 
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
extern char SDPath[4];   /* SD logical drive path */
extern FATFS SDFatFS;    /* File system object for SD logical drive */
extern FIL SDFile;       /* File object for SD */
FIL myFile;
UINT myBytes;
/* USER CODE END 0 */
 
/**
  * @brief  The application entry point.
  * @retval int
  */
int main(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_SDIO_SD_Init();
  MX_FATFS_Init();
  /* USER CODE BEGIN 2 */
 
  if(f_mount(&SDFatFS, SDPath, 0) == FR_OK)
  
      HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_SET);
 

 
      if(f_open(&SDFile, "F7FILE2.TXT", FA_CREATE_ALWAYS | FA_WRITE) == FR_OK)
      
        HAL_GPIO_WritePin(GPIOD,GPIO_PIN_13, GPIO_PIN_SET);
 
        char myData[] = "Helllo";
 
        if(f_write(&SDFile,myData, sizeof(myData), &myBytes) == FR_OK)
        
 
            HAL_GPIO_WritePin(GPIOD,GPIO_PIN_15, GPIO_PIN_SET);
 
        
        f_close(&SDFile);
      
    
  else
  
      HAL_GPIO_WritePin(GPIOD,GPIO_PIN_14, GPIO_PIN_SET);
  
 
 
  /* 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
  */
void SystemClock_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 CPU, AHB and APB busses clocks 
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLM = 8;
  RCC_OscInitStruct.PLL.PLLN = 72;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 3;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  
    Error_Handler();
  
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV4;
 
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  
    Error_Handler();
  

 
/**
  * @brief SDIO Initialization Function
  * @param None
  * @retval None
  */
static 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_1B;
  hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
  hsd.Init.ClockDiv = 3;
  /* USER CODE BEGIN SDIO_Init 2 */
 
  /* USER CODE END SDIO_Init 2 */
 

 
/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)

  GPIO_InitTypeDef GPIO_InitStruct = 0;
 
  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOC_CLK_ENABLE();
 
  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15, GPIO_PIN_RESET);
 
  /*Configure GPIO pins : PD12 PD13 PD14 PD15 */
  GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
 

 
/* USER CODE BEGIN 4 */
 
/* USER CODE END 4 */
 
/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)

  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
 
  /* USER CODE END Error_Handler_Debug */

 
#ifdef  USE_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
  */
void assert_failed(uint8_t *file, uint32_t line)
 
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */

#endif /* USE_FULL_ASSERT */
 
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/


自从第一次发帖以来我做了什么。

问题来自于 open 函数。我有一个FR_DISK_ERR

调试时,脚本如下:

FRESULT f_open (
    FIL* fp,            /* Pointer to the blank file object */
    const TCHAR* path,  /* Pointer to the file name */
    BYTE mode           /* Access mode and file open mode flags */
)

    FRESULT res;
    DIR dj;
    FATFS *fs;
#if !_FS_READONLY
    DWORD dw, cl, bcs, clst, sc;
    FSIZE_t ofs;
#endif
    DEF_NAMBUF


    if (!fp) return FR_INVALID_OBJECT;

    /* Get logical drive */
    mode &= _FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND | FA_SEEKEND;
    res = find_volume(&path, &fs, mode); /* The script failed at this line  ! */
    if (res == FR_OK) 
        dj.obj.fs = fs;
        INIT_NAMBUF(fs);
        res = follow_path(&dj, path);   /* Follow the file path */

脚本在res = find_volume 行失败,跳转到open函数的末尾:

    if (res != FR_OK) fp->obj.fs = 0;   /* Invalidate file object on error */

    LEAVE_FF(fs, res);

关于open函数询问的变量:

FRESULT f_open (
    FIL* fp,            /* Pointer to the blank file object */
    const TCHAR* path,  /* Pointer to the file name */
    BYTE mode           /* Access mode and file open mode flags */
)

我认为变量 fp 和 mode 都可以。正如你所说,错误可能与const TCHAR* path 有关。

f_open(&SDFile, "F7FILE2.TXT", FA_CREATE_ALWAYS | FA_WRITE);

我对@9​​87654333@ 进行了调查,但在调试时我无法遵循该行为...函数可能返回的错误类型:

1 - 获取扇区大小(仅限多扇区大小cfg)

#if _MAX_SS != _MIN_SS                  /* Get sector size (multiple sector size cfg only) */
    if (disk_ioctl(fs->drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK) return FR_DISK_ERR;
    if (SS(fs) > _MAX_SS || SS(fs) < _MIN_SS || (SS(fs) & (SS(fs) - 1))) return FR_DISK_ERR;
#endif

2 - 磁盘 I/O 层发生错误

    /* Find an FAT partition on the drive. Supports only generic partitioning rules, FDISK and SFD. */
    bsect = 0;
    fmt = check_fs(fs, bsect);          /* Load sector 0 and check if it is an FAT-VBR as SFD */
    if (fmt == 2 || (fmt < 2 && LD2PT(vol) != 0))  /* Not an FAT-VBR or forced partition number */
        for (i = 0; i < 4; i++)        /* Get partition offset */
            pt = fs->win + (MBR_Table + i * SZ_PTE);
            br[i] = pt[PTE_System] ? ld_dword(pt + PTE_StLba) : 0;
        
        i = LD2PT(vol);                 /* Partition number: 0:auto, 1-4:forced */
        if (i) i--;
        do                             /* Find an FAT volume */
            bsect = br[i];
            fmt = bsect ? check_fs(fs, bsect) : 3;  /* Check the partition */
         while (LD2PT(vol) == 0 && fmt >= 2 && ++i < 4);
    
    if (fmt == 4) return FR_DISK_ERR;       /* An error occured in the disk I/O layer */

3 - 检查位图位置是否在假设中(在第一个集群)

/* Check if bitmap location is in assumption (at the first cluster) */
        if (move_window(fs, clust2sect(fs, fs->dirbase)) != FR_OK) return FR_DISK_ERR;

有一个关于 FATFS 配置的屏幕截图,但即使 FF_USE_LFN 设置为 1 Enable LFN with static working buffer on the BSS. Always NOT thread-safe. 我也得到了FR_DISK_ERR ...

你有什么想法吗?

最终答案:

从 find_volume 函数中,我发现 Exfat 没有启用为FF_USE_LFN

我有一个 FR_NO_FILE_SYSTEM。我花点时间上网看了一下,发现STM32上的SDIO没有DMA就不能工作……另外你需要设置除CLK之外的上拉电阻。

这篇文章解决了我所有的问题:

https://community.st.com/s/question/0D50X00009XkWceSAF/stm32f411re-stm32cube-fatfs-sdio-sdcard-always-returns-frdiskerr

同时保持 stm32 卡和外部 sd 卡原型板之间的接线尽可能短。

我遇到了很多问题,因为我使用了长接线。

【问题讨论】:

【参考方案1】:

我的第一个猜测(没有看到您的 FatFs 配置)将是您尚未启用长文件名 (LFS)。因此,FatFs 仅配置为与 8.3 filenames 一起使用。这些只能是 8 个字符长。因此,"F7FILE2.TXT" 没有得到正确处理。

对于enable LFS,你应该设置FF_USE_LFN = 1。 (有更多选项可用,请选择最适合您情况的选项。)

【讨论】:

我已经用 FATFS 配置更新了这个问题。谢谢

以上是关于STM32F407 - SDIO | FATFS - 将文件保存到 SD 卡的主要内容,如果未能解决你的问题,请参考以下文章

stm32f_open失败

无法使用 STM32F407 上的 Chan FatFs 库通过 SPI 写入 SD 卡文件

3STM32F407ZGT6具体参数

stm32f407H库和stm32f103H库有区别吗

stm32f407v与stm32f407zg的区别

STM32F407之搭建工程