返回列表 发新帖我要提问本帖赏金: 60.00元(功能说明)

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

[复制链接]
3563|13
 楼主| 单片小菜 发表于 2023-8-3 16:44 | 显示全部楼层 |阅读模式
本帖最后由 单片小菜 于 2023-8-7 16:51 编辑

#申请原创# @21小跑堂
引:
        最小的文件系统,可以是控制U盘的时候,实现,也可以是控制SD卡的时候实现,甚至是一颗FLASH芯片也可以做最小的文件系统FATFS。这篇文章,说一下如何使用SDIO接口形式对SD卡进行操作​:
        主芯片:APM32F​4系列单片机。
        ​接口形式:SDIO
        开发环境​:KEIL
        ​FATFS版本:R0.11a
38ad388479638b060f95f6e988342d27
一、FATFS下载
        可以直接在官网进行下载。​这部分是开源的。​官网地址如下:
http://elm-chan.org/
​       57f7eed330ac75c60eb61edd9ffaf89d
        进入官网之后,选择softwares
0050865c09c5ce3811a1cafc9ef99609
        在softwares中选择FatFs Module​,进入之后,拉到网页的最下端,看见版本号,进行下载,现在的版本号已经更新到R0.15:      
31ce2d57d0a0bb657161373c71bc0ab3
二、将FATFS添加到工程中
       将以下几个文件添加到项目中:
  1. diskio.c
  2. ff.c
  3. cc936.c
  4. bsp_sdio.sd.c

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

  23. voidSD_LowLevel_Init(void)
  24. {  
  25.     GPIO_InitTypeDef  GPIO_InitStructure;​  /* GPIOC and GPIOD Periph clock enable */  
  26.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD, ENABLE);​  
  27.     GPIO_PinAFConfig(GPIOC, GPIO_PinSource8, GPIO_AF_SDIO);  
  28.     GPIO_PinAFConfig(GPIOC, GPIO_PinSource9, GPIO_AF_SDIO);  
  29.     GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_SDIO);  
  30.     GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_SDIO);  
  31.     GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_SDIO);  
  32.     GPIO_PinAFConfig(GPIOD, GPIO_PinSource2, GPIO_AF_SDIO);​  /* Configure PC.08, PC.09, PC.10, PC.11 pins: D0, D1, D2, D3 pins */  
  33.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11;  
  34.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
  35.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;  
  36.     GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;  
  37.     GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;  
  38.     GPIO_Init(GPIOC, &GPIO_InitStructure);​  /* Configure PD.02 CMD line */  
  39.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;  
  40.     GPIO_Init(GPIOD, &GPIO_InitStructure);​  /* Configure PC.12 pin: CLK pin */  
  41.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;  
  42.     GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;  
  43.     GPIO_Init(GPIOC, &GPIO_InitStructure);​  /* Enable the SDIO APB2 Clock */  
  44.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_SDIO, ENABLE);​  /* Enable the DMA2 Clock */  
  45.     RCC_AHB1PeriphClockCmd(SD_SDIO_DMA_CLK, ENABLE);
  46. }

        以下是SD卡初始化函数:
  1. SD_Error SD_Init(void)
  2. {  
  3.     __IO SD_Error errorstatus = SD_OK;    /**************配置SDIO中断 DMA中断**********************/  
  4.     NVIC_InitTypeDef NVIC_InitStructure;    // Configure the NVIC Preemption Priority Bits   
  5.     NVIC_PriorityGroupConfig (NVIC_PriorityGroup_1);  
  6.     NVIC_InitStructure.NVIC_IRQChannel = SDIO_IRQn;  
  7.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  
  8.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  
  9.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  
  10.     NVIC_Init (&NVIC_InitStructure);  
  11.     NVIC_InitStructure.NVIC_IRQChannel = SD_SDIO_DMA_IRQn;  
  12.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  
  13.     NVIC_Init (&NVIC_InitStructure);  /**********************************************************/    /* SDIO Peripheral Low Level Init */  
  14.     SD_LowLevel_Init();​  
  15.     SDIO_DeInit();​  
  16.     errorstatus = SD_PowerON();​  
  17.     if (errorstatus != SD_OK)  
  18.     {    /*!< CMD Response TimeOut (wait for CMDSENT flag) */   
  19.         return(errorstatus);  
  20.     }​  
  21.     errorstatus = SD_InitializeCards();​  
  22.     if (errorstatus != SD_OK)  
  23.     {    /*!< CMD Response TimeOut (wait for CMDSENT flag) */   
  24.         return(errorstatus);  
  25.     }​  /*!< Configure the SDIO peripheral */  /*!< SDIO_CK = SDIOCLK / (SDIO_TRANSFER_CLK_DIV + 2) */  /*!< on STM32F4xx devices, SDIOCLK is fixed to 48MHz */  
  26.     SDIO_InitStructure.SDIO_ClockDiv = SDIO_TRANSFER_CLK_DIV;  
  27.     SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising;  
  28.     SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable;  
  29.     SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable;  
  30.     SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_1b;  
  31.     SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable;  
  32.     SDIO_Init(&SDIO_InitStructure);​  /*----------------- Read CSD/CID MSD registers ------------------*/  
  33.     errorstatus = SD_GetCardInfo(&SDCardInfo);​  
  34.     if (errorstatus == SD_OK)  
  35.     {    /*----------------- Select Card --------------------------------*/   
  36.         errorstatus = SD_SelectDeselect((uint32_t)
  37.          (SDCardInfo.RCA << 16));  
  38.     }​  
  39.     if (errorstatus == SD_OK)  
  40.     {   
  41.         errorstatus = SD_EnableWideBusOperation(SDIO_BusWide_4b);  
  42.     }  ​  
  43.     return(errorstatus);
  44. }

三、应用接口函数
        在刚刚下载的网站中可以看见应用的接口函数,这部分重点说明一下:
打开文件函数f_open函数,这个函数既可以作为打开文件的函数,也可以作为建立文件的函数,里面含有打开文件的模式“只读”,“读、写”,“创建、写”等等,根据自己的需要进行打开或者建立文件。如果查看SD卡中没有相应的文件,会直接建立该文件名的文件。
关闭文件,关闭文件和打开文件相对应的,可以形象的知道在win操作中,打开一个文件的话,如果不关闭文件是无法保存编辑的过程。所以在操作文件的时候,必然最后要进行关闭文件,不然无法进行文件的编辑操作。
读取文件,打开文件之后,进行读取,需要设定读取的指针还有读取数据到哪个缓冲区中,需要建立一个缓冲区,然后将读取的数据赋值到缓冲区中,方便后续的调用操作。
写入文件,写入文件的时候,需要使用到另外一个函数f_lseek函数,这个函数之前的时候没有用好,在后续的例子程序中有说明。
F_mount函数,这个函数的作用是建立工作区,在FAT系统中进行工作区的建立,后续的代码中有这方面的说明。
F_mkfs函数,这个函数的作用是检查SD卡的系统是否是FAT系统,如果不是fat系统,那么会在SD卡中建立一个FAT系统,在这里可以选用FAT32进行格式化操作。
5ec4b9e686cdf8c88c080d90eddbe687
        当然,也可以在ff.h中进行查看​接口函数的说明。
4e3c8b38cc779e25331d7ed323fb252d
四、实际操作
       在c文件开始部分增加以下​引用,文件系统对象的定义,文件对象的定义,文件操作结果的定义,文件成功读写的数量,需要定义四个变量,我们在跟踪调试的时候,主要查看res_sd变量是否为ok是很重要的。
  1. #include "ff.h"
  2. FATFS fs;             /* FatFs文件系统对象 */
  3. FIL fnew;             /* 文件对象 */
  4. FRESULT res_sd;       /* 文件操作结果 */
  5. UINT fnum;            /* 文件成功读写数量 */

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

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

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

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

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

打赏榜单

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

评论

实操单片机使用FATFS,便于单片机操作文件系统。  发表于 2023-8-9 15:50
chenjun89 发表于 2023-8-6 00:02 来自手机 | 显示全部楼层
FATfs的官网什么时候变得这么花哨了,哈哈。
weifeng90 发表于 2023-8-6 16:08 来自手机 | 显示全部楼层
FATFS要是支持多级目录就好了。
guijial511 发表于 2023-8-7 09:56 来自手机 | 显示全部楼层
fatfs还是嵌入式系统必选文件系统啊
tpgf 发表于 2023-9-4 10:02 | 显示全部楼层
现在主流的文件系统除了fatfs之外还有其他的文件系统吗
qcliu 发表于 2023-9-4 10:25 | 显示全部楼层
单片机运行文件系统的最低要求是什么呢
drer 发表于 2023-9-4 10:53 | 显示全部楼层
如果单片机只是能勉强运行系统的话 在这个过程中会出现卡顿的现象吗
coshi 发表于 2023-9-4 11:26 | 显示全部楼层
跑系统的话 如何评估线程对资源的占用呢
 楼主| 单片小菜 发表于 2023-9-4 11:38 | 显示全部楼层
coshi 发表于 2023-9-4 11:26
跑系统的话 如何评估线程对资源的占用呢

可以写一个测试函数
kxsi 发表于 2023-9-4 11:49 | 显示全部楼层
文件系统除了这种接口形式之外还有其他形式的接口吗
wiba 发表于 2023-9-4 12:12 | 显示全部楼层
对文件系统的操作必须要使用指针吗
Fanexs168 发表于 2023-9-5 15:38 | 显示全部楼层
适配和移植FatFS,得好好看看FatFS自带的这些API接口描述、介绍,还有FS的框架。亲自上手适配下就有感觉了
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

107

主题

2354

帖子

10

粉丝
快速回复 在线客服 返回列表 返回顶部