本帖最后由 zero949079783 于 2021-12-12 15:52 编辑
开发环境:VSCODE(gcc编译链)+STM32CubeMX(也可以使用HUAWEI-LiteOS-Studio) 。 代码文件:SDIO_SD 代码:链接:https://pan.baidu.com/s/1uXfIR0GFQOBZPl1NfQP08w
提取码:6b0c
diskio.c和diskio.h是硬件层,需要根据存储介质来修改
ff.c和ff.h是FATFS的文件系统层和文件系统的API层
移植步骤:
1、数据类型:在integer.h 里面去定义好数据的类型。这里需要了解你用的编译器的数据类型,并根据编译器定义好数据类型。
2、配置:通过ffconf.h配置FATFS的相关功能,以满足你的需要。
3、函数编写:打开diskio.c,进行底层驱动编写,一般需要编写6 个接口函数
相关配置宏:
_FS_TINY mini版本的FATFS
_FS_READONLY 设置只读,可以减少所占的空间
_FS_MINIMIZE 削减函数
_USE_STRFUNC 字符及字符串操作函数
_USE_MKFS 是否启用格式化
_USE_FASTSEEK 使能快速定位
_USE_LABEL 是否支持磁盘盘符的设置和读取
_CODE_PAGE 设置语言936-中文GBK编码
_USE_LFN 是否支持长文件名,值不同存储的位置不同
_MAX_LFN 文件名的最大长度
_VOLUMES 支持的逻辑设备数目
_MAX_SS 扇区缓冲最大值,一般为512
文件名 功能 说明
ffconf.h FATFS模块配置文件 需要根据需求来配置参数。
ff.h FATFS和应用模块公用的包含文件 不需要修改
ff.c FATFS模块源码 不需要修改
diskio.h FATFS和disk I/O模块公用的包含文件 不需要修改
diskio.c FATFS和disk I/O模块接口层文件 与平台相关的代码,需要用户根据存储介质来编写函数。
interger.h 数据类型定义 与编译器有关。
option文件夹 可选的外部功能(比如支持中文等) 汉字实验把字库放到SPI FLASH需要修改
typedef enum {
FR_OK = 0, /* (0) Succeeded 成功*/
FR_DISK_ERR, /* (1) A hard error occured in the low level disk I/O layer 硬盘底层I/O层发生硬错误。 */
FR_INT_ERR, /* (2) Assertion failed由于一个错误的 FAT 结构或一个内部错误,而导致该函数失败。 */
FR_NOT_READY, /* (3) The physical drive cannot work */
FR_NO_FILE, /* (4) Could not find the file */
FR_NO_PATH, /* (5) Could not find the path */
FR_INVALID_NAME, /* (6) The path name format is invalid */
FR_DENIED, /* (7) Acces denied due to prohibited access or directory full */
FR_EXIST, /* (8) Acces denied due to prohibited access */
FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */
FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */
FR_INVALID_DRIVE, /* (11) The logical drive number is invalid 逻辑驱动器号无效 */
FR_NOT_ENABLED, /* (12) The volume has no work area */
FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume 磁盘上没有有效地 FAT 卷*/
FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any parameter error */
FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */
FR_LOCKED, /* (16) The operation is rejected according to the file shareing policy */
FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */
FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > _FS_SHARE */
FR_INVALID_PARAMETER /* (19) Given parameter is invalid */
} FRESULT;
返回值
FR_OK (0)
函数成功,该文件对象有效。
FR_NO_FILE
找不到该文件。
FR_NO_PATH
找不到该路径。
FR_INVALID_NAME
文件名无效。
FR_INVALID_DRIVE
驱动器号无效。
FR_EXIST
该文件已存在。
FR_DENIED
由于下列原因,所需的访问被拒绝:
n ▲ 以写模式打开一个只读文件。
n ▲ 由于存在一个同名的只读文件或目录,而导致文件无法被创建。
n ▲ 由于目录表或磁盘已满,而导致文件无法被创建。
FR_NOT_READY
由于驱动器中没有存储介质或任何其他原因,而导致磁盘驱动器无法工作。
FR_WRITE_PROTECTED
在存储介质被写保护的情况下,以写模式打开或创建文件对象。
FR_DISK_ERR
由于底层磁盘 I/O 接口函数中的一个错误,而导致该函数失败。
FR_INT_ERR
由于一个错误的 FAT 结构或一个内部错误,而导致该函数失败。
FR_NOT_ENABLED
逻辑驱动器没有工作区。
FR_NO_FILESYSTEM
磁盘上没有有效地 FAT 卷。
在官网有详细的使用指南,看着使用指南再对照源码就会基本掌握函数的使用。
几个重要结构体:
文件对象结构体(FIL类型):存放文件的相关信息,打开关闭读写文件等操作时需要使用其指针
目录对象结构体(DIR类型):存放目录的相关信息,对目录操作时需要其指针
文件状态结构体(FILINFO类型):存放文件的大小属性文件名等信息
文件系统对象结构体(FATFS类型):暂时没见怎么用过
文件的属性宏定义(用在打开时):
可以使用或运算符使得该文件具有多种性质,注意在读写时一定要以相应的属性打开文件
文件夹文件属性宏定义:
可以使用或运算符使得该文件具有多种性质,提供了函数可以修改文件的属性
注意:传参时的path(路径)应为一个字符串,是要操作的文件的完整路径,根目录0表示SD 卡,1表示外部SRAM
要注意数据类型的统一,在integer.h中定义的文件系统所用到的数据类型
大部分函数若执行成功返回0,若失败会返回一个错误码,该错误码为枚举类型(FRESULT)中的成员,在调试时打印错误码会事半功倍
printf("SD文件系统测试\r\n");
retSD = f_mount(&SDFatFS,(const TCHAR*)SDPath,1); //挂载
printf("\r\nmount_res = %d \r\n",retSD);
printf_fatfs_error(retSD);
if(retSD == FR_NO_FILESYSTEM) //无文件系统时,先格式化SD
{
//格式化SD�??
retSD= f_mkfs((const TCHAR*)SDPath,0,0);
printf("\r\n mkfs_res = %d \r\n",retSD);
if(retSD == FR_OK)
{
printf("\r\n FLASH 格式化成功\r\n");
}
}
else if(retSD == FR_OK)
{
retSD = f_open(&SDFile,"SD.txt",FA_OPEN_ALWAYS|FA_WRITE|FA_READ);
printf("\r\nf_open_res = %d \r\n",retSD);
//retUSER = f_write(&USERFile,wData,sizeof(wData),&bw); //写数据
f_lseek(&SDFile,0); //文件定位
f_printf(&SDFile,"Hello wrold\n");
printf("\r\n bw = %d \r\n",bw);
if(retSD == FR_OK)
{
f_lseek(&SDFile,0); //文件定位
retSD = f_read(&SDFile,rData,f_size(&SDFile),&br); //读取数据
if(retSD == FR_OK)
{
printf("\r\n 文件内容 %s br = %d\r\n",rData,br);
}
}
f_close(&SDFile); //关闭和保存文件
}
printf("\r\n-------------------------------------------------------\r\n");
printf("FLASH文件系统测试\r\n");
retUSER = f_mount(&USERFatFS,(const TCHAR*)USERPath,1); //挂载
printf("\r\nmount_res = %d \r\n",retUSER);
printf_fatfs_error(retUSER);
if(retUSER == FR_NO_FILESYSTEM) //无文件系统时,先格式化SD
{
//格式化
retUSER= f_mkfs((const TCHAR*)USERPath,0,0);
printf("\r\n mkfs_res = %d \r\n",retUSER);
if(retUSER == FR_OK)
{
printf("\r\n FLASH 格式化成功\r\n");
retUSER = f_mount(NULL,(const TCHAR*)USERPath,1); //取消挂载
retUSER = f_mount(&USERFatFS,(const TCHAR*)USERPath,1); //重新挂载
}
}
else if(retUSER == FR_OK)
{
retUSER = f_open(&USERFile,"FLASH.txt",FA_OPEN_ALWAYS|FA_WRITE|FA_READ);
printf("\r\nf_open_res = %d \r\n",retUSER);
//retUSER = f_write(&USERFile,wData,sizeof(wData),&bw); //写数据
f_lseek(&USERFile,0); //文件定位
f_printf(&USERFile,"Hello wrold\n");
printf("\r\n bw = %d \r\n",bw);
if(retUSER == FR_OK)
{
f_lseek(&USERFile,0); //文件定位
retUSER = f_read(&USERFile,rData,f_size(&USERFile),&br); //读取数据
if(retUSER == FR_OK)
{
printf("\r\n 文件内容 %s br = %d\r\n",rData,br);
}
}
f_close(&USERFile); //关闭和保存文件
}
FLASH:
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] 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;
SPI_Flash_WAKEUP();
if(SPI_FLASH_ReadDeviceID()== sFLASH_ID )
{
Stat =RES_OK;
}
return Stat;
/* USER CODE END INIT */
}
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] 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(SPI_FLASH_ReadDeviceID()== sFLASH_ID )
{
Stat =RES_OK;
}
return Stat;
/* USER CODE END STATUS */
}
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] 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 */
/* 扇区偏移2MB,外部Flash文件系统空间放在SPI Flash后面6MB空间 */
sector += 512;//偏移512扇区
SPI_FLASH_BufferRead(sector*4096,buff,count*4096);
return RES_OK;
/* 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 */
/* 扇区偏移2MB,外部Flash文件系统空间放在SPI Flash后面6MB空间 */
sector += 512;//偏移512扇区
SPI_FLASH_SectorErase(sector*4096);
SPI_FLASH_BufferWrite(sector*4096,(uint8_t *)buff,count*4096);
return RES_OK;
/* 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;
switch(cmd)
{
case GET_SECTOR_COUNT:
*(DWORD*)buff =1536;//返回扇区个数
break;
case GET_SECTOR_SIZE:
*(WORD*)buff =4096;//返回扇区大小
break;
case GET_BLOCK_SIZE:
*(DWORD*)buff =1; //返回擦除扇区最小个数
break;
}
res = RES_OK;
return res;
/* USER CODE END IOCTL */
}
|