本帖最后由 huangxuejia-29212 于 2018-6-3 22:47 编辑 7 m0 b; |6 d" m
" e7 f* o1 d8 J D
SPIFFS文件系统,有什么特别呢?: D% _- W: t" j2 ]" h8 ?
从名字就可知,这是一个用于SPI FLASH的file system。
/ d) ^# N0 a* b5 o# G现在好像在ESP8266上用的很多,感觉慢慢有的人气了。
8 o8 O0 F' w1 D; n从特性上说,这个文件系统用很少的RAM资源。
7 Q' j5 S: \ m& z, T/ f. u但是,我最后会吐槽吐槽这个特性的。, R" {" l3 N# }8 T8 Y
网络上资源不多,好在github上说明文档挺全。 7 V3 A' W. x6 k) Y: I
github地址:http://github.com/pellepl/spiffs+ ~# T/ \: }+ [! G1 a9 c
最新版本是0.3.7.1 _* o+ f+ v4 j% q* }
目录结构如下:
" G$ A- g0 f; h7 Z( Y
" o! I0 R' a# C) I添加到工程的文件如下:
- T2 d8 o% o: B1 _6 m0 r + P- W# ?5 @$ T6 d8 a2 v( I
移植概述2 u/ P w- z1 V: a% H
下面我们说说移植的过程。; [' ^& z! E W6 F8 @
1. 配置1 N! L% X3 m1 `) A |
spiffs-0.3.7\src\default下有一个默认的配置文件,spiffs_config.h。" D$ w& K3 K, [2 i u; [
为了方便,拷贝一个到spiffs-0.3.7\src。
6 \! Q: A h' r在工程中,头文件搜索路径,只包含src路径,不要包含\src\default。
( U: _3 z: }! R. C _. S4 z3 Qspiffs_config.h头部包含的头文件修改如下。
* ]# D$ `- S( \; Y' ^9 @在这里也定义了变量名。 - //#include <stdio.h>
! a, S" u- i& A, x/ X2 O4 [# N - #include <stdlib.h>
' v7 A2 c4 n8 }8 |- Q) U0 d8 E - #include <string.h> {0 @& U9 a: a
- #include <stddef.h>
3 R/ u+ w Q3 I3 w3 r - #include "stm32f4xx.h"
" z+ \ h1 G U8 W P - 8 |9 R% L2 H# y9 U
- typedef u32 u32_t;
) g Y5 L, a, b; ?! \) [0 h - typedef u16 u16_t;
5 q+ ~8 X* L/ O& E - typedef u8 u8_t;5 Z) I& J- b$ A
3 y- C* P3 q9 `- typedef s32 s32_t;5 s9 A& ?! x$ {) }( m3 {+ v
- typedef s16 s16_t;+ @+ U% h9 A- c4 ~, Y8 e8 j
- typedef s8 s8_t;
复制代码
. Z: q1 P- L& a* s3 U- t修改几个宏定义 - #ifndef SPIFFS_USE_MAGIC7 P+ w# ]/ z1 w' [9 N3 q
- #define SPIFFS_USE_MAGIC (1), }; h6 n. ^2 V ?/ O, J$ P
- #endif, n+ n7 l! f+ d3 G- U. r
7 H* R: E! _" s7 H7 u$ M0 M- #ifndef SPIFFS_USE_MAGIC_LENGTH
; p, A3 G6 Z5 ]* `: Q j; x - #define SPIFFS_USE_MAGIC_LENGTH (1)/ o) u# ]" ]2 d$ O w% O
- #endif* ~2 K+ O, W, T" e2 ?
- #endif
复制代码 2 k% D& E) x) R3 e% [ \
在spiffs-0.3.7\src\spiffs_nucleus.h文件中定义的联合用了一个gcc特性,在MDK编译会报错。
8 i) Q! y& m& q9 A. ^在联合前增加一句代码#pragma anon_unions 即可。 - 0 |+ k7 u4 M% M8 {
- #pragma anon_unions
( B3 m+ y, o* e - union {; v# i: c2 b; {- b6 S2 k; L
- // type read cache
. o0 O8 R4 m& U x- k" t$ a9 s7 S - struct {1 l. V. u s2 k, c" Z, U. b
- // read cache page index- W) _9 A; P8 G& w6 `# N
- spiffs_page_ix pix;* [4 g: y% o l# }' D
- };9 q; v4 C# }; @
- #if SPIFFS_CACHE_WR: E1 M0 Y5 I% `: E: T9 ?2 ]
- // type write cache3 B/ V- i6 i9 {! y/ r* \
- struct {
+ r7 z- D2 y4 w, M - // write cache1 \9 P0 G* V9 ]# y5 W) }5 G6 ]8 G
- spiffs_obj_id obj_id;
: G( m! c% g: s) i! R0 { - // offset in cache page1 C* D: W0 Q; P: J; t: b
- u32_t offset;
' e) R% A# I/ u& Z - // size of cache page
2 \0 C, c+ v- x - u16_t size;; D2 h( D3 z7 Z# ?) i# P
- };' [8 N/ ^+ Y) _$ C: H& G* G
- #endif
9 P/ e: K Z. I2 Q0 B - };& ~+ F Y3 D4 l/ E4 d$ ~* R: k
- } spiffs_cache_page;
复制代码
0 f9 Q4 Q1 ]' @) C2 应用
, Y% ?$ U/ D- k6 B) K) ]) }, d6 h首先,
* F2 f; g# B& O- l要实现SPI FLASH操作函数,SPIFFS需要的函数格式如下 - s32_t core_spiflash_spiffs_read(u32_t addr, u32_t size, u8_t *dst);
! Z: h5 f5 f) h - static s32_t core_spiflash_spiffs_write(u32_t addr, u32_t size, u8_t *src);
; o N% b5 k! Q" ]6 u - static s32_t core_spiflash_spiffs_erase(u32_t addr, u32_t size);
复制代码
7 Y$ h) Z+ Q2 L8 o第二,& f0 Q f- U2 c9 r+ s. u
定义文件系统相关参数 - /*
) U3 z. }3 I) h - 文件系统配置: |8 x& m# r$ N3 Y; V, r6 [
-
/ T# B! ^: o# V$ j0 C* Q - #define SPIFFS_SINGLETON (0) 这个宏配置为0,也就是支持多个spiffs
& h: a& K5 C3 }& x; W' r - */: V7 w) l* G% ^' N/ d+ s
- spiffs_config cfg=/ A$ T |$ \% ^7 L
- { % N( F9 Z4 ~2 v+ r7 f; [" n9 k) h9 z; D$ G
- /* 分配给SPIFFS的空间 要是逻辑扇区的整数倍*/- f3 ?! Z1 m" ^% [
- .phys_size = 1024*4*4*128,* s; \: G+ Y8 i4 Z
- /* 起始地址 */. ?$ j: N' a# C V% {$ D7 F
- .phys_addr = 0,
! D$ i. C f7 @+ \& Z( n - /*
9 m7 | k" Y4 L - 物理扇区,也就是一次擦除的大小,要跟hal_erase_f函数擦除的一致
, [. F2 j u2 D - */
8 ] G: A, B; S9 J- s - .phys_erase_block = 1024*4, . v/ V5 _8 O7 R: N- b* M
- /* 逻辑扇区,必须是物理扇区的整数倍
! A& F3 S. F: c2 R - 最好是: log_block_size/log_page_size = (32-512). c% d, P! _ s% I) m- {
- */, m* z6 V3 a. i+ d2 s
- .log_block_size = 1024*4*4,
8 i5 i- _* t' A2 |& o - /*逻辑页,通常是256*/. C3 d5 K" l. }2 j" q
- .log_page_size = LOG_PAGE_SIZE, 7 X' }* ` v# P n1 D
-
7 {! ^/ y3 u* W% K; g6 `0 q( O - .hal_read_f = core_spiflash_spiffs_read,
/ }' A' l: A$ q* @ - .hal_write_f = core_spiflash_spiffs_write,
+ l: W) @& Y5 y8 n7 V6 S - .hal_erase_f = core_spiflash_spiffs_erase,; D; p% b E0 N9 @! [ x
-
' {( n2 h" d% t# p% h - };
复制代码 5 d% Y. h- ^1 r) G, }2 @6 \
第三,* M1 t4 H+ [; t4 g; V( n! x
挂载文件系统,如果是第一次挂载会失败,需要卸载文件系统再格式化文件系统,最后重新挂载即可。 - /*文件系统结构体*/3 m4 y+ Q, ^6 I
- static spiffs fs;
" I8 ~! ^: F+ T* V8 _: p - % K# C2 F( Q' h: x# |. f
- /*页定义*/- t x/ o/ W5 u- y2 c( K1 ]7 j/ y
- #define LOG_PAGE_SIZE 256
3 A. ^- t" ^/ S/ ?- x9 v4 C1 P
+ m* k+ h- ?# l+ x5 U8 o/ l- static u8_t spiffs_work_buf[LOG_PAGE_SIZE*2];: E3 p# n3 Z3 s
- static u8_t spiffs_fds[32*4];
6 g" P( A. L* t8 r' j- x% u - static u8_t spiffs_cache_buf[(LOG_PAGE_SIZE+32)*4];0 z- M0 p! j9 Y
3 g( {4 l& }( h- /**
% U; l$ G. B# p5 A& f& R - *@brief:
( Q2 V1 D+ x R7 O* i, v0 u9 w. { - *@details: 格式化文件系统# z8 i( F& _+ }3 G! Z
- *@param[in]
9 m: ?! r, Z z6 }# M8 Q0 z# U - *@param[out] ) ~2 a$ g* M# S1 f5 Q* L. C" j
- *@retval:
9 x$ x6 a) D+ S$ M) i" t3 \ - */
7 ^+ |$ E B8 n- U* L& Z - void sys_spiffs_format(void)
3 h) x* {5 m% z a* {+ w - {
8 F8 h8 g) P5 \; l" \) O5 z: J
0 D/ p7 f: p; G% @7 z2 u- z- wjq_log(LOG_INFO, ">---format spiffs coreflash\r\n");+ R$ g$ H7 N e: M! `( H
. T/ P* \, r/ d: e1 s- /* 格式化之前要先unmount */
$ w4 N5 t4 n. Y( m9 t - SPIFFS_unmount(&fs);
$ B3 J& O. ]4 @- O -
+ q# i2 O4 W( Z& s6 N- } - SPIFFS_format(&fs);
7 u' y7 |5 n. f
, u n2 p s# X- x4 G- int res = SPIFFS_mount(&fs,6 a3 _% t% J& I% D8 F1 M b
- &cfg,/ L3 r" U# N1 l! u" q% \9 f3 |
- spiffs_work_buf,
: h( e9 x( g* V/ L& f2 ^ - spiffs_fds,
5 B& H' x7 U2 W4 O - sizeof(spiffs_fds),# Y+ z/ H, @: K# ?
- spiffs_cache_buf,
' `9 Y) G- Q. Z - sizeof(spiffs_cache_buf),
3 x8 x0 g% \+ o' h1 n - 0);$ P; w+ C0 B1 U8 e2 C/ B( s
- wjq_log(LOG_INFO, "mount res: %i\r\n", res);: v/ `! e+ v- U+ U0 V3 {$ N
- . @# I }% ?5 |
- wjq_log(LOG_INFO, ">---format spiffs coreflash finish\r\n");
$ _, q) B/ G7 n8 U8 @! R
0 e& i; c* L7 H; N* P6 f* t7 y7 ]- }
6 y8 n, Z/ } ]/ z! v q2 \3 f - /**# ?7 F( w- B7 {0 a$ x* ^
- *@brief:
. m' Z% I$ U. w2 g" s0 O - *@details: 挂载spiffs文件系统
1 h6 P# m# m7 v" m* K$ Y - *@param[in]
% i2 k( e+ K7 c& n - *@param[out] " {( R! W1 O3 A$ d
- *@retval: 9 R. z7 Q: o2 R3 w' C
- */
% `& W5 U6 o: G9 i - void sys_spiffs_mount_coreflash(void)
5 d5 G) ~0 h: u5 f7 i; l - {
' J$ d# f6 I4 h6 i: `( F( c - int res = SPIFFS_mount(&fs,
" u+ q5 M* u4 s" K: w1 E - &cfg,
, w# [( }6 o9 Z/ `! z! _8 h - spiffs_work_buf,
, u- p, o& Q. J - spiffs_fds,
2 U$ Y3 |: @" C) _% d - sizeof(spiffs_fds),2 I9 E( Q& a) O8 r
- spiffs_cache_buf,6 W: d* A, o/ b4 ]
- sizeof(spiffs_cache_buf),4 ]4 X% D& P8 q. ^3 Z
- 0);
8 h) C6 k% e: c" j& L - wjq_log(LOG_INFO, "mount res: %i\r\n", res);
/ l. A' b, o5 T" b. Q* p: m9 @
) c+ }6 H9 i' K0 l, K- if(SPIFFS_ERR_NOT_A_FS == res )
9 @. L! g, _+ Z1 |( ?( V( q5 Y - {
* V: G& P. C3 ? - sys_spiffs_format();
" L7 g( g6 D2 c7 g1 ^ - }* v$ M7 {* o( C: }2 o
- }
复制代码
8 o, l9 |: {# C4 ?( K然后,就可以进行基本读写测试啦。 / z7 |8 {6 V+ f
吐槽
4 O. f. d' D: x* O) @11 U% b4 A% K8 G: ^' `; F" k$ G
如果要好好用这个文件系统,需要多次测试找一个设置平衡点。3 a% V) N' V0 G8 {. X$ b" M
什么意思呢?也就是你这个文件系统如何配置。( w. H9 \! @- u8 |6 |6 A( N, v
也就是spiffs_config中的:逻辑块多大,页设置多大。不同的设置会严重影响性能。" H2 Y# r, W% ^* U6 D
因为这个文件系统为了省内存,没有任何索引。
6 ~' h" ~4 e0 K* Y具体细节大家自己分析,我就说一个事实: 打开一个文件,很可能要轮询所有BLOCK的第一页。
. H3 H2 r& t: O. T; }当你BLOCK跟页设置小,数量就多,操作文件就会很慢很慢,,,,。1 ?+ P( a% ~3 j% w
为什么这么慢?作者在WIKI上有说明。
2
4 X/ X. v% c' T$ q9 Z8 c" j7 N$ r F目前还没有认真使用这个文件系统,但是总感觉它有点上不成下不就的感觉。0 x% f& |( P9 {
是的,嵌入式RAM紧张,但是嵌入式对速度也敏感啊!$ Z! ]# `" ^, T7 p3 d x0 ~" W
想起以前公司用的文件系统,速度不慢,RAM用得也不算多。
% J4 h2 b& Y; u: n怎么做到呢?
0 G3 ^& d2 y- L& ^/ S# z限制其他性能,例如,最多只能创建100个文件,文件系统最大只能到1M。! z8 d( q. A! c& L7 M
这样的限制对于单片机系统来说,其实是能用的。: P! X6 C8 ]) I/ \9 }* n; |
(带这个文件系统的产品估计出货也1000万台了吧) + E1 F4 Z. P, R9 x
移植源码
& U* B9 t& y2 b+ u这个仓库是个人写的一些代码,主要是按照经验进行了一些程序设计,自认为比大部分教程的例程代码要好。2 P3 d5 w/ b* b6 Y3 J
后续会慢慢添加各种外设驱动。大家拿来用即可。# X m I# N. p/ r8 \/ G$ Z; U7 ^, x
欢迎商用,后果自负
8 t, u* } m- r
5 H2 J1 y" P' |2 e- y |