基于STM32+CS创世 SD NAND(贴片SD卡)完成FATFS文件系统移植与测试(下篇)

news2024/12/25 1:24:26

四、移植FATFS文件系统

前面第3章,完成了SD NAND的驱动代码编写,这一章节实现FATFS文件的移植。

4.1 FATFS文件系统介绍

(1)介绍

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

(2)特点

【1】Windows兼容的FAT文件系统

【2】不依赖于平台,易于移植

【3】代码和工作区占用空间非常小

【4】多种配置选项

【5】多卷(物理驱动器和分区)

【6】多ANSI/OEM代码页,包括DBCS

【7】在ANSI/OEM或Unicode中长文件名的支持

【8】RTOS的支持

【9】多扇区大小的支持

【10】只读,最少API,I/O缓冲区等等

(3)移植性

fatfs模块是ANSI C(C89)编写的。 没有平台的依赖, 编译器只要符合ANSI C标准就可以编译。

fatf模块假设大小的字符/短/长8/16/32位和int是16或32位。 这些数据类型在integer.h文件中定义。这些数据类型在大多数的编译器中定义都符合要求。 如果现有的定义与编译器有任何冲突发生时,需要自己解决。

4.2 下载源码

下载地址:http://elm-chan.org/fsw/ff/00index_e.html

FATFS有两个版本,一个大版本,一个小版本。小版本主要用于8位机(内存小)使用。

下载图:

4.3 源码结构介绍

将下载的源码解压后可以得到两个文件夹: doc 和 src。 doc 里面主要是对 FATFS 的介绍(离线文档—英文和日文),而 src 里面才是我们需要的源码。

其中,与平台无关的是:

  1. ffconf.h     FATFS配置文件

  2. ff.h        应用层头文件

  3. ff.c        应用层源文件

  4. diskio.h    硬件层头文件

  5. interger.h  数据类型定义头文件

  6. option      可选的外部功能(比如支持中文等)

与平台相关的代码:

  1. diskio.c     底层接口文件(需要用户提供)

FATFS 模块在移植的时候,我们一般只需要修改 2 个文件,即 ffconf.h 和 diskio.c。

FATFS模块的所有配置项都是存放在 ffconf.h 里面,我们可以通过配置里面的一些选项,来满足自己的需求。

最顶层是应用层,使用者无需理会 FATFS 的内部结构和复杂的 FAT 协议,只需要调用FATFS 模块提供给用户的一系列应用接口函数,如 f_open, f_read, f_write 和 f_close 等,就可以像在 PC 上读写文件那样简单。

中间层 FATFS 模块, 实现了 FAT 文件读/写协议。 FATFS 模块提供的是 ff.c 和 ff.h。除非有必要,使用者一般不用修改,使用时将头文件直接包含进去即可。

需要我们编写移植代码的是 FATFS 模块提供的底层接口,它包括存储媒介读/写接口 ( disk、I/O) 和供给文件创建修改时间的实时时钟。

4.4 下载源码并加入到工程

先准备好一个有SD NAND驱动代码的STM32工程(代码前面第3章已经贴了),接着就完成下面的步骤。

打开KEIL工程,添加FATFS文件源码:

加入.h文件主要是方便配。cc936.c 用于支持中文。

4.5 修改代码进行移植

(1)修改diskio.c文件


 

注释掉现在不需要的用到的文件,因为我们现在用的是SD卡,与USB,ATA,MMC卡没关系。

并加入一个新的宏 :

#define SD 0

定义SD卡的物理驱动器号为0。

修改 disk_status函数,该函数主要是用来获取磁盘状态。现在未用到,可以直接函数体内代码删除。

修改截图:

代码示例:

#include "diskio.h"   /* fatf底层API */

#include "sd.h"       /* SD卡驱动头文件  */

/* 定义每个驱动器的物理驱动器号*/

#define SD    0

/*-----------------------------------------------------------------------*/

/* 获取设备(磁盘)状态                                                     */

/*-----------------------------------------------------------------------*/

DSTATUS disk_status (

BYTE pdrv /* 物理驱动识别 */

)

{

   return 0;  //该函数现在无需用到,直接返回0

}

修改disk_initialize函数,添加SD卡的初始化,其他不用到的代码直接删掉,该函数成功返回0,失败返回1。

修改截图:

代码示例:

/*-----------------------------------------------------------------------*/

/* 初始化磁盘驱动                                                        */

/*-----------------------------------------------------------------------*/

DSTATUS disk_initialize (

BYTE pdrv /* 物理驱动识别 */

)

{

DSTATUS stat;

int result;

switch (pdrv) {

case SD :            //选择SD卡

stat=SD_Init();   //初始化SD卡-用户自己提供

}

if(stat)return STA_NOINIT;  //磁盘未初始化

return 0; //初始化成功

}

修改disk_read函数,加入SD卡读任意扇区的函数(需要用户自己提供),其他不用到的选项可以删掉。


修改代码如下:

/*-----------------------------------------------------------------------*/

/* 读扇区                                                                */

/*-----------------------------------------------------------------------*/

DRESULT disk_read (

BYTE pdrv, /* 物理驱动编号 - 范围0-9*/

BYTE *buff, /* 数据缓冲区存储读取数据 */

DWORD sector,   /* 扇区地址*/

UINT count /* 需要读取的扇区数*/

)

{

DRESULT res;

int result;

switch (pdrv) {

case SD:

  res=SD_Read_Data((u8*)buff,sector,count);  //读SD扇区函数--用户提供

  return res; //在此处可以判错误

}

return RES_PARERR;  //无效参数

}


修改disk_write 函数,添加写扇区函数:

代码:

/*-----------------------------------------------------------------------*/

/* 写扇区                                                                */

/*-----------------------------------------------------------------------*/

#if _USE_WRITE

DRESULT disk_write (

BYTE pdrv,   /* 物理驱动号*/

const BYTE *buff,        /* 要写入数据的首地址 */

DWORD sector,    /* 扇区地址 */

UINT count    /* 扇区数量*/

)

{

DRESULT res;

int result;

switch (pdrv) {

case SD:

res=SD_Write_Data((u8*)buff,sector,count); //写入扇区

  return res;

}

return RES_PARERR;  //无效参数

}

#endif

修改disk_ioctl 函数,填充ioctl命令功能。这些功能是标准的命令,在diskio.h有定义。

代码如下:

/*-----------------------------------------------------------------------*/

/* 其他函数                                              */

/*-----------------------------------------------------------------------*/

#if _USE_IOCTL

DRESULT disk_ioctl (

BYTE pdrv, /* 物理驱动号 */

BYTE cmd,   /* 控制码  */

void *buff /* 发送/接收数据缓冲区地址 */

)

{

DRESULT res;

int result;

switch (pdrv) {

case SD:

 switch(cmd)

 {

 case CTRL_SYNC:      //等待写过程

 SD_CS(0);          //选中SD卡

 if(SD_Wait_Ready())result = RES_ERROR;/*等待卡准备好*/

     else res = RES_OK;     //成功

 SD_CS(1);            //释放SD卡

                        break;  

 case GET_SECTOR_SIZE://获取扇区大小

   *(DWORD*)buff = 512; 

        res = RES_OK;     //成功

        break;

 case GET_BLOCK_SIZE:    //获取块大小

*(WORD*)buff = 8;      //块大小(扇区为单位),一块等于8个扇区

         res = RES_OK;

         break;

 case GET_SECTOR_COUNT: //获取总扇区数量

        *(DWORD*)buff = SD_Get_Sector_Count();

        res = RES_OK;

        break;

default:  //命令错误

        res = RES_PARERR;

        break;

 }

return res;

}

return RES_PARERR;  //返回状态

}

(2)修改ffconf.h文件

需要注意的一些宏配置:

#define _CODE_PAGE 936   //采用中文GBK编码       (64行)

#define _USE_LFN 3     //动态的堆上工作             (93行)

#define _MAX_LFN 255   /*_USE_LFN选项开关LFN(长文件名)特性。

#define _VOLUMES 1     /* 支持的磁盘数量(逻辑驱动器)。 */   (142行)

#define _MIN_SS 512                                  (165行)

#define _MAX_SS 512   /*这些选项配置支持扇区大小的范围。(512,1024, 4096*/ 

#define _FS_NORTC     0    /*启用RTC时间功能*/   (202行)

#define _NORTC_MON     1

#define _NORTC_MDAY 1

#define _NORTC_YEAR 2015 //年  

/*需要实现:get_fattime()函数*/


ffconf.h 文件源码:

/*---------------------------------------------------------------------------/

/  FatFs - FAT文件系统模块配置文件  R0.11a (C)ChaN, 2015

/---------------------------------------------------------------------------*/

#define _FFCONF 64180 /* 版本识别*/

/*---------------------------------------------------------------------------/

/ 功能配置

/---------------------------------------------------------------------------*/

#define _FS_READONLY 0

/* 这个选项开关只读配置。(0:读/写或1:只读)   

/只读配置删除编写API函数,f_write(),f_sync(),   

/ f_unlink(),f_mkdir(),f_chmod(),f_rename(),f_truncate(),f_getfree()   

/写和可选的功能. */

#define _FS_MINIMIZE 0

/*此选项定义删除一些基本的API函数极小化水平。  

/   

/ 0:所有基本功能都是激活的。  

/ 1:f_stat(),f_getfree(),f_unlink(),f_mkdir(),f_chmod(),f_utime(),   

/ f_truncate()和f_rename()函数删除。  

/ 2:f_opendir(),f_readdir()和f_closedir()中除了1。  

/ 3:f_lseek()函数删除除了2。*/

#define _USE_STRFUNC 1

/*这个选项开关字符串函数,f_gets(),f_putc(),f_puts()和 

/ f_printf()。  

/   

/ 0:禁用字符串函数。  

/ 1:启用没有LF-CRLF转换。  

/ 2:启用LF-CRLF(回车换行)转换。*/

#define _USE_FIND 0

/*这个选项开关过滤目录读取特性和相关功能,   

/ f_findfirst()和f_findnext()。(0:禁用或1:启用)*/

#define _USE_MKFS 1

/* 这个选项开关f_mkfs()函数。(0:禁用或1:启用) */

#define _USE_FASTSEEK 1

/* 这个选项开关快速寻求功能。(0:禁用或1:启用) */

#define _USE_LABEL 1

/*   磁盘卷标这个选项开关功能,f_getlabel()和f_setlabel()。  

/(0:禁用或1:启用) */

#define _USE_FORWARD 0

/*  这个选项开关f_forward()函数。(0:禁用或1:启用)   

/启用它,也_FS_TINY需要设置为1. */

/*---------------------------------------------------------------------------/

/ 语言环境和名称空间配置

/---------------------------------------------------------------------------*/

#define _CODE_PAGE 936  //采用中文GBK编码

/* 这个选项指定OEM代码页在目标系统上使用。  

/不正确的代码页的设置会导致文件打开失败.

/

/   1   - ASCII (没有扩展字符。Non-LFN cfg。只有)

/   437 - U.S.

/   720 - 阿拉伯语

/   737 - 希腊语;

/   771 - 阿富汗

/   775 - 波罗的海

/   850 - 拉丁1

/   852 - 拉丁2

/   855 - 西里尔字母

/   857 - 土耳其语

/   860 - 葡萄牙语

/   861 - 冰岛语

/   862 - 希伯来人

/   863 - 加拿大法语

/   864 - 阿拉伯语

/   865 - 日耳曼民族的

/   866 - 俄语

/   869 - 希腊 2

/   932 - 日本人 (DBCS)

/   936 - 简体中文(DBCS)

/   949 - 韩国人 (DBCS)

/   950 - 繁体中文(DBCS)

*/

#define _USE_LFN 3 //动态的堆上工作

#define _MAX_LFN 255

/*_USE_LFN选项开关LFN(长文件名)特性。

/

/ 0:禁用LFN特性。_MAX_LFN没有影响。  

/ 1:启用LFN BSS静态工作缓冲区。总是不是线程安全的。  

/ 2:启用LFN与动态缓冲栈上的工作。  

/ 3:使LFN与动态缓冲区在堆上工作。

/

/  当启用LFN(长文件名)特性,Unicode(选项/ unicode.c)必须处理功能  

/被添加到项目中。LFN工作缓冲区占用(_MAX_LFN + 1)* 2字节。  

/当使用堆栈缓冲区,照顾堆栈溢出。当使用堆  

/工作缓冲区内存,内存管理功能,ff_memalloc()和  

/ ff_memfree(),必须添加到项目中。 */

#define _LFN_UNICODE 0 

/* 这个选项开关字符编码的API。(0:ANSI / OEM或1:Unicode)   

路径名/使用Unicode字符串,并设置_LFN_UNICODE启用LFN特性  

/1。这个选项也会影响行为的字符串的I / O功能。

*/

#define _STRF_ENCODE 3

/* 当_LFN(长文件名)_UNICODE是1,这个选项选择文件的字符编码  

/通过字符串读取/写入I /O功能,f_gets(),f_putc(),f_puts和f_printf().

/

/  0: ANSI/OEM

/  1: UTF-16LE

/  2: UTF-16BE

/  3: UTF-8

/

/ 当_LFN_UNICODE = 0时,该选项没有影响。*/

#define _FS_RPATH 0

/* 这个选项配置相对路径的功能。  /   

/ 0:禁用相对路径特性和删除相关功能。  

/ 1:启用相对路径特性。f_chdir()和f_chdrive()是可用的。  

/ 2:f_getcwd()函数可用除了1。  /   

/注意,目录项读通过f_readdir()这个选项。 

*/

/*---------------------------------------------------------------------------/

/ 驱动/卷配置

/---------------------------------------------------------------------------*/

#define _VOLUMES 1

/* 支持的磁盘数量(逻辑驱动器)。 */

#define _STR_VOLUME_ID 0

#define _VOLUME_STRS "RAM","NAND","CF","SD1","SD2","USB1","USB2","USB3"

/* STR_VOLUME_ID选项开关卷ID字符串功能。  

/当_STR_VOLUME_ID设置为1时,也可以使用预先定义的字符串在路径名称/数量。

为每个_VOLUME_STRS定义驱动ID字符串  

/逻辑驱动器。条目的数量必须等于_VOLUMES。有效字符  

/驱动ID字符串:a - z和0 - 9。*/

#define _MULTI_PARTITION 0

/*  这个选项开关多分区的特性。在默认情况下(0),每个逻辑驱动器  

/号绑定到相同的物理驱动器号  

/物理驱动器将被安装。当启用分区特性(1),   

/每个逻辑驱动器号是绑定到任意物理驱动器和分区  

/中列出VolToPart[]。还f_fdisk()函数可用. */

#define _MIN_SS 512

#define _MAX_SS 512

/*  这些选项配置支持扇区大小的范围。(512,1024,   

/ 2048或4096)总是为大多数系统设置两个512,卡和所有类型的内存  

/硬盘。但是可能需要更大的值为车载闪存和一些  

/类型的光学媒体。当_MAX_SS大于_MIN_SS,fatf配置  

/变量扇区大小和GET_SECTOR_SIZE命令必须执行  disk_ioctl()函数. */

#define _USE_TRIM 0

/* 这个选项开关ATA-TRIM特性。(0:禁用或1:启用)   

/启用削减特性,也应该实现CTRL_TRIM命令  

/ disk_ioctl()函数。*/

#define _FS_NOFSINFO 0

/*   

如果你需要知道正确的自由空间体积FAT32,设置一些0   

/选项,f_getfree()函数在第一次后体积将迫使山  

/全脂肪扫描。位1控制使用的集群数量分配。  /   

/ bit0 = 0:使用免费的集群计算FSINFO如果可用。  

/ bit0 = 1:不相信自由FSINFO集群计算。  

/ bit1 = 0:最后使用集群可用FSINFO如果数量分配。  

/ bit1 = 1:不相信最后分配FSINFO集群数量.

*/

/*---------------------------------------------------------------------------/

/ 系统配置列表

/---------------------------------------------------------------------------*/

#define _FS_TINY 0

/* 这个选项开关小缓冲区配置。(0:正常或1:小)   

/小配置,文件对象的大小(FIL)_MAX_SS减少字节。而不是私人部门从文件对象,缓冲了  

/公共部门缓冲文件系统中的对象(fatf)是用于该文件  

/数据传输. */

#define _FS_NORTC 0

#define _NORTC_MON 1

#define _NORTC_MDAY 1

#define _NORTC_YEAR 2015 //年

/* _FS_NORTC选项开关时间戳的特性。如果系统没有/

 RTC函数或不需要有效的时间戳,_FS_NORTC 1设置为禁用/

 时间戳的特性。所有对象修改fatf将有一个固定的时间戳。/

  固定的时间定义为_NORTC_MON _NORTC_MDAY _NORTC_YEAR。  

/当启用时间戳特性(_FS_NORTC = = 0),需要实现get_fattime()函数。  /

 添加到项目RTC读当前时间形式。_NORTC_MON,   /

_NORTC_MDAY和_NORTC_YEAR没有效果。  

/这些选项没有影响只读配置(_FS_READONLY = = 1)。 */

#define _FS_LOCK 0

/*  _FS_LOCK选项开关控制复制的文件打开的文件锁定功能  

/和非法操作打开对象。这个选项_FS_READONLY时必须是0   

/是1。  /   

/ 0:禁用文件锁定功能。为了避免体积腐败、应用程序  

/应该避免非法打开,删除和重命名的开放对象。  

/ > 0:启用文件锁定功能。值定义了多少文件/子目录  

可以同时打开的/文件锁的控制之下。注意,这个文件独立于re-entrancy /锁功能。 */

#define _FS_REENTRANT 0

#define _FS_TIMEOUT 1000

#define _SYNC_t HANDLE

/*  _FS_REENTRANT选项开关re-entrancy fatf的(线程安全)   

/模块本身。注意,不管这个选项,文件访问不同  

/体积始终是凹角和音量控制功能,f_mount(),f_mkfs()   

/和f_fdisk()函数,总是不凹角。只有文件/目录的访问  

/相同的体积是这个功能的控制。  

/   

/ 0:禁用re-entrancy。_FS_TIMEOUT和_SYNC_t没有效果。  

/ 1:启用re-entrancy。还提供用户同步处理程序,   

/ ff_req_grant(),ff_rel_grant(),ff_del_syncobj()和ff_cre_syncobj()   

/函数,必须添加到项目中。样品中可用  

/选项

/ syscall.c。

/

/  _FS_TIMEOUT定义超时时间单位的滴答声。  

/ _SYNC_t定义了O 

/ S依赖同步对象类型。例如处理、ID、OS_EVENT *   

/ SemaphoreHandle_t等. .O / S的头文件定义需要  

/包括在ff.c的范围。 */

#define _WORD_ACCESS 0

/* _WORD_ACCESS选项是一个只有依赖于平台的选择。

它定义了这个词/访问方法是用来体积上的数据。

/

/ 0:逐字节的访问。总是兼容所有平台。  

/ 1:词的访问。不要选择这个,除非在下列条件。  

/   

/ *地址对齐内存访问总是允许所有指令。  

/ *字节顺序的记忆是低位优先。  

/   

/如果是这样的情况,_WORD_ACCESS也可以减少代码的大小设置为1。  

/下表显示允许设置某种类型的处理器。

/

/  ARM7TDMI   0   *2          ColdFire   0    *1         V850E      0    *2

/  Cortex-M3  0   *3          Z80        0/1             V850ES     0/1

/  Cortex-M0  0   *2          x86        0/1             TLCS-870   0/1

/  AVR        0/1             RX600(LE)  0/1             TLCS-900   0/1

/  AVR32      0   *1          RL78       0    *2         R32C       0    *2

/  PIC18      0/1             SH-2       0    *1         M16C       0/1

/  PIC24      0   *2          H8S        0    *1         MSP430     0    *2

/  PIC32      0   *1          H8/300H    0    *1         8051       0/1

/

/   

* 1:高位优先。  / 

* 2:不支持不连续的内存访问。  / 

* 3:一些编译器生成LDM(逻辑磁盘管理器 ) / STM mem_cpy(内存拷贝)函数。

*/

(3)实现动态内存分配函数与时间函数

ff.h文件有动态内存的释放,动态内存申请,时间获取函数接口。

在diskio.c文件实现函数功能:

代码实现如下:

//动态内存分配

void* ff_memalloc (UINT msize)     /* 分配内存块 */

{

return (void*)malloc(msize); //分配空间

}

//动态内存释放

void ff_memfree (void* mblock)     /* 空闲内存块 */

{

free(mblock);              //释放空间

}

//返回FATFS时间

//获得时间  

DWORD get_fattime (void)

{

//Get_RTC_Timer(); //获取一次RTC时间

return (RTC_Timer.year-1980)<<25|   //年

  RTC_Timer.month<<21|  //月

       RTC_Timer.day<<16|    //日

       RTC_Timer.hour<<11|   //时

       RTC_Timer.minute<<5|  //分

       RTC_Timer.sec;        //秒

}

/*

Return Value

Currnet local time is returned with packed into a DWORD value. The bit field is as follows:

bit31:25

Year origin from the 1980 (0..127)

bit24:21

Month (1..12)

bit20:16

Day of the month(1..31)

bit15:11

Hour (0..23)

bit10:5

Minute (0..59)

bit4:0

Second / 2 (0..29)

*/

(4)修改堆栈空间

完成了上述的修改,还需要修改堆栈空间,因为长文件支持需要占用堆空间。

修改STM32启动文件如下:


 

(5)编译工程测试

修改完毕之后,给开发板插上SD卡,调用API函数在SD卡创建一个文件,并写入数据,测试是否成功:

#include "ff.h"

FATFS fs;  // 用户定义的文件系统结构体

FIL  file;  // 用户定义的文件系统结构体

u8 buff[]="123 知识!!";

int main(void)

{

u32 data;                //检测SD卡容量

u8 i,res;

    LED_Init();              //LED灯初始化

    Delay_Init();

    KEY_Init();

    USART1_Init(72,115200);

    USART2_Init(36,115200);

     FLASH_Init();

  Set_Font_addr(); //字库地址初始化

  FSMC_SRAM_Init();

  LCD_Init();

  RTC_Init();     //RTC时钟初始化

  while(SD_Init())    //检测不到SD卡,SD相关硬件初始化

{

i=!i;

LCD_ShowString(60,150,200,16,16,"SD Card Error!  Please Check SD Card!!",0xf800);

Delay_ms(500);

LED1(i)//DS0闪烁

}

       f_mount(&fs,"0",1);  // 注册工作区,驱动器号 0,初始化后其他函数可使用里面的参数

printf("注册工作区!\n");

if(f_mkfs("0",0,4096))  //格式化SD卡

{

printf("格式化失败!!\n");

}

else

{

printf("格式化成功!!\n");

}

res = f_open(&file, "/file.c", FA_OPEN_ALWAYS | FA_READ | FA_WRITE);

if(res==0)

{

printf("文件创建成功!!\n");

}

else

{

printf("文件创建失败!!\n");

}

res =f_write(&file,buff,strlen((const char*)buff),&data);

if(res==0)

{

printf("数据写入成功!!\n");

}

else

{

printf("数据写入失败!!\n");

}

printf("成功写入%d字节数据\n",data);

f_close(&file);  //关闭文件

//_FS_RPATH

while(1)

{

Delay_ms(1000);

LED1(1);

Delay_ms(500);

LED1(0);

}

}

五、案例使用

5.1 读取GBK字库文件(LCD汉字显示)

产品开发中,如果设备带有LCD显示屏,一般会显示各种文字提示,或者机器操作说明,显示中文需要字库,为了方便字模的提取,可以将字库文件制作好之后放到SD NAND上,通过文件系统打开字库文件,读取字模进行显示。

下面贴出文件系统读取字模的核心代码:

/*

函数功能: 显示GBK字库数据

          u32 x  范围0~319

          u32 y  范围0~479

          u32 size  数据的宽度(必须是8的倍数)  是正方形

          u8 *p  中文

说明: 取模横向坐标必须保证是8的倍数

*/

void ILI9341_DisplayGBKData(u32 x,u32 y,u32 size,u8 *p)

{

FIL fp;

UINT br;

u8 L,H;

  u32 Addr;

  u16 font_size=size/8*size; //字体占用的点阵码字节大小

  u8 *buff=NULL;

H=*p;

L=*(p+1);

if(L<0x7f)L=L-0x40;

else L=L-0x41;

H=H-0x81;

Addr=(190*H+L)*font_size; //中文在字库里的偏移量

buff=malloc(font_size);   //使用的堆空间

if(buff==NULL)return;

switch(size)

{

case 16:

if(f_open(&fp,"0:/font/gbk16.DZK",FA_READ)!=FR_OK)

                {

                      printf("f_open error.\r\n");

                }

f_lseek(&fp,Addr);

f_read(&fp,buff,font_size,&br);

f_close(&fp);

               

break;

case 24:

                f_open(&fp,"0:/font/gbk24.DZK",FA_READ);

f_lseek(&fp,Addr);

f_read(&fp,buff,font_size,&br);

f_close(&fp);

break;

case 32:

break;

}

//显示中文

ILI9341_DisplayData(x,y,size,size,buff);

//释放空间

free(buff);

}

这是读取字模,显示的效果:

5.2 读取MP3文件播放(开机音乐)

这个例子是演示文件系统的目录扫描函数使用方式,读取指定目录下的MP3文件进行播放。

u8 PlayerMP3(const char *path);

FATFS FatFs;

int main()

{

LED_Init();

BEEP_Init();

KeyInit();

  USARTx_Init(USART1,72,115200);

  

  SDCardDeviceInit(); //初始化SD卡

  

//  res=f_mkfs("0:",FM_ANY,0,work,sizeof work);

//  if(res)printf("格式化失败!\n");

//  else printf("格式化成功!\n");

  f_mount(&FatFs, "0:", 0);   //注册工作区

  

  PlayerMP3("0:/MP3");

  

while(1)

{

    DelayMs(100);

    LED0=!LED0;

}

}

/*

函数功能: 扫描目录mp3播放

0表示成功 1表示失败

*/

u8 PlayerMP3(const char *path)

{

    DIR dir;

    FRESULT res; 

    FILINFO fno; //存放读取的文件信息

    char *abs_path=NULL;  

    

    /*1. 打开目录*/    

    res=f_opendir(&dir,path);

    if(res!=FR_OK)return res;

    

    /*2. 循环读取目录*/

     while(1)

     {

        res=f_readdir(&dir,&fno);

        if(fno.fname[0] == 0 || res!=0)break;

        printf("文件名称: %s,文件大小: %ld 字节\r\n",fno.fname,fno.fsize);

        /*过滤目录*/

        if(strstr(fno.fname,".mp3"))

        {

            //申请存放文件名称的长度

            abs_path=malloc(strlen(path)+strlen(fno.fname)+1);

            if(abs_path==NULL)break;

             

            strcpy(abs_path,path);

            strcat(abs_path,"/");

            strcat(abs_path,fno.fname);

          

            printf("abs_path=%s\n",abs_path);

            VS1053_MP3(0,0,abs_path);     

            free(abs_path);

        }

    }

    

    /*3. 关闭目录*/

    f_closedir(&dir);

    return 0;

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/386461.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

07--组件

一、小程序组件分类微信团队为开发者提供了一系列基础组件&#xff0c;开发者可以通过组合这些基础组件进行快速开发。小程序中的组件也是非常丰富的&#xff0c;开发者可以基于组件快速搭建出漂亮的页面结构。小程序中的组件其实相当于网页中的HTML标签&#xff0c;只不过标签…

5年测试路,终于爬到了半山腰,结果碰到00后入场,我该拿什么争,我不想35岁被淘汰......

软件测试是一个付出就有回报的工作&#xff0c;可能很多人会说软件测试就是吃青春饭&#xff0c;然而其他工作又何尝不是&#xff1f;没有哪一家公司养尸位素餐之人&#xff0c;大龄员工有被辞退的&#xff0c;也有没被辞退的。干任何职业&#xff0c;抱着一劳永逸的心态&#…

关于k8s集群备份和恢复工具Velero 的一些笔记整理

写在前面 分享一个k8s集群容灾备份恢复开源工具 Velero博文内容涉及&#xff1a; Velero 的简单介绍Velero 安装下载备份恢复 Demo&#xff0c;以及容灾测试 Demo恢复失败情况分析 理解不足小伙伴帮忙指正 我所渴求的&#xff0c;無非是將心中脫穎語出的本性付諸生活&#xff0…

第九届蓝桥杯省赛 C++ A/B组 - 全球变暖

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?spm1011.2415.3001.5343 &#x1f4da;专栏地址&#xff1a;蓝桥杯题解集合 &#x1f4dd;原题地址&#xff1a;全球变暖 &#x1f4e3;专栏定位&#xff1a;为想参加蓝桥杯的小伙伴整理常考算法题解&#xff0c;祝大家…

分布式架构设计模式

咖啡不冲&#xff0c;你一定会成功 分布式架构设计模式一、什么是设计模式&#xff1f;1.1 设计模式的由来1.2 设计模式有哪些种类1.3 如何学习设计模式二、六大设计原则2.1 开闭原则2.2 单一职责原则2.3 里氏替换原则2.4 迪米特法则2.5 接口隔离原则2.6 依赖倒置原则三、创建型…

websocket原理及简单应用

websocket是什么&#xff1f; 一般做系统开发前后端交互使用最多的就是http协议&#xff0c;但http协议是无状态协议每一次前端发起的请求都认为是一次单独的请求和之前的请求无任何关系&#xff0c;所以我们需要http协议分别用户信息时&#xff0c;就需要使用cookie、session…

Rust学习总结之if,while,loop,for使用

目录 一&#xff1a;if的使用 二&#xff1a;while的使用 三&#xff1a;loop的使用 四&#xff1a;for的使用 本文总结的四种语句&#xff08;if&#xff0c;while&#xff0c;loop&#xff0c;for&#xff09;除了loop&#xff0c;其他的三个在C语言或者Python中都是常见…

DDD系列 - 第1讲 DDD相关概念入门

目录一、引言二、 统一语言Ubiquitous Language三、 三个阶段&#xff08;战略、战术、实现&#xff09;阶段1&#xff1a;战略设计阶段阶段2&#xff1a;战术设计阶段阶段3&#xff1a;技术实现阶段四、限界上下文Bounded Context五、上下文映射Context Map防腐层Anti-Corrupt…

深度学习代码怎么读-小白阶段性思路

深度学习代码怎么读-小白阶段性思路目前思路学习资料读代码工具-chatgpt目前思路 努力上路的小白一枚&#xff0c;麻烦路过的大佬指导一二&#xff0c;同时希望能和大家交流学习~ 和学长、实习老师们交流后的目前思路&#xff1a; 先找到自己研究领域的顶级期刊&#xff0c;…

21 Nacos客户端本地缓存及故障转移

Nacos客户端本地缓存及故障转移 在Nacos本地缓存的时候有的时候必然会出现一些故障&#xff0c;这些故障就需要进行处理&#xff0c;涉及到的核心类为ServiceInfoHolder和FailoverReactor。 本地缓存有两方面&#xff0c;第一方面是从注册中心获得实例信息会缓存在内存当中&a…

AGV机器人出圈:助力产线物流自动化

随着开年档电影《流浪地球2》的热映&#xff0c;里面的四足仿生机器人机械狗“笨笨”、可穿戴的外骨骼机器人等“黑科技”&#xff0c;都让人对机器人的魅力刮目相看&#xff0c;机器人成功“出圈”了&#xff0c;随着智能技术的发展与进步&#xff0c;我们常见的机器人种类越来…

Linux命令之sed

sed&#xff0c;Stream Editor&#xff08;字符流编辑器&#xff09;的缩写&#xff0c;简称流编辑器&#xff0c;是操作、过滤、转换文本内容的工具。 常用功能包括结合正则表达式对文件实现快速的增删改查。 工作原理 sed有2个空间来缓存数据&#xff0c;paattern space&am…

Qt交叉编译环境搭建

环境及版本&#xff1a;Deepin 20.3 Qt 5.12.9 arm编译工具 gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz 1.下载Qt源码&#xff1a;qt-everywhere-src-5.12.9.tar.xz&#xff0c;并解压 2.下载arm编译工具&#xff1a; gcc-linaro-7.5.0-2019.12-x86_64_arm…

央企集团是怎么设置信息化、数字化部门的?

在数字经济大潮中&#xff0c;数字化转型已不是企业的“选修课”&#xff0c;而是关乎企业生存和长远发展的“必修课”。在企业数字化转型中&#xff0c;国有企业特别是中央企业普遍将数字化转型战略作为“十四五”时期业务规划的重要内容之一&#xff0c;数字化能力也成为衡量…

代码随想录【Day31】| 455. 分发饼干、376. 摆动序列、53. 最大子数组和

455. 分发饼干 题目链接 题目描述&#xff1a; 假设你是一位很棒的家长&#xff0c;想要给你的孩子们一些小饼干。但是&#xff0c;每个孩子最多只能给一块饼干。 对每个孩子 i&#xff0c;都有一个胃口值 g[i]&#xff0c;这是能让孩子们满足胃口的饼干的最小尺寸&#xff…

用Docker搭建yolov5开发环境

拉取镜像 sudo docker pull pytorch/pytorch:latest 创建容器 sudo docker run -it -d --gpus "device0" pytorch/pytorch bash 查看所有容器 sudo docker ps -a 查看运行中的容器 sudo docker ps 进入容器 docker start -i 容器ID 将依赖包全都导入到requiremen…

如何将图数据库应用于电影智能推荐

导读 电影&#xff0c;是一种结合视觉与听觉的现代艺术。如今&#xff0c;电影已不单是人们娱乐消遣的生活方式&#xff0c;也逐渐成为国家文化软实力的重要标志之一。据有关数据统计&#xff0c;2021年中国影视行业市场规模达2349亿元&#xff0c;同比增长23.2%&#xff0c;预…

java--IO

IO1.文件流2.常用的文件操作&#xff08;1&#xff09;根据路径构建一个File对象&#xff08;2&#xff09;根据父目录文件子路径构建&#xff08;3&#xff09;根据父目录子路径构建&#xff08;4&#xff09;获取文件相关信息&#xff08;5&#xff09;目录的操作和文件的删除…

计算机图形学07:有效边表法的多边形扫描转换

作者&#xff1a;非妃是公主 专栏&#xff1a;《计算机图形学》 博客地址&#xff1a;https://blog.csdn.net/myf_666 个性签&#xff1a;顺境不惰&#xff0c;逆境不馁&#xff0c;以心制境&#xff0c;万事可成。——曾国藩 文章目录专栏推荐专栏系列文章序一、算法原理二、…

Git 企业级分支提交流程

Git 企业级分支提交流程 首先在本地分支hfdev上进行开发&#xff0c;开发后要经过测试。 如果测试通过了&#xff0c;那么久可以合并到本地分支develop&#xff0c;合并之后hfdev和development应该完全一样。 git add 文件 git commit -m ‘注释’ git checkout develop //切换…