STM32例程之FATFS文件系统(SPI方式)移植笔记

发布者:温柔心绪最新更新时间:2016-10-17 来源: eefocus关键字:STM32例程  FATFS文件系统  SPI方式 手机看文章 扫描二维码
随时随地手机看文章
一、序言
    经常在网上、群里看到很多人问关于STM32的FATFS文件系统移植的问题,刚好自己最近也在调试这个程序,为了让大家少走弯路,我把我的调试过程和方法也贡献给大家。

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

三、移植准备
    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
四、开始移植
    1、在已经建立好的工程目录User文件夹下新建两个文件夹,FATFS_V0.09和SPI_SD_Card,FATFS_V0.09用于存放FATFS源文件,SPI_SD_Card用于存放SPI的驱动文件。
    2、如图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卡,若该文件系统为只读文件系统则不用实现该函数
        DRESULT disk_ioctl (BYTE, BYTE, void*);//获取SD卡文件系统相关信息


图1
    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;
                }
        }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;
                }
        }FATFS底层读数据函数: DRESULT disk_read (
                BYTE drv,                /* Physical drive nmuber (0..) */
                BYTE *buff,                /* Data buffer to store read data */
                DWORD sector,        /* Sector address (LBA) */
                BYTE count                /* Number of sectors to read (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;
                }
        }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;
                }
        }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; 
        }以上函数都只是实现一个框架,并没有做实际的事情,下一步就需要把操作SD卡的程序填充在这个框架里面。
    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;
                        }
                }d、发送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;
                }
        }MSD0_Init()函数在SPI_MSD0_Driver.c文件中实现。
    5、实现disk_read()函数
        该函数是读取SD卡扇区数据的函数,根据SD卡数据传输协议可知有读取单扇区和读取多扇区两种操作模式,为提高读文件的速度应该实现读取多扇区函数。
        实现后的程序如下: DRESULT disk_read (
                BYTE drv,                /* Physical drive nmuber (0..) */
                BYTE *buff,                /* Data buffer to store read data */
                DWORD sector,        /* Sector address (LBA) */
                BYTE count                /* Number of sectors to read (1..255) */
        )
        {
                int Status;
                if( !count )
                {    
                        return RES_PARERR;  /* count不能等于0,否则返回参数错误 */
                }
                switch (drv)
                {
                        case 0:
                            if(count==1)            /* 1个sector的读操作 */      
                            {   
                                        Status =  MSD0_ReadSingleBlock( sector ,buff );
                                        if(Status == 0){
                                                return RES_OK;
                                        }else{
                                                return RES_ERROR;
                                        }    
                            }                                                
                            else                    /* 多个sector的读操作 */     
                            {  
                                        Status = MSD0_ReadMultiBlock( sector , buff ,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;
                }
        }MSD0_ReadSingleBlock()和MSD0_ReadMultiBlock()函数都是SD卡操作的底层函数,我们在SPI_MSD0_Driver.c文件中实现。
    6、实现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) ); 
                                        if(Status == 0){
                                                return RES_OK;
                                        }else{
                                                return RES_ERROR;
                                        } 
                            }                                                
                            else                    /* 多个sector的写操作 */    
                            {  
                                        Status = MSD0_WriteMultiBlock( sector , (uint8_t *)(&buff) , 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;
                }
        }MSD0_WriteSingleBlock()和MSD0_WriteMultiBlock()函数都是SD卡操作的底层函数,我们在SPI_MSD0_Driver.c文件中实现。
    7、实现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; 
        }MSD0_GetCardInfo()函数也在SPI_MSD0_Driver.c文件中实现,其中SD0_CardInfo为PMSD_CARDINFO类型的全局变量,它在SPI_MSD0_Driver.h文件中被定义。
    8、到此diskio.c这个文件中的所有函数就已经实现,下一步就是实现SPI_MSD0_Driver.c文件中的相关函数,SPI_MSD0_Driver.c文件可以在网上下载,参考的程序比较多,本工程使用的这个文件也是在网上下载并进行一定的修改过的。本文件中函数的实现方式可以参考源代码。
五、文件系统测试
    1、测试写文件
    测试代码如下: //写文件测试
        printf("write file test......\n\r");
        res = f_open(&fdst, "0:/test.txt", FA_CREATE_ALWAYS | FA_WRITE);
        if(res != FR_OK){
                printf("open file error : %d\n\r",res);
        }else{
                res = f_write(&fdst, textFileBuffer, sizeof(textFileBuffer), &bw);               /* Write it to the dst file */
                if(res == FR_OK){
                        printf("write data ok! %d\n\r",bw);
                }else{
                        printf("write data error : %d\n\r",res);
                }
                /*close file */
                f_close(&fdst);
        }注意:成功打开文件后一定要调用f_close()函数,否则数据无法写入SD卡中。
    2、测试读文件 //读文件测试
        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);
        }3、测试结果
    测试结果如图2所示。

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

图3

图4

图5
    3、再次下载到板子中运行,发现中文的长文件名显示正常了。如图6所示。

    4、若不需要支持中文长文件名而只支持英文长文件名则可以将宏定义做如下修改:
    #define        _CODE_PAGE        437
    #define        _USE_LFN        1                /* 0 to 3 */
    同时将ccsbcs.c添加到工程目录中,这样就可以减小很多大代码量。将程序下载板子后再次运行结果如图7所示,可以看到可以支持英文的长文件名。
关键字:STM32例程  FATFS文件系统  SPI方式 引用地址:STM32例程之FATFS文件系统(SPI方式)移植笔记

上一篇:s3c2440 spi子系统分析
下一篇:STM32F407驱动MT9T001 CMOS图像传感器模块

推荐阅读最新更新时间:2024-03-16 15:15

关于stm32 的 USB 转串口 virtual_Com_Port的例程的一些问题
当从虚拟串口到真实串口发数据时,没有错误,但发送一个稍微大一点的文件时(如1M),误码率很高。不知是为什么? 其中: int main(void) { #ifdef DEBUG debug(); #endif Set_System(); Set_USBClock(); USB_Interrupts_Config(); USB_Init(); while (1) { if (count_out != 0) { USB_To_USART_Send_Data(&buffer_out , count_out);//这句是发数据的函数,原型如下。 count_out = 0;
[单片机]
STM32SPI采用DMA方式传输测试
环境: 主机:WIN7 开发环境:MDK4.23 MCU:STM32F103CBT6 说明: 参考链接: http://www.openedv.com/posts/list/3159.htm SPI传输数据分为连续传输和非连续传输. 连续传输时序图: 非连续传输时序图: 非连续传输模式可以参考上篇文章: http://blog.csdn.net/jdh99/article/details/7598573 官方pdf关于连续和非连续传输的说明: 当在主模式下发送数据时,如果软件足够快,能够在检测到每次TXE的上升沿(或TXE中断),并立即在正在进行的传输结束之前写入SPI_DR寄存器,则能够实现连续的通信;此时,
[单片机]
<font color='red'>STM32</font>的<font color='red'>SPI</font>采用DMA<font color='red'>方式</font>传输测试
STM32SPI从机例程
#include stm32f10x.h /* RCC时钟配置 */ void RCC_config(void) { ErrorStatus HSEStartUpStatus; /* RCC寄存器设置为默认配置 */ RCC_DeInit(); /* 打开外部高速时钟 */ RCC_HSEConfig(RCC_HSE_ON); /* 等待外部高速时钟稳定 */ HSEStartUpStatus = RCC_WaitForHSEStartUp(); if(HSEStartUpStatus == SUCCESS) { /* 设置HCLK = SYSCLK */ RCC_HCLKConfig(RCC_SYSCLK_D
[单片机]
STM32F103_SD卡FatFs文件系统移植
一、下载解压官方源码压缩包 1 添加下列文件至项目列表并包含头文件。 二、配置函数接口 主要有以下几个文件 : diskio.c ffconf.h 1 官方源码已经有写好的代码框架,只需要添加ATA(指SD卡)部分即可 (1) 获取设备状态部分,由于SD卡起始状态特别多,此处默认检测到SD卡设备。 case ATA : //默认设备检测成功 status &= ~STA_NOINIT; break; (2) 设备初始化部分,调用sdio.c中的初始化函数即可。 case ATA : if(SD_Init()==SD_OK) {
[单片机]
STM32F103_SD卡<font color='red'>FatFs</font><font color='red'>文件系统</font>移植
STM32:DMA方式接收SPI总线数据,并按照协议进行处理
一、前言 为满足高速数据传输的要求,采用SPI总线。MCU端(STM32F072 Cortex-M0)接收CPU发送的SPI数据(数据18个字节为一包,起始包为0xAA,最后一包为CheckSum校验),接收完成后,将校验正确的数据分配给RF发送给接收端。 二、硬件电路 如下图所示,SPI部分使用SPI2即PB12 PB13 PB14 PB15 三、程序流程 3.1 SPI初始化 SPI初始化为从模式,代码如下: pre name= code class= cpp void BSP_SPI2_Init(void) { SPI_InitTypeDef SPI_InitStructure; GPIO_In
[单片机]
STM32 SPI方式读写SD卡
前段时间在51上模拟SPI实现了对SD卡的读取,效果还算不错,最近将其移植到STM32上,不过使用硬件SPI和使用软件SPI还是有差别的。 代码如下: void User_SPIInit(void) { GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_SPI1,ENABLE); //使能时钟 GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5|GPIO_Pin_6|GPIO
[单片机]
STM32 24C02函数 I2C例程STM32方式
#define ADDR_24CXX 0xA0 void I2C_24CXX_Write(u16 nAddr, u8* pDat, u16 nLen) { u16 i = 0; /* Enable I2C1 acknowledgement if it is already disabled by other function */ //I2C_AcknowledgeConfig(I2C1, ENABLE); STM32_I2c1_Regs- cr1.bit.ACK=1; // ACK:应答使能 1:在接收到一个字节后返回一个应答(匹配的地址或数据) /* Send I2C1 START condition */ //
[单片机]
FATFS简介
一、概述 1、目的 在移植之前,先将源代码大概的阅读一遍,主要是了解文件系统的结构、各个函数的功能和接口、与移植 相关的代码等等。 2、准备工作 在官方网站下载了0.07c版本的源代码,利用记事本进行阅读。 二、源代码的结构 1、源代码组成 源代码压缩包解压后,共两个文件夹,doc是说明,src里就是代码。src文件夹里共五个文件和一个文 件夹。文件夹是option,还有00readme.txt、diskio.c、diskio.h、ff.c、ff.h、integer.h。对比网上 的文章,版本已经不同了,已经没有所谓的tff.c和tff.h了,估计现在都采用条件编译解决这个问题了, 当然文件更少,可能编译选项可能越复
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
热门活动
换一批
更多
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

更多每日新闻
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved