Fatfs文件系统

一般用在读写SD卡,u盘等

主要文件:

ff.c
ff.h
ffconf.h
diskio.h
ffsystem.c
ffunicode.c
必须实现diskio.c对应diskio.h文件

FatFs会调用:

disk_initialize()
disk_status()
disk_read()
disk_write()
disk_ioctl()

使用FatFs的基本流程

移植
复制FatFs核心
+ 配置ffconf.h
+ 实现diskio.c
+ 实现SD卡底层驱动
+ 实现get_fattime
+ 加入构建系统
+ 初始化并f_mount

完成这些以后,才可以直接调用:

f_open()
f_read()
f_write()
f_close()
f_mkfs()

Diskio.c

/*-----------------------------------------------------------------------*/
/* Low level disk I/O module for FatFs  -  SD Card (SPI4) drive           */
/* 盘0 = SD 卡 (SPI4 模式), 作为唯一 FatFs 卷 (存储相片/USB 导出)         */
/*-----------------------------------------------------------------------*/
#include "ff.h"
#include "diskio.h"			/* FatFs lower layer API */
#include "string.h"
#include "mh2457.h"
#include "sd_spi.h"         /* SD_SPI_GetSectorCount */
#include "rtc_calendar.h"   /* BSP RTC calendar / FAT timestamp */
#include <stdbool.h>

/* 物理盘编号:0 = SD 卡 */
#define SD_PDRV       0

/* U盘模式互斥:USB 挂载 PC 时 SD 卡 FAT 卷独占给 USB MSD,
 * 拒绝一切设备侧读/写,防止本机与 PC 双写同一卷损坏 FAT、或任务上下文与
 * USB ISR 重入 SPI 总线。USB 自身在 ISR 内持锁(s_usbSdLocked)时放行。
 * 注意:此处跨层引用 usb_msd.c 的符号,用 extern 避免头路径耦合。
 * (与参考工程 JW-MCU-TM30/BSP/Source/diskio.c 的实现一致) */
extern bool usb_msd_is_active(void);
extern bool usb_msd_sd_locked(void);

/* SPI4/SD transaction lock shared by task-side FatFs and USB IRQ callbacks.
 * s_usbSdLocked only identifies the caller; it is not itself a lock.  Without
 * this guard USB1_IRQHandler could preempt an in-progress CMD17/CMD24 sequence
 * and inject a second SD command into the same SPI transaction. */
static volatile bool s_sdIoBusy = false;

static bool sd_io_try_lock(void)
{
	bool acquired = false;
	uint32_t primask = __get_PRIMASK();
	__disable_irq();
	if (!s_sdIoBusy && (!usb_msd_is_active() || usb_msd_sd_locked())) {
		s_sdIoBusy = true;
		acquired = true;
	}
	__set_PRIMASK(primask);
	return acquired;
}

static void sd_io_unlock(void)
{
	uint32_t primask = __get_PRIMASK();
	__disable_irq();
	s_sdIoBusy = false;
	__set_PRIMASK(primask);
}

/*-----------------------------------------------------------------------*/
/* 获取设备状态                                                          */
/*-----------------------------------------------------------------------*/
DSTATUS disk_status(
	BYTE pdrv		/* 物理编号 */
)
{
	if (pdrv != SD_PDRV) {
		return STA_NOINIT;
	}
	return SD_SPI_Status();
}

/*-----------------------------------------------------------------------*/
/* 设备初始化                                                            */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize(
	BYTE pdrv		/* 物理编号 */
)
{
	DSTATUS result;

	if (pdrv != SD_PDRV) {
		return STA_NOINIT;
	}

	/* SPI4 + GPIO + 电源已在 SD_SPI_Init() 中完成 (main 启动阶段调用)。
	 * 此处执行 SD 卡协议初始化。
	 *
	 * 与 disk_read/disk_write 共用同一把锁:SD 协议初始化同样是一段完整的
	 * 总线事务(CMD0..CMD9),U盘模式期间本机一律不得发起。不加锁的话,
	 * 安全性就只能依赖"调用它的时候 USB 恰好没在读"这种隐含前提。 */
	if (!sd_io_try_lock()) {
		return STA_NOINIT;
	}
	result = SD_SPI_Initialize();
	sd_io_unlock();
	return result;
}

/*-----------------------------------------------------------------------*/
/* 读扇区                                                                */
/*-----------------------------------------------------------------------*/
DRESULT disk_read(
	BYTE pdrv,		/* 物理编号 */
	BYTE* buff,		/* 数据缓冲区 */
	DWORD sector,	/* 起始扇区 */
	UINT count		/* 扇区个数 */
)
{
	if (pdrv != SD_PDRV || count == 0) {
		return RES_PARERR;
	}

	/* 原子地完成所有权检查和 SPI 事务加锁;既阻止本机在 UMS 期间进入,
	 * 也阻止 USB IRQ 嵌套进已经开始的本机 SD 命令。 */
	if (!sd_io_try_lock()) {
		return RES_NOTRDY;
	}

	DRESULT result = SD_SPI_ReadSectors(buff, sector, count);
	sd_io_unlock();
	return result;
}

/*-----------------------------------------------------------------------*/
/* 写扇区                                                                */
/*-----------------------------------------------------------------------*/
DRESULT disk_write(
	BYTE pdrv,				/* 物理编号 */
	const BYTE* buff,		/* 待写入数据 */
	DWORD sector,			/* 起始扇区 */
	UINT count				/* 扇区个数 */
)
{
	if (pdrv != SD_PDRV || count == 0) {
		return RES_PARERR;
	}

	if (!sd_io_try_lock()) {
		return RES_NOTRDY;
	}

	DRESULT result = SD_SPI_WriteSectors(buff, sector, count);
	sd_io_unlock();
	return result;
}

/*-----------------------------------------------------------------------*/
/* 其他控制                                                              */
/*-----------------------------------------------------------------------*/
DRESULT disk_ioctl(
	BYTE pdrv,		/* 物理编号 */
	BYTE cmd,		/* 控制命令 */
	void* buff		/* 结果缓冲区 */
)
{
	if (pdrv != SD_PDRV) {
		return RES_PARERR;
	}

	switch (cmd) {
		case GET_SECTOR_SIZE:	/* 返回扇区大小 (固定 512) */
			*(WORD*)buff = 512;
			return RES_OK;
		case GET_SECTOR_COUNT:	/* 返回卡总扇区数 (CSD 解析) */
			*(DWORD*)buff = SD_SPI_GetSectorCount();
			return RES_OK;
		case GET_BLOCK_SIZE:	/* 返回擦除块大小(以扇区为单位) */
			*(DWORD*)buff = 8;   /* SD 卡典型 4KB/512B */
			return RES_OK;
		case CTRL_SYNC:
			return RES_OK;
		case CTRL_TRIM:
			return RES_OK;
		default:
			return RES_PARERR;
	}
}

/*-----------------------------------------------------------------------*/
/* 获取当前 RTC 日历时间                                                  */
/*-----------------------------------------------------------------------*/
__WEAK DWORD get_fattime(void)
{
	return (DWORD)RtcCalendar_GetFatTime();
}