|
看着高手们写的SD卡读fat32或fat的程序。动辄数百行,模块强大,功能丰富。近似完美,通用性强,但看着也眼花缭乱的,让新手无从下手。
玩tft彩屏和sd卡的目的之一就是把sd卡中的图片显示到彩屏上。
这有个简单的办法找到fat32文件系统中的图片依次显示在彩屏上。
sd卡驱动和彩屏驱动这里不涉及。就拿240*320的tft彩屏和1G的sd卡为例。
fat32文件系统和bmp格式的资料网上屎一样多。对照文件的hex值很容易研究明白
读fat32的简单规律就是先从根目录出发。找到bmp和相关字眼,记下第一簇位置。然后读fat表。找到第一簇,和下一簇簇号。把当前簇的几个扇区的内容写在屏幕上。然后循环几次找到其余簇就把全部图片显示出来。至于循环次数可以图片的用字节大小计算。
我这里SD卡中第一fat表是第36扇区,根目录是第3904扇区,每簇8扇区。可以从分区头文件中读出来。为了方便我就在下面直接写数值了。
/////////////////////////////下面的函数是显示图片第一簇的,因为bmp文件第一簇第一扇区有一些文件信息。要跳过,不予显示。当然你也可以写函数读出来。
writefirst(unsigned int kk)//显示第一簇,kk为扇区号,去掉头信息
{
unsigned int m,j;
unsigned char tp1,tp2,tp3,k;
SD_Read_Sector(kk,buf);
for(j=27;j<=255;j++) //第一扇区中的第一像素从第54字节开始,前面的是文件信息。因此此扇区单独处理
{
tp2=buf[2*j+1];//读第2*j像素高字节
tp1=buf[2*j];//读第2*j像素低字节
m=(tp2<<8)|tp1;
tp3=m&0b11111;
m=((m&0b1111111111100000)<<1)|(tp3);//把555格式转换成565格式,m为565格式的一个像素值。
writedata(m);//这是往tft写像素点的函数,在别处定义。
}
for(k=1;k<8;k++)//其余7个扇区全部写入。
{
SD_Read_Sector(kk+k,buf);
for(j=0;j<=255;j++)
{
tp2=buf[2*j+1];//同上
tp1=buf[2*j];//
m=(tp2<<8)|tp1;
tp3=m&0b11111;
m=((m&0b1111111111100000)<<1)|(tp3);//同上
writedata(m);
}
}
}
//////////////////////////////////下面的函数是写正常簇的(除了第一簇)。一个簇8个扇区全写入,原理同上
writecu(unsigned int kk)//显示一簇,kk为扇区号
{
unsigned int m,j;
unsigned char tp1,tp2,tp3,k;
for(k=0;k<8;k++)
{
SD_Read_Sector(kk+k,buf);
for(j=0;j<=255;j++)
{
tp2=buf[2*j+1];
tp1=buf[2*j];
m=(tp2<<8)|tp1;
tp3=m&0b11111;
m=((m&0b1111111111100000)<<1)|(tp3);
writedata(m);
}
}
}
/////////////////////////主要部分//////////////////tp1到tp6为uchar,其余为uint。buf为512字节的缓冲区(全局变量)。
////////////////////void SD_Read_Sector(扇区数,缓冲数组) 为sd卡读扇区的函数。应位于sd读写模块中。
fatcunum=0;//一个全局变量,表示当前是fat表中第几簇。
for(n1=0;n1<8;n1++)//依次读根目录中的8个扇区,虽然fat32不限制文件数量,这里不考虑(偷懒,但不是直接读第九个扇区)。
{
SD_Read_Sector(3904+n1,buf);//读根目录的第n1扇区,从第3904扇区开始
for(n2=0;n2<16;n2++)//把一个簇分成16块,在相应位置查找关键字。(把sd卡格式化,保存几个文件,找找规律就明白了)
{
tp1=buf[n2*32+8];
tp2=buf[n2*32+9];
tp3=buf[n2*32];//文件名首字节
tp4=buf[n2*32+29];
tp5=buf[n2*32+30];
if((tp1==0x42)&&(tp2==0x4d)&&(tp3!=0xe5)&&(tp4==0x58)&&(tp5==0x02))
///////////////////////查找root中的bmp字样和文件大小字样,我这里查找"B","M","0xe5",“0x58”,“0x02”这几个字样,
//////////////////////符合要求的图片大小一定是0x25836字节或者0x25838字节。文件名首字节如果是0xE5表示已删除,如果全部满足条件,开始读取文件。
{
addset(0,0,239,319);//这是在定义tft中的显示区域,应在显示模块中定义。
cu=(buf[n2*32+27]<<8)|buf[n2*32+26];//记下文件第一簇簇号,一共有4个字节,在此只读取后两字节。(偷懒呗,一般用不到那么多)
fatcunum=cu/0x80;//每扇区有0x80个簇号,fatcunum表示下一个簇号位于fat表的第几簇,全局变量
SD_Read_Sector(36+fatcunum,buf);//读下一簇号指向的的fat表的簇
writefirst((cu-3)*8+3912);//写第一簇的数据(赋值为扇区号),这是刚才上面定义的函数。文件的第一簇簇号为3,所以需要减去。8表示每簇8扇区。3912为数据区的第一扇区。
for(tp6=0;tp6<37;tp6++)//为什么循环37次后面解释。
{
SD_Read_Sector(36+fatcunum,buf);//一定再次读fat表(刚才的操作把buf中的数改了)
nextcu=(buf[(cu-(fatcunum*0x80))*4+1]<<8)|buf[(cu-(fatcunum*0x80))*4];//下一簇簇号
fatcunum=nextcu/0x80;//算出下一簇号位于fat表的第几簇
writecu((nextcu-3)*8+3912);//写其余扇区的数据,刚才上面定义的。
cu=nextcu;
SD_Read_Sector(36+fatcunum,buf);//一定再读fat表
}
}
SD_Read_Sector(3904+n1,buf);//一定再读根目录。刚才的操作可能改buf中的数据。
}
}
为什么循环37次,因为一个符合要求的16位的240×320的bmp图片大小一定是0x25836字节或者0x25838字节。以0x25836为例。
转成十进制就是153654字节,以本程序为例,每簇8扇区。153654÷512÷8=37.51,就是说占了37个半簇,第一簇特殊处理。还剩下36.5簇。因此这里循环37次,当然最后的一个簇的不完整会导致显示有问题,通过观察只是屏幕最左侧或者最右侧有一两条线异常。无伤大雅。如果你不嫌麻烦就写个处理结尾簇的函数。
这就完事了。通过观察,8M晶振,显示完整每幅图片需要1.5秒,是连续显示。如果你需要按键操作的话自己加个标志位。
这个方法仅针对显示240×320的16位bmp图片,不能直接移植到其它用途。因为写的太简单了,漏洞百出。高手勿喷。入门比较不错。
下图以我用的卡为例。
(原文件名:32feiqu.JPG) |
|