|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
移植Tiny-FATFS文件系统到STM8L152C6T6,使用硬件SPI+RTC& L5 l- h/ p) x; f* V) h" T o
, D+ ^2 W4 u D. w' t4 D
最近老师需要做一个低功耗的各种环境参数采集仪,要求不多,就是采集各种参数并储存起来,但是要求低功耗。所以我第一时间就想到了stm8L系列单片机,发出去做的板子还没回来,就先使用现有的一块板子先调试文件系统和RTC这些啦。今晚还有点时间就写个移植过程吧,给后来人种一颗小树苗吧。。。5 Y$ m; t/ G" q9 a0 X% D9 V Y: s
. h' N4 |, F- W" A o# n硬件介绍:0 M$ {& r% v. d2 W/ P2 ?4 M; z
单片机:STM8l152c6t6, 2K RAM,32K FLASH
7 U3 H4 F+ q/ }* c4 ^& S6 x4 A% z sd卡:4G和8G # n. i5 K( |! c/ h+ Y g
接口连接://SD_DT3--SD_CS----------------PB44 E6 n. a+ N* o
//SD_CMD--spi1_MOSI--------PB6
- R. \0 j# O4 Z( w6 M //SD_SCK--SPI1_SCK-----------PB5
1 ~4 [* c* e) [& q0 O: k* y6 L //SD_DT0--SPI1_MISO--------PB7! f3 A# o+ o5 v$ t q% k( O: M* R: l
- J7 V: c9 _$ U& X3 r+ y2 J软件介绍:
3 Q- U7 s- z9 H, d+ b 编译器:IAR for STM8
" i! k+ u5 i+ K2 s G9 ?/ f FATFs版本:R0.11a ,直接到FAT官网上面下载即可,下面附件也有。+ B# G; ]7 G) v" T# }
通信协议:硬件SPI (附件也有一份软件SPI的源码)8 c8 y L& n) d6 B
RTC:使用STM8L内置的RTC% t& i6 W; x* p0 e
5 L9 d. E% z: L4 C$ o移植意义:可能大家觉得在STM8这种这么low的单片机上面运行FAT文件系统没什么意义,但是我觉得STM8l在用在低功耗采集或者信息节点的场合很多时候都需要存储数据,那么说到存储数据最适合的存储介质我觉得就是SD卡了,低成本大内存。那么自然想到FAT,存储的文件可以直接在电脑打开,每天新建一个文件来存储等。所以我觉得这东西上面使用FAT还是很有意义的。Tiny-FATFS需要单片机的1K的RAM用做工作区,具体需要多大的FLASH看你的要求了,可以适当的裁剪FATFS,我编译出来是20多K。! n8 O" [! p/ [; D# t
# Y8 F0 Y" G3 T0 \/ _% K' }/ Q' u4 O移植步骤:
: K3 K+ c( v: P0 Y& [. Z 1.下载FATFS源码包,解压之后把src文件夹直接复制到工程目录中,并把带的字库和说明删掉,如下图;7 H- n! l9 x' p. n% \( ?: p7 y
8 u7 d+ } }% U
2.打开STM8工程,把FATFS中的xx.c文件(diskio.c和ff.c)添加至工程目中,并在option中包含路径
4 P+ | K% b0 l. L6 H
2 c a( k! N0 J* ~% { 3.配置ffconf.h(简要说下,具体可参考原子哥的手册);
3 |& \. ^: I/ W$ O f5 A1 Y j #define _FS_READONLY 0 //可读可写
. C- E" K- z p9 y1 b: x ##define _FS_MINIMIZE 3 //_FS_MINIMIZE、 _FS_READONLY、 _USE_STRFUNC、 _USE_MKFS、_USE_FORWARD 这些宏是用来对文件系统进行裁剪的,具体见下表
6 r" z. |9 \# e+ d9 u) @3 _ #define _CODE_PAGE 1 //就支持ASCII码,小内存伤不起
4 f( e' X* y3 k% s$ B+ b 。。。。。。。
) A0 L" I) w& Z j1 p. D V5 Z9 h #define _VOLUMES 1 //注意:Tiny-FATFS仅支持一个逻辑设备,设为1) \# V7 r5 g' g$ @+ g8 U$ X
#define _FS_TINY 1 //就是这个地方,设为1之后就是Tiny-FATFS了,编译之后你会发现比设为0的时候用的RAM少不少,只需1K,当然牺牲就是变慢了8 u {6 L2 n' n
; q* C& y b& n9 C
4.编写diskio.c里面的5个接口函数。: ~! l8 g; t# ]- k! W/ ^1 b! f) k+ }
前面5个接口函数我是参照原子哥的代码改的,我把除了SD_CARD的其他选项全删了,只留下SD_CARD的部分,因为其他的设备都用不上。(具体代码见附件)$ }: `7 I4 n. r |
get_fattime函数:8 C% `! i ]3 u. @- `; i
DWORD get_fattime (void)
( ~1 S) n( u |; @" Y4 h5 K* `9 A# m{
: F# ?( \+ F, U' J4 k; p4 N return (((u32)(RTC_DateStr.RTC_Year-(RTC_DateStr.RTC_Year>>4)*6)+20) << 25) /* Year */6 L% {7 s K, W9 Y5 i d3 y
| ((u32)(RTC_DateStr.RTC_Month-(RTC_DateStr.RTC_Month>>4)*6) << 21) /* Month*/1 e. C/ \) ~& ~9 J" Y5 R1 r' N
| ((u32)(RTC_DateStr.RTC_Date-(RTC_DateStr.RTC_Date>>4)*6) << 16) /* Day*/
& H: [% \1 n: W+ S$ c: t. d1 V5 F | ((u32)(RTC_TimeStr.RTC_Hours-(RTC_TimeStr.RTC_Hours>>4)*6) << 11) /* Hour*/9 h$ _; w1 P2 B2 S
| ((u32)(RTC_TimeStr.RTC_Minutes-(RTC_TimeStr.RTC_Minutes>>4)*6) << 5) /* Min*/
4 L9 E+ k# u. k | ((u32)(RTC_TimeStr.RTC_Seconds-(RTC_TimeStr.RTC_Seconds>>4)*6) >> 1) /* Sec*/, Z4 C1 M5 e7 A& F# ~
;
5 W& h. ~1 @5 _8 ^}' E1 D# T8 k6 t8 d+ i Y* A
9 a" U# U, {3 h2 y6 J6 O 这里使用的是内部的RTC,每1S时间产生一个唤醒中断,然后在中断函数中更新日期时间,注意这里的日期时间都是BCD吗,例如0x16表示的是16年,所以上面做了转换。
0 q7 o3 s$ }3 q; z: \, n! f) j3 Y8 P7 W5 Y# A1 N
5.RTC初始化配置见附件源码了。下面是初始化过程中一个需要注意的地方4 v- P, I4 J! D" c' p: S, G9 [
if ((RTC->ISR1 & RTC_ISR1_INITS) == RESET) //Check calendar has been initialized or not?
5 G; l" k5 T* y {5 W% r9 t2 j* ]0 D
RTC_DateStructInit(&RTC_DateStr);
8 M w8 P! \. @# Z, D RTC_DateStr.RTC_WeekDay = RTC_Weekday_Friday;# I/ T7 p1 g. z& C& A
RTC_DateStr.RTC_Date = 6;
& i7 i) d) W0 J* B RTC_DateStr.RTC_Month = RTC_Month_March;
6 m0 Q* R/ \. o! k. ]1 O e' h5 f RTC_DateStr.RTC_Year = 16;. r& T0 ~, O. _/ E- l' D8 z
RTC_SetDate(RTC_Format_BIN, &RTC_DateStr);4 n9 S9 `2 x8 J( a1 p: J- x
6 S# I) X8 z3 P) } T2 G
RTC_TimeStructInit(&RTC_TimeStr);2 C$ K _/ h/ \2 J- \4 ~; z$ m
RTC_TimeStr.RTC_Hours = 20;
5 F) i* p# I1 G. C RTC_TimeStr.RTC_Minutes = 02;
/ f0 T [) e* R RTC_TimeStr.RTC_Seconds = 00;
6 ~. G+ H" U8 q& M. _6 X" S3 {, }* H RTC_SetTime(RTC_Format_BIN, &RTC_TimeStr);
1 q/ y/ z) L1 O' O1 ~ }( C/ w; ?7 d6 L
! H9 M: s" U* j0 n就是设置日期时间之前检查是RTC是否已经被设置过了,设置过了就不需要设置了。类似于stm32的备份寄存器,这样子只要不断电RTC的时间就不会复位。顺便提一句,STM8l的RTC还是很稳定、很准的,当时测了两三天跟电脑的时间差不了2秒,美中不足的是没有留出电池供电引脚,单片机断电就完了。 Z% y! x9 n5 T3 U+ y
6.硬件SPI配置,这步非常简单,看图6 X. k8 j% U7 z S1 |! `
! U$ b/ D# t b5 Y4 a: }* I
, k4 m3 P# G( @5 m简简单单几句就把它给配置了,做好这个借口,直接就跟原子哥的MMC_SD.c和MMC_SD.H对接上了,修改很少。3 X; _% k# N& z8 h0 N1 e
) r7 m' L) @7 o! ]8 H7.最后主函数:不适用内存分配函数,直接全局变量作为静态工作区。
1 _8 l$ Z# J& ~0 w ! c3 m& O+ m# c2 j( X: ?
测试代码:挂载并新建文件/ `+ v# r' k! k( h) t% F
0 I, O1 B" Q( t: m0 H$ g/ b! [$ {" y7 A
编译运行结果(RAM包含了自己测试用的一个长度为514字节的buff数组):
2 D) \. l$ ]0 r, N* L
# F6 N5 P0 N4 W" t2 U: t9 N' e J1 |/ P6 q T' r
9 N( `+ i6 L8 h! a2 F在工程Backup文件夹里面还有一段测试读文件的代码:+ x* K$ o/ ~- m- ~1 \* V* e
res = f_open(&fl,"news/news.txt",FA_READ); % E6 L1 [' G, A2 s2 i. P' n) L. v
if(res == FR_OK)4 x$ K6 d) L$ K# h! Z0 j. q
{0 k" s E1 U( l) l0 P5 Q
Usart1_Str("Open file ok!\r\n");
! s& x4 b, g# C( E6 Y: r, K- X: n Usart1_Str("The news is:\r\n");
3 R: s A- Z: o2 B do8 z. V2 _4 M' f( v6 @
{
1 Q/ D _- o( P) c7 O/ w6 r //for(i=0;i<50;i++) buff = 0;6 @* \% m2 @: Q
res = f_read(&fl,buff,512,&r);
* j# [6 K; F. g //Usart1_Str(buff);
" b+ P9 W( b2 P if(res || r<512): ]6 W+ h2 o) o( r9 H2 d, W
{7 K+ H- J8 s% C4 t- @% F
Usart1_Str("Read over!\r\n");6 `* o" q* y6 c9 `% s8 x" q
break; //error or eof 9 }& p: L; ~. J0 L( h) V7 x$ r* }
}) k: p! f# M% R% _7 w( ]! n: U
}& ]. R+ |3 O2 q& d
while(1);
1 D/ J3 Q: l r: ^- q6 T% j: z }4 {* I9 f7 @: W0 w& }
else0 \$ Y1 k. S9 v
Usart1_Str("Open file failed!\r\n");
0 W- C; y. @1 J" F f_close(&fl);0 ]5 r8 h$ w: I& C! `" ~9 u
! U' S7 D9 E1 L8 U! `
/ z8 ~$ Q" K7 \6 H! f6 @4 A事先在SD卡里新建一个news/news.txt文件,大小为1026KB,最后测试读出时间是14秒,平均速度是60多Kbyte/s的读出速度,对于一般应用相当够用;# ~/ y) [% Y1 S" K, `
跟另一个用模拟SPI写的工程比较,读取同样的这个文件,耗时一分49秒,平均速度只有10Kbyte/s左右。写入速度没测试,应该会慢一些。。
* F) c: v$ p9 V' R1 F) X2 F5 f& `' C! a8 y4 u
9 j0 r5 i9 s2 q6 Z' R- C0 k* O下载:
+ @# b" S/ j5 N! ?8 t- j% l% s' F
* @2 H( L+ C6 b8 }6 j |
|