前言
FATFS文件系统支持挂载多个存储设备,下面以挂载TF卡和Flash来说明FATFS挂载多个存储设备的方法。
1.底层设备驱动函数编写
(1)为存储设备定义编号
和PC上使用C开头表示盘符不同,FATFS使用数字表示存储设备,因此我们需要为物理设备定义编号。这里将TF卡定义为0,Flash定义为1:
#define TF 0 // TF卡
#define EX_FLASH 1 // 外部SPI Flash
(2)设备状态获取函数修改
这里主要根据存储设备编号不同返回存储设备是否存在的状态信息:
DSTATUS disk_status (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
DSTATUS stat;
switch (pdrv)
{
case TF:
stat = disk.drv[pdrv]->disk_status(disk.lun[pdrv]);
break;
case EX_FLASH:
// 读取Flash设备ID是否正确
if (SPI_FLASH_ReadID() == sFLASH_ID)
{
stat = ERR_OK;
}
else
{
stat = STA_NOINIT;
}
break;
default:
stat = STA_NOINIT;
break;
}
return stat;
}
(3)设备初始化
这里主要根据存储设备编号不同对存储设备进行初始化操作:
DSTATUS disk_initialize (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
DSTATUS stat = RES_OK;
switch (pdrv)
{
case TF:
if(disk.is_initialized[pdrv] == 0)
{
disk.is_initialized[pdrv] = 1;
stat = disk.drv[pdrv]->disk_initialize(disk.lun[pdrv]);
}
break;
case EX_FLASH:
// 初始化flash
SPI_FLASH_Init();
Delay_ms(10);
// 唤醒flash
SPI_Flash_WAKEUP();
stat = disk_status(EX_FLASH);
break;
default:
stat = STA_NOINIT;
break;
}
return stat;
}
(4)扇区读取
这里主要根据存储设备编号不同对存储设备的扇区进行读取操作:
DRESULT disk_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 */
)
{
DRESULT res;
switch (pdrv)
{
case TF:
res = disk.drv[pdrv]->disk_read(disk.lun[pdrv], buff, sector, count);
break;
case EX_FLASH:
SPI_FLASH_BufferRead(buff, sector * 4096, count * 4096);
res = RES_OK;
break;
default:
res = RES_ERROR;
break;
}
return res;
}
(5)扇区写入
这里主要根据存储设备编号不同对存储设备的扇区进行写入操作:
DRESULT disk_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 */
)
{
DRESULT res;
switch (pdrv)
{
case TF:
res = disk.drv[pdrv]->disk_write(disk.lun[pdrv], buff, sector, count);
break;
case EX_FLASH:
SPI_FLASH_SectorErase(sector * 4096);
SPI_FLASH_BufferWrite((u8 *)buff, sector * 4096, count * 4096);
res = RES_OK;
break;
default:
res = RES_ERROR;
break;
}
return res;
}
(6)I/O控制操作
这里主要根据存储设备编号不同,返回文件系统请求的存储设备信息:扇区数量、扇区大小等。
DRESULT disk_ioctl (
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
DRESULT res;
switch (pdrv)
{
case TF:
res = disk.drv[pdrv]->disk_ioctl(disk.lun[pdrv], cmd, buff);
break;
case EX_FLASH:
switch (cmd)
{
/* 扇区数量:4096*4096/1024/1024=16(MB) */
case GET_SECTOR_COUNT:
*(DWORD * )buff = 4096;
break;
/* 扇区大小 */
case GET_SECTOR_SIZE :
*(WORD * )buff = 4096;
break;
/* 同时擦除扇区个数 */
case GET_BLOCK_SIZE :
*(DWORD * )buff = 1;
break;
}
res = RES_OK;
break;
default:
res = RES_ERROR;
break;
}
return res;
}
(7)时间戳获取
这里我们提前开启了rtc功能,直接返回rtc寄存器数据即可:
__weak DWORD get_fattime (void)
{
RTC_DateTypeDef date;
RTC_TimeTypeDef time;
RTC_TimeAndDate_Get(&date, &time);
return ((DWORD)(date.Year + 2000 - 1980) << 25) // 从 1980 至今是多少年,范围是(0..127)
| ((DWORD)date.Month << 21) // 月份,范围为 (1..12)
| ((DWORD)date.Date << 16) // 日期,范围为 (1..31)
| ((DWORD)time.Hours << 11) // 时,范围为 (0..23)
| ((DWORD)time.Minutes << 5) // 分,范围为 (0..59)
| ((DWORD)time.Seconds / 2 >> 1); // 秒/ 2,范围为 (0..29)
}