细说STM32单片机SD卡的FatFS文件系统并使用轮询模式访问SD卡的方法及其应用
检测SD卡状态在SD卡的应用程序里很重要,为了提高程序的鲁棒性IDE自动生成的程序代码里多次出现检查SD卡的状态,因此,如果使用者忽略了对SD卡状态的检查(检查是否插入、检查是否装载),程序的容错能力就会很弱,就会发生意想不到的失误,比如,不能装载、不能写入、不能读出,偏偏有的时候还能装载或写入或读出。此处,声明了5个Disk IO函数,即SD_initialize()、SD_status()、S
目录
为了更好地阅读本文,推荐先阅读本文作者发布的相关文章,可参考的文章如下:
参考文章1:细说STM32单片机使用轮询模式直接访问SD卡的方法及其应用-CSDN博客 细说STM32单片机使用轮询模式直接访问SD卡的方法及其应用-CSDN博客
参考文章2:细说STM32单片机SPI-Flash芯片的FatFS移植及其应用实例详解_stm32 cube spi flash fats 创建-CSDN博客 细说STM32单片机SPI-Flash芯片的FatFS移植及其应用实例详解_stm32 cube spi flash fats 创建-CSDN博客
参考文章1介绍了通过SD的HAL驱动程序直接读写SD卡数据块的方法,但是因为SD卡容量比较大,在实际使用中,一般是在SD卡上创建文件系统,将数据以文件形式进行管理。本文介绍如何用FatFS管理SD卡的文件系统。
一、 SD卡文件系统概述
SD卡容量一般是16GB、32GB或更大,存储的数据格式可能也比较复杂,所以在实际使用中,一般是在SD卡上创建文件系统,用FatFS管理SD卡上的文件系统。
参考文章2介绍了在User-defined存储介质上移植FatFS的原理,并以SPI-Flash存储芯片W25Q16为例,介绍了FatFS移植的具体实现过程。FatFS的移植主要就是针对存储介质实现几个Disk IO函数。如果将SD卡看作一个User-defined存储介质,可以用同样的方法进行FatFS的移植,只需使用SD的HAL驱动程序实现几个Disk IO函数即可。
在使用CubeMX进行SD卡的FatFS文件系统管理开发时,无须自己实现FatFS底层Disk IO函数的移植,CubeMX生成的代码中,已经针对SDIO接口和SD卡做好了FatFS的移植,直接使用FatFS的应用层接口函数进行文件系统管理即可。
使用FatFS管理SD卡的文件系统时,SD卡的数据读写可以采用轮询方式或DMA方式。本示例中,SDIO以轮询方式访问SD卡,介绍FatFS管理SD卡文件系统的原理,以及FatFS针对SD卡的Disk IO访问函数的实现原理。
二、示例:阻塞式访问SD卡
1、示例功能与CubeMX项目设置
设计一个示例,使用FatFS在SD卡上创建文件系统,并测试文件读写功能。本示例采用SD的HAL阻塞式数据传输函数访问SD卡。
本示例继续使用旺宝红龙开发板STM32F407ZGT6 KIT V1.0。
(1)USART6、CodeGenerator、SYS
与参考文件相同。
(2)SDIO
SDIO的模式设置为SD 4 bits Wide bus,分频系数(SDIOCLK clock divide factor)决定了给SD卡的时钟信号SDIO_CK的频率,这里的系数设置为6,SDIO_CK的频率就是48MHz/8=6MHz。

(3)RCC
本示例要使用RTC为FatFS提供时间戳,所以在RCC组件的模式设置中启用LSE,并在时钟树上将LSE设置为RTC的时钟源。启用SDIO后,在时钟树上会自动启用48MHz时钟信号。如果这个时钟信号不是48MHz,使用自动解决时钟信号。
(4)GPIO
本示例要用到4个按键用于菜单切换。1个SD卡侦测线(可以不设置)。

(5)RTC
在RTC的模式设置中,只需启用时钟源和日历。

(6)FATFS
FatFS的模式设置选择SD Card。需要先启用SDIO接口后,才可以在此选择SD Card,否则该选项是不可选的。
模式选择为SD Card后,FatFS的参数设置部分有4个页面,相对于使用User-defined模式时,多了Advanced Settings和Platform Settings页面。

Set Defines页面的参数大部分保留其默认值,只需要将代码页(CODE_PAGE)设置为简体中文,而且本示例为测试使用长文件名,将USE_LFN参数设置为Enabled with dynamic working buffer on the HEAP,也就是由FatFS在其堆空间自动为LFN分配存储空间。MAX_SS(最大扇区大小)和MIN_SS(最小扇区大小)都被自动设置为512,因为SD卡的块大小固定为512字节,FatFS中的扇区就对应于SD卡的块。

Advanced Settings页面几个参数的意义和设置选项如下。
- SDIO instance,SDIO接口方式。这里只能设置为SDIO。STM32F4的SDIO接口只支持SDIO模式,不支持SPI模式。
- Use dma template,是否使用DMA模板。如果要使用DMA方式进行SD卡数据读写,就需要设置为Enabled。本示例使用轮询方式进行SD卡数据读写,所以设置为Disabled。
- BSP code for SD,SD卡的BSP(board support package)代码,这个参数固定为Generic,也就是只能使用CubeMX自动生成的SD卡BSP代码。

FatFS的Platform Settings这个页面用于设置SD卡的卡检测信号引脚CD,本例使用的开发板上microSD卡槽有CD信号PF6,所以在第二个下拉列表框中,选择PF6即可。
如果没有设置FatFS的Platform Settings页面的参数(有的开发板没有设计),在完成设置后,CubeMX会自动生成代码,这时会出现提示没有设置FatFS的Platform Settings页面的参数的警告对话框,单击Yes按钮,继续生成代码即可。
(7)NVIC
把timebase时基的中断优先级修改为0。其它默认。
2、项目文件组成和初始代码分析
(1)项目文件组成
完成设置后,CubeMX生成CubelIDE项目代码。项目的Middlewares目录下存有FatFS的源代码,其文件组成如图所示。FatFs\src\option目录下多了一个文件cc936.c,这是与简体中文Unicode码相关的一个文件,因为本示例设置了使用LFN和UTF-8编码。Middlewares\lFatFs\目录下其他文件的作用可以参考本文作者发布的其他相关文章。
\Src和\Inc和\FATFS目录下的文件,包含针对SD卡生成的FatFS相关文件,或可修改的FatFS用户程序文件。其中,与FatFS的SD卡移植程序相关的几个文件描述如下。
- sdio.h和sdio.c是SDIO接口的外设初始化程序文件,包含函数MX_SDIO_SD_Init(),但是这个函数与参考文章中的初始化函数MX_SDIO_SD_Init()有些差异。
- ffconf.h是FatFS的配置文件,包含很多的宏定义,与CubeMX里的FatFS设置对应。
- fatfs.h和fatfs.c是用户的FatFS初始化程序文件,包括FatFS初始化函数MX_FATFS_Init(),几个全局变量的定义,以及需要重新实现的获取RTC时间作为文件系统时间戳的函数get_fattime()。
- sd_diskio.h和sd_diskio.c是实现了SD卡的Disk IO函数的程序文件,例如,读取SD卡数据的函数SD_read(),写数据的函数SD_write()。这些函数的代码已经针对SD卡自动移植好了,无须用户再编写任何代码。
- bsp_driver_sd.h和bsp_driver_sd.c是SD卡各种具体操作的BSP函数的程序文件,如读取SD卡数据块的函数BSP_SD_ReadBlocks(),而这个函数则是调用HAL驱动函数HAL_SD_ReadBlocks()实现的。文件sd_diskio.c中的一些Disk IO函数的实现就是调用相应的BSP函数,例如,Disk IO函数SD_read()就是调用BSP_SD_ReadBlocks()。
本示例中,SD卡的FatFS相关文件的层次和关系如下图所示。与使用SPI-Flash芯片的FatFS文件的层次和关系相比(参考文章2),两个示例的文件差别就在具体硬件访问层部分。在本示例中,SD卡的Disk IO函数在文件sd_diskio.c中实现,这些Disk IO函数的实现依赖于BSP驱动程序文件bsp_driver_sd.h/.c,而BSP驱动程序则依赖SD的HAL驱动程序实现SD卡的数据块的读写。

(2)初始主程序
CubeMX生成的初始主程序代码如下,主要就是各个外设的初始化,以及FatFS的初始化:
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "fatfs.h"
#include "rtc.h"
#include "sdio.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "keyled.h"
#include "file_opera.h"
#include <stdio.h>
/* USER CODE END Includes */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
__IO uint8_t status = SD_PRESENT;
/* USER CODE END PM */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* Configure the system clock */
SystemClock_Config();
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART6_UART_Init();
MX_RTC_Init();
MX_FATFS_Init();
MX_SDIO_SD_Init();
//以下的代码接着的下文待续
(3)SDIO接口和SD卡初始化
主程序中,函数MX_SDIO_SD_Init()对SDIO接口和SD卡进行初始化,这个函数在文件sdio.h和sdio.c中定义和实现,代码如下:
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file sdio.c
* @brief This file provides code for the configuration
* of the SDIO instances.
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "sdio.h"
SD_HandleTypeDef hsd;
/* SDIO init function */
void MX_SDIO_SD_Init(void)
{
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 = 6;
}
void HAL_SD_MspInit(SD_HandleTypeDef* sdHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(sdHandle->Instance==SDIO)
{
/* SDIO clock enable */
__HAL_RCC_SDIO_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
/**SDIO GPIO Configuration
PC8 ------> SDIO_D0
PC9 ------> SDIO_D1
PC10 ------> SDIO_D2
PC11 ------> SDIO_D3
PC12 ------> SDIO_CK
PD2 ------> SDIO_CMD
*/
GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11
|GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF12_SDIO;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF12_SDIO;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
}
}
参考文章1中,通过SD的HAL驱动程序直接读写SD卡数据时,展示过SDIO接口和SD卡的初始化函数MX_SDIO_SD_Init()的代码。而本示例的函数MX_SDIO_SD_Init()代码只是设置了SD对象变量hsd的属性,并没有调用函数HAL_SD_Init()进行初始化,也没有调用函数HAL_SD_ConfigWideBusOperation()设置SD总线为4位宽度。
本示例中,SDIO接口和SD卡的初始化过程是在文件sd_diskio.c中定义的Disk IO函数SD_initialize()中完成的。
(4)FatFS的初始化
函数MX_FATFS_Init()是在文件fasfs.h和fastfs.c中定义和实现的,是FatFS的初始化函数。文件fatfs.h的完整代码如下:
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file fatfs.h
* @brief Header for fatfs applications
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __fatfs_H
#define __fatfs_H
#ifdef __cplusplus
extern "C" {
#endif
#include "ff.h"
#include "ff_gen_drv.h"
#include "sd_diskio.h" /* defines SD_Driver as external */
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
extern uint8_t retSD; /* Return value for SD */
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 */
void MX_FATFS_Init(void);
/* USER CODE BEGIN Prototypes */
/* USER CODE END Prototypes */
#ifdef __cplusplus
}
#endif
#endif /*__fatfs_H */
创建了文件系统的SD卡称为SD逻辑驱动器,假设在SD卡上只有一个分区。用extern声明的4个变量是在文件fatfs.c中定义的。SDPath用于表示SD逻辑驱动器的路径,赋值后是“0:/”。SDFatFS是一个FATFS结构体变量,用于表示SD逻辑驱动器上的文件系统。SDFile是一个FIL类型结构体变量,表示文件对象,在操作文件时,可以使用这个文件对象。源程序文件fatfs.c的完整代码如下:
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file fatfs.c
* @brief Code for fatfs applications
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
#include "fatfs.h"
uint8_t retSD; /* Return value for SD */
char SDPath[4]; /* SD logical drive path */
FATFS SDFatFS; /* File system object for SD logical drive */
FIL SDFile; /* File object for SD */
/* USER CODE BEGIN Variables */
#include "file_opera.h"
/* USER CODE END Variables */
void MX_FATFS_Init(void)
{
/*## FatFS: Link the SD driver ###########################*/
retSD = FATFS_LinkDriver(&SD_Driver, SDPath);
/* USER CODE BEGIN Init */
/* additional user code for init */
/* USER CODE END Init */
}
/**
* @brief Gets Time from RTC
* @param None
* @retval Time in DWORD
*/
DWORD get_fattime(void)
{
/* USER CODE BEGIN get_fattime */
return fat_GetFatTimeFromRTC();
/* USER CODE END get_fattime */
}
函数MX_FATFS_Init()用于FatFS的初始化,只有一行代码,即
retSD =FATFS_LinkDriver(&SD_Driver,SDPath);
SD_Driver是在文件sd_diskio.c中定义的,是一个Diskio_drvTypeDef结构体类型的变量,它将Disk IO访问的函数指针指向文件sd_diskio.c中实现的SD卡访问的Disk IO函数。执行这行代码的作用就是将SD_Driver链接到系统的驱动器列表,以及为SDPath赋值。执行此行代码后,SDPath被赋值为“0:/”。
函数get_fattime()用于获取RTC时间,作为创建文件或修改文件时的时间戳数据。这里显示了函数get_fattime()实现后的代码,这也是FatFS针对SD卡移植时唯一需要用户实现的一个Disk IO函数。函数get_fattime()里就是调用了文件file_opera.h中定义的一个函数fat_GetFatTimeFromRTC()。
3、 SD卡的Disk IO函数实现
在FatFS相关文件中,与SD卡的Disk IO函数实现相关的就是具体硬件访问层的几个文件,即文件sd_diskio.h/.c和文件bsp_driver_sd.h/.c。
文件sd_diskio.h/.c定义和实现了SD卡的Disk IO函数。文件bsp_driver_sd.b/.c定义和实现了SD卡常用操作的函数。而底层硬件接口驱动则是SD的HAL驱动程序。
实现SD卡Disk IO访问函数的代码都是CubeMX自动生成的。
(1)文件sd_diskio.h/.c概览
文件sd_diskio.h中只有一行有效语句,就是对外声明了变量SD_Driver,表示SD驱动器:
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file sd_diskio.h
* @brief Header for sd_diskio.c module
******************************************************************************
*/
/* USER CODE END Header */
/* Note: code generation based on sd_diskio_template.h */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __SD_DISKIO_H
#define __SD_DISKIO_H
/* USER CODE BEGIN firstSection */
/* can be used to modify / undefine following code or add new definitions */
/* USER CODE END firstSection */
/* Includes ------------------------------------------------------------------*/
#include "bsp_driver_sd.h"
/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
/* Exported functions ------------------------------------------------------- */
extern const Diskio_drvTypeDef SD_Driver;
/* USER CODE BEGIN lastSection */
/* can be used to modify / undefine previous code or add new definitions */
/* USER CODE END lastSection */
#endif /* __SD_DISKIO_H */
变量SD_Driver是在文件sd_diskio.c中定义的。文件sd_diskio.c还实现了FatFS的几个DiskIO函数,只是这些函数都是文件sd_diskio.c的私有函数,在定义变量SD_Driver时,将Disk IO操作函数指针指向这些函数即可。
文件sd_diskio.c开头的声明部分的代码如下。为了使程序结构更清晰,这里省略了一些不成立的条件编译代码,删除了对编译条件_USE_WRITE和_USE_IOCTL的判断,这两个参数默认都是1。
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file sd_diskio.c
* @brief SD Disk I/O driver
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "ff_gen_drv.h"
#include "sd_diskio.h"
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* use the default SD timout as defined in the platform BSP driver*/
#if defined(SDMMC_DATATIMEOUT)
#define SD_TIMEOUT SDMMC_DATATIMEOUT
#elif defined(SD_DATATIMEOUT)
#define SD_TIMEOUT SD_DATATIMEOUT
#else
#define SD_TIMEOUT 30 * 1000
#endif
#define SD_DEFAULT_BLOCK_SIZE 512
/*
* Depending on the use case, the SD card initialization could be done at the
* application level: if it is the case define the flag below to disable
* the BSP_SD_Init() call in the SD_Initialize() and add a call to
* BSP_SD_Init() elsewhere in the application.
*/
/* USER CODE BEGIN disableSDInit */
/* #define DISABLE_SD_INIT */
/* USER CODE END disableSDInit */
/* Private variables ---------------------------------------------------------*/
/* Disk status */
static volatile DSTATUS Stat = STA_NOINIT;
/* Private function prototypes -----------------------------------------------*/
static DSTATUS SD_CheckStatus(BYTE lun); //检查SD卡状态
//以下是DISKIO访问关联函数
DSTATUS SD_initialize (BYTE); //SD卡初始化,关联函数disk_initialize()
DSTATUS SD_status (BYTE); //SD卡状态,关联函数disk_status()
DRESULT SD_read (BYTE, BYTE*, DWORD, UINT); //读取SD卡,关联函数disk_read()
#if _USE_WRITE == 1
DRESULT SD_write (BYTE, const BYTE*, DWORD, UINT);//写入SD卡,关联函数disk_write()
#endif /* _USE_WRITE == 1 */
#if _USE_IOCTL == 1
DRESULT SD_ioctl (BYTE, BYTE, void*); //SD卡IO控制,关联函数disk_ioct1()
#endif /* _USE_IOCTL == 1 */
/*下面的代码是对结构体变量SD_Driver的各成员变量赋值,结构体类型Diskio_drvTypeDef在文件*ff_gen_drv.h中定义,其成员变量是函数指针,赋值使其指向本文件中定义的SD函数
*/
const Diskio_drvTypeDef SD_Driver =
{
SD_initialize, //函数指针disk_initialize指向函数SD_initialize()
SD_status, //函数指针disk_status指向函数SD_status()
SD_read, //函数指针disk_write指向函数SD_write()
#if _USE_WRITE == 1
SD_write, //函数指针disk_write指向函数SD_write()
#endif /* _USE_WRITE == 1 */
#if _USE_IOCTL == 1
SD_ioctl, //函数指针disk_ioct1指向函数SD_ioct1()
#endif /* _USE_IOCTL == 1 */
};
/* USER CODE BEGIN beforeFunctionSection */
/* can be used to modify / undefine following code or add new code */
/* USER CODE END beforeFunctionSection */
/* Private functions ---------------------------------------------------------*/
//以下代码省略,需要的去IDE
此处,声明了5个Disk IO函数,即SD_initialize()、SD_status()、SD_read()、SD_write()、SD_ioctl()。由于这几个函数是文件内的私有函数,因此在文件sd_diskio.c的开头部分定义函数原型。
定义的变量SD_Driver是结构体类型Diskio_drvTypeDef,定义的时候就给SD_Driver的各成员变量赋值了,也就是指向这5个Disk IO函数。Diskio_drvTypeDef是在文件ff_gen_drv.h中定义的,其成员变量就是5个函数指针。ff_gen_drv.h和ff_gen_drv.c是FatFS的不可修改的源程序文件。
(2)文件bsp_driver_sd.h.c概览
bsp_driver_sd.h/.c是SD卡的BSP驱动程序,也就是针对开发板上SD卡具体硬件电路实现的SD卡常用操作函数的程序文件。BSP驱动程序是比HAL驱动程序高一级的驱动程序。文件bsp_driver_sd.h的代码如下:
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file bsp_driver_sd.h for F4 (based on stm324x9i_eval_sd.h)
* @brief This file contains the common defines and functions prototypes for
* the bsp_driver_sd.c driver.
******************************************************************************
*/
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __STM32F4_SD_H
#define __STM32F4_SD_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_hal.h"
#include "fatfs_platform.h"
/* Exported types --------------------------------------------------------*/
/**
* @brief SD Card information structure
*/
#define BSP_SD_CardInfo HAL_SD_CardInfoTypeDef
/* Exported constants --------------------------------------------------------*/
/**
* @brief SD status structure definition
*/
#define MSD_OK ((uint8_t)0x00)
#define MSD_ERROR ((uint8_t)0x01)
/**
* @brief SD transfer state definition
*/
#define SD_TRANSFER_OK ((uint8_t)0x00)
#define SD_TRANSFER_BUSY ((uint8_t)0x01)
#define SD_PRESENT ((uint8_t)0x01)
#define SD_NOT_PRESENT ((uint8_t)0x00)
#define SD_DATATIMEOUT ((uint32_t)100000000)
#ifdef OLD_API
/* kept to avoid issue when migrating old projects. */
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
#else
/* USER CODE BEGIN BSP_H_CODE */
/* Exported functions --------------------------------------------------------*/
uint8_t BSP_SD_Init(void);
uint8_t BSP_SD_ITConfig(void);
void BSP_SD_DetectIT(void);
void BSP_SD_DetectCallback(void);
uint8_t BSP_SD_ReadBlocks(uint32_t *pData, uint32_t ReadAddr, uint32_t NumOfBlocks, uint32_t Timeout);
uint8_t BSP_SD_WriteBlocks(uint32_t *pData, uint32_t WriteAddr, uint32_t NumOfBlocks, uint32_t Timeout);
uint8_t BSP_SD_ReadBlocks_DMA(uint32_t *pData, uint32_t ReadAddr, uint32_t NumOfBlocks);
uint8_t BSP_SD_WriteBlocks_DMA(uint32_t *pData, uint32_t WriteAddr, uint32_t NumOfBlocks);
uint8_t BSP_SD_Erase(uint32_t StartAddr, uint32_t EndAddr);
void BSP_SD_IRQHandler(void);
void BSP_SD_DMA_Tx_IRQHandler(void);
void BSP_SD_DMA_Rx_IRQHandler(void);
uint8_t BSP_SD_GetCardState(void);
void BSP_SD_GetCardInfo(HAL_SD_CardInfoTypeDef *CardInfo);
uint8_t BSP_SD_IsDetected(void);
/* These functions can be modified in case the current settings (e.g. DMA stream)
need to be changed for specific application needs */
void BSP_SD_AbortCallback(void);
void BSP_SD_WriteCpltCallback(void);
void BSP_SD_ReadCpltCallback(void);
/* USER CODE END BSP_H_CODE */
#endif
#ifdef __cplusplus
}
#endif
#endif /* __STM32F4_SD_H */
文件bsp_driver_sd.h定义了一些宏和函数。从这些函数的名字上可大致知道这些函数的功能,例如,BSP_SD_Init()用于SD卡初始化,BSP_SD_ReadBlocks()用于读取SD卡数据块。
文件bsp_driver_sd.c开头部分的代码如下:
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file bsp_driver_sd.c for F4 (based on stm324x9i_eval_sd.c)
*/
/* USER CODE END Header */
#ifdef OLD_API
/* kept to avoid issue when migrating old projects. */
#else
/* USER CODE BEGIN FirstSection */
/* can be used to modify / undefine following code or add new definitions */
/* USER CODE END FirstSection */
/* Includes ------------------------------------------------------------------*/
#include "bsp_driver_sd.h"
/* Extern variables ---------------------------------------------------------*/
extern SD_HandleTypeDef hsd;
//此处省略以下的代码,需要看就去IDE
这里只声明了一个外部变量hsd,也就是文件sdio.c中定义的SD对象变量。因为文件bsp_driver_sd.c要使用SD的HAL驱动函数,需要用到SD对象变量。
(3)函数SD_status()的实现
文件sd_diskio.c中的函数SD_status()关联通用Disk IO函数disk_status(),用于检测SD卡的状态。文件sd_diskio.c中函数SD_status()和相关函数的代码如下。函数中的参数lun表示逻辑驱动器编号,只有一个驱动器时lun就是0。
/**
* @brief Gets Disk Status
* @param lun : not used
* @retval DSTATUS: Operation status
*/
DSTATUS SD_status(BYTE lun) //检查SD卡状态
{
return SD_CheckStatus(lun);
}
/* USER CODE BEGIN beforeFunctionSection */
/* can be used to modify / undefine following code or add new code */
/* USER CODE END beforeFunctionSection */
/* Private functions ---------------------------------------------------------*/
static DSTATUS SD_CheckStatus(BYTE lun) //检查SD卡状态
{
Stat = STA_NOINIT;
if(BSP_SD_GetCardState() == MSD_OK) //调用相应的BSP函数
{
Stat &= ~STA_NOINIT;
}
return Stat;
}
函数SD_CheckStatus()调用了BSP函数BSP_SD_GetCardState(),文件bsp_driver_sd.c中,这个函数的代码如下:
/**
* @brief Gets the current SD card data status.
* @param None
* @retval Data transfer state.
* This value can be one of the following values:
* @arg SD_TRANSFER_OK: No data transfer is acting
* @arg SD_TRANSFER_BUSY: Data transfer is acting
*/
__weak uint8_t BSP_SD_GetCardState(void) //检查SD卡状态
{
return ((HAL_SD_GetCardState(&hsd) == HAL_SD_CARD_TRANSFER ) ? SD_TRANSFER_OK : SD_TRANSFER_BUSY);
}
函数BSP_SD_GetCardState()调用SD驱动函数HAL_SD_GetCardState()检测SD卡状态。如果状态为HAL_SD_CARD_TRANSFER,函数就返回SD_TRANSFER_OK,表示SD卡处于空闲状态;否则,函数返回SD_TRANSFER_BUSY,表示SD卡有未完成的操作。
检测SD卡状态在SD卡的应用程序里很重要,为了提高程序的鲁棒性IDE自动生成的程序代码里多次出现检查SD卡的状态,因此,如果使用者忽略了对SD卡状态的检查(检查是否插入、检查是否装载),程序的容错能力就会很弱,就会发生意想不到的失误,比如,不能装载、不能写入、不能读出,偏偏有的时候还能装载或写入或读出。
(4)函数SD_initialize()的实现
文件sd_diskio.c中的函数SD_initialize()关联通用Disk IO函数disk_initialize(),用于SD卡的初始化。文件sd_diskio.c中,函数SD_initialize()的代码如下:
/**
* @brief Initializes a Drive
* @param lun : not used
* @retval DSTATUS: Operation status
*/
DSTATUS SD_initialize(BYTE lun) //SD卡初始化
{
Stat = STA_NOINIT;
#if !defined(DISABLE_SD_INIT)
if(BSP_SD_Init() == MSD_OK) //调用相应BSP函数
{
Stat = SD_CheckStatus(lun); //检查SD卡状态
}
#else
Stat = SD_CheckStatus(lun);
#endif
return Stat;
}
函数SD_initialize()调用BSP函数BSP_SD_Init()进行SD卡初始化。文件bsp_driver_sd.c中,函数BSP_SD_Init()以及相关函数的代码如下:
/* USER CODE BEGIN BeforeInitSection */
/* can be used to modify / undefine following code or add code */
/* USER CODE END BeforeInitSection */
/**
* @brief Initializes the SD card device.
* @retval SD status
*/
__weak uint8_t BSP_SD_Init(void) //完成SDIO接口和SD卡初始化过程
{
uint8_t sd_state = MSD_OK;
/* Check if the SD card is plugged in the slot */
if (BSP_SD_IsDetected() != SD_PRESENT) //检测SD卡是否插入了卡槽
{
return MSD_ERROR;
}
/* HAL SD initialization */
sd_state = HAL_SD_Init(&hsd); //SDIO接口和SD卡初始化
/* Configure SD Bus width (4 bits mode selected) */
if (sd_state == MSD_OK)
{
/* Enable wide operation配置SD总线宽度为4位*/
if (HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B) != HAL_OK)
{
sd_state = MSD_ERROR;
}
}
return sd_state;
}
/**
* @brief Detects if SD card is correctly plugged in the memory slot or not.
* @param None
* @retval Returns if SD is detected or not
*/
/*检测SD卡是否插入了卡槽,如果没有CD信号引脚,所以总是返回SD_PRESENT
* 如果配置了CD信号引脚,就要检测引脚,此时需要重写BSP_SD_IsDetected(void)*/
__weak uint8_t BSP_SD_IsDetected(void)
{
__IO uint8_t status = SD_PRESENT;
if (BSP_PlatformIsDetected() == 0x0)
{
status = SD_NOT_PRESENT;
}
return status;
}
函数BSP_SD_Init()首先调用了函数BSP_SD_IsDetected(),检测SD卡是否已插入卡槽。函数BSP_SD_Init()还调用了HAL_SD_Init()和HAL_SD_ConfigWideBusOperation(),也就是完成了函数MX_SDIO_SD_Init()里没有完成的SDIO接口和SD卡初始化。
(5)函数SD_ioctl()的实现
文件sd_diskio.c中的函数SD_ioctl()关联通用Disk IO函数disk_ioctl(),用于响应SD卡的一些IO控制指令。函数SD_ioctl()的代码如下:
/* USER CODE BEGIN beforeIoctlSection */
/* can be used to modify previous code / undefine following code / add new code */
/* USER CODE END beforeIoctlSection */
/**
* @brief I/O control operation
* @param lun : not used
* @param cmd: Control code
* @param *buff: Buffer to send/receive control data
* @retval DRESULT: Operation result
*/
#if _USE_IOCTL == 1
DRESULT SD_ioctl(BYTE lun, BYTE cmd, void *buff)
{
DRESULT res = RES_ERROR;
BSP_SD_CardInfo CardInfo; //SD卡信息结构体变量
if (Stat & STA_NOINIT) return RES_NOTRDY;
switch (cmd)
{
/* Make sure that no pending write process */
case CTRL_SYNC :
res = RES_OK;
break;
/* Get number of sectors on the disk (DWORD) */
case GET_SECTOR_COUNT :
BSP_SD_GetCardInfo(&CardInfo); //获取SD卡信息
*(DWORD*)buff = CardInfo.LogBlockNbr; //扇区个数就是SD卡的逻辑块个数
res = RES_OK;
break;
/* Get R/W sector size (WORD) */
case GET_SECTOR_SIZE :
BSP_SD_GetCardInfo(&CardInfo); //获取SD卡信息
*(WORD*)buff = CardInfo.LogBlockSize; //扇区大小就是逻辑块的大小,默认为512字节
res = RES_OK;
break;
/* Get erase block size in unit of sector (DWORD) */
case GET_BLOCK_SIZE :
BSP_SD_GetCardInfo(&CardInfo); //获取SD卡信息
//FAT块大小=SD卡逻辑块大小/SD卡数据块默认大小
*(DWORD*)buff = CardInfo.LogBlockSize / SD_DEFAULT_BLOCK_SIZE;
res = RES_OK;
break;
default:
res = RES_PARERR;
}
return res;
}
#endif /* _USE_IOCTL == 1 */
在上述程序中,多处使用了函数BSP_SD_GetCardInfo()获取SD卡信息。文件bsp_driver_sd.c中实现的这个函数的代码如下,其功能就是调用函数HAL_SD_GetCardInfo()获取SD卡信息。
/**
* @brief Get SD information about specific SD card.
* @param CardInfo: Pointer to HAL_SD_CardInfoTypedef structure
* @retval None
*/
__weak void BSP_SD_GetCardInfo(HAL_SD_CardInfoTypeDef *CardInfo)
{
/* Get SD card Information */
HAL_SD_GetCardInfo(&hsd, CardInfo);
}
从函数SD_ioctl()的代码可以看出FAT的参数与SD卡的参数之间的关系。
- GET_SECTOR_COUNT指令的响应代码返回FAT的扇区(Sector)个数。从代码可以看出:FAT的扇区个数就是SD卡的逻辑块个数,而SD卡的逻辑块个数一般就等于实际的数据块个数。
- GET_SECTOR_SIZE指令的响应代码返回FAT扇区大小,单位是字节。从代码可以看出:FAT扇区大小就等于SD卡逻辑块的大小,也就是512字节。
- GET_BLOCK_SIZE指令的响应代码返回FAT块的大小,单位是扇区个数。FAT擦除存储介质的最小单位是块,不要与SD卡的数据块混淆。从代码可以看出:FAT块的大小等于SD卡逻辑块大小除以SD卡默认块大小,而CardInfo.LogBlockSize和SD_DEFAULT_BLOCK_SIZE的值都是512,所以计算出来的FAT块大小就是1。
所以,FAT的一个扇区就等于SD卡的一个数据块,扇区大小就是512字节,FAT擦除数据的最小单位就是一个SD卡数据块。
(6)函数SD_read()的实现
文件sd_diskio.c中的函数SD_read()关联通用Disk IO函数disk_read(),用于从SD卡读取数据。读取数据的最小单位是扇区,也就是SD卡的数据块,可以一次读取一个或多个扇区的数据。函数SD_read()的代码如下:
/* USER CODE BEGIN beforeReadSection */
/* can be used to modify previous code / undefine following code / add new code */
/* USER CODE END beforeReadSection */
/**
* @brief Reads Sector(s)
* @param lun : not used
* @param *buff: Data buffer to store read data
* @param sector: Sector address (LBA)
* @param count: Number of sectors to read (1..128)
* @retval DRESULT: Operation result
*/
DRESULT SD_read(BYTE lun, BYTE *buff, DWORD sector, UINT count)
{
DRESULT res = RES_ERROR;
if(BSP_SD_ReadBlocks((uint32_t*)buff,
(uint32_t) (sector),
count, SD_TIMEOUT) == MSD_OK)
{
/* wait until the read operation is finished */
while(BSP_SD_GetCardState()!= MSD_OK)
{
}
res = RES_OK;
}
return res;
}
上述程序调用了BSP函数BSP_SD_ReadBlocks()。文件bsp_driver_sd.c中实现的这个函数热代码如下,其原理就是调用SD的HAL驱动函数HAL_SD_ReadBlocks()读取SD卡的数据块。
/* USER CODE BEGIN BeforeReadBlocksSection */
/* can be used to modify previous code / undefine following code / add code */
/* USER CODE END BeforeReadBlocksSection */
/**
* @brief Reads block(s) from a specified address in an SD card, in polling mode.
* @param pData: Pointer to the buffer that will contain the data to transmit
* @param ReadAddr: Address from where data is to be read
* @param NumOfBlocks: Number of SD blocks to read
* @param Timeout: Timeout for read operation
* @retval SD status
*/
__weak uint8_t BSP_SD_ReadBlocks(uint32_t *pData, uint32_t ReadAddr, uint32_t NumOfBlocks, uint32_t Timeout)
{
uint8_t sd_state = MSD_OK;
if (HAL_SD_ReadBlocks(&hsd, (uint8_t *)pData, ReadAddr, NumOfBlocks, Timeout) != HAL_OK)
{
sd_state = MSD_ERROR;
}
return sd_state;
}
(7)函数SD_write()的实现
文件sd_diskio.c中的函数SD_write()关联通用Disk IO函数disk_write(),用于向SD卡写入数据。写入数据的最小单位是扇区,也就是SD卡的数据块,可以一次写入一个或多个扇区的数据。函数SD_write()的代码如下:
/* USER CODE BEGIN beforeWriteSection */
/* can be used to modify previous code / undefine following code / add new code */
/* USER CODE END beforeWriteSection */
/**
* @brief Writes Sector(s)
* @param lun : not used
* @param *buff: Data to be written
* @param sector: Sector address (LBA)
* @param count: Number of sectors to write (1..128)
* @retval DRESULT: Operation result
*/
#if _USE_WRITE == 1
DRESULT SD_write(BYTE lun, const BYTE *buff, DWORD sector, UINT count)
{
DRESULT res = RES_ERROR;
if(BSP_SD_WriteBlocks((uint32_t*)buff,
(uint32_t)(sector),
count, SD_TIMEOUT) == MSD_OK)
{
/* wait until the Write operation is finished */
while(BSP_SD_GetCardState() != MSD_OK)
{
}
res = RES_OK;
}
return res;
}
#endif /* _USE_WRITE == 1 */
上述程序调用了BSP函数BSP_SD_WriteBlocks()。文件bsp_driver_sd.c中实现的这个函数的代码如下,其原理就是调用SD的HAL驱动函数HAL_SD_WriteBlocks()向SD卡写入数据。
/* USER CODE BEGIN BeforeWriteDMABlocksSection */
/* can be used to modify previous code / undefine following code / add code */
/* USER CODE END BeforeWriteDMABlocksSection */
/**
* @brief Writes block(s) to a specified address in an SD card, in DMA mode.
* @param pData: Pointer to the buffer that will contain the data to transmit
* @param WriteAddr: Address from where data is to be written
* @param NumOfBlocks: Number of SD blocks to write
* @retval SD status
*/
__weak uint8_t BSP_SD_WriteBlocks_DMA(uint32_t *pData, uint32_t WriteAddr, uint32_t NumOfBlocks)
{
uint8_t sd_state = MSD_OK;
/* Write block(s) in DMA transfer mode */
if (HAL_SD_WriteBlocks_DMA(&hsd, (uint8_t *)pData, WriteAddr, NumOfBlocks) != HAL_OK)
{
sd_state = MSD_ERROR;
}
return sd_state;
}
4、 SD卡文件管理功能的实现
(1)主程序功能
有了CubeMX生成的代码,就可以直接使用FatFS的API函数管理SD卡上的文件系统了。本示例在主程序中创建两级菜单,在SD卡上测试使用文件管理功能。
将KEY_LED和FILE_TEST添加到项目的搜索路径。
其中,FILE_TEST目录下包含文件file_opera.h和file_opera.c,使用其中的函数,或添加新的测试函数。详见参考文章2。
添加用户功能代码后的主程序代码如下:
//继续前文main.c程序代码
/* USER CODE BEGIN 2 */
// Start Menu
printf("Demo14_1: FatFS on SD card.\r\n\r\n");
//SD detect
BSP_SD_IsDetected();
if(status)
printf("SD has been present.\r\n");
else
printf("SD has been absent.\r\n");
//Mount the file system
FRESULT res = FR_OK;
res=f_mount(NULL,"0:",1); // Uninstall the drive first
//HAL_Delay(10);
res = f_mount(&SDFatFS, "0:", 1); // Mount the SD card file system
// If the mount is not successful, uninstall it first and then mount it.
if(res != FR_OK)
{
BYTE workBuffer[4*512];
res=f_mkfs("0:",FM_FAT32,0,workBuffer,4*512);
}
else
{
res=f_mount(NULL,"0:",1); //uninstall it first
HAL_Delay(10);
res=f_mount(&SDFatFS,"0:",1); //and then mount it
}
if (res == FR_OK) // Mounted successfully
printf("FatFS is mounted, OK.\r\n\r\n");
else
printf("No file system, to format.\r\n\r\n");
//Menu Item 1
printf("[S2]KeyUp =Format SD card. \r\n");
printf("[S1]KeyRight=SD card info. \r\n");
printf("[S4]KeyLeft =FAT disk info. \r\n");
printf("[S3]KeyDown =Next menu page. \r\n\r\n");
KEYS waitKey;
while(1)
{
waitKey=ScanPressedKey(KEY_WAIT_ALWAYS); // Waiting for the button pressed
if(waitKey == KEY_UP) // KeyUp=Format SD card
{
BYTE workBuffer[4*BLOCKSIZE]; // Working cache area
DWORD clusterSize=0; // The cluster size must be greater than or equal to 1 sector, 0 is automatically set.
printf("\r\nFormating (10secs)...\r\n");
FRESULT res=f_mkfs("0:", FM_FAT32, clusterSize, workBuffer, 4*BLOCKSIZE); //FM_FAT32
if (res == FR_OK)
printf("Format OK, to reset.\r\n");
else
printf("Format fail, to reset.\r\n");
}
else if(waitKey == KEY_LEFT) //KeyLeft =FAT disk info
fatTest_GetDiskInfo();
else if (waitKey == KEY_RIGHT) //KeyRight=SD card info"
SDCard_ShowInfo();
else
break; //turn into the next menu
printf("Reselect menu item or reset.\r\n\r\n");
HAL_Delay(500); //Delay, eliminate the impact of key jitter
}
//Menu Item 2
printf("\r\n");
printf("[S2]KeyUp =Write files.\r\n");
printf("[S3]KeyDown =Get a file info.\r\n");
printf("[S4]KeyLeft =Read a TXT file.\r\n");
printf("[S1]KeyRight=Read a BIN file.\r\n\r\n");
HAL_Delay(500);
while(2)
{
waitKey=ScanPressedKey(KEY_WAIT_ALWAYS);
//Test using long file names
if (waitKey==KEY_UP )
{
fatTest_WriteTXTFile("SD_readme.txt",2025,07,11);
fatTest_WriteTXTFile("SD_help.txt", 2025,07,11);
fatTest_WriteBinFile("SD_ADC2000.dat",30, 2000);
fatTest_WriteBinFile("SD_ADC1000.dat",100,1000);
f_mkdir("0:/SD_SubDirectory");
f_mkdir("0:/SD_Documents");
}
else if (waitKey==KEY_LEFT )
fatTest_ReadTXTFile("SD_readme.txt");
else if (waitKey==KEY_RIGHT)
fatTest_ReadBinFile("SD_ADC2000.dat");
else if (waitKey==KEY_DOWN)
fatTest_GetFileInfo("SD_ADC1000.dat");
printf("Reselect menu item or reset.\r\n\r\n");
HAL_Delay(500);
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
//以下的main.c代码未完,下文待续
完成外设初始化后,关键的操作就是先检查SD卡是否插入,然后先卸载驱动器再挂在驱动器,多数情况下遇到的SD卡不能加载、不能初始化、不能读写,都是违反了这样的原则。只要改过来,一切都顺利了。
f_mount(&SDFatFS,"0:",1)函数中,SDFatFS是在文件fatfs.c中定义的FATFS类型变量,表示SD卡上的文件系统。系统中只有一个驱动器,驱动器号是“0:”。这样挂载后,SDFatFS就表示逻辑驱动器0上的文件系统。
如果函数f_mount()的返回值为FR_OK,就表示驱动器挂载成功,SD卡已经被格式化过,可以进行文件系统的操作了;否则,就可能是没有文件系统,需要先执行函数f_mkfs()进行SD卡格式化操作。
不管函数f_mount()的返回结果是什么,程序都会在串口助手上显示一组菜单,内容如下:
[S2]KeyUp =Format SD card.
[S1]KeyRight=SD card info.
[S4]KeyLeft =FAT disk info.
[S3]KeyDown =Next menu page.
使用开发板上的4个按键进行选择操作,函数ScanPressedKey()是文件keyled.h中定义的轮询方式检测按键的函数。按下KeyDown键后会显示第二组菜单,内容如下:
[S2]KeyUp =Write files.
[S3]KeyDown =Get a file info.
[S4]KeyLeft =Read a TXT file.
[S1]KeyRight=Read a BIN file.
按下某个按键就会执行相应的操作,在某一级菜单中可以重新按键选择操作。
(2)SD卡格式化
要在SD卡上使用FAT文件系统,必须先使用函数f_mkfs()在SD卡上创建文件系统,也就是进行SD卡格式化操作。在主程序中,响应菜单项[S2]KeyUp=Format SD card,对SD卡进行格式化操作的代码如下:
if(waitKey == KEY_UP) // KeyUp=Format SD card
{
BYTE workBuffer[4*BLOCKSIZE]; // Working cache area
DWORD clusterSize=0; // The cluster size must be greater than or equal to 1 sector, 0 is automatically set.
printf("\r\nFormating (10secs)...\r\n");
FRESULT res=f_mkfs("0:", FM_FAT32, clusterSize, workBuffer, 4*BLOCKSIZE); //FM_FAT32
if (res == FR_OK)
printf("Format OK, to reset.\r\n");
else
printf("Format fail, to reset.\r\n");
}
在FAT文件系统中,簇(Cluster)的大小必须是扇区的整数倍,在调用函数f_mkfs()进行SD卡格式化时,如果设置簇大小为0,就由FatFS自动确定簇的大小。由于测试中使用的SD卡容量是8GB,不能再使用FAT12或FAT16文件系统,而只能使用FAT32文件系统,因此在函数f_mkfs()中指定文件系统类型为FM_FAT32。
(3)获取FAT磁盘信息
将SD卡格式化后,我们就可以通过FatFS的API函数f_getfree()获取FAT磁盘信息。菜单项[S4]KeyLeft=FAT disk info的响应代码就是调用了测试函数fatTest_GetDiskInfo()。对于SD卡,参数_MAX_SS和_MIN_SS都等于512。
在已经执行菜单项[S2]KeyUp=Write files创建了4个文件和2个目录后,一个8GB的SD卡的磁盘信息在串口助手上显示的内容如下:
FAT type= 3
[1=FAT12,2=FAT16,3=FAT32,4=exFAT]
Sector size(bytes)= 512
Cluster size(sectors)= 64
Total cluster count= 236321
Total sector count= 15124544
Total space(MB)= 7385
Free cluster count= 236320
Free sector count= 15124480
Free space(MB)= 7385
Get FAT disk info OK.
Reselect menu item or reset.
(4)获取SD卡信息
菜单项[S1]KeyRight=SD card info用于获取SD卡的原始参数信息,包括SD卡类型、数据块个数、容量等信息。这个菜单项的响应代码调用了文件mian.h/.c中定义和实现的函数SDCard_ShowInfo()。
函数SDCard_ShowInfo()显示的是SD卡的原始信息,即使SD卡没有被格式化,也可以返回这些信息。
Card Type= 1
Card Version= 1
Card Class= 1461
Relative Card Address= 4660
Block Count= 15126528
Block Size(Bytes)= 512
LogiBlockCount= 15126528
LogiBlockSize(Bytes)= 512
SD Card Capacity(MB)= 7386
(5)其它代码
main.h私有函数声明:
/* USER CODE BEGIN Private defines */
void SDCard_ShowInfo(); //SDCard Show Info
/* USER CODE END Private defines */
main.c私有函数定义:
/* USER CODE BEGIN 4 */
/* Show SD Information */
void SDCard_ShowInfo()
{
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*** SD card info *** \r\n\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);
uint32_t cap = cardInfo.BlockNbr/1024; //KB
cap = cap*cardInfo.BlockSize; //KB
cap = cap/1024; //MB
printf("SD Card Capacity(MB)= %ld \r\n\r\n", cap);
//return HAL_OK;
}
int __io_putchar(int ch)
{
HAL_UART_Transmit(&huart6,(uint8_t*)&ch,1,0xFFFF);
return ch;
}
//SD card detect
//Low level indicates that a card is inserted
uint8_t BSP_SD_IsDetected(void)
{
if(HAL_GPIO_ReadPin(SD_DETECT_GPIO_PORT, SD_DETECT_PIN) == GPIO_PIN_RESET)
status = SD_PRESENT;
else
{
status = SD_NOT_PRESENT;
}
return status;
}
/* USER CODE END 4 */
fatfs.c私有文件包含、函数重写:
/* USER CODE BEGIN Variables */
#include "file_opera.h"
/* USER CODE END Variables */
/**
* @brief Gets Time from RTC
* @param None
* @retval Time in DWORD
*/
DWORD get_fattime(void)
{
/* USER CODE BEGIN get_fattime */
return fat_GetFatTimeFromRTC();
/* USER CODE END get_fattime */
}
三、运行与调试
下载后,可以运行、调试。只要注意到程序的鲁棒性,不要忽略了SD卡状态检测这一细节。一般情况下都可以完美实现SD的FATFS文件操作。
本文相关代码运行、调试后,各个操作结果通过串口助手存储到一个TXT文件,读者可以自己去印证:
Demo14_1: FatFS on SD card.
SD has been present.
FatFS is mounted, OK.
[S2]KeyUp =Format SD card.
[S1]KeyRight=SD card info.
[S4]KeyLeft =FAT disk info.
[S3]KeyDown =Next menu page.
Formating (10secs)...
Format OK, to reset.
Reselect menu item or reset.
*** SD card info ***
Card Type= 1
Card Version= 1
Card Class= 1461
Relative Card Address= 4660
Block Count= 15126528
Block Size(Bytes)= 512
LogiBlockCount= 15126528
LogiBlockSize(Bytes)= 512
SD Card Capacity(MB)= 7386
Reselect menu item or reset.
*** FAT disk info ***.
FAT type= 3
[1=FAT12,2=FAT16,3=FAT32,4=exFAT]
Sector size(bytes)= 512
Cluster size(sectors)= 64
Total cluster count= 236321
Total sector count= 15124544
Total space(MB)= 7385
Free cluster count= 236320
Free sector count= 15124480
Free space(MB)= 7385
Get FAT disk info OK.
Reselect menu item or reset.
[S2]KeyUp =Write files.
[S3]KeyDown =Get a file info.
[S4]KeyLeft =Read a TXT file.
[S1]KeyRight=Read a BIN file.
Write file OK: SD_readme.txt
Write file OK: SD_help.txt
Write file OK: SD_ADC2000.dat
Write file OK: SD_ADC1000.dat
Reselect menu item or reset.
File info of: SD_ADC1000.dat
File size(bytes)= 418
File attribute= 32
File Name= SD_ADC1000.dat
File Date= 2025- 7-11
File Time= 11:27: 2
Reselect menu item or reset.
Reading TXT file: Line1: Hello, FatFS
Reading TXT file: Line2: UPC, AnHui Hefei
Reading TXT file: Line3: Date: 2025-7-11
Reselect menu item or reset.
Reading BIN file: ADC1-IN5
Sampling freq: 2000
Point count: 30
Reselect menu item or reset.
更多推荐




所有评论(0)