平台:STM32ZET6(核心板)+ST-LINK/V2+SD卡+USB串口线

工程介绍:主要文件在USER组中,bsp_sdio_sdcard.c,bsp_sdio_sdcard.h和main.c,另外FatFs是用来后面移植文件系统使用的,对于本节内容暂时不需要。bsp_sdio_sdcard.c和bsp_sdio_sdcard.h文件主要参考教材《STM32库开发实战指南——基于STM32F03》。另外就是本教材用到的FatFs系统源代码,这里温馨提示一下,关于FatFs目前网上笔者找到的最新版的参考资料不是太多,所以笔者在用最新版做的时候,一些函数原型发生了变化,虽然变化不大,也给我造成了一定的阻碍,所以建议大家下载稍微老一点的版本,这样资料较多,出问题好解决,我最终用的版本是R0.09,因为在使用函数f_mkfs()的过程中遇到问题,无法格式化SD卡,最终选择较老的版本。本文有些内容来自于其他网友总结,在此表示感谢。

整体的项目和上一节中的SDIO读写SD卡类似,细节上在添加了FatFs组,专门存放FatFs移植的内容。对于移植过程,就是对diskio.c文件的修改的过程。这个文件完成了与底层有多的操作,我们只需要实现一下函数即可,不同的平台函数实现略有不同,但是提供给用户最终的接口是相同的,也正是因为这一点,使得FatFs文件系统有了很好的可移植性。

再次申明一下,FatFs文件系统与存储设备的接口函数再diskio.c文件中,我们只需要完善该文件即可。这也就是所谓的软件移植裁剪过程吧。我们需要完善五个函数disk_status,disk_initialize,disk_read,disk_write,disk_ioctl等,还有一个获取时间戳的函数,可以直接return 0;

1.宏定义

#define SD_CARD     0  //SD卡,卷标为0
#define EX_FLASH 1    //外部flash,卷标为1,为以后外部Flash扩展预留的
 
#define SD_BLOCKSIZE 512
 
//声明外部变量
extern SD_CardInfo SDCardInfo;
2.dis_status 获取磁盘的状态

//获得磁盘状态
DSTATUS disk_status (
    BYTE pdrv        /* Physical drive nmuber to identify the drive */
)
{
    DSTATUS status = STA_NOINIT;
    switch(pdrv)
    {
        case SD_CARD://SD卡
            status &= ~STA_NOINIT;
          break;
        case EX_FLASH://外部flash
             break;
        default:
            status = STA_NOINIT;
            break;
    }
    return status;
}  
3. disk_initialize 初始化磁盘,当遇到无法挂载SD卡,或者其他问题时,优先检查该函数,该函数会调用SD_Init函数,需要检查
SD_Init函数的返回结果是否为SD_OK。

//初始化磁盘
DSTATUS disk_initialize (
    BYTE pdrv                /* Physical drive nmuber to identify the drive */
)
{
    DSTATUS status = STA_NOINIT;
    switch(pdrv)
    {
        case SD_CARD://SD卡
            if(SD_OK == SD_Init())//SD卡初始化 
            {
                status &= ~STA_NOINIT;
            }
            else
            {
                status = STA_NOINIT;
            }
          break;
        case EX_FLASH://外部flash
             break;
        default:
            status = STA_NOINIT;
            break;
    }
    return status;

4. disk_read 读取SD卡

//读扇区
//pdrv:磁盘编号0~9
//*buff:数据接收缓冲首地址
//sector:扇区地址
//count:需要读取的扇区数
DRESULT disk_read (
    BYTE pdrv,        /* Physical drive nmuber to identify the drive */
    BYTE *buff,        /* Data buffer to store read data */
    DWORD sector,    /* Sector address in LBA */
    UINT count        /* Number of sectors to read */
)
{
    DRESULT status = RES_PARERR;
    SD_Error SD_Status = SD_OK;
    
    switch(pdrv)
    {
        case SD_CARD://SD卡
            if((DWORD)buff & 3)
            {
                DRESULT res = RES_OK;
                DWORD scratch[SD_BLOCKSIZE / 4];
                
                while(count--)
                {
                    res = disk_read(SD_CARD, (void*)scratch, sector++, 1);
                    
                    if(res != RES_OK)
                    {
                        break;
                    }
                    memcpy(buff, scratch, SD_BLOCKSIZE);
                    buff += SD_BLOCKSIZE;
                }
                return res;
            }
            
            SD_Status = SD_ReadMultiBlocks(buff, sector*SD_BLOCKSIZE,SD_BLOCKSIZE,count);
            
            if(SD_Status == SD_OK)
            {
                //检查传输是否完成
                SD_Status = SD_WaitReadOperation();
                while(SD_GetStatus() != SD_TRANSFER_OK);
            }
            
            if(SD_Status != SD_OK)
            {
                status = RES_PARERR;
            }
            else
            {
                status = RES_OK;
            }
          break;
        case EX_FLASH://外部flash
             break;
        default:
            status = RES_PARERR;
            break;
    }
    return status;
}
5.disk_write 写数据到SD卡

//写扇区
//pdrv:磁盘编号0~9
//*buff:发送数据首地址
//sector:扇区地址
//count:需要写入的扇区数
#if _USE_WRITE
DRESULT disk_write (
    BYTE pdrv,            /* Physical drive nmuber to identify the drive */
    const BYTE *buff,    /* Data to be written */
    DWORD sector,        /* Sector address in LBA */
    UINT count            /* Number of sectors to write */
)
{
    DRESULT status = RES_PARERR;
    SD_Error SD_Status = SD_OK;
    
    //检查参数
    if(!count)
    {
        return RES_PARERR;
    }
    
    switch(pdrv)
    {
        case SD_CARD://SD卡
            if((DWORD)buff & 3)
            {
                DRESULT res = RES_OK;
                DWORD scratch[SD_BLOCKSIZE / 4];
                
                while(count--)
                {
                    memcpy(scratch, buff, SD_BLOCKSIZE);
                    
                    res = disk_write(SD_CARD, (void*)scratch, sector++, 1);
                    if(res != RES_OK)
                    {
                        break;
                    }
                    buff += SD_BLOCKSIZE;
                }
                return res;
            }
            
            SD_Status = SD_WriteMultiBlocks((uint8_t *)buff, sector*SD_BLOCKSIZE,SD_BLOCKSIZE,count);
            
            if(SD_Status == SD_OK)
            {
                //检查传输是否完成
                SD_Status = SD_WaitReadOperation();
                while(SD_GetStatus() != SD_TRANSFER_OK);
            }
            
            if(SD_Status != SD_OK)
            {
                status = RES_PARERR;
            }
            else
            {
                status = RES_OK;
            }
          break;
        case EX_FLASH://外部flash
             break;
        default:
            status = RES_PARERR;
            break;
    }
    return status;
}
6.disk_ioctl 函数,获取SD卡的某些参数,如块大小等。

//其他表参数的获得
//pdrv:磁盘编号0~9
//ctrl:控制代码
//*buff:发送/接收缓冲区指针
#if _USE_IOCTL
DRESULT disk_ioctl (
    BYTE pdrv,        /* Physical drive nmuber (0..) */
    BYTE cmd,        /* Control code */
    void *buff        /* Buffer to send/receive control data */
)
{
    DRESULT res = RES_ERROR;                                           
    switch(pdrv)//SD卡
    {
        case SD_CARD:
        switch(cmd)
        {
            case GET_SECTOR_SIZE:
                    *(WORD*)buff = SD_BLOCKSIZE; 
              break;     
            case GET_BLOCK_SIZE:
                    *(DWORD*)buff = SDCardInfo.CardBlockSize;
              break;     
            case GET_SECTOR_COUNT:
              *(DWORD*)buff = SDCardInfo.CardCapacity / SD_BLOCKSIZE;
              break;
            case CTRL_SYNC:
              break;
        }
          res = RES_OK;
            break;
        case EX_FLASH://外部flash
             break;
        default:
            res = RES_PARERR;
            break;
    }
  return res;
}
7.get_fattime函数,获取时间戳。没有实现。

//获得时间                                                                                                                                                                                                                                  
DWORD get_fattime (void)
{
    //返回当前时间戳
    return 0;
}
8.另外需要修改ffconf.h文件,另外注意,新版本的该文件都会在宏的前面加上FF前缀,例如对于_USE_LEN,新版本的可能是
FF_USE_LLEN,本节的内容都是按照旧版本来说的,因此宏没有以FF为前缀。

#define _USE_LEN 2  //长文件名支持,默认不支持长文件名
#define _USE_MKFS 1  //格式化功能选择,使能FatFs的格式化功能
#define _CODE_PAGE 936  //语言功能选择,需要同时把语言文件添加到工程中,为支持简体中文,我们需要添加cc963.c文件
#define _VOLUMES 2  //物理设备数量,这里设置为2,包括SD卡和预留的外部Flash芯片
#define _MIN_SS 512   //指定扇区大小的最小值
#define _MAX_SS 4096  //指定扇区大小的最大值
以上就完成了FatFs的移植过程,接下来是对功能的测试,直接上代码了。
测试:

//FAT功能测试:格式化测试,文件写入测试,文件读取测试(基本功能)
FATFS fs; //FatFs文件系统对象
FIL fnew; //文件对象
FRESULT res_sd;//文件操作结果
UINT fnum; //文件成功读写数量
BYTE ReadBuffer[1024] = {0};
BYTE WriteBuffer[] = "成功移植了FatFs文件系统!\r\n"; //写缓存区
main 主函数,包含格式化测试和文件读写测试。
int main()
{
    //串口配置
    USART_Config();
    
    //初始化LED
    LED_GPIO_Config();
    
    //初始化SD卡
    if(SD_Init() == SD_OK)
    {
        printf("SD卡初始化成功,即将挂载SD卡。\r\n");
    }
    
    //挂载SD卡
    res_sd = f_mount(&fs, "0:", 1);
    
    //***********************格式化测试****************************
    if(res_sd == FR_NO_FILESYSTEM)
    {
        printf("SD卡没有文件系统,即将进行格式化...\r\n");
        //格式化
        res_sd = f_mkfs("0:", 0, 0);
        
        if(res_sd == FR_OK)
        {
            printf("SD卡成功格式化!\r\n");
            //格式化后先取消挂载
            res_sd = f_mount(NULL, "0:", 1);
            //再重新挂载
            res_sd = f_mount(&fs, "0:", 1);
        }
        else
        {
            printf("文件格式化失败!错误代码:%d\r\n",res_sd);
            while(1);
        }
    }
    else if(res_sd != FR_OK)
    {
        printf("挂载文件系统失败!可能是因为文件初始化失败!错误代码:%d\r\n", res_sd);
    }
    else
    {
        printf("文件系统挂载成功, 可进行读写测试!\r\n");
    }
    
    //***********************写测试****************************
    //打开文件,如果文件不存在则创建它
    printf("即将进行文件写入测试....\r\n");
    //打开文件,若不存在就创建
    res_sd = f_open(&fnew, "0:FatFs读写测试文件.txt", FA_CREATE_ALWAYS | FA_WRITE);
    //文件打开成功
    if(res_sd == FR_OK)
    {
        printf("打开文件成功!开始写入数据!\r\n");
        res_sd= f_write(&fnew, WriteBuffer, sizeof(WriteBuffer), &fnum);
        
        if(res_sd == FR_OK)
        {
            printf("数据写入成功!\r\n");
            printf("数据:%s。共写入%d个字符\r\n", WriteBuffer, fnum);
        }
        else
        {
            printf("数据写入失败!\r\n");
        }
        
        //关闭文件
        f_close(&fnew);
    }
    
    //***********************读测试****************************
    //打开文件,如果文件不存在则创建它
    printf("即将进行文件读取测试....\r\n");
    //打开文件,若不存在就创建
    res_sd = f_open(&fnew, "0:FatFs读写测试文件.txt", FA_OPEN_EXISTING | FA_READ);
    //文件打开成功
    if(res_sd == FR_OK)
    {
        printf("打开文件成功!开始读取数据!\r\n");
        res_sd= f_read(&fnew, ReadBuffer, sizeof(ReadBuffer), &fnum);
        
        if(res_sd == FR_OK)
        {
            printf("数据读取成功!\r\n");
            printf("数据:%s\r\n", ReadBuffer);
        }
        else
        {
            printf("数据读取失败!\r\n");
        }
        
        //关闭文件
        f_close(&fnew);
    }
    
    scan_files("FatFs读写测试文件.txt");
    
    //其他功能测试
    file_check();
    
    //多项功能测试
    miscellaneous();
    
    //取消挂载文件系统
    f_mount(NULL, "0:", 1);
    
    while(1);
}
补充,如有需要可以借鉴,其他功能测试。
//多项功能测试
static FRESULT miscellaneous()
{
    DIR dir;
    FATFS *pfs;
    DWORD fre_clust, fre_sect, tot_sect;
    
    printf("\r\n*************************设备信息获取***************************\r\n");
    //获取设备信息和空簇大小
    res_sd = f_getfree("0:", &fre_clust, &pfs);
    
    //计算得到总的扇区个数和空扇区个数
    tot_sect = (pfs->n_fatent - 2) * pfs->csize;
    fre_sect = fre_clust * pfs->csize;
    
    printf("设备总空间:%10lu KB\r\n可用空间:%10lu KB。\r\n", tot_sect*4, fre_sect*4);
    
    printf("\r\n*************************文件定位和格式化写入功能测试***************************\r\n");
    //打开文件,若不存在就创建
    res_sd = f_open(&fnew, "0:FatFs多项功能测试文件.txt", FA_CREATE_ALWAYS | FA_WRITE | FA_READ);
    //文件打开成功
    if(res_sd == FR_OK)
    {
        printf("打开文件成功!开始读取数据!\r\n");
        res_sd= f_write(&fnew, WriteBuffer, sizeof(WriteBuffer), &fnum);
        
        if(res_sd == FR_OK)
        {
            printf("数据写入成功!\r\n");
            printf("数据:%s\r\n", WriteBuffer);
            //文件定位,定位到文件末尾,f_size获取文件大小
            res_sd = f_lseek(&fnew, f_size(&fnew) - 1);
            if(res_sd == FR_OK)
            {
                //在原文件中追加一行内容
                f_printf(&fnew, "在原文件中追加一行内容。\n");
                f_printf(&fnew, "设备总空间:%10lu KB\r\n可用空间:%10lu KB。\r\n", tot_sect*4, fre_sect*4);
                
                //文件定位到起始位置
                res_sd = f_lseek(&fnew, 0);
                
                if(res_sd == FR_OK)
                {
                    //打开文件,若不存在就创建
                    res_sd = f_open(&fnew, "0:FatFs多项功能测试文件.txt", FA_OPEN_EXISTING | FA_READ);
                    //文件打开成功
                    if(res_sd == FR_OK)
                    {
                        printf("打开文件成功!开始读取数据!\r\n");
                        res_sd= f_read(&fnew, ReadBuffer, sizeof(ReadBuffer), &fnum);
                        
                        if(res_sd == FR_OK)
                        {
                            printf("数据读取成功!\r\n");
                            printf("数据:%s\r\n", ReadBuffer);
                        }
                        else
                        {
                            printf("数据读取失败!\r\n");
                        }
                        
                        //关闭文件
                        f_close(&fnew);
                    }
                }
            }
        }
        else
        {
            printf("数据读取失败!\r\n");
        }
        
        //关闭文件
        f_close(&fnew);
    }
    printf("\r\n*************************目录创建和重命名功能测试***************************\r\n");
    //尝试打开目录
    res_sd = f_opendir(&dir, "0:TestDir");
    if(res_sd != FR_OK)
    {
        //打开目录失败,开始创建目录
        res_sd = f_mkdir("0:TestDir");
    }
    else
    {
        //如果目录已经存在,关闭它
        res_sd = f_closedir(&dir);
        //删除文件
        f_unlink("0:FatFs读写测试文件.txt");
    }
    
    if(res_sd == FR_OK)
    {
        //重命名并移动文件
        res_sd = f_rename("0:FatFs多项功能测试文件.txt", "0:/TestDir/FatFs多项功能测试文件.txt");
        
        if(res_sd == FR_OK)
        {
            printf("重命名并移动文件成功!\r\n");
        }
        else
        {
            printf("重命名并移动文件失败!\r\n");
        }
    }
}
 
//文件信息获取
static FRESULT file_check()
{
    //文件信息
    static FILINFO fno;
    
    //获取文件信息,必须确保文件存在
    res_sd = f_stat("0:FatFs读写测试文件.txt", &fno);
    
    if(res_sd == FR_OK)
    {
        printf("0:FatFs读写测试文件.txt的信息如下:\r\n");
        printf("文件大小:%ld\r\n", fno.fsize);
        printf("时间戳:%u/%02u/%02u, %02u:%02u\r\n", (fno.fdate >> 9) + 1980, fno.fdate >> 5 & 15, fno.fdate & 31, fno.ftime >> 11, fno.ftime >> 5 &63);
        printf("属性:%c%c%c%c%c\r\n", 
        (fno.fattrib & AM_DIR) ? 'D' : '-', //目录
        (fno.fattrib & AM_RDO) ? 'R' : '-', //只读
        (fno.fattrib & AM_HID) ? 'H' : '-', //隐藏
        (fno.fattrib & AM_SYS) ? 'S' : '-', //系统文件
        (fno.fattrib & AM_ARC) ? 'A' : '-');//档案文件
    }
}
 
//路径扫描
static FRESULT scan_files(char* path)
{
    FRESULT res;//在递归过程中被修改,不用全局变量
    FILINFO fno;
    DIR dir;
    int i;
    char* fn;
    
    #if _USE_LEN//长文件名支持
    //简体中文需要两个字节保存一个字
    static char lfn[_MAX_LFN*2+1];
    fno.lfname = lfn;
    fno.lfsize = sizeof(lfn);
    #endif
    
    //打开目录
    res = f_opendir(&dir, path);
    if(res == FR_OK)
    {
        i = strlen(path);
        while(1)
        {
            //读取目录下的内容
            res = f_readdir(&dir, &fno);
            if(res != FR_OK || fno.fname[0] == 0)
            {
                break;
            }
            #if _USE_LEN
            fn = *fno.lfname ? fno.lfname : fno.fname;
            #else
            fn = fno.fname;
            #endif
            
            //点表示当前目录,跳过
            if(*fn == '.')
                continue;
            //目录,递归读取
            if(fno.fattrib & AM_DIR)
            {
                //合成完整目录名
                sprintf(&path[i], "/%s", fn);
                
                //递归遍历
                res = scan_files(path);
                
                path[i] = 0;
                if(res != FR_OK)
                {
                    break;
                }
                else
                {
                    printf("%s/%s\r\n", path, fn);//输出文件名,可以在这里提取特定格式的文件路径
                }
            }
        }
    }
    return res;
}
感谢大家的耐心阅读,如果有问题,欢迎一起讨论。
————————————————
版权声明:本文为CSDN博主「CallMeJacky」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zyxhangiian123456789/article/details/79098483

基于SD卡的FatFs文件系统(FatFs移植到STM32)相关推荐

  1. Exynos4412 移植针对Samsung的Linux-6.1(二)SD卡驱动——解决无法挂载SD卡的根文件系统

    系列文章目录 Exynos4412 移植针对Samsung的Linux-6.1(一)下载.配置.编译Linux-6.1 Exynos4412 移植针对Samsung的Linux-6.1(二)SD卡驱动 ...

  2. cubemx 读卡器_STM32CubeMX基于SD卡的FATFS文件系统测试

    1.问题描述:使用STM32CubeMX配置SD基于SD卡的FATFS文件系统,并进行测试文件的读写. 2.测试准备: (1)测试环境: 软件平台:STM32CubeMX -4.22.0;KEIL5 ...

  3. 1.6 Cubemx_STM32F103_NOOS SDIO_DMA_FATFS基于SD卡的FATFS测试(一)

    一.遇到的问题: 1.刚开始挂载成功,f_open一直不行. :后面发现新版cubemx FATFS->configuration->Platform Setting里有一个BSP设置函数 ...

  4. FatFs文件系统的移植

    FatFs 的底层可以写一次命令,读写多个扇区.FatFs的设计的读写的思想就很好,小块的数据,我就经过Buffer来存储,大块的数据,我就直接进行存取,那样速度,效率高了很多,看图: FatFs文件 ...

  5. ARM单片机FATFS文件系统的移植

    ARM单片机FATFS文件系统的移植 测试效果 前提条件 下载所需源码 FATFS 文件系统 SFUD万能驱动 加入工程 接口驱动 测试代码 FreeRTOS10.0.1 FATFS FF14A SF ...

  6. 基于ATmega32的SD卡上FAT32文件系统数据读取

    SD卡(secure digital memory card,安全数码卡)是一种基于半导体快闪记忆器的新一代记忆设备,它被广泛地于便携式装置上使用,例如数码相机.个人数码助理(PDA)和多媒体播放器等 ...

  7. 基于ATXMEGA128A1U-EK开发板的应用案例002: 基于SD卡热电偶温度采集应用

    By Mcuzone 实现功能:7705差分输入端外接热电偶可测得测量端温度,再与LM75所测得的冷端温度相加,就得到了热电偶端的真实温度.在1.8寸屏上会实时显示当前温度.DS3231的实时时间.当 ...

  8. WINCE6.0+S3C6410基于SD卡启动

    ********************************LoongEmbedded******************************** 作者:LoongEmbedded(kandi ...

  9. 嵌入式Linux系统镜像制作(基于SD卡)

    文章目录 目的 原理 环境准备 系统镜像制作 从现有SD卡备份 从空白文件开始制作 系统镜像使用 其它补充 总结 目的 嵌入式Linux在开发过程中对于软件方面通常是 bootloader.linux ...

  10. 从sd卡启动之文件系统制作

    在调试andriod的代码时候发现了一个问题.在andriod调试的时候无法进入常规的文件系统进行简单的操作.由于想要把alsa移植到210的开发板中,发现"/"文件系统是无法进行 ...

最新文章

  1. Linux线程-互斥锁pthread_mutex_t
  2. 分享一张oracle scan图
  3. 科兴生物董事长:两针建立免疫记忆 三针后抗体提升十倍
  4. 多出20倍?Android 收集用户数据量远超 iPhone
  5. windows操作系统的使用 —— 资源监视器
  6. c语言创建线程代码,如何用C语言实现多线程
  7. Oracle 补丁体系 及 opatch 工具 介绍
  8. windows上怎么用libnfc的库函数编程
  9. Excel怎么批量将各数据复制填充指定次数
  10. arm64_linux head.S的执行流程(3.18)- 12.msm8996 地址空间布局
  11. java中怎么输入中文_MultiMC下载-MultiMC中文实用版 v1.0
  12. 大数据必学Java知识(一):Java基础入门语法和安装
  13. 头条二面智力题!难受!
  14. 软件设计模式——用Java代码实现物种特征
  15. vue-cli模拟后台数据交互
  16. 使用 Nginx 提供 DDNS 服务(中篇)
  17. 如何使用cad版本转换器?转换cad版本的技巧
  18. 小学生 学计算机通信 知识,小学通信知识ppt
  19. 欧美简约商务PPT模板
  20. 专利挖掘文章研读笔记

热门文章

  1. android加载html
  2. PowerDesign的简单使用方法
  3. 上周热点回顾(10.1-10.7)
  4. 民航资源网招聘出奇兵效法麦当劳
  5. HP 520 双系统 vista xp
  6. Sigmoid是品优函数吗?
  7. 【Arduino】库分析及如何编写自己的Arduino库
  8. 【小项目关键技术七】MPU6050
  9. 【控制】《多智能体系统一致性与复杂网络同步控制》郭凌老师-目录
  10. Ardino基础教程 11_PWM调控灯光亮度