智能家居DIY教程连载(3)——文件系统灵活用

2019-7-31 16:55 [复制链接] 5 1188

智能家居 DIY 教程连载(3)

2 T6 N6 |# K: T& H) D
$ l6 N. P4 P. {! |! R- \2 Q1 A

文件系统灵活用,so easy

$ f5 v8 j9 n7 [, r  G1 Y0 a/ D
9 m% J: l0 b( y% C- c. y" b+ {

Hi,各位小伙伴,DIY 活动已经来到了第三周!前两周的任务大家都完成了吗?本周将会迎来新的挑战——文件系统。本文将从 SPI Flash 和 SD Card 两方面给大家讲解如何使用文件系统,以及针对本次 DIY 做出的一些优化,会大大增强系统性能,一起来看看吧~

0 u4 Q* |) d1 m$ e

本文目录

6 C" ~! L! R. i# \7 S1 S+ K/ w% a
    8 W5 b: B' F8 K- f
  • ) u6 Z+ ?% B/ j
      " L# o3 i% [1 j' T- K
    1. 第三周任务回顾
    2. : t4 L# f1 {3 x; s9 S/ Q) j
    & A/ w7 e) V. M' ^$ C- F% Z  e
  • 1 u! x( e6 Z, D6 m7 J9 B% J/ b
  • 9 x& c. B. i9 J8 o7 B
      & \! _6 S  w. r1 p
    1. RT-Thread 文件系统简要介绍
    2. 6 v) F+ |. O8 R9 R8 @6 F" z& w4 T
    - K; Z# v# W6 j* J
  • , ?4 M# L; `5 G  D4 ^0 _
  • # K3 @1 Q* `0 ~
      ! K( e: Q. {5 U6 b: H3 c  b+ J- F
    1. 在 SPI Flash 上使用文件系统
    2. 0 z% @3 H" P# m* f, d  K) \, M
    $ i3 x+ D" Z2 P  h0 ^. P" T# y
  • 4 l( O1 {# o/ c6 \+ ]( _
  • ) X! R" S- `* |, i
        N$ \3 d/ w5 C& |! f6 m5 o6 p
    1. 在 SD Card 上使用文件系统
    2. % x- }5 `; r# F
    " Y1 ^, Q, g( J8 ^9 R
  • / I8 H3 ^- Y* `
  • ( V4 k$ p9 U- @: W
      # l( _8 _6 O% G3 ^9 H, T, D
    1. 针对本次 DIY 的一些系统性能优化(重点)
    2. ) r/ \, y1 B1 [8 n# X) V
    5 s; Q* N+ G* l$ E
  • 3 x: W" W; M. Y1 M# r: S, ?
  • 2 t$ w$ n. X. B) k& K( F
      2 f" H( v, ^4 V# o
    1. 开源代码
    2. 2 n. T+ Q4 l8 b
    0 n8 V* O1 T# G# I9 c! |5 X  D
  • 9 j' y* \, ?5 E+ ?
  • 9 x" J" I( }' C4 A0 k
      - [, D+ c- k/ g
    1. 注意事项
    2. + A  g2 u4 a1 e, S9 T* R
    ( Y" t5 }, v' G9 g* t, g
  •   }" x; m5 R. c7 u
9 c1 U. l/ Y+ ^+ s

1. 第三周任务概览

& f( f9 Q% V; [* d* y: P" U" [( T7 E, U

我们来回顾一下第三周的任务:

/ |  W# w8 C' t0 e
    3 r& |' s& r  W
  • 了解 RT-Thread 文件系统,在接收节点中使用文件系统,存放来自发送节点发送过来的数据
  • % C" n0 `9 i- h, S
3 |5 M+ j6 H( q" i

上述任务比较单一,只是文件系统而已。不过,能巧妙灵活的把文件系统用好用对,可不是一件轻松的事情。

/ `3 E( ^1 F5 q+ r' }

2. RT-Thread 文件系统简要介绍

2 h9 r* q) I# G* W' {" B( i0 s! Y

DFS 是 RT-Thread 提供的虚拟文件系统组件,全称为 Device File System,即设备虚拟文件系统,文件系统的名称使用类似 UNIX 文件、文件夹的风格。

2 C2 R8 E# Q' }& e  O6 [4 j

RT-Thread DFS 组件的主要功能特点有:

( U3 i( Z9 |4 L; @1 n7 G4 H+ N+ n
    + Y. @! \  V% \& J# F
  • 为应用程序提供统一的 POSIX 文件和目录操作接口:read、write、poll/select 等。
  • , g7 s# t# S4 o- C" t; o! ]
  • 支持多种类型的文件系统,如 FatFS、RomFS、DevFS 等,并提供普通文件、设备文件、网络文件描述符的管理。
  • 1 |' w% H$ A" G
  • 支持多种类型的存储设备,如 SD Card、SPI Flash、Nand Flash 等。
  • ( D  o  r9 a9 `8 r! d9 J
) ?) W4 A3 u: M, ^7 n0 g# x

DFS 的层次架构如下图所示,主要分为 POSIX 接口层、虚拟文件系统层和设备抽象层。如下图:

2 M9 A  W+ _5 Y  U

fs-layer.png

) v5 b! J) _) ^- Q' n

DFS 的更多内容,请在 RT-Thread 文档中心中查看,亦可点击本链接跳转。这里不过多赘述。

- n  V+ K& w- X: x) b

3. 在 SPI Flash 上使用文件系统

1 G( f+ u1 }; C5 }# l# p

3.1 准备工作

2 ?5 P6 v- S3 a+ L( K  F# ^

以正点原子的潘多拉开发板 (Iot Board) 为例,教大家在 SPI Flash 上使用文件系统。

+ V5 Z+ q: G- h7 G$ P

值得一提的是,RT-Thread 已经将 libc 那套文件系统接口对接到 DSF 上了,在 env 工具中开启 libc 和 DFS 即可,本次教程使用 libc 的那套接口进行文件的打开/关闭、读取/写入。

+ {5 ]  m  g- F" C/ g5 q6 k0 q

在 menuconfig 中开启 libc:

( R3 n2 u5 r0 h$ M$ Q
RT-Thread Components  --->
0 x0 w, G0 d; L7 ]1 b  d5 l; Y    POSIX layer and C standard library  --->
9 @, U1 `* g  x' Q$ j0 Q- p        Enable libc APIs from toolchain
/ r0 E9 P0 M) O

在 meunconfig 中开启 DFS,本教程使用 elmfatfs 文件系统,需要将 elmfatfs 挂载到 RT-Thread 的 DFS 上,所以 elmfatfs 也要开启:

( q' i) ^: _5 E) z2 `% B8 ?2 ?: x9 l
RT-Thread Components  --->
: ^& c2 `: o2 C# p1 U    Device virtual file system  --->
  e2 w  H& p; E- H) k        Using device virtual file system- e5 ^" a" g) F2 x2 {1 d
          Enable elm-chan fatfs
4 ^! `; n  M% Y- c; M6 `& W) w

当然,不要忘记在 meunconfig 中开启 SPI Flash:

' }" r" Q" g9 x7 H( a7 z: ?
Hardware Drivers Config  --->
! ^! l- I$ j, Z( `9 @    Onboard Peripheral Drivers  --->
  x& @+ k* g; t, I4 |1 X        Enable QSPI FLASH (W25Q128 qspi1)
% @8 j+ }/ C/ y+ M' [9 o* C6 O2 s* ~

潘多拉开发板上的 SPI Flash 使用的是 QSPI 接口,还需要在 meunconfig 中把 QSPI 接口开启:

0 ~) P/ }- g7 V: P  M3 M- q: J
Hardware Drivers Config  --->0 u. i9 v0 G0 u  _' w) ?! O4 v$ h. n6 j
    On-chip Peripheral Drivers  --->3 A4 C8 i: q% t7 S
        Enable QSPI BUS
* y0 T0 c9 H- }# B. M

退出 menuconfig 后需要输入 “scons --target=mdk5” 更新工程。

7 r+ S& `- I  ~

3.2 文件系统的挂载

, ?1 w' }/ j6 ^+ ?0 {5 R9 u- o$ R

本次 DIY 使用的文件系统是 elmfatfs,elmfatfs 需要在块设备上才能进行文件操作。潘多拉板子上的 SPI Flash 是 W25Q128,我们需要将 W25Q128 注册成块设备,才能使用 elmfatfs 进行文件操作。如下示例代码:

5 r& a" |+ {: m# ~& b1 J6 }- L- z5 B
static int rt_hw_qspi_flash_with_sfud_init(void)+ Q2 M( s9 }; }2 ^& K5 D
{& O  x$ f* Y- g4 T! z, H
    stm32_qspi_bus_attach_device("qspi1", "qspi10", RT_NULL, 4, w25qxx_enter_qspi_mode, RT_NULL);
( L; B' J: t% H, j0 j    if (RT_NULL == rt_sfud_flash_probe("W25Q128", "qspi10"))6 a7 _4 n6 }8 V* {2 ^( @$ i
        return -RT_ERROR;8 B; N9 M$ o9 k' f4 O0 z; j
    return RT_EOK;
" z. h+ g7 V2 k  e# z$ k}+ m# c$ f* r: O; B
INIT_COMPONENT_EXPORT(rt_hw_qspi_flash_with_sfud_init);
/ |3 Z$ z2 l1 S7 C5 R

在 FinSH 中输入 “list_decive”,即可看到 W25Q128 注册成了块设备了,并挂载在 QSPI 上:

6 [3 E' B; l% h

W25Q128.png

3 g* {! `, N% }( J, z) r* \

W25Q128 注册成了块设备后,就能将 elmfatfs  这个文件系统挂载到 RT-Thread 的 DFS 上了,如下示例代码:

3 S5 t2 E/ x1 r+ U1 O
dfs_mount("W25Q128", "/", "elm", 0, 0)
" I# r- O+ B; y+ z" C4 _& `# |: P

3.3 文件操作

1 S# X9 ~& ~- y+ Z7 h) \

到此为止,我们就可以使用 libc 的接口进行文件操作了,将接收到的数据以文件方式存放到 W25Q128 里面去,举个简单的例子,如下示例代码:

1 a2 c# z2 N  H. e* T0 |; |9 ]
FILE *recvdata_p0;
; x2 V9 _7 y  j6 {5 Y+ g( Hrecvdata_p0 = fopen("recvdata_p0.csv", "a+");! l) Y( Z& c7 E# J: z$ A
if (recvdata_p0 != RT_NULL)
1 ~! j' Z2 A1 X  B9 F% G{
0 k& c" `5 Y+ Z6 n0 J; c; g    fputs((char *)RxBuf_P0, recvdata_p0);8 Z% C/ v0 k( _) ^+ ?7 {7 n
    fputs("\n", recvdata_p0);
# S3 M/ C/ q2 l: ~  g/ S    fclose(recvdata_p0);
7 z0 {; z6 J# r% C}
- V) u  X% l4 n) U2 v, [- V" ]9 h

在 Finsh 中输入 “ls” 可以查看当前文件系统中的文件目录,如下图:

/ x& \% A2 L, ^& n

ls.png

( ]" Y+ c, c9 m! X7 _

输入 “cat XXX” 可以查看文件内容,如下图:

% R/ I# I! j7 z7 X

cat.png

( f  g" D! `, }, g+ w( e- a: ^- L3 _. O/ D

简单的几步就可以进行文件操作了,RT-Thread 的文件系统还是相当易用的。

6 {3 b' U1 L: F* R- [) u

4. 在 SD Card 上使用文件系统

) R6 P- t( |& ]& W& j

4.1 准备工作

3 H# _( U( n: @+ ?

以正点原子的潘多拉开发板 (Iot Board) 为例,教大家在 SD Card 上使用文件系统。

! w; [' L( i& `0 ]9 V0 l

和上面的 SPI Flash 一样,在 menuconfig 中开启相关选项:SD Card,SPI(潘多拉板子的 SD 卡是用 SPI 驱动的而不是 SDIO),libc,DFS,elmfatfs。

: {! O5 R9 g+ g( _% K

4.2 文件系统的挂载

  j9 U/ M7 p7 G7 K0 u3 ~6 K

与 SPI Flash 一样,需要将 SD Card 注册成块设备,才能挂载文件系统。如下示例代码:

/ J* F( g: {5 [5 B/ M
static int rt_hw_spi1_tfcard(void)4 d  y; \$ z1 s
{; k" ^9 z' r* N! F1 O( ~1 g- m
    __HAL_RCC_GPIOC_CLK_ENABLE();
. k7 B  `  u0 ^. n! H. P    rt_hw_spi_device_attach("spi1", "spi10", GPIOC, GPIO_PIN_3);; \9 h$ K$ V. H$ K0 m
    return msd_init("sd0", "spi10");
' d) V! q/ Y) {6 `& w}1 f0 W- k9 A% b5 f, U
INIT_DEVICE_EXPORT(rt_hw_spi1_tfcard);
" Q$ {/ L( g4 T7 E5 e/ k- U

在 FinSH 中输入 “list_decive”,即可看到 SD Card 注册成了块设备了,并挂载在 SPI 上:

7 j4 R: A; o/ p7 {' _% J8 }

sd0.png

8 V3 A1 Q- Y0 x- E

SD Card 注册成了块设备后,就能将 elmfatfs  这个文件系统挂载到 RT-Thread 的 DFS 上了,如下示例代码:

# q% F& ?. i* ?+ u3 ^
dfs_mount("sd0", "/", "elm", 0, 0)
3 V7 g" e, f: k2 O1 R; f

需要注意的是,如果大家手头的板子是使用 SDIO 接口来驱动 SD Card 的,那么将 SD Card 注册成块设备将不用我们操心,RT-Thread 源码中的 “...rt-thread\components\drivers\sdio\block_dev.c” 文件中,会将 SD Card 注册成块设备的。当然,文件系统的挂载还是需要我们手动敲代码去实现的。

# j6 c2 v0 T1 Z! M6 f

4.3 文件操作

) h, B: U2 w  w7 k# v

与 SPI Flash 一样,可以直接使用 libc 的接口进行文件操作,如下示例代码:

1 Z1 d' k7 _/ A% m2 H
FILE *recvdata_p0;
$ _) Y4 ]& z' w% B$ @3 p9 y/ jrecvdata_p0 = fopen("recvdata_p0.csv", "a+");9 i% L$ a  ^9 T2 f/ F/ y7 g  F  e
if (recvdata_p0 != RT_NULL)
/ d0 B! t  E. e' M  c{! ]5 ]. N5 N2 w
    fputs((char *)RxBuf_P0, recvdata_p0);
7 _; g! [+ b0 R* R    fputs("\n", recvdata_p0);) h3 ^$ k9 D3 H6 \2 ~
    fclose(recvdata_p0);2 y$ ?' ]# s# T5 v1 p
}
- U2 d) j" W7 J3 H! s* ]3 k

5. 针对本次 DIY 的一些优化

0 r3 l) M! Q! M4 C9 A  l

5.1 优化1

" z- Z$ [+ Y9 k5 |5 m3 ~

将文件系统用起来,进行文件操作,是一件相对比较容易的事情。不过当将文件系统运用到实际项目中的时候,往往会因为一些需求或者说是其他因素,导致事情不那么好办。就拿这个 DIY 来说,如果就像上面的示例代码这么用文件系统,虽然系统能正常工作,但是会带来一些问题:

% e$ G" o8 `+ F; W& h
      U7 A" k- I8 U6 n
  • 众所周知,文件的操作是需要占用大量时间和资源的,通俗来说就是慢,像文件的读,写,打开,创建等,都是比较慢的。如果发送节点发数据过来,接收节点每收到一条数据,就用文件系统记录这个数据,这样会导致系统性能下降。如何保证减少文件操作次数,提高系统性能,又能保证每条数据都不丢失呢?
  • & ]( U" ~' o" \: @. N- G
* n' ?% L) G2 |) h9 K$ ]

这里使用 ringbuffer 来避免这个问题。

2 R2 W$ p5 R: N' r& V- P8 e9 G

ringbuffer 是一种先进先出的 FIFO 环形缓冲区,DIY 的接收节点工程中,我们创建了两个线程去工作,一个是 nrf24l01_thread 线程,用于接收来自发送节点的数据,另一个是 DFS_thread 线程,用于利用文件系统保存数据的。并且创建一个 4KB 大小的一个 ringbuffer:

- z+ ?$ ?) R4 r/ x4 m# \6 g& {1 x
static struct rt_ringbuffer *recvdatabuf;" w9 K- d3 B. }/ x* j. l; Y9 b; D
recvdatabuf = rt_ringbuffer_create(4069); /* ringbuffer的大小是4KB */
" H8 G) Z  u, A8 T6 r5 N! V

每当 nrf24l01_thread 线程接收到一条数据,就存放到 ringbuffer 中去:

1 t( F  L( S8 Q0 A# _9 x
rt_ringbuffer_put(recvdatabuf, (rt_uint8_t *)str_data, strlen(str_data));
2 b+ w5 r5 Q" J& ~( l

DFS_thread 线程中,我们设置一个 ringbuffer 的阈值,这里我将阈值设置成了 ringbuffer 大小的一半,当写入的数据达到了 ringbuffer 的阈值之后,就将 ringbuffer 中所有的数据统统写入文件中去:

7 q& K$ P" E6 V) N
/* 判断写入的数据大小到没到所设置的ringbuffer的阈值 */4 |& ~* X/ Q& V
if (rt_ringbuffer_data_len(recvdatabuf) > (4096 / 2))
0 z' c- [3 n6 C: ]0 F/ y; p( `{4 o  Y1 ~9 J/ f. \
    /* 到阈值就直接写数据 */8 y: Q* I( f+ T$ r4 c
    recvdatafile_p0 = fopen("recvdata_p0.csv", "ab+");
$ ]7 ~5 v2 o& _' U    if (recvdatafile_p0 != RT_NULL)% p/ W* P; Y$ T7 h  ]
    {' Y  j( {9 C  A' k
        while(rt_ringbuffer_data_len(recvdatabuf))( F* T3 r  N3 J  g$ ?" Y& b
        {
- [1 d% t: L: g# ]            size = rt_ringbuffer_get(recvdatabuf, (rt_uint8_t *)writebuffer, (4096 / 2));% [- t( l" {: b8 m. E
            fwrite(writebuffer, 1, size, recvdatafile_p0);
5 j+ c- V6 i+ ^: E' W        }/ _- E0 Y1 T& o9 o
        fclose(recvdatafile_p0);
" K1 ]9 F1 [* L    }) R* B, t& Z1 b" q7 o
}
/ B& J2 D: q* {- R

这么做,就可以尽可能的减少了文件的操作,提高了系统的性能,同时又保证每一条数据都不会丢失。

9 h7 p* i! D7 R  E( S' _

5.2 优化2

% `  V! R. K. k8 l/ t% e

但是,还有一个问题:

/ p5 ^# \" `. J9 t
    * \1 b$ _  a" C2 z7 Q. i* p
  • 如果发送节点很久很久才发数据过来,或者说是接收节点很久很久才收到数据,那么 ringbuffer 要很久很久才能到阈值。如果这时候,已经写了整整一天的数据进 ringbuffer 中了,只差一点点就要到阈值了,很快就可以将数据写入到文件中去了,这时候偏偏断电了!整整一天的数据白白丢失了,心痛吗?
  • / L* G, R& k" ]
9 p+ c$ l- }" w

当然,掉电丢数据这种情况是不可以避免的,但是我们可以通过一些算法优化(姑且叫它算法吧),尽可能的减少丢失数据的可能。

$ u8 ?  `7 s  m- ^7 P

解决思路是:定个固定时间,计时,如果时间一到,此时数据还没写满 ringbuffer 的阈值,这时候就不管数据到没到阈值了,直接将 ringbuffer 里的数据全部写入文件中去。要实现这个思路需要搭配事件集 (event) 使用。

" z6 R& @, A3 S# y7 S! I

nrf24l01_thread 线程中,每收到一个数据,就发送一个事件:

9 G4 e8 n8 c6 D) o4 l5 l
while (1)* [0 ~, u4 Y6 C3 |" C2 q
{- i. }; ~* h: ~& C5 B, b
    if (!rx_pipe_num_choose())" b, N# e, z1 o, ]
    {" k. h" x. z3 m0 K
        /* 通过sscnaf解析收到的数据 */! Y: k3 v/ e/ F& z( p" w2 ~9 A0 i+ {
        if(sscanf((char *)RxBuf_P0, "%d,+%f", &buf.timestamp, &buf.temperature) != 2), g; }% f1 b0 I
        {1 r+ O/ O$ w1 ^8 ^: Q
            /* 通过sscnaf解析收到的数据 */7 W8 t" |" X$ u. [4 u) y
            if(sscanf((char *)RxBuf_P0, "%d,-%f", &buf.timestamp, &buf.temperature) != 2)
6 }5 `" F6 O! N9 Z2 n, r            {
2 c/ M8 E, l8 j5 ~8 ?                continue;# [' F# ~# G2 Y. U
            }
% J1 T( m; t/ e% M3 ~7 \+ {; v% E! n            buf.temperature = -buf.temperature;! ^! \7 `0 m4 t" m' e
        }4 O, R- Z7 {/ m, W/ n! d0 T; I4 b+ {
        sprintf(str_data, "%d,%f\n", buf.timestamp, buf.temperature);
0 |5 `! b; S/ C        /* 将数据存放到ringbuffer里 */# X8 K: |6 N+ ?! i
        rt_ringbuffer_put(recvdatabuf, (rt_uint8_t *)str_data, strlen(str_data));
2 |' b8 T6 L- l2 S* R6 \& O8 S: Q        /* 收到数据,并将数据存放到ringbuffer里后,才发送事件 */
6 _7 m5 w, w# D3 c9 {        rt_event_send(recvdata_event, WRITE_EVENT);
1 @# P- y$ E! b/ {    }
) X  {4 d9 u  R; [    rt_thread_mdelay(30);8 \. H6 J- a, ^6 V- {7 [
}
/ b8 J% k6 r1 ~3 M  T, w  m

DFS_thread 线程中,通过接收两次事件,并设置接收事件的超时时间,达到计时的目的:

; Y5 I' e( M! p, r
while (1); w$ {* c. l( m, h( o, y1 i
{: s* N2 X$ v$ e; H$ t! t1 g
    /* 接收感兴趣的事件WRITE_EVENT,以永久等待方式去接收 */* L5 q8 J4 U7 _0 R! V. B
    if (rt_event_recv(recvdata_event, WRITE_EVENT, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, &set) != RT_EOK)
! R1 U4 D6 h6 k  E5 l+ u1 R5 W) P8 h        continue;
8 b3 d" A$ K( K6 U, y3 M% \    do
( D4 Y: `% P+ M4 }    {
! \2 N8 E) C5 w- K        /* 接收感兴趣的事件WRITE_EVENT,以1000ms超时方式接收 */
. t- n" A0 n9 {        if (rt_event_recv(recvdata_event, WRITE_EVENT, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, rt_tick_from_millisecond(1000), &set) == RT_EOK)
/ j* G0 E" G1 u2 c4 Y1 u8 l        {
& n) e& u6 ~' w& y" W; b0 u            /* 判断写入的数据大小到没到所设置的ringbuffer的阈值 */3 f4 T- ~* y3 R2 `
            if (rt_ringbuffer_data_len(recvdatabuf) > THRESHOLD)
% {" i: A* w, v& t" [. }# S  j. |            {# \& G- ?" S" L9 b
                /* 到阈值就直接写数据 */) Q+ n" U2 k, \+ f
                recvdatafile_p0 = fopen("recvdata_p0.csv", "ab+");% ]. ], ]! _6 o8 L- @9 p8 b
                if (recvdatafile_p0 != RT_NULL). H* V1 l' A& a
                {1 _1 r7 I: T. \" d
                    while(rt_ringbuffer_data_len(recvdatabuf))6 s/ V+ {3 T& _  s7 N: ~3 w! M
                    {" N; b1 ~2 p% c" Q* A; O
                        size = rt_ringbuffer_get(recvdatabuf, (rt_uint8_t *)writebuffer, THRESHOLD);
  q) Q5 r+ J) J                        fwrite(writebuffer, 1, size, recvdatafile_p0);0 L4 J$ u1 O% e
                    }$ f- s2 h6 Q2 C3 i! \3 ~5 k
                    fclose(recvdatafile_p0);( ^9 x+ \- U1 R( ^1 S" N
                }4 x0 {# s8 @/ h" b8 J2 d% \
            }
( E: S' E* c" D            /* 阈值没到就继续接收感兴趣的事件WRITE_EVENT,以1000ms超时方式接收 */  j* f0 k! Y- c5 E, H
            continue;- j% h$ x1 N6 q, |; Z6 x, m* h
        }9 A$ @6 N% h+ v% L- c7 W2 F- T, j
        /* 1000ms到了,还没有收到感兴趣的事件,这时候不管到没到阈值,直接写 */
$ k' t& R1 D. B. p1 ?        recvdatafile_p0 = fopen("recvdata_p0.csv", "ab+");# M$ d5 |# K. {% D
        if (recvdatafile_p0 != RT_NULL)$ C( b+ E* N7 W0 F) k( j: z
        {0 G! [0 E5 @7 O2 ]9 u
            while(rt_ringbuffer_data_len(recvdatabuf))
$ V4 e, t9 k/ I            {
( \$ \/ \0 {, `& X; K# E; J                size = rt_ringbuffer_get(recvdatabuf, (rt_uint8_t *)writebuffer, THRESHOLD);2 C; B0 J; Y3 H& U
                fwrite(writebuffer, 1, size, recvdatafile_p0);
9 z6 @  z5 ~$ B/ d+ L' K6 X5 S& U3 W            }: w/ @9 [& M' Z% W0 \% Y2 B+ p
            fclose(recvdatafile_p0);
/ M- w5 B8 P/ _/ N7 q0 `& E        }
/ E$ z/ w5 i3 a! V- n    } while(0);
" S- b+ X1 y5 @}
" q" B# |  P5 k2 J4 ~& V8 @

这样就尽最大力度的解决了掉电丢失数据的可能了。当然,第二次接收事件的超时时间可以根据自己需求设定长短。

: P' d: Z: E9 C' s2 K9 e

6. 开源代码

# D# j; q! D2 y2 Q( \

为了更进一步便于大家学习,第三周任务的代码已经开源啦~ 请点击这里查看

' H" c/ }' g9 v2 i

7. 注意事项

) F4 M: B& ~$ m! X9 Q
    & m9 C9 T+ t8 S- i8 P& v
  • 0 [( r0 Q5 z9 I" |1 m+ G

    第三周的源码中,只上传了两个 demo 工程,均是本次 DIY 中接收节点的代码

      V1 w  _. ~6 r, o; h' A- H) N/ ]
      6 Z4 M! v7 M& \
    • RECEIVE(stm32l475-atk-pandora)(SD_Card) 是在 SD Card 上使用文件系统的 demo
    • $ _9 R/ q3 h0 T# F% w
    • RECEIVE(stm32l475-atk-pandora)(SPI_Flash) 是在 SPI Flash 上使用文件系统的 demo
    • 4 L& ^* a7 q, h$ A9 J# Q4 m0 L
    3 \! M5 n, s, v& S8 ^$ s- Y0 X7 u- J
  • ' t- l; K0 N! B1 |7 u! t7 @
  • 8 G! j" `: O1 _

    发送节点的代码,在第二周的 demo 工程中有,这里不再重复上传相同 demo 工程

    . F! r% w4 A: _: A2 A8 j2 R
  • 6 }5 P% d# P# w/ [% I  u
  • SPI Flash 的 sector 大小为 4096 字节,需要在 menuconfig 中修改:
  • # Y. |9 Q* d% q" H# i2 v0 g  u& A6 w' E
, H5 u7 t0 z8 S1 [$ B3 ?+ I- [: j4 l+ }

spiflashblocksize.png

% P% u* m; X) c2 A- [3 P7 w
    # @; q, w1 f; \, }7 K' l  `, C* p
  • SD Card 的 sector 大小为 512 字节,需要在 menuconfig 中修改:
  • ; A( ^8 B: F0 }: ~; Z$ [
/ t6 \* {" B8 B( l# S1 x, V4 x

sdcardblocksize.png

4 }* T6 @! g5 L! F
使用道具 举报 显示全部楼层 回复
最新评论 | 正序浏览
显示全部楼层 |楼层直达:
发表于 2019-7-31 16:58:32 | 显示全部楼层
使用道具 举报 回复
发表于 2019-7-31 16:58:50 | 显示全部楼层
自个儿顶自个儿
使用道具 举报 回复
发表于 2019-7-31 16:59:18 | 显示全部楼层

, J  `$ |1 C5 X, X8 x1 ^
使用道具 举报 回复
发表于 2020-1-1 19:39:12 | 显示全部楼层
我的板子是自己做的,用的是STM32F103RE,按照你的步骤做,用函数dfs_mount("W25Q16", "/", "elm", 0, 0);后程序崩溃了输出(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread) assertion failed at function:rt_thread_control, line numbe
使用道具 举报 回复
发表于 2020-5-22 15:39:25 | 显示全部楼层
写的时候,为什么writebuffer是1024呢,而且也不报错,我实验的时候,改成了rt_uint8_t  writebuffer[1024] = {0};也不报错呀,这个rt_ringbuffer_get不是把2048个字节存到了writebuffer[1024]吗
使用道具 举报 回复
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

  1. 6 主题
  2. 852 帖子
  3. 852 积分

Ta的主页 发消息

Archiver|手机版|小黑屋|RT-Thread开发者社区 ( 沪ICP备13014002号-1

有害信息举报电话:021-31165890 手机:18930558079

© 2006-2019 上海睿赛德电子科技有限公司

Powered by RT-Thread

快速回复 返回顶部 返回列表