STM32F030 SD卡文件系统读取实例

一、硬件连接(STM32F030 + SD卡模块)

1.1 硬件连接表

SD卡引脚 STM32F030引脚 说明
CS PA4 SPI片选
MOSI PA7 SPI主出从入
MISO PA6 SPI主入从出
SCK PA5 SPI时钟
VCC 3.3V 电源
GND GND

1.2 电路注意事项

  • SD卡模块必须支持3.3V电平(切勿接5V!)
  • 建议在CS线上加上拉电阻(10kΩ)
  • 电源端加100nF去耦电容
  • 长线连接时,SCK和MOSI加22Ω串联电阻

二、完整工程代码

2.1 主程序 (main.c)

c 复制代码
/**
  ******************************************************************************
  * @file    main.c
  * @brief   STM32F030 SD卡文件系统读取示例
  ******************************************************************************
  */

#include "stm32f0xx_hal.h"
#include "fatfs.h"
#include "sd_spi.h"
#include <stdio.h>
#include <string.h>

/* 全局变量 */
FATFS fs;           // 文件系统对象
FIL file;           // 文件对象
FRESULT res;        // FATFS结果
UINT br;            // 读取字节数
char buffer[128];   // 缓冲区

/* 函数声明 */
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_SPI1_Init(void);
void Error_Handler(void);

int main(void)
{
  /* HAL初始化 */
  HAL_Init();
  
  /* 系统时钟配置:48MHz */
  SystemClock_Config();
  
  /* GPIO初始化 */
  MX_GPIO_Init();
  
  /* SPI1初始化 */
  MX_SPI1_Init();
  
  /* SD卡初始化 */
  printf("Initializing SD card...\r\n");
  if(SD_Init() != SD_OK)
  {
    printf("SD card init failed!\r\n");
    Error_Handler();
  }
  printf("SD card initialized successfully!\r\n");
  
  /* 挂载文件系统 */
  printf("Mounting filesystem...\r\n");
  res = f_mount(&fs, "", 1);
  if(res != FR_OK)
  {
    printf("Mount failed! Error: %d\r\n", res);
    Error_Handler();
  }
  printf("Filesystem mounted successfully!\r\n");
  
  /* 列出根目录文件 */
  printf("\r\nListing root directory:\r\n");
  DIR dir;
  FILINFO fileInfo;
  
  res = f_opendir(&dir, "/");
  if(res == FR_OK)
  {
    while(1)
    {
      res = f_readdir(&dir, &fileInfo);
      if(res != FR_OK || fileInfo.fname[0] == 0) break;
      printf("  %s\t\t%d bytes\r\n", fileInfo.fname, (int)fileInfo.fsize);
    }
    f_closedir(&dir);
  }
  
  /* 读取测试文件 */
  printf("\r\nReading test.txt:\r\n");
  res = f_open(&file, "TEST.TXT", FA_READ);
  if(res == FR_OK)
  {
    printf("Content of TEST.TXT:\r\n");
    printf("----------------------------------------\r\n");
    
    while(!f_eof(&file))
    {
      memset(buffer, 0, sizeof(buffer));
      res = f_read(&file, buffer, sizeof(buffer)-1, &br);
      if(res != FR_OK) break;
      printf("%s", buffer);
    }
    
    printf("\r\n----------------------------------------\r\n");
    f_close(&file);
  }
  else
  {
    printf("Cannot open TEST.TXT! Error: %d\r\n", res);
  }
  
  /* 读取配置文件 */
  printf("\r\nReading config.ini:\r\n");
  res = f_open(&file, "CONFIG.INI", FA_READ);
  if(res == FR_OK)
  {
    char line[64];
    printf("Config content:\r\n");
    
    while(!f_eof(&file))
    {
      if(f_gets(line, sizeof(line), &file) != NULL)
      {
        printf("  %s", line);
      }
    }
    f_close(&file);
  }
  
  /* 卸载文件系统 */
  f_unmount("");
  
  /* 主循环 */
  while (1)
  {
    HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_8);  // LED闪烁表示正常运行
    HAL_Delay(500);
  }
}

/**
  * @brief 系统时钟配置
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  
  /* 启用HSI振荡器 */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL12;
  RCC_OscInitStruct.PLL.PREDIV = RCC_PREDIV_DIV1;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  
  /* 初始化CPU、AHB和APB总线时钟 */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief GPIO初始化
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  
  /* 启用GPIO时钟 */
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOC_CLK_ENABLE();
  
  /* 配置LED引脚 */
  GPIO_InitStruct.Pin = GPIO_PIN_8;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
  
  /* 配置SD卡检测引脚(如果有) */
  GPIO_InitStruct.Pin = GPIO_PIN_0;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

/**
  * @brief SPI1初始化
  */
static void MX_SPI1_Init(void)
{
  SPI_HandleTypeDef hspi1 = {0};
  
  __HAL_RCC_SPI1_CLK_ENABLE();
  
  hspi1.Instance = SPI1;
  hspi1.Init.Mode = SPI_MODE_MASTER;
  hspi1.Init.Direction = SPI_DIRECTION_2LINES;
  hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi1.Init.NSS = SPI_NSS_SOFT;
  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64; // 48MHz/64 = 750kHz
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi1.Init.CRCPolynomial = 7;
  
  if (HAL_SPI_Init(&hspi1) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief 错误处理
  */
void Error_Handler(void)
{
  while(1)
  {
    HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_8);
    HAL_Delay(100);
  }
}

2.2 SD卡SPI驱动 (sd_spi.c)

c 复制代码
/**
  ******************************************************************************
  * @file    sd_spi.c
  * @brief   SD卡SPI驱动
  ******************************************************************************
  */

#include "sd_spi.h"
#include "stm32f0xx_hal.h"

/* SD卡类型 */
typedef enum {
  SD_TYPE_UNKNOWN = 0,
  SD_TYPE_V1,
  SD_TYPE_V2,
  SD_TYPE_V2HC
} SD_Type;

/* 全局变量 */
static SD_Type sd_type = SD_TYPE_UNKNOWN;
static SPI_HandleTypeDef *hspi = &hspi1;

/* SD卡命令 */
#define CMD0    0   // 复位
#define CMD8    8   // 发送接口条件
#define CMD9    9   // 读取CSD
#define CMD10   10  // 读取CID
#define CMD12   12  // 停止传输
#define CMD16   16  // 设置块长度
#define CMD17   17  // 读取单块
#define CMD18   18  // 读取多块
#define CMD24   24  // 写入单块
#define CMD25   25  // 写入多块
#define CMD55   55  // 应用特定命令
#define CMD58   58  // 读取OCR
#define ACMD41  41  // 发送操作条件

/* 私有函数 */
static void SD_CS_Enable(void);
static void SD_CS_Disable(void);
static uint8_t SD_SendCommand(uint8_t cmd, uint32_t arg, uint8_t crc);
static uint8_t SD_WaitReady(void);
static uint8_t SD_ReadBuffer(uint8_t *buffer, uint16_t len);
static uint8_t SD_WriteBuffer(uint8_t *buffer, uint16_t len);

/**
  * @brief  SD卡初始化
  */
SD_Result SD_Init(void)
{
  uint8_t response;
  uint16_t retry = 0;
  
  /* 1. 初始化SPI接口 */
  SD_CS_Disable();
  
  /* 2. 发送至少74个时钟脉冲 */
  for(retry = 0; retry < 10; retry++)
  {
    SD_SendCommand(0xFF, 0, 0xFF);
  }
  
  /* 3. 发送CMD0复位SD卡 */
  SD_CS_Enable();
  response = SD_SendCommand(CMD0, 0, 0x95);  // CRC=0x95 for CMD0
  SD_CS_Disable();
  
  if(response != 0x01)
  {
    return SD_ERROR;
  }
  
  /* 4. 发送CMD8检查版本 */
  SD_CS_Enable();
  response = SD_SendCommand(CMD8, 0x1AA, 0x87);  // CRC=0x87 for CMD8
  SD_CS_Disable();
  
  if(response == 0x01)
  {
    /* 5. 初始化V2卡 */
    for(retry = 0; retry < 1000; retry++)
    {
      SD_CS_Enable();
      response = SD_SendCommand(CMD55, 0, 0xFF);
      if(response != 0x01) break;
      
      response = SD_SendCommand(ACMD41, 0x40000000, 0xFF);
      SD_CS_Disable();
      
      if(response == 0x00)
      {
        sd_type = SD_TYPE_V2HC;
        break;
      }
    }
  }
  else
  {
    /* 6. 初始化V1卡 */
    SD_CS_Enable();
    response = SD_SendCommand(CMD55, 0, 0xFF);
    response = SD_SendCommand(ACMD41, 0, 0xFF);
    SD_CS_Disable();
    
    if(response == 0x00)
    {
      sd_type = SD_TYPE_V1;
    }
  }
  
  /* 7. 设置块长度为512字节 */
  SD_CS_Enable();
  response = SD_SendCommand(CMD16, 512, 0xFF);
  SD_CS_Disable();
  
  if(response != 0x00)
  {
    return SD_ERROR;
  }
  
  return SD_OK;
}

/**
  * @brief  读取单个扇区
  */
SD_Result SD_ReadSector(uint32_t sector, uint8_t *buffer)
{
  uint8_t response;
  
  if(sd_type != SD_TYPE_V1)
  {
    sector *= 512;  // V2卡使用字节地址
  }
  
  SD_CS_Enable();
  
  /* 发送读扇区命令 */
  response = SD_SendCommand(CMD17, sector, 0xFF);
  if(response != 0x00)
  {
    SD_CS_Disable();
    return SD_ERROR;
  }
  
  /* 等待数据令牌 */
  uint16_t timeout = 0;
  while(SD_SendCommand(0xFF, 0, 0xFF) != 0xFE)
  {
    if(++timeout > 65535)
    {
      SD_CS_Disable();
      return SD_TIMEOUT;
    }
  }
  
  /* 读取512字节数据 */
  SD_ReadBuffer(buffer, 512);
  
  /* 读取CRC(忽略) */
  SD_SendCommand(0xFF, 0, 0xFF);
  SD_SendCommand(0xFF, 0, 0xFF);
  
  SD_CS_Disable();
  return SD_OK;
}

/**
  * @brief  写入单个扇区
  */
SD_Result SD_WriteSector(uint32_t sector, uint8_t *buffer)
{
  uint8_t response;
  
  if(sd_type != SD_TYPE_V1)
  {
    sector *= 512;
  }
  
  SD_CS_Enable();
  
  /* 发送写扇区命令 */
  response = SD_SendCommand(CMD24, sector, 0xFF);
  if(response != 0x00)
  {
    SD_CS_Disable();
    return SD_ERROR;
  }
  
  /* 发送数据令牌 */
  SD_SendCommand(0xFE, 0, 0xFF);
  
  /* 写入512字节数据 */
  SD_WriteBuffer(buffer, 512);
  
  /* 发送伪CRC */
  SD_SendCommand(0xFF, 0, 0xFF);
  SD_SendCommand(0xFF, 0, 0xFF);
  
  /* 检查响应 */
  response = SD_SendCommand(0xFF, 0, 0xFF);
  if((response & 0x1F) != 0x05)
  {
    SD_CS_Disable();
    return SD_ERROR;
  }
  
  /* 等待写入完成 */
  while(SD_SendCommand(0xFF, 0, 0xFF) == 0x00);
  
  SD_CS_Disable();
  return SD_OK;
}

/* 私有函数实现 */
static void SD_CS_Enable(void)
{
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
}

static void SD_CS_Disable(void)
{
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
}

static uint8_t SD_SendCommand(uint8_t cmd, uint32_t arg, uint8_t crc)
{
  uint8_t response;
  
  /* 发送命令 */
  SD_SendByte(cmd | 0x40);
  SD_SendByte(arg >> 24);
  SD_SendByte(arg >> 16);
  SD_SendByte(arg >> 8);
  SD_SendByte(arg);
  SD_SendByte(crc);
  
  /* 等待响应 */
  uint8_t retry = 0;
  do {
    response = SD_SendByte(0xFF);
    retry++;
  } while(response == 0xFF && retry < 255);
  
  return response;
}

static uint8_t SD_WaitReady(void)
{
  uint16_t timeout = 0;
  while(SD_SendByte(0xFF) != 0xFF)
  {
    if(++timeout > 65535)
    {
      return 0;
    }
  }
  return 1;
}

static uint8_t SD_ReadBuffer(uint8_t *buffer, uint16_t len)
{
  for(uint16_t i = 0; i < len; i++)
  {
    buffer[i] = SD_SendByte(0xFF);
  }
  return 1;
}

static uint8_t SD_WriteBuffer(uint8_t *buffer, uint16_t len)
{
  for(uint16_t i = 0; i < len; i++)
  {
    SD_SendByte(buffer[i]);
  }
  return 1;
}

uint8_t SD_SendByte(uint8_t byte)
{
  uint8_t received;
  HAL_SPI_TransmitReceive(hspi, &byte, &received, 1, 100);
  return received;
}

2.3 SD卡SPI头文件 (sd_spi.h)

c 复制代码
#ifndef __SD_SPI_H
#define __SD_SPI_H

#include "stm32f0xx_hal.h"

/* SD卡结果枚举 */
typedef enum {
  SD_OK = 0,
  SD_ERROR,
  SD_TIMEOUT,
  SD_NOT_PRESENT
} SD_Result;

/* 函数声明 */
SD_Result SD_Init(void);
SD_Result SD_ReadSector(uint32_t sector, uint8_t *buffer);
SD_Result SD_WriteSector(uint32_t sector, uint8_t *buffer);
uint8_t SD_SendByte(uint8_t byte);

#endif /* __SD_SPI_H */

2.4 FATFS移植层 (diskio.c)

c 复制代码
/**
  ******************************************************************************
  * @file    diskio.c
  * @brief   FATFS磁盘IO接口
  ******************************************************************************
  */

#include "diskio.h"
#include "sd_spi.h"

/* 物理驱动器编号 */
#define DEV_SD  0

/* 磁盘状态 */
static volatile DSTATUS Stat = STA_NOINIT;

/*-----------------------------------------------------------------------*/
/* 获取磁盘状态                                                    */
/*-----------------------------------------------------------------------*/
DSTATUS disk_status (
  BYTE pdrv    /* 物理驱动器编号 */
)
{
  if (pdrv != DEV_SD) return STA_NOINIT;
  return Stat;
}

/*-----------------------------------------------------------------------*/
/* 初始化磁盘                                                      */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize (
  BYTE pdrv    /* 物理驱动器编号 */
)
{
  if (pdrv != DEV_SD) return STA_NOINIT;
  
  /* 初始化SD卡 */
  SD_Result res = SD_Init();
  if(res != SD_OK)
  {
    Stat = STA_NOINIT;
    return Stat;
  }
  
  Stat &= ~STA_NOINIT;  /* 清除NOINIT标志 */
  return Stat;
}

/*-----------------------------------------------------------------------*/
/* 读取扇区                                                        */
/*-----------------------------------------------------------------------*/
DRESULT disk_read (
  BYTE pdrv,      /* 物理驱动器编号 */
  BYTE *buff,     /* 数据缓冲区 */
  DWORD sector,   /* 起始扇区 */
  UINT count      /* 扇区数量 */
)
{
  if (pdrv != DEV_SD || !count) return RES_PARERR;
  if (Stat & STA_NOINIT) return RES_NOTRDY;
  
  /* 读取多个扇区 */
  for (UINT i = 0; i < count; i++)
  {
    if (SD_ReadSector(sector + i, buff + i * 512) != SD_OK)
    {
      return RES_ERROR;
    }
  }
  
  return RES_OK;
}

/*-----------------------------------------------------------------------*/
/* 写入扇区                                                        */
/*-----------------------------------------------------------------------*/
#if _USE_WRITE
DRESULT disk_write (
  BYTE pdrv,      /* 物理驱动器编号 */
  const BYTE *buff, /* 数据缓冲区 */
  DWORD sector,   /* 起始扇区 */
  UINT count      /* 扇区数量 */
)
{
  if (pdrv != DEV_SD || !count) return RES_PARERR;
  if (Stat & STA_NOINIT) return RES_NOTRDY;
  
  /* 写入多个扇区 */
  for (UINT i = 0; i < count; i++)
  {
    if (SD_WriteSector(sector + i, (uint8_t*)(buff + i * 512)) != SD_OK)
    {
      return RES_ERROR;
    }
  }
  
  return RES_OK;
}
#endif

/*-----------------------------------------------------------------------*/
/* 磁盘控制                                                        */
/*-----------------------------------------------------------------------*/
#if _USE_IOCTL
DRESULT disk_ioctl (
  BYTE pdrv,      /* 物理驱动器编号 */
  BYTE cmd,       /* 控制命令 */
  void *buff      /* 缓冲区 */
)
{
  if (pdrv != DEV_SD) return RES_PARERR;
  if (Stat & STA_NOINIT) return RES_NOTRDY;
  
  DRESULT res = RES_OK;
  
  switch (cmd) {
    case CTRL_SYNC:     /* 同步缓存 */
      res = RES_OK;
      break;
      
    case GET_SECTOR_COUNT:  /* 获取扇区总数 */
      *(DWORD*)buff = 0;  // 这里应该读取SD卡容量
      res = RES_OK;
      break;
      
    case GET_SECTOR_SIZE:   /* 获取扇区大小 */
      *(WORD*)buff = 512;
      res = RES_OK;
      break;
      
    case GET_BLOCK_SIZE:    /* 获取块大小 */
      *(DWORD*)buff = 1;
      res = RES_OK;
      break;
      
    default:
      res = RES_PARERR;
      break;
  }
  
  return res;
}
#endif

2.5 FATFS配置文件 (ffconf.h)

c 复制代码
/*---------------------------------------------------------------------------/
/  FatFs - FAT file system module configuration file
/---------------------------------------------------------------------------*/

#define FFCONF_DEF 86604  /* Revision ID */

/*---------------------------------------------------------------------------/
/ Function Configurations
/---------------------------------------------------------------------------*/
#define FF_FS_READONLY  0   /* 0:Read/Write, 1:Read only */
#define FF_FS_MINIMIZE  0   /* 0 to 3 */
#define FF_USE_FIND     1   /* 0:Disable, 1:Enable */
#define FF_USE_MKFS     1   /* 0:Disable, 1:Enable */
#define FF_USE_FASTSEEK 0   /* 0:Disable, 1:Enable */
#define FF_USE_EXPAND   0   /* 0:Disable, 1:Enable */
#define FF_USE_CHMOD    0   /* 0:Disable, 1:Enable */
#define FF_USE_LABEL    1   /* 0:Disable, 1:Enable */
#define FF_USE_FORWARD  0   /* 0:Disable, 1:Enable */

/*---------------------------------------------------------------------------/
/ Locale and Namespace Configurations
/---------------------------------------------------------------------------*/
#define FF_CODE_PAGE    936   /* 936:GBK, 437:ASCII */
#define FF_USE_LFN      1     /* 0 to 3 */
#define FF_MAX_LFN      255   /* Max LFN length */
#define FF_LFN_UNICODE  0     /* 0:ANSI, 1:Unicode */
#define FF_STRF_ENCODE  0     /* 0:ANSI, 1:UTF-16, 2:UTF-8 */
#define FF_LFN_BUF      255   /* LFN working buffer size */
#define FF_SFN_BUF      12    /* SFN working buffer size */

/*---------------------------------------------------------------------------/
/ Drive/Volume Configurations
/---------------------------------------------------------------------------*/
#define FF_VOLUMES      1     /* Number of volumes */
#define FF_STR_VOLUME_ID 0    /* 0:Disable, 1:Enable */
#define FF_VOLUME_STRS  {"SD","USB"}
#define FF_MULTI_PARTITION 0  /* 0:Single partition, 1:Multiple partition */
#define FF_MIN_SS       512   /* Minimum sector size */
#define FF_MAX_SS       512   /* Maximum sector size */
#define FF_LBA64        0     /* 0:Disable, 1:Enable 64-bit LBA */

/*---------------------------------------------------------------------------/
/ System Configurations
/---------------------------------------------------------------------------*/
#define FF_FS_TINY      0     /* 0:Normal, 1:Tiny */
#define FF_FS_EXFAT     0     /* 0:Disable, 1:Enable exFAT */
#define FF_FS_NORTC     1     /* 0:Use RTC, 1:No RTC */
#define FF_NORTC_MON    1     /* Month (1..12) */
#define FF_NORTC_YEAR   2024  /* Year (1980..2107) */

/*---------------------------------------------------------------------------/
/ Debug Configurations
/---------------------------------------------------------------------------*/
#define FF_DBG_LOG      0     /* 0:Disable, 1:Enable debug log */
#define FF_DBG_TIMER    0     /* 0:Disable, 1:Enable debug timer */

三、测试文件准备

3.1 SD卡文件结构

复制代码
SD卡根目录/
├── TEST.TXT          (测试文本文件)
├── CONFIG.INI        (配置文件)
├── DATA/
│   ├── sensor.csv   (传感器数据)
│   └── log.txt      (日志文件)
└── README.TXT       (说明文件)

3.2 测试文件内容示例

TEST.TXT:

复制代码
Hello STM32F030 SD Card!
This is a test file for FATFS.
Date: 2024-01-15
Time: 10:30:00

CONFIG.INI:

复制代码
[SYSTEM]
VERSION=1.0
MODE=RUN
DEBUG=1

[SENSOR]
TEMP_THRESHOLD=25.5
HUMI_THRESHOLD=60.0
INTERVAL=5000

[NETWORK]
SSID=MyWiFi
PASSWORD=12345678

参考代码 文件系统sd卡读取(stm32f030 SD实例) www.youwenfan.com/contentcsv/115930.html

四、编译与调试

4.1 编译环境

  • IDE: STM32CubeIDE / Keil MDK
  • 编译器: ARM GCC
  • 优化级别: -O1

4.2 调试技巧

问题1:SD卡初始化失败

c 复制代码
// 在SD_Init()中添加调试输出
printf("Sending CMD0...\r\n");
response = SD_SendCommand(CMD0, 0, 0x95);
printf("CMD0 response: 0x%02X\r\n", response);

问题2:文件系统挂载失败

c 复制代码
// 检查FATFS错误代码
if(res == FR_NO_FILESYSTEM)
{
    printf("No filesystem found! Format SD card first.\r\n");
}

问题3:读取文件为空

c 复制代码
// 检查文件大小
printf("File size: %ld bytes\r\n", file.obj.objsize);

4.3 性能优化建议

  1. SPI时钟优化
c 复制代码
// 初始化时使用低速,数据传输时使用高速
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64; // 初始化
HAL_SPI_Init(&hspi1);
// ... 初始化完成后
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;  // 18MHz
HAL_SPI_Init(&hspi1);
  1. DMA传输
c 复制代码
// 使用DMA读取大文件
HAL_SPI_TransmitReceive_DMA(&hspi1, tx_buffer, rx_buffer, 512);
  1. 缓存机制
c 复制代码
// 使用RAM缓存减少SD卡访问次数
uint8_t file_cache[4096];

五、扩展功能

5.1 文件写入功能

c 复制代码
/* 写入数据到文件 */
void WriteToFile(char *filename, char *data)
{
  FIL file;
  UINT bw;
  
  res = f_open(&file, filename, FA_WRITE | FA_OPEN_APPEND);
  if(res == FR_OK)
  {
    f_write(&file, data, strlen(data), &bw);
    f_close(&file);
  }
}

5.2 目录操作

c 复制代码
/* 创建目录 */
void CreateDirectory(char *dirname)
{
  res = f_mkdir(dirname);
  if(res == FR_OK)
  {
    printf("Directory created: %s\r\n", dirname);
  }
}

5.3 文件删除

c 复制代码
/* 删除文件 */
void DeleteFile(char *filename)
{
  res = f_unlink(filename);
  if(res == FR_OK)
  {
    printf("File deleted: %s\r\n", filename);
  }
}

这个完整的STM32F030 SD卡文件系统示例可以直接在你的开发板上运行。记得先格式化SD卡为FAT32格式,然后放入测试文件。如果遇到问题,首先检查硬件连接和电源电压是否稳定。

相关推荐
kebidaixu2 小时前
FreeRTOS 移植到 STM32F407VETX 记录(三)
stm32·单片机·嵌入式硬件
普中科技12 小时前
【普中STM32F1xx开发攻略--标准库版】-- 第 45 章 FSMC-外扩 SRAM 实验
stm32·单片机·嵌入式硬件·fsmc·普中科技·外扩sram·is62wv51216
xiaoyuchidayuma14 小时前
永磁同步发电机的线电压和直流母线电压的关系
嵌入式硬件
潜创微科技14 小时前
4K60 over IP 方案简介
网络·嵌入式硬件·网络协议·tcp/ip·音视频
rit843249914 小时前
基于C#的USB HID设备读取测试软件
嵌入式硬件
三佛科技-1873661339715 小时前
FT32F103C8AT7兼容GD32F103C8T632 位通用微控制器MCU,替代性分析
单片机·嵌入式硬件
iCxhust15 小时前
8086汇编 word ptr
汇编·单片机·嵌入式硬件·微机原理·8088单板机
嵌入式ZYXC15 小时前
第3篇:《面试题:I2C为什么要加上拉电阻?阻值怎么选?》
stm32·单片机·嵌入式硬件·面试·职场和发展
leo__52016 小时前
C# 虚拟键盘(软键盘)实现
单片机·c#·计算机外设