stm32 Fatfs 读写SD卡

 读写SD是嵌入式系统中一个比较基础的功能,在很多应用中都可以用得上SD卡。折腾了几天,总算移植成功了 最新版Fatfs(Fatfs R0.09) ,成功读写SD卡下文件。
 
     QQ截图20120825031714.png
 
    FatFs ( http://elm-chan.org/fsw/ff/00index_e.html)是一个通用的文件系统模块,用于在小型嵌入式系统中实现FAT文件系统。 FatFs 的编写遵循ANSI C,因此不依赖于硬件平台。它可以嵌入到便宜的微控制器中,如 8051, PIC, AVR, SH, Z80, H8, ARM 等等,不需要做任何修改。
 
1. SD卡/TF卡 硬件接口
 
SD卡有两种操作接口,SDIO和SPI。 使用SDIO口的速度比较快,SPI的速度比较慢 。
 
    SD卡引脚描述如下:                                                            SD卡SPI接法如下:
sd引脚定义.png  stm32_sd.png
 我使用的是正点原子的开发板,所以采用的是SPI接口的模式。
 
    TF卡SDIO 模式和SPI模式 引脚定义:
 
spi-tf.png
 
        可以发现Micro SD卡只有8个引脚是因为比SD卡少了一个Vss。使用TF转SD的卡套套在Micro SD卡上,这样一来大小就和SD卡一样大,这时候卡套上的9个引脚就和SD卡一样了,你可以完全当做SD卡来操作。
 
2. SD卡底层驱动
 
SD卡的操作比较复杂,需要多看看一些文档 。 这里附上SD底层驱动代码,代码说明详见注释
 
Sd卡SPi操作底层代码 :  sdcard.c   sdcard.h
3. Fatfs 移植
 
FatFs 软件包中相关文件:
 
        ffconf.h     FatFs 模块配置文件
        ff.h            FatFs 和应用模块公用的包含文件
        ff.c            FatFs 模块
        diskio.h     FatFs and disk I/O 模块公用的包含文件
        integer.h   数据类型定义
        option      可选的外部功能
        diskio.c     FatFs 与disk I/O 模块接口层文件(不属于 FatFs 需要由用户提供)
 
FatFs 配置,文件系统的配置项都在 ffconf.h 文件之中:
 
        (1) _FS_TINY :这个选项在R0.07 版本之中开始出现,在之前的版本都是以独立的文件出现,现在通过一个宏来修改使用起来更方便;
        (2) _FS_MINIMIZE、_FS_READONLY、_USE_STRFUNC、_USE_MKFS、_USE_FORWARD 这些宏是用来对文件系统进行裁剪
        (3) _CODE_PAGE :本选项用于设置语言码的类型
        (4) _USE_LFN :取值为0~3,主要用于长文件名的支持及缓冲区的动态分配:
                0:不支持长文件名;
                1:支持长文件名存储的静态分配,一般是存储在BSS 段;
                2:支持长文件名存储的动态分配,存储在栈上;
                3:支持长文件名存储的动态分配,存储在堆上。
        (5) _MAX_LFN :可存储长文件的最大长度,其值一般为(12~255),但是缓冲区一般占(_MAX_LFN + 1) * 2 bytes;
        (6) _LFN_UNICODE :为1 时才支持unicode 码;
        (7) _FS_RPATH :R0.08a 版本改动配置项,取值范围0~2:
                0:去除相对路径支持和函数;
                1:开启相对路径并且开启f_chdrive()和f_chdir()两个函数;
                2:在1 的基础上添加f_getcwd()函数。
        (8) _VOLUMES :支持的逻辑设备数目;
        (9) _MAX_SS :扇区缓冲的最大值,其值一般为512;
        (10) _MULTI_PARTITION:定义为1 时,支持磁盘多个分区;
        (11) _USE_ERASE :R0.08a 新加入的配置项,设置为1 时,支持扇区擦除;
        (12) _WORD_ACCESS :如果定义为1,则可以使用word 访问;
        (13) _FS_REENTRANT :定义为1 时,文件系统支持重入,但是需要加上跟操作系统信号量相关的几个函数,函数在syscall.c 文件中;
        (14) _FS_SHARE :文件支持的共享数目。
 
Fatfs 开源文件系统 从R0.07e 之后 版本开始就不再提供底层接口文件 diskio.c 模板,这里附上根据
以上SD卡底层驱动对应的 diskio.c 源码:
 
001#include "common.h"
002/*-----------------------------------------------------------------------*/
003/* Inidialize a Drive                                                    */
004 
005DSTATUS disk_initialize (
006    BYTE drv                /* Physical drive nmuber (0..) */
007)
008{
009    u8 state;
010 
011    if(drv)
012    {
013        return STA_NOINIT;  //仅支持磁盘0的操作
014    }
015 
016    state = SD_Init();
017    if(state == STA_NODISK)
018    {
019        return STA_NODISK;
020    }
021    else if(state != 0)
022    {
023        return STA_NOINIT;  //其他错误:初始化失败
024    }
025    else
026    {
027        return 0;           //初始化成功
028    }
029}
030 
031 
032 
033/*-----------------------------------------------------------------------*/
034/* Return Disk Status                                                    */
035 
036DSTATUS disk_status (
037    BYTE drv        /* Physical drive nmuber (0..) */
038)
039{
040    if(drv)
041    {
042        return STA_NOINIT;  //仅支持磁盘0操作
043    }
044 
045    //检查SD卡是否插入
046    if(!SD_DET())
047    {
048        return STA_NODISK;
049    }
050    return 0;
051}
052 
053 
054 
055/*-----------------------------------------------------------------------*/
056/* Read Sector(s)                                                        */
057 
058DRESULT disk_read (
059    BYTE drv,       /* Physical drive nmuber (0..) */
060    BYTE *buff,     /* Data buffer to store read data */
061    DWORD sector,   /* Sector address (LBA) */
062    BYTE count      /* Number of sectors to read (1..255) */
063)
064{
065    u8 res=0;
066    if (drv || !count)
067    {   
068        return RES_PARERR;  //仅支持单磁盘操作,count不能等于0,否则返回参数错误
069    }
070    if(!SD_DET())
071    {
072        return RES_NOTRDY;  //没有检测到SD卡,报NOT READY错误
073    }
074 
075     
076     
077    if(count==1)            //1个sector的读操作     
078    {                                               
079        res = SD_ReadSingleBlock(sector, buff);     
080    }                                               
081    else                    //多个sector的读操作    
082    {                                               
083        res = SD_ReadMultiBlock(sector, buff, count);
084    }                                               
085    /*
086    do                          
087    {                                         
088        if(SD_ReadSingleBlock(sector, buff)!=0)
089        {                                     
090            res = 1;                          
091            break;                            
092        }                                     
093        buff+=512;                            
094    }while(--count);                                        
095    */
096    //处理返回值,将SPI_SD_driver.c的返回值转成ff.c的返回值
097    if(res == 0x00)
098    {
099        return RES_OK;
100    }
101    else
102    {
103        return RES_ERROR;
104    }
105}
106 
107 
108 
109/*-----------------------------------------------------------------------*/
110/* Write Sector(s)                                                       */
111 
112#if _READONLY == 0
113DRESULT disk_write (
114    BYTE drv,           /* Physical drive nmuber (0..) */
115    const BYTE *buff,   /* Data to be written */
116    DWORD sector,       /* Sector address (LBA) */
117    BYTE count          /* Number of sectors to write (1..255) */
118)
119{
120    u8 res;
121 
122    if (drv || !count)
123    {   
124        return RES_PARERR;  //仅支持单磁盘操作,count不能等于0,否则返回参数错误
125    }
126    if(!SD_DET())
127    {
128        return RES_NOTRDY;  //没有检测到SD卡,报NOT READY错误
129    }
130 
131    // 读写操作
132    if(count == 1)
133    {
134        res = SD_WriteSingleBlock(sector, buff);
135    }
136    else
137    {
138        res = SD_WriteMultiBlock(sector, buff, count);
139    }
140    // 返回值转换
141    if(res == 0)
142    {
143        return RES_OK;
144    }
145    else
146    {
147        return RES_ERROR;
148    }
149}
150#endif /* _READONLY */
151 
152 
153 
154/*-----------------------------------------------------------------------*/
155/* Miscellaneous Functions                                               */
156 
157DRESULT disk_ioctl (
158    BYTE drv,       /* Physical drive nmuber (0..) */
159    BYTE ctrl,      /* Control code */
160    void *buff      /* Buffer to send/receive control data */
161)
162{
163    DRESULT res;
164 
165 
166    if (drv)
167    {   
168        return RES_PARERR;  //仅支持单磁盘操作,否则返回参数错误
169    }
170     
171    //FATFS目前版本仅需处理CTRL_SYNC,GET_SECTOR_COUNT,GET_BLOCK_SIZ三个命令
172    switch(ctrl)
173    {
174    case CTRL_SYNC:
175        SD_CS_ENABLE();
176        if(SD_WaitReady()==0)
177        {
178            res = RES_OK;
179        }
180        else
181        {
182            res = RES_ERROR;
183        }
184        SD_CS_DISABLE();
185        break;
186         
187    case GET_BLOCK_SIZE:
188        *(WORD*)buff = 512;
189        res = RES_OK;
190        break;
191 
192    case GET_SECTOR_COUNT:
193        *(DWORD*)buff = SD_GetCapacity();
194        res = RES_OK;
195        break;
196    default:
197        res = RES_PARERR;
198        break;
199    }
200 
201    return res;
202}
203 
204/*-----------------------------------------------------------------------*/
205/* User defined function to give a current time to fatfs module          */
206/* 31-25: Year(0-127 org.1980), 24-21: Month(1-12), 20-16: Day(1-31) */                                                                                                                                                                                                                                         
207/* 15-11: Hour(0-23), 10-5: Minute(0-59), 4-0: Second(0-29 *2) */                                                                                                                                                                                                                                               
208DWORD get_fattime (void)
209{
210  return 0;
211}
这里的结构函数为Fatfs提供和SD卡的通信接口。 在 最新版本的Fatfs中还加入了对中文文件名的支持,需要修改  ffconf.h    
    #define _CODE_PAGE    936   //- Simplified Chinese GBK (DBCS, OEM, Windows) 
同时应该添加  option/cc936.c文件。但是这个文件有700多K占相当大的ROM, 像stm32F103RBT6这种小FLASH的MCU根本不行 ,加入当前工程文件中代码将增加160KB 左右。
 
配置好Stm32的串口和SPI等IO口设置后,就可以使用Fatfs做一些文件操作了。
 
4. Fatfs 文件操作
 
文件分配表FAT(File AllocationTable)用来记录文件所在位置的表格.它对于硬盘的使用是非常重要的,假若丢失文件分配表,那么硬盘上的数据就会因无法定位而不能使用了。 
 
Fatfs 文件系统减轻了操作SD卡的工作量,调用其提供的函数就可以方便的操作文件,读写删改等。
这里提供一个main.c 示例:
001#include "common.h"
002#include <string.h>
003 
004FRESULT scan_files (char* path);
005 
006#define  F_PUTS         1       //测试向文件写入字符串
007#define  F_READ         1       //测试从文件中读出数据
008#define  F_UNLINK       0       //测试删除文件
009#define  SCAN_FILES     1       //测试目录扫描
010 
011FATFS fs;
012FRESULT res;
013FIL file;                      
014UINT br;
015BYTE buffer[4096];              //以上变量作为全局变量 可以避免一些Bug
016 
017int main(void)
018{
019    u16 i,n;   
020     
021 
022    //stm32 初始化    
023    RCC_Configuration();
024    NVIC_Configuration();      
025    USART_Configuration();
026    SPI_Configuration();
027    GPIO_Configuration();
028 
029 
030    //fatfs 操作
031 
032    f_mount(0, &fs);
033 
034    //如果data.txt存在,则打开;否则,创建一个新文件
035    res = f_open(&file, "0:/data.txt",FA_OPEN_ALWAYS|FA_READ|FA_WRITE );
036 
037    if(res!=FR_OK)
038    {
039        printf("\r\n f_open() fail .. \r\n");
040    }else{
041        printf("\r\n f_open() success .. \r\n");
042    }
043 
044#if F_READ
045 
046    while(1){                                    //使用f_read读文件
047        res = f_read(&file, buffer, 1, &br);     //一次读一个字节知道读完全部文件信息
048 
049        if (res == FR_OK )
050        {
051            printf("%s",buffer);
052        }else{
053            printf("\r\n f_read() fail .. \r\n");  
054        }
055 
056        if(f_eof(&file)) {break;}
057    }
058 
059    /*if( f_gets(buffer,sizeof(buffer),&file) != NULL)   //使用f_gets读文件  ,存在 Bugs 待调试
060    {
061        printf("%s",buffer);
062    }else{
063        printf("\r\n f_gets() fail .. \r\n");  
064    } */
065 
066#endif
067 
068#if F_PUTS
069 
070    //将指针指向文件末
071    //res = f_lseek(&file,(&file)->fsize);
072    res = f_lseek(&file,file.fsize);   
073 
074    n = f_puts("\r\n hello dog ..\r\n", &file) ;  //向文件末写入字符串
075                           
076    if(n<1)  //判断写是否成功                       
077    {                                                     
078        printf("\r\n f_puts() fail .. \r\n");                                            
079    }else{
080        printf("\r\n f_puts() success .. \r\n");
081    }
082  
083#endif
084 
085#if F_UNLINK
086 
087    res = f_unlink("test.jpg");    //前提SD下存在一个test.jpg
088 
089    if(res!=FR_OK)
090    {
091        printf("\r\n f_unlink() fail .. \r\n");
092    }else{
093        printf("\r\n f_unlink() success .. \r\n");
094    }
095 
096#endif
097 
098#if SCAN_FILES
099 
100    printf("\r\n the directory files : \r\n");
101    scan_files("/");          //扫描根目录
102 
103#endif
104 
105    f_close(&file);
106    f_mount(0, NULL);
107 
108    while(1);
109}
110 
111 
112FRESULT scan_files (
113    char* path        /* Start node to be scanned (also used as work area) */
114)
115{
116    FRESULT res;
117    FILINFO fno;
118    DIR dir;
119    int i;
120    char *fn;   /* This function is assuming non-Unicode cfg. */
121#if _USE_LFN
122    static char lfn[_MAX_LFN + 1];
123    fno.lfname = lfn;
124    fno.lfsize = sizeof lfn;
125#endif
126 
127 
128    res = f_opendir(&dir, path);                       /* Open the directory */
129    if (res == FR_OK) {
130        i = strlen(path);
131        for (;;) {
132            res = f_readdir(&dir, &fno);                   /* Read a directory item */
133            if (res != FR_OK || fno.fname[0] == 0) break/* Break on error or end of dir */
134            if (fno.fname[0] == '.') continue;             /* Ignore dot entry */
135#if _USE_LFN
136            fn = *fno.lfname ? fno.lfname : fno.fname;
137#else
138            fn = fno.fname;
139#endif
140            if (fno.fattrib & AM_DIR) {                    /* It is a directory */
141                sprintf(&path[i], "/%s", fn);
142                res = scan_files(path);
143                if (res != FR_OK) break;
144                path[i] = 0;
145            } else {                                       /* It is a file. */
146                printf("\r\n %s/%s \r\n", path, fn);
147            }
148        }
149    }
150 
151    return res;
152}
其中 目录扫描函数 scan_files( char * path) 参数格式如下:
 
QQ截图20120824202000.png
 
这里使用到了f_puts()函数,所以必须在ffconf.h 中修改 #define _USE_STRFUNC  1
 
  • 1
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值