📄 mmc.c
字号:
#include <string.h>
#include <stdio.h>
#include "uart.h"
#include "menu.h"
#include "Lcd.h"
#include "system.h"
#include "mmc.h"
#include "usbtest.h"
char FILENO=0;
unsigned long WCnt=0;
unsigned long RCnt=0;
char Found; //在目录中找到的文件或目录个数
unsigned long DCounter; //查找目录时字节计数器
FILESTRUCT File[13]; //保存查找到的文件或目录信息
FILESTRUCT CurF; //存放当前目录
unsigned char MMCDataBuf[513]; //存放MMC一个块的数据
unsigned char MMCUSBBuf[513]; //用作与PC通过USB交换数据
char PATH[40];
char DEPTH;
unsigned long SecPClu=0; //每簇所含块数
unsigned long BytePClu=0; //每簇所含字节数
unsigned long DIR_ORI=0; //根目录起始点 (根目录共占32个Sector,16kB)
unsigned long DIR_END=0; //根目录结束点 (紧随其后是第二簇,没有第零,一簇)
unsigned long DIR_LEN=0; //根目长度
unsigned long CLU_ORI=0; //用户区起始地址,假设存在的第零簇的位置
unsigned long FAT1_ORI=0; //FAT表1起始地址
unsigned long FAT2_ORI=0; //FAT表2起始地址
unsigned char code FileIcon[]={"砬"};
unsigned char code FoldIcon[]={"苈"};
void SYS_INIT()
{
SFRPAGE=0x0f; //c8051f120系列中要访问P4-P7必须设置SFRPAGE=0x0F
XBR2|=0x40; //交叉开关使能
XBR0|=0x06; //SPI,UART0使能(在C8051F0XX系列中时SMbBus使能,即0x03)*/
P0MDOUT|=0x14; //p0.2:SPI-SCK为PUSH-PULL,p0.4:SPI-MOSI 设为PUSH-PULL fpage
P7MDOUT|=0x60; //P7.5:CS设置为PUSH-PULL,P7.6:PWR设为PUSH-PULL,f page
// SPI 0 PAGE
SFRPAGE=0x00;
SPIEN=0; //turn off SPIEN before setting phase and polarity
SPI0CFG |= 0x70; //设置时钟相位和极性,SPI设置为主模式
SPI0CN |= 0x01; //SSPI使能!transmit buffer empty!由于使三线所以设置NSSnMD为00
SPI0CKR = 0x00; //SPI时钟速率
//skip p1.1 pin because spi set to 3 lines mode
// XBR0 |= 0x40;
// P1MDIN &= 0xFD;
SFRPAGE=0x0f;
MMC_power=0; //打开电源
}
//////////////////////////////////////////////////////////////////////////////////////////////////
// SPI底层单字节数据收发 //
//////////////////////////////////////////////////////////////////////////////////////////////////
unsigned char SPI_SEND(unsigned char Bdata) //与MMC卡交换数据函数
{
SFRPAGE=0x00;
SPI0DAT=Bdata;
while(!SPIF); //当SPI中断为到,即数据为传输完时则循环等待.
SPIF=0; //由于硬件中断后没有清零,所以需要手动清零
return SPI0DAT;
}
//////////////////////////////////////////////////////////////////////////////////////////////////
// MMC卡SPI模式命令层 //
//////////////////////////////////////////////////////////////////////////////////////////////////
unsigned char MMC_SEND_CMD(unsigned char index,argument1,argument2,argument3,argument4)
{
/*
每一个MMC命令均为48bits,所以发送6个字节的数据
而默认的情况下:MMC的命令格式如下:
位 47 46 [45:40] [39:8] [7:1] 0
值 0 1 X X X 1
宽度 1 1 6 32 7 1
功能 起始位 传输位 命令值 参数 CRC7 中止位
SPI模式下CRC校验默认为关闭,相应的位可为任意值.
*/
signed char temp;
//以下的6个语句用来reset MMC to SPI mode
SPI_SEND(index|0x40); //01xx xxxx
SPI_SEND(argument1);
SPI_SEND(argument2);
SPI_SEND(argument3);
SPI_SEND(argument4); //32bit参数zz
SPI_SEND(0x95); //crc校验位,进入spi模式后默认关闭,值可以任意
SPI_SEND(0xff);
temp=SPI_SEND(0xff);
if ((temp-0x80)>=0)
{ //0b1xxxxxxx=err
temp=0xff;
while (SPI_SEND(0xff)==0);
}
else
if (index&0xbf==13)
{ //如果是读状态命令
temp=SPI_SEND(0xff); //read STATUS
while (SPI_SEND(0xff)==0);
}
else
{ //是其他命令
while (SPI_SEND(0xff)==0);
}
return temp;
}
unsigned char MMC_SPI_INIT(void)
{
unsigned char counter,temp;
temp=0xff;
SFRPAGE=0x0F;
MMC_CS=0;
//延时
for(counter=0;counter<10;counter++)
SPI_SEND(0xff);
delay(500);
temp=MMC_SEND_CMD(MMC_GO_IDLE_STATE,0,0,0,0); //进入SPI模式
for(counter=0;counter<=80;counter++) //延时80周期
{
temp=MMC_SEND_CMD(MMC_SEND_OP_COND,0,0,0,0);
if(temp==0) break;
}
if(counter==81) return KO;
return OK;
MMC_CS=1;
}
unsigned char MMC_block_read(unsigned char *buf,unsigned long Addr32) //读取Addr32所在的SECTOR到Buf
{
int counter=0;
unsigned char temp;
unsigned char P[3];
P[2]=(unsigned char)(Addr32>>8)&0xFE;
P[1]=(unsigned char)(Addr32>>16);
P[0]=(unsigned char)(Addr32>>32);
MMC_CS=0;
temp=MMC_SEND_CMD(MMC_READ_SINGLE_BLOCK,P[0],P[1],P[2],0); //发送单块读命令
while(temp!=0xfe && counter<255) //等待返回0xFE起始字节
{
temp=SPI_SEND(0xff);
counter++;
}
if(counter==255)
{
return KO;
}
for(counter=0;counter<512;counter++)
{
buf[counter]=SPI_SEND(0xff);
}
SPI_SEND(0xff);
SPI_SEND(0xff);
MMC_CS=1;
return OK;
}
unsigned char MMC_block_write(unsigned char *buf,unsigned long Addr32)//将512B的Buf写入到Addr32所在的SECTOR
{
int counter;
unsigned char temp;
unsigned char P[3];
P[2]=(unsigned char)(Addr32>>8)&0xFE;
P[1]=(unsigned char)(Addr32>>16);
P[0]=(unsigned char)(Addr32>>32);
MMC_CS=0;
SPI_SEND(0xff);
temp=MMC_SEND_CMD(MMC_WRITE_BLOCK,P[0],P[1],P[2],0);
for(counter=0;counter<255;counter++)
SPI_SEND(0xff);
SPI_SEND(0xfe); //发送起始字节
for(counter=0;counter<512;counter++) //发送数据
{
SPI_SEND(buf[counter]);
}
temp=SPI_SEND(0xff); //CRC码
temp=SPI_SEND(0xff);
counter=0;
do
{
temp=SPI_SEND(0xff);
counter++;
}
while( (temp&0x0f)!=5 && counter<255); //等待返回写入成功字节xxx00101
if(counter==255) return KO;
temp=0;
while((temp==0)&&counter<512)
{
temp=SPI_SEND(0xff);
counter++;
delay(10);
}
MMC_CS=1;
if(counter==512)
return KO;
return OK;
}
void CheckCard()
{
unsigned long ReservedSector=0;
unsigned long RootEntries=0;
unsigned long SectorsperFAT=0;
MMC_block_read(MMCDataBuf,0); //读入DBR,分析BPB
SecPClu=MMCDataBuf[0x0d];
BytePClu=SecPClu*512;
ReservedSector=MMCDataBuf[0x0e]+MMCDataBuf[0x0f]*256;
RootEntries=MMCDataBuf[0x11]+MMCDataBuf[0x12]*256;
SectorsperFAT=MMCDataBuf[0x16]+MMCDataBuf[0x17]*256;
FAT1_ORI=ReservedSector*512;
FAT2_ORI=FAT1_ORI+SectorsperFAT*512;
DIR_ORI=FAT2_ORI+SectorsperFAT*512;
DIR_LEN=RootEntries*32;
CLU_ORI=DIR_ORI+DIR_LEN-2*BytePClu;
return ;
}
//////////////////////////////////////////////////////////////////////////////////////////////////
// 文件层数据读取 //
//////////////////////////////////////////////////////////////////////////////////////////////////
unsigned int SearchEmptyCluster() //在FAT表中搜寻未被占用的簇号
{ //成功返回簇号,失败返回0
unsigned long Addr32=FAT1_ORI;
unsigned int counter=0;
int i;
while(Addr32<FAT2_ORI) //搜索FAT1表
{
MMC_block_read(MMCDataBuf,Addr32); //读入一块
for(i=0;i<512;i+=2)
{
if(MMCDataBuf[i]==0 && MMCDataBuf[i+1]==0) //判断是否为空白块
{
MMCDataBuf[i]=0xFF;
MMCDataBuf[i+1]=0xFF;
if(MMC_block_write(MMCDataBuf,Addr32)==OK) //将其占用
return counter*256+i/2;
else
return 0;
}
}
counter++;
Addr32+=512;
}
return 0;
}
unsigned int Find_NextN_Cluster(unsigned int First_Cluster,unsigned int n) //计算给定簇的下N个簇序号
{ //超过文件界限返回0
int i;
unsigned int temp=First_Cluster;
if(n==0)
return First_Cluster;
for(i=0;i<n;i++)
{
MMC_block_read(MMCDataBuf,FAT1_ORI+temp*2);
temp=MMCDataBuf[(temp%256)*2]+MMCDataBuf[(temp%256)*2+1]*0x100;
if(temp==0xFFFF)
break;
}
if(i>=n-1)
return temp;
else
return 0;
}
int ReadFile(FILESTRUCT *fp,unsigned long offset) //读取文件fp偏移offset所在块到buf,
{ //返回offset在buf中的偏移,失败返回-1
unsigned long sector_sn=offset/512;
unsigned int cluster_sn=sector_sn/SecPClu;
int sector_offset=offset%512;
char cluster_0ffset=sector_sn%SecPClu;
unsigned long temp=Find_NextN_Cluster(fp->firstcluster,cluster_sn);
if(fp->firstcluster==0)
{
if(fp->attribute==0) //读根目录文件
{
if(offset>=DIR_LEN)
return -1;
MMC_block_read(MMCDataBuf,DIR_ORI+offset);
}
else //读的是空文件
{
return -1;
}
}
else //普通文件
{
if (temp==0xFFFF)
return -1;
MMC_block_read(MMCDataBuf,CLU_ORI+temp*BytePClu+cluster_0ffset*0x200);
}
SFRPAGE=0x0f;
return sector_offset;
}
int WriteFile(unsigned char *buf,FILESTRUCT *fp,unsigned long offset) //将512字节buf内容写入fp文件offset偏移处所在块
{
unsigned long sector_sn=offset/512;
unsigned int cluster_sn=sector_sn/SecPClu;
int sector_offset=offset%512;
char cluster_0ffset=sector_sn%SecPClu;
unsigned int Temp1,Temp2;
if(fp->firstcluster==0)
{
if(offset>DIR_LEN)
return -1;
MMC_block_write(buf,DIR_ORI+offset);
return 1;
}
Temp1=Find_NextN_Cluster(fp->firstcluster,cluster_sn);
if(Temp1==0) //偏移越界,写文件失败,返回
return -1;
if(Temp1==0xFFFF) //需要分配新簇
{
Temp1=SearchEmptyCluster();
//将新簇链接到FAT1表中
Temp2=Find_NextN_Cluster(fp->firstcluster,cluster_sn-1)*2;
MMC_block_read(MMCDataBuf,FAT1_ORI+Temp2);
MMCDataBuf[Temp2%512]=Temp1;
MMCDataBuf[Temp2%512+1]=Temp1>>8;
MMC_block_write(MMCDataBuf,FAT1_ORI+Temp2);
}
MMC_block_write(buf,CLU_ORI+Temp1*BytePClu+cluster_0ffset*0x200);
return 1;
}
//////////////////////////////////////////////////////////////////////////////////////////////////
// 应用层显示目录与浏览文本文件 //
//////////////////////////////////////////////////////////////////////////////////////////////////
void Get_File_List(unsigned long addr32,int From) //获取DataBuf中从From开始的目录信息,直到Found=13或查找完毕
{
int i,j;
for(i=From;i<512;i+=32)
{
DCounter+=32;
//空目录项,跳过(下面不再会有有效记录??)
if(MMCDataBuf[i]==0) continue;
//曾经被占用过的目录项,下一个
if(MMCDataBuf[i]==0xE5) continue;
if(MMCDataBuf[i+0x0b]==0x0F) continue; //该项为长文件名项,目前程序不支持长文件名,不必理会
//紧随其后会有其短文件名
//该条目录有效,解释出相关信息,保存到File结构中
//保存该条目录地址
File[Found].Addr=addr32+DCounter-32;
//文件名
File[Found].filename[8]=0;
File[Found].filename[9]=0;
for(j=0;j<8;j++)
{
if(MMCDataBuf[i+j]==0x20)
{
File[Found].filename[j]=0;
break;
}
File[Found].filename[j]=MMCDataBuf[i+j];
}
if(MMCDataBuf[i]=='.')
{
if(MMCDataBuf[i+1]=='.') //。。表示上级目录
{
memcpy(File[Found].filename,"上级目录",8);
File[Found].filename[9]=1;
}
if(MMCDataBuf[i+1]==0x20) //。表示当前目录,保存到CurrentFile中
{
CurF.firstcluster=MMCDataBuf[i+0x1a]+MMCDataBuf[i+0x1b]*0x100;
continue;
}
}
//扩展名
File[Found].extension[3]=0;
for(j=0;j<3;j++)
{
if(MMCDataBuf[i+0x08+j]==0x20)
{
File[Found].extension[j]=0;
break;
}
File[Found].extension[j]=MMCDataBuf[i+0x08+j];
}
//权限(识别文件还是目录)
if((MMCDataBuf[i+0x0b]&0x10)!=0) //注意:!=运算优先级高于 &
File[Found].attribute=0;
else
File[Found].attribute=1;
//时分
{
unsigned int temp=MMCDataBuf[i+0x16]+MMCDataBuf[i+0x17]*0x100;
File[Found].minute=(temp&0x07E0)>>5;
File[Found].hour=temp>>11;
}
//年月日
{
unsigned int temp=MMCDataBuf[i+0x18]+MMCDataBuf[i+0x19]*0x100;
File[Found].year=(temp>>9)-20;
File[Found].month=(temp&0x01E0)>>5;
File[Found].day=(temp&0x001F);
}
//首簇号
File[Found].firstcluster=MMCDataBuf[i+0x1a]+MMCDataBuf[i+0x1b]*0x100;
//文件大小
File[Found].size=0;
for(j=0x1f;j>=0x1c;j--)
File[Found].size=MMCDataBuf[i+j]+File[Found].size*0x100;
Found++;
if(Found==13)
return ;
}
}
void Show_Dir_List()
{
char n;
SFRPAGE=0x0F;
Clear(0,208);
for(n=0;n<Found;n++)
{
dprintf(4,16*n,File[n].filename);
if(File[n].attribute==1) //如果是文件,显示文件图标,扩展名,文件大小
{
dprintf(2,16*n,FileIcon);
if (strlen(File[n].extension) != 0)
{
dprintf(4+strlen(File[n].filename),16*n,".");
dprintf(5+strlen(File[n].filename),16*n,File[n].extension);
}
putufig(16,16*n, File[n].size,8,0,' ');
}
else //如果是目录,显示文件夹图标
{
dprintf(2,16*n,FoldIcon);
}
dprintf(28,16*n, "/ / :");
putufig(26,16*n, File[n].year,2,0,'0');
putufig(29,16*n, File[n].month,2,0,'0');
putufig(32,16*n, File[n].day,2,0,'0');
putufig(35,16*n, File[n].hour,2,0,'0');
putufig(38,16*n, File[n].minute,2,0,'0');
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -