|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
移植Tiny-FATFS文件系统到STM8L152C6T6,使用硬件SPI+RTC
- g0 t$ H/ q: {$ S. g- J% Q* H8 p3 S
最近老师需要做一个低功耗的各种环境参数采集仪,要求不多,就是采集各种参数并储存起来,但是要求低功耗。所以我第一时间就想到了stm8L系列单片机,发出去做的板子还没回来,就先使用现有的一块板子先调试文件系统和RTC这些啦。今晚还有点时间就写个移植过程吧,给后来人种一颗小树苗吧。。。+ c5 V/ ]" E1 E* y
0 Z8 X ?; d. b: P6 p' k
硬件介绍:% P: t! ?( K6 D5 d
单片机:STM8l152c6t6, 2K RAM,32K FLASH
0 \ |8 K% Z# ?; g! K sd卡:4G和8G
) L1 W9 C+ q1 E/ o- R 接口连接://SD_DT3--SD_CS----------------PB4+ T3 W. L3 S& C7 V5 D
//SD_CMD--spi1_MOSI--------PB6% B) g" z5 ~. Q3 A' c
//SD_SCK--SPI1_SCK-----------PB50 Q. h% Y; Y: ?: o+ e& o* P
//SD_DT0--SPI1_MISO--------PB7( s5 ^# u& h8 q7 ~
1 z% P: k( K) ]) M9 o/ `4 f. F
软件介绍:
6 C) u2 k4 Q7 y! ^ 编译器:IAR for STM8! V% A% H( T% G. ^1 u+ l
FATFs版本:R0.11a ,直接到FAT官网上面下载即可,下面附件也有。
9 ]) q* @: |9 a$ }+ n! L, M 通信协议:硬件SPI (附件也有一份软件SPI的源码)
, F2 }6 k! F( w4 L+ n9 n+ M RTC:使用STM8L内置的RTC
9 O4 ]7 Z' m# p' Y9 O- M* a' @. J' G! a8 C7 V1 [) n! l) ?
移植意义:可能大家觉得在STM8这种这么low的单片机上面运行FAT文件系统没什么意义,但是我觉得STM8l在用在低功耗采集或者信息节点的场合很多时候都需要存储数据,那么说到存储数据最适合的存储介质我觉得就是SD卡了,低成本大内存。那么自然想到FAT,存储的文件可以直接在电脑打开,每天新建一个文件来存储等。所以我觉得这东西上面使用FAT还是很有意义的。Tiny-FATFS需要单片机的1K的RAM用做工作区,具体需要多大的FLASH看你的要求了,可以适当的裁剪FATFS,我编译出来是20多K。/ `* ]% @5 \( Z
9 ~1 `8 T) V- z3 H
移植步骤:! o; X% X8 h7 @/ W! X7 r' @+ v
1.下载FATFS源码包,解压之后把src文件夹直接复制到工程目录中,并把带的字库和说明删掉,如下图;
" R, C$ y& j! m) z ( e6 Q$ y7 I. A2 _6 n
2.打开STM8工程,把FATFS中的xx.c文件(diskio.c和ff.c)添加至工程目中,并在option中包含路径0 b& q, W! r% _ O# r
0 v0 I7 K! b; s, e 3.配置ffconf.h(简要说下,具体可参考原子哥的手册);
% `. \# S2 o' s: F% O$ F) ~6 ] #define _FS_READONLY 0 //可读可写
% w5 ?2 q. e4 E' r% L4 X/ Y ##define _FS_MINIMIZE 3 //_FS_MINIMIZE、 _FS_READONLY、 _USE_STRFUNC、 _USE_MKFS、_USE_FORWARD 这些宏是用来对文件系统进行裁剪的,具体见下表
' |0 U# F( c! z9 h0 O #define _CODE_PAGE 1 //就支持ASCII码,小内存伤不起
, U& I: F4 j6 I7 n: h' ~3 `5 D 。。。。。。。' W! Z2 ]" {) x p
#define _VOLUMES 1 //注意:Tiny-FATFS仅支持一个逻辑设备,设为1
) @% l& v& P( m0 D8 l# E; ? #define _FS_TINY 1 //就是这个地方,设为1之后就是Tiny-FATFS了,编译之后你会发现比设为0的时候用的RAM少不少,只需1K,当然牺牲就是变慢了$ Q6 q7 ]- B3 f/ _1 @1 Z- D% D
0 f2 G0 @. o6 G2 X" a6 R8 p
4.编写diskio.c里面的5个接口函数。. `$ O9 H8 F1 E$ \+ O7 x
前面5个接口函数我是参照原子哥的代码改的,我把除了SD_CARD的其他选项全删了,只留下SD_CARD的部分,因为其他的设备都用不上。(具体代码见附件)5 l+ L) u" j/ e( ?
get_fattime函数:! Q* i1 k" {8 k& J
DWORD get_fattime (void)0 z6 Q k U4 d0 ~2 Y! u
{ . {7 E. Y d( P @2 d; I
return (((u32)(RTC_DateStr.RTC_Year-(RTC_DateStr.RTC_Year>>4)*6)+20) << 25) /* Year *// ?" B; ?& ~$ V6 D' K" @
| ((u32)(RTC_DateStr.RTC_Month-(RTC_DateStr.RTC_Month>>4)*6) << 21) /* Month*/. E9 `: J: N, [8 M! X1 } g: w
| ((u32)(RTC_DateStr.RTC_Date-(RTC_DateStr.RTC_Date>>4)*6) << 16) /* Day*/
+ e- C, f9 R0 R( r: O | ((u32)(RTC_TimeStr.RTC_Hours-(RTC_TimeStr.RTC_Hours>>4)*6) << 11) /* Hour*/$ [# z' r2 q; f# o, P5 e7 K5 E. d
| ((u32)(RTC_TimeStr.RTC_Minutes-(RTC_TimeStr.RTC_Minutes>>4)*6) << 5) /* Min*/
c3 Q/ @, z6 @+ p( ] | ((u32)(RTC_TimeStr.RTC_Seconds-(RTC_TimeStr.RTC_Seconds>>4)*6) >> 1) /* Sec*/
, b! v- E- B6 v5 y G5 `;
' Y9 }( I- N( A2 e" i8 d}9 ^$ a6 N8 h; r8 k; \' n9 N
( @. z- G$ O6 \2 o) M8 n
这里使用的是内部的RTC,每1S时间产生一个唤醒中断,然后在中断函数中更新日期时间,注意这里的日期时间都是BCD吗,例如0x16表示的是16年,所以上面做了转换。
' @ M% W: P) z. v9 z) l% L, {( ]( O( P% s0 _! O
5.RTC初始化配置见附件源码了。下面是初始化过程中一个需要注意的地方
! A' A5 e+ y# v8 G% i x if ((RTC->ISR1 & RTC_ISR1_INITS) == RESET) //Check calendar has been initialized or not?' \' `9 H5 \/ Z- U
{6 f, r: W0 ]1 h& p: g2 T# x/ ~3 Q
RTC_DateStructInit(&RTC_DateStr);9 D# r5 t5 C' d- e* w
RTC_DateStr.RTC_WeekDay = RTC_Weekday_Friday;6 }) z& Z& I1 V; F
RTC_DateStr.RTC_Date = 6;
+ i# n* h5 `% n8 L RTC_DateStr.RTC_Month = RTC_Month_March;) }; p! C4 J4 n1 B1 w: J- }0 y
RTC_DateStr.RTC_Year = 16;- V* Y. v: @6 X* Y' `
RTC_SetDate(RTC_Format_BIN, &RTC_DateStr);
4 ]$ E! k+ x& E' C( R2 o% W4 ^# j' q6 _3 F% b, P3 _2 ^
RTC_TimeStructInit(&RTC_TimeStr);
# {2 I& F$ i$ ]$ H/ p* |3 ? RTC_TimeStr.RTC_Hours = 20;
# Z; @1 o9 Y* G E6 \2 r RTC_TimeStr.RTC_Minutes = 02;
' ~. _7 t- u' }/ @$ K RTC_TimeStr.RTC_Seconds = 00;
6 v1 ? h/ y1 u5 ?0 G1 G RTC_SetTime(RTC_Format_BIN, &RTC_TimeStr); $ M, j3 V2 ?$ C" V) y) @
}
8 c# D7 K4 B& l9 k! T; y
; g4 }: v* s7 s5 q8 p; s( u就是设置日期时间之前检查是RTC是否已经被设置过了,设置过了就不需要设置了。类似于stm32的备份寄存器,这样子只要不断电RTC的时间就不会复位。顺便提一句,STM8l的RTC还是很稳定、很准的,当时测了两三天跟电脑的时间差不了2秒,美中不足的是没有留出电池供电引脚,单片机断电就完了。: u1 h3 ~* ~3 W+ k4 q9 C
6.硬件SPI配置,这步非常简单,看图
# ^2 ^1 V% ]' x7 ~) z6 u+ C' C6 j+ `& c. d
! e; d3 z5 J( S/ P
简简单单几句就把它给配置了,做好这个借口,直接就跟原子哥的MMC_SD.c和MMC_SD.H对接上了,修改很少。
& ]. ?( b3 {4 u% P; z q( T ^6 a8 h( `2 c7 T- o0 f
7.最后主函数:不适用内存分配函数,直接全局变量作为静态工作区。
7 h5 O5 @( c: q$ ~2 {2 J 2 g0 F1 }5 L9 X, l$ A
测试代码:挂载并新建文件( W- O! I8 O, i: o! X7 H; J
4 [/ q8 t: b- C
8 F$ k: g+ {. P. M) h
编译运行结果(RAM包含了自己测试用的一个长度为514字节的buff数组):
) O2 y: Z7 v+ l+ _/ [& D) n7 K; F4 v/ H- J# i2 G
- o3 i, m& } g! t" _5 c5 p; W3 s4 K% j) l, n* p
在工程Backup文件夹里面还有一段测试读文件的代码:
# n- `. _" a- E" w. F1 e& | res = f_open(&fl,"news/news.txt",FA_READ);
7 z. c) n n; y3 R$ s, q if(res == FR_OK): s: P [* r( R7 L
{9 ]. m- L6 e3 Q1 ^5 ?4 d4 l0 Y3 D
Usart1_Str("Open file ok!\r\n");% R. J' q6 \6 H% c z+ d
Usart1_Str("The news is:\r\n");
% U1 T/ K c8 ] P' ^% V; f do
. {3 I F3 M8 }. }$ |+ c+ Q {7 w% D% O* L+ F5 ]7 G( D
//for(i=0;i<50;i++) buff = 0;+ K3 M6 X% }! V$ _6 i6 k0 E
res = f_read(&fl,buff,512,&r);
# R) v+ m! T* p7 s //Usart1_Str(buff);
6 }! Z- I2 V% V/ S5 @' v% P if(res || r<512)
- c, R: J1 J4 X* q {. [. s! G9 b" k8 p/ ?9 S
Usart1_Str("Read over!\r\n");7 u( Q! s- G1 \2 H' c
break; //error or eof
3 D$ J0 R$ r5 W/ {! e }
+ M$ O. Z/ X5 K& o; F: E }
7 `1 S5 S t4 A2 @' C% v( z while(1);
" o( f! {' s8 G0 q# f; S! B% V }
. w6 E+ C5 i& T! U, E# } else$ g+ t8 R9 J: s5 K9 }! d b; q" F9 t& w: P
Usart1_Str("Open file failed!\r\n");" O$ |7 X( t! X
f_close(&fl);) p+ U7 Q) b. c) v
( d* C/ M5 r% P* {0 g& H
8 L p* h9 v) X5 Q# X
事先在SD卡里新建一个news/news.txt文件,大小为1026KB,最后测试读出时间是14秒,平均速度是60多Kbyte/s的读出速度,对于一般应用相当够用;/ l9 f4 V+ \/ @% l6 @, d5 N }9 k
跟另一个用模拟SPI写的工程比较,读取同样的这个文件,耗时一分49秒,平均速度只有10Kbyte/s左右。写入速度没测试,应该会慢一些。。
1 |% f" j% ^9 D4 E2 m7 U
7 r# q1 A8 Q; j/ [7 _+ D& J( H" s% I. P$ x
下载:% W0 R* o. y/ r% i1 I6 Y
F6 H. q A& o0 D# ?: ^% g5 n8 k |
|