本帖最后由 单片小菜 于 2023-8-7 16:51 编辑
#申请原创# @21小跑堂
引:
最小的文件系统,可以是控制U盘的时候,实现,也可以是控制SD卡的时候实现,甚至是一颗FLASH芯片也可以做最小的文件系统FATFS。这篇文章,说一下如何使用SDIO接口形式对SD卡进行操作:
主芯片:APM32F4系列单片机。
接口形式: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中进行文件系统的移植工作。
|
实操单片机使用FATFS,便于单片机操作文件系统。