本次分享一下如何驱动SPI接口TF卡,如何实现TF卡读写移植FATFS文件系统。
板上TF卡接口:

电路部分如下,和SPI flash共用SPI接口,片选不同。

下面实现spi接口驱动TF。首先实现几个接口函数:
/* 片选控制 */
void SD_SPI_CS_Low(void); /* 拉低CS,选中TF卡 */
void SD_SPI_CS_High(void); /* 拉高CS,释放TF卡 */
/* SPI读写一个字节(同时收发) */
uint8_t SD_SPI_ReadWriteByte(uint8_t tx_data);
/* 时钟频率切换(初始化时必须≤400kHz,正常读写可提高) */
void SD_SPI_SetSpeedLow(void); /* ≤400kHz,用于初始化 */
void SD_SPI_SetSpeedHigh(void); /* 正常读写速度,建议≤25MHz */
/* 毫秒级延时(用于超时等待) */
void SD_SPI_DelayMs(uint32_t ms);
#include "main.h"
#include "sd_spi_interface.h"
#include "spi.h" /* HAL库生成的SPI句柄 */
#include "gpio.h" /* HAL库生成的GPIO定义 */
/* 用户根据实际工程修改以下宏 */
#define SD_SPI_HANDLE hspi1
#define SD_CS_PIN TF_CS_Pin
#define SD_CS_PORT TF_CS_GPIO_Port
void SD_SPI_CS_Low(void)
{
HAL_GPIO_WritePin(SD_CS_PORT, SD_CS_PIN, GPIO_PIN_RESET);
}
void SD_SPI_CS_High(void)
{
HAL_GPIO_WritePin(SD_CS_PORT, SD_CS_PIN, GPIO_PIN_SET);
}
uint8_t SD_SPI_ReadWriteByte(uint8_t tx_data)
{
uint8_t rx_data;
HAL_SPI_TransmitReceive(&SD_SPI_HANDLE, &tx_data, &rx_data, 1, 100);
return rx_data;
}
void SD_SPI_SetSpeedLow(void)
{
/* 设置为400kHz以下 */
SD_SPI_HANDLE.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
HAL_SPI_Init(&SD_SPI_HANDLE);
}
void SD_SPI_SetSpeedHigh(void)
{
/* 设置为较高速度,如18MHz(根据MCU调整) */
SD_SPI_HANDLE.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
HAL_SPI_Init(&SD_SPI_HANDLE);
}
void SD_SPI_DelayMs(uint32_t ms)
{
HAL_Delay(ms);
}
TF卡驱动部分代码如下:
/* 命令类型 SD/MMC command list - SPI mode */
#define CMD0 0 /* Reset */
#define CMD1 1 /* Send Operator Condition - SEND_OP_COND */
#define CMD8 8 /* Send Interface Condition - SEND_IF_COND */
#define CMD9 9 /* Read CSD */
#define CMD10 10 /* Read CID */
#define CMD12 12 /* Stop data transmit */
#define CMD16 16 /* Set block size, should return 0x00 */
#define CMD17 17 /* Read single block */
#define CMD18 18 /* Read multi block */
#define ACMD23 23 /* Prepare erase N-blokcs before multi block write (需先发CMD55)*/
#define CMD24 24 /* Write single block */
#define CMD25 25 /* Write multi block */
#define ACMD41 41 /* should return 0x00 (需先发CMD55)*/
#define CMD55 55 /* should return 0x01 */
#define CMD58 58 /* Read OCR */
#define CMD59 59 /* CRC disable/enbale, should return 0x00 */
/* 响应类型 */
#define R1_READY 0x00 /* 就绪 */
#define R1_IDLE 0x01 /* 卡处于空闲状态 */
#define R1_ILLEGAL_CMD 0x04 /* 非法命令 */
/* 卡类型 */
#define SD_TYPE_UNKNOWN 0
#define SD_TYPE_V1 1 /* SD卡 v1.x */
#define SD_TYPE_V2 2 /* SD卡 v2.0 */
#define SD_TYPE_HC 3 /* SDHC/SDXC */
#define SD_TYPE_MMC 4 /* MMC */
/* 函数声明 */
uint8_t SD_Init(void);
uint8_t SD_ReadBlock(uint32_t sector, uint8_t *buffer);
uint8_t SD_WriteBlock(uint32_t sector, const uint8_t *buffer);
uint64_t SD_GetSectorCount(void);
uint8_t SD_GetCardType(void);
#include "main.h"
#include "sd_driver.h"
#include "sd_spi_interface.h"
/* ---------- 私有宏 ---------- */
#define SD_DUMMY_BYTE 0xFF
#define SD_CMD_TIMEOUT 1000 /* 命令超时(ms) */
/* ---------- 私有变量 ---------- */
static uint8_t sd_card_type = SD_TYPE_UNKNOWN;
static uint64_t sd_sector_count = 0;
/* ---------- 私有函数 ---------- */
/* 向SD卡发送6字节命令,返回R1响应 */
static uint8_t SD_SendCmd(uint8_t cmd, uint32_t arg, uint8_t crc)
{
uint8_t cmd_frame[6];
uint8_t resp;
uint32_t timeout;
/* 构造命令帧:CMD + 4字节参数 + CRC */
cmd_frame[0] = cmd | 0x40; /* 起始位0 + 命令索引 */
cmd_frame[1] = (uint8_t)(arg >> 24);
cmd_frame[2] = (uint8_t)(arg >> 16);
cmd_frame[3] = (uint8_t)(arg >> 8);
cmd_frame[4] = (uint8_t)(arg >> 0);
cmd_frame[5] = crc;
/* 发送命令前先发若干dummy时钟,保证上一操作完成 */
SD_SPI_ReadWriteByte(SD_DUMMY_BYTE);
/* 片选有效,开始发送命令 */
SD_SPI_CS_Low();
/* 发送6字节命令 */
for (uint8_t i = 0; i < 6; i++)
{
SD_SPI_ReadWriteByte(cmd_frame[i]);
}
/* 等待R1响应(0x00~0xFF,最高位为0表示有效响应) */
timeout = SD_CMD_TIMEOUT;
do {
resp = SD_SPI_ReadWriteByte(SD_DUMMY_BYTE);
timeout--;
} while ((resp & 0x80) && timeout);
// printf("resp %d = %d \r\n",cmd, resp);
return resp;
}
/* 发送带APP前缀的命令(ACMD) */
static uint8_t SD_SendAppCmd(uint8_t cmd, uint32_t arg, uint8_t crc)
{
uint8_t resp;
/* 先发送CMD55,告诉SD卡下一条是应用命令 */
resp = SD_SendCmd(CMD55, 0, 0x65); /* CMD55的CRC固定为0x65 */
if (resp != R1_READY) {
return resp;
}
/* 发送实际的ACMD命令 */
return SD_SendCmd(cmd, arg, crc);
}
/* 等待SD卡退出忙状态(数据线变高) */
static uint8_t SD_WaitReady(void)
{
uint32_t timeout = 5000; /* 5ms * 5000 = 25秒超时 */
uint8_t resp;
do {
resp = SD_SPI_ReadWriteByte(SD_DUMMY_BYTE);
timeout--;
if (timeout == 0) {
return 0; /* 超时失败 */
}
} while (resp != SD_DUMMY_BYTE);
return 1; /* 成功 */
}
/**
* @brief 从CSD寄存器解析扇区总数
* @param csd 指向16字节CSD数据的指针
* @return 扇区总数(每扇区512字节)
*/
static uint64_t SD_ParseCSD(uint8_t *csd)
{
uint64_t sector_cnt = 0;
/* 检查CSD版本(位于第1字节的高2位) */
uint8_t csd_version = (csd[0] & 0xC0) >> 6;
if (csd_version == 0x01) /* CSD V2.0 —— SDHC / SDXC */
{
/* C_SIZE 为 22位,位于 Byte7[5:0]、Byte8[7:0]、Byte9[7:0] */
uint32_t c_size = ((uint32_t)(csd[7] & 0x3F) << 16) |
((uint32_t)csd[8] << 8) |
((uint32_t)csd[9]);
/* V2.0卡:扇区数 = (C_SIZE + 1) * 1024 */
sector_cnt = (uint64_t)(c_size + 1) * 1024;
}
else if (csd_version == 0x00) /* CSD V1.0 —— 标准容量卡 (≤2GB) */
{
/* READ_BL_LEN: 4位,位于 Byte5[3:0] */
uint8_t read_bl_len = csd[5] & 0x0F;
/* C_SIZE: 12位,位于 Byte6[1:0]、Byte7[7:0]、Byte8[7:6] */
uint16_t c_size = ((uint16_t)(csd[6] & 0x03) << 10) |
((uint16_t)csd[7] << 2) |
((uint16_t)(csd[8] & 0xC0) >> 6);
/* C_SIZE_MULT: 3位,位于 Byte9[1:0]、Byte10[7] */
uint8_t c_size_mult = ((csd[9] & 0x03) << 1) |
((csd[10] & 0x80) >> 7);
/* 总字节数 = (C_SIZE+1) * 2^(C_SIZE_MULT+2) * 2^(READ_BL_LEN) */
/* 扇区数(512B) = 总字节数 / 512 */
uint32_t exp = c_size_mult + 2 + read_bl_len - 9;
sector_cnt = (uint64_t)(c_size + 1) << exp;
}
else
{
/* 不支持的CSD版本 */
sector_cnt = 0;
}
return sector_cnt;
}
/* ---------- 公共函数 ---------- */
/**
* @brief 初始化SD卡,使其进入SPI模式
* @return 0=成功, 1=失败
*/
uint8_t SD_Init(void)
{
uint8_t ocr[4];
uint8_t resp;
uint32_t i;
printf("\r\nsd init start ...\r\n");
SD_SPI_SetSpeedLow();
SD_SPI_CS_High();
for (i = 0; i < 100; i++) SD_SPI_ReadWriteByte(SD_DUMMY_BYTE);
/* CMD0 */
i = 0;
do {
resp = SD_SendCmd(CMD0, 0, 0x95);
i++;
if(100 == i)
{
SD_SPI_CS_High();
printf("Reset card into IDLE state failed!\r\n");
return 1;
}
} while (resp != R1_IDLE);
/* CMD8 检测V2 */
resp = SD_SendCmd(CMD8, 0x000001AA, 0x87);
if (resp == R1_IDLE)
{
for (i = 0; i < 4; i++)
{
ocr[i] = SD_SPI_ReadWriteByte(SD_DUMMY_BYTE);
}
SD_SPI_CS_High();
printf("CMD8_C :");
for(i=0;i<4;i++)
{
printf("%02X ",ocr[i]);
}
printf("\r\n");
sd_card_type = SD_TYPE_V2;
} else
{
sd_card_type = SD_TYPE_V1;
SD_SPI_CS_High();
}
/* ACMD41 初始化 */
i = 0;
do {
resp = SD_SendAppCmd(ACMD41, 0x40000000, 0x77);
i++;
if (i >= 10)
{
SD_SPI_CS_High();
printf("ACMD41 failed!\r\n");
/* CMD1 MMC card initialize start*/
i = 0;
do {
resp = SD_SendCmd(CMD1, 0x40000000, 0x77);
i++;
if (i >= 10)
{
SD_SPI_CS_High();
printf("CMD1 failed!\r\n");
return 1;
}
} while (resp != R1_READY);
sd_card_type = SD_TYPE_MMC;
break;
}
} while (resp != R1_READY);
/* 判断HC卡 */
if (sd_card_type == SD_TYPE_V2)
{
resp = SD_SendCmd(CMD58, 0, 0xFD);
if (resp == R1_READY)
{
for (i = 0; i < 4; i++)
{
ocr[i] = SD_SPI_ReadWriteByte(SD_DUMMY_BYTE);
}
SD_SPI_CS_High();
printf("OCR_V2:");
for(i=0;i<4;i++)
{
printf("%02X ",ocr[i]);
}
printf("\r\n");
if (ocr[0] & 0x40) sd_card_type = SD_TYPE_HC;
}
SD_SPI_CS_High();
}
printf("sd_card_type = %d.\r\n",sd_card_type);
/* ====== 读取CSD获取容量 ====== */
uint8_t csd[16];
resp = SD_SendCmd(CMD9, 0, 0xFF);
if (resp == R1_READY)
{
i = 1000;
do {
resp = SD_SPI_ReadWriteByte(SD_DUMMY_BYTE);
i--;
} while ((resp != 0xFE) && i);
if (resp == 0xFE)
{
for (uint8_t idx = 0; idx < 16; idx++)
{
csd[idx] = SD_SPI_ReadWriteByte(SD_DUMMY_BYTE);
}
SD_SPI_ReadWriteByte(SD_DUMMY_BYTE);
SD_SPI_ReadWriteByte(SD_DUMMY_BYTE);
SD_SPI_CS_High();
printf("CSD_Tab:");
for(i=0;i<16;i++)
{
printf("%02X ",csd[i]);
}
printf("\r\n");
sd_sector_count = SD_ParseCSD(csd);
}
}
SD_SPI_CS_High();
printf("sd_sector_count = %u .\r\n",(uint32_t)sd_sector_count);
/* ====== 读取CID ====== */
resp = SD_SendCmd(CMD10, 0, 0xFF);
if (resp == R1_READY)
{
i = 1000;
do {
resp = SD_SPI_ReadWriteByte(SD_DUMMY_BYTE);
i--;
} while ((resp != 0xFE) && i);
if (resp == 0xFE)
{
for (uint8_t idx = 0; idx < 16; idx++)
{
csd[idx] = SD_SPI_ReadWriteByte(SD_DUMMY_BYTE);
}
SD_SPI_ReadWriteByte(SD_DUMMY_BYTE);
SD_SPI_ReadWriteByte(SD_DUMMY_BYTE);
SD_SPI_CS_High();
printf("CID_Tab:");
for(i=0;i<16;i++)
{
printf("%02X ",csd[i]);
}
printf("\r\n");
}
}
SD_SPI_CS_High();
/* 设置块大小 */
resp = SD_SendCmd(CMD16, 512, 0xFF);
SD_SPI_CS_High();
if (resp != R1_READY) return 1;
printf("sd init ok.\r\n");
SD_SPI_SetSpeedHigh();
return 0;
}
/**
* @brief 读取一个扇区(512字节)
* @param sector 扇区地址(对于SDHC卡为LBA地址,SD v1为字节地址/512)
* @param buffer 数据缓冲区(至少512字节)
* @return 0=成功, 1=失败
*/
uint8_t SD_ReadBlock(uint32_t sector, uint8_t *buffer)
{
uint8_t resp;
uint32_t i;
/* 如果是V1卡,扇区地址需转换为字节地址 */
uint32_t addr = sector;
if (sd_card_type != SD_TYPE_HC)
{
addr = sector << 9; /* 乘以512 */
}
/* 发送CMD17读单块命令 */
resp = SD_SendCmd(CMD17, addr, 0x00);
if (resp != R1_READY) {
SD_SPI_CS_High();
return 1;
}
/* 等待数据令牌(0xFE) */
i = 10000;
do {
resp = SD_SPI_ReadWriteByte(SD_DUMMY_BYTE);
i--;
if (i == 0)
{
SD_SPI_CS_High();
return 1;
}
} while (resp != 0xFE);
/* 读取512字节数据 */
for (i = 0; i < 512; i++)
{
buffer[i] = SD_SPI_ReadWriteByte(SD_DUMMY_BYTE);
}
/* 读取2字节CRC(忽略) */
SD_SPI_ReadWriteByte(SD_DUMMY_BYTE);
SD_SPI_ReadWriteByte(SD_DUMMY_BYTE);
/* 释放片选 */
SD_SPI_CS_High();
/* 发送CMD12结束命令 */
resp = SD_SendCmd(CMD12, 0, 0);
/* 释放片选 */
SD_SPI_CS_High();
return 0;
}
/**
* @brief 写入一个扇区(512字节)
* @param sector 扇区地址
* @param buffer 数据缓冲区(512字节)
* @return 0=成功, 1=失败
*/
uint8_t SD_WriteBlock(uint32_t sector, const uint8_t *buffer)
{
uint8_t resp;
uint16_t i;
/* 地址转换(同读操作) */
uint32_t addr = sector;
if (sd_card_type != SD_TYPE_HC)
{
addr = sector << 9;
}
/* 发送CMD24写单块命令 */
resp = SD_SendCmd(CMD24, addr, 0x00);
if (resp != R1_READY) {
SD_SPI_CS_High();
return 1;
}
/* Card enable, Prepare to write */
SD_SPI_CS_Low();
SD_SPI_ReadWriteByte(SD_DUMMY_BYTE);
SD_SPI_ReadWriteByte(SD_DUMMY_BYTE);
SD_SPI_ReadWriteByte(SD_DUMMY_BYTE);
/* 发送数据起始令牌 0xFE */
SD_SPI_ReadWriteByte(0xFE);
/* 发送512字节数据 */
for (i = 0; i < 512; i++) {
SD_SPI_ReadWriteByte(buffer[i]);
}
/* 发送2字节CRC(SPI模式下通常忽略,填0xFF) */
SD_SPI_ReadWriteByte(SD_DUMMY_BYTE);
SD_SPI_ReadWriteByte(SD_DUMMY_BYTE);
/* 读取数据响应(0x05=数据接受) */
resp = SD_SPI_ReadWriteByte(SD_DUMMY_BYTE);
if ((resp & 0x1F) != 0x05) {
SD_SPI_CS_High();
return 1;
}
/* 等待写完成(卡忙时输出0x00) */
if (!SD_WaitReady()) {
SD_SPI_CS_High();
return 1;
}
SD_SPI_CS_High();
return 0;
}
/**
* @brief 获取卡类型
*/
uint8_t SD_GetCardType(void)
{
return sd_card_type;
}
/**
* @brief 获取扇区总数(需通过CSD解析,此处为占位)
*/
uint64_t SD_GetSectorCount(void)
{
return sd_sector_count;
}
TF卡驱动部分注意:
| 初始化时钟 |
SD卡初始化时SPI时钟必须 ≤400kHz ,否则可能无法识别 |
| 片选控制 |
建议用独立GPIO 手动控制CS,不要使用硬件NSS自动模式 |
| 上电延时 |
上电后需发送至少74个时钟脉冲 让SD卡完成自检 |
| 命令CRC |
CMD0和CMD8等特定命令有固定CRC值,不可随意更改 |
| 速度切换 |
初始化完成后可提高SPI时钟,但建议不超过25MHz |
可以通过下面shell命令读TF内容:
//LSY_SHELL
#include "lsy_shell_core.h"
extern uint32_t str2hex(char *str);
int cmd_tf_read(int argc, char **argv)
{
uint32_t i,addr,size;
addr= 0; size = 512;
if(argc >= 2) addr = str2hex(argv[1]);
printf("tf_read [addr=0x%0X] [len:512].\r\n",addr);
/* read test */
if (SD_ReadBlock(addr >>9 , work_buffer) == 0)
{
printf("Read the tf card data success. Start from 0x%08X, size is %u. The data is:\r\n", addr, size);
printf("Offset (h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\r\n");
for (i = 0; i < size; i++) {
if (i % 16 == 0) {
printf("[%08X] ", addr + i);
}
printf("%02X ", work_buffer[i]);
if (((i + 1) % 16 == 0) || i == size - 1) {
printf("\r\n");
}
}
printf("\r\n");
} else {
printf("Read the tf card data failed.\r\n");
}
return SHELL_OK;
}
LSY_SHELL_CMD_EXPORT(tf_read, cmd_tf_read, "TF card read data.");
完成TF卡驱动初始化和读写之后就可以移植到FATFS文件系统上了。
首先就是实现 diskio.c 的核心接口函数。
#include "ff_gen_drv.h"
#define DEV_SD_CARD 0 // 为TF卡分配一个物理编号
/* 定义扇区大小,SD卡通常为512字节 */
#define SD_SECTOR_SIZE 512
/* Private function prototypes -----------------------------------------------*/
DSTATUS SD_initialize (BYTE);
DSTATUS SD_status (BYTE);
DRESULT SD_read (BYTE, BYTE*, DWORD, UINT);
#if _USE_WRITE == 1
DRESULT SD_write (BYTE, const BYTE*, DWORD, UINT);
#endif /* _USE_WRITE == 1 */
#if _USE_IOCTL == 1
DRESULT SD_ioctl (BYTE, BYTE, void*);
#endif /* _USE_IOCTL == 1 */
const Diskio_drvTypeDef SD_Driver =
{
SD_initialize,
SD_status,
SD_read,
#if _USE_WRITE == 1
SD_write,
#endif /* _USE_WRITE == 1 */
#if _USE_IOCTL == 1
SD_ioctl,
#endif /* _USE_IOCTL == 1 */
};
/**
* @brief Initializes a Drive
* @param lun : not used
* @retval DSTATUS: Operation status
*/
DSTATUS SD_initialize(
BYTE pdrv /* 物理驱动编号 */
)
{
DSTATUS status = STA_NOINIT;
switch (pdrv) {
case DEV_SD_CARD:
/* 调用之前实现的SD卡初始化函数 */
/* 假设初始化成功返回0,失败返回1 */
if (SD_Init() == 0) {
status &= ~STA_NOINIT; // 清除“未初始化”标志
} else {
status = STA_NOINIT;
}
break;
default:
status = STA_NOINIT;
}
return status;
}
/**
* @brief Gets Disk Status
* @param lun : not used
* @retval DSTATUS: Operation status
*/
DSTATUS SD_status(
BYTE pdrv /* 物理驱动编号 */
)
{
DSTATUS status = STA_NOINIT;
switch (pdrv) {
case DEV_SD_CARD:
/* 简单起见,我们认为只要驱动能工作,设备就是正常的 */
/* 更严谨的做法是调用驱动中的检测函数,比如 SD_IsDetected() */
status &= ~STA_NOINIT;
break;
default:
status = STA_NOINIT;
}
return status;
}
/**
* @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 pdrv, /* 物理驱动编号 */
BYTE *buff, /* 存放读取数据的缓冲区指针 */
DWORD sector, /* 起始扇区地址 (LBA) */
UINT count /* 要读取的扇区数量 */
)
{
DRESULT res = RES_OK;
switch (pdrv) {
case DEV_SD_CARD:
/* 循环读取每个扇区 */
for (UINT i = 0; i < count; i++) {
/* 调用驱动中的读扇区函数 */
/* 假设成功返回0,失败返回非0 */
if (SD_ReadBlock(sector + i, buff + (i * SD_SECTOR_SIZE)) != 0) {
res = RES_ERROR;
break;
}
}
break;
default:
res = RES_PARERR; // 参数错误
}
return res;
}
/**
* @brief Writes Sector(s)
* @param lun : not used
* @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 SD_write(
BYTE pdrv, /* 物理驱动编号 */
const BYTE *buff, /* 要写入数据的缓冲区指针 */
DWORD sector, /* 起始扇区地址 (LBA) */
UINT count /* 要写入的扇区数量 */
)
{
DRESULT res = RES_OK;
switch (pdrv) {
case DEV_SD_CARD:
for (UINT i = 0; i < count; i++) {
/* 调用驱动中的写扇区函数 */
if (SD_WriteBlock(sector + i, buff + (i * SD_SECTOR_SIZE)) != 0) {
res = RES_ERROR;
break;
}
}
break;
default:
res = RES_PARERR;
}
return res;
}
#endif /* _USE_WRITE == 1 */
/**
* @brief I/O control operation
* @param lun : not used
* @param cmd: Control code
* @param *buff: Buffer to send/receive control data
* @retval DRESULT: Operation result
*/
#if _USE_IOCTL == 1
DRESULT SD_ioctl(
BYTE pdrv, /* 物理驱动编号 */
BYTE cmd, /* 控制命令 */
void *buff /* 命令相关的数据缓冲区 */
)
{
DRESULT res = RES_OK;
switch (pdrv) {
case DEV_SD_CARD:
switch (cmd) {
case CTRL_SYNC: /* 确保所有写操作已完成 */
/* SPI模式无需额外操作,SD总线模式可调用等待函数 */
res = RES_OK;
break;
case GET_SECTOR_SIZE: /* 获取扇区大小 */
*(DWORD*)buff = SD_SECTOR_SIZE;
break;
case GET_BLOCK_SIZE: /* 获取擦除块大小(扇区为单位),通常与GET_SECTOR_SIZE相同 */
*(DWORD*)buff = SD_SECTOR_SIZE;
break;
case GET_SECTOR_COUNT: /* 获取总扇区数 */
*(DWORD*)buff = (DWORD)SD_GetSectorCount();
break;
default:
res = RES_PARERR;
break;
}
break;
default:
res = RES_PARERR;
}
return res;
}
#endif /* _USE_IOCTL == 1 */
再就是配置 ffconf.h
ffconf.h 是FatFS的配置文件,你需要根据需求进行配置。
_USE_WRITE :设置为 1 以启用写操作功能。
_VOLUMES :设置逻辑驱动器的数量,这里设置为2,支持SPI FLASH和TF卡2个。
_MAX_SS :设置扇区缓冲区大小。对于SD卡,通常设为 512。
_FS_MINIMUM :如果你只需要最基本的文件读写功能,可以设置为 1 或 2 以减小代码体积。
最后就是链接到文件系统,挂载驱动器。
FATFS_LinkDriver(&USER_Driver, USERPath);
FATFS_LinkDriver(&SD_Driver , USERPath);

void fatfs_test_demo(void)
{
FRESULT res;
FIL fsrc; /* file objects */
UINT br;
const uint8_t textFileBuffer[] = "Thank you for using Development Board ^_^ \r\n";
res = f_mount(&fs,"0:",1);
printf("f_mount disk0 res=%d!\r\n",res);
if(res == FR_NO_FILESYSTEM)
{
sfud_chip_erase((sfud_flash *)sfud_get_device_table());
res = f_mkfs("0:",FM_FAT,4096,work_buffer,4096);
printf("f_mkfs disk0 res=%d!\r\n",res);
res = f_mount(&fs,"0:",1);
printf("f_mount disk0 res2=%d!\r\n",res);
}
if(res == 0)
{
res = f_open( &fsrc , "0:/demo.txt" , FA_OPEN_APPEND | FA_WRITE | FA_READ);
if ( res == FR_OK )
{
/* Write buffer to file */
res = f_write(&fsrc, textFileBuffer, sizeof(textFileBuffer), &br);
printf("Demo.TXT successfully created \r\n");
}else if ( res == FR_EXIST )
{
/* Write buffer to file */
res = f_write(&fsrc, textFileBuffer, sizeof(textFileBuffer), &br);
printf("Demo.TXT created in the disk1 \r\n");
}else
{
printf("Demo.TXT created in the disk1 error %d.\r\n",res);
}
/*close file */
f_close(&fsrc);
cur_path[0]='0';
cur_path[1]=':';
cur_path[2]='/';
cur_path[3]= 0;
disk_total_size(cur_path);
}
res = f_mount(&fs,"1:",1);
printf("f_mount disk1 res=%d!\r\n",res);
if(res == FR_NO_FILESYSTEM)
{
printf("f_mkfs disk1 res=%d!\r\n",res);
res = f_mount(&fs,"1:",1);
printf("f_mount disk0 res2=%d!\r\n",res);
}
if(res == 0)
{
cur_path[0]='1';
cur_path[1]=':';
cur_path[2]='/';
cur_path[3]= 0;
disk_total_size(cur_path);
}
}
最终编译下载之后就可以正常挂载2个存储器了。

