OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 16296|回复: 21

FAT表压缩(利用FAT表的连贯性,压缩FAT表.实现MP3的快进和快退)

[复制链接]

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165309
金钱
165309
注册时间
2010-12-1
在线时间
2108 小时
发表于 2011-1-11 01:17:33 | 显示全部楼层 |阅读模式

 这两天在弄VS1053的mp3,VS1053可以支持所有格式的快进和快退,虽然VS1053很强大,但是如果单片机跟不上,那么其强大的优势就体现不出来.现在我的已经可以正常和快速播放各种VS1053支持的格式(MIDI未测).今天在弄VS1053的快进和快退,但是由于文件系统的链式结构的特殊性,实现向下访问很容易,但是如果要实现向上访问,那么久比较麻烦了.具体原因大家如果了解FAT结构就清楚了.
现在要做快进怎么办呢?一般的方法就是把整个文件的FAT表拷贝下来.这样虽然可以随机访问,但是如果文件比较大的话,就会遇到RAM不够的问题了.假设我们一个簇是30K大小,那么一首30M大小的歌曲 要1000个簇,我们也就需要1000个u32型数据来存储这个链表.占用了4K的sram.对于AVR来说,这无疑是不行的.当然对于我用的STM32还是可以的. 但是如果一个300M的歌曲合集呢?所以这种方法耗资源严重.
仔细分析FAT的链式结构,可以得出结论:后一个簇一般会大于前一个簇,,而且一般是大1.根据这个原理,这样我们就可以用较少的RAM来COPY整个FILE的Cluster了.
以下是我今天想到的方法:
//tinyFAT表结构
//利用FAT表的一致性,一般后一个簇比前一个簇只是大一.
//将FAT表压缩成小的tinyFAT表   
typedef __packed struct 
{
#define Fat_Table_Size 10        //tinyFAT表大小
#define Fat_Head_Size  10  //tinyFAT表头大小
u8  Fat_Head_Pos;                //tinyFAT表的上一个表头位置
u32 Fat_Base_Head[Fat_Head_Size];//tinyFAT表的 表头数组
u32 Fat_Base_Tab[Fat_Table_Size];//文件的tinyFAT基址表
u16 Fat_Base_Len[Fat_Table_Size];//基址偏移量
u8  Fat_Over;                    //文件的tinyFAT表是否全部读出标记位   
}FAT_TABLE;  
上面的结构体利用两个数组Fat_Base_Tab和Fat_Base_Len分别存放FAT表的基址和这个基址所管辖的长度.比如,一首歌曲如果从首簇到最后一个簇都是连续的,那么我用一个u32的字节和一个u16的字节就可以表示整个歌曲的FAT表了(歌曲不能大于65536个簇),Fat_Table_Size 表示的一个小FATtinyTab所包含的不连续簇的个数,这里设置为10个.也就是一个tinyFAT最多可以提供十个基址.但是我们的歌曲可能不止10个不连续的簇(事实上我现在碰到基本都是一个基址就够了),这样我可以重新开辟一个新的tinyFAT,来满足多基址的需求,在开辟新的tinyFAT的时候,把先前的tinyFAT头保存在Fat_Base_Head里面,这是为了方便向上查找.然后从最后一个cluster开始,COPY余下的FAT到tinyFAT,直到整个文件COPY完,此时置位Fat_Over.表示整个文件的FAT copy 完了.这样对于任意大小的文件,我都可以向下查找.但是向上查找只限于Fat_Table_Size*Fat_Head_Size 个不同基址的范围.这里是100.对一般应用,这已经是足够了的.

贴上现在在用的代码.已经移植到VS1053的MP3上,后续补充测试结果.现在正常播放没问题.证明前向访问是没有问题的.
个人一时头脑发热,难免有误,望各位网友指正.

//cluster:文件的首簇
//将FAT表,从头到尾COPY过来,如果没COPY完,则Fat_Over=0,否则为1
//正点原子@HYW
//091119
void Copy_Fat_Table(unsigned long cluster)
{
u32 bcluster; 
u8 fat_base;//0~Fat_Table_Size  
FAT_TAB.Fat_Over=0;    
    for(fat_base=0;fat_base<Fat_Table_Size;fat_base++)
{
FAT_TAB.Fat_Base_Tab[fat_base]=0;
FAT_TAB.Fat_Base_Len[fat_base]=0;//全部清空
}
fat_base=0;
bcluster=cluster;//起始簇,不能丢失的.
FAT_TAB.Fat_Base_Tab[fat_base]=bcluster;
FAT_TAB.Fat_Base_Len[fat_base]=1;//有1个数据了,就是最新的bcluster   
while(1)
{
    bcluster=FAT_NextCluster(bcluster);    
if((FAT32_Enable==0&&bcluster==0xffff)||bcluster==0x0ffffff8||bcluster==0x0fffffff)//文件结束 
{
FAT_TAB.Fat_Over=1;//文件COPY结束
break; 
}   
if(bcluster-FAT_TAB.Fat_Base_Tab[fat_base]-FAT_TAB.Fat_Base_Len[fat_base]!=0)//是否满足偏移条件
{
fat_base++;//启用下一个BASE
if(fat_base>=Fat_Table_Size)//超出了缓冲区范围,文件太大了/磁盘太零散了!!!
{
FAT_TAB.Fat_Over=0;//文件COPY结束
break; 
}
FAT_TAB.Fat_Base_Tab[fat_base]=bcluster;
FAT_TAB.Fat_Base_Len[fat_base]=1;//有1个数据了,就是最新的bcluster
}else FAT_TAB.Fat_Base_Len[fat_base]++;//基址偏移量增加    
}
//监控用
printf("Fat_Over:%d\n",FAT_TAB.Fat_Over);
printf("Fat_Head_Pos:%d\n",FAT_TAB.Fat_Head_Pos);
printf("Fat_Base_Tab[0]:%d\n",FAT_TAB.Fat_Base_Tab[0]);
printf("Fat_Base_Tab[1]:%d\n",FAT_TAB.Fat_Base_Tab[1]);
printf("Fat_Base_Tab[2]:%d\n",FAT_TAB.Fat_Base_Tab[2]);  
}     
//得到cluster的上一个簇号
//正点原子@HYW
//091119
u32 FatTab_Prev_Cluster(unsigned long cluster)
{
u8 t;  
u32 tempclust;
RSTP:
for(t=0;t<Fat_Table_Size;t++)
{
if(cluster<=(FAT_TAB.Fat_Base_Tab[t]+FAT_TAB.Fat_Base_Len[t]-1)&&cluster>=FAT_TAB.Fat_Base_Tab[t])break;//在这个BASE内
}   
if(cluster==FAT_TAB.Fat_Base_Tab[t])//是在BASE,但是是第一个
{  
if(t==0)//这份FAT表 全部找完还没找到
{       
if(FAT_TAB.Fat_Head_Pos>0)//不超过范围
{
FAT_TAB.Fat_Head_Pos--;
tempclust=FAT_TAB.Fat_Base_Head[FAT_TAB.Fat_Head_Pos];//拷贝上一个tinyFAT表的表头
}else return cluster;   //无法继续向上
Copy_Fat_Table(tempclust);//COPY 上一个tinyFAT表
goto RSTP;
//return FAT_TAB.Fat_Base_Tab[Fat_Table_Size-1]+FAT_TAB.Fat_Base_Len[Fat_Table_Size-1]-1;//返回上一个簇号  
}   
return FAT_TAB.Fat_Base_Tab[t-1]+FAT_TAB.Fat_Base_Len[t-1]-1;//上一个簇号
}else return --cluster;//返回上一个簇   
}  
//得到cluster的下一个簇号
//正点原子@HYW
//091119
u32 FatTab_Next_Cluster(unsigned long cluster)
{
u8 t;  
RESN:
for(t=0;t<Fat_Table_Size;t++)
{
if(cluster<=(FAT_TAB.Fat_Base_Tab[t]+FAT_TAB.Fat_Base_Len[t]-1)&&cluster>=FAT_TAB.Fat_Base_Tab[t])break;//在这个BASE内
}   
if(cluster==FAT_TAB.Fat_Base_Tab[t]+FAT_TAB.Fat_Base_Len[t]-1)//是在BASE,但是是最后一个了
{  
if((t+1)==Fat_Table_Size)//全部找完还没找到
{   
if(FAT_TAB.Fat_Over)return 0x0ffffff8;//文件结束了  
if(FAT_TAB.Fat_Head_Pos<Fat_Head_Size-1)//不超过范围
{   
FAT_TAB.Fat_Base_Head[FAT_TAB.Fat_Head_Pos]=FAT_TAB.Fat_Base_Tab[0];//拷贝当前tinyFAT表的第一个簇
FAT_TAB.Fat_Head_Pos++;
}
Copy_Fat_Table(cluster);//COPY 余下的FAT表
goto RESN;

if(FAT_TAB.Fat_Base_Len[t+1]==0)return 0x0ffffff8;//没有后续簇了
return FAT_TAB.Fat_Base_Tab[t+1];//下一个簇号
}else return ++cluster;//返回下一个簇   
}

//读下一簇簇号
//Return the cluster number of next cluster of file
//Suitable for system which has limited RAM     
unsigned long FAT_NextCluster(unsigned long cluster)
{
DWORD sector;
DWORD offset;  

if(FAT32_Enable)offset = cluster/128;//FAT32的FAT表中,用四个字节表示一个簇地址.512/4=128
else offset = cluster/256;  //FAT16的FAT表中,用两个字节表示一个簇地址.512/2=256
if(cluster<2)return 0x0ffffff8;  //簇0,1不能用于存放
sector=FirstFATSector+offset;//计算该簇实际所在扇区

if(SD_ReadSingleBlock(sector,fat_buffer))return 0x0ffffff8;//读取FAT表,发生错误是返回0x0ffffff8
if(FAT32_Enable)
{
offset=cluster%128;//计算在扇区内的偏移
sector=((unsigned long *)fat_buffer)[offset];//u32
}
else
{
offset=cluster%256;//计算在扇区内的偏移
sector=((unsigned short *)fat_buffer)[offset];//u16
}    
return (unsigned long)sector;//return the cluste number
}

 
  

我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

98

主题

408

帖子

3

精华

金牌会员

Rank: 6Rank: 6

积分
1280
金钱
1280
注册时间
2010-12-14
在线时间
0 小时
发表于 2011-1-11 02:14:35 | 显示全部楼层
回复 支持 反对

使用道具 举报

13

主题

32

帖子

0

精华

初级会员

Rank: 2

积分
122
金钱
122
注册时间
2011-1-20
在线时间
5 小时
发表于 2011-2-9 17:43:36 | 显示全部楼层
看的头大,跟着原子哥你的手册链接过来的,FAT部分的程序号恶心呐~~~
回复 支持 反对

使用道具 举报

98

主题

408

帖子

3

精华

金牌会员

Rank: 6Rank: 6

积分
1280
金钱
1280
注册时间
2010-12-14
在线时间
0 小时
发表于 2011-2-9 18:59:53 | 显示全部楼层
建议先看《FAT32简单教程》,我们光盘的“FAT资料”目录里面可以找到这个文档!
希望openedv能给大家提供一个友好的技术交流平台!
回复 支持 反对

使用道具 举报

13

主题

32

帖子

0

精华

初级会员

Rank: 2

积分
122
金钱
122
注册时间
2011-1-20
在线时间
5 小时
发表于 2011-2-9 21:16:32 | 显示全部楼层
回复【4楼】张洋:
-------------------------------
哥,俺就是看完那个才感觉熟悉了FAT32结构才敢看原子哥的程序的,发现这个确实很难,原子哥定义了一堆结构,比如bootsector710,bpb710,partrecord,还有partsector等,俺就郁闷了,感觉好乱,光一个FAT_INIT程序俺就啃不过去哈~~
回复 支持 反对

使用道具 举报

4

主题

13

帖子

0

精华

初级会员

Rank: 2

积分
50
金钱
50
注册时间
2011-5-2
在线时间
0 小时
发表于 2011-6-10 22:00:56 | 显示全部楼层

---------------------------------
回复【5楼】smy096:
---------------------------------
回复【5楼】smy096:
---------------------------------
原子哥,可以把typedef __packed struct  

#define Fat_Table_Size 10        //tinyFAT表大小 
#define Fat_Head_Size  10  //tinyFAT表头大小   。。。何用?
u8  Fat_Head_Pos;                //tinyFAT表的上一个表头位置     。。。何用?
u32 Fat_Base_Head[Fat_Head_Size];//tinyFAT表的 表头数组  。。。何用?
u32 Fat_Base_Tab[Fat_Table_Size];//文件的tinyFAT基址表 
u16 Fat_Base_Len[Fat_Table_Size];//基址偏移量 
u8  Fat_Over;                    //文件的tinyFAT表是否全部读出标记位    
}FAT_TABLE;   
讲解一下,已经看FAT32一星期了,依然无头绪。
回复 支持 反对

使用道具 举报

4

主题

13

帖子

0

精华

初级会员

Rank: 2

积分
50
金钱
50
注册时间
2011-5-2
在线时间
0 小时
发表于 2011-6-10 22:03:29 | 显示全部楼层
回复【楼主位】正点原子:
---------------------------------
原子哥,可以把typedef __packed struct   
{  
#define Fat_Table_Size 10        //tinyFAT表大小  
#define Fat_Head_Size  10  //tinyFAT表头大小   。。。何用? 
u8  Fat_Head_Pos;                //tinyFAT表的上一个表头位置     。。。何用? 
u32 Fat_Base_Head[Fat_Head_Size];//tinyFAT表的 表头数组  。。。何用? 
u32 Fat_Base_Tab[Fat_Table_Size];//文件的tinyFAT基址表  
u16 Fat_Base_Len[Fat_Table_Size];//基址偏移量  
u8  Fat_Over;                    //文件的tinyFAT表是否全部读出标记位     
}FAT_TABLE;    
讲解一下,已经看FAT32一星期了,依然无头绪。 

回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165309
金钱
165309
注册时间
2010-12-1
在线时间
2108 小时
 楼主| 发表于 2011-6-10 22:14:58 | 显示全部楼层
回复【7楼】kuhaiwuya:
---------------------------------

文件存放的地址可以从FAT表里面找到.
如果情况比较坏(文件比较大,或者磁盘空间比较乱),一个文件的地址在fat表里面可能不是连续的,比如第2,3,4,接着又是 8,9,10,11,接着又是23,24,25,26.这样,这个文件的存放就是不连续的,这里的2,8,23,就是表头.属于表头数组的一个元素.

如果一个文件是连续的,比如从2~200.那么他就只有一个表头,就是2.

这样,应该理解了吧?
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

4

主题

13

帖子

0

精华

初级会员

Rank: 2

积分
50
金钱
50
注册时间
2011-5-2
在线时间
0 小时
发表于 2011-6-11 20:19:33 | 显示全部楼层
回复【8楼】正点原子:
---------------------------------
u32 Fat_Base_Head[Fat_Head_Size];//tinyFAT表的 表头数组   
u32 Fat_Base_Tab[Fat_Table_Size];//文件的tinyFAT基址表   

表头数组表示的是  文件的首簇号?
和 基址表 有和不同?
u8  Fat_Head_Pos;                //tinyFAT表的     上一个表头(指上一个表的标头 还是本表的表头?)          位置  
  
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165309
金钱
165309
注册时间
2010-12-1
在线时间
2108 小时
 楼主| 发表于 2011-6-12 00:54:36 | 显示全部楼层
结合我上面的说明,还不明白么?
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

4

主题

13

帖子

0

精华

初级会员

Rank: 2

积分
50
金钱
50
注册时间
2011-5-2
在线时间
0 小时
发表于 2011-6-12 18:18:26 | 显示全部楼层
回复【10楼】正点原子:
---------------------------------
u32 Fat_Base_Head[Fat_Head_Size];//tinyFAT表的 表头数组    
u32 Fat_Base_Tab[Fat_Table_Size];//文件的tinyFAT基址表  
还是不太明白 表头数组   和  文件基址  有什么不同?
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165309
金钱
165309
注册时间
2010-12-1
在线时间
2108 小时
 楼主| 发表于 2011-6-13 00:20:37 | 显示全部楼层
回复【11楼】kuhaiwuya:
---------------------------------
之前我的解释,应该放在Fat_Base_Tab才对,而不是对Fat_Base_Head.

这里的Fat_Base_Head是一个表头数组,而Fat_Base_Tab是一个基址数组. 整个不连续簇的最大范围是Fat_Base_Tab.size*Fat_Base_Head.size.

常用的是Fat_Base_Tab,在不连续簇不大于Fat_Base_Tab.size的时候,不需要用到Fat_Base_Head.
Fat_Base_Head用于存放每Fat_Base_Tab.size个不连续簇的首簇地址.
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

3

主题

123

帖子

1

精华

中级会员

Rank: 3Rank: 3

积分
364
金钱
364
注册时间
2012-4-30
在线时间
11 小时
发表于 2012-6-4 22:01:49 | 显示全部楼层
回复 支持 反对

使用道具 举报

0

主题

1

帖子

0

精华

新手入门

积分
21
金钱
21
注册时间
2013-5-20
在线时间
0 小时
发表于 2013-5-20 22:02:18 | 显示全部楼层
回复 支持 反对

使用道具 举报

10

主题

83

帖子

0

精华

初级会员

Rank: 2

积分
174
金钱
174
注册时间
2013-4-24
在线时间
4 小时
发表于 2013-5-21 10:41:26 | 显示全部楼层
这也是一种思路哈,学习了。。。。
牛逼死了,牛还活着吗?
回复 支持 反对

使用道具 举报

10

主题

83

帖子

0

精华

初级会员

Rank: 2

积分
174
金钱
174
注册时间
2013-4-24
在线时间
4 小时
发表于 2013-5-21 10:51:37 | 显示全部楼层
回复【12楼】正点原子:
---------------------------------
用fatfs不好这样实现吧?
牛逼死了,牛还活着吗?
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165309
金钱
165309
注册时间
2010-12-1
在线时间
2108 小时
 楼主| 发表于 2013-5-21 13:53:42 | 显示全部楼层
回复【16楼】cortexM3:
---------------------------------
fatfs有f_lseek函数,很好用。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

10

主题

83

帖子

0

精华

初级会员

Rank: 2

积分
174
金钱
174
注册时间
2013-4-24
在线时间
4 小时
发表于 2013-5-21 17:11:12 | 显示全部楼层
回复【17楼】正点原子:
---------------------------------
奥  多谢。再看看这个、
牛逼死了,牛还活着吗?
回复 支持 反对

使用道具 举报

3

主题

62

帖子

0

精华

初级会员

Rank: 2

积分
95
金钱
95
注册时间
2013-7-18
在线时间
0 小时
发表于 2013-7-18 20:10:07 | 显示全部楼层
原子哥,这段程序我看了很长时间,在一个地方看不懂,在u32 FatTab_Prev_Cluster(unsigned long cluster)函数里,其中在Copy_Fat_Table(tempclust);后面要不要加上cluster=tempclust;
回复 支持 反对

使用道具 举报

6

主题

35

帖子

0

精华

初级会员

Rank: 2

积分
79
金钱
79
注册时间
2012-6-25
在线时间
0 小时
发表于 2013-11-8 10:29:57 | 显示全部楼层
回复【楼主位】正点原子:
---------------------------------
if(bcluster-FAT_TAB.Fat_Base_Tab[fat_base]-FAT_TAB.Fat_Base_Len[fat_base]!=0)//原子哥 i这个语句里面不要再加个括号吗?if((bcluster-FAT_TAB.Fat_Base_Tab[fat_base]-FAT_TAB.Fat_Base_Len[fat_base])!=0)//
回复 支持 反对

使用道具 举报

0

主题

3

帖子

0

精华

新手上路

积分
25
金钱
25
注册时间
2014-2-11
在线时间
1 小时
发表于 2014-2-14 11:29:34 | 显示全部楼层
领教一下
回复 支持 反对

使用道具 举报

13

主题

27

帖子

0

精华

初级会员

Rank: 2

积分
176
金钱
176
注册时间
2017-7-14
在线时间
56 小时
发表于 2017-8-13 20:44:45 | 显示全部楼层
mark mark
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-22 17:09

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表