
第十五节 SD文件系统的学习 详细内容请看附件 ! R1 [$ x4 S9 w8 h, P 本节我们通过科星F107开发板的SPI接口对SD卡进行数据的读写。主要学习的内容就是SPI的配置与文件系统的移植。这里我们用到的文件系统是FatFs, FatFS 是一个通用的文件系统模块,用于在小型嵌入式系统中实现FAT文件系统。 FatFs 的编写遵循ANSI C,因此不依赖于硬件平台。它可以嵌入到便宜的微控制器中,如 8051, PIC, AVR, SH, Z80, H8, ARM 等等,不需要做任何修改。 ! E6 N6 ~& I7 x/ a 最顶层是应用层,使用者无需理会FatFs Module 的内部结构和复杂的FAT 协议,只需 要调用FatFs Module 提供给用户的一系列应用接口函数,如f_open,f_read,f_write 和f_close 等,就可以像在PC 上读/写文件那样简单。 中间层FatFs Module 实现了FAT 文件读/写协议。FatFs Module 的完全版提供的是ff.c、 ff.h,简化版Tiny-FatFs 提供的是tff.c、tff.h。除非有必要,使用者一般不用修改,使用时将 需要版本的头文件直接包含进去即可。 需要使用者编写移植代码的是FatFs Module 提供的底层接口,它包括存储媒介读/写 / ?0 @( v6 s4 E$ N接口Disk I/O 和供给文件创建修改时间的实时时钟。 0 N i7 g$ ^+ G7 |0 y) O) X! M ?. h% O2 P" G
& ^5 C1 i% l( {+ P: C; F 程序的编写 下面我们开始代码的编写: 4 N D5 Y/ s" K- H1、 主函数的编写 * v* Q$ q' p8 Y8 D5 K/ Q7 l+ N" Bint main(void) { , c+ v5 I& l, v3 i1 e" D RCC_Configuration(); GPIO_Configuration(); USART_Configuration(); SPI_Configuration(); ; l+ A u C$ g- }; Y# r FATFS_TEST(); //FATFS常用功能测试函数 while (1) ) C/ A j9 o) z" k$ w$ e. U3 S0 N { 2 ^0 x9 [- X/ m- o: ^; I } } 这里新接触的配置函数就是SPI的配置了,也是使用库提供的结构体对SPI进行定义,更多的SPI的介绍,我们这里就不多说了,MCU的芯片手册写的也很清楚了,这里复制过来也没啥意思。下面我们看一下SPI的配置函数,代码如下: 0 w/ ~+ V% T4 D% N/ Xvoid SPI_Configuration(void) { + r5 A. p' M. L) Z SPI_InitTypeDef SPI_InitStructure; /* SPI3 Config */ / B( P: C. }0 I5 t: T //一开始SD初始化阶段,SPI<i>时钟频率必须<span lang="EN-US" style="color:#4F81BD;mso-themecolor:accent1">SD卡驱动函数的编写 / a B R5 [/ e J- G这里使用文件SD_driver.c和SD_driver.h来写出SD读写的一些驱动函数。 ) k$ s1 j1 ~% z1 }! O" T函数列表如下: s4 p9 _4 H( |. L" v' {/* Private define ------------------------------------------------------------*/ /* SD卡类型定义 */ 6 m7 L6 @3 [+ z+ \& T" H#define SD_TYPE_MMC 0 # S: q9 n% m2 t! J, {, b#define SD_TYPE_V1 1 4 u" M- `3 P( j6 ]/ T#define SD_TYPE_V2 2 1 d4 B1 A4 k" [% M( x, i* J#define SD_TYPE_V2HC 4 % Q# K8 y6 p5 _( Q2 p/ c1 I; N f/* SPI总线速度设置*/ #define SPI_SPEED_LOW 0 5 h. o" [/ p! c: s) L#define SPI_SPEED_MID 1 ; V$ a" |7 P% |5 J) y- Q" P#define SPI_SPEED_HIGH 2 /* SD传输数据结束后是否释放总线宏定义 */ #define NO_RELEASE 0 4 M) E7 t; A8 r e* `5 W% E#define RELEASE 1 & x6 v' F" L$ J, H3 U$ u' x! n) [; e' x /* SD卡指令表 */ ! k/ H8 d+ r* h8 [& f( g, k+ L#define CMD0 0 //卡复位 1 x$ O) D. u) ^/ G; Q#define CMD9 9 //命令9 ,读CSD数据 4 y2 i% @! M& c. E, Z#define CMD10 10 //命令10,读CID数据 #define CMD12 12 //命令12,停止数据传输 #define CMD16 16 //命令16,设置SectorSize 应返回0x00 #define CMD17 17 //命令17,读sector # p1 K% U2 t8 V9 ?* G7 D#define CMD18 18 //命令18,读Multi sector #define ACMD23 23 //命令23,设置多sector写入前预先擦除N个block #define CMD24 24 //命令24,写sector * Y$ o( X- J* _1 H n+ |) N#define CMD25 25 //命令25,写Multi sector #define ACMD41 41 //命令41,应返回0x00 ) |; [: z8 s* Z) y' w% F6 H; ^#define CMD55 55 //命令55,应返回0x01 #define CMD58 58 //命令58,读OCR信息 #define CMD59 59 //命令59,使能/禁止CRC,应返回0x00 ( H! J5 h6 L5 X5 Z' O/* Private macro -------------------------------------------------------------*/ 7 M$ k9 x( k9 U7 B, J9 y" T//SD卡CS片选使能端操作: #define SD_CS_ENABLE() GPIO_ResetBits(GPIOD,GPIO_Pin_2) //选中SD卡 0 d0 [* @: ]" t* W6 k' ]#define SD_CS_DISABLE() GPIO_SetBits(GPIOD,GPIO_Pin_2) //不选中SD卡 #define SD_PWR_ON() GPIO_ResetBits(GPIOD,GPIO_Pin_10) //SD卡上电 : _: U- y8 {6 C7 d b#define SD_PWR_OFF() GPIO_SetBits(GPIOD,GPIO_Pin_10) //SD卡断电 5 f5 Q" W8 g. t& ~' g2 P- p#define SD_DET() 1 // !GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2) //检测有卡 //1-有 0-无 / J* {2 G; ~$ {% |% ^/* Private function prototypes -----------------------------------------------*/ 7 C6 }" Q: v- [2 |- V; ^//void SPI_Configuration(void); void SPI_SetSpeed(u8 SpeedSet); 2 T' e1 {; r" }8 g5 ju8 SPI_ReadWriteByte(u8 TxData); //SPI总线读写一个字节 : g. p- K; l& Y3 m; ku8 SD_WaitReady(void); //等待SD卡就绪 + P. d5 _* l; Q* S5 P# du8 SD_SendCommand(u8 cmd, u32 arg, u8 crc); //SD卡发送一个命令 + `& w# M! D+ C( m( a [u8 SD_SendCommand_NoDeassert(u8 cmd, u32 arg, u8 crc); u8 SD_Init(void); //SD卡初始化 5 m; }- J) |7 J% A/ J/ W // 1 V7 Q( Q! W6 X; I6 \, Bu8 SD_ReceiveData(u8 *data, u16 len, u8 release);//SD卡读数据 4 p" |9 r% G8 G& J& i% Lu8 SD_GetCID(u8 *cid_data); //读SD卡CID # m" j; N8 g: lu8 SD_GetCSD(u8 *csd_data); //读SD卡CSD u32 SD_GetCapacity(void); //取SD卡容量 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 . z: y p% r# w" ju8 SD_WriteMultiBlock(u32 sector,const u8 *data, u8 count);//写多个sector 3 k" w a, O% w, u; G) G" x3 h 3、 文件系统的移植 FatFs文件系统主要有以下函数: /*-函数详细的解释请查阅附录,点击链接也可以到----*/ /* FatFs module application interface */ 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 */ 2 J! n" s/ X) _, j! q+ J- ^9 h( I8 K( i" u/ z$ y' f 4、 底层接口I/O函数的编写 $ e* z# u: T0 V底层驱动是必须由用户自己完成的,函数均放在文件diskio.c中,函数如下: 3 s* E" I$ a9 w/* Prototypes for disk control functions */ 4 s; F2 T9 @" z( \. D# U+ h! E4 WDSTATUS disk_initialize (BYTE); * c6 {- ~$ | j+ ~. G3 f9 y8 aDSTATUS disk_status (BYTE); 7 w' l% H1 Z' A! i0 b1 h c% K. ~" ?DRESULT disk_read (BYTE, BYTE*, DWORD, BYTE); , S1 k2 \1 ^* k! H# M5 J0 HDRESULT disk_write (BYTE, const BYTE*, DWORD, BYTE); DRESULT 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个功能如下: - G4 L" S- r2 | 每一个功能都对应一个函数,各个函数如下: // 0 读取磁盘容量 The f_getfree function gets number of the free clusters void Test_f_getfree(void); 4 _2 j6 z6 s9 r//1 读磁盘目录 The f_readdir function reads directory entries. 8 a; b8 P1 {4 D1 ?& X ^void Test_f_readdir(void); //2 创建目录 The f_mkdir function creates a new directory void Test_f_mkdir(void); //3 读取文件 The f_read function reads data from a file. void Test_f_read(void); 9 p4 h" F. w1 x- g' \//4 文件拷贝 The f_write writes data to a file. void Test_f_write(void); ) { R) X- L2 j& S1 v, u* F; p; |//5 重命名 Rename file or directory void Test_f_rename(void); //6 删除文件 The f_unlink removes file or directory void Test_f_unlink(void); //7 格式化 The f_mkfs fucntion creates a file system on the drive. P7 @: J. M) t6 r9 \3 D3 t- Bvoid Test_f_mkfs(void); % P+ g; Z- r2 x7 D h3 I3 p 这8个函数的调用是通过一个二维的函数指针数组完成的,该数组如下: ! l3 B6 m b, jvoid *FATFS_Function[][2]= { 9 A" S K, C+ U0 l6 Y (void*)Test_f_getfree, "磁盘容量", (void*)Test_f_readdir, "读目录 ", % s: c7 s" V; _8 a (void*)Test_f_mkdir, "创建路径", (void*)Test_f_read, "读文件 ", (void*)Test_f_write, "文件拷贝", ) I! f# T/ r# w" D (void*)Test_f_rename, "重命名 ", (void*)Test_f_unlink, "删除文件", (void*)Test_f_mkfs, "格式化 ", & h; |) S* x5 F6 D) X8 O 0x00,0x00 }; . V! d' ~: Y$ l- z这8个函数完成的功能都是调用FatFs文件系统里的函数,FatFs文件系统的函数前面已经列出了列表,在本节最后面我们也附上了函数的详细定义及用法,请查阅。下面我们着重分析一下这几个应用函数: + i& x; O1 ~5 X7 G) @0)//The f_getfree function gets number of the free clusters 9 c' n( c6 k4 I* tvoid Test_f_getfree(void) { * a' A3 L0 ?( Z. H$ F& b D- V FATFS fs; % z9 W4 a- a# }: e4 m2 E& [ FATFS *pfs; 8 x, t8 ^0 U1 H Y5 b$ @$ K DWORD clust; ) `& N8 _ z4 K7 S/ [ FRESULT res; // FatFs function common result code . ^6 e) Y f& ?0 V+ X! e0 b& ?/ {5 i, T$ _8 g' \0 L$ C/ y //检测磁盘是否插好 ! M; H# i$ K7 R$ l. J" ^, @3 |) I- O( w if( disk_detect_OK()==FALSE ) 2 y3 J2 Q! ]) Z- ^ |$ \ return; T- j2 y$ K6 `6 `, t7 x5 ^7 Q( Q pfs=&fs;//指向 // Register a work area for logical drive 0 . T. v W. k# a//温馨提示:该函数是文件系统FatFs里的函数,详细说明请查阅本章节最后的附录,可以用Ctrl+F快捷键,弹出搜索框,输入该函数名,找到该函数。以下类似函数均可以这样找到详细说明,不再赘述。该类函数我们会标记红色,并加粗,请留意。比如f_mount, 解释如下: ! U$ d) M" j, _f_mount(0, &fs); // 获取空闲簇Get free clusters ,空闲簇的值存在clust res = f_getfree("/", &clust, &pfs);//必须是根目录,默认磁盘0 if ( res==FR_OK ) / |( K% H k* ?/ @5 H$ ^ { ) g& l k8 }$ {: s // Get free space 这里是打印显示出 SD卡总的空间大小以及剩余空间大小 //计算方法: // 总空间大小 最大簇数×每簇的扇区数÷2÷1024 5 Y/ e$ z. ], i* y* K// 剩余空间大小 空闲簇数×每簇的扇区数÷2÷1024 printp("\r\n%d MB total disk space.\r\n%d MB available on the disk.\r\n", - ?9 @- a7 U) `+ A0 ?3 Q (DWORD)(pfs->max_clust - 2) * pfs->csize /2/1024, 2 ^% ?6 q) ~- D5 J4 X clust * pfs->csize /2/1024); } 5 [! w) J( E4 | E else * Z" m) S0 w6 V3 \8 h: { die(res);//测试函数执行结果分析 ; p _0 w3 S/ Y2 c // Unregister a work area before discard it f_mount(0, NULL); % \, d7 s2 M+ s. t4 ]* x+ S& A} //1 读取目录 The f_readdir function reads directory entries. void Test_f_readdir(void) 9 Q* J; y* D* h# K{ - ^4 O& P7 ?" ^/ Z0 ^3 P5 c2 E* A FATFS fs; // Work area (file system object) for logical drive char path[20]; //检测磁盘是否插好 4 A0 ^+ J, c2 O: h if( disk_detect_OK()==FALSE ) return; f_mount(0, &fs); // Register a work area for logical drive 0 9 n" P& Q9 ]& b) @ printp("\r\nread directory:>/");//默认输出主目录下的文件 9 r9 k- _0 D4 f8 ]; R% F USART_Scanf_Name(path);//通过串口输入路径名 5 V9 ~! ?0 F7 y1 D# B4 L$ Q8 @# ]! @! k; v+ E' Y+ g //扫描当前路径下子文件夹,并输出到串口 scan_current_folder(path); $ n; `, u% y5 ~: K/ K+ E //扫描当前路径下文件,并输出到串口 scan_current_files(path); , m$ Q3 p6 O0 [; A f_mount(0, NULL);// Unregister a work area before discard it 2 H- {$ r" s" }/ u: s' i3 u; Z} * U+ a7 n$ T0 @ - t; T# d. V. { e# i //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 } $ W- ^: H% Q8 U7 ]! I! f * {8 j- I* G# |+ \5 z: ?6 H$ S4 _) R //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 ); $ a2 v7 }+ U# C //time_start=Time_GetCalendarTime();//记录开始读时间 //unix_start=Time_ConvCalendarToUnix(time_start); ! Q" _5 n, ]; w1 S" w E5 g8 ] J //输出提示.. 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页笔记+源码