文章目录
-
- 一、前言
-
- [1.1 技术背景](#1.1 技术背景)
- [1.2 本文目标](#1.2 本文目标)
- [1.3 技术栈](#1.3 技术栈)
- 二、环境准备
-
- [2.1 硬件准备](#2.1 硬件准备)
- [2.2 SD卡简介](#2.2 SD卡简介)
- 三、项目创建与配置
-
- [3.1 创建STM32CubeIDE工程](#3.1 创建STM32CubeIDE工程)
- [3.2 配置SPI接口](#3.2 配置SPI接口)
- [3.3 配置FatFs](#3.3 配置FatFs)
- [3.4 生成代码](#3.4 生成代码)
- 四、SD卡驱动实现
-
- [4.1 SD卡命令定义](#4.1 SD卡命令定义)
- [4.2 FatFs磁盘接口](#4.2 FatFs磁盘接口)
- [4.3 文件操作示例](#4.3 文件操作示例)
- [4.4 主程序](#4.4 主程序)
- [4.5 系统架构流程图](#4.5 系统架构流程图)
- 五、编译与下载
-
- [5.1 编译工程](#5.1 编译工程)
- [5.2 测试步骤](#5.2 测试步骤)
- 六、故障排查与问题解决
-
- [6.1 SD卡初始化失败](#6.1 SD卡初始化失败)
- [6.2 文件系统挂载失败](#6.2 文件系统挂载失败)
- 七、总结
-
- [7.1 核心知识点回顾](#7.1 核心知识点回顾)
- [7.2 扩展学习方向](#7.2 扩展学习方向)
- [7.3 学习资源](#7.3 学习资源)
一、前言
1.1 技术背景
在嵌入式系统中,文件系统是管理存储设备数据的重要组成部分。FatFs是一个开源的FAT文件系统模块,专为小型嵌入式系统设计,具有代码量小、可移植性强、功能完善等特点。结合SD卡(Secure Digital Card),可以实现大容量数据存储功能。
STM32F103系列微控制器虽然资源有限,但通过SPI接口与SD卡通信,配合FatFs文件系统,完全可以实现文件读写、目录管理等操作。
1.2 本文目标
通过本教程,你将学会:
- SD卡的工作原理和通信协议
- FatFs文件系统的移植
- SPI接口驱动SD卡
- 文件读写操作
- 目录管理
1.3 技术栈
硬件平台:
- STM32F103C8T6(主控芯片)
- SD卡模块(SPI模式)
- 8GB MicroSD卡
软件环境:
- STM32CubeIDE v1.10.0+
- FatFs R0.12c+
- HAL库
二、环境准备
2.1 硬件准备
| 设备 | 数量 | 说明 |
|---|---|---|
| STM32F103C8T6最小系统板 | 1块 | 主控芯片,72MHz |
| SD卡模块 | 1个 | SPI接口 |
| MicroSD卡 | 1张 | 8GB或更大 |
| ST-Link V2 | 1个 | 程序下载 |
硬件连接图:
STM32F103C8T6 SD卡模块
PA4 ─────────── CS
PA5 ─────────── SCK
PA6 ─────────── MISO
PA7 ─────────── MOSI
3.3V ─────────── VCC
GND ─────────── GND
2.2 SD卡简介
SD卡支持两种通信模式:
- SD模式:4位并行数据传输,速度快
- SPI模式:串行通信,引脚少,适合单片机
本文使用SPI模式,引脚定义:
- CS:片选信号(低电平有效)
- SCK:时钟信号
- MOSI:主出从入
- MISO:主入从出
- VCC:3.3V电源
- GND:地
三、项目创建与配置
3.1 创建STM32CubeIDE工程
- 打开STM32CubeIDE
- 创建新工程,选择STM32F103C8T6
- 工程名称:STM32_FatFs_SD
3.2 配置SPI接口
-
打开
.ioc文件 -
配置SPI1:
- PA4:GPIO_Output(软件CS)
- PA5:SPI1_SCK
- PA6:SPI1_MISO
- PA7:SPI1_MOSI
-
SPI参数配置:
Mode: Full-Duplex Master
Hardware NSS: Disable
Frame Format: Motorola
Data Size: 8 bits
First Bit: MSB First
Clock Polarity: Low
Clock Phase: 1 Edge
Baud Rate: 281.25 Kbits/s(初始化时低速)
3.3 配置FatFs
- 在CubeMX中,点击
Middleware -> FATFS - 选择
User-defined - 配置参数:
Generic配置:
FS_READONLY: Disabled
FS_MINIMIZE: 0
USE_STRFUNC: 1
USE_FIND: 0
USE_MKFS: 1
USE_FASTSEEK: 0
USE_EXPAND: 0
USE_CHMOD: 0
USE_LABEL: 0
USE_FORWARD: 0
Locale and Namespace配置:
CODE_PAGE: 936(简体中文)
USE_LFN: 1
MAX_LFN: 255
LFN_UNICODE: 0
Physical Drive配置:
VOLUMES: 1
MAX_SS: 512
MIN_SS: 512
MULTI_PARTITION: 0
3.4 生成代码
- 点击
Project -> Generate Code - 等待代码生成完成
四、SD卡驱动实现
4.1 SD卡命令定义
📄 创建文件:
Core/Src/sd_driver/sd_spi.c
c
/*
* sd_spi.c - SD卡SPI驱动
*
* 功能:
* - SD卡初始化
* - SPI通信接口
* - 块读写操作
*/
#include "sd_spi.h"
#include "stm32f1xx_hal.h"
#include "spi.h"
#include <string.h>
/* SD卡命令 */
#define CMD0 0 /* GO_IDLE_STATE */
#define CMD1 1 /* SEND_OP_COND */
#define CMD8 8 /* SEND_IF_COND */
#define CMD9 9 /* SEND_CSD */
#define CMD10 10 /* SEND_CID */
#define CMD12 12 /* STOP_TRANSMISSION */
#define CMD16 16 /* SET_BLOCKLEN */
#define CMD17 17 /* READ_SINGLE_BLOCK */
#define CMD18 18 /* READ_MULTIPLE_BLOCK */
#define CMD24 24 /* WRITE_BLOCK */
#define CMD25 25 /* WRITE_MULTIPLE_BLOCK */
#define CMD55 55 /* APP_CMD */
#define CMD58 58 /* READ_OCR */
#define ACMD41 41 /* SD_SEND_OP_COND */
/* SD卡类型 */
#define SD_TYPE_MMC 0
#define SD_TYPE_V1 1
#define SD_TYPE_V2 2
#define SD_TYPE_V2HC 3
/* 引脚定义 */
#define SD_CS_PORT GPIOA
#define SD_CS_PIN GPIO_PIN_4
/* SPI句柄 */
extern SPI_HandleTypeDef hspi1;
/* SD卡状态 */
static uint8_t sd_type = 0;
static uint8_t sd_ready = 0;
/* 函数声明 */
static void sd_select(void);
static void sd_deselect(void);
static uint8_t sd_spi_transfer(uint8_t data);
static uint8_t sd_send_cmd(uint8_t cmd, uint32_t arg);
static uint8_t sd_wait_ready(void);
/* CS引脚控制 */
static void sd_select(void)
{
HAL_GPIO_WritePin(SD_CS_PORT, SD_CS_PIN, GPIO_PIN_RESET);
}
static void sd_deselect(void)
{
HAL_GPIO_WritePin(SD_CS_PORT, SD_CS_PIN, GPIO_PIN_SET);
}
/* SPI数据传输 */
static uint8_t sd_spi_transfer(uint8_t data)
{
uint8_t rx_data;
HAL_SPI_TransmitReceive(&hspi1, &data, &rx_data, 1, HAL_MAX_DELAY);
return rx_data;
}
/* 发送命令 */
static uint8_t sd_send_cmd(uint8_t cmd, uint32_t arg)
{
uint8_t buf[6];
uint8_t result;
uint8_t n;
/* 如果是停止命令,需要额外时钟 */
if (cmd & 0x80) {
cmd &= 0x7F;
result = sd_send_cmd(CMD12, 0);
if (result > 1) return result;
}
/* 片选 */
sd_deselect();
sd_spi_transfer(0xFF);
sd_select();
sd_spi_transfer(0xFF);
/* 发送命令包 */
buf[0] = 0x40 | cmd;
buf[1] = (uint8_t)(arg >> 24);
buf[2] = (uint8_t)(arg >> 16);
buf[3] = (uint8_t)(arg >> 8);
buf[4] = (uint8_t)arg;
/* CRC */
if (cmd == CMD0) buf[5] = 0x95;
else if (cmd == CMD8) buf[5] = 0x87;
else buf[5] = 0x01;
for (n = 0; n < 6; n++) {
sd_spi_transfer(buf[n]);
}
/* 等待响应 */
n = 10;
do {
result = sd_spi_transfer(0xFF);
} while ((result & 0x80) && --n);
return result;
}
/* 等待就绪 */
static uint8_t sd_wait_ready(void)
{
uint8_t result;
uint32_t timeout = 500000;
sd_spi_transfer(0xFF);
do {
result = sd_spi_transfer(0xFF);
} while ((result != 0xFF) && --timeout);
return result;
}
/* SD卡初始化 */
uint8_t sd_init(void)
{
uint8_t n, cmd, ty, ocr[4];
uint32_t timeout;
/* 初始化CS引脚 */
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin = SD_CS_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(SD_CS_PORT, &GPIO_InitStruct);
sd_deselect();
/* 发送至少74个时钟 */
for (n = 0; n < 10; n++) {
sd_spi_transfer(0xFF);
}
ty = 0;
/* 进入空闲状态 */
if (sd_send_cmd(CMD0, 0) == 1) {
timeout = 1000;
/* 检查SD卡版本 */
if (sd_send_cmd(CMD8, 0x1AA) == 1) {
/* SD V2 */
for (n = 0; n < 4; n++) {
ocr[n] = sd_spi_transfer(0xFF);
}
/* 检查电压范围 */
if (ocr[2] == 0x01 && ocr[3] == 0xAA) {
/* 激活初始化 */
do {
if (sd_send_cmd(CMD55, 0) <= 1 &&
sd_send_cmd(ACMD41, 0x40000000) == 0) {
break;
}
} while (--timeout);
/* 检查CCS */
if (timeout && sd_send_cmd(CMD58, 0) == 0) {
for (n = 0; n < 4; n++) {
ocr[n] = sd_spi_transfer(0xFF);
}
ty = (ocr[0] & 0x40) ? SD_TYPE_V2HC : SD_TYPE_V2;
}
}
} else {
/* SD V1 或 MMC */
if (sd_send_cmd(CMD55, 0) <= 1 && sd_send_cmd(ACMD41, 0) <= 1) {
/* SD V1 */
ty = SD_TYPE_V1;
do {
if (sd_send_cmd(CMD55, 0) <= 1 &&
sd_send_cmd(ACMD41, 0) == 0) {
break;
}
} while (--timeout);
} else {
/* MMC */
ty = SD_TYPE_MMC;
do {
if (sd_send_cmd(CMD1, 0) == 0) {
break;
}
} while (--timeout);
}
/* 设置块大小 */
if (timeout && sd_send_cmd(CMD16, 512) != 0) {
ty = 0;
}
}
}
sd_deselect();
sd_spi_transfer(0xFF);
sd_type = ty;
sd_ready = (ty > 0) ? 1 : 0;
return sd_ready;
}
/* 获取SD卡状态 */
uint8_t sd_get_status(void)
{
return sd_ready;
}
/* 获取SD卡类型 */
uint8_t sd_get_type(void)
{
return sd_type;
}
/* 读取单个块 */
uint8_t sd_read_block(uint8_t *buf, uint32_t sector)
{
uint8_t result;
uint16_t i;
if (!sd_ready) return 1;
/* 转换扇区地址 */
if (sd_type != SD_TYPE_V2HC) {
sector <<= 9;
}
result = sd_send_cmd(CMD17, sector);
if (result != 0) {
sd_deselect();
return 1;
}
/* 等待数据令牌 */
uint32_t timeout = 100000;
do {
result = sd_spi_transfer(0xFF);
} while (result == 0xFF && --timeout);
if (result != 0xFE) {
sd_deselect();
return 1;
}
/* 读取数据 */
for (i = 0; i < 512; i++) {
buf[i] = sd_spi_transfer(0xFF);
}
/* 读取CRC */
sd_spi_transfer(0xFF);
sd_spi_transfer(0xFF);
sd_deselect();
sd_spi_transfer(0xFF);
return 0;
}
/* 写入单个块 */
uint8_t sd_write_block(const uint8_t *buf, uint32_t sector)
{
uint8_t result;
uint16_t i;
if (!sd_ready) return 1;
/* 转换扇区地址 */
if (sd_type != SD_TYPE_V2HC) {
sector <<= 9;
}
result = sd_send_cmd(CMD24, sector);
if (result != 0) {
sd_deselect();
return 1;
}
/* 发送数据令牌 */
sd_spi_transfer(0xFE);
/* 发送数据 */
for (i = 0; i < 512; i++) {
sd_spi_transfer(buf[i]);
}
/* 发送CRC */
sd_spi_transfer(0xFF);
sd_spi_transfer(0xFF);
/* 检查响应 */
result = sd_spi_transfer(0xFF);
if ((result & 0x1F) != 0x05) {
sd_deselect();
return 1;
}
/* 等待写入完成 */
if (sd_wait_ready() != 0xFF) {
sd_deselect();
return 1;
}
sd_deselect();
sd_spi_transfer(0xFF);
return 0;
}
📄 创建文件:
Core/Inc/sd_driver/sd_spi.h
c
/*
* sd_spi.h - SD卡SPI驱动头文件
*/
#ifndef __SD_SPI_H__
#define __SD_SPI_H__
#include <stdint.h>
/* SD卡初始化 */
uint8_t sd_init(void);
/* 获取SD卡状态 */
uint8_t sd_get_status(void);
/* 获取SD卡类型 */
uint8_t sd_get_type(void);
/* 读取单个块 */
uint8_t sd_read_block(uint8_t *buf, uint32_t sector);
/* 写入单个块 */
uint8_t sd_write_block(const uint8_t *buf, uint32_t sector);
#endif /* __SD_SPI_H__ */
4.2 FatFs磁盘接口
📄 创建文件:
Core/Src/fatfs_diskio.c
c
/*
* fatfs_diskio.c - FatFs磁盘I/O接口
*/
#include "ff.h"
#include "diskio.h"
#include "sd_spi.h"
/* 磁盘状态 */
static volatile DSTATUS Stat = STA_NOINIT;
/* 获取磁盘状态 */
DSTATUS disk_status(BYTE pdrv)
{
if (pdrv != 0) return STA_NOINIT;
return Stat;
}
/* 初始化磁盘 */
DSTATUS disk_initialize(BYTE pdrv)
{
if (pdrv != 0) return STA_NOINIT;
if (sd_init()) {
Stat &= ~STA_NOINIT;
}
return Stat;
}
/* 读取扇区 */
DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count)
{
if (pdrv != 0 || !sd_get_status()) return RES_NOTRDY;
for (UINT i = 0; i < count; i++) {
if (sd_read_block(buff + i * 512, sector + i) != 0) {
return RES_ERROR;
}
}
return RES_OK;
}
/* 写入扇区 */
DRESULT disk_write(BYTE pdrv, const BYTE *buff, LBA_t sector, UINT count)
{
if (pdrv != 0 || !sd_get_status()) return RES_NOTRDY;
for (UINT i = 0; i < count; i++) {
if (sd_write_block(buff + i * 512, sector + i) != 0) {
return RES_ERROR;
}
}
return RES_OK;
}
/* 控制函数 */
DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void *buff)
{
if (pdrv != 0) return RES_PARERR;
switch (cmd) {
case CTRL_SYNC:
/* 同步 */
return RES_OK;
case GET_SECTOR_COUNT:
/* 获取扇区数量 */
*(DWORD *)buff = 0; /* 需要实现 */
return RES_OK;
case GET_SECTOR_SIZE:
/* 获取扇区大小 */
*(WORD *)buff = 512;
return RES_OK;
case GET_BLOCK_SIZE:
/* 获取块大小 */
*(DWORD *)buff = 1;
return RES_OK;
default:
return RES_PARERR;
}
}
/* 获取当前时间 */
DWORD get_fattime(void)
{
/* 返回固定时间 */
return ((2026 - 1980) << 25) | /* 年 */
(4 << 21) | /* 月 */
(13 << 16) | /* 日 */
(12 << 11) | /* 时 */
(0 << 5) | /* 分 */
(0 >> 1); /* 秒 */
}
4.3 文件操作示例
📄 创建文件:
Core/Src/file_operations.c
c
/*
* file_operations.c - 文件操作示例
*/
#include "file_operations.h"
#include "ff.h"
#include <stdio.h>
#include <string.h>
/* 文件系统对象 */
static FATFS fs;
static FIL file;
/* 初始化文件系统 */
FRESULT file_system_init(void)
{
FRESULT res;
/* 挂载文件系统 */
res = f_mount(&fs, "", 1);
if (res == FR_NO_FILESYSTEM) {
/* 需要格式化 */
printf("No filesystem, formatting...\\n");
uint8_t work[512];
res = f_mkfs("", FM_FAT32, 0, work, sizeof(work));
if (res != FR_OK) {
printf("Format failed: %d\\n", res);
return res;
}
/* 重新挂载 */
res = f_mount(&fs, "", 1);
}
if (res == FR_OK) {
printf("Filesystem mounted successfully\\n");
} else {
printf("Mount failed: %d\\n", res);
}
return res;
}
/* 写入文件 */
FRESULT file_write_test(void)
{
FRESULT res;
UINT bw;
const char *text = "Hello, STM32 FatFs!\\r\\nThis is a test file.\\r\\n";
/* 打开文件 */
res = f_open(&file, "test.txt", FA_WRITE | FA_CREATE_ALWAYS);
if (res != FR_OK) {
printf("Open file failed: %d\\n", res);
return res;
}
/* 写入数据 */
res = f_write(&file, text, strlen(text), &bw);
if (res != FR_OK) {
printf("Write failed: %d\\n", res);
f_close(&file);
return res;
}
printf("Written %u bytes\\n", bw);
/* 关闭文件 */
f_close(&file);
return FR_OK;
}
/* 读取文件 */
FRESULT file_read_test(void)
{
FRESULT res;
UINT br;
char buffer[256];
/* 打开文件 */
res = f_open(&file, "test.txt", FA_READ);
if (res != FR_OK) {
printf("Open file failed: %d\\n", res);
return res;
}
/* 读取数据 */
res = f_read(&file, buffer, sizeof(buffer) - 1, &br);
if (res != FR_OK) {
printf("Read failed: %d\\n", res);
f_close(&file);
return res;
}
buffer[br] = '\\0';
printf("Read %u bytes:\\n%s\\n", br, buffer);
/* 关闭文件 */
f_close(&file);
return FR_OK;
}
/* 目录操作 */
FRESULT directory_test(void)
{
FRESULT res;
DIR dir;
FILINFO fno;
/* 创建目录 */
res = f_mkdir("/data");
if (res != FR_OK && res != FR_EXIST) {
printf("Create directory failed: %d\\n", res);
return res;
}
/* 打开目录 */
res = f_opendir(&dir, "/");
if (res != FR_OK) {
printf("Open directory failed: %d\\n", res);
return res;
}
/* 列出文件 */
printf("Directory listing:\\n");
while (1) {
res = f_readdir(&dir, &fno);
if (res != FR_OK || fno.fname[0] == 0) break;
printf(" %c %10lu %s\\n",
(fno.fattrib & AM_DIR) ? 'D' : 'F',
fno.fsize, fno.fname);
}
f_closedir(&dir);
return FR_OK;
}
/* 获取SD卡信息 */
void sd_card_info(void)
{
FATFS *pfs;
DWORD fre_clust, fre_sect, tot_sect;
FRESULT res;
/* 获取空闲空间 */
res = f_getfree("", &fre_clust, &pfs);
if (res == FR_OK) {
tot_sect = (pfs->n_fatent - 2) * pfs->csize;
fre_sect = fre_clust * pfs->csize;
printf("SD Card Info:\\n");
printf(" Total: %lu MB\\n", tot_sect / 2048);
printf(" Free: %lu MB\\n", fre_sect / 2048);
}
}
📄 创建文件:
Core/Inc/file_operations.h
c
/*
* file_operations.h - 文件操作头文件
*/
#ifndef __FILE_OPERATIONS_H__
#define __FILE_OPERATIONS_H__
#include "ff.h"
/* 初始化文件系统 */
FRESULT file_system_init(void);
/* 写入文件测试 */
FRESULT file_write_test(void);
/* 读取文件测试 */
FRESULT file_read_test(void);
/* 目录操作测试 */
FRESULT directory_test(void);
/* 获取SD卡信息 */
void sd_card_info(void);
#endif /* __FILE_OPERATIONS_H__ */
4.4 主程序
📝 修改文件:
Core/Src/main.c
c
/*
* main.c - 主程序入口
*/
#include "main.h"
#include "sd_spi.h"
#include "file_operations.h"
#include <stdio.h>
/* 私有函数声明 */
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_SPI1_Init(void);
static void MX_USART1_UART_Init(void);
/* 重定向printf */
int _write(int file, char *ptr, int len)
{
HAL_UART_Transmit(&huart1, (uint8_t *)ptr, len, HAL_MAX_DELAY);
return len;
}
/* 主函数 */
int main(void)
{
/* MCU配置 */
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_SPI1_Init();
MX_USART1_UART_Init();
printf("\\n");
printf("========================================\\n");
printf(" STM32 FatFs SD Card Demo\\n");
printf("========================================\\n");
printf("\\n");
/* 初始化SD卡 */
printf("Initializing SD card...\\n");
if (!sd_init()) {
printf("SD card init failed!\\n");
Error_Handler();
}
printf("SD card initialized, type: %d\\n", sd_get_type());
/* 初始化文件系统 */
printf("Mounting filesystem...\\n");
if (file_system_init() != FR_OK) {
printf("Filesystem init failed!\\n");
Error_Handler();
}
/* 显示SD卡信息 */
sd_card_info();
/* 写入测试 */
printf("\\nWriting test file...\\n");
file_write_test();
/* 读取测试 */
printf("\\nReading test file...\\n");
file_read_test();
/* 目录测试 */
printf("\\nDirectory test...\\n");
directory_test();
printf("\\nAll tests completed!\\n");
/* 主循环 */
while (1)
{
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
HAL_Delay(500);
}
}
/* 错误处理 */
void Error_Handler(void)
{
__disable_irq();
while (1) {
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
HAL_Delay(100);
}
}
4.5 系统架构流程图
硬件层
驱动层
I/O层
文件系统层
应用层
main.c
文件操作
file_operations.c
FatFs
R0.12c
磁盘I/O
diskio.c
SD卡驱动
sd_spi.c
SPI驱动
HAL库
STM32F103
SD卡模块
五、编译与下载
5.1 编译工程
- 在STM32CubeIDE中,点击
Project -> Build All - 等待编译完成
5.2 测试步骤
- 将SD卡插入模块
- 下载程序到开发板
- 打开串口调试助手(115200波特率)
- 观察输出结果
预期输出:
========================================
STM32 FatFs SD Card Demo
========================================
Initializing SD card...
SD card initialized, type: 2
Mounting filesystem...
Filesystem mounted successfully
SD Card Info:
Total: 7580 MB
Free: 7578 MB
Writing test file...
Written 50 bytes
Reading test file...
Read 50 bytes:
Hello, STM32 FatFs!
This is a test file.
Directory test...
Directory listing:
F 50 test.txt
All tests completed!
六、故障排查与问题解决
6.1 SD卡初始化失败
原因分析:
- SPI通信异常
- SD卡未正确插入
- 电源不稳定
解决方案:
c
// 添加调试输出
printf("CMD0 response: 0x%02X\\n", result);
// 降低SPI速度
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
6.2 文件系统挂载失败
原因分析:
- SD卡未格式化
- 文件系统损坏
解决方案:
c
// 在PC上格式化SD卡为FAT32
// 或在代码中自动格式化
if (res == FR_NO_FILESYSTEM) {
f_mkfs("", FM_FAT32, 0, work, sizeof(work));
}
七、总结
7.1 核心知识点回顾
- SD卡协议:掌握SPI模式下SD卡的初始化流程
- FatFs移植:理解FatFs的磁盘I/O接口
- 文件操作:掌握文件的读写、目录管理
- SPI通信:理解SPI接口的配置和数据传输
7.2 扩展学习方向
- SDIO模式:使用SDIO接口提高传输速度
- 多分区管理:实现多分区文件系统
- USB Mass Storage:将SD卡作为U盘使用
- 日志系统:实现循环日志记录
7.3 学习资源
官方文档: