发新帖本帖赏金 60.00元(功能说明)我要提问
返回列表
打印
[APM32F4]

单片机中使用文件系统FATFS(SDIO接口SD卡)

[复制链接]
2146|13
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 单片小菜 于 2023-8-7 16:51 编辑

#申请原创# @21小跑堂
引:
        最小的文件系统,可以是控制U盘的时候,实现,也可以是控制SD卡的时候实现,甚至是一颗FLASH芯片也可以做最小的文件系统FATFS。这篇文章,说一下如何使用SDIO接口形式对SD卡进行操作​:
        主芯片:APM32F​4系列单片机。
        ​接口形式:SDIO
        开发环境​:KEIL
        ​FATFS版本:R0.11a

一、FATFS下载
        可以直接在官网进行下载。​这部分是开源的。​官网地址如下:
http://elm-chan.org/
​      
        进入官网之后,选择softwares

        在softwares中选择FatFs Module​,进入之后,拉到网页的最下端,看见版本号,进行下载,现在的版本号已经更新到R0.15:      

二、将FATFS添加到工程中
       将以下几个文件添加到项目中:
diskio.c
ff.c
cc936.c
bsp_sdio.sd.c


       前面的几个文件可以不修改,主要查看bsp_sdio_sd.c文件的管脚是否使用正确,​打开C文件,查看一下。
voidSD_LowLevel_DeInit(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;    /*!< Disable SDIO Clock */  
    SDIO_ClockCmd(DISABLE);    /*!< Set Power State to OFF */  
    SDIO_SetPowerState(SDIO_PowerState_OFF);​  /*!< DeInitializes the SDIO peripheral */  
    SDIO_DeInit();    /* Disable the SDIO APB2 Clock */  
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SDIO, DISABLE);​  
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource8, GPIO_AF_MCO);  
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource9, GPIO_AF_MCO);  
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_MCO);  
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_MCO);  
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_MCO);  
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource2, GPIO_AF_MCO);​  /* Configure PC.08, PC.09, PC.10, PC.11 pins: D0, D1, D2, D3 pins */  
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11;  
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;  
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;  
    GPIO_Init(GPIOC, &GPIO_InitStructure);​  /* Configure PD.02 CMD line */  
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;  
    GPIO_Init(GPIOD, &GPIO_InitStructure);​  /* Configure PC.12 pin: CLK pin */  
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;  
    GPIO_Init(GPIOC, &GPIO_InitStructure);
}

voidSD_LowLevel_Init(void)
{  
    GPIO_InitTypeDef  GPIO_InitStructure;​  /* GPIOC and GPIOD Periph clock enable */  
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD, ENABLE);​  
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource8, GPIO_AF_SDIO);  
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource9, GPIO_AF_SDIO);  
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_SDIO);  
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_SDIO);  
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_SDIO);  
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource2, GPIO_AF_SDIO);​  /* Configure PC.08, PC.09, PC.10, PC.11 pins: D0, D1, D2, D3 pins */  
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11;  
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;  
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;  
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;  
    GPIO_Init(GPIOC, &GPIO_InitStructure);​  /* Configure PD.02 CMD line */  
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;  
    GPIO_Init(GPIOD, &GPIO_InitStructure);​  /* Configure PC.12 pin: CLK pin */  
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;  
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;  
    GPIO_Init(GPIOC, &GPIO_InitStructure);​  /* Enable the SDIO APB2 Clock */  
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SDIO, ENABLE);​  /* Enable the DMA2 Clock */  
    RCC_AHB1PeriphClockCmd(SD_SDIO_DMA_CLK, ENABLE);
}

        以下是SD卡初始化函数:
SD_Error SD_Init(void)
{  
    __IO SD_Error errorstatus = SD_OK;    /**************配置SDIO中断 DMA中断**********************/  
    NVIC_InitTypeDef NVIC_InitStructure;    // Configure the NVIC Preemption Priority Bits   
    NVIC_PriorityGroupConfig (NVIC_PriorityGroup_1);  
    NVIC_InitStructure.NVIC_IRQChannel = SDIO_IRQn;  
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  
    NVIC_Init (&NVIC_InitStructure);  
    NVIC_InitStructure.NVIC_IRQChannel = SD_SDIO_DMA_IRQn;  
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  
    NVIC_Init (&NVIC_InitStructure);  /**********************************************************/    /* SDIO Peripheral Low Level Init */  
    SD_LowLevel_Init();​  
    SDIO_DeInit();​  
    errorstatus = SD_PowerON();​  
    if (errorstatus != SD_OK)  
    {    /*!< CMD Response TimeOut (wait for CMDSENT flag) */   
        return(errorstatus);  
    }​  
    errorstatus = SD_InitializeCards();​  
    if (errorstatus != SD_OK)  
    {    /*!< CMD Response TimeOut (wait for CMDSENT flag) */   
        return(errorstatus);  
    }​  /*!< Configure the SDIO peripheral */  /*!< SDIO_CK = SDIOCLK / (SDIO_TRANSFER_CLK_DIV + 2) */  /*!< on STM32F4xx devices, SDIOCLK is fixed to 48MHz */  
    SDIO_InitStructure.SDIO_ClockDiv = SDIO_TRANSFER_CLK_DIV;  
    SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising;  
    SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable;  
    SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable;  
    SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_1b;  
    SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable;  
    SDIO_Init(&SDIO_InitStructure);​  /*----------------- Read CSD/CID MSD registers ------------------*/  
    errorstatus = SD_GetCardInfo(&SDCardInfo);​  
    if (errorstatus == SD_OK)  
    {    /*----------------- Select Card --------------------------------*/   
        errorstatus = SD_SelectDeselect((uint32_t)
         (SDCardInfo.RCA << 16));  
    }​  
    if (errorstatus == SD_OK)  
    {   
        errorstatus = SD_EnableWideBusOperation(SDIO_BusWide_4b);  
    }  ​  
    return(errorstatus);
}

三、应用接口函数
        在刚刚下载的网站中可以看见应用的接口函数,这部分重点说明一下:
打开文件函数f_open函数,这个函数既可以作为打开文件的函数,也可以作为建立文件的函数,里面含有打开文件的模式“只读”,“读、写”,“创建、写”等等,根据自己的需要进行打开或者建立文件。如果查看SD卡中没有相应的文件,会直接建立该文件名的文件。
关闭文件,关闭文件和打开文件相对应的,可以形象的知道在win操作中,打开一个文件的话,如果不关闭文件是无法保存编辑的过程。所以在操作文件的时候,必然最后要进行关闭文件,不然无法进行文件的编辑操作。
读取文件,打开文件之后,进行读取,需要设定读取的指针还有读取数据到哪个缓冲区中,需要建立一个缓冲区,然后将读取的数据赋值到缓冲区中,方便后续的调用操作。
写入文件,写入文件的时候,需要使用到另外一个函数f_lseek函数,这个函数之前的时候没有用好,在后续的例子程序中有说明。
F_mount函数,这个函数的作用是建立工作区,在FAT系统中进行工作区的建立,后续的代码中有这方面的说明。
F_mkfs函数,这个函数的作用是检查SD卡的系统是否是FAT系统,如果不是fat系统,那么会在SD卡中建立一个FAT系统,在这里可以选用FAT32进行格式化操作。

        当然,也可以在ff.h中进行查看​接口函数的说明。

四、实际操作
       在c文件开始部分增加以下​引用,文件系统对象的定义,文件对象的定义,文件操作结果的定义,文件成功读写的数量,需要定义四个变量,我们在跟踪调试的时候,主要查看res_sd变量是否为ok是很重要的。
#include "ff.h"
FATFS fs;             /* FatFs文件系统对象 */
FIL fnew;             /* 文件对象 */
FRESULT res_sd;       /* 文件操作结果 */
UINT fnum;            /* 文件成功读写数量 */

        查看SD卡中是否存在文件系统,如果不存在,我们进行格式化​SD操作。这里就用到了上文提到的两个函数,建立空间和FAT系统函数。
//SD卡文件初始化,查看卡中有多少文件
voidSD_file_test(void)
{   
    char path[16]={""};   
    res_sd = f_mount(&fs,"0:",1);   
    if(res_sd == FR_NO_FILESYSTEM)   
    {        
        res_sd=f_mkfs("0:",0,0);        
        if(res_sd == FR_OK)        
        {            
            res_sd = f_mount(NULL,"0:",1);            
            res_sd = f_mount(&fs,"0:",1);        
        }   
    }   
    scan_files(path);   
    f_mount(NULL,"0:",1);
}

        建立一个0:00.csv的文件,当然这里面也可以建立一个txt文件,或者是建立dat文件,无论建立哪种形式的文件,将SD卡取出放到读卡器中,接入电脑,都可以用文本文件的形式进行打开,查看在文件中写入的内容,这个文件的格式也是采用8+3的格式进行建立的。标准的格式。
//建立0.00的CSV文件
voidSD_WFile_Init(void)
{   
    unsignedchar numberTmp;   
    TCHAR filenameTmp[13]="0:00.csv";   
    res_sd = f_mount(&fs,"0:",1);   
    if(res_sd == FR_NO_FILESYSTEM)   
    {        
        res_sd=f_mkfs("0:",0,0);        
        if(res_sd == FR_OK)        
        {            
            res_sd = f_mount(NULL,"0:",1);            
            res_sd = f_mount(&fs,"0:",1);        
        }   
    }   
    numberTmp = filenumber;   
    if((numberTmp==0)||(numberTmp==50))   
    {        
        filenameTmp[3] = '1';   
    }   
    else
    {        
        if(numberTmp>9)        
        {            
            filenameTmp[2] = (numberTmp +1)/10+ 0x30;            
            filenameTmp[3] = (numberTmp +1)%10+ 0x30;        
        }        
        else
        {            
            filenameTmp[3] = numberTmp + 1 + 0x30;        
        }   
    }   
    f_open(&fnew, filenameTmp,FA_CREATE_ALWAYS | FA_WRITE );
}

        将buffer中的数据写入到0:00.csv中,这部分使用的函数,就是先进行指针的判断,查看文件中包含多少字节,用到的函数就是前面提到的f_lseek函数,先把文件尾部的指针确定之后,再将buf中的数据写入到文件中,如果没有f_lseek函数,那么指针还是从0开始的,也就是从文件的开始进行写入,会覆盖之前写入的内容。这部分需要注意下。
//文件写入
voidSD_WFile_Write(void)
{   
     res_sd=f_lseek(&fnew,fnew.fsize);   
     res_sd=f_write(&fnew,buffer,sizeof(buffer),&fnum);
}

        写完文件之后,记得关闭文件,关闭文件的同时,将工作区也一并进行关闭才是可以的。关闭文件之后,将SD卡通过读卡器接入电脑,才可以查看文件中的内容,不然会发现是一个空文件。
//文件关闭
voidSD_WFile_Close(void)
{   
    f_close(&fnew);   
    f_mount(NULL,"0:",1);
}

结:
        以上就是单片机对文件系统的操作,只要是单片机带有SDIO接口的话,都可以直接应用这样的方式进行文件的建立和​操作。如果大家感兴趣的话,后续可以做一些SPI接口的SD卡的文件系统,同时,也可以在flash中进行文件系统的移植工作。

d257e7bccc8af1a6c8153eb8edd79235 (28.28 KB )

d257e7bccc8af1a6c8153eb8edd79235

736cd77f6974305b13842ed02eb19ccf (52.7 KB )

736cd77f6974305b13842ed02eb19ccf

238ad0625d3a03edcb00b4d1e2d27c89 (24.19 KB )

238ad0625d3a03edcb00b4d1e2d27c89

e4bd8783d11aca14da19b3f594f039e2 (25.1 KB )

e4bd8783d11aca14da19b3f594f039e2

7383acaf6147c6849e4c1515bcb90b95 (237.83 KB )

7383acaf6147c6849e4c1515bcb90b95

236ecf39914abbfd03c0946477192e8d (702.39 KB )

236ecf39914abbfd03c0946477192e8d

0917b0f57e4aff591c99fa8e288a4168 (6.77 KB )

0917b0f57e4aff591c99fa8e288a4168

使用特权

评论回复

打赏榜单

21小跑堂 打赏了 60.00 元 2023-08-09
理由:恭喜通过原创审核!期待您更多的原创作品~

评论
21小跑堂 2023-8-9 15:50 回复TA
实操单片机使用FATFS,便于单片机操作文件系统。 
沙发
chenjun89| | 2023-8-6 00:02 | 只看该作者
FATfs的官网什么时候变得这么花哨了,哈哈。

使用特权

评论回复
板凳
weifeng90| | 2023-8-6 16:08 | 只看该作者
FATFS要是支持多级目录就好了。

使用特权

评论回复
地板
guijial511| | 2023-8-7 09:56 | 只看该作者
fatfs还是嵌入式系统必选文件系统啊

使用特权

评论回复
5
tpgf| | 2023-9-4 10:02 | 只看该作者
现在主流的文件系统除了fatfs之外还有其他的文件系统吗

使用特权

评论回复
6
qcliu| | 2023-9-4 10:25 | 只看该作者
单片机运行文件系统的最低要求是什么呢

使用特权

评论回复
7
drer| | 2023-9-4 10:53 | 只看该作者
如果单片机只是能勉强运行系统的话 在这个过程中会出现卡顿的现象吗

使用特权

评论回复
8
coshi| | 2023-9-4 11:26 | 只看该作者
跑系统的话 如何评估线程对资源的占用呢

使用特权

评论回复
9
单片小菜|  楼主 | 2023-9-4 11:38 | 只看该作者
coshi 发表于 2023-9-4 11:26
跑系统的话 如何评估线程对资源的占用呢

可以写一个测试函数

使用特权

评论回复
10
kxsi| | 2023-9-4 11:49 | 只看该作者
文件系统除了这种接口形式之外还有其他形式的接口吗

使用特权

评论回复
11
wiba| | 2023-9-4 12:12 | 只看该作者
对文件系统的操作必须要使用指针吗

使用特权

评论回复
12
Fanexs168| | 2023-9-5 15:38 | 只看该作者
适配和移植FatFS,得好好看看FatFS自带的这些API接口描述、介绍,还有FS的框架。亲自上手适配下就有感觉了

使用特权

评论回复
发新帖 本帖赏金 60.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:资深专家
简介:丰富的嵌入式软硬件开发管理经验; 丰富的项目管理经验并具备敏锐的市场嗅觉; 丰富的产品的供应链资源及工厂管控能力; 具备很强的产品落地经验(从产品企划到产品量产);

107

主题

2308

帖子

9

粉丝