|
第十五节 SD文件系统的学习 详细内容请看附件6 O' t: ~3 z- l 本节我们通过科星F107开发板的SPI接口对SD卡进行数据的读写。主要学习的内容就是SPI的配置与文件系统的移植。这里我们用到的文件系统是FatFs, FatFS 是一个通用的文件系统模块,用于在小型嵌入式系统中实现FAT文件系统。 FatFs 的编写遵循ANSI C,因此不依赖于硬件平台。它可以嵌入到便宜的微控制器中,如 8051, PIC, AVR, SH, Z80, H8, ARM 等等,不需要做任何修改。 $ r9 H- `- f( t 最顶层是应用层,使用者无需理会FatFs Module 的内部结构和复杂的FAT 协议,只需 # ?& y6 F% ~% ]' u; ^0 i8 [要调用FatFs Module 提供给用户的一系列应用接口函数,如f_open,f_read,f_write 和f_close 等,就可以像在PC 上读/写文件那样简单。 , |4 n) B& D. R& h6 f" |8 h- J( n中间层FatFs Module 实现了FAT 文件读/写协议。FatFs Module 的完全版提供的是ff.c、 0 i( e1 n+ g% Xff.h,简化版Tiny-FatFs 提供的是tff.c、tff.h。除非有必要,使用者一般不用修改,使用时将 % b0 U5 p* H& j# m需要版本的头文件直接包含进去即可。 需要使用者编写移植代码的是FatFs Module 提供的底层接口,它包括存储媒介读/写 & q$ @" _# U* @! i7 Q( j接口Disk I/O 和供给文件创建修改时间的实时时钟。 * h" t V4 m6 h. X/ m& M: G' ^
0 k: n% M, ^. r1 b
程序的编写 # t+ _: |$ p& O3 w7 \" x# |下面我们开始代码的编写: / l9 A5 n |+ R6 h6 k/ ?1、 主函数的编写 ( V+ _( o2 A+ O) l( ]/ g+ _7 ]' fint main(void) { ' T0 H7 S8 A9 f RCC_Configuration(); GPIO_Configuration(); 8 }. K0 E3 u, t6 k USART_Configuration(); SPI_Configuration(); & ^) o5 s- q7 j: g' [( A FATFS_TEST(); //FATFS常用功能测试函数 * x3 F* D; j8 k4 `2 S7 x; a1 e while (1) 2 X& R; ~! q* K( v3 x" J) C { } 7 c! L2 o( E/ F$ Q7 [} 这里新接触的配置函数就是SPI的配置了,也是使用库提供的结构体对SPI进行定义,更多的SPI的介绍,我们这里就不多说了,MCU的芯片手册写的也很清楚了,这里复制过来也没啥意思。下面我们看一下SPI的配置函数,代码如下: 4 z& a* F( p: [1 tvoid SPI_Configuration(void) { 6 u; Z; o+ R& s3 ^ SPI_InitTypeDef SPI_InitStructure; /* SPI3 Config */ //一开始SD初始化阶段,SPI<i>时钟频率必须<span lang="EN-US" style="color:#4F81BD;mso-themecolor:accent1">SD卡驱动函数的编写 7 f6 _# F0 y( ^. K' T2 v" F5 U这里使用文件SD_driver.c和SD_driver.h来写出SD读写的一些驱动函数。 ' k' |1 p' H# s, ^函数列表如下: 6 t; w$ ]- V' E" c/* Private define ------------------------------------------------------------*/ /* SD卡类型定义 */ #define SD_TYPE_MMC 0 ; C3 A0 H& P% c' g+ a#define SD_TYPE_V1 1 #define SD_TYPE_V2 2 ' K. @8 {8 W; F8 |6 r% r#define SD_TYPE_V2HC 4 /* SPI总线速度设置*/ 0 e5 a, E" v( F4 a#define SPI_SPEED_LOW 0 #define SPI_SPEED_MID 1 #define SPI_SPEED_HIGH 2 ; t8 o8 D1 t$ E; v8 R- }) x, f/* SD传输数据结束后是否释放总线宏定义 */ 5 [, S+ D' J" E* f4 i* ?#define NO_RELEASE 0 " {/ i% u2 E) d) P#define RELEASE 1 5 B+ t8 O) `3 {: a/ T2 y2 g/ D1 }( T" Z3 q, J2 { /* SD卡指令表 */ #define CMD0 0 //卡复位 #define CMD9 9 //命令9 ,读CSD数据 + X! {$ E7 {( j#define CMD10 10 //命令10,读CID数据 #define CMD12 12 //命令12,停止数据传输 #define CMD16 16 //命令16,设置SectorSize 应返回0x00 #define CMD17 17 //命令17,读sector . }4 b7 P6 L( D: |& n. d#define CMD18 18 //命令18,读Multi sector 6 I( t3 v k8 a5 c; I#define ACMD23 23 //命令23,设置多sector写入前预先擦除N个block , T+ Z. k# q1 O3 g) X+ D#define CMD24 24 //命令24,写sector #define CMD25 25 //命令25,写Multi sector #define ACMD41 41 //命令41,应返回0x00 #define CMD55 55 //命令55,应返回0x01 #define CMD58 58 //命令58,读OCR信息 #define CMD59 59 //命令59,使能/禁止CRC,应返回0x00 /* Private macro -------------------------------------------------------------*/ //SD卡CS片选使能端操作: #define SD_CS_ENABLE() GPIO_ResetBits(GPIOD,GPIO_Pin_2) //选中SD卡 #define SD_CS_DISABLE() GPIO_SetBits(GPIOD,GPIO_Pin_2) //不选中SD卡 #define SD_PWR_ON() GPIO_ResetBits(GPIOD,GPIO_Pin_10) //SD卡上电 #define SD_PWR_OFF() GPIO_SetBits(GPIOD,GPIO_Pin_10) //SD卡断电 ' j/ z2 w+ ?7 D! \. H' ]6 x8 j#define SD_DET() 1 // !GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2) //检测有卡 1 Y6 I ^" j4 `; t" J: \ //1-有 0-无 1 _: G* U" V9 E1 e, r Z+ E4 V2 K, d2 }2 O. w /* Private function prototypes -----------------------------------------------*/ * j0 u0 O7 Y5 n, d: X3 p6 v//void SPI_Configuration(void); void SPI_SetSpeed(u8 SpeedSet); b# i; q6 K. [* L" k" N: n4 N u8 SPI_ReadWriteByte(u8 TxData); //SPI总线读写一个字节 7 k! D$ X4 L/ X% D* ou8 SD_WaitReady(void); //等待SD卡就绪 u8 SD_SendCommand(u8 cmd, u32 arg, u8 crc); //SD卡发送一个命令 ' }3 b; A; z6 _" }2 k3 G2 fu8 SD_SendCommand_NoDeassert(u8 cmd, u32 arg, u8 crc); u8 SD_Init(void); //SD卡初始化 # H% v) K P+ j# c // ) o, m. z. _* F+ t' Ou8 SD_ReceiveData(u8 *data, u16 len, u8 release);//SD卡读数据 0 C+ w: t$ \0 z, b! ]u8 SD_GetCID(u8 *cid_data); //读SD卡CID 9 R" M1 \5 ]1 lu8 SD_GetCSD(u8 *csd_data); //读SD卡CSD u32 SD_GetCapacity(void); //取SD卡容量 $ W% q. n# H+ M4 @" E u8 SD_ReadSingleBlock(u32 sector, u8 *buffer); //读一个sector u8 SD_WriteSingleBlock(u32 sector,const u8 *buffer); //写一个sector u8 SD_ReadMultiBlock(u32 sector, u8 *buffer, u8 count); //读多个sector 1 g: B& W: a$ m: ] H0 Eu8 SD_WriteMultiBlock(u32 sector,const u8 *data, u8 count);//写多个sector 3、 文件系统的移植 FatFs文件系统主要有以下函数: * x0 H6 e5 Z0 B3 ~. R. o/*-函数详细的解释请查阅附录,点击链接也可以到----*/ /* FatFs module application interface */ l) ^ d4 \, S% p# l) B" E FRESULT f_mount (BYTE, FATFS*); /* Mount/Unmount a logical drive */ FRESULT f_open (FIL*, const char*, BYTE); /* Open or create a file */ FRESULT f_read (FIL*, void*, UINT, UINT*); /* Read data from a file */ FRESULT f_write (FIL*, const void*, UINT, UINT*); /* Write data to a file */ FRESULT f_lseek (FIL*, DWORD); /* Move file pointer of a file object */ FRESULT f_close (FIL*); /* Close an open file object */ FRESULT f_opendir (DIR*, const char*); /* Open an existing directory */ FRESULT f_readdir (DIR*, FILINFO*); /* Read a directory item */ FRESULT f_stat (const char*, FILINFO*); /* Get file status */ FRESULT f_getfree (const char*, DWORD*, FATFS**); /* Get number of free clusters on the drive */ FRESULT f_truncate (FIL*); /* Truncate file */ FRESULT f_sync (FIL*); /* Flush cached data of a writing file */ FRESULT f_unlink (const char*); /* Delete an existing file or directory */ FRESULT f_mkdir (const char*); /* Create a new directory */ FRESULT f_chmod (const char*, BYTE, BYTE); /* Change file/dir attriburte */ FRESULT f_utime (const char*, const FILINFO*); /* Change file/dir timestamp */ FRESULT f_rename (const char*, const char*); /* Rename/Move a file or directory */ FRESULT f_mkfs (BYTE, BYTE, WORD); /* Create a file system on the drive */ 4、 底层接口I/O函数的编写 底层驱动是必须由用户自己完成的,函数均放在文件diskio.c中,函数如下: 6 v) A2 R$ G6 g; y/* Prototypes for disk control functions */ DSTATUS disk_initialize (BYTE); 6 W$ j/ t* ?: L- G% l( `6 EDSTATUS disk_status (BYTE); DRESULT disk_read (BYTE, BYTE*, DWORD, BYTE); : X; i$ C+ T# ?! N5 ^( Y- YDRESULT disk_write (BYTE, const BYTE*, DWORD, BYTE); 9 M3 Y+ j" a" ~6 S( r! xDRESULT disk_ioctl (BYTE, BYTE, void*); DWORD get_fattime (void); 6 个接口函数的详细信息如下图所示。 Disk I/O 函数结构图 因为FatFs 模块完全与磁盘I/O 层分开,因此需要下面的函数来实现底层物理磁盘的读写与获取当前时间。底层磁盘I/O 模块并不是FatFs 的一部分,并且必须由用户提供。下面的函数位于diskio.c 中。 1)disk_initialize 2) disk_status 3) disk_read 4)disk_write 5)disk_ioctl 6) get_fattime 5、 应用层函数的编写 我们这个例程要实现的8个功能如下: 6 _5 \; x1 { p5 \. g5 o2 D4 M4 r( U' P3 ? 每一个功能都对应一个函数,各个函数如下: E# _% A+ y; Z& U// 0 读取磁盘容量 The f_getfree function gets number of the free clusters void Test_f_getfree(void); //1 读磁盘目录 The f_readdir function reads directory entries. void Test_f_readdir(void); //2 创建目录 The f_mkdir function creates a new directory void Test_f_mkdir(void); 2 R( o# f; w. E//3 读取文件 The f_read function reads data from a file. . ~& H" H2 v5 T% G; Rvoid Test_f_read(void); //4 文件拷贝 The f_write writes data to a file. void Test_f_write(void); //5 重命名 Rename file or directory ( l* I$ d1 S9 e2 R* J% }4 Fvoid Test_f_rename(void); //6 删除文件 The f_unlink removes file or directory $ G# o# \" D4 v0 ?4 Y) Dvoid Test_f_unlink(void); //7 格式化 The f_mkfs fucntion creates a file system on the drive. ( t& |, X6 i1 j" `6 ~void Test_f_mkfs(void); 这8个函数的调用是通过一个二维的函数指针数组完成的,该数组如下: void *FATFS_Function[][2]= " k1 T- z3 }4 e: z; P6 e; Z+ f2 t. _# R{ (void*)Test_f_getfree, "磁盘容量", (void*)Test_f_readdir, "读目录 ", $ b' [& I# q; L" |. y N (void*)Test_f_mkdir, "创建路径", 0 C$ J& i0 w0 [, R- E (void*)Test_f_read, "读文件 ", 3 Q/ x: _/ O" g8 h- D9 Y (void*)Test_f_write, "文件拷贝", 8 ]/ \& l0 Q. C& ~) n' a8 E (void*)Test_f_rename, "重命名 ", (void*)Test_f_unlink, "删除文件", (void*)Test_f_mkfs, "格式化 ", 6 o% U0 k) D6 E 0x00,0x00 }; , {+ o( t, t( H这8个函数完成的功能都是调用FatFs文件系统里的函数,FatFs文件系统的函数前面已经列出了列表,在本节最后面我们也附上了函数的详细定义及用法,请查阅。下面我们着重分析一下这几个应用函数: 0)//The f_getfree function gets number of the free clusters void Test_f_getfree(void) ( B3 o$ x# j# r7 w& v1 V{ FATFS fs; ( k3 p$ z6 }( _4 w6 F FATFS *pfs; . A4 F1 C, Q0 o9 { DWORD clust; FRESULT res; // FatFs function common result code # Y7 c% f% o3 M0 Q2 @! Y: M8 R: H //检测磁盘是否插好 if( disk_detect_OK()==FALSE ) return; ) k7 A1 N, O+ q) v. R3 P" z, t8 J- ?( G2 x i$ Q pfs=&fs;//指向 * z) F v5 y' }// Register a work area for logical drive 0 //温馨提示:该函数是文件系统FatFs里的函数,详细说明请查阅本章节最后的附录,可以用Ctrl+F快捷键,弹出搜索框,输入该函数名,找到该函数。以下类似函数均可以这样找到详细说明,不再赘述。该类函数我们会标记红色,并加粗,请留意。比如f_mount, 解释如下: i$ L5 s$ _! M% H9 U7 Mf_mount(0, &fs); $ F7 Q% y- }8 E; [* w. E1 |// 获取空闲簇Get free clusters ,空闲簇的值存在clust : U( z2 V9 d# A/ F, C res = f_getfree("/", &clust, &pfs);//必须是根目录,默认磁盘0 4 F% X% D' h2 _/ ?* d if ( res==FR_OK ) ) ]2 D# ?8 v/ o { & d9 W6 A" r& @# T& H" v+ \ // Get free space 这里是打印显示出 SD卡总的空间大小以及剩余空间大小 L& ?- R2 E$ ^* `$ P //计算方法: // 总空间大小 最大簇数×每簇的扇区数÷2÷1024 // 剩余空间大小 空闲簇数×每簇的扇区数÷2÷1024 printp("\r\n%d MB total disk space.\r\n%d MB available on the disk.\r\n", # l2 |, E' D, v$ @7 ^ (DWORD)(pfs->max_clust - 2) * pfs->csize /2/1024, clust * pfs->csize /2/1024); } 4 p* Q2 T e6 i( S9 [; Q else die(res);//测试函数执行结果分析 X/ Q+ f* V. o& n6 n* c // Unregister a work area before discard it ( i: A) `( e& N7 j' p f_mount(0, NULL); " B0 c( ^1 }6 P/ W3 _" |} * U# f6 r+ v, K K( R+ q% V8 v3 M* V, U1 ~2 D! |4 o' x //1 读取目录 The f_readdir function reads directory entries. * f; A7 }" ^# i' o! q* {void Test_f_readdir(void) { ; z6 D4 k1 T) @ FATFS fs; // Work area (file system object) for logical drive , y! z4 V1 x7 S h) ?; D char path[20]; 9 d. W$ V3 G5 j& ?+ L- w. o& `/ Q //检测磁盘是否插好 ( O) C9 }' C l1 j: [6 B if( disk_detect_OK()==FALSE ) / N/ W1 m$ _' z) z' N return; f_mount(0, &fs); // Register a work area for logical drive 0 8 ?+ F- z4 T7 M; @" D5 @5 p6 h printp("\r\nread directory:>/");//默认输出主目录下的文件 ; _( i4 H7 O2 }2 O& w! B) @8 y" a' v USART_Scanf_Name(path);//通过串口输入路径名 . q2 a; W; A: [3 D$ q( j. Q( q! }( X; A //扫描当前路径下子文件夹,并输出到串口 9 H7 g7 X) @. d' }' y. w scan_current_folder(path); //扫描当前路径下文件,并输出到串口 2 A( Z8 l. A3 }$ D1 l% M, W scan_current_files(path); ! E' @6 g1 L, S! C( T! K f_mount(0, NULL);// Unregister a work area before discard it - S+ ^: J/ B- s$ C} ; f- c0 X% x! u7 ]& c" U* y- T9 ^ b: F1 q . b- K; J5 l3 D: u* f1 m) X& O //2 读取目录 The f_mkdir function creates a new directory void Test_f_mkdir(void) { FATFS fs; // Work area (file system object) for logical drive FRESULT res; // FatFs function common result code char path[20]; //检测磁盘是否插好 if( disk_detect_OK()==FALSE ) return; f_mount(0, &fs); // Register a work area for logical drive 0 printp("\r\nmake directory:>"); USART_Scanf_Name(path);//通过串口输入路径名 res = f_mkdir(path); die(res);//测试函数执行结果分析 f_mount(0, NULL);// Unregister a work area before discard it } 4 Y/ E/ s- G! I# z5 b //3 读文件 The f_read function reads data from a file. void Test_f_read(void) { //用来记录读文件起始时间、结束时间 struct tm time_start,time_end ; //用来记录读文件起始时间、结束时间 ,unix时间格式,用来记录时间差。 time_t unix_start,unix_end,interval; char path[20]; //检测磁盘是否插好 if( disk_detect_OK()==FALSE ) return; //printp("\r\nread filebbbb:>"); // Register a work area for logical drive 0 f_mount(0, &fs); printp("\r\nread file:>"); USART_Scanf_Name(path);//通过串口输入文件路径名 //Open source file res = f_open(&fsrc, path, FA_OPEN_EXISTING | FA_READ); die(res); //buffer空间设大一点,会提高读的速度。 //如果文件实际大小512byte, //设为buffer[512]时,只需要循坏一次,如果设为buffer[1],需要循坏512次。 //下面两行主要是去除1s误差。 //Read_File_Flag = 0; //while( Read_File_Flag==0 ); //time_start=Time_GetCalendarTime();//记录开始读时间 //unix_start=Time_ConvCalendarToUnix(time_start); * d* h8 r- V0 X3 E1 @& ]* A' y . o7 K) \' r$ k8 I6 w4 O% V; z! } //输出提示.. if( res==FR_OK ) printp("\r\nread file start time :%02d:%02d:%02d\r\nplease waiting...\r\n", time_start.tm_hour, time_start.tm_min, time_start.tm_sec); for (;;) { //清除缓存 <span lang="EN-US"> for(i=0;iRTC函数的编写 |
15ç§æF107å¼åæ¿å¦ä¹ ç¬è®°âSDå¡ä¸æä»¶ç³»ç»FatFsçå¦ä¹ .pdf
下载2.25 MB, 下载次数: 949
微信公众号
手机版
求助一个STM32F103VCT6与AT45DB081芯片通讯添加文件管理系统FATFS,SPI2的接口的示例
回复:STM32 FatFs文件系统 移植,SD读写数据 58页笔记+源码
15ç§æF107å¼åæ¿å¦ä¹ ç¬è®°æºç âSDå¡ä¸æä»¶ç³»ç»FatFsçå¦ä¹ .rar
2013-11-20 09:58 上传
点击文件名下载附件
863.7 KB, 下载次数: 886
RE:STM32 FatFs文件系统 移植,SD读写数据 58页笔记+源码
RE:STM32 FatFs文件系统 移植,SD读写数据 58页笔记+源码
RE:STM32 FatFs文件系统 移植,SD读写数据 58页笔记+源码
RE:STM32 FatFs文件系统 移植,SD读写数据 58页笔记+源码
RE:STM32 FatFs文件系统 移植,SD读写数据 58页笔记+源码
回复:STM32 FatFs文件系统 移植,SD读写数据 58页笔记+源码
回复:STM32 FatFs文件系统 移植,SD读写数据 58页笔记+源码