本文在STM32F103VET6硬件环境下,通过SDIO总线,实现了对Micro_SD Card的读写实验,并成功移植了Fatfs文件系统。下面开始讲解步骤(代码主要移植自原子系列教程,不做商业用途,仅供学习)。
目录
-
一、SD卡的读写
第一步,我们先实现对SD卡的读写功能。、
首先配置SDIO总线,如图:
然后记得打开中断。以后可能会用到。
然后我们在工程里添加下面四个文件(记得包含他们的文件路径):
然后在Main.c中,调用sdio_test.c中的 SD_Test();
编译,然后下载,结果如下:
-
二、移植Fatfs文件系统
和SD卡的通信测试完成后,我们开始移植Fatfs文件系统
首先勾选这两项内容:
然后参数只需配置下面的内容即可,其他保持默认:
然后生成我们的工程。
这次我们去掉之前的sd_test文件
然后去user_diskio.c文件中添加用户代码
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file user_diskio.c
* @brief This file includes a diskio driver skeleton to be completed by the user.
******************************************************************************
* @attention
*
* <h2><center>© 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 */
#ifdef USE_OBSOLETE_USER_CODE_SECTION_0
/*
* Warning: the user section 0 is no more in use (starting from CubeMx version 4.16.0)
* To be suppressed in the future.
* Kept to ensure backward compatibility with previous CubeMx versions when
* migrating projects.
* User code previously added there should be copied in the new user sections before
* the section contents can be deleted.
*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
#endif
/* USER CODE BEGIN DECL */
/* Includes ------------------------------------------------------------------*/
#include <string.h>
#include "ff_gen_drv.h"
/* Private typedef -----------------------------------------------------------*/
#include "bsp_sdio_sdcard.h"
/* Private define ------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Disk status */
static volatile DSTATUS Stat = STA_NOINIT;
/* USER CODE END DECL */
/* Private function prototypes -----------------------------------------------*/
DSTATUS USER_initialize (BYTE pdrv);
DSTATUS USER_status (BYTE pdrv);
DRESULT USER_read (BYTE pdrv, BYTE *buff, DWORD sector, UINT count);
#if _USE_WRITE == 1
DRESULT USER_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count);
#endif /* _USE_WRITE == 1 */
#if _USE_IOCTL == 1
DRESULT USER_ioctl (BYTE pdrv, BYTE cmd, void *buff);
#endif /* _USE_IOCTL == 1 */
Diskio_drvTypeDef USER_Driver =
{
USER_initialize,
USER_status,
USER_read,
#if _USE_WRITE
USER_write,
#endif /* _USE_WRITE == 1 */
#if _USE_IOCTL == 1
USER_ioctl,
#endif /* _USE_IOCTL == 1 */
};
/* Private functions ---------------------------------------------------------*/
/**
* @brief Initializes a Drive
* @param pdrv: Physical drive number (0..)
* @retval DSTATUS: Operation status
*/
DSTATUS USER_initialize (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
/* USER CODE BEGIN INIT */
Stat = STA_NOINIT;
return Stat;
/* USER CODE END INIT */
}
/**
* @brief Gets Disk Status
* @param pdrv: Physical drive number (0..)
* @retval DSTATUS: Operation status
*/
DSTATUS USER_status (
BYTE pdrv /* Physical drive number to identify the drive */
)
{
/* USER CODE BEGIN STATUS */
Stat = STA_NOINIT;
if(BSP_SD_GetCardState() == MSD_OK) {
Stat &= ~STA_NOINIT;
}
return Stat;
/* USER CODE END STATUS */
}
/**
* @brief Reads Sector(s)
* @param pdrv: Physical drive number (0..)
* @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 USER_read (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
BYTE *buff, /* Data buffer to store read data */
DWORD sector, /* Sector address in LBA */
UINT count /* Number of sectors to read */
)
{
/* USER CODE BEGIN READ */
DRESULT res = RES_ERROR;
uint32_t timeout = 100000;
if(BSP_SD_ReadBlocks((uint32_t*)buff,
(uint32_t) (sector),
count, SD_DATATIMEOUT) == MSD_OK) {
while(BSP_SD_GetCardState()!= MSD_OK) {
if (timeout-- == 0) {
return RES_ERROR;
}
}
res = RES_OK;
}
return res;
/* USER CODE END READ */
}
/**
* @brief Writes Sector(s)
* @param pdrv: Physical drive number (0..)
* @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 USER_write (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
const BYTE *buff, /* Data to be written */
DWORD sector, /* Sector address in LBA */
UINT count /* Number of sectors to write */
)
{
/* USER CODE BEGIN WRITE */
/* USER CODE HERE */
DRESULT res = RES_ERROR;
uint32_t timeout = 100000;
if(BSP_SD_WriteBlocks((uint32_t*)buff,
(uint32_t)(sector),
count, SD_DATATIMEOUT) == MSD_OK)
{
while(BSP_SD_GetCardState()!= MSD_OK)
{
if (timeout-- == 0)
{
return RES_ERROR;
}
}
res = RES_OK;
}
return res;
/* USER CODE END WRITE */
}
#endif /* _USE_WRITE == 1 */
/**
* @brief I/O control operation
* @param pdrv: Physical drive number (0..)
* @param cmd: Control code
* @param *buff: Buffer to send/receive control data
* @retval DRESULT: Operation result
*/
#if _USE_IOCTL == 1
DRESULT USER_ioctl (
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
/* USER CODE BEGIN IOCTL */
DRESULT res = RES_ERROR;
BSP_SD_CardInfo CardInfo;
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);
*(DWORD*)buff = CardInfo.LogBlockNbr;
res = RES_OK;
break;
/* Get R/W sector size (WORD) */
case GET_SECTOR_SIZE :
BSP_SD_GetCardInfo(&CardInfo);
*(WORD*)buff = CardInfo.LogBlockSize;
res = RES_OK;
break;
/* Get erase block size in unit of sector (DWORD) */
case GET_BLOCK_SIZE :
BSP_SD_GetCardInfo(&CardInfo);
*(DWORD*)buff = CardInfo.LogBlockSize;
res = RES_OK;
break;
default:
res = RES_PARERR;
}
return res;
/* USER CODE END IOCTL */
}
#endif /* _USE_IOCTL == 1 */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
然后回到main.c中添加主要逻辑,先初始化
char SDPath[4]; /* SD卡逻辑设备路径 */
FATFS fs; /* FatFs文件系统对象 */
FIL file; /* 文件对象 */
FRESULT f_res; /* 文件操作结果 */
UINT fnum; /* 文件成功读写数量 */
BYTE ReadBuffer[1024]={0}; /* 读缓冲区 */
BYTE WriteBuffer[]= "Welcome to the wildfire STM32 development board. Today is a good day to create new file system test files\r\n";
然后在main函数中添加代码
/**
* @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();
/* Initialize interrupts */
MX_NVIC_Init();
/* USER CODE BEGIN 2 */
//DBGPRINTF("\r\r\n****** This is a SD card file system experiment ******\r\r\n");
// 注册一个FatFS设备:SD卡 //
if(FATFS_LinkDriver(&SD_Driver, SDPath) == 0)
{
//在SD卡挂载文件系统,文件系统挂载时会对SD卡初始化
f_res = f_mount(&fs,(TCHAR const*)SDPath,1);
printf_fatfs_error(f_res);
//----------------------- 格式化测试 ---------------------------//
// 如果没有文件系统就格式化创建创建文件系统 */
if(f_res == FR_NO_FILESYSTEM)
{
DBGPRINTF("The SD card does not have a file system yet and will be formatted...\r\n");
// 格式化 //
f_res=f_mkfs((TCHAR const*)SDPath,0,0);
if(f_res == FR_OK)
{
DBGPRINTF("The SD card has successfully formatted the file system\r\n");
// 格式化后,先取消挂载 //
f_res = f_mount(NULL,(TCHAR const*)SDPath,1);
// 重新挂载 //
f_res = f_mount(&fs,(TCHAR const*)SDPath,1);
}
else
{
DBGPRINTF("Format failed\r\n");
while(1);
}
}
else if(f_res!=FR_OK)
{
DBGPRINTF("The SD card failed to mount the file system.(%d)\r\n",f_res);
printf_fatfs_error(f_res);
while(1);
}
else
{
DBGPRINTF("The file system is mounted successfully and can be read and written\r\n");
}
//----------------------- 文件系统测试:写测试 -----------------------------//
// 打开文件,如果文件不存在则创建它 //
DBGPRINTF("****** A file write test will be performed... ******\r\n");
f_res = f_open(&file, "FatFsRWFiles.txt",FA_CREATE_ALWAYS | FA_WRITE );
if ( f_res == FR_OK )
{
DBGPRINTF("Open / create FatFs read / write test file. TXT file successfully, write data to the file.\r\n");
// 将指定存储区内容写入到文件内 //
f_res=f_write(&file,WriteBuffer,sizeof(WriteBuffer),&fnum);
if(f_res==FR_OK)
{
DBGPRINTF("File write success, write byte data:%d\r\n",fnum);
DBGPRINTF("The data written to the file is: \r\n%s\r\n",WriteBuffer);
}
else
{
DBGPRINTF(" File write failure:(%d)\r\n",f_res);
}
// 不再读写,关闭文件 //
f_close(&file);
}
else
{
DBGPRINTF("Failed to open / create file.\r\n");
}
//------------------- 文件系统测试:读测试 ------------------------------------//
DBGPRINTF("****** File read test is about to take place... ******\r\n");
f_res = f_open(&file, "FatFsRWFiles.txt", FA_OPEN_EXISTING | FA_READ);
if(f_res == FR_OK)
{
DBGPRINTF("File opened successfully.\r\n");
f_res = f_read(&file, ReadBuffer, sizeof(ReadBuffer), &fnum);
if(f_res==FR_OK)
{
DBGPRINTF("File read successfully, read byte data: %d\r\n",fnum);
DBGPRINTF("The obtained file data are as follows:\r\n%s \r\n", ReadBuffer);
}
else
{
DBGPRINTF("File read failure:(%d)\r\n",f_res);
}
}
else
{
DBGPRINTF("Failed to open file.\r\n");
}
// 不再读写,关闭文件 //
f_close(&file);
// 不再使用,取消挂载 //
f_res = f_mount(NULL,(TCHAR const*)SDPath,1);
}
// 注销一个FatFS设备:SD卡 //
FATFS_UnLinkDriver(SDPath);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
然后还要补充一个日志报错函数:
/* USER CODE BEGIN 4 */
/**
* @brief 打印输出信息
* @param 无
* @retval 无
*/
void printf_fatfs_error(FRESULT fresult)
{
switch(fresult)
{
case FR_OK:
DBGPRINTF("!!The operation was successful.\r\n");
break;
case FR_DISK_ERR:
DBGPRINTF("!!Hardware I / O driver error.\r\n");
break;
case FR_INT_ERR:
DBGPRINTF("!!Assertion error.\r\n");
break;
case FR_NOT_READY:
DBGPRINTF("!!The physical device is not working.\r\n");
break;
case FR_NO_FILE:
DBGPRINTF("!!Unable to find file.\r\n");
break;
case FR_NO_PATH:
DBGPRINTF("!!The path could not be found.\r\n");
break;
case FR_INVALID_NAME:
DBGPRINTF("!!Invalid pathname.\r\n");
break;
case FR_DENIED:
case FR_EXIST:
DBGPRINTF("!!Access denied.\r\n");
break;
case FR_INVALID_OBJECT:
DBGPRINTF("!!Invalid file or path.\r\n");
break;
case FR_WRITE_PROTECTED:
DBGPRINTF("!!Logical device write protection.\r\n");
break;
case FR_INVALID_DRIVE:
DBGPRINTF("!!Invalid logical device.\r\n");
break;
case FR_NOT_ENABLED:
DBGPRINTF("!!Invalid workspace.\r\n");
break;
case FR_NO_FILESYSTEM:
DBGPRINTF("!!Invalid file system.\r\n");
break;
case FR_MKFS_ABORTED:
DBGPRINTF("!!Fmkfs function operation failed due to function parameter problem.\r\n");
break;
case FR_TIMEOUT:
DBGPRINTF("!!The operation timed out.\r\n");
break;
case FR_LOCKED:
DBGPRINTF("!!The file is protected.\r\n");
break;
case FR_NOT_ENOUGH_CORE:
DBGPRINTF("!!Long file name support failed to get heap space.\r\n");
break;
case FR_TOO_MANY_OPEN_FILES:
DBGPRINTF("!!Too many files open.\r\n");
break;
case FR_INVALID_PARAMETER:
DBGPRINTF("!!Invalid parameter.\r\n");
break;
}
}
/* USER CODE END 4 */
编译后,烧录程序,得到以下结果: