0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

【嵌入式SD NAND】基于FATFS/Littlefs文件系統(tǒng)的日志框架實現

jim ? 來源:雷龍發(fā)展 ? 作者:雷龍發(fā)展 ? 2024-03-14 18:13 ? 次閱讀

文章目錄

嵌入式】基于FATFS/Littlefs文件系統(tǒng)的日志框架實現

1. 概述

2. 設計概要

3. 設計實現

3.1 初始化 `init`

3.2 日志寫入 `write`

3.3 日志讀取 `read`

3.4 注銷 `deinit`

3.5 全部代碼匯總

4. 測試

5. 總結

1. 概述

那么在移植好了文件系統(tǒng)之后,我們又應該如何應用文件系統(tǒng)呢?

很多人會說,這個簡單,就操作文件嘛!open、read、write、close不就行了嗎!當然對于簡單的使用,掌握open、read、write、close,去存儲一兩個文件或者從一兩個文件中簡單的讀取下數據這確實沒有什么難度。但在實際應用中,特別是產品開發(fā)過程中,往往不只是簡單的操作一兩個文件就可以的,如果真是這樣,那費那么大勁移植文件系統(tǒng)多少有點浪費!

在實際項目開發(fā)中,往往需要依托文件系統(tǒng)操作諸多的文件,操作諸多的數據。如通過配置文件配置機器設備信息、通過升級文件進行產品升級、通過存放字庫文件實現多語言支持等等,這些都是比較簡單的操作,讀寫不是很頻繁,相對來說實現比較簡單,還有一類需求讀寫會相當頻繁,且大多數產品內都希望存在的,那便是日志文件,通過日志文件來記錄設備的運行數據。日志文件不同于其他功能,其往往需要具備幾個基本特性需求:

單個文件大小限制

日志總大小空間占用限制

自動循環(huán)覆蓋

網上也有一些開源的日志框架,如 Log4j,不過大都是基于 java / c ++ 實現的,雖然功能比較全面,但比較繁雜,且也難以移植應用于嵌入式開發(fā)中。而在嵌入式開發(fā)中,可能也受限于資源限制,并沒有發(fā)現不錯的基于文件系統(tǒng)的開源日志框架(至少博主目前沒有發(fā)現,有的話歡迎大家評論區(qū)討論 ),對于如何實現一個日志框架很多人一下子可能沒有頭緒,綜上,本文將分享一個簡單的基于文件系統(tǒng)的日志程序以供大家思考。

2. 設計概要

我們需要實現的日志模塊的核心需求為:

單個文件大小限制

日志總大小空間占用限制

自動循環(huán)覆蓋

對于一個模塊,對外僅需提供其操作的接口即可,內部的算法實現均無需對外開放,而對于此日志模塊,對外只需提供基本的以下四個接口即可:

初始化 init

寫日志 write

讀日志 read

注銷 deinit

關于日志存儲的核心思想如下:

寫數據之前先判斷當前操作的文件是否超出單個文件大小限制,如超出大小限制則進行日志輪轉,創(chuàng)建一個新的日志文件并判斷日志文件總大小是否超出限制,如果超出則刪除最早的那一份日志文件

關于日志存儲的詳細設計如下:

日志文件格式采用:.log ,當當前文件達到單個文件大小之后,進行文件輪轉;

假定當前限制日志每個日志文件大小為2048Byte,最多存儲10個文件;

當當前文件達到單個文件大小之后,迭代修改日志文件名:

.log -> .log.0

.log.0 -> .log.1

.log.1 -> .log.2

.log.8 -> .log.9

刪除 .log.9

ps:注意實際代碼操作的時候,文件修改順序是反過來的,也就是先 刪除.log.9再將.log.8->.log.9

3. 設計實現

3.1 初始化init

初始化部分代碼主要功能是完成日志數據結構體的構造,并通過傳入參數log_file_cfg_t cfg配置日志文件的配置信息,如單個日志文件大小、日志文件名、最多存放的日志文件數等內容,日志模塊初始化部分代碼如下:

log_file_t log_storage_init(log_file_cfg_t cfg)

{

log_file_t log = NULL;

log_file_cfg_t log_cfg = NULL;

log_file_read_t log_read = NULL;

log = (log_file_t)malloc(sizeof(struct log_file_config));

if (log == NULL)

goto error;

log_cfg = (log_file_cfg_t)malloc(sizeof(struct log_file_config));

if (log_cfg == NULL) {

free(log);

log = NULL;

goto error;

}

log_read = (log_file_read_t)malloc(sizeof(struct log_file_read));

if (log_read == NULL) {

free(log);

log = NULL;

free(log_cfg);

log_cfg = NULL;

goto error;

}

memcpy(log_cfg, cfg, sizeof(struct log_file_config));

log_read->rotate_index = 0;

log_read->file_offset = 0;

log->cfg = log_cfg;

log->read = log_read;

log->user_data = NULL;

error:

return log;

}

3.2 日志寫入write

日志寫入部分代碼主要分為兩大部分,一部分是正常寫入,另一部分是文件輪轉;當寫入的文件超過單個文件大小限制時,即會觸發(fā)文件輪轉操作。

在文件輪轉中,主要做的是:創(chuàng)建一個新的日志文件并判斷日志文件總大小是否超出限制,如果超出則刪除最早的那一份日志文件,具體設計細節(jié)可參考上文設計概要中的詳細設計部分。

實現代碼如下:

static int log_rotate(log_file_t log)

{

int ret = 0;

FILE *fp;

char old_filename[NAME_MAX + 10] = {0};

char new_filename[NAME_MAX + 10] = {0};

for (int i = log->cfg->rotate_num; i > 0; i --) {

memset(old_filename, 0, sizeof(old_filename));

memset(new_filename, 0, sizeof(new_filename));

snprintf(old_filename, sizeof(old_filename), i ? "%s_%d.log" : "%s.log", log->cfg->filename, i - 1);

snprintf(new_filename, sizeof(new_filename), "%s_%d.log", log->cfg->filename, i);

printf("old:%s new:%sn", old_filename, new_filename);

if ((fp = fopen(new_filename, "r")) != NULL) {

if (fclose(fp) != 0) {

ret = -1;

goto error;

}

if (remove(new_filename) != 0) {

ret = -2;

goto error;

}

}

if ((fp = fopen(old_filename, "r")) != NULL) {

if (fclose(fp) != 0) {

ret = -1;

goto error;

}

if (rename(old_filename, new_filename) != 0) {

ret = -3;

goto error;

}

}

}

error:

return ret;

}

int log_storage_write(log_file_t log, const unsigned char *buf, unsigned int len)

{

int ret = 0;

int file_size = 0;

char full_filename[NAME_MAX + 5] = {0};

FILE *fp = NULL;

if (log == NULL || log->cfg == NULL || log->read == NULL || buf == NULL || len == 0) {

ret = -1;

goto param_error;

}

snprintf(full_filename, sizeof(full_filename), "%s.log", log->cfg->filename);

printf("fullfilename:%sn", full_filename);

log_file_lock();

fp = fopen(full_filename, "a+b");

if (fp == NULL) {

ret = -2;

goto error;

}

fseek(fp, 0L, SEEK_END);

file_size = ftell(fp);

printf("file_size:%dn", file_size);

if ((file_size + len) > log->cfg->max_size) {

if (fclose(fp) != 0) {

ret = -3;

goto error;

}

int j = 0;

j = log_rotate(log);

printf("log rotate:%dn", j);

fp = fopen(full_filename, "a+b");

if (fp == NULL) {

ret = -2;

goto error;

}

}

if (fwrite(buf, len, 1, fp) != 1) {

fclose(fp);

ret = -4;

goto error;

}

error:

if (fp != NULL) {

if (fclose(fp) != 0) {

ret = -3;

goto error;

}

}

log_file_unlock();

param_error:

return ret;

}

3.3 日志讀取read

此處日志讀取在本文主題中非重點設計內容,因此此處做簡單設計,通過傳入參數判斷應該讀取哪一份文件之后進行直接讀取。設計代碼如下:

int log_storage_read(log_file_t log, unsigned int rotate_num, unsigned char *buf, unsigned int *len)

{

int ret = 0;

int file_size = 0;

char full_filename[NAME_MAX + 5] = {0};

FILE *fp = NULL;

if (log == NULL || log->cfg == NULL || log->read == NULL || buf == NULL || len == 0) {

ret = -1;

goto param_error;

}

if (rotate_num == 0)

snprintf(full_filename, sizeof(full_filename), "%s.log", log->cfg->filename);

else

snprintf(full_filename, sizeof(full_filename), "%s.log.%d", log->cfg->filename, rotate_num);

log_file_lock();

fp = fopen(full_filename, "a+b");

if (fp == NULL) {

ret = -2;

goto error;

}

/* check file length. */

fseek(fp, 0L, SEEK_END);

file_size = ftell(fp);

printf("file_size:%dn", file_size);

if (file_size < *len)

*len = file_size;

fseek(fp, 0L, SEEK_SET);

if (fread(buf, *len, 1, fp) != 1) {

ret = -3;

fclose(fp);

goto error;

}

error:

if (fp != NULL) {

if (fclose(fp) != 0) {

ret = -4;

goto error;

}

}

log_file_unlock();

param_error:

return ret;

}

3.4 注銷deinit

注銷的主要功能是將我們在init時創(chuàng)建的數據結構進行回收,如果模塊內部有功能處于打開裝填,也應關閉模塊的功能,此處我們僅需對init時創(chuàng)建的log_file_t log數據結構體進行注銷、內存回收即可,具體代碼實現如下:

int log_storage_deinit(log_file_t log)

{

if (log == NULL)

return -1;

if (log->cfg != NULL)

free(log->cfg);

if (log->read != NULL)

free(log->read);

if (log->user_data != NULL)

free(log->user_data);

free(log);

return 0;

}

3.5 全部代碼匯總

日志模塊內核頭文件:simple_storage.h

#ifndef __SIMPLE_STORAGE_H__

#define __SIMPLE_STORAGE_H__

#define NAME_MAX 40

struct log_file_config {

const char filename[NAME_MAX]; /* Filename of this type. */

int max_size; /* single file max size. */

int rotate_num; /* The number of files that support rotate. */

};

typedef struct log_file_config* log_file_cfg_t;

struct log_file_read {

int rotate_index; /* The rotate file index. */

int file_offset; /* The offset of the currently read file. */

};

typedef struct log_file_read* log_file_read_t;

struct log_file {

log_file_cfg_t cfg;

log_file_read_t read;

void *user_data;

};

typedef struct log_file* log_file_t;

log_file_t log_storage_init(log_file_cfg_t cfg);

int log_storage_write(log_file_t log, const unsigned char *buf, unsigned int len);

int log_storage_read(log_file_t log, unsigned int rotate_num, unsigned char *buf, unsigned int *len);

int log_storage_deinit(log_file_t log);

#endif /* __SIMPLE_STORAGE_H__ */

日志模塊內核文件:simple_storage.c

#include "simple_storage.h"

#include "simple_storage_port.h"

#include

#include

log_file_t log_storage_init(log_file_cfg_t cfg)

{

log_file_t log = NULL;

log_file_cfg_t log_cfg = NULL;

log_file_read_t log_read = NULL;

log = (log_file_t)malloc(sizeof(struct log_file_config));

if (log == NULL)

goto error;

log_cfg = (log_file_cfg_t)malloc(sizeof(struct log_file_config));

if (log_cfg == NULL) {

free(log);

log = NULL;

goto error;

}

log_read = (log_file_read_t)malloc(sizeof(struct log_file_read));

if (log_read == NULL) {

free(log);

log = NULL;

free(log_cfg);

log_cfg = NULL;

goto error;

}

memcpy(log_cfg, cfg, sizeof(struct log_file_config));

log_read->rotate_index = 0;

log_read->file_offset = 0;

log->cfg = log_cfg;

log->read = log_read;

log->user_data = NULL;

error:

return log;

}

static int log_rotate(log_file_t log)

{

int ret = 0;

FILE *fp;

char old_filename[NAME_MAX + 10] = {0};

char new_filename[NAME_MAX + 10] = {0};

for (int i = log->cfg->rotate_num; i > 0; i --) {

memset(old_filename, 0, sizeof(old_filename));

memset(new_filename, 0, sizeof(new_filename));

snprintf(old_filename, sizeof(old_filename), i ? "%s_%d.log" : "%s.log", log->cfg->filename, i - 1);

snprintf(new_filename, sizeof(new_filename), "%s_%d.log", log->cfg->filename, i);

printf("old:%s new:%sn", old_filename, new_filename);

if ((fp = fopen(new_filename, "r")) != NULL) {

if (fclose(fp) != 0) {

ret = -1;

goto error;

}

if (remove(new_filename) != 0) {

ret = -2;

goto error;

}

}

if ((fp = fopen(old_filename, "r")) != NULL) {

if (fclose(fp) != 0) {

ret = -1;

goto error;

}

if (rename(old_filename, new_filename) != 0) {

ret = -3;

goto error;

}

}

}

error:

return ret;

}

int log_storage_write(log_file_t log, const unsigned char *buf, unsigned int len)

{

int ret = 0;

int file_size = 0;

char full_filename[NAME_MAX + 5] = {0};

FILE *fp = NULL;

if (log == NULL || log->cfg == NULL || log->read == NULL || buf == NULL || len == 0) {

ret = -1;

goto param_error;

}

snprintf(full_filename, sizeof(full_filename), "%s.log", log->cfg->filename);

printf("fullfilename:%sn", full_filename);

log_file_lock();

fp = fopen(full_filename, "a+b");

if (fp == NULL) {

ret = -2;

goto error;

}

fseek(fp, 0L, SEEK_END);

file_size = ftell(fp);

printf("file_size:%dn", file_size);

if ((file_size + len) > log->cfg->max_size) {

if (fclose(fp) != 0) {

ret = -3;

goto error;

}

int j = 0;

j = log_rotate(log);

printf("log rotate:%dn", j);

fp = fopen(full_filename, "a+b");

if (fp == NULL) {

ret = -2;

goto error;

}

}

if (fwrite(buf, len, 1, fp) != 1) {

fclose(fp);

ret = -4;

goto error;

}

error:

if (fp != NULL) {

if (fclose(fp) != 0) {

//TODO: check the amount of disk space, delete if there is not enough space.

ret = -3;

goto error;

}

}

log_file_unlock();

param_error:

return ret;

}

int log_storage_read(log_file_t log, unsigned int rotate_num, unsigned char *buf, unsigned int *len)

{

int ret = 0;

int file_size = 0;

char full_filename[NAME_MAX + 5] = {0};

FILE *fp = NULL;

if (log == NULL || log->cfg == NULL || log->read == NULL || buf == NULL || len == 0) {

ret = -1;

goto param_error;

}

if (rotate_num == 0)

snprintf(full_filename, sizeof(full_filename), "%s.log", log->cfg->filename);

else

snprintf(full_filename, sizeof(full_filename), "%s.log.%d", log->cfg->filename, rotate_num);

log_file_lock();

fp = fopen(full_filename, "a+b");

if (fp == NULL) {

ret = -2;

goto error;

}

/* check file length. */

fseek(fp, 0L, SEEK_END);

file_size = ftell(fp);

printf("file_size:%dn", file_size);

if (file_size < *len)

*len = file_size;

fseek(fp, 0L, SEEK_SET);

if (fread(buf, *len, 1, fp) != 1) {

ret = -3;

fclose(fp);

goto error;

}

error:

if (fp != NULL) {

if (fclose(fp) != 0) {

ret = -4;

goto error;

}

}

log_file_unlock();

param_error:

return ret;

}

int log_storage_deinit(log_file_t log)

{

if (log == NULL)

return -1;

if (log->cfg != NULL)

free(log->cfg);

if (log->read != NULL)

free(log->read);

if (log->user_data != NULL)

free(log->user_data);

free(log);

return 0;

}

在日志模塊源文件的代碼中,我們可以看到實際每次操作文件的時候,都有調用一個函數鎖操作,考慮到不同平臺的鎖操作實現不一樣,因此將此部分通過函數導出來,放置在模塊的端口文件中。不同的平臺、系統(tǒng)根據各自的平臺和系統(tǒng)的情況進行實現,如像裸機編程這類不需要進行鎖操作的不進行函數實現即可。

日志模塊端口頭文件:simple_storage_port.c

#ifndef __SIMPLE_STORAGE_PORT_H__

#define __SIMPLE_STORAGE_PORT_H__

int log_file_init(void);

int log_file_lock(void);

int log_file_unlock(void);

#endif /* __SIMPLE_STORAGE_PORT_H__ */


日志模塊端口源文件:simple_storage_port.h

#include "simple_storage_port.h"

int log_file_init(void)

{

return 0;

}

int log_file_lock(void)

{

return 0;

}

int log_file_unlock(void)

{

return 0;

}

4. 測試

將以上代碼進行運行測試,硬件平臺如下:

控制器stm32f103vet6,野火指南者開發(fā)板

存儲芯片: CS創(chuàng)世 SD nand,型號:CSNP4GCR01-AMW

文件系統(tǒng): FATFS,注意此日志不受文件系統(tǒng)限制

操作系統(tǒng)RT-Thread,此模塊與操作系統(tǒng)無關,此處只是方便使用故自行移植了rtthread

wKgZomXyza2APA-TACTREhVCSEw691.png

應用層代碼如下:

int main(void)

{

/* 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_SDIO_SD_Init();

MX_USART1_UART_Init();

MX_FATFS_Init();

/* USER CODE BEGIN 2 */

struct log_file_config log_cfg = {

.filename = "test",

.max_size = 2048,

.rotate_num = 10,

};

log_file_t log = NULL;

log = log_storage_init(&log_cfg);

if (log == NULL)

return;

/* USER CODE END 2 */

/* Infinite loop */

/* USER CODE BEGIN WHILE */

unsigned char buf[2048] = {0};

int len = 0;

while (1) {

// ... 省略用戶代碼

/* 寫入測試 */

for (int i = 0; i < 2048; i++) {

log_storage_write(log, "hello world", sizeof("hello world"));

rt_thread_mdelay(100);

}

/* 讀取測試 */

len = sizeof(buf);

memset(buf, 0, sizeof(buf));

log_storage_read(log, 1, buf, &len);

for (int i = 0; i < len; i ++)

rt_kprintf("%c", buf[i]);

rt_thread_mdelay(1000);

}

}


測試結果如下:

msg> hello worldhello world hello world hello world hello world hello world hello world hello world hello world ...省略

msh > ls

test.log 2046

test.log.0 2046

test.log.1 2046

test.log.2 2046

test.log.3 2046

test.log.4 2046

5. 總結

綜上便是基于文件系統(tǒng)的簡易日志模塊設計的全部內容了,雖然簡陋了點,但相信對于大部分沒有接觸過日志系統(tǒng)設計的人來說提供了很好的一條設計思路。

也正因為簡易,給大家對于日志系統(tǒng)設計的優(yōu)化留足了大量的優(yōu)化空間。比如:

文件輪轉的時候需要對每個文件的文件名進行修改,是否可以有更好的方式不用每個文件都修改呢?

文件名的設計是不方便閱讀的,是否可以引入時間參數?

文件名設計如何引入了時間參數,當設備RTC備用電池掉電的時候又如何保證文件不會被錯誤覆蓋?

文件的讀取顯然優(yōu)化空間更大,實際上用戶不應該傳入rotate_num 參數,因為這是模塊內部的參數,用戶不可感知的

文件讀取如何做到分多次讀取一個文件的內容,且不會重復,是順序讀取?

等等,以上只是我簡單想到的幾點內容,大家不妨思考下如何實現方案更好呢?當然又還有哪些需求是需要引入的呢,也歡迎大家在評論區(qū)留言,關注我,后續(xù)抽時間再分享下改良版日志系統(tǒng)?。?!

審核編輯 黃宇

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯系本站處理。 舉報投訴
  • 文件系統(tǒng)

    關注

    0

    文章

    278

    瀏覽量

    19830
  • 開發(fā)板
    +關注

    關注

    25

    文章

    4769

    瀏覽量

    96150
  • 存儲芯片
    +關注

    關注

    11

    文章

    874

    瀏覽量

    42868
  • RT-Thread
    +關注

    關注

    31

    文章

    1239

    瀏覽量

    39425
收藏 人收藏

    評論

    相關推薦

    STM32CubeMx入門教程(10):Fatfs文件系統(tǒng)的應用

    導語"fatfs是一個小型的文件系統(tǒng),在小型的嵌入式系統(tǒng)中使用非常的廣泛,STM32CubeMx自帶該文件系統(tǒng),我們通過簡單的配置就能夠使用
    發(fā)表于 07-12 11:39 ?4276次閱讀
    STM32CubeMx入門教程(10):<b class='flag-5'>Fatfs</b><b class='flag-5'>文件系統(tǒng)</b>的應用

    STM32+SD NAND(貼片SD卡)完成FATFS文件系統(tǒng)移植與測試

    這篇文章就手把手教大家,在STM32上完成FATFS文件系統(tǒng)的移植;主控芯片采用STM32F103ZET6, 存儲芯片我這里采用(雷龍) CS創(chuàng)世 SD NAND
    的頭像 發(fā)表于 07-17 17:24 ?6683次閱讀
    STM32+<b class='flag-5'>SD</b> <b class='flag-5'>NAND</b>(貼片<b class='flag-5'>SD</b>卡)完成<b class='flag-5'>FATFS</b><b class='flag-5'>文件系統(tǒng)</b>移植與測試

    嵌入式SD NAND】基于FATFS/Littlefs文件系統(tǒng)日志框架實現

    文章目錄 【嵌入式】基于FATFS/Littlefs文件系統(tǒng)日志框架
    發(fā)表于 03-14 18:12

    嵌入式文件系統(tǒng)μC/FS的日志使用

    盡管在PC領域NTFS已經取代了FAT,但FAT文件系統(tǒng)仍然是嵌入式開發(fā)的首選。除了為嵌入式應用程序提供與PC(因為Windows繼續(xù)支持FAT)的無縫交互,對于電源不穩(wěn)定的設備開發(fā)者來說
    發(fā)表于 09-19 16:41

    讀寫SD嵌入式系統(tǒng)中的作用

    讀寫SD嵌入式系統(tǒng)中一個比較基礎的功能,在很多應用中都可以用得上SD卡。折騰了幾天,總算移植成功了 最新版Fatfs
    發(fā)表于 08-05 08:14

    fatFs/LittleFs/RelianceEdge Fs/LwExt4嵌入式文件系統(tǒng)寫入速度對比哪個快?

    fatFs/LittleFs/RelianceEdge Fs/LwExt4嵌入式文件系統(tǒng)寫入速度對比哪個快?
    發(fā)表于 12-27 06:37

    求一種在rtthread系統(tǒng)上添加并使用文件系統(tǒng)的設計方案

    里創(chuàng)建幾個目錄,用于elm-FAT文件系統(tǒng)littlefs文件系統(tǒng)的掛載點。elm-FAT文件系統(tǒng)FatFs 是一個通用的
    發(fā)表于 05-06 14:42

    基于SD卡的FATFS文件系統(tǒng)的研究與應用_崔鵬偉

    基于SD卡的FATFS文件系統(tǒng)的研究與應用_崔鵬偉。
    發(fā)表于 04-14 16:46 ?40次下載

    Fatfs文件系統(tǒng)的移植)

    對ffconf.h文件進行修改API查詢五、測試使用頭文件文件一、文件系統(tǒng)介紹FatFs是用于小型嵌入
    發(fā)表于 11-15 18:51 ?22次下載
    <b class='flag-5'>Fatfs</b>(<b class='flag-5'>文件系統(tǒng)</b>的移植)

    文件系統(tǒng)FatFs文件系統(tǒng)嵌入式芯片LPC18XX上的移植

    文件系統(tǒng)FatFs文件系統(tǒng)嵌入式芯片LPC18XX上的移植
    發(fā)表于 12-04 10:51 ?12次下載
    【<b class='flag-5'>文件系統(tǒng)</b>】<b class='flag-5'>FatFs</b><b class='flag-5'>文件系統(tǒng)</b>在<b class='flag-5'>嵌入式</b>芯片LPC18XX上的移植

    文件系統(tǒng)FatFSLittleFS的區(qū)別

    對于許多嵌入式電子設備而言,擁有一個小型且具有“彈性”的文件系統(tǒng)至關重要。
    的頭像 發(fā)表于 03-25 19:45 ?6097次閱讀

    基于OpenHarmony3.1的LittleFS文件系統(tǒng)hdf驅動實現

    ?? ? LittleFS是一個小型的Flash文件系統(tǒng),它結合日志結構(log-structured)文件系統(tǒng)和COW(copy-on-write)
    的頭像 發(fā)表于 09-30 18:32 ?1506次閱讀

    基于OpenHarmony3.1的LittleFS文件系統(tǒng)hdf驅動實現

    一、簡介LittleFS是一個小型的Flash文件系統(tǒng),它結合日志結構(log-structured)文件系統(tǒng)和COW(copy-on-write)
    的頭像 發(fā)表于 06-22 09:42 ?756次閱讀
    基于OpenHarmony3.1的<b class='flag-5'>LittleFS</b><b class='flag-5'>文件系統(tǒng)</b>hdf驅動<b class='flag-5'>實現</b>

    基于STM32+CS創(chuàng)世 SD NAND(貼片SD卡)完成FATFS文件系統(tǒng)移植與測試(下篇)

    四、移植FATFS文件系統(tǒng)前面第3章,完成了SDNAND的驅動代碼編寫,這一章節(jié)實現FATFS文件的移植。4.1
    的頭像 發(fā)表于 03-03 13:52 ?1200次閱讀
    基于STM32+CS創(chuàng)世 <b class='flag-5'>SD</b> <b class='flag-5'>NAND</b>(貼片<b class='flag-5'>SD</b>卡)完成<b class='flag-5'>FATFS</b><b class='flag-5'>文件系統(tǒng)</b>移植與測試(下篇)

    嵌入式SD NAND】基于FATFS/Littlefs文件系統(tǒng)日志框架實現

    文章目錄【嵌入式】基于FATFS/Littlefs文件系統(tǒng)日志框架
    的頭像 發(fā)表于 03-14 18:12 ?993次閱讀
    【<b class='flag-5'>嵌入式</b><b class='flag-5'>SD</b> <b class='flag-5'>NAND</b>】基于<b class='flag-5'>FATFS</b>/<b class='flag-5'>Littlefs</b><b class='flag-5'>文件系統(tǒng)</b>的<b class='flag-5'>日志</b><b class='flag-5'>框架</b><b class='flag-5'>實現</b>