STM32实战:基于STM32F103的FatFs文件系统移植(SD卡读写)

文章目录

    • 一、前言
      • [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工程

  1. 打开STM32CubeIDE
  2. 创建新工程,选择STM32F103C8T6
  3. 工程名称:STM32_FatFs_SD

3.2 配置SPI接口

  1. 打开 .ioc 文件

  2. 配置SPI1:

    • PA4:GPIO_Output(软件CS)
    • PA5:SPI1_SCK
    • PA6:SPI1_MISO
    • PA7:SPI1_MOSI
  3. 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

  1. 在CubeMX中,点击 Middleware -> FATFS
  2. 选择 User-defined
  3. 配置参数:

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 生成代码

  1. 点击 Project -> Generate Code
  2. 等待代码生成完成

四、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 编译工程

  1. 在STM32CubeIDE中,点击 Project -> Build All
  2. 等待编译完成

5.2 测试步骤

  1. 将SD卡插入模块
  2. 下载程序到开发板
  3. 打开串口调试助手(115200波特率)
  4. 观察输出结果

预期输出:

复制代码
========================================
  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 核心知识点回顾

  1. SD卡协议:掌握SPI模式下SD卡的初始化流程
  2. FatFs移植:理解FatFs的磁盘I/O接口
  3. 文件操作:掌握文件的读写、目录管理
  4. SPI通信:理解SPI接口的配置和数据传输

7.2 扩展学习方向

  • SDIO模式:使用SDIO接口提高传输速度
  • 多分区管理:实现多分区文件系统
  • USB Mass Storage:将SD卡作为U盘使用
  • 日志系统:实现循环日志记录

7.3 学习资源

官方文档:

相关推荐
minji...2 小时前
Linux 网络套接字编程(二)从 0 到 1 实现 UDP 回声服务器,recvfrom,sendto
linux·运维·网络·单片机·udp
rit84324992 小时前
基于STM32的RTC(实时时钟)程序设计与实现
stm32·嵌入式硬件·实时音视频
九鼎创展科技2 小时前
联发科 MT8883 核心优势深度解析:对比 MT8385/MT8788/MT8183
人工智能·科技·嵌入式硬件·边缘计算
zmj32032415 小时前
单片机串口收发数据不可靠--用做指令会执行错误动作
单片机·嵌入式硬件·串口
yuan1999715 小时前
STM32 驱动 RC522(MFRC522)实现方案
单片机·嵌入式硬件
踏着七彩祥云的小丑16 小时前
嵌入式——认识电子元器件——电容系列
单片机·嵌入式硬件
Sean_VIP17 小时前
SeanLib系列函数库-MyList
stm32
NQBJT18 小时前
DMA —— 让 CPU “偷懒”的数据搬运工
stm32·单片机·dma·嵌入式
xiangw@GZ18 小时前
EMC原理:CS传导抗扰度测试总结
单片机·嵌入式硬件