配套资源下载地址
本文在STM32F103VET6硬件环境下,通过SDIO总线,实现了对Micro_SD Card的读写实验,并成功移植了Fatfs文件系统。下面开始讲解步骤(代码主要移植自原子系列教程,不做商业用途,仅供学习)。
目录
一、SD卡的读写
二、移植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 == 1DRESULT USER_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count);
#endif /* _USE_WRITE == 1 */
#if _USE_IOCTL == 1DRESULT USER_ioctl (BYTE pdrv, BYTE cmd, void *buff);
#endif /* _USE_IOCTL == 1 */Diskio_drvTypeDef USER_Driver =
{USER_initialize,USER_status,USER_read,
#if _USE_WRITEUSER_write,
#endif /* _USE_WRITE == 1 */
#if _USE_IOCTL == 1USER_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 */
编译后,烧录程序,得到以下结果: