细说STM32单片机SD卡的FatFS文件系统并使用DMA模式访问SD卡的方法及其应用

目录

一、示例功能和CubeMX项目设置

1、RCC、SYS、USART6、GPIO、FATFS、RTC、CodeGenerator

2、SDIO

3、NVIC

二、Disk IO函数实现代码分析

1、文件sd_diskio.c中的代码

2、文件bsp_driver_sd.c中的代码

三、SD卡文件管理功能的实现

1、main.c 

2、sd_diskio.c里的私有函数

3、其它文件

(1)main.h私有函数声明

(2)file_opera.c

四、运行与调试


        使用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.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wenchm

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值