STM32学习——FATFS文件系统

2023-11-02

目录

什么是文件系统

常用的文件系统

FATFS的特点:

FATFS层次结构:

移植步骤:

相关配置宏:

FATFS文件系统移植实验

FATFS程序结构图

FATFS底层设备驱动函数

宏定义

设备状态获取

设备初始化

读取扇区

扇区写入


什么是文件系统

负责管理和存储文件信息的软件机构,在磁盘上组织文件的方法。

即使读者可能不了解文件系统,读者也一定对“文件”这个概念十分熟悉,数据在PC上是以文件的形式储存在磁盘中的,这些数据的形式一般为ASCII码或者二进制形式出现。在之前我已经写过了SPI Flash芯片W25Q64的驱动函数,我们可以非常方便的在SPI FLASH芯片上读写数据。如需要记录一系列字符串时,可以先把字符串转化为ASCII码,存储在数组中,然后调用SPI_FLASH_BufferWrite函数,把数组内容写入到SPI Flash芯片的指定地址上,在需要的时候从地址把数据读取出来,再对读出来的数据以ASCII码的格式进行解读。

但是,这样存储数据会带来极大的不便,如难以记录有效数据的位置,难以确定存储介质的剩余空间,以及应以何种格式来解读数据。就如同一个巨大的图书馆无人管理,杂乱无章的存放着各种书籍,难以查找所需的文档。这样直接存储数据的方式对于小容量的EEPROM还可以接受,但对于SPI FLASH芯片或者SD卡之类的大容量设备,我们需要一种高效的方式来管理它的存储内容。

这些管理方式即为文件系统,她是为了存储和管理数据,而在存储介质建立的一种组织结构,这些结构包括操作系统引导区,目录和文件。常见的windows下的文件系统格式包括FAT32、NTFS、exFAT.在使用文件系统之前,要先对存储介质进行格式化。格式化先擦除原来内容,在存储介质上新建立一个文件分配表和目录。这样,文件系统就可以记录数据存放的物理地址、剩余空间。

使用文件系统时,数据都以文件的形式存储。写入新文件时先在目录中创建一个文件索引,它指示了文件存放的物理地址,再把数据存储到该地址中。当需要读取数据时,可以从目录中找到该文件的索引,进而在相应的地址中读取出数据。具体还涉及到逻辑地址、簇大小、不连续存储等一系列辅助结构或处理过程。

文件系统的存在使我们在存储数据时,不再是简单的向某物理地址直接读写,而是要遵循它的读写格式。如经过逻辑转换,一个完整的文件可能被分开成多段存储到不连续的物理地址,使用目录或者链表的方式来获知下一段的位置。

常用的文件系统

FAT/FATFS 小型嵌入式系统

NTFS           WINDOWS

CDFS           光盘

exFAT 更适合用于闪存

FATFS优点:免费开源,专门为小型嵌入式系统设计,完全用C语言编写;支持FAT12,FAT16与FAT32,支持多种存储媒介,有独立的缓冲区,可对多个文件进行读写,可裁剪的文件系统(极为重要)

FATFS的特点:

  • Windows兼容的FAT文件系统,(支持FAT12,FAT16,FAT32)
  • 与平台无关,移植简单
  • 代码量少,效率高
  • 多种配置选项,可以裁剪
    • 支持多卷(物理驱动器或分区,最多10个卷)
    • 多个ANSI/OEM代码页包括DBCS
    • 支持长文件名、ANSI/OEM或Unicode
    • 支持RTOS
    • 支持多种扇区大小
    • 只读,最小化的API和I/O缓冲区等

由于它以上的特点,使得FATFS在嵌入式系统中被广泛应用

FATFS层次结构:

  1.  底层接口,包括存储媒介读/写接口(disk I/O)和供给文件创建修改时间的实时时钟,需要我们根据平台和存储介质编写移植代码
  2. 中间层FATFS模块,实现了FAT文件读/写协议。FATFS模块提供的是ff.c和ff.h。除非有必要,使用者一般不用修改,使用时将头文件直接包含进去即可。
  3. 最顶层是应用层,使用者无需理会FATFS的内部结构和复杂的FAT协议,只需要调用FATFS模块提供给用户的一系列应用接口函数,如f_open,f_read,f_write,f_close等,就可以像在PC上读写文件那样简单。

FATFS的整个系统可以在FATFS的官网下载:官网地址

同时在官网还可以查看每个函数的说明,同时大部分的函数都带有示例,是不错的学习资源。

系统包的结构:

 diskio.c和diskio.h是硬件层,需要根据存储介质来修改

ff.c和ff.h是FATFS的文件系统层和文件系统的API层

移植步骤:

  1. 数据类型:在integer.h里面定义好数据的类型,这里需要了解你用的编译器的数据类型,并根据编译器定义好数据类型。
  2. 配置:通过ffconf.h配置FATFS的相关功能,以满足你的需要
  3. 函数编写:打开diskio.c,进行底层驱动编写,一般需要编写6个接口

相关配置宏:

_FS_TINY        mini版本的FATFS

_FS_READONLY        设置只读,可以减少所占的空间

_FS_MINIMIZE        削减函数

_USE_STRFUNC        字符及字符串操作函数

_USE_MKFS        是否启用格式化

_USE_FASTSEEK        使能快速定位

_USE_LABEL                是否支持磁盘盘符的设置和读取

_CODE_PAGE        设置语言936——中文GBK编码

_USE_LFN                是否支持长文件名,值不同存储的位置不同

_MAX_LFN                文件名的最大长度

_VOLUMES                支持的逻辑设备数目

_MAX_SS                扇区缓冲最大值,一般为512

FATFS文件系统移植实验

FATFS程序结构图

移植FATFS文件系统之前,我们先通过FATFS的程序结构图了解FATFS在程序中的关系网络,见图FATFS程序结构图。

 用户应用程序需要由用户编写,想实现什么功能就编写什么的程序,一般我们只用到f_mount()、f_open()、f_write()、f_read()就可以实现文件的读写操作。

FATFS组件是FATFS的主体,文件都在源码src中,其中ff.c和ff.h、integer.h以及diskio.h四个文件我们不需要改动,只需要修改ffconf.h和diskio.c两个文件。

底层设备输入输出要求实现存储设备的读写操作函数、存储设备信息获取函数等等。我们使用SPI FLASH芯片作为物理设备,在之前已经编写好的驱动程序,这里我们可以直接使用。

FATFS底层设备驱动函数

FATFS文件系统与底层介质的驱动分离开来,对底层介质的操作都要交给用户去实现,它仅仅是提供了一个函数接口而已。表FATFS移植需要用户支持函数为FATFS移植时用户必须支持的函数。通过表FATFS移植需要用户支持函数我们可以清晰知道很多函数是在一定条件下才需要添加的,只有前三个函数是必须添加的。我们完全可以根据实际需求选择实现用到的函数。

前三个函数是实现读文件的最基本需求。接下来三个函数是实现创建文件、修改文件需要的。为实现格式化功能,需要在disk_ioctl添加两个获取物理设备信息选项。我们一般只需要实现前面6个函数就可以了,已经满足大部分功能。

底层设备驱动函数是diskio.c文件,我们的目的就是把diskio.c中的函数接口与SPI Flash芯片驱动连接起来。总共有5个函数,分别为设备状态获取(disk_status)、设备初始化(disk_initialize)、扇区读取(disk_read)、扇区写入(disk_write)、其他控制(disk_ioctl)。

下面我们结合SPI Flash芯片驱动函数做详细讲解。

宏定义

/*为每个设备定义一个物理编号*/
#define     ATA    0    //预留SD卡用
#define     SPI_FLASH    1    //外部SPI FLASH

这个两个宏定义在FATFS文件系统中非常重要,FATFS是支持多物理设备的,必须为每个物理设备定义一个不同的编号。

SD卡是预留接口,是和SDIO有关的内容。

设备状态获取

DSTATUS disk_status(

    BYTE    prdv    /*物理编号*/
)
{
    DSTATUS    status = STA_NOINIT;

    switch(pdrv){

       case ATA :    /*SD CARD*/
            break;
        case SPI_FLASH :
        /*spi flash 状态检测:读取SPI_FLASH设备ID*/
        if(sFLASH_ID == SPI_FLASH_ReadID())
        /*设备ID读取结果正常*/
        {
                status &= ~STA_NOINIT;
        }
        else
        {
                /*设备ID读取结果失误*/
                status = STA_NOINIT;
        }
            break;
default:
        status = STA_NOINIT;

        }
    return status;

}

disk_status函数只有一个参数pdrv,表示物理编号。一般我们都是使用switch函数实现对prdv的分支判断。对于SD卡只是预留接口,留空即可。对于SPI FLASH芯片,我们直接调用在SPI_FLASH_ReadID()获取设备ID,然后判断是否正确,如果正确,函数返回正常标准;如果错误,函数返回异常标志。SPI_FLASHReadID()是定义在之前的SPI FLASH驱动文件中的函数。

设备初始化

DSTATUS disk_initialize(
    BYTE prdv    /*物理编号*/
)
{
    uint16_t i;
    DSTATUS stauts = STA_NOINIT;
    switch(pdrv){
        case ATA :
        /*sd card*/
        break;
        case SPI_FLASH :
        SPI_FLASH_Init();
        /*延时一小段时间*/
        i= 500;    
        while(i--);
        /*唤醒SPI FLASH*/
        SPI_FLASH_WAKEUP();
        /*获取SPI FLASH芯片状态*/
        status = disk_status(SPI_FLASH);
        break;
     default :
        status = STA_NOINIT;

    }
    return status;

}

disk_initialize函数也是有一个参数prdv,用来指定设备物理编号。对于SPI FLASH芯片我们调用SPI_FLASH_Init()函数实现对SPI FLASH芯片引脚GPIO初始化配置以及SPI通信参数配置。SPI_FLASH_WAKEUP()函数唤醒SPI FLASH芯片,当芯片处于睡眠模式时需要唤醒芯片才可以进行读写。

最后调用disk_status函数获取SPI FLASH芯片状态,并返回状态值。

读取扇区

DRESULT disk_read(
        BYTE pdrv,    //设备物理编号
        BYTE *buff,    //数据缓存区
        DWORD sector,    //扇区首地址
        UINT count        //扇区个数
)
{
    DRESULT status = RES_PARERR;
    switch(pdrv)
    {
        case ATA :
        break;
        case SPI_FLASH :
        /*扇区偏移2MB,外部FLASH文件系统空间放在SPI FLASH后面的6MB空间*/
        sector += 512;
        SPI_FLASH_BufferRead(buff,sector<<12,count<<12);
        stauts = RES_OK;
        break;
        default :
        status = RES_PARERR;

    }
    return status;

}

disk_read函数有4个形参,pdrv为设备物理编号,buff是一个BYTE型指针变量,buff指向用来存放读取到数据的存储首地址。sector是一个DWORD类型变量,指定要读取数据的扇区首地址,count是一个UINT类型变量,指定扇区数量。

扇区写入

DRESULT disk_write(
    BYTE prdv,    //设备物理编号
    const BYTE *buff, //欲写入数据的缓存区
    DWORD sector, //扇区首地址
    UINT count    //扇区个数

)
{
    uint32_t write_addr;
    DRESULT status = RES_PARERR;
     if(!count){

                return RES_PARERR;
                }
      switch(prdv){

        case ATA :
            break;

        case SPI_FALSH :
        /*扇区偏移2MB,外部FLASH文件系统空间放在SPI FLASH 后面的6MB空间*/
        sector += 512;
        write_addr = sector<<12;
        SPI_FLASH_SectorErase(write_addr);
        SPI_FALSH_BufferWrite((u8 *)buff,write_addr,count<<12);
        status = RES_OK;
        break;
    default:
        status = RES_PARERR;

        }

    return status;

}

disk_write函数有四个形参,pdrv为设备物理编号,buff指向待写入扇区数据的首地址。sector指定要写入数据的扇区首地址,count指定扇区数量。对于SPI FLASH芯片,在写入数据之前,需要先擦除,所以用到扇区擦除函数(SPI_FLASH_SectorErase).然后就是在调用数据写入函数(SPI_FLASH_BufferWrite)把数据写入到指定位置。

FATFS给用户提供了大量的API函数,可以满足我们对文件的各种操作。

f_mount                              注册/注销一个工作区域

f_open                                       打开/创建一个文件

f_close                                        关闭一个文件

f_read                                        读文件

f_write                                         写文件

f_lseek                                        移动文件读写指针

f_sync                                冲洗缓冲数据Flush Cached Data

f_forward                        直接转移文件数据到一个数据流

f_stat                                获取文件状态

f_opendir                打开一个目录

f_closedir                关闭一个已经打开的目录

诸如其他的还有很多的相关的文件操作,具体的和C语言的文件操作大同小异,可以去FATFS文件系统官网看看详细的文件操作函数。这里笔者不再赘述。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32学习——FATFS文件系统 的相关文章

  • Pytorch 入门 ----学习笔记

    本文是在参加DataWhale开源组队学习 深入浅出Pytorch 过程中 整理的学习笔记 Pytorch 基础知识 张量 张量的创建 张量 也叫做多维数组 常常我们对于一维张量也叫做标量 二位张量叫做矩阵 大部分时候 张量是三维及三维以上

随机推荐

  • LeetCode 1302. 层数最深叶子节点的和

    给你一棵二叉树的根节点 root 请你返回 层数最深的叶子节点的和 示例 1 输入 root 1 2 3 4 5 null 6 7 null null null null 8 输出 15 解法一 递归法 每次递归返回当前节点所处层数和以该节
  • AutoSet 根据配置表信息解析到新框架里面

    using System using System Collections Generic using System ComponentModel using System IO using System Reflection using
  • 深度学习理论篇

    目录 传统神经网络nn整体 nn总结 前向传播 像素点参数预处理 input 权重参数初始化 得分函数 W x 激活函数 f x 分类问题 反向传播 更新W 损失函数 output和target比较 卷积神经网络CNN CNN总结 卷积层
  • Mat类下的data指针的深刻理解

    摘要 本文主要介绍了Mat类中data指针访问每一个像素的方法 在访问和修改图像矩阵像素值的时候 我们经常会用到at ptr 以及迭代器MatIterator等 对于用Mat存储的图像的像素值的访问方法 文章http blog csdn n
  • html标记符之间不可以,HTML期末复习试题及参考答案

    HTML期末复习题 含答案 第1题判断正误 1 HTML标记符的属性一般不区分大小写 对 2 网站就是一个链接的页面集合 对 3 将网页上传到Internet时通常采用FTP方式 对 4 所有的HTML标记符都包括开始标记符和结束标记符 错
  • python数据评估

    未清理的数据 脏数据与杂乱数据 未清理数据分为两种 脏数据 也称为低质量数据 低质量数据存在内容问题 杂乱数据 也称为不整洁数据 不整洁数据存在结构问题 将数据可视化 例如 绘制图形 是编程评估的一部分 而非我们在这里说的目测评估 即通过目
  • NodeJs服务器启动后在浏览器访问时中文显示乱码处理方法

    创建一个叫 server js 的文件 并写入以下代码 使用 require 指令来载入 http 模块 并将实例化的 HTTP 赋值给变量 http var http require http 使用 http createServer 方
  • Dice相似系数(Dice Similarity Coefficient, DSC)

    Dice相似系数 Dice Similarity Coefficient DSC 分母可以解析为 FP TP 所有分类为阳性的样本 TP FN 真阳 假阴 所有真的是阳性的样本
  • LitJSON之JSON读取和写入

    JSON读取和写入 使用JsonReader例子 使用JsonWriter 目录 JSON读取和写入 一些开发者可能熟悉JSON数据的另一种处理方法 即通过利用类似流的方式来读取和写入数据 实现这种方法的是JsonReader类和 Json
  • jenkins+newman+postman持续集成环境搭建

    目录 一 Newman简介 二 Newman应用 三 安装newman 四 Html报告插件安装 五 安装nodejs 六 Jenkins集成步骤 一 Newman简介 Newman是一款基于Node js开发的 可以运用postman工具
  • jQuery的scroll

    scrollTop垂直滚动 scrollLeft水平滚动 scrollTop 读取或设置滚动条的y坐标 代码示例如下
  • echarts修改柱状图的宽度

    echarts修改柱状图的宽度 series bar barWidth 自适应 numberstring 柱条的宽度 不设时自适应 可以是绝对值例如 40 或者百分数例如 60 百分数基于自动计算出的每一类目的宽度 在同一坐标系上 此属性会
  • Hx711称重模块+STM32+CubeMX

    文章目录 一 模块和接线 二 CubeMX配置 1 时钟及sys 2 IO口 1 数据线DT设置为Input 2 时钟线SCK设置为Output 3 串口 4 后续配置 三 程序 1 main c 2 hx711 c 3 hx711 h 4
  • R(N)

    http acm hdu edu cn showproblem php pid 3835 Problem Description We know that some positive integer x can be expressed a
  • vue 动态修改margin-top_详解 vue 组件三大核心概念

    给前端大全加星标 提升前端技能 作者 前端工匠 公号 浪里行舟 本文来自作者投稿 前言 本文主要介绍属性 事件和插槽这三个vue基础概念 使用方法及其容易被忽略的一些重要细节 如果你阅读别人写的组件 可以从这三个部分展开 它们可以帮助你快速
  • 区块链学习(1) sha256算法 c语言实现

    sha256算法 网上有很多的介绍 摘抄一段如下 SHA 256 算法输入报文的最大长度不超过2 64 bit 输入按512 bit 分组进行处理 产生的输出是一个256 bit 的报文摘要 该算法处理包括以下几步 STEP1 附加填充比特
  • Python学习笔记——多线程

    mtsleepA import thread from time import sleep ctime loops 4 2 def loop nloop nsec lock print start loop nloop at ctime s
  • Node.js mm131图片批量下载爬虫1.00 iconv协助转码

    mm131图片批量下载爬虫1 00 2017年11月15日 内置http模块 var http require http 内置文件处理模块 用于创建目录和图片文件 var fs require fs 用于转码 非Utf8的网页如gb2132
  • java反射详解

    本篇文章依旧采用小例子来说明 因为我始终觉的 案例驱动是最好的 要不然只看理论的话 看了也不懂 不过建议大家在看完文章之后 在回过头去看看理论 会有更好的理解 下面开始正文 案例1 通过一个对象获得完整的包名和类名 1 2 3 4 5
  • STM32学习——FATFS文件系统

    目录 什么是文件系统 常用的文件系统 FATFS的特点 FATFS层次结构 移植步骤 相关配置宏 FATFS文件系统移植实验 FATFS程序结构图 FATFS底层设备驱动函数 宏定义 设备状态获取 设备初始化 读取扇区 扇区写入 什么是文件