STM32系列文章( FatFs文件系统——使用SPI读写MicroSD卡)


前言

本文仅代表个人的记录和理解,不具有权威性,请谨慎参考。

STM32 MCU 读写SD/MicroSD卡最简单和快速的方式是使用SDIO口,但有些系列的STM32 MCU没有SDIO接口必须采用SPI操作SD/MicroSD卡。

一、FatFs是什么?

在这里插入图片描述

FatFs 是用于小型嵌入式系统的通用 FAT/exFAT 文件系统模块。FatFs 模块是按照 ANSI C (C89) 编写的,并且与磁盘 I/O 层完全分离。因此,它独立于平台。它可以集成到资源有限的小型微控制器中。如果我们需要MCU读写SD/MicroSD卡上的文件如bmp图片,就需要用到FatFs 文件系统。

1.应用界面层

在这里插入图片描述

FatFs 为应用程序提供了各种文件系统功能,如下所示:

  • 文件访问
    f_open - 打开/创建文件
    f_close - 关闭打开的文件
    f_read - 从文件中读取数据
    f_write - 将数据写入文件
    f_lseek - 移动读/写指针,扩展大小
    f_truncate - 截断文件大小
    f_sync - 刷新缓存的数据
    f_forward - 将数据转发到流
    f_expand - 为文件分配一个连续的块
    f_gets - 读取字符串
    f_putc - 写一个字符
    f_puts - 写一个字符串
    f_printf - 编写格式化字符串
    f_tell - 获取当前读/写指针
    f_eof - 测试文件结束
    f_size - 获取尺寸
    f_error - 测试错误

  • 目录访问
    f_opendir - 打开目录
    f_closedir - 关闭打开的目录
    f_readdir - 读取目录项
    f_findfirst - 打开目录并读取匹配的第一个项目
    f_findnext - 读取下一个匹配的项目

  • 文件和目录管理
    f_stat - 检查文件或子目录是否存在
    f_unlink - 删除文件或子目录
    f_rename - 重命名/移动文件或子目录
    f_chmod - 更改文件或子目录的属性
    f_utime - 更改文件或子目录的时间戳
    f_mkdir - 创建子目录
    f_chdir - 更改当前目录
    f_chdrive - 更改电流驱动器
    f_getcwd - 检索当前目录和驱动器

  • 卷管理和系统配置
    f_mount - 注册/注销卷的工作区
    f_mkfs - 在逻辑驱动器上创建 FAT 卷
    f_fdisk - 在物理驱动器上创建分区
    f_getfree - 获取卷上的可用空间
    f_getlabel - 获取卷标
    f_setlabel - 设置卷标
    f_setcp - 设置活动代码页

2.媒体访问接口层

在这里插入图片描述

由于 FatFs 模块是独立于平台和存储介质的文件系统层,因此它与物理设备(如存储卡、硬盘和任何类型的存储设备)完全分离。存储设备控制模块不是 FatFs 模块的任何部分,需要由实现者提供。FatFs 通过如下所示的简单媒体访问接口控制存储设备。此外,下载中还提供了某些平台的示例实现。此处提供了存储设备控制模块的功能检查器:

  • 存储设备控制
    disk_status - 获取设备状态
    disk_initialize - 初始化设备
    disk_read - 读取数据
    disk_write - 写入数据
    disk_ioctl - 控制设备相关功能
  • 实时时钟
    get_fattime - 获取当前时间

二、硬件

1.MicroSD卡转接板

为方便把MicroSD卡连接到STM32开发板,使用一个转接板进行转接。转接板接口如下:
在这里插入图片描述

2.STM32F103C8T6开发板

开发板上面有一颗STM32F103C8T6 MCU。
在这里插入图片描述

3.连接关系

MicroSD卡和STM32F103C8T6的连接关系如下表:

序号 MicroSD卡 MCU
1 CS PA4
2 SCK PA5
3 MOSI PA7
4 MISO PA6
5 VCC 3.3V
6 GND GND

三、代码

使用CubeMx生成代码,步骤如下:(注意:这个工程只列出了关键步骤,为节省篇幅一些步骤省略了,如果对CubeMX和KEIIL不太熟悉,请谨慎参考)

1.SPI

使用SPI1控制MicroSD卡,采用软件片选信号。
在这里插入图片描述

2.GPIO

为SPI1接口增加片选信号SD_CS。
在这里插入图片描述

3.UART

使用USART1作为打印输出接口。
在这里插入图片描述

4.FatFs

在这里插入图片描述

5.生成代码

生成的MDK代码关于FatFs的文件如下:
在这里插入图片描述
为了使用FatFs文件系统通过SPI读写MicroSD卡我们需要完成的工作有两步:

  • 编写SPI控制MicroSD卡的代码。
  • 给user_diskio.c里的空函数加入实现方法。

7.SPI控制MicroSD卡代码

新建两个文件:fatfs_sd.h和fatfs_sd.c,并将其添加到工程中。代码如下:

fatfs_sd.h

#ifndef __FATFS_SD_H
#define __FATFS_SD_H
 
#include "stm32l5xx_hal.h"
#include "diskio.h"
 
/* Definitions for MMC/SDC command */
#define CMD0     (0x40+0)       /* GO_IDLE_STATE */
#define CMD1     (0x40+1)       /* SEND_OP_COND */
#define CMD8     (0x40+8)       /* SEND_IF_COND */
#define CMD9     (0x40+9)       /* SEND_CSD */
#define CMD10    (0x40+10)      /* SEND_CID */
#define CMD12    (0x40+12)      /* STOP_TRANSMISSION */
#define CMD16    (0x40+16)      /* SET_BLOCKLEN */
#define CMD17    (0x40+17)      /* READ_SINGLE_BLOCK */
#define CMD18    (0x40+18)      /* READ_MULTIPLE_BLOCK */
#define CMD23    (0x40+23)      /* SET_BLOCK_COUNT */
#define CMD24    (0x40+24)      /* WRITE_BLOCK */
#define CMD25    (0x40+25)      /* WRITE_MULTIPLE_BLOCK */
#define CMD41    (0x40+41)      /* SEND_OP_COND (ACMD) */
#define CMD55    (0x40+55)      /* APP_CMD */
#define CMD58    (0x40+58)      /* READ_OCR */
 
/* MMC card type flags (MMC_GET_TYPE) */
#define CT_MMC    0x01    /* MMC ver 3 */
#define CT_SD1    0x02    /* SD ver 1 */
#define CT_SD2    0x04    /* SD ver 2 */
#define CT_SDC    0x06    /* SD */
#define CT_BLOCK  0x08    /* Block addressing */
 
#define ACMD41_HCS           0x40000000
#define ACMD41_SDXC_POWER    0x10000000
#define ACMD41_S18R          0x04000000
#define ACMD41_VOLTAGE       0x00ff8000
#define ACMD41_ARG_HC        (ACMD41_HCS|ACMD41_SDXC_POWER|ACMD41_VOLTAGE)
#define ACMD41_ARG_SC        (ACMD41_VOLTAGE)  
 
/* Functions */
DSTATUS SD_disk_initialize (BYTE pdrv);
DSTATUS SD_disk_status (BYTE pdrv);
DRESULT SD_disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count);
DRESULT SD_disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count);
DRESULT SD_disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);
 
#define SPI_TIMEOUT 100
 
extern SPI_HandleTypeDef  hspi2;
#define HSPI_SDCARD     &hspi2
#define SD_CS_PORT      GPIOB
#define SD_CS_PIN     GPIO_PIN_11
 
#endif

fatfs_sd.c

#define TRUE  1
#define FALSE 0
#define bool BYTE
 
#include "fatfs_sd.h"
#include <stdio.h>
 
uint16_t Timer1, Timer2;          /* 1ms Timer Counter */
 
static volatile DSTATUS Stat = STA_NOINIT;  /* Disk Status */
static uint8_t CardType;                    /* Type 0:MMC, 1:SDC, 2:Block addressing */
static uint8_t PowerFlag = 0;       /* Power flag */
 
/***************************************
 * SPI functions
 **************************************/
 
/* slave select */
static void SELECT(void)
{
   
   
  HAL_GPIO_WritePin(SD_CS_PORT, SD_CS_PIN, GPIO_PIN_RESET);
  HAL_Delay(1);
}
 
/* slave deselect */
static void DESELECT(void)
{
   
   
  HAL_GPIO_WritePin(SD_CS_PORT, SD_CS_PIN, GPIO_PIN_SET);
  HAL_Delay(1);
}
 
/* SPI transmit a byte */
static void SPI_TxByte(uint8_t data)
{
   
   
  while(!__HAL_SPI_GET_FLAG(HSPI_SDCARD, SPI_FLAG_TXE));
  HAL_SPI_Transmit(HSPI_SDCARD, &data, 1, SPI_TIMEOUT);
}
 
/* SPI transmit buffer */
static void SPI_TxBuffer(uint8_t *buffer, uint16_t len)
{
   
   
  while(!__HAL_SPI_GET_FLAG(HSPI_SDCARD, SPI_FLAG_TXE));
  HAL_SPI_Transmit(HSPI_SDCARD, buffer, len, SPI_TIMEOUT);
}
 
/* SPI receive a byte */
static uint8_t SPI_RxByte(void)
{
   
   
  uint8_t dummy, data;
  dummy = 0xFF;
 
  while(!__HAL_SPI_GET_FLAG(HSPI_SDCARD, SPI_FLAG_TXE));
  HAL_SPI_TransmitReceive(HSPI_SDCARD, &dummy, &data, 1, SPI_TIMEOUT);
 
  return data;
}
 
/* SPI receive a byte via pointer */
static void SPI_RxBytePtr
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值