第十五节 SD文件系统的学习
详细内容请看附件
本节我们通过科星F107开发板的SPI接口对SD卡进行数据的读写。主要学习的内容就是SPI的配置与文件系统的移植。这里我们用到的文件系统是FatFs, FatFS 是一个通用的文件系统模块,用于在小型嵌入式系统中实现FAT文件系统。 FatFs 的编写遵循ANSI C,因此不依赖于硬件平台。它可以嵌入到便宜的微控制器中,如 8051, PIC, AVR, SH, Z80, H8, ARM 等等,不需要做任何修改。
最顶层是应用层,使用者无需理会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 提供的底层接口,它包括存储媒介读/写
接口Disk I/O 和供给文件创建修改时间的实时时钟。
程序的编写
下面我们开始代码的编写:
1、 主函数的编写
int main(void)
{
RCC_Configuration();
GPIO_Configuration();
USART_Configuration();
SPI_Configuration();
FATFS_TEST(); //FATFS常用功能测试函数
while (1)
{
}
}
这里新接触的配置函数就是SPI的配置了,也是使用库提供的结构体对SPI进行定义,更多的SPI的介绍,我们这里就不多说了,MCU的芯片手册写的也很清楚了,这里复制过来也没啥意思。下面我们看一下SPI的配置函数,代码如下:
void SPI_Configuration(void)
{
SPI_InitTypeDef SPI_InitStructure;
/* SPI3 Config */
//一开始SD初始化阶段,SPI<i>时钟频率必须<span lang="EN-US" style="color:#4F81BD;mso-themecolor:accent1">SD卡驱动函数的编写
这里使用文件SD_driver.c和SD_driver.h来写出SD读写的一些驱动函数。
函数列表如下:
/* Private define ------------------------------------------------------------*/
/* SD卡类型定义 */
#define SD_TYPE_MMC 0
#define SD_TYPE_V1 1
#define SD_TYPE_V2 2
#define SD_TYPE_V2HC 4
/* SPI总线速度设置*/
#define SPI_SPEED_LOW 0
#define SPI_SPEED_MID 1
#define SPI_SPEED_HIGH 2
/* SD传输数据结束后是否释放总线宏定义 */
#define NO_RELEASE 0
#define RELEASE 1
/* SD卡指令表 */
#define CMD0 0 //卡复位
#define CMD9 9 //命令9 ,读CSD数据
#define CMD10 10 //命令10,读CID数据
#define CMD12 12 //命令12,停止数据传输
#define CMD16 16 //命令16,设置SectorSize 应返回0x00
#define CMD17 17 //命令17,读sector
#define CMD18 18 //命令18,读Multi sector
#define ACMD23 23 //命令23,设置多sector写入前预先擦除N个block
#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卡断电
#define SD_DET() 1 // !GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2) //检测有卡
//1-有 0-无
/* Private function prototypes -----------------------------------------------*/
//void SPI_Configuration(void);
void SPI_SetSpeed(u8 SpeedSet);
u8 SPI_ReadWriteByte(u8 TxData); //SPI总线读写一个字节
u8 SD_WaitReady(void); //等待SD卡就绪
u8 SD_SendCommand(u8 cmd, u32 arg, u8 crc); //SD卡发送一个命令
u8 SD_SendCommand_NoDeassert(u8 cmd, u32 arg, u8 crc);
u8 SD_Init(void); //SD卡初始化
//
u8 SD_ReceiveData(u8 *data, u16 len, u8 release);//SD卡读数据
u8 SD_GetCID(u8 *cid_data); //读SD卡CID
u8 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
u8 SD_WriteMultiBlock(u32 sector,const u8 *data, u8 count);//写多个sector
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 */
4、 底层接口I/O函数的编写
底层驱动是必须由用户自己完成的,函数均放在文件diskio.c中,函数如下:
/* Prototypes for disk control functions */
DSTATUS disk_initialize (BYTE);
DSTATUS disk_status (BYTE);
DRESULT disk_read (BYTE, BYTE*, DWORD, BYTE);
DRESULT disk_write (BYTE, const BYTE*, DWORD, BYTE);
DRESULT disk_ioctl (BYTE, BYTE, void*);
DWORD get_fattime (void);
因为FatFs 模块完全与磁盘I/O 层分开,因此需要下面的函数来实现底层物理磁盘的读写与获取当前时间。底层磁盘I/O 模块并不是FatFs 的一部分,并且必须由用户提供。下面的函数位于diskio.c 中。
5、 应用层函数的编写
我们这个例程要实现的8个功能如下:
每一个功能都对应一个函数,各个函数如下:
// 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);
//3 读取文件 The f_read function reads data from a file.
void Test_f_read(void);
//4 文件拷贝 The f_write writes data to a file.
void Test_f_write(void);
//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.
void Test_f_mkfs(void);
这8个函数的调用是通过一个二维的函数指针数组完成的,该数组如下:
void *FATFS_Function[][2]=
{
(void*)Test_f_getfree, "磁盘容量",
(void*)Test_f_readdir, "读目录 ",
(void*)Test_f_mkdir, "创建路径",
(void*)Test_f_read, "读文件 ",
(void*)Test_f_write, "文件拷贝",
(void*)Test_f_rename, "重命名 ",
(void*)Test_f_unlink, "删除文件",
(void*)Test_f_mkfs, "格式化 ",
0x00,0x00
};
这8个函数完成的功能都是调用FatFs文件系统里的函数,FatFs文件系统的函数前面已经列出了列表,在本节最后面我们也附上了函数的详细定义及用法,请查阅。下面我们着重分析一下这几个应用函数:
0)//The f_getfree function gets number of the free clusters
void Test_f_getfree(void)
{
FATFS fs;
FATFS *pfs;
DWORD clust;
FRESULT res; // FatFs function common result code
//检测磁盘是否插好
if( disk_detect_OK()==FALSE )
return;
pfs=&fs;//指向
// Register a work area for logical drive 0
//温馨提示:该函数是文件系统FatFs里的函数,详细说明请查阅本章节最后的附录,可以用Ctrl+F快捷键,弹出搜索框,输入该函数名,找到该函数。以下类似函数均可以这样找到详细说明,不再赘述。该类函数我们会标记红色,并加粗,请留意。比如f_mount, 解释如下:
f_mount(0, &fs);
// 获取空闲簇Get free clusters ,空闲簇的值存在clust
res = f_getfree("/", &clust, &pfs);//必须是根目录,默认磁盘0
if ( res==FR_OK )
{
// Get free space 这里是打印显示出 SD卡总的空间大小以及剩余空间大小
//计算方法:
// 总空间大小 最大簇数×每簇的扇区数÷2÷1024
// 剩余空间大小 空闲簇数×每簇的扇区数÷2÷1024
printp("\r\n%d MB total disk space.\r\n%d MB available on the disk.\r\n",
(DWORD)(pfs->max_clust - 2) * pfs->csize /2/1024,
clust * pfs->csize /2/1024);
}
else
die(res);//测试函数执行结果分析
// Unregister a work area before discard it
f_mount(0, NULL);
}
//1 读取目录 The f_readdir function reads directory entries.
void Test_f_readdir(void)
{
FATFS fs; // Work area (file system object) for logical drive
char path[20];
//检测磁盘是否插好
if( disk_detect_OK()==FALSE )
return;
f_mount(0, &fs); // Register a work area for logical drive 0
printp("\r\nread directory:>/");//默认输出主目录下的文件
USART_Scanf_Name(path);//通过串口输入路径名
//扫描当前路径下子文件夹,并输出到串口
scan_current_folder(path);
//扫描当前路径下文件,并输出到串口
scan_current_files(path);
f_mount(0, NULL);// Unregister a work area before discard it
}
//2 读取目录 The f_mkdir function creates a new directory
FATFS fs; // Work area (file system object) for logical drive
FRESULT res; // FatFs function common result code
if( disk_detect_OK()==FALSE )
f_mount(0, &fs); // Register a work area for logical drive 0
printp("\r\nmake directory:>");
USART_Scanf_Name(path);//通过串口输入路径名
f_mount(0, NULL);// Unregister a work area before discard it
//3 读文件 The f_read function reads data from a file.
struct tm time_start,time_end ;
//用来记录读文件起始时间、结束时间 ,unix时间格式,用来记录时间差。
time_t unix_start,unix_end,interval;
if( disk_detect_OK()==FALSE )
//printp("\r\nread filebbbb:>");
// Register a work area for logical drive 0
printp("\r\nread file:>");
USART_Scanf_Name(path);//通过串口输入文件路径名
res = f_open(&fsrc, path, FA_OPEN_EXISTING | FA_READ);
//设为buffer[512]时,只需要循坏一次,如果设为buffer[1],需要循坏512次。
//while( Read_File_Flag==0 );
//time_start=Time_GetCalendarTime();//记录开始读时间
//unix_start=Time_ConvCalendarToUnix(time_start);
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);
<span lang="EN-US"> for(i=0;iRTC函数的编写
|