打印
[应用相关]

STM32移植FATFS

[复制链接]
4873|23
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
STM32移植FATFS及详细过程!
沙发
Diyer2015|  楼主 | 2018-9-20 12:34 | 只看该作者
    经常在网上、群里看到很多人问关于STM32的FATFS文件系统移植的问题,刚好自己最近也在调试这个程序,为了让大家少走弯路,我把我的调试过程和方法也贡献给大家。

使用特权

评论回复
板凳
Diyer2015|  楼主 | 2018-9-20 12:35 | 只看该作者
FatFs Module是一种完全免费开源的FAT文件系统模块,专门为小型的嵌入式系统而设计。它完全用标准C语言编写,所以具有良好的硬件平**立性,可以移植到8051、PIC、AVR、SH、Z80、H8、ARM等系列单片机上而只需做简单的修改。它支持FATl2、FATl6和FAT32,支持多个存储媒介;有独立的缓冲区,可以对多个文件进行读/写,并特别对8位单片机和16位单片机做了优化。

使用特权

评论回复
地板
Diyer2015|  楼主 | 2018-9-20 12:35 | 只看该作者
1、FATFS源代码的获取,可以到官网下载:http://elm-chan.org/fsw/ff/00index_e.html 最新版本是R0.09版本,我们就移植这个版本的。
    2、解压文件会得到两个文件夹,一个是doc文件夹,这里是FATFS的一些使用文档和说明,以后在文件编程的时候可以查看该文档。另一个是src文件夹,里面就是我们所要的源文件。
    3、建立一个STM32的工程,为方便调试,我们应重载printf()底层函数实现串口打印输出。可以参考已经建立好的printf()打印输出工程:http://www.viewtool.com/bbs/foru ... d=77&extra=page%3D1

使用特权

评论回复
5
Diyer2015|  楼主 | 2018-9-20 12:36 | 只看该作者
1、在已经建立好的工程目录User文件夹下新建两个文件夹,FATFS_V0.09和SPI_SD_Card,FATFS_V0.09用于存放FATFS源文件,SPI_SD_Card用于存放SPI的驱动文件。
如图1将ff.c添加到工程文件夹中,并新建diskio.c文件,在diskio.c文件中实现五个函数:

DSTATUS disk_initialize (BYTE);//SD卡的初始化
        DSTATUS disk_status (BYTE);//获取SD卡的状态,这里可以不用管
        DRESULT disk_read (BYTE, BYTE*, DWORD, BYTE);//从SD卡读取数据
        DRESULT disk_write (BYTE, const BYTE*, DWORD, BYTE);//将数据写入SD卡,若该文件系统为只读文件系统则不用实现该函数

使用特权

评论回复
6
Diyer2015|  楼主 | 2018-9-20 12:38 | 只看该作者
DRESULT disk_ioctl (BYTE, BYTE, void*);//获取SD卡文件系统相关信息


使用特权

评论回复
7
Diyer2015|  楼主 | 2018-9-20 12:38 | 只看该作者
3、初步实现以上五个函数
        FATFS初始化函数:

DSTATUS disk_initialize (
                BYTE drv                                /* Physical drive nmuber (0..) */
        )
        {
                switch (drv)
                {
                        case 0 :
                                return RES_OK;
                        case 1 :
                                return RES_OK;         
                        case 2 :
                                return RES_OK;         
                        case 3 :
                                return RES_OK;
                        default:
                                return STA_NOINIT;
                }
        }

使用特权

评论回复
8
Diyer2015|  楼主 | 2018-9-20 12:40 | 只看该作者
FATFS状态获取函数:

DSTATUS disk_status (
                BYTE drv                /* Physical drive nmuber (0..) */
        )
        {
                switch (drv)
                {
                        case 0 :
                                return RES_OK;
                        case 1 :
                                return RES_OK;
                        case 2 :
                                return RES_OK;
                        default:
                                return STA_NOINIT;
                }
        }

使用特权

评论回复
9
Diyer2015|  楼主 | 2018-9-20 12:41 | 只看该作者
FATFS底层写数据函数:

DRESULT disk_write (
                BYTE drv,                        /* Physical drive nmuber (0..) */
                const BYTE *buff,                /* Data to be written */
                DWORD sector,                /* Sector address (LBA) */
                BYTE count                        /* Number of sectors to write (1..255) */
        )
        {
                if( !count )
                {   
                        return RES_PARERR;  /* count不能等于0,否则返回参数错误 */
                }
                switch (drv)
                {
                        case 0:
                            if(count==1)            /* 1个sector的写操作 */      
                            {   
                                        return RES_OK;
                            }                                                
                            else                    /* 多个sector的写操作 */   
                            {  
                                        return RES_OK;  
                            }                                                
                        case 1:
                            if(count==1)            /* 1个sector的写操作 */      
                            {  
                                        return RES_OK;
                            }                                                
                            else                    /* 多个sector的写操作 */   
                            {  
                                        return RES_OK;
                            }                                                
         
                        default:return RES_ERROR;
                }
        }

使用特权

评论回复
10
Diyer2015|  楼主 | 2018-9-20 12:42 | 只看该作者
FATFS磁盘控制函数:

DRESULT disk_ioctl (
                BYTE drv,                /* Physical drive nmuber (0..) */
                BYTE ctrl,                /* Control code */
                void *buff                /* Buffer to send/receive control data */
        )
        {
                if (drv==0)
                {   
                        switch (ctrl)
                        {
                                case CTRL_SYNC :
                                        return RES_OK;
                                case GET_SECTOR_COUNT :
                                return RES_OK;
                                case GET_BLOCK_SIZE :
                                return RES_OK;        
                                case CTRL_POWER :
                                        break;
                                case CTRL_LOCK :
                                        break;
                                case CTRL_EJECT :
                                        break;
                        /* MMC/SDC command */
                                case MMC_GET_TYPE :
                                        break;
                                case MMC_GET_CSD :
                                        break;
                                case MMC_GET_CID :
                                        break;
                                case MMC_GET_OCR :
                                        break;
                                case MMC_GET_SDSTAT :
                                        break;        
                        }
            }else if(drv==1){
                        switch (ctrl)
                        {
                                case CTRL_SYNC :
                                        return RES_OK;
                                case GET_SECTOR_COUNT :
                                return RES_OK;
                                case GET_SECTOR_SIZE :
                                        return RES_OK;
                                case GET_BLOCK_SIZE :
                                return RES_OK;        
                                case CTRL_POWER :
                                        break;
                                case CTRL_LOCK :
                                        break;
                                case CTRL_EJECT :
                                        break;
                        /* MMC/SDC command */
                                case MMC_GET_TYPE :
                                        break;
                                case MMC_GET_CSD :
                                        break;
                                case MMC_GET_CID :
                                        break;
                                case MMC_GET_OCR :
                                        break;
                                case MMC_GET_SDSTAT :
                                        break;        
                        }         
                }
                else{                                 
                        return RES_PARERR;  
                }
                return RES_PARERR;
        }

使用特权

评论回复
11
Diyer2015|  楼主 | 2018-9-20 12:44 | 只看该作者
4、实现disk_initialize()函数
        该函数在挂载文件系统的时候会被调用,主要是实现读写SD卡前对SD卡进行初始化,根据SD卡的传输协议,我们按照如下步骤初始化SD卡:
        a、判断SD卡是否插入,可以通过检查SD卡卡座的CD脚电平进行判断,一般插入卡后该引脚会变成低电平。
        b、稍微延时一段时间后发送至少74个时钟给SD卡。
        c、发送CMD0命令给SD卡,直到SD卡返回0x01为止,这里可以循环多次发送。
                程序如下:

  /* Start send CMD0 till return 0x01 means in IDLE state */
                for(retry=0; retry<0xFFF; retry++)
                {
                        r1 = MSD0_send_command(CMD0, 0, 0x95);
                        if(r1 == 0x01)
                        {
                                retry = 0;
                                break;
                        }
                }

使用特权

评论回复
12
Diyer2015|  楼主 | 2018-9-20 12:45 | 只看该作者
//读文件测试
        printf("read file test......\n\r");
        res = f_open(&fsrc, "0:/test.txt", FA_OPEN_EXISTING | FA_READ);
        if(res != FR_OK){
                printf("open file error : %d\n\r",res);
        }else{
                res = f_read(&fsrc, buffer, sizeof(textFileBuffer), &br);     /* Read a chunk of src file */
                if(res==FR_OK){
                        printf("read data num : %d\n\r",br);
                        printf("%s\n\r",buffer);
                }else{
                        printf("read file error : %d\n\r",res);
                }
                /*close file */
                f_close(&fsrc);
        }

使用特权

评论回复
13
Diyer2015|  楼主 | 2018-9-20 12:46 | 只看该作者
实现disk_initialize()函数
        该函数在挂载文件系统的时候会被调用,主要是实现读写SD卡前对SD卡进行初始化,根据SD卡的传输协议,我们按照如下步骤初始化SD卡:
        a、判断SD卡是否插入,可以通过检查SD卡卡座的CD脚电平进行判断,一般插入卡后该引脚会变成低电平。
        b、稍微延时一段时间后发送至少74个时钟给SD卡。
        c、发送CMD0命令给SD卡,直到SD卡返回0x01为止,这里可以循环多次发送。
                程序如下:

  /* Start send CMD0 till return 0x01 means in IDLE state */
                for(retry=0; retry<0xFFF; retry++)
                {
                        r1 = MSD0_send_command(CMD0, 0, 0x95);
                        if(r1 == 0x01)
                        {
                                retry = 0;
                                break;
                        }
                }

使用特权

评论回复
14
Diyer2015|  楼主 | 2018-9-20 12:46 | 只看该作者
发送CMD8获取卡的类型,不同类型的卡其初始化方式有所不同。
        e、根据卡的类型对卡进行初始化。具体初始化方式可以参考附件程序。
        注:在初始化SD卡之前应该初始化SPI接口和相关的管脚。
        实现后的程序如下:

DSTATUS disk_initialize (
                BYTE drv                                /* Physical drive nmuber (0..) */
        )
        {
                int Status;
                switch (drv)
                {
                        case 0 :
                                Status = MSD0_Init();
                                if(Status==0){
                                        return RES_OK;
                                }else{
                                        return STA_NOINIT;
                                }
                        case 1 :
                                return RES_OK;         
                        case 2 :
                                return RES_OK;         
                        case 3 :
                                return RES_OK;
                        default:
                                return STA_NOINIT;
                }
        }

使用特权

评论回复
15
Diyer2015|  楼主 | 2018-9-20 12:49 | 只看该作者
实现disk_write()函数
        该函数主要实现对SD卡进行写数据操作,和读数据操作一样也分单块写和多块写,建议实现多块写的方式,这样可以提高写数据速度。
        实现后的程序如下:

DRESULT disk_write (
                BYTE drv,                        /* Physical drive nmuber (0..) */
                const BYTE *buff,                /* Data to be written */
                DWORD sector,                /* Sector address (LBA) */
                BYTE count                        /* Number of sectors to write (1..255) */
        )
        {
                int Status;
                if( !count )
                {   
                        return RES_PARERR;  /* count不能等于0,否则返回参数错误 */
                }
                switch (drv)
                {
                        case 0:
                            if(count==1)            /* 1个sector的写操作 */      
                            {   
                                        Status = MSD0_WriteSingleBlock( sector , (uint8_t *)(&buff[0]) );
                                        if(Status == 0){
                                                return RES_OK;
                                        }else{
                                                return RES_ERROR;
                                        }
                            }                                                
                            else                    /* 多个sector的写操作 */   
                            {  
                                        Status = MSD0_WriteMultiBlock( sector , (uint8_t *)(&buff[0]) , count );
                                        if(Status == 0){
                                                return RES_OK;
                                        }else{
                                                return RES_ERROR;
                                        }   
                            }                                                
                        case 1:
                            if(count==1)            /* 1个sector的写操作 */      
                            {  
                                        return RES_OK;
                            }                                                
                            else                    /* 多个sector的写操作 */   
                            {  
                                        return RES_OK;
                            }                                                
         
                        default:return RES_ERROR;
                }
        }

使用特权

评论回复
16
Diyer2015|  楼主 | 2018-9-20 12:50 | 只看该作者
实现disk_ioctl()函数
        该函数在磁盘格式化、获取文件系统信息等操作时会被调用。
        实现后的程序如下:

DRESULT disk_ioctl (
                BYTE drv,                /* Physical drive nmuber (0..) */
                BYTE ctrl,                /* Control code */
                void *buff                /* Buffer to send/receive control data */
        )
        {
                if (drv==0)
                {   
                        MSD0_GetCardInfo(&SD0_CardInfo);
                        switch (ctrl)
                        {
                                case CTRL_SYNC :
                                        return RES_OK;
                                case GET_SECTOR_COUNT :
                                        *(DWORD*)buff = SD0_CardInfo.Capacity/SD0_CardInfo.BlockSize;
                                return RES_OK;
                                case GET_BLOCK_SIZE :
                                        *(WORD*)buff = SD0_CardInfo.BlockSize;
                                return RES_OK;        
                                case CTRL_POWER :
                                        break;
                                case CTRL_LOCK :
                                        break;
                                case CTRL_EJECT :
                                        break;
                        /* MMC/SDC command */
                                case MMC_GET_TYPE :
                                        break;
                                case MMC_GET_CSD :
                                        break;
                                case MMC_GET_CID :
                                        break;
                                case MMC_GET_OCR :
                                        break;
                                case MMC_GET_SDSTAT :
                                        break;        
                        }
            }else if(drv==1){
                        switch (ctrl)
                        {
                                case CTRL_SYNC :
                                        return RES_OK;
                                case GET_SECTOR_COUNT :
                                return RES_OK;
                                case GET_SECTOR_SIZE :
                                        return RES_OK;
                                case GET_BLOCK_SIZE :
                                return RES_OK;        
                                case CTRL_POWER :
                                        break;
                                case CTRL_LOCK :
                                        break;
                                case CTRL_EJECT :
                                        break;
                        /* MMC/SDC command */
                                case MMC_GET_TYPE :
                                        break;
                                case MMC_GET_CSD :
                                        break;
                                case MMC_GET_CID :
                                        break;
                                case MMC_GET_OCR :
                                        break;
                                case MMC_GET_SDSTAT :
                                        break;        
                        }         
                }
                else{                                 
                        return RES_PARERR;  
                }
                return RES_PARERR;
        }

使用特权

评论回复
17
Diyer2015|  楼主 | 2018-9-20 12:50 | 只看该作者
//读文件测试
        printf("read file test......\n\r");
        res = f_open(&fsrc, "0:/test.txt", FA_OPEN_EXISTING | FA_READ);
        if(res != FR_OK){
                printf("open file error : %d\n\r",res);
        }else{
                res = f_read(&fsrc, buffer, sizeof(textFileBuffer), &br);     /* Read a chunk of src file */
                if(res==FR_OK){
                        printf("read data num : %d\n\r",br);
                        printf("%s\n\r",buffer);
                }else{
                        printf("read file error : %d\n\r",res);
                }
                /*close file */
                f_close(&fsrc);
        }

使用特权

评论回复
18
Diyer2015|  楼主 | 2018-9-20 12:50 | 只看该作者

使用特权

评论回复
19
Diyer2015|  楼主 | 2018-9-20 12:51 | 只看该作者
中文长文件名支持
    1、要支持长文件名需要在ffconf.h文件中修改两个宏定义。如下为我们修改后的宏定义。
    #define        _CODE_PAGE        936
    #define        _USE_LFN        1                /* 0 to 3 */
    2、添加支持中文编码的文件
    重新编译会发现有如图3的错误。原因是要支持中文文件名需要包含另外一个文件cc936.c,该文件在FATFS文件系统源码的.\src\option目录下,将它添加到工程文件目录FATFS中。如图4是我们添加文件后的工程文件结构,再次编译就通过了。如图5所示。我们发现增加这个文件后代码量增加了很多,主要原因是这个文件是我们支持中文所需要的中文编码文件。

使用特权

评论回复
20
Diyer2015|  楼主 | 2018-9-20 12:51 | 只看该作者


使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

63

主题

1615

帖子

13

粉丝