目录
1、RCC、SYS、USART6、GPIO、FATFS、RTC、CodeGenerator
使用FatFS管理SD卡的文件系统时,SD卡的数据读写可以采用轮询方式或DMA方式。本文以实际案例介绍如何采用DMA模式访问SD卡的数据。 为了更好地理解本文的内容和使用的方法,需要参考本文作者发布的其他文章里的相关内容。
参考文章1:细说STM32单片机SD卡的FatFS文件系统并使用轮询模式访问SD卡的方法及其应用_bsp code for sd-优快云博客 https://wenchm.blog.youkuaiyun.com/article/details/149288991?spm=1011.2415.3001.5331
参考文章2:细说STM32单片机使用DMA模式直接访问SD卡的方法及其应用_stm32 dma stream-优快云博客 https://wenchm.blog.youkuaiyun.com/article/details/149242997?spm=1011.2415.3001.5331
一、示例功能和CubeMX项目设置
本文创建一个示例演示SDIO以DMA方式访问SD卡时,如何使用FatFS管理SD卡的文件系统。
继续使用旺宝红龙开发板STM32F407VET6 KIT V1.0。
并且,继续使用KEYLED和FILE_TEST里的文件,相关文件的完整源码请参考本文作者发布的其他文章,作者曾经上传过的。
本文项目与参考文章1相比,基本设置都不变,SDIO的模式和基本参数也不变,只需设置SDIO的DMA。
1、RCC、SYS、USART6、GPIO、FATFS、RTC、CodeGenerator
与参考文章1相同。
2、SDIO
在SDIO的配置界面为DMA请求SDIO_RX和SDIO_TX设置DMA流。
3、NVIC
二、Disk IO函数实现代码分析
由CubeMX生成代码,生成的CubeIDE项目的文件组成与项目参考文章1的基本相同,只是多了文件dma.h和dma.c,这是DMA外设初始化程序文件。FatFS的Disk IO函数的移植主要在文件bsp_driver_sd.h/.c和sd_diskio.h/.c中。
1、文件sd_diskio.c中的代码
以文件sd_diskio.c中函数SD_read()为例,分析DMA方式下Disk IO函数的实现原理。文件sd_diskio.c中的相关代码如下。
/* 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;
uint32_t timeout;
#if defined(ENABLE_SCRATCH_BUFFER)
uint8_t ret;
#endif
#if (ENABLE_SD_DMA_CACHE_MAINTENANCE == 1)
uint32_t alignedAddr;
#endif
/*
* ensure the SDCard is ready for a new operation
*/
if (SD_CheckStatusWithTimeout(SD_TIMEOUT) < 0)
{
return res;
}
#if defined(ENABLE_SCRATCH_BUFFER)
if (!((uint32_t)buff & 0x3))
{
#endif
if(BSP_SD_ReadBlocks_DMA((uint32_t*)buff,
(uint32_t) (sector),
count) == MSD_OK)
{
ReadStatus = 0;
/* Wait that the reading process is completed or a timeout occurs */
timeout = HAL_GetTick();
while((ReadStatus == 0) && ((HAL_GetTick() - timeout) < SD_TIMEOUT))
{
}
/* in case of a timeout return error */
if (ReadStatus == 0)
{
res = RES_ERROR;
}
else
{
ReadStatus = 0;
timeout = HAL_GetTick();
while((HAL_GetTick() - timeout) < SD_TIMEOUT)
{
if (BSP_SD_GetCardState() == SD_TRANSFER_OK)
{
res = RES_OK;
#if (ENABLE_SD_DMA_CACHE_MAINTENANCE == 1)
/*
the SCB_InvalidateDCache_by_Addr() requires a 32-Byte aligned address,
adjust the address and the D-Cache size to invalidate accordingly.
*/
alignedAddr = (uint32_t)buff & ~0x1F;
SCB_InvalidateDCache_by_Addr((uint32_t*)alignedAddr, count*BLOCKSIZE + ((uint32_t)buff - alignedAddr));
#endif
break;
}
}
}
}
#if defined(ENABLE_SCRATCH_BUFFER)
}
else
{
/* Slow path, fetch each sector a part and memcpy to destination buffer */
int i;
for (i = 0; i < count; i++) {
ret = BSP_SD_ReadBlocks_DMA((uint32_t*)scratch, (uint32_t)sector++, 1);
if (ret == MSD_OK) {
/* wait until the read is successful or a timeout occurs */
timeout = HAL_GetTick();
while((ReadStatus == 0) && ((HAL_GetTick() - timeout) < SD_TIMEOUT))
{
}
if (ReadStatus == 0)
{
res = RES_ERROR;
break;
}
ReadStatus = 0;
#if (ENABLE_SD_DMA_CACHE_MAINTENANCE == 1)
/*
*
* invalidate the scratch buffer before the next read to get the actual data instead of the cached one
*/
SCB_InvalidateDCache_by_Addr((uint32_t*)scratch, BLOCKSIZE);
#endif
memcpy(buff, scratch, BLOCKSIZE);
buff += BLOCKSIZE;
}
else
{
break;
}
}
if ((i == count) && (ret == MSD_OK))
res = RES_OK;
}
#endif
return res;
}
上述代码定义了两个初值为0的全局变量WriteStatus和ReadStatus。函数BSP_SD_WriteCpltCallback()里WriteStatus置为1,函数BSP_SD_ReadCpltCallback()将ReadStatus置为1。
Disk IO函数SD_read()调用函数BSP_SD_ReadBlocks_DMA()以DMA方式读取SD卡的数据。这个函数在文件bsp_driver_sd.c里实现,其实现代码就是调用了HAL驱动函数HAL_SD_ReadBlocks_DMA()。
DMA方式是非阻塞式操作,执行函数BSP_SD_ReadBlocks_DMA()返回后,只表示DMA传输启动成功,并不表示DMA数据传输完成。函数SD_read()调用BSP_SD_ReadBlocks_DMA()之后,立刻将ReadStatus设置为0,然后一直延时等待ReadStatus变为1或超时。如果ReadStatus变为1,还要查询SD卡的状态,等待其变为空闲状态,才算完成了一次DMA方式数据读取操作。
2、文件bsp_driver_sd.c中的代码
文件sd_diskio.c中定义的函数BSP_SD_WriteCpltCallback()和BSP_SD_ReadCpltCallback(),实际上是文件bsp_driver_sd.c中两个同名的弱函数的重新实现。文件bsp_driver_sd.c中几个相关函数的代码如下:
/**
* @brief Tx Transfer completed callback
* @param hsd: SD handle
* @retval None
*/
void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)
{
BSP_SD_WriteCpltCallback();
}
/**
* @brief Rx Transfer completed callback
* @param hsd: SD handle
* @retval None
*/
void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)
{
BSP_SD_ReadCpltCallback();
}
/**
* @brief BSP Tx Transfer completed callback
* @retval None
* @note empty (up to the user to fill it in or to remove it if useless)
*/
__weak void BSP_SD_WriteCpltCallback(void)
{
}
/**
* @brief BSP Rx Transfer completed callback
* @retval None
* @note empty (up to the user to fill it in or to remove it if useless)
*/
__weak void BSP_SD_ReadCpltCallback(void)
{
}
HAL_SD_TxCpltCallback()是DMA方式数据发送完成时的中断回调函数,HAL_SD_RxCpltCallback()是DMA方式数据接收完成时的中断回调函数。文件bsp_driver_sd.c重新实现了这两个回调函数,并在代码里分别调用了另外两个BSP函数,而这两个BSP函数是用__weak修饰符定义的弱函数,并且函数代码里为空。
文件sd_diskio.c重新实现了弱函数BSP_SD_WriteCpltCallback()和BSP_SD_ReadCpltCallback(),用于将标志变量WriteStatus和ReadStatus置为1,也就是在DMA流的中断回调函数里,将这两个标志变量置为1。
三、SD卡文件管理功能的实现
本示例与参考文章1的主要区别在于FatFS的Disk IO函数的实现方式不同,本示例的这些底层函数的实现使用了SDIO的DMA传输方式,而参考文章1使用的是SDIO的阻塞式访问方式。两个示例的主程序大致功能,也就是应用层的功能,是相同的。主程序代码如下:
1、main.c
//main.c
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "dma.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>
/* 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)
{
/* 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_DMA_Init();
MX_RTC_Init();
MX_USART6_UART_Init();
MX_SDIO_SD_Init();
MX_FATFS_Init();
/* USER CODE BEGIN 2 */
// Start Menu
printf("Demo14_2: FatFS on SD card(DMA).\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; /* (0) Succeeded */
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,until have it mounted.
while(res != FR_OK)
{
BYTE workBuffer[4*512];
res=f_mkfs("0:",FM_FAT32,0,workBuffer,4*512);
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
//format/SD info/disk info
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 menu2
printf("Reselect menu item or reset.\r\n\r\n");
HAL_Delay(500); //Delay, eliminate the impact of key jitter
}
printf("\r\n");
//Menu Item 2
//test read/write SD with FATFS universal function
printf("test read/write SD with FATFS universal function.\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 and a BIN file.\r\n");
printf("[S1]KeyRight=Next menu page.\r\n\r\n");
HAL_Delay(500);
/* Menu2,use FATFS function operates files,such as 'f_'.
* For example, These functions with "f_" as prefix,
* are located under the \Middlewares\Third_Party\FatFs\src\.
* These functions exist as long as FATFS is turned on,
* but they have nothing to do with DMA.
* Whether DMA is enabled or not, these functions are implemented the same way.
*
*/
while(2)
{
waitKey=ScanPressedKey(KEY_WAIT_ALWAYS);
//Create a file with the following text and name it.
if (waitKey==KEY_UP )
{
fatTest_WriteTXTFile("DMA_readme.txt",2025,07,14);
fatTest_WriteTXTFile("DMA_help.txt", 2025,07,14);
fatTest_WriteBinFile("DMA_ADC2000.dat",30, 2000);
fatTest_WriteBinFile("DMA_ADC1000.dat",100,1000);
f_mkdir("0:/SubDirectory");
f_mkdir("0:/Documents");
}
else if (waitKey==KEY_DOWN)
{
fatTest_GetFileInfo("DMA_ADC1000.dat");
}
else if (waitKey==KEY_LEFT )
{
printf("Read a TXT file:\r\n");
fatTest_ReadTXTFile("DMA_readme.txt");
printf("Read a BIN file:\r\n");
fatTest_ReadBinFile("DMA_ADC2000.dat");
}
else if (waitKey==KEY_RIGHT)
break; //turn into the next menu3
printf("Reselect menu item or reset.\r\n\r\n");
HAL_Delay(500);
}
printf("\r\n");
//Menu Item 3
//test erase/sd info/read/write with specialized SD_Function
printf("test erase/SD info/read/write with specialized SD_Function.\r\n");
printf("[S2]KeyUp =SD card info. \r\n");
printf("[S3]KeyDown =Erase 0-10 blocks. \r\n");
printf("[S4]KeyLeft =Write block. \r\n");
printf("[S1]KeyRight=Read block. \r\n\r\n");
HAL_Delay(500);
/* Menu 3,use FATFS function operates files,such as 'SD_'.
* For example, These functions with "SD_" as prefix,
* are located under the \FATFS\Target\.
* These functions exist as long as FATFS is turned on,
* but they are related to DMA.
* Whether DMA is enabled or not, these functions are implemented different way.
*
*/
while(3)
{
waitKey=ScanPressedKey(KEY_WAIT_ALWAYS);
if (waitKey==KEY_UP)
{
SDCard_ShowInfo();
//printf("Reselect menu item or reset. \r\n");
}
else if (waitKey== KEY_DOWN)
{
SDCard_EraseBlocks(); //EraseBlocks 0-10
//printf("Reselect menu item or reset. \r\n");
}
else if (waitKey== KEY_LEFT)
SDCard_TestWrite_DMA();
else if (waitKey== KEY_RIGHT)
SDCard_TestRead_DMA();
HAL_Delay(500);
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 */
}
/* 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 */
SDIO的DMA初始化过程可参考示例参考文章2,SDIO接口和SD卡的初始化过程可参考示例参考文章1,本示例相当于是这两个示例的综合,但是编程所使用的的代码调用的函数有所不同。
菜单1:使用FATFS文件系统通用函数,位于ff.c。执行:格式化SD、获取SD卡信息、获取FAT disk信息;
菜单2:使用FATFS文件系统通用函数,位于ff.c。执行:写文件、获取文件信息、读取文件;
菜单3:使用FATFS文件系统SD卡专用函数,位于sd_diskio.c。执行:获取SD卡信息、擦除、写、读;
本文创建了3级菜单,在每个菜单中使用不同的FATFS文件系统函数,旨在分享给读者无论使用FATFS文件系统通用函数还是使用FATFS文件系统SD卡专用函数,甚至使用STM32的HAL驱动函数,都可以实现对SD卡的操作。只是,使用不同的函数编程时需要考虑的细节不同,一般来说,使用的函数越底层,编程时需要考虑的因素越多,越需要考虑程序的鲁棒性;反之,使用的函数越专用,编程时需要考虑的细节越少,因为该考虑的细节和稳定性,IDE已经办到了。
经过测试, FATFS文件系统针对SD卡的专用函数为sd_diskio.c/h和bsp_driver_sd.c/.h,在自己的应用里调用这些专用函数,比如SD_write(),需要把私有函数写在sd_diskio.c的沙箱里。否则编译器无法通过。
2、sd_diskio.c里的私有函数
//sd_diskio.c
/* USER CODE BEGIN firstSection */
/* can be used to modify / undefine following code or add new definitions */
//these private includes usedto be private function
#include "main.h"
#include <stdio.h>
#include "usart.h"
#include "sdio.h"
/* USER CODE END firstSection*/
/* Includes ------------------------------------------------------------------*/
#include "ff_gen_drv.h"
#include "sd_diskio.h"
#include <string.h>
/* USER CODE BEGIN enableScratchBuffer */
/* #define ENABLE_SCRATCH_BUFFER */
// private variable
uint8_t SDBuf_TX[BLOCKSIZE]; //Data Sending Cache, BLOCKSIZE=512
uint8_t SDBuf_RX[BLOCKSIZE]; //Data Received Cache
extern SD_HandleTypeDef hsd;
/* USER CODE END enableScratchBuffer */
//以下的代码此处省略
//sd_diskio.c private function
/* USER CODE BEGIN lastSection */
/* BSP_SD_Erase(), Erase SD card Blocks
* can replace with HAL_SD_Erase()
* */
void SDCard_EraseBlocks()
{
uint32_t BlockAddrStart=0; // Block 0 start Addresses using block number
uint32_t BlockAddrEnd=10; // Block 10
printf("\r\n*** Erasing blocks *** \r\n\r\n");
if (BSP_SD_Erase(BlockAddrStart, BlockAddrEnd) == HAL_OK)
{
printf("Erasing blocks,OK. \r\n");
printf("Blocks 0-10 is erased. \r\n");
}
else
printf("Erasing blocks,fail. \r\n");
HAL_SD_CardStateTypeDef cardState = BSP_SD_GetCardState();
printf("GetCardState()= %ld \r\n", cardState);
// The following code has nothing to do with erasure and can be deleted.[wen]
while(cardState != HAL_SD_CARD_TRANSFER) //Wait for return to transmission status
{
HAL_Delay(1);
cardState = BSP_SD_GetCardState();
return; //Otherwise, it will not come out after entering the loop
}
}
/* SD_write(), Write SD card */
void SDCard_TestWrite_DMA() //DMA mode, test write
{
printf("\r\n*** DMA Writing blocks *** \r\n\r\n");
for(uint16_t i=0;i<BLOCKSIZE; i++)
SDBuf_TX[i]=i; //generate data
printf("Writing block 6. \r\n");
printf("Data in [10:15] is: %d ",SDBuf_TX[10]);
for (uint16_t j=11; j<=15;j++)
{
printf(", %d", SDBuf_TX[j]);
}
printf("\r\n SD_write() is to be called. \r\n");
uint32_t BlockAddr=6; //Block Address
uint32_t BlockCount=1; //Block Count
BYTE lun={};
if(SD_write(lun,SDBuf_TX,BlockAddr,BlockCount) == RES_OK) //can erase block automatically
//SD_write(lun,SDBuf_TX,BlockAddr,BlockCount);
{
printf("DMA write complete. \r\n");
printf("SD_write() DMA Callback is called. \r\n");
}
}
/* SD_read(), Read SD card */
void SDCard_TestRead_DMA() //test read
{
printf("\r\n*** DMA Reading blocks *** \r\n\r\n");
printf("\r\nHAL_SD_ReadBlocks_DMA() is to be called. \r\n");
uint32_t BlockAddr=6; //Block Address
uint32_t BlockCount=1; //Block Count
BYTE lun={};
if(SD_read(lun,SDBuf_RX,BlockAddr,BlockCount) == RES_OK)
{
printf("DMA read complete. \r\n");
printf("SD_read() DMA Callback is called. \r\n");
printf("Data in [10:15] is: %d",SDBuf_RX[10]);
for (uint16_t j=11; j<=15;j++)
{
printf(", %d", SDBuf_RX[j]);
}
printf("\r\n");
}
}
/* USER CODE END lastSection */
3、其它文件
(1)main.h私有函数声明
/* USER CODE BEGIN EFP */
void SDCard_ShowInfo();
void SDCard_EraseBlocks();
void SDCard_TestWrite_DMA();
void SDCard_TestRead_DMA();
/* USER CODE END EFP */
(2)file_opera.c
因为部分函数有所修改,因此再次发布此函数。
/*
* file_opera.c
*
* Created on: Dec 13, 2019
* Author: Wang Weibo
* Function: Test FatFS's application interface function
*/
#include "usart.h"
#include "ff.h"
#include "fatfs.h"
#include "file_opera.h"
#include "rtc.h"
#include <stdio.h>
DWORD dt={};
void fatTest_WriteBinFile(TCHAR *filename, uint32_t pointCount, uint32_t sampFreq)
{
FIL file;
FRESULT res=f_open(&file,filename, FA_CREATE_ALWAYS | FA_WRITE);
if(res == FR_OK)
{
TCHAR headStr[]="ADC1-IN5\n";
f_puts(headStr, &file); //Write string data, ending with "\n" without "\0"
UINT bw=0; //Actual number of bytes written
f_write(&file, &pointCount, sizeof(uint32_t), &bw); //Number of data points
f_write(&file, &sampFreq, sizeof(uint32_t), &bw); //Sampling frequency
uint32_t value=1000;
for(uint16_t i=0; i<pointCount; i++,value++)
f_write(&file, &value, sizeof(uint32_t), &bw);
printf("Write file OK: %s\r\n",filename); //Put a formatted string to the file
}
f_close(&file);
}
void fatTest_ReadBinFile(TCHAR *filename)
{
FIL file;
FRESULT res=f_open(&file,filename, FA_READ);
if(res == FR_OK)
{
TCHAR str[50];
f_gets(str,50, &file); //Read 1 string
UINT bw=0; //The actual number of bytes read
uint32_t pointCount, sampFreq; //Save variables for reading data
f_read(&file, &pointCount, sizeof(uint32_t), &bw); //Number of data points
f_read(&file, &sampFreq, sizeof(uint32_t), &bw); //Sampling frequency
uint32_t value;
for(uint16_t i=0; i< pointCount; i++)
f_read(&file, &value, sizeof(uint32_t), &bw);
//USART display
printf("Reading BIN file: %s",str);
printf("Sampling freq: %ld\r\n",sampFreq);
printf("Point count: %ld\r\n",pointCount);
}
else if (res==FR_NO_FILE)
printf("File does not exist.\r\n");
else
printf("f_open() error.\r\n");
f_close(&file);
}
//Create a text file and write texts to the file.
void fatTest_WriteTXTFile(TCHAR *filename,uint16_t year, uint8_t month, uint8_t day)
{
FIL file;
FRESULT res=f_open(&file,filename, FA_CREATE_ALWAYS | FA_WRITE);
if(res == FR_OK)
{
TCHAR str[]="Line1: Hello, FatFS\n"; //The string must have a newline character "\n"
f_puts(str, &file); //The ending character "\0" will not be written
TCHAR str2[]="Line2: UPC, AnHui Hefei\n";
f_puts(str2, &file);
f_printf(&file, "Line3: Date: %d-%d-%d\n",year,month,day);
printf("Write file OK: %s\r\n",filename);
}
else
printf("Open file error.\r\n");
f_close(&file);
}
//Read the texts of a text file.
void fatTest_ReadTXTFile(TCHAR *filename)
{
FIL file;
FRESULT res=f_open(&file,filename, FA_READ); //Open the file read-only
if(res == FR_OK)
{
TCHAR str[100];
while (!f_eof(&file))
{
f_gets(str,100, &file); //Read 1 string and automatically add the ending character "\0"
printf("Reading TXT file: %s",str);
}
}
else if (res==FR_NO_FILE)
printf("File does not exist.\r\n");
else
printf("f_open() error.\r\n");
f_close(&file);
}
/* Get disk information and display it on USART Assistant */
void fatTest_GetDiskInfo()
{
FATFS *fs;
DWORD fre_clust; //The number of remaining clusters
FRESULT res = f_getfree("0:", &fre_clust, &fs); //Need to call refresh, fre_clust
if (res != FR_OK)
{
printf("f_getfree() error.\r\n");
return;
}
printf("\r\n*** FAT disk info ***.\r\n\r\n");
DWORD tot_sect = (fs->n_fatent - 2) * fs->csize; //Total number of sectors
DWORD fre_sect = fre_clust * fs->csize; //The number of sectors remaining = the number of clusters remaining * the number of sectors per cluster
#if _MAX_SS == _MIN_SS //For SD cards and USB drives, _MIN_SS=512 bytes
// The SD card's _MIN_SS is fixed to 512, and the right shift of 11 bits is equivalent to dividing by 2048
DWORD freespace= (fre_sect>>11); //Remaining space size, unit: MB, used for SD card, USB drive
DWORD totalSpace= (tot_sect>>11); //Total space size, unit: MB, used for SD card, USB drive
#else //Flash memory, small capacity
DWORD freespace= (fre_sect*fs->ssize)>>10; //Remaining space size, unit: KB
DWORD totalSpace= (tot_sect*fs->ssize)>>10; //Total space size, unit: KB
#endif
printf("FAT type= %d\r\n", fs->fs_type);
printf("[1=FAT12,2=FAT16,3=FAT32,4=exFAT] \r\n");
#if _MAX_SS == _MIN_SS //SD card, USB drive
printf("Sector size(bytes)= %d\r\n",_MIN_SS);
#else
printf("Sector size(bytes)= %d\r\n", fs->ssize);
#endif
printf("Cluster size(sectors)= %d\r\n", fs->csize);
printf("Total cluster count= %ld\r\n", fs->n_fatent-2);
printf("Total sector count= %ld\r\n", tot_sect);
#if _MAX_SS == _MIN_SS //SD card, USB drive
printf("Total space(MB)= %ld\r\n", totalSpace);
#else
printf("Total space(KB)= %ld\r\n", totalSpace);
#endif
printf("Free cluster count= %ld\r\n", fre_clust);
printf("Free sector count= %ld\r\n", fre_sect);
#if _MAX_SS == _MIN_SS //SD card, USB drive
printf("Free space(MB)= %ld\r\n", freespace);
#else
printf("Free space(KB)= %ld\r\n", freespace);
#endif
printf("Get FAT disk info OK.\r\n");
}
//Get file information for a file
void fatTest_GetFileInfo(TCHAR *filename)
{
printf("File info of: %s\r\n", filename);
FILINFO fno;
FRESULT fr=f_stat(filename, &fno);
if (fr==FR_OK)
{
printf("File size(DWORD)= %ld\r\n",fno.fsize);
printf("File fdate(WORD)= %d\r\n",fno.fdate);
printf("File ftime(WORD)= %d\r\n",fno.ftime);
printf("File attribute= %d\r\n",fno.fattrib);
printf("File Name= %s\r\n",fno.fname);
RTC_DateTypeDef sDate;
RTC_TimeTypeDef sTime;
char str[40];
//test
HAL_RTC_GetDate(&hrtc,&sDate,RTC_FORMAT_BIN);
HAL_RTC_GetTime(&hrtc,&sTime,RTC_FORMAT_BIN);
//Test RTC DATE
sprintf(str,"RTC Date= %d-%2d-%2d",2000+sDate.Year,sDate.Month,sDate.Date);
printf("%s\r\n",str);
//Test RTC TIME
sprintf(str,"RTC Time= %2d:%2d:%2d",sTime.Hours, sTime.Minutes,sTime.Seconds);
printf("%s\r\n",str);
//Set the FILE timestamp by RTC.
fat_GetFatTimeFromRTC();
fno.ftime = (dt & 0xFFFF);
dt = dt >>16;
fno.fdate = (dt & 0xFFFF);
fat_GetTimeStamp(&fno, &sDate, &sTime); //Convert time stamps to easy-to-display format
//sprintf(str,"File Date= %d",fno.fdate);
sprintf(str,"File Date= %d-%2d-%2d",2000+sDate.Year,sDate.Month,sDate.Date);
printf("%s\r\n",str);
//sprintf(str,"File Time= %d",fno.ftime);
sprintf(str,"File Time= %2d:%2d:%2d",sTime.Hours, sTime.Minutes,sTime.Seconds);
printf("%s\r\n",str);
}
else if (fr==FR_NO_FILE)
printf("File does not exist. \r\n");
else
printf("f_stat() error. \r\n");
}
//Set time stamp
//Set a time stamp for a file or directory
//need configure Enable _USE_CHMOD && !_FS_READONLY
void fat_SetTimeStamp(TCHAR *filename, const RTC_DateTypeDef *sDate, const RTC_TimeTypeDef *sTime)
{
FILINFO fno;
WORD date=(2000+sDate->Year-1980)<<9;
WORD month=(sDate->Month)<<5;
fno.fdate = date | month | (sDate->Date);
WORD time=(sTime->Hours)<<11;
WORD minute=(sTime->Minutes)<<5;
fno.ftime =time | minute | ((sTime->Seconds) >>1);
f_utime(filename, &fno);
// date2019-12-13
// WORD date=(2019-1980)<<9;
// WORD month=12<<5;
// fno.fdate = date | month |13;
//
// time 14:32:15
// WORD time=14<<11;
// WORD minute=32<<5;
// WORD sec=15>>1; //Divided by 2
// fno.ftime =time | minute | sec;
// f_utime("readme.txt", &fno);
}
// Convert date time from a FILINFO to RTC date time
void fat_GetTimeStamp(const FILINFO *fno, RTC_DateTypeDef *sDate, RTC_TimeTypeDef *sTime)
{
// WORD fdate; /* Modified date */
// bit[15:9], date from 1980 ; bit[8:5], month; bit[4:0], day
// WORD ftime; /* Modified time */
// bit[15:11], hour; bit[10:5], minute; bit[4:0], second/2
WORD date=fno->fdate; //Temporary variables
sDate->Date= (date & 0x001F); //Day
date = date>>5;
sDate->Month= (date & 0x000F); //Month
date= date>>4; //year from 1980
sDate->Year= (1980+date-2000); //Year, from 2000
WORD time=fno->ftime; //Temporary variables
sTime->Seconds= (time & 0x001F)<<1; //seconds
time =time >>5;
sTime->Minutes = time & 0x003F; //minute
sTime->Hours = (time >>6); //hour
}
//Get time from RTC as the file system time stamp data
//dt is modified global variable.
DWORD fat_GetFatTimeFromRTC()
{
RTC_TimeTypeDef sTime;
RTC_DateTypeDef sDate;
if (HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN) == HAL_OK)
{
HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
WORD date=(2000+sDate.Year-1980)<<9;
date = date |(sDate.Month<<5) |sDate.Date;
WORD time=sTime.Hours<<11;
time = time | (sTime.Minutes<<5) | (sTime.Seconds>1);
dt=(date<<16) | time;
return dt;
}
else
return 0;
}
// Scan and display files and directories in the specified directory
void fatTest_ScanDir(const TCHAR* PathName)
{
DIR dir; //Directory Objects
FILINFO fno; //File information
//FRESULT res = f_opendir(&dir, "0:"); //open dir
FRESULT res = f_opendir(&dir, PathName);
if (res != FR_OK)
{
f_closedir(&dir);
return;
}
printf("All entries in dir %s.\r\n",PathName);
while(1)
{
res = f_readdir(&dir, &fno); //Read an item in the directory
if (res != FR_OK || fno.fname[0] == 0)
break; // The file name is empty means that there are not many items to read.
if (fno.fattrib & AM_DIR) //It's a directory
printf("DIR %s.\r\n",fno.fname);
else //It's a file
printf("FILE %s.\r\n",fno.fname);
}
printf("Scan dir OK.\r\n");
f_closedir(&dir);
}
void fatTest_RemoveAll() //Delete all files and directories in the root directory
{
DIR dir; //Directory Objects
FILINFO fno; //File information
FRESULT res = f_opendir(&dir, "0:");//Open the root directory
if (res != FR_OK)
{
f_closedir(&dir);
return;
}
printf("Remove all entries...\r\n");
uint16_t cnt=0;
while(1)
{
res = f_readdir(&dir, &fno); //Read an item in the directory
if (res != FR_OK || fno.fname[0] == 0)
break; //The file name is empty means that there are not many items to read.
f_unlink(fno.fname); //Delete 1 file
cnt++;
}
f_closedir(&dir);
printf("Removed entries count= %d\r\n",cnt);
}
// List matching files in the directory, such as *.txt
void TestFindFiles(TCHAR *filter)
{
DIR dir; /* Search directory object */
FILINFO fno; /* File information */
//FRESULT fr = f_findfirst(&dir, &fno, "", "*.txt"); /* Find the first matching file */
FRESULT fr = f_findfirst(&dir, &fno, "", filter); /* Find the first matching file */
while (fr == FR_OK && fno.fname[0])
{
printf(" %s\r\n",fno.fname);
fr = f_findnext(&dir, &fno); /* Find the next match */
}
f_closedir(&dir); /* Close the directory */
}
四、运行与调试
以下是通过串口助手调试后生成的TXT文件:
Demo14_2: FatFS on SD card(DMA).
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.
test read/write SD with FATFS universal function.
[S2]KeyUp =Write files.
[S3]KeyDown =Get a file info.
[S4]KeyLeft =Read a TXT file and a BIN file.
[S1]KeyRight=Next menu page.
Write file OK: DMA_readme.txt
Write file OK: DMA_help.txt
Write file OK: DMA_ADC2000.dat
Write file OK: DMA_ADC1000.dat
Reselect menu item or reset.
File info of: DMA_ADC1000.dat
File size(DWORD)= 418
File fdate(WORD)= 0
File ftime(WORD)= 0
File attribute= 32
File Name= DMA_ADC1000.dat
RTC Date= 2025- 7-25
RTC Time= 12:47:42
File Date= 2025- 7-25
File Time= 12:47: 2
Reselect menu item or reset.
Read a TXT file:
Reading TXT file: Line1: Hello, FatFS
Reading TXT file: Line2: UPC, AnHui Hefei
Reading TXT file: Line3: Date: 2025-7-14
Read a BIN file:
Reading BIN file: ADC1-IN5
Sampling freq: 2000
Point count: 30
Reselect menu item or reset.
test erase/SD info/read/write with specialized SD_Function.
[S2]KeyUp =SD card info.
[S3]KeyDown =Erase 0-10 blocks.
[S4]KeyLeft =Write block.
[S1]KeyRight=Read block.
*** 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.
*** Erasing blocks ***
Erasing blocks,OK.
Blocks 0-10 is erased.
GetCardState()= 0
Reselect menu item or reset.
*** DMA Writing blocks ***
Writing block 6.
Data in [10:15] is: 10 , 11, 12, 13, 14, 15
SD_write() is to be called.
DMA write complete.
SD_write() DMA Callback is called.
Reselect menu item or reset.
*** DMA Reading blocks ***
HAL_SD_ReadBlocks_DMA() is to be called.
DMA read complete.
SD_read() DMA Callback is called.
Data in [10:15] is: 10, 11, 12, 13, 14, 15
Reselect menu item or reset.