切换到宽版
  • 3493阅读
  • 23回复

[C51]【播放上古卷轴5】51单片机使用FAT32文件系统读取8G SDHC在LCD12864上播放视频 [复制链接]

上一主题 下一主题
离线ningzhiying
 

发帖
10
M币
-429
专家
1
粉丝
2
只看楼主 倒序阅读 使用道具 楼主  发表于: 2016-08-19
51单片机FAT32文件系统读取8G SDHC在LCD12864上播放视频 ,这是我今年五月份在51劳动节当天完成的作品,51劳动节完成51单片机的作品,感觉很有意义的样子。之后由于忙着学习(怕挂科)一直没有时间把这作品发出来给大家看看。正值暑假,把我作品给大家看看,希望能对大家有所帮助。由于不是新手教程,所以很多基础的内容我并做出解释和说明,所以希望大家能谅解。
0.首先看看前辈的作品
51单片机(STC12C5A32S2)性能极限!19264+SD+FAT播放视音频!附视频取模软件|
前辈的代码写的非常规范,有windows程序员的风格,非常值得大家把代码下来看看,我的代码就是在前辈的基础之上改进的。说一说不同之处:

前辈的FATFS支持FAT16和FAT32
我改进后的FATFS只支持FAT32,不过增添了DBR扇区查找函数,以便跳过SD卡的隐藏扇区
LCD选用的是基于KS0108的LCD19264LCD选用的是基于ST7920的LCD12864
SD卡SPI模式是由单片机硬件SPI驱动SD卡SPI模式是由IO口模拟SPI驱动
SD卡驱动不支持SDHC卡编写的SD卡驱动支持SDHC,用8G SD卡测试过,更高容量的没有试过


1.效果视频如下:【建议从2:00看,画面较清晰】



附上源视频,这样大家也可以知道放的是什么(T_T),原视频很燃的
上古卷轴5的官方宣传视频:



由于是12864所以分辨率是硬伤,看不清楚在所难免,对了,视频的BIN文件是用取模软件获取的,取模软件见最下面的附件,不是我编写的,最近一直在练习windows编程,我打算自己写一个,到时有空发给大家。不想自己提取BIN文件的也可以用我已经提取好的BIN文件
上古卷轴5视频BIN文件:

2.作品主要物件如下
STC12C5AXXX单片机                                                            


8G SDHC卡
                            

基于ST7920的LCD12864



3.FAT32文件系统运行流程简介
初始FAT32文件系统--->打开指定路径的FAT文件--->8.3文件名转换--->打开目录并匹配目录的文件--->确认目录的内容--->按顺序读出簇链上的扇区

4.FAT32文件系统代码
这个FAT32代码是在前辈的基础之上加以改进的,删除了一些多余的代码,加入了DBR扇区查找函数,以便跳过SD卡的隐藏扇区,另外每条重要的语句我都加了注释,方便大家理解,不过也可能会有出错的地方,大家以批判的眼光去看吧。没有包含头文件所以不能编译成功,只是用作方便大家查看之用,需要完整工程可以下载下面的附件。
  1. #include "SD.h"
  2. #include"FAT.h"
  3. #define PRINTF             Lcd12864PrintfOne         //打印        参数:字符串指针
  4. #define FATFSDEVICERESET    !sd_reset             //设备复位    无参    返回:BOOL 成功:TRUE
  5. #define FATFSDEVICEINITIAL    !sd_init              //设备初始化    无参    返回:BOOL 成功:TRUE
  6. #define FATFSDEVICEREADBLOCK    !sd_read_sector             //设备读块    参数:DWORD扇区地址,扇区缓存(512B)    返回:BOOL 成功:TRUE
  7. #define FATFSDEVICEWRITEBLOCK    !sd_write_sector         //设备写块    参数:DWORD扇区地址,扇区缓存(512B)    返回:BOOL 成功:TRUE
  8. #define BPB_POINT            BPB_Point
  9. #define BPB_BYTEPERSEC        WORDLE(BPB_POINT[11], BPB_POINT[12])
  10. #define BPB_SECPERCLUS        BYTELE(BPB_POINT[13])
  11. #define BPB_RSVDSECCNT        WORDLE(BPB_POINT[14], BPB_POINT[15])
  12. #define BPB_NUMFATS            BYTELE(BPB_POINT[16])
  13. #define BPB_ROOTENTCNT        WORDLE(BPB_POINT[17], BPB_POINT[18])
  14. #define BPB_TOTSEC16        WORDLE(BPB_POINT[19], BPB_POINT[20])
  15. #define BPB_MEDIA            BYTELE(BPB_POINT[21])
  16. #define BPB_FATSZ16            WORDLE(BPB_POINT[22],BPB_POINT[23])
  17. #define BPB_SECPERTRK        WORDLE(BPB_POINT[24], BPB_POINT[25])
  18. #define BPB_NUMHEADS        WORDLE(BPB_POINT[26], BPB_POINT[27])
  19. #define BPB_HIDDSEC            DWORDLE(BPB_POINT[28], BPB_POINT[29], BPB_POINT[30], BPB_POINT[31])
  20. #define BPB_TOTSEC32        DWORDLE(BPB_POINT[32], BPB_POINT[33], BPB_POINT[34], BPB_POINT[35])
  21. //FAT 32
  22. #define BPB_FATSZ32            DWORDLE(BPB_POINT[36], BPB_POINT[37], BPB_POINT[38], BPB_POINT[39])
  23. #define BPB_EXTFLAGS        WORDLE(BPB_POINT[40], BPB_POINT[41])
  24. #define BPB_FSVER            WORDLE(BPB_POINT[42], BPB_POINT[43])
  25. #define BPB_ROOTCLUS        DWORDLE(BPB_POINT[44], BPB_POINT[45], BPB_POINT[46], BPB_POINT[47])
  26. #define BPB_FSINFO            WORDLE(BPB_POINT[48], BPB_POINT[49])
  27. #define BPB_BKBOOTSEC        WORDLE(BPB_POINT[50], BPB_POINT[51])
  28. //FSInfo
  29. #define FSI_POINT            FSI_Point
  30. #define FSI_LEADSIG            DWORDLE(FSI_POINT[0], FSI_POINT[1], FSI_POINT[2], FSI_POINT[3])
  31. #define FSI_STRUCSIG        DWORDLE(FSI_POINT[4], FSI_POINT[5], FSI_POINT[6], FSI_POINT[7])
  32. #define FSI_FREE_COUNT        DWORDLE(FSI_POINT[488], FSI_POINT[489], FSI_POINT[490], FSI_POINT[491])
  33. #define FSI_NXT_FREE        DWORDLE(FSI_POINT[492], FSI_POINT[493], FSI_POINT[494], FSI_POINT[495])
  34. //BPBSec
  35. #define BPB_SEC             DWORDLE(BPB_POINT[454], BPB_POINT[455], BPB_POINT[456], BPB_POINT[457]);
  36. #define SHORTNAMELENCACHELEN 13              //命名空间大小
  37. #define FAT16MAXPERCHECKCLUS 4             //FAT16最大预处理簇
  38. #define FAT32MAXPERCHECKCLUS 4             //FAT32最大预处理簇
  39. #define UNDWORD union dword
  40. sbit TPIN = P1^0;
  41. struct Byte4 {
  42.     BYTE a;
  43.     BYTE b;
  44.     BYTE c;
  45.     BYTE d;
  46. };
  47. struct Word2 {
  48.     WORD a;
  49.     WORD b;
  50. };
  51. union dword {
  52.     DWORD Data;
  53.     struct Word2 word;
  54.     struct Byte4 byte;
  55. };
  56. struct FATFSTemp13 {
  57.     union dword dworda;
  58.     union dword dwordb;
  59.     WORD worda;
  60.     WORD wordb;
  61.     BYTE bytea;
  62. };
  63. union FATFSStringDataUnion {
  64.     struct FATFSTemp13 Data;    
  65.     BYTE String[SHORTNAMELENCACHELEN];
  66. };
  67. #define g_wBytePerSec 512
  68. #ifndef g_wBytePerSec                      //暂时只支持512byte/扇区的储存介质
  69. WORD  data g_wBytePerSec;
  70. #endif
  71. union FATFSStringDataUnion g_unTemp;                          
  72. #define g_szFileShortName g_unTemp.String        //命名空间复用
  73. #ifndef g_szFileShortName
  74. BYTE  data g_szFileShortName[SHORTNAMELENCACHELEN];
  75. #endif
  76. BOOL  g_IsFATFS = FALSE;
  77. BOOL  g_IsFAT16 = TRUE;
  78. DWORD data g_dwFileFirstClus;
  79. UNDWORD data g_dwFileCurrentClus;
  80. DWORD data g_dwFileSize;
  81. UNDWORD data g_dwFileCurrentSec;
  82. DWORD data g_dwTotalClus;
  83. BYTE  data g_bFileAttrib;
  84. WORD  data g_wRsvdSecCnt;
  85. BYTE  data g_bSecPerClus;
  86. BYTE  data g_bSecPerClusBin;
  87. DWORD data g_dwFirstRootDirNum;
  88. DWORD data g_dwFirstDataSector;
  89. BYTE  data g_bNextBlock;
  90. DWORD data g_dwFATSz;
  91. DWORD data g_dHddsec;
  92. #ifdef _FATFSWRITE_
  93. BYTE  data g_bNumFATs;
  94. WORD  data g_wFSInfoNxtFree;    //FAT32用作 FSInfo指针 FAT16用作最后空扇区搜索
  95. #endif
  96. /*************************************************************************
  97. 函数:AnalysisMBR
  98. 描述:分析MBR,找出BPB的物理地址
  99. *************************************************************************/
  100. WORD AnalysisMBR(LPBYTE BPB_POINT)
  101. {
  102.     if(FATFSDEVICEREADBLOCK(0, BPB_POINT)){
  103.         return BPB_SEC;
  104.     }else{
  105.         g_IsFATFS = FALSE;
  106.         RETURN(FATFS_READ_BPB_SEC_ERROR);
  107.     }    
  108. }
  109. /*************************************************************************
  110. 函数:FATFSShortName
  111. 描述:8.3文件名转换    
  112. *************************************************************************/
  113. BYTE FATFSShortName(BYTE NameLen){
  114.     BYTE ExtLen;
  115.     BYTE Cache[3]={0x20,0x20,0x20}; //ASCII码值代表空格
  116.     for(NameLen=0;g_szFileShortName[NameLen]!='.'&&g_szFileShortName[NameLen]!='\0';++NameLen); //停在'.'的位置
  117.     if(g_szFileShortName[NameLen]!='\0'){
  118.         ++NameLen;
  119.         for(ExtLen=0;g_szFileShortName[NameLen + ExtLen]!='\0';++ExtLen){
  120.                 Cache[ExtLen] = g_szFileShortName[NameLen + ExtLen];
  121.         }
  122.         --NameLen;//回到'.'的位置
  123.     }
  124.     if(NameLen<9){
  125.         for(;NameLen<11;++NameLen){
  126.             g_szFileShortName[NameLen] = ' ';
  127.         }
  128.         NameLen = 8;
  129.         for(;NameLen<11;++NameLen){
  130.             g_szFileShortName[NameLen] = Cache[NameLen-8]; //插入后缀名
  131.         }
  132.         g_szFileShortName[11] = '\0';  //再次插入字符串结束标志
  133.         RETURN(FATFS_OPERATION_SUCCESS);
  134.     }else{
  135.         RETURN(FATFS_NAME_NAME_TOO_LONG);
  136.     }
  137. }
  138. /*************************************************************************
  139. 函数:    InitialFATFS
  140. 描述:    初始化FATFS并获得该FAT信息
  141. *************************************************************************/
  142. BYTE FATFSInitial(LPBYTE BPB_POINT){                                 //g_dwTotalClus 作临时变量
  143.     if(FATFSDEVICERESET()){
  144.         if(FATFSDEVICEINITIAL()){
  145.                if(FATFSDEVICEREADBLOCK(AnalysisMBR(BPB_POINT), BPB_POINT)){
  146.             //if(FATFSDEVICEREADBLOCK(8192, BPB_POINT)){                //获得0扇区上的BPB只支持一个分区了不然要分析MBR
  147.                 if(BPB_POINT[510]!=0x55 && BPB_POINT[510]!=0xAA){        //AA55和55AA都可以,AA55一般为硬盘主引导扇区末尾
  148.                     g_IsFATFS = FALSE;
  149.                     RETURN(FATFS_FS_NOT_SUPPORT);
  150.                 }
  151.                 g_dHddsec = BPB_HIDDSEC; //隐藏扇区数
  152.                 g_wRsvdSecCnt = BPB_RSVDSECCNT; //保留扇区数
  153.                 g_dwFATSz = BPB_FATSZ32; //FAT表所占扇区数
  154.                 g_dwFirstDataSector = g_wRsvdSecCnt + BPB_NUMFATS * g_dwFATSz + g_dwTotalClus + g_dHddsec; //数据区第一个扇区,记得加上隐藏扇区数
  155.                 g_dwTotalClus = BPB_TOTSEC32 - g_dwFirstDataSector + g_dHddsec; //从BPB中获得的总扇区数包括隐藏扇区数
  156.                 g_bSecPerClus = BPB_SECPERCLUS;    //每簇扇区数
  157.                 g_dwTotalClus = (g_dwTotalClus + (g_bSecPerClus/2)) / g_bSecPerClus; //数据区簇数 = 数据扇区数 / 每簇扇区数
  158.                 if(g_dwTotalClus > 65525UL){
  159.                     g_dwFirstRootDirNum = BPB_ROOTCLUS;    //FAT32特有,根目录所在第一个簇簇号,通常为2
  160.                     g_IsFAT16 = FALSE;
  161.                 }else{
  162.                     g_IsFATFS = FALSE;
  163.                     RETURN(FATFS_FATFS_NOT_SUPPORT);
  164.                 }
  165. #ifndef g_wBytePerSec    
  166.                 g_wBytePerSec = BPB_BYTEPERSEC;
  167. #else            //暂时只支持512byte/扇区的储存介质
  168.                 if(g_wBytePerSec != BPB_BYTEPERSEC){
  169.                     RETURN(FATFS_FATFS_NOT_SUPPORT);
  170.                 }
  171. #endif                
  172.                 g_bSecPerClusBin = 0; //取二的幂
  173.                 while(g_bSecPerClus > 1){
  174.                      ++g_bSecPerClusBin;
  175.                      g_bSecPerClus >>= 1;  //每簇扇区数/2
  176.                 }
  177.                 g_dwFileFirstClus = g_dwFirstRootDirNum; //文件开始簇 = 根目录所在首簇 / 扇区号
  178.                 g_bSecPerClus = BPB_SECPERCLUS;    //值已被更改,所以重新赋值
  179.                 g_bNextBlock = 0; //下一块要读的扇区
  180.                 g_IsFATFS = TRUE; //判断为FAT32系统
  181.                 RETURN(FATFS_OPERATION_SUCCESS);
  182.             }else{
  183.                 g_IsFATFS = FALSE;
  184.                 RETURN(FATFS_READ_BPB_SEC_ERROR);
  185.             }
  186.         }else{
  187.             g_IsFATFS = FALSE;
  188.             RETURN(FATFS_DEVICE_INITIAL_ERROR);
  189.         }
  190.     }else{
  191.         g_IsFATFS = FALSE;
  192.         RETURN(FATFS_DEVICE_RESET_ERROR);
  193.     }
  194. }
  195. /*************************************************************************
  196. 函数:    FATFSReadNextBlock
  197. 描述:    按顺序读出簇链上的扇区
  198. *************************************************************************/
  199. BYTE FATFSReadNextBlock(LPBYTE FileCache, LPWORD BlockLen){
  200.     if(g_bNextBlock>=g_bSecPerClus){ //已经读的扇区数>=每簇扇区数
  201.         //跳到下一个簇
  202.             //FAT32
  203.             if(g_unTemp.Data.bytea == 0){    //不是连续的簇链
  204.                 g_dwFileCurrentClus.Data<<=2;  //对FAT32而言,每个簇占据4个字节
  205.                 g_unTemp.Data.dworda.Data = (g_dwFileCurrentClus.Data / g_wBytePerSec) + g_wRsvdSecCnt + g_dHddsec;//该文件所在的FAT扇区数,加上隐藏扇区
  206.                 if(!FATFSDEVICEREADBLOCK(g_unTemp.Data.dworda.Data, FileCache)){ //对相应的FAT扇区进行读块
  207.                     *BlockLen = 0; //长度为0
  208.                     RETURN(FATFS_READ_FAT_ERROR); //返回读取FAT表失败
  209.                 }
  210.                 g_unTemp.Data.worda = g_dwFileCurrentClus.Data%g_wBytePerSec; //文件所在FAT表的表项数
  211.                 g_dwFileCurrentClus.byte.a = FileCache[g_unTemp.Data.worda+3];    //获取文件下一簇簇号    C51:大端
  212.                 g_dwFileCurrentClus.byte.b = FileCache[g_unTemp.Data.worda+2];    //获取文件下一簇簇号    X86:小端
  213.                 g_dwFileCurrentClus.byte.c = FileCache[g_unTemp.Data.worda+1];  //获取文件下一簇簇号
  214.                 g_dwFileCurrentClus.byte.d = FileCache[g_unTemp.Data.worda];    //获取文件下一簇簇号
  215.                 g_unTemp.Data.dwordb.Data = g_dwFileCurrentClus.Data;  //得到文件下一簇簇号
  216.                 while(g_unTemp.Data.bytea < FAT32MAXPERCHECKCLUS){       //FATFS最大预处理簇
  217.                     g_dwFileCurrentSec.Data = g_unTemp.Data.dwordb.Data << 2;  //文件下一簇号 X 每簇号占据4字节
  218.                     if(g_unTemp.Data.dworda.Data == ((g_dwFileCurrentSec.Data / g_wBytePerSec) + g_wRsvdSecCnt)){  //如果文件的下一簇的FAT表项数还在原来的扇区内
  219.                         g_unTemp.Data.worda = g_dwFileCurrentSec.Data % g_wBytePerSec; //获取表项数
  220.                         g_dwFileCurrentSec.byte.a = FileCache[g_unTemp.Data.worda+3];  //获取文件下下一簇簇号
  221.                         g_dwFileCurrentSec.byte.b = FileCache[g_unTemp.Data.worda+2];  //获取文件下下一簇簇号
  222.                         g_dwFileCurrentSec.byte.c = FileCache[g_unTemp.Data.worda+1];  //获取文件下下一簇簇号
  223.                         g_dwFileCurrentSec.byte.d = FileCache[g_unTemp.Data.worda];       //获取文件下下一簇簇号
  224.                         if(g_dwFileCurrentSec.Data == g_unTemp.Data.dwordb.Data + 1){ //如果下下一簇刚好接着下一簇
  225.                             g_unTemp.Data.bytea++;
  226.                             g_unTemp.Data.dwordb.Data = g_dwFileCurrentSec.Data;  //下一簇定义为下下一簇
  227.                         }else{
  228.                             break;
  229.                         }
  230.                     }else{
  231.                         break;
  232.                     }
  233.                 }
  234.                 if((g_dwFileCurrentClus.Data&=0x0FFFFFFFUL) > 0x0FFFFFEFUL){ //起始簇(名义上)或连续簇的首簇    【备注1】:见末尾
  235.                     *BlockLen = 0;                      //保留簇 坏簇 文件完
  236.                     if(g_dwFileCurrentClus.Data < 0x0FFFFFF6UL){
  237.                         RETURN(FATFS_RETAINED_CLUSTER); //保留簇
  238.                     }else if(g_dwFileCurrentClus.Data == 0x0FFFFFF7UL){
  239.                         RETURN(FATFS_BAD_CLUSTER); //坏簇
  240.                     }else{
  241.                         RETURN(FATFS_LAST_CLUSTER); //最后一簇
  242.                     }
  243.                 }
  244.             }else{ //是连续的簇链
  245.                 g_dwFileCurrentClus.Data++; //文件起始(变化)簇号   接着下一簇
  246.                 g_unTemp.Data.bytea--; //连续簇数减1
  247.             }    
  248.         g_dwFileCurrentSec.Data = ((g_dwFileCurrentClus.Data - 2)<<g_bSecPerClusBin) + g_dwFirstDataSector;    //更新文件起始扇区号
  249.         g_bNextBlock = 0; //已经读的扇区数 = 0
  250.     }
  251.     //当前簇
  252.     if(g_bNextBlock<g_bSecPerClus){    //如果要读的扇区数 < 每簇扇区数
  253.         if(BlockLen){      //对常量而言,地址为0
  254.             if(g_dwFileSize){ //文件大小不为0
  255.                 if(g_dwFileSize >= g_wBytePerSec){  //文件大小 >= 每扇区的字节数
  256.                     *BlockLen = g_wBytePerSec;        //实际读出长度 = 每扇区的字节数
  257.                     g_dwFileSize -= g_wBytePerSec;    //文件大小减少一个扇区
  258.                 }else{
  259.                     *BlockLen = g_dwFileSize;  //实际读出长度 = 文件大小
  260.                     g_dwFileSize = 0; //文件大小更新为0
  261.                 }
  262.             }else{
  263.                 *BlockLen = 0; //实际读出的长度为0
  264.                 RETURN(FATFS_END_OF_FILE);    //返回文件结束
  265.             }
  266.         }
  267.         if(!FATFSDEVICEREADBLOCK(g_dwFileCurrentSec.Data, FileCache)){  //如果对文件起始(变化)读块不成功
  268.             *BlockLen = 0;     //实际长度为0
  269.             RETURN(FATFS_READ_SEC_ERROR); //返回读扇区失败
  270.         }
  271.         ++g_dwFileCurrentSec.Data;    //加1为文件下一个扇区
  272.         ++g_bNextBlock;    //已经读的扇区加1
  273.         RETURN(FATFS_OPERATION_SUCCESS);  //返回操作成功
  274.     }else{
  275.         RETURN(FATFS_READ_BLOCK_OVERRANG);    //已经完成读块
  276.     }        
  277. }
  278. /*************************************************************************
  279. 函数:    FATFSMatchAttrib
  280. 描述:    确认目录的内容
  281. *************************************************************************/
  282. BYTE FATFSValidateAttrib(BYTE Attr){
  283.     if((Attr&ATTR_LONG_NAME_MASK)==ATTR_LONG_NAME){
  284.         return ATTR_LONG_NAME;
  285.     }else{
  286.         if((Attr&(ATTR_DIRECTORY|ATTR_VOLUME_ID))==0x00){
  287.             //文件
  288.             return ATTR_FILE;
  289.         }else if((Attr&(ATTR_DIRECTORY|ATTR_VOLUME_ID))==ATTR_DIRECTORY){
  290.             //目录
  291.             return ATTR_DIRECTORY;
  292.         }else if((Attr&(ATTR_DIRECTORY|ATTR_VOLUME_ID))==ATTR_VOLUME_ID){
  293.             //卷标
  294.             return ATTR_VOLUME_ID;
  295.         }else{
  296.             //什么都不是[暂且就识别这几个文件属性]
  297.             return ATTR_NOTHING;
  298.         }
  299.     }
  300. }
  301. /*************************************************************************
  302. 函数:    FATFSMatchDir
  303. 描述:    匹配FAT目录项中的名称并读取有关数据
  304. *************************************************************************/
  305. BYTE FATFSMatchDir(LPBYTE DirCache, BOOL IsFile){
  306.     WORD j;
  307.     BYTE k;
  308.     for(j=0;j<g_wBytePerSec;j+=32){    //每个扇区字节数,每个目录项的大小为32字节
  309.         if(DirCache[j] == 0xE5){    //说明该目录项被使用过,但已被删除
  310.             continue;    //继续查找
  311.         }
  312.         if(DirCache[j] == 0x00){    //说明该目录项从未被使用过,由于目录项的分配是按顺序,故说明从这开始后面都为0X00
  313.             RETURN(FATFS_END_OF_FATDIR);
  314.         }
  315.         for(k=0;k<11;++k){
  316.             if(DirCache[j+k] == g_szFileShortName[k]){  //将该目录项的前11个ASCII码与数组进行对比
  317.                 continue;  //继续比较其余字符
  318.             }else{
  319.                 break; //跳出
  320.             }
  321.         }
  322.         if(k==11){
  323.             g_bFileAttrib = BYTELE(DirCache[j+11]);     //文件属性     0x01-文件 0x01-只读 0x02-隐藏 0x04-系统文件 0x08-卷标 0x0f-表示该目录项为长文件名目录项 0x10-目录 0x20-存档                                                    
  324.             if(IsFile){    //文件                                                                                
  325.                 if(FATFSValidateAttrib(g_bFileAttrib)!=ATTR_FILE){ //查看是不是文件
  326.                     RETURN(FATFS_FILE_NOT_FOUND);
  327.                 }
  328.             }else{
  329.                 if(FATFSValidateAttrib(g_bFileAttrib)!=ATTR_DIRECTORY){//查看是不是目录
  330.                     RETURN(FATFS_DIRECTORY_NOT_FOUND);
  331.                 }
  332.             }
  333.             g_dwFileFirstClus = DWORDLE(DirCache[j+26], DirCache[j+27], DirCache[j+20], DirCache[j+21]); //文件开始簇
  334.             g_dwFileSize = DWORDLE(DirCache[j+28], DirCache[j+29], DirCache[j+30], DirCache[j+31]);    //文件内容大小字节数
  335.             RETURN(FATFS_OPERATION_SUCCESS);
  336.         }
  337.     }
  338.     RETURN(FATFS_CACHE_NOMATCH);  
  339. }
  340. /*************************************************************************
  341. 函数:    FATFSOpenDir
  342. 描述:    打开当前目录 并匹配该目录的文件
  343. *************************************************************************/
  344. BYTE FATFSDirOperation(LPBYTE DataCache, BOOL IsFile){
  345.     BYTE k;
  346.     do{    //只执行一次的“循环” 最优化代码
  347.         if(g_dwFileFirstClus == g_dwFirstRootDirNum){ //如果是根目录
  348.                 g_dwFileCurrentClus.Data = g_dwFirstRootDirNum;    //FAT32根目录项表当文件打开,赋值根目录所在的簇号
  349.         }else{ //不是根目录
  350.             g_dwFileCurrentClus.Data =     g_dwFileFirstClus;
  351.         }
  352.         g_dwFileCurrentSec.Data = ((g_dwFileCurrentClus.Data - 2)<<g_bSecPerClusBin) + g_dwFirstDataSector;     //文件开始扇区号(减2是因为FAT32是从2开始的)
  353.         g_bNextBlock = 0; //要读的扇区块为0
  354.         while(FATFSReadNextBlock(DataCache,0)==FATFS_OPERATION_SUCCESS){  //按顺序读出簇链上的扇区,读一块
  355.             k = FATFSMatchDir(DataCache, IsFile); //将读到的数据与数组进行比较,进行匹配查找
  356.             if(k == FATFS_CACHE_NOMATCH){  //没有找到
  357.                 continue; //再次循环
  358.             }else{
  359.                 break; //跳出循环
  360.             }
  361.         }
  362.     }while(0);
  363.     if(k != FATFS_OPERATION_SUCCESS){ //如果没有匹配成功
  364.         if(IsFile){    //是文件
  365.             RETURN(FATFS_FILE_NOT_FOUND);
  366.         }else{ //是文件夹(目录)
  367.             RETURN(FATFS_DIRECTORY_NOT_FOUND);
  368.         }
  369.     }
  370.     g_dwFileCurrentClus.Data = g_dwFileFirstClus; //文件数据开始簇
  371.     g_dwFileCurrentSec.Data = ((g_dwFileCurrentClus.Data - 2)<<g_bSecPerClusBin) + g_dwFirstDataSector;    //文件起始扇区号
  372.     g_bNextBlock = 0; //要读的扇区数
  373.     g_unTemp.Data.bytea = 0; //簇链的连续簇数为0
  374.     RETURN(FATFS_OPERATION_SUCCESS);
  375. }
  376. /*************************************************************************
  377. 函数:    FATFSOpen
  378. 描述:    打开指定路径的FAT文件
  379. *************************************************************************/
  380. BYTE FATFSOpen(LPBYTE DataCache, LPBYTE szPath){
  381. //e.g.    "\ABC\DEF\BAD.BIN" "ABC\DEF\BAD.BIN" "\BAD.BIN" "BAD.BIN"
  382.     BYTE i;
  383.     if(g_IsFATFS){
  384.         i = 0;
  385.         if(szPath[0] == '\\'){    //查看目标路径的首字符是否是'\'
  386.             szPath += 1; //跳过'\'
  387.         }
  388.         while(1){
  389.             szPath = szPath + i;
  390.             for(i=0;szPath[i]!='\0'&&szPath[i]!='\\'&&i<SHORTNAMELENCACHELEN-1;++i){ //将名称转移至数组g_szFileShortName
  391.                 g_szFileShortName[i] = szPath[i];
  392.             }
  393.             if(i==SHORTNAMELENCACHELEN-1){
  394.                 RETURN(FATFS_FILENAME_ERROR);
  395.             }else{
  396.                 g_szFileShortName[i] = '\0';  //插入字符串结束标志
  397.                 if(FATFSShortName(i) != FATFS_OPERATION_SUCCESS){ //将8.3文件名进行转换                
  398.                     break;
  399.                 }
  400.                 if(szPath[i]=='\0'){ //是否文件末尾
  401.                     if(FATFSDirOperation(DataCache, TRUE) == FATFS_OPERATION_SUCCESS){  //在目录项中匹配文件
  402.                         RETURN(FATFS_OPERATION_SUCCESS);
  403.                     }else{
  404.                         break;
  405.                     }
  406.                 }else{ //文件夹末尾
  407.                     ++i;    //跳过"\"
  408.                     if(FATFSDirOperation(DataCache, FALSE) == FATFS_OPERATION_SUCCESS){ //在目录项中匹配文件夹
  409.                         continue;  //进行下一次的文件/文件夹名查找
  410.                     }else{
  411.                         break;
  412.                     }
  413.                 }
  414.             }
  415.         }
  416.         RETURN(FATFS_OPEN_FILE_FAILED);
  417.     }
  418.     RETURN(FATFS_FAT_REQUIRE_INITIAL);
  419. }
  420. /*************************************************************************
  421. 函数:    FATFSDebugErrLvlMsg
  422. 描述:    输出FAT_ERROR_LEVEL定义到屏幕 定义_DEBUG_生效 RETURN 宏自动调用
  423. *************************************************************************/
  424. #ifdef _DEBUG_
  425. void FATFSDebugErrLvlMsg(BYTE ErrLvl){
  426.     switch(ErrLvl){
  427.         case FATFS_OPERATION_SUCCESS:
  428.             PRINTF("操作成功");
  429.             break;
  430.         case FATFS_DEVICE_RESET_ERROR:
  431.             PRINTF("SD复位错误");
  432.             break;
  433.         case FATFS_DEVICE_INITIAL_ERROR:
  434.             PRINTF("初始化成功");
  435.             break;
  436.         case FATFS_READ_BPB_SEC_ERROR:
  437.             PRINTF("读取BPB扇区错误");
  438.             break;
  439.         case FATFS_FATFS_NOT_SUPPORT:
  440.             PRINTF("不支持FATFS");
  441.             break;
  442.         case FATFS_FS_NOT_SUPPORT:
  443.             PRINTF("FATFS_FS_NOT_SUPPORT");
  444.             break;
  445.         case FATFS_OPERATION_MODE_ERROR:
  446.             PRINTF("模式选择错误");
  447.             break;
  448.         case FATFS_READ_FSI_SEC_ERROR:
  449.             PRINTF("FATFS_READ_FSI_SEC_ERROR");
  450.             break;
  451.         case FATFS_WRITE_FSI_SEC_ERROR:
  452.             PRINTF("FATFS_WRITE_FSI_SEC_ERROR");
  453.             break;
  454.         case FATFS_READ_DIR_ERROR:
  455.             PRINTF("FATFS_READ_DIR_ERROR");
  456.             break;
  457.         case FATFS_FAT_REQUIRE_INITIAL:
  458.             PRINTF("FATFS_FAT_REQUIRE_INITIAL");
  459.             break;
  460.         case FATFS_FILENAME_ERROR:
  461.             PRINTF("FATFS_FILENAME_ERROR");
  462.             break;
  463.         case FATFS_FILE_NOT_FOUND:
  464.             PRINTF("FATFS_FILE_NOT_FOUND");
  465.             break;
  466.         case FATFS_DIRECTORY_NOT_FOUND:
  467.             PRINTF("FATFS_DIRECTORY_NOT_FOUND");
  468.             break;
  469.         case FATFS_END_OF_FATDIR:
  470.             PRINTF("FATFS_END_OF_FATDIR");
  471.             break;
  472.         case FATFS_CACHE_NOMATCH:
  473.             PRINTF("FATFS_CACHE_NOMATCH");
  474.             break;
  475.         case FATFS_READ_FAT_ERROR:
  476.             PRINTF("FATFS_READ_FAT_ERROR");
  477.             break;
  478.         case FATFS_WRITE_FAT_ERROR:
  479.             PRINTF("FATFS_WRITE_FAT_ERROR");
  480.             break;
  481.         case FATFS_UPDATE_FSI_ERROR:
  482.             PRINTF("FATFS_UPDATE_FSI_ERROR");
  483.             break;
  484.         case FATFS_READ_SEC_ERROR:
  485.             PRINTF("FATFS_READ_SEC_ERROR");
  486.             break;
  487.         case FATFS_WRITE_SEC_ERROR:
  488.             PRINTF("FATFS_WRITE_SEC_ERROR");
  489.             break;
  490.         case FATFS_CLUS_SEC_ERROR:
  491.             PRINTF("FATFS_CLUS_SEC_ERROR");
  492.             break;
  493.         case FATFS_RETAINED_CLUSTER:
  494.             PRINTF("FATFS_RETAINED_CLUSTER");
  495.             break;
  496.         case FATFS_BAD_CLUSTER:
  497.             PRINTF("FATFS_BAD_CLUSTER");
  498.             break;
  499.         case FATFS_READ_BLOCK_OVERRANG:
  500.             PRINTF("FATFS_READ_BLOCK_OVERRANG");
  501.             break;
  502.         case FATFS_WRITE_BLOCK_OVERRANG:
  503.             PRINTF("FATFS_WRITE_BLOCK_OVERRANG");
  504.             break;
  505.         case FATFS_END_OF_FILE:
  506.             PRINTF("FATFS_END_OF_FILE");
  507.             break;
  508.         case FATFS_REQUIRE_EMPTYCLUS_ERROR:
  509.             PRINTF("FATFS_REQUIRE_EMPTYCLUS_ERROR");
  510.             break;
  511.         case FATFS_LAST_CLUSTER:
  512.             PRINTF("FATFS_LAST_CLUSTER");
  513.             break;
  514.         case FATFS_EMPTYCLUS_NOT_FOUND:
  515.             PRINTF("FATFS_EMPTYCLUS_NOT_FOUND");
  516.             break;
  517.         case FATFS_CACHE_NOEMPTYDIR:
  518.             PRINTF("FATFS_CACHE_NOEMPTYDIR");
  519.             break;
  520.         case FATFS_FOUND_EMPTYCLUS_ERROR:
  521.             PRINTF("FATFS_FOUND_EMPTYCLUS_ERROR");
  522.             break;
  523.         case FATFS_NAME_EXT_TOO_LONG:
  524.             PRINTF("后缀太长");
  525.             break;
  526.         case FATFS_NAME_NAME_TOO_LONG:
  527.             PRINTF("命名太长");
  528.             break;
  529.         case FATFS_OPEN_FILE_FAILED:
  530.             PRINTF("打开文件错误");
  531.             break;
  532.     }
  533. }
  534. #endif


5.附件下载
取模软件:
KEIL工程包:
说一下,由于硬件的不同,所以我的工程在大家的电路上可能不能运行,所以仅作为参考,希望大家都能有所收获。
6.SD卡DBR扇区查找
1.如图为SD卡物理0扇区主引导扇区MBR


MBR由446个字节的引导代码、64个字节的主分区(4个)表及两个字节的签名值“55AA”组成。
不知道为什么SD卡的前446字节全是0,可能是由于SD卡不存在系统引导问题吧,另外声明一下,我的8G SD卡是在windows操作系统格式化为FAT32文件系统的。
这里主要关注的是我用红框围起来的4个字节的值 00 00 20 00 (读的顺序按照大端小端),这个值就是文件系统开始的物理扇区号,把十六进制0X2000转换为十进制就是8192,也就是说文件系统开始的物理扇区号是8192,如下图:


这样就找到DBR扇区也就是文件系统开始扇区又可以叫做逻辑0扇区,读取这个扇区的值就可以获得如每扇区字节数,每簇扇区数等必要的信息了。
这也就是DBR扇区查找函数的原理。



[ 此帖被ningzhiying在2016-08-19 16:54重新编辑 ]
本文内容包含图片或附件,获取更多资讯,请 登录 后查看;或者 注册 成为会员获得更多权限
本帖最近打赏记录:共9条打赏M币+29
无聊到处逛 M币 +3 謝謝分享 2016-08-26
kinneng M币 +5 謝謝分享 2016-08-22
jjbboox M币 +3 優秀文章 2016-08-21
jpdd521 M币 +3 搞不搞STM32,搞的话赞助5片STM32F103核心板PCB(QQ群299202988赞助)以及1块STM32F401开发板(个人赞助) 2016-08-20
无语·回忆 M币 +3 不错,支持一下 2016-08-20
lisingch M币 +3 原創內容 2016-08-20
jjjiiiinnnnn M币 +3 很不错,要是带灰度的视频就很有感觉了 2016-08-19
2545889167 M币 +3 真的很强,不过给楼主个建议,换个视频吧,图像稍微简单点的,不然这样放出来,效果太不好了 2016-08-19
zengming00 M币 +3 51单片机也能放出这种音效? 2016-08-19
ahyu99 M币 +3 厉害,值得鼓励! 2016-08-19
离线ahyu99

发帖
1489
M币
3766
专家
5
粉丝
29
只看该作者 1楼 发表于: 2016-08-19
厉害,值得鼓励!
离线zengming00

发帖
152
M币
944
专家
5
粉丝
14
只看该作者 2楼 发表于: 2016-08-19
51单片机也能放出这种音效?
离线ningzhiying

发帖
10
M币
-429
专家
1
粉丝
2
只看该作者 3楼 发表于: 2016-08-19
回 zengming00 的帖子
zengming00:51单片机也能放出这种音效? (2016-08-19 15:56) 回 zengming00 的帖子

暂止没有音乐,只是视频,另外用的是1T单片机STC12C5A32S2芯片,外接晶振频率是22.1184MZ,不是纯粹的89C51


离线2545889167

发帖
11067
M币
26837
专家
120
粉丝
4344
只看该作者 4楼 发表于: 2016-08-19
真的很强,不过给楼主个建议,换个视频吧,图像稍微简单点的,不然这样放出来,效果太不好了
离线ningzhiying

发帖
10
M币
-429
专家
1
粉丝
2
只看该作者 5楼 发表于: 2016-08-19
回 2545889167 的帖子
2545889167:真的很强,不过给楼主个建议,换个视频吧,图像稍微简单点的,不然这样放出来,效果太不好了 (2016-08-19 16:15) 回 2545889167 的帖子

确实是效果不太好。。。不过重在原理嘛 :)
离线jjjiiiinnnnn

发帖
190
M币
909
专家
2
粉丝
10
只看该作者 6楼 发表于: 2016-08-19
高手呀,可以发个视频链接吗?手机浏览器点不开视频
离线lxa0

发帖
1021
M币
402
专家
9
粉丝
37
只看该作者 7楼 发表于: 2016-08-19
厉害,值得鼓励!

发帖
4320
M币
3403
专家
11
粉丝
77
只看该作者 8楼 发表于: 2016-08-20
不错,支持一下
离线jpdd521

发帖
18875
M币
232
专家
15
粉丝
307
只看该作者 9楼 发表于: 2016-08-20
搞不搞STM32呢?搞的话赞助5片STM32F103核心板裸板PCB(QQ群299202988全体成员赞助)以及1块STM32F401带原件开发板(本人个人赞助)
刚应EFM32G222赞助商提供DIP(兼容51)版PCB两块,EFM32TG110核心板(带散件)一块。
STM8L152开发板PCB一块。
以上为免费内容。

以下为特约赞助成本价提供:
1.8寸彩屏CSTN液晶2元(包含转接板),限5套。
1.8寸彩屏TFT液晶 4元(包含转接板),本帖作者备注信息,赠送可配套升压板。限2套。

所有赞助均为QQ群:299202988,全体成员特约赞助!
快速回复
限80 字节
“新手上路”发帖需审核后才能显示(请认真发帖),达到数码9级后取消此限制
 
上一个 下一个