STM32

STM32CubeMX学习笔记(27)——FatFs文件系统使用

2021-12-07  本文已影响0人  Leung_ManWah

一、FatFs简介

FatFs 是面向小型嵌入式系统的一种通用的 FAT 文件系统。它完全是由 ANSI C 语言编写并且完全独立于底层的 I/O 介质。因此它可以很容易地不加修改地移植到其他的处理器当中,如 8051、PIC、AVR、SH、Z80、H8、ARM 等。FatFs 支持 FAT12、FAT16、FAT32 等格式,所以我们利用前面写好的 SPI Flash 芯片驱动,把 FatFs 文件系统代码移植到工程之中,就可以利用文件系统的各种函数,对 SPI Flash 芯片以“文件”格式进行读写操作了。

FatFs 文件系统的源码可以从 fatfs 官网下载:
http://elm-chan.org/fsw/ff/00index_e.html

1.1 FatFs文件系统布局

簇是文件存储的最小单元,FAT32分区大小与对应簇空间大小关系如下表示:

分区空间大小 簇空间大小 每个簇包含的扇区数
< 8GB 4KB 8
[ 8GB, 16GB ) 8KB 16
[ 16GB, 32GB ) 16KB 32
>= 32GB 32KB 64

例如:创建一个50字节的test.txt文件,文件大小是50字节,但是占用磁盘空间为4096字节(一个簇)

1.2 FatFs层次结构

FATFS源码相关文件介绍如下表示;移植FATFS模块时,一般只需要修改2个文件(即ffconf.hdiskio.c

与平台无关:

文件 说明
ffconf.h FATFS模块配置文件
ff.h FATFS和应用模块公用的包含文件
ff.c FATFS模块
diskio.h FATFS和disk I/O模块公用的包含文件
interger.h 数据类型定义
option 可选的外部功能(比如支持中文)

与平台相关:

文件 说明
diskio.c FATFS和disk I/O模块接口层文件

1.3 FatFs API

1.3.1 f_mount

功能 在FatFs模块上注册、注销一个工作区(文件系统对象)
函数定义 FRESULT f_mount(FATFS* fs, const TCHAR* path, BYTE opt)
参数 fs:工作区(文件系统对象)指针
path:注册/注销工作区的逻辑驱动器号
opt:注册或注销选项
返回 操作结果

1.3.2 f_open

功能 创建/打开一个文件对象
函数定义 FRESULT f_open(FIL* fp, const TCHAR* path, BYTE mode)
参数 fp:将被创建的文件对象结构的指针
path:文件名指针,指定将创建或打开的文件名
mode:访问类型和打开方法,由一下标准的一个组合指定的
返回 操作结果
模式 描述
FA_READ 指定读访问对象。可以从文件中读取数据。 与FA_WRITE 结 合可以进行读写访问。
FA_WRITE 指定写访问对象。可以向文件中写入数据。与FA_READ 结合 可以进行读写访问。
FA_OPEN_EXISTING 打开文件。如果文件不存在,则打开失败。(默认)
FA_OPEN_ALWAYS 如果文件存在,则打开;否则,创建一个新文件。
FA_CREATE_NEW 创建一个新文件。如果文件已存在,则创建失败。
FA_CREATE_ALWAYS 创建一个新文件。如果文件已存在,则它将被截断并覆盖。

1.3.3 f_close

功能 关闭一个打开的文件
函数定义 FRESULT f_close(FIL* fp)
参数 fp:指向将被关闭的已打开的文件对象结构的指针
返回 操作结果

1.3.4 f_read

功能 从一个打开的文件中读取数据
函数定义 FRESULT f_read(FIL* fp, void* buff, UINT btr, UINT* br)
参数 fp:指向将被读取的已打开的文件对象结构的指针
buff:指向存储读取数据的缓冲区的指针
btr:要读取的字节数
br:指向返回已读取字节数的UINT变量的指针,返回为实际读取的字节数
返回 操作结果

1.3.5 f_write

功能 写入数据到一个已打开的文件
函数定义 FRESULT f_write(FIL* fp, void* buff, UINT btw, UINT* bw)
参数 fp:指向将被写入的已打开的文件对象结构的指针
buff:指向存储写入数据的缓冲区的指针
btw:要写入的字节数
bw:指向返回已写入字节数的UINT变量的指针,返回为实际写入的字节数
返回 操作结果

另外FatFs还有很多API操作函数,在这里不再作详细的介绍,详细信息请查看FatFs文件系统官网。

二、新建工程

1. 打开 STM32CubeMX 软件,点击“新建工程”

2. 选择 MCU 和封装

3. 配置时钟
RCC 设置,选择 HSE(外部高速时钟) 为 Crystal/Ceramic Resonator(晶振/陶瓷谐振器)


选择 Clock Configuration,配置系统时钟 SYSCLK 为 72MHz
修改 HCLK 的值为 72 后,输入回车,软件会自动修改所有配置

4. 配置调试模式
非常重要的一步,否则会造成第一次烧录程序后续无法识别调试器
SYS 设置,选择 Debug 为 Serial Wire

三、SDIO

STM32 控制器可以控制使用单线或 4 线传输,本开发板设计使用 4 线传输。

3.1 参数配置

Connetivity 中选择 SDIO 设置,并选择 SD 4 bits Wide bus 四线SD模式


此时 SDIO 对应的管脚也被选中。

Parameter Settings 进行具体参数配置。

Clock transition on which the bit capture is made: Rising transition。主时钟 SDIOCLK 产生 CLK 引脚时钟有效沿选择,可选上升沿或下降沿,它设定 SDIO 时钟控制寄存器(SDIO_CLKCR)的 NEGEDGE 位的值,一般选择设置为上升沿

SDIO Clock divider bypass: Disable。时钟分频旁路使用,可选使能或禁用,它设定 SDIO_CLKCR 寄存器的 BYPASS 位。如果使能旁路,SDIOCLK 直接驱动 CLK 线输出时钟;如果禁用,使用 SDIO_CLKCR 寄存器的 CLKDIV 位值分频 SDIOCLK,然后输出到 CLK 线。一般选择禁用时钟分频旁路

SDIO Clock output enable when the bus is idle: Disable the power save for the clock。节能模式选择,可选使能或禁用,它设定 SDIO_CLKCR 寄存器的 PWRSAV 位的值。如果使能节能模式,CLK 线只有在总线激活时才有时钟输出;如果禁用节能模式,始终使能 CLK 线输出时钟。

SDIO hardware flow control: The hardware control flow is disabled。硬件流控制选择,可选使能或禁用,它设定 SDIO_CLKCR 寄存器的 HWFC_EN 位的值。硬件流控制功能可以避免 FIFO 发送上溢和下溢错误。

SDIOCLK clock divide factor: 6。时钟分频系数,它设定 SDIO_CLKCR 寄存器的 CLKDIV 位的值,设置 SDIOCLK 与 CLK 线输出时钟分频系数:CLK 线时钟频率=SDIOCLK/([CLKDIV+2])。

SDIO_CK 引脚的时钟信号在卡识别模式时要求不超过 400KHz,而在识别后的数据传输模式时则希望有更高的速度(最大不超过 25MHz),所以会针对这两种模式配置 SDIOCLK 的时钟。

这里参数描述建议将SDIOCLK clock divede factor 参数使用默认值为0,SDIOCLK为72MHz,可以得到最大频率36MHz,但请注意,有些型号的SD卡可能不支持36MHz这么高的频率,所以还是要以实际情况而定。

3.2 配置DMA

SDIO 外设支持生成 DMA 请求,使用 DMA 传输可以提高数据传输效率,因此在 SDIO 的控制代码中,可以把它设置为 DMA 传输模式或轮询模式,ST 标准库提供 SDIO 示例中针对这两个模式做了区分处理。应用中一般都使用DMA 传输模式。

点击 DMA Settings 添加 SDIO 对应 DMA2 的通道4。DMA模式选择循环模式,方向选为内存到外设。

3.3 配置NVIC

DMA及SDIO中断设置,原则是全局中断优先级高于DMA中断


四、FATFS

4.1 参数配置

Middleware 中选择 FATFS 设置,并勾选 SD Card 配置为SD卡

缓存工作区为什么放在栈?其实fatfs提供了三个选项:BSS,STACK , HEAP,根据个人情况选一个。
在BSS上启用带有静态工作缓冲区的LFN,不能动态分配。
如果选择了HEAP(堆)且自己有属于自己的malloc就去重写ff_memalloc ff_memfree函数。如果是库的malloc就不需要。
一般都选择使用STACK(栈),能动态分配。
当使用堆栈作为工作缓冲区时,请注意堆栈溢出。

4.2 配置SD卡检测引脚

SD卡插入检测引脚,如果不配置一个引脚生成文件时会报错,所以这里即使没有硬件连接,也可以任意设置一引脚使用,生成工程后注释代码。



4.3 增大栈空间

将最小栈空间改到 0x1000

注意:由于刚才设置长文件名动态缓存存储在堆中,故需要增大堆大小,如果不修改则程序运行时堆会生成溢出,程序进入硬件错误中断(HardFault),死循环。

4.4 生成代码

输入项目名和项目路径


选择应用的 IDE 开发环境 MDK-ARM V5

每个外设生成独立的 ’.c/.h’ 文件
不勾:所有初始化代码都生成在 main.c
勾选:初始化代码生成在对应的外设文件。 如 GPIO 初始化代码生成在 gpio.c 中。

点击 GENERATE CODE 生成代码

五、屏蔽SD卡插入检测代码

由于没有检测SD卡插入的硬件引脚,打开 bsp_driver_sd.c 文件,修改 BSP_SD_IsDetected() 函数,始终返回SD_PRESENT:


六、修改main函数

定义相关变量:

FATFS fs;                       /* FatFs 文件系统对象 */
FIL file;                       /* 文件对象 */
FRESULT f_res;                  /* 文件操作结果 */
UINT fnum;                      /* 文件成功读写数量 */
BYTE ReadBuffer[1024] = {0};    /* 读缓冲区 */
BYTE WriteBuffer[] =            /* 写缓冲区 */
    "This is STM32 working with FatFs\r\n";

修改main函数:

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
  MX_SDIO_SD_Init();
  MX_FATFS_Init();
  /* USER CODE BEGIN 2 */
    printf("\r\n****** FatFs Example ******\r\n\r\n");
    
    //在外部 SD 卡挂载文件系统,文件系统挂载时会对 SD 卡初始化
    f_res = f_mount(&fs, "0:", 1);
    
    /*----------------------- 格式化测试 ---------------------------*/
    printf("\r\n****** Register the file system object to the FatFs module ******\r\n");
    /* 如果没有文件系统就格式化创建创建文件系统 */
    if(f_res == FR_NO_FILESYSTEM)
    {
        printf("The SD card does not yet have a file system and is about to be formatted...\r\n");
        /* 格式化 */
        f_res = f_mkfs("0:", 0, 0);
        if(f_res == FR_OK)
        {
            printf("The SD card successfully formatted the file system\r\n");
            /* 格式化后,先取消挂载 */
            f_res = f_mount(NULL, "0:", 1);
            /* 重新挂载 */
            f_res = f_mount(&fs, "0:", 1);
        }
        else
        {
            printf("The format failed\r\n");
            while(1);
        }       
    }
    else if(f_res != FR_OK)
    {
        printf(" mount error : %d \r\n", f_res);
        while(1);
    }
    else
    {
        printf(" mount sucess!!! \r\n");
    }
    
    /*----------------------- 文件系统测试:写测试 -----------------------------*/
    /* 打开文件,如果文件不存在则创建它 */
    printf("\r\n****** Create and Open new text file objects with write access ******\r\n");
    f_res = f_open(&file, "0:FatFs STM32cube.txt", FA_CREATE_ALWAYS | FA_WRITE);
    if(f_res == FR_OK)
    {
        printf(" open file sucess!!! \r\n");
        /* 将指定存储区内容写入到文件内 */
        printf("\r\n****** Write data to the text files ******\r\n");
        f_res = f_write(&file, WriteBuffer, sizeof(WriteBuffer), &fnum);
        if(f_res == FR_OK)
        {
            printf(" write file sucess!!! (%d)\n", fnum);
            printf(" write Data : %s\r\n", WriteBuffer);
        }
        else
        {
            printf(" write file error : %d\r\n", f_res);
        }
        /* 不再读写,关闭文件 */
        f_close(&file);
    }
    else
    {
        printf(" open file error : %d\r\n", f_res);
    }
    
    /*------------------- 文件系统测试:读测试 ------------------------------------*/
    printf("\r\n****** Read data from the text files ******\r\n");
    f_res = f_open(&file, "0:FatFs STM32cube.txt", FA_OPEN_EXISTING | FA_READ);
    if(f_res == FR_OK)
    {
        printf(" open file sucess!!! \r\n");
        f_res = f_read(&file, ReadBuffer, sizeof(ReadBuffer), &fnum);
        if(f_res == FR_OK)
        {
            printf("read sucess!!! (%d)\n", fnum);
            printf("read Data : %s\r\n", ReadBuffer);
        }
        else
        {
            printf(" read error!!! %d\r\n", f_res);
        }
    }
    else
    {
        printf(" open file error : %d\r\n", f_res);
    }
    /* 不再读写,关闭文件 */
    f_close(&file);
    /* 不再使用文件系统,取消挂载文件系统 */
    f_mount(NULL, "0:", 1);
    /* 操作完成,停机 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

七、查看打印

串口打印功能查看 STM32CubeMX学习笔记(6)——USART串口使用

八、工程代码

链接:https://pan.baidu.com/s/1kgJ7AmbnW89yHcrgBzD_1w 提取码:kg5a

九、注意事项

f_open、f_write、f_read如果偶尔有问题;f_mkfs报错 FS_DISK_ERR,可以加上了SDIO硬件流使能试试。


用户代码要加在 USER CODE BEGIN NUSER CODE END N 之间,否则下次使用 STM32CubeMX 重新生成代码后,会被删除。


• 由 Leung 写于 2021 年 12 月 7 日

• 参考:STM32CubeMX系列教程18:文件系统FATFS
    STM32CubeMX系列|FATFS文件系统
    1.6 Cubemx_STM32F103_NOOS SDIO_DMA_FATFS基于SD卡的FATFS测试(一)
    【STM32Cube_20】在SD卡上移植FATFS文件系统
    STM32CubeMX之SD卡+FatFs

上一篇下一篇

猜你喜欢

热点阅读