STM32CubeMX學(xué)習(xí)筆記(27)——FatFs文件系統(tǒng)使用(操作SD卡)

一、FatFs簡(jiǎn)介

FatFs 是面向小型嵌入式系統(tǒng)的一種通用的 FAT 文件系統(tǒng)。它完全是由 ANSI C 語(yǔ)言編寫(xiě)并且完全獨(dú)立于底層的 I/O 介質(zhì)。因此它可以很容易地不加修改地移植到其他的處理器當(dāng)中,如 8051、PIC、AVR、SH、Z80、H8、ARM 等。FatFs 支持 FAT12、FAT16、FAT32 等格式,所以我們利用前面寫(xiě)好的 SPI Flash 芯片驅(qū)動(dòng),把 FatFs 文件系統(tǒng)代碼移植到工程之中,就可以利用文件系統(tǒng)的各種函數(shù),對(duì) SPI Flash 芯片以“文件”格式進(jìn)行讀寫(xiě)操作了。

FatFs 文件系統(tǒng)的源碼可以從 fatfs 官網(wǎng)下載:
http://elm-chan.org/fsw/ff/00index_e.html

1.1 FatFs文件系統(tǒng)布局

簇是文件存儲(chǔ)的最小單元,F(xiàn)AT32分區(qū)大小與對(duì)應(yīng)簇空間大小關(guān)系如下表示:

分區(qū)空間大小 簇空間大小 每個(gè)簇包含的扇區(qū)數(shù)
< 8GB 4KB 8
[ 8GB, 16GB ) 8KB 16
[ 16GB, 32GB ) 16KB 32
>= 32GB 32KB 64

例如:創(chuàng)建一個(gè)50字節(jié)的test.txt文件,文件大小是50字節(jié),但是占用磁盤(pán)空間為4096字節(jié)(一個(gè)簇)

1.2 FatFs層次結(jié)構(gòu)

  • 最頂層是應(yīng)用層:使用者只需要調(diào)用FATFS模塊提供給用戶的一系列應(yīng)用接口函數(shù)(如f_open, f_read, f_write和f_close等),就可以像在PC上讀寫(xiě)文件那樣簡(jiǎn)單

  • 中間層FATFS模塊:實(shí)現(xiàn)了FAT文件讀寫(xiě)協(xié)議;它提供了ff.c和ff.h文件,一般情況下不用修改,使用時(shí)將頭文件包含進(jìn)去即可

  • 最底層是FATFS模塊的底層接口:包括存儲(chǔ)媒介讀寫(xiě)接口和供給文件創(chuàng)建修改時(shí)間的實(shí)時(shí)時(shí)鐘,需要在移植時(shí)編寫(xiě)對(duì)應(yīng)的代碼

FATFS源碼相關(guān)文件介紹如下表示;移植FATFS模塊時(shí),一般只需要修改2個(gè)文件(即ffconf.hdiskio.c

與平臺(tái)無(wú)關(guān):

文件 說(shuō)明
ffconf.h FATFS模塊配置文件
ff.h FATFS和應(yīng)用模塊公用的包含文件
ff.c FATFS模塊
diskio.h FATFS和disk I/O模塊公用的包含文件
interger.h 數(shù)據(jù)類(lèi)型定義
option 可選的外部功能(比如支持中文)

與平臺(tái)相關(guān):

文件 說(shuō)明
diskio.c FATFS和disk I/O模塊接口層文件

1.3 FatFs API

1.3.1 f_mount

功能 在FatFs模塊上注冊(cè)、注銷(xiāo)一個(gè)工作區(qū)(文件系統(tǒng)對(duì)象)
函數(shù)定義 FRESULT f_mount(FATFS* fs, const TCHAR* path, BYTE opt)
參數(shù) fs:工作區(qū)(文件系統(tǒng)對(duì)象)指針
path:注冊(cè)/注銷(xiāo)工作區(qū)的邏輯驅(qū)動(dòng)器號(hào)
opt:注冊(cè)或注銷(xiāo)選項(xiàng)
返回 操作結(jié)果

1.3.2 f_open

功能 創(chuàng)建/打開(kāi)一個(gè)文件對(duì)象
函數(shù)定義 FRESULT f_open(FIL* fp, const TCHAR* path, BYTE mode)
參數(shù) fp:將被創(chuàng)建的文件對(duì)象結(jié)構(gòu)的指針
path:文件名指針,指定將創(chuàng)建或打開(kāi)的文件名
mode:訪問(wèn)類(lèi)型和打開(kāi)方法,由一下標(biāo)準(zhǔn)的一個(gè)組合指定的
返回 操作結(jié)果
模式 描述
FA_READ 指定讀訪問(wèn)對(duì)象??梢詮奈募凶x取數(shù)據(jù)。 與FA_WRITE 結(jié) 合可以進(jìn)行讀寫(xiě)訪問(wèn)。
FA_WRITE 指定寫(xiě)訪問(wèn)對(duì)象??梢韵蛭募袑?xiě)入數(shù)據(jù)。與FA_READ 結(jié)合 可以進(jìn)行讀寫(xiě)訪問(wèn)。
FA_OPEN_EXISTING 打開(kāi)文件。如果文件不存在,則打開(kāi)失敗。(默認(rèn))
FA_OPEN_ALWAYS 如果文件存在,則打開(kāi);否則,創(chuàng)建一個(gè)新文件。
FA_CREATE_NEW 創(chuàng)建一個(gè)新文件。如果文件已存在,則創(chuàng)建失敗。
FA_CREATE_ALWAYS 創(chuàng)建一個(gè)新文件。如果文件已存在,則它將被截?cái)嗖⒏采w。

1.3.3 f_close

功能 關(guān)閉一個(gè)打開(kāi)的文件
函數(shù)定義 FRESULT f_close(FIL* fp)
參數(shù) fp:指向?qū)⒈魂P(guān)閉的已打開(kāi)的文件對(duì)象結(jié)構(gòu)的指針
返回 操作結(jié)果

1.3.4 f_read

功能 從一個(gè)打開(kāi)的文件中讀取數(shù)據(jù)
函數(shù)定義 FRESULT f_read(FIL* fp, void* buff, UINT btr, UINT* br)
參數(shù) fp:指向?qū)⒈蛔x取的已打開(kāi)的文件對(duì)象結(jié)構(gòu)的指針
buff:指向存儲(chǔ)讀取數(shù)據(jù)的緩沖區(qū)的指針
btr:要讀取的字節(jié)數(shù)
br:指向返回已讀取字節(jié)數(shù)的UINT變量的指針,返回為實(shí)際讀取的字節(jié)數(shù)
返回 操作結(jié)果

1.3.5 f_write

功能 寫(xiě)入數(shù)據(jù)到一個(gè)已打開(kāi)的文件
函數(shù)定義 FRESULT f_write(FIL* fp, void* buff, UINT btw, UINT* bw)
參數(shù) fp:指向?qū)⒈粚?xiě)入的已打開(kāi)的文件對(duì)象結(jié)構(gòu)的指針
buff:指向存儲(chǔ)寫(xiě)入數(shù)據(jù)的緩沖區(qū)的指針
btw:要寫(xiě)入的字節(jié)數(shù)
bw:指向返回已寫(xiě)入字節(jié)數(shù)的UINT變量的指針,返回為實(shí)際寫(xiě)入的字節(jié)數(shù)
返回 操作結(jié)果

另外FatFs還有很多API操作函數(shù),在這里不再作詳細(xì)的介紹,詳細(xì)信息請(qǐng)查看FatFs文件系統(tǒng)官網(wǎng)。

二、新建工程

1. 打開(kāi) STM32CubeMX 軟件,點(diǎn)擊“新建工程”

2. 選擇 MCU 和封裝

3. 配置時(shí)鐘
RCC 設(shè)置,選擇 HSE(外部高速時(shí)鐘) 為 Crystal/Ceramic Resonator(晶振/陶瓷諧振器)


選擇 Clock Configuration,配置系統(tǒng)時(shí)鐘 SYSCLK 為 72MHz
修改 HCLK 的值為 72 后,輸入回車(chē),軟件會(huì)自動(dòng)修改所有配置

4. 配置調(diào)試模式
非常重要的一步,否則會(huì)造成第一次燒錄程序后續(xù)無(wú)法識(shí)別調(diào)試器
SYS 設(shè)置,選擇 Debug 為 Serial Wire

三、SDIO

STM32 控制器可以控制使用單線或 4 線傳輸,本開(kāi)發(fā)板設(shè)計(jì)使用 4 線傳輸。

3.1 參數(shù)配置

Connetivity 中選擇 SDIO 設(shè)置,并選擇 SD 4 bits Wide bus 四線SD模式


此時(shí) SDIO 對(duì)應(yīng)的管腳也被選中。

Parameter Settings 進(jìn)行具體參數(shù)配置。

Clock transition on which the bit capture is made: Rising transition。主時(shí)鐘 SDIOCLK 產(chǎn)生 CLK 引腳時(shí)鐘有效沿選擇,可選上升沿或下降沿,它設(shè)定 SDIO 時(shí)鐘控制寄存器(SDIO_CLKCR)的 NEGEDGE 位的值,一般選擇設(shè)置為上升沿。

SDIO Clock divider bypass: Disable。時(shí)鐘分頻旁路使用,可選使能或禁用,它設(shè)定 SDIO_CLKCR 寄存器的 BYPASS 位。如果使能旁路,SDIOCLK 直接驅(qū)動(dòng) CLK 線輸出時(shí)鐘;如果禁用,使用 SDIO_CLKCR 寄存器的 CLKDIV 位值分頻 SDIOCLK,然后輸出到 CLK 線。一般選擇禁用時(shí)鐘分頻旁路。

SDIO Clock output enable when the bus is idle: Disable the power save for the clock。節(jié)能模式選擇,可選使能或禁用,它設(shè)定 SDIO_CLKCR 寄存器的 PWRSAV 位的值。如果使能節(jié)能模式,CLK 線只有在總線激活時(shí)才有時(shí)鐘輸出;如果禁用節(jié)能模式,始終使能 CLK 線輸出時(shí)鐘。

SDIO hardware flow control: The hardware control flow is disabled。硬件流控制選擇,可選使能或禁用,它設(shè)定 SDIO_CLKCR 寄存器的 HWFC_EN 位的值。硬件流控制功能可以避免 FIFO 發(fā)送上溢和下溢錯(cuò)誤。

SDIOCLK clock divide factor: 6。時(shí)鐘分頻系數(shù),它設(shè)定 SDIO_CLKCR 寄存器的 CLKDIV 位的值,設(shè)置 SDIOCLK 與 CLK 線輸出時(shí)鐘分頻系數(shù):CLK 線時(shí)鐘頻率=SDIOCLK/([CLKDIV+2])。

SDIO_CK 引腳的時(shí)鐘信號(hào)在卡識(shí)別模式時(shí)要求不超過(guò) 400KHz,而在識(shí)別后的數(shù)據(jù)傳輸模式時(shí)則希望有更高的速度(最大不超過(guò) 25MHz),所以會(huì)針對(duì)這兩種模式配置 SDIOCLK 的時(shí)鐘。

這里參數(shù)描述建議將SDIOCLK clock divede factor 參數(shù)使用默認(rèn)值為0,SDIOCLK為72MHz,可以得到最大頻率36MHz,但請(qǐng)注意,有些型號(hào)的SD卡可能不支持36MHz這么高的頻率,所以還是要以實(shí)際情況而定。

3.2 配置DMA

SDIO 外設(shè)支持生成 DMA 請(qǐng)求,使用 DMA 傳輸可以提高數(shù)據(jù)傳輸效率,因此在 SDIO 的控制代碼中,可以把它設(shè)置為 DMA 傳輸模式或輪詢(xún)模式,ST 標(biāo)準(zhǔn)庫(kù)提供 SDIO 示例中針對(duì)這兩個(gè)模式做了區(qū)分處理。應(yīng)用中一般都使用DMA 傳輸模式。

點(diǎn)擊 DMA Settings 添加 SDIO 對(duì)應(yīng) DMA2 的通道4。DMA模式選擇循環(huán)模式,方向選為內(nèi)存到外設(shè)。

  • Priority
    當(dāng)發(fā)生多個(gè) DMA 通道請(qǐng)求時(shí),就意味著有先后響應(yīng)處理的順序問(wèn)題,這個(gè)就由仲裁器也管理。仲裁器管理 DMA 通道請(qǐng)求分為兩個(gè)階段。第一階段屬于軟件階段,可以在 DMA_CCRx 寄存器中設(shè)置,有 4 個(gè)等級(jí):非常高、高、中和低四個(gè)優(yōu)先級(jí)。第二階段屬于硬件階段,如果兩個(gè)或以上的 DMA 通道請(qǐng)求設(shè)置的優(yōu)先級(jí)一樣,則他們優(yōu)先級(jí)取決于通 道編號(hào),編號(hào)越低優(yōu)先權(quán)越高,比如通道 0 高于通道 1。在大容量產(chǎn)品和互聯(lián)型產(chǎn)品中,DMA1 控制器擁有高于 DMA2 控制器的優(yōu)先級(jí)。
  • Mode
    Normal 表示單次傳輸,傳輸一次后終止傳輸。
    Circular 表示循環(huán)傳輸,傳輸完成后又重新開(kāi)始繼續(xù)傳輸,不斷循環(huán)永不停止。
  • Increment Address
    Peripheral 表示外設(shè)地址自增。
    Memory 表示內(nèi)存地址自增。
  • Data Width
    Byte 一個(gè)字節(jié)。
    Half Word 半個(gè)字,等于兩字節(jié)。
    Word 一個(gè)字,等于四字節(jié)。

3.3 配置NVIC

DMA及SDIO中斷設(shè)置,原則是全局中斷優(yōu)先級(jí)高于DMA中斷


四、FATFS

4.1 參數(shù)配置

Middleware 中選擇 FATFS 設(shè)置,并勾選 SD Card 配置為SD卡

  • Function Parameters 跳過(guò)

  • Locale and Namespace Parameters:

    • CODE_PAGE(Code page on target): Simplified Chinese GBK(DBCS,OEM,Windows) 支持簡(jiǎn)體中文編碼
    • USE_LFN(Use Long Filename): Enabled with dynamic working buffer on the STACK 支持長(zhǎng)文件名,并指定使用??臻g為緩沖區(qū)

緩存工作區(qū)為什么放在棧?其實(shí)fatfs提供了三個(gè)選項(xiàng):BSS,STACK , HEAP,根據(jù)個(gè)人情況選一個(gè)。
在BSS上啟用帶有靜態(tài)工作緩沖區(qū)的LFN,不能動(dòng)態(tài)分配。
如果選擇了HEAP(堆)且自己有屬于自己的malloc就去重寫(xiě)ff_memalloc ff_memfree函數(shù)。如果是庫(kù)的malloc就不需要。
一般都選擇使用STACK(棧),能動(dòng)態(tài)分配。
當(dāng)使用堆棧作為工作緩沖區(qū)時(shí),請(qǐng)注意堆棧溢出。

  • Physical Drive Parameters:
    • VOLUMES(Logical drivers): 2 指定物理設(shè)備數(shù)量,這里設(shè)置為 2,包括預(yù)留 SD 卡和 SPI Flash 芯片
    • MAX_SS(Maximum Sector Size): 512 指定扇區(qū)大小的最大值。SD 卡扇區(qū)大小一般都為 512 字節(jié),SPI Flash 芯片扇區(qū)大小一般設(shè)置為 4096 字節(jié),所以需要把 _MAX_SS 改為 512
    • MIN_SS(Minimum Sector Size): 512 指定扇區(qū)大小的最小值

4.2 配置SD卡檢測(cè)引腳

SD卡插入檢測(cè)引腳,如果不配置一個(gè)引腳生成文件時(shí)會(huì)報(bào)錯(cuò),所以這里即使沒(méi)有硬件連接,也可以任意設(shè)置一引腳使用,生成工程后注釋代碼。



4.3 增大??臻g

將最小棧空間改到 0x1000

注意:由于剛才設(shè)置長(zhǎng)文件名動(dòng)態(tài)緩存存儲(chǔ)在堆中,故需要增大堆大小,如果不修改則程序運(yùn)行時(shí)堆會(huì)生成溢出,程序進(jìn)入硬件錯(cuò)誤中斷(HardFault),死循環(huán)。

4.4 生成代碼

輸入項(xiàng)目名和項(xiàng)目路徑


選擇應(yīng)用的 IDE 開(kāi)發(fā)環(huán)境 MDK-ARM V5

每個(gè)外設(shè)生成獨(dú)立的 ’.c/.h’ 文件
不勾:所有初始化代碼都生成在 main.c
勾選:初始化代碼生成在對(duì)應(yīng)的外設(shè)文件。 如 GPIO 初始化代碼生成在 gpio.c 中。

點(diǎn)擊 GENERATE CODE 生成代碼

五、屏蔽SD卡插入檢測(cè)代碼

由于沒(méi)有檢測(cè)SD卡插入的硬件引腳,打開(kāi) bsp_driver_sd.c 文件,修改 BSP_SD_IsDetected() 函數(shù),始終返回SD_PRESENT:


六、修改main函數(shù)

定義相關(guān)變量:

FATFS fs;                       /* FatFs 文件系統(tǒng)對(duì)象 */
FIL file;                       /* 文件對(duì)象 */
FRESULT f_res;                  /* 文件操作結(jié)果 */
UINT fnum;                      /* 文件成功讀寫(xiě)數(shù)量 */
BYTE ReadBuffer[1024] = {0};    /* 讀緩沖區(qū) */
BYTE WriteBuffer[] =            /* 寫(xiě)緩沖區(qū) */
    "This is STM32 working with FatFs\r\n";

修改main函數(shù):

/**
  * @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 卡掛載文件系統(tǒng),文件系統(tǒng)掛載時(shí)會(huì)對(duì) SD 卡初始化
    f_res = f_mount(&fs, "0:", 1);
    
    /*----------------------- 格式化測(cè)試 ---------------------------*/
    printf("\r\n****** Register the file system object to the FatFs module ******\r\n");
    /* 如果沒(méi)有文件系統(tǒng)就格式化創(chuàng)建創(chuàng)建文件系統(tǒng) */
    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");
    }
    
    /*----------------------- 文件系統(tǒng)測(cè)試:寫(xiě)測(cè)試 -----------------------------*/
    /* 打開(kāi)文件,如果文件不存在則創(chuàng)建它 */
    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");
        /* 將指定存儲(chǔ)區(qū)內(nèi)容寫(xiě)入到文件內(nèi) */
        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);
        }
        /* 不再讀寫(xiě),關(guān)閉文件 */
        f_close(&file);
    }
    else
    {
        printf(" open file error : %d\r\n", f_res);
    }
    
    /*------------------- 文件系統(tǒng)測(cè)試:讀測(cè)試 ------------------------------------*/
    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);
    }
    /* 不再讀寫(xiě),關(guān)閉文件 */
    f_close(&file);
    /* 不再使用文件系統(tǒng),取消掛載文件系統(tǒng) */
    f_mount(NULL, "0:", 1);
    /* 操作完成,停機(jī) */

  /* 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學(xué)習(xí)筆記(6)——USART串口使用

八、工程代碼

鏈接:https://pan.baidu.com/s/1kgJ7AmbnW89yHcrgBzD_1w 提取碼:kg5a

九、注意事項(xiàng)

f_open、f_write、f_read如果偶爾有問(wèn)題;f_mkfs報(bào)錯(cuò) FS_DISK_ERR,可以加上了SDIO硬件流使能試試。


用戶代碼要加在 USER CODE BEGIN NUSER CODE END N 之間,否則下次使用 STM32CubeMX 重新生成代碼后,會(huì)被刪除。


? 由 Leung 寫(xiě)于 2021 年 12 月 7 日

? 參考:STM32CubeMX系列教程18:文件系統(tǒng)FATFS
    STM32CubeMX系列|FATFS文件系統(tǒng)
    1.6 Cubemx_STM32F103_NOOS SDIO_DMA_FATFS基于SD卡的FATFS測(cè)試(一)
    【STM32Cube_20】在SD卡上移植FATFS文件系統(tǒng)
    STM32CubeMX之SD卡+FatFs

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容