HAL库操作SD卡,加载FATFS文件系统

HAL库操作SD卡,加载FATFS文件系统
在这里插入图片描述
功能配置
FS_MINIMIZE 这个设置是选择是否裁剪掉一些函数,disabled,不剪裁,全功能
USE_STRFUNC 这个用来设置是否支持字符串类操作,我们选择使用。带LF->CRLF转换
USE_FIND 使能或禁用在指定目录内搜索指定文件函数:f_findfirst和f_findnext
USE_MKFS 使能或禁用f_mkfs函数
USE_FASTSEEK 使能或禁用快速搜索功能,使能后,可以加快f_lseek、f_read和f_write函数执行。
USE_EXPAND 使能或禁用f_expand函数,该函数可以为文件分配连续数据区域。
USE_CHMOD 使能或禁用元数据控制函数:f_chmod和f_utime。
USE_LABEL 使能或禁用卷标签API函数:f_getlabel和f_setlabel。
USE_FORWARD 使能或禁用f_forward函数。

命名空间和本地环境配置
CODE_PAGE 选择支持中文
规定目标系统使用的OEM代码。如果该代码设置的不正确,可能会引起文件打开失败。如果没有根本没有使用扩展字符,则使用任何代码都没区别。
USE_LFN
使能或禁用长文件名(LFN)。
0,当然就是不支持长文件名了,节省空间.
1,支持长文件名,但不支持重入.
2,支持长文件名,工作区存放在STACK,也就是死数组里面.
3,支持长文件名,工作区采用动态内存管理,存放在HEAP,也就是必须实现动态内存管理.
MAX_LFN
定义长文件名工作缓冲区大小,可以为12~255字节。当禁用长文件名时,此选项无效。
LFN_UNICODE
使能或禁用Unicode。如果要使用Unicode(UTF16)字符串路径名,需要使能LFN和设置本选项为1。此选项还影响字符串I/O功能函数。如果禁用长文件名,此选项必须为0。
STRF_ENCODE
通过设置_LFN_UNICODE为1使能Unicode API函数时,这个选项定义通过字符串I/O函数读写的文件字符编码。字符串I/O函数有f_gets、f_putc、f_puts和f_printf。当_LFN_UNICODE等于0时,此选项不起作用。
FS_RPATH
配置相对路径函数。

卷/驱动器配置
VOLUMES
配置可用卷的数目,可设置为1~10。
MIN_SS、MAX_SS
定义扇区大小,有效值为512、1024、2048、4096,需要根据硬件配置来定义。_MIN_SS定义最小扇区大小,_MAX_SS定义最大扇区大小。都设置为512可以兼容所有SD卡和硬盘,但是在某些片上Flash和其它存储设备可能需要更大值。当_MAX_SS > _MIN_SS,FatFs被配置为扇区大小可变的并且必须在函数disk_ioctl中实现GET_SECTOR_SIZE命令。
MULTI_PARTITION
使能或禁止多分区函数。默认禁止,此时每个逻辑驱动器数目一定与物理驱动器数目相同,并且物理驱动器仅能安装FAT卷。如果使能,物理设备上可以有多个逻辑扇区,每个逻辑驱动器一定要预先定义在分区解析表VolToPart[]中。同时,f_disk函数有效。
USE_TRIM
使能或禁用ATA-TRIM函数。TRIM指令被文件系统用来通知设备哪些逻辑地址不再被占用,可以被设备回收为空闲空间。对于FatFs来说,使用函数f_unlink移除一个文件时,只是将对应的FAT区域设置为空,文件实际上在扇区中。如果想在移除文件时强制擦除扇区,只需将_USE_TRIM设置为1。如果使能TRIM函数,必须在函数disk_ioctl中实现CTRL_TRIM命令。
FS_NOFSINFO
使能或禁用空闲簇计数和最后分配的簇计数。

系统配置
FS_TINY
配置FatFs为正常模式或者微型(TINY)模式。配置为微型模式后,对内存需求变小,文件对象数据结构FIL会减少_MAX_SS字节。程序复用FATFS数据结构中的缓冲区代替FIL数据结构中去除掉的缓冲区。
FS_EXFAT
使能或禁用exFAT文件系统。要使能exFAT文件系统,必须使能长文件名功能并且配置_LFN_UNICODE = 1。如果要使用全功能版的exFAT,推荐_MAX_LFN = 255。注意,使能exFAT意味着不再兼容C89,因为要用到64位整形数。
FS_NORTC
使能或禁用时间戳函数。使能时间戳函数需要硬件RTC,并且需要提供底层函数get_fattime。如果系统没有硬件RTC或者不需要时间戳功能,设置_FS_NORTC为1禁用时间戳函数。此时,如果FatFs修改任何文件,使用固定的时间戳,固定时间戳由宏_NORTC_MON、_NORTC_MDAY和_NORTC_YEAR定义。如果系统没有RTC,这些宏用来定义固定时间戳。只读或者_FS_NORTC=0时,这些宏无意义。
比如:
#define _NORTC_MON 1
#define _NORTC_MDAY 1
#define _NORTC_YEAR 2016
FS_REENTRANT
使能或禁用FatFs模块的可重入特性。注意,访问不同卷上的文件/目录总是可重入的,无论是否使能本参数,卷控制函数f_mount、f_mkfs和f_fdisk总是不可重入的。要使能可重入特性,用户必须提供同步处理,要向工程中添加ff_req_grant、ff_rel_grant、ff_del_syncobj和ff_cre_syncobj函数。可以在文件option/syscall.c中找到示例。
FS_TIMEOUT
设置超时时间,单位为系统时钟滴答周期,当宏_FS_REENTRANT=0时,本设置无效。
FS_LOCK
使能或禁用文件锁功能。控制重复打开文件和非法打开文件对象。注意:文件锁功能不具有可重入性。只读模式下,这个宏必须为0。

修改红框中两项,其他默认
在这里插入图片描述

在这里插入图片描述
其他配置见前篇
在这里插入图片描述
红框部分,调大堆栈

在main.c中添加如下定义

FATFS fs;                 // Work area (file system object) for logical drive
	FIL fil;                  // file objects
	uint8_t wtext[] = "This is STM32 working with FatFs"; //要写入文件内容
	uint8_t rtext[100];                    //存储读取文件内容的数组
	char filename[] = "STM32cube.txt";  //新建的文件名
	uint8_t work[512];   //格式化SD卡用的缓存空间

在sdmmc.c文件中添加如下代码

/* USER CODE BEGIN 1 */
HAL_StatusTypeDef SD_DMAConfigRx(SD_HandleTypeDef *hsd)
{
  HAL_StatusTypeDef status = HAL_ERROR;
  
  /* Configure DMA Rx parameters */
  hdma_sdmmc1_rx.Instance = DMA2_Channel4;
	hdma_sdmmc1_rx.Init.Request = DMA_REQUEST_7;
	hdma_sdmmc1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
	hdma_sdmmc1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
	hdma_sdmmc1_rx.Init.MemInc = DMA_MINC_ENABLE;
	hdma_sdmmc1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
	hdma_sdmmc1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
	hdma_sdmmc1_rx.Init.Mode = DMA_NORMAL;
  hdma_sdmmc1_rx.Init.Priority = DMA_PRIORITY_LOW;
 
  /* Associate the DMA handle */
  __HAL_LINKDMA(hsd,hdmarx,hdma_sdmmc1_rx);
 
  /* Stop any ongoing transfer and reset the state*/
  HAL_DMA_Abort(&hdma_sdmmc1_rx);
  
  /* Deinitialize the Channel for new transfer */
  HAL_DMA_DeInit(&hdma_sdmmc1_tx);//注意这里!!!DeInit的是另一个通道!!!
 
  /* Configure the DMA Channel */
  status = HAL_DMA_Init(&hdma_sdmmc1_rx);
    
  return (status);
}

HAL_StatusTypeDef SD_DMAConfigTx(SD_HandleTypeDef *hsd)
{
  HAL_StatusTypeDef status;
  
  /* SDMMC1_TX Init */
	hdma_sdmmc1_tx.Instance = DMA2_Channel5;
	hdma_sdmmc1_tx.Init.Request = DMA_REQUEST_7;
	hdma_sdmmc1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
	hdma_sdmmc1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
	hdma_sdmmc1_tx.Init.MemInc = DMA_MINC_ENABLE;
	hdma_sdmmc1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
	hdma_sdmmc1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
	hdma_sdmmc1_tx.Init.Mode = DMA_NORMAL;//这里NORMAL或其他都可以,无所谓
	hdma_sdmmc1_tx.Init.Priority = DMA_PRIORITY_LOW;
 
  /* Associate the DMA handle */
  __HAL_LINKDMA(hsd, hdmatx, hdma_sdmmc1_tx);
  
  /* Stop any ongoing transfer and reset the state*/
  HAL_DMA_Abort(&hdma_sdmmc1_tx);
  
  /* Deinitialize the Channel for new transfer */
  HAL_DMA_DeInit(&hdma_sdmmc1_rx);  //注意这里!!!DeInit的是另一个通道!!!
  
  /* Configure the DMA Channel */
  status = HAL_DMA_Init(&hdma_sdmmc1_tx); 
 
  return (status);
}
/* USER CODE END 1 */

在sdmmc.h文件中添加函数声明

/* USER CODE BEGIN Prototypes */
HAL_StatusTypeDef SD_DMAConfigTx(SD_HandleTypeDef *hsd);
HAL_StatusTypeDef SD_DMAConfigRx(SD_HandleTypeDef *hsd);
/* USER CODE END Prototypes */

然后,打开bsp_driver_sd.c文件,到BSP_SD_ReadBlocks_DMA函数中,在

if (HAL_SD_ReadBlocks_DMA(&hsd1, (uint8_t *)pData, ReadAddr, NumOfBlocks) != HAL_OK)

{
sd_state = MSD_ERROR;
}

上方添加以下代码:

if(SD_DMAConfigRx(&hsd1) != HAL_OK)
{
		return MSD_ERROR;
}

在BSP_SD_WriteBlocks_DMA,在

if (HAL_SD_WriteBlocks_DMA(&hsd1, (uint8_t *)pData, WriteAddr, NumOfBlocks) != HAL_OK)

{
sd_state = MSD_ERROR;
}

上方添加以下代码:

if(SD_DMAConfigTx(&hsd1) != HAL_OK)
{
		return MSD_ERROR;
}

bsp_driver_sd.c这两个添加的代码没有在USER CODE里,重新生成工程后就没了

以上修改因为,ST官方的例子,在每次调用HAL_SD_WriteBlocks_DMA和HAL_SD_ReadBlocks_DMA之前都进行了DMA通道的初始化配置,这就是解决问题的关键。而CubeMX生成的源码缺少了这个配置

//retSD = f_mkfs("", FM_ANY, 0, work, sizeof work);
//	HAL_Delay(100);	
retSD = f_mount(&fs, "", 1);
	HAL_Delay(100);	
retSD = f_open(&fil, filename, FA_CREATE_ALWAYS | FA_WRITE);
	HAL_Delay(100);	
retSD = f_write(&fil, wtext, sizeof(wtext), (void *)&byteswritten);
	HAL_Delay(100);	
retSD = f_close(&fil);
	HAL_Delay(100);	
retSD = f_open(&fil, filename, FA_READ);
	HAL_Delay(100);	
retSD = f_read(&fil, rtext, sizeof(rtext), (UINT*)&bytesread);
	HAL_Delay(100);	
retSD = f_close(&fil);

在线调试时用watch窗口查看读取的数组

当文件系统挂在不上,错误代码0x0D时,是SD需要格式化
retSD = f_mkfs("", FM_ANY, 0, work, sizeof work); 或者用电脑格式化

©️2020 CSDN 皮肤主题: 游动-白 设计师:上身试试 返回首页