原创 元器件知识之单片机对SD卡的驱动方法及程序2

2011-7-6 08:22 1670 7 8 分类: 工程师职场


写命令的例程:
//-----------------------------------------------------------------------------------------------
  
SD卡中写入命令,并返回回应的第二个字节
//-----------------------------------------------------------------------------------------------
unsigned char Write_Command_SD(unsigned char *CMD)
{
   unsigned char tmp;
   unsigned char retry=0;
   unsigned char i;
   //
禁止SD卡片选
   SPI_CS=1;
   //
发送8个时钟信号
   Write_Byte_SD(0xFF);
   //
使能SD卡片选
   SPI_CS=0;
   //
SD卡发送6字节命令
   for (i=0;i<0x06;i++)
   {
      Write_Byte_SD(*CMD++);
   }
   
   //
获得16位的回应
   Read_Byte_SD(); //read the first byte,ignore it.
   do
   {  //
读取后8
      tmp = Read_Byte_SD();
      retry++;
   }
   while((tmp==0xff)&&(retry<100));
   return(tmp);
}
2
初始化
SD
卡的初始化是非常重要的,只有进行了正确的初始化,才能进行后面的各项操作。在初始化过程中,SPI的时钟不能太快,否则会造初始化失败。在初始化成功后,应尽量提高SPI的速率。在刚开始要先发送至少74个时钟信号,这是必须的。在很多读者的实验中,很多是因为疏忽了这一点,而使初始化不成功。随后就是写入两个命令CMD0CMD1,使SD卡进入SPI模式
           
初始化时序图:
  

           
初始化例程:
//--------------------------------------------------------------------------
   
初始化SD卡到SPI模式
//--------------------------------------------------------------------------
unsigned char SD_Init()
{  
   unsigned char retry,temp;
   unsigned char i;
   unsigned char CMD[] = {0x40,0x00,0x00,0x00,0x00,0x95};
   SD_Port_Init(); //
初始化驱动端口
   
   Init_Flag=1; //
将初始化标志置1
   for (i=0;i<0x0f;i++)
   {
      Write_Byte_SD(0xff); //
发送至少74个时钟信号
   }

   //
SD卡发送CMD0
   retry=0;
   do
   { //
为了能够成功写入CMD0,在这里写200
     temp=Write_Command_SD(CMD);
     retry++;
     if(retry==200)
     { //
超过200
       return(INIT_CMD0_ERROR);//CMD0 Error!
     }
   }
   while(temp!=1);  //
回应01h,停止写入
   
   //
发送CMD1SD
   CMD[0] = 0x41; //CMD1
   CMD[5] = 0xFF;
   retry=0;
   do
   { //
为了能成功写入CMD1,100
     temp=Write_Command_SD(CMD);
     retry++;
     if(retry==100)
     { //
超过100
       return(INIT_CMD1_ERROR);//CMD1 Error!
     }
   }
   while(temp!=0);//
回应00h停止写入
   
   Init_Flag=0; //
初始化完毕,初始化标志清零
   
   SPI_CS=1;  //
片选无效
   return(0); //
初始化成功
}


3
读取CID
CID
寄存器存储了SD卡的标识码。每一个卡都有唯一的标识码。
CID
寄存器长度为128位。它的寄存器结构如下:

名称

数据宽度

CID划分

生产标识号

MID

8

[127:120]

OEM/应用标识

OID

16

[119:104]

产品名称

PNM

40

[103:64]

产品版本

PRV

8

[63:56]

产品序列号

PSN

32

[55:24]

保留

4

[23:20]

生产日期

MDT

12

[19:8]

CRC7校验合

CRC

7

[7:1]

未使用,始终为1

1

[0:0]


它的读取时序如下:

        
与此时序相对应的程序如下:
//------------------------------------------------------------------------------------
   
读取SD卡的CID寄存器   16字节   成功返回0
//-------------------------------------------------------------------------------------
unsigned char Read_CID_SD(unsigned char *Buffer)
{
   //
读取CID寄存器的命令
   unsigned char CMD[] = {0x4A,0x00,0x00,0x00,0x00,0xFF};
   unsigned char temp;
   temp=SD_Read_Block(CMD,Buffer,16); //read 16 bytes
   return(temp);
}

4
读取CSD
CSDCard-Specific Data)寄存器提供了读写SD卡的一些信息。其中的一些单元可以由用户重新编程。具体的CSD结构如下:

名称

数据宽度

单元类型

CSD划分

CSD结构

CSD_STRUCTURE

2

R

[127:126]

保留

-

6

R

[125:120]

数据读取时间1

TAAC

8

R

[119:112]

数据在CLK周期内读取时间2NSAC*100

NSAC

8

R

[111:104]

最大数据传输率

TRAN_SPEED

8

R

[103:96]

卡命令集合

CCC

12

R

[95:84]

最大读取数据块长

READ_BL_LEN

4

R

[83:80]

允许读的部分块

READ_BL_PARTIAL

1

R

[79:79]

非线写块

WRITE_BLK_MISALIGN

1

R

[78:78]

非线读块

READ_BLK_MISALIGN

1

R

[77:77]

DSR条件

DSR_IMP

1

R

[76:76]

保留

-

2

R

[75:74]

设备容量

C_SIZE

12

R

[73:62]

最大读取电流@VDD min

VDD_R_CURR_MIN

3

R

[61:59]

最大读取电流@VDD max

VDD_R_CURR_MAX

3

R

[58:56]

最大写电流@VDD min

VDD_W_CURR_MIN

3

R

[55:53]

最大写电流@VDD max

VDD_W_CURR_MAX

3

R

[52:50]

设备容量乘子

C_SIZE_MULT

3

R

[49:47]

擦除单块使能

ERASE_BLK_EN

1

R

[46:46]

擦除扇区大小

SECTOR_SIZE

7

R

[45:39]

写保护群大小

WP_GRP_SIZE

7

R

[38:32]

写保护群使能

WP_GRP_ENABLE

1

R

[31:31]

保留

-

2

R

[30:29]

写速度因子

R2W_FACTOR

3

R

[28:26]

最大写数据块长度

WRITE_BL_LEN

4

R

[25:22]

允许写的部分部

WRITE_BL_PARTIAL

1

R

[21:21]

保留

-

5

R

[20:16]

文件系统群

FILE_OFRMAT_GRP

1

R/W

[15:15]

拷贝标志

COPY

1

R/W

[14:14]

永久写保护

PERM_WRITE_PROTECT

1

R/W

[13:13]

暂时写保护

TMP_WRITE_PROTECT

1

R/W

[12:12]

文件系统

FIL_FORMAT

2

R/W

[11:10]

保留

-

2

R/W

[9:8]

CRC

CRC

7

R/W

[7:1]

未用,始终为1

-

1

[0:0]


           
读取CSD 的时序:
  
           
相应的程序例程如下:
//-----------------------------------------------------------------------------------------
   
SD卡的CSD寄存器   16字节    返回0说明读取成功
//-----------------------------------------------------------------------------------------
unsigned char Read_CSD_SD(unsigned char *Buffer)
{
   //
读取CSD寄存器的命令
   unsigned char CMD[] = {0x49,0x00,0x00,0x00,0x00,0xFF};
   unsigned char temp;
   temp=SD_Read_Block(CMD,Buffer,16); //read 16 bytes
   return(temp);
}

4
读取SD卡信息
综合上面对CIDCSD寄存器的读取,可以知道很多关于SD卡的信息,以下程序可以获取这些信息。如下:
//-----------------------------------------------------------------------------------------------
//
返回
//  SD
卡的容量,单位为M
//  sector count and multiplier MB are in
u08 == C_SIZE / (2^(9-C_SIZE_MULT))
//  SD
卡的名称
//-----------------------------------------------------------------------------------------------
void SD_get_volume_info()
{   
    unsigned char i;
    unsigned char c_temp[5];
    VOLUME_INFO_TYPE SD_volume_Info,*vinf;
    vinf=&SD_volume_Info; //Init the pointoer;
/
读取CSD寄存器
    Read_CSD_SD(sectorBuffer.dat);
//
获取总扇区数
vinf->sector_count = sectorBuffer.dat[6] & 0x03;
vinf->sector_count <<= 8;
vinf->sector_count += sectorBuffer.dat[7];
vinf->sector_count <<= 2;
vinf->sector_count += (sectorBuffer.dat[8] & 0xc0) >> 6;
//
获取multiplier
vinf->sector_multiply = sectorBuffer.dat[9] & 0x03;
vinf->sector_multiply <<= 1;
vinf->sector_multiply += (sectorBuffer.dat[10] & 0x80) >> 7;
//
获取SD卡的容量
vinf->size_MB = vinf->sector_count >> (9-vinf->sector_multiply);
// get the name of the card
Read_CID_SD(sectorBuffer.dat);
vinf->name[0] = sectorBuffer.dat[3];
vinf->name[1] = sectorBuffer.dat[4];
vinf->name[2] = sectorBuffer.dat[5];
vinf->name[3] = sectorBuffer.dat[6];
vinf->name[4] = sectorBuffer.dat[7];
vinf->name[5] = 0x00; //end flag
}
         
以上程序将信息装载到一个结构体中,这个结构体的定义如下:
typedef struct SD_VOLUME_INFO
{ //SD/SD Card info
  unsigned int  size_MB;
  unsigned char sector_multiply;
  unsigned int  sector_count;
  unsigned char name[6];
} VOLUME_INFO_TYPE;

5
扇区读
扇区读是对SD卡驱动的目的之一。SD卡的每一个扇区中有512个字节,一次扇区读操作将把某一个扇区内的512个字节全部读出。过程很简单,先写入命令,在得到相应的回应后,开始数据读取。
扇区读的时序:

      
扇区读的程序例程:
unsigned char SD_Read_Sector(unsigned long sector,unsigned char *buffer)
{  
   unsigned char retry;
   //
命令16
   unsigned char CMD[] = {0x51,0x00,0x00,0x00,0x00,0xFF};
   unsigned char temp;
   
   //
地址变换   由逻辑块地址转为字节地址
   sector = sector << 9; //sector = sector * 512
   CMD[1] = ((sector & 0xFF000000) >>24 );
   CMD[2] = ((sector & 0x00FF0000) >>16 );
   CMD[3] = ((sector & 0x0000FF00) >>8 );
   //
将命令16写入SD
   retry=0;
   do
   {  //
为了保证写入命令  一共写100
      temp=Write_Command_MMC(CMD);
      retry++;
      if(retry==100)
      {
        return(READ_BLOCK_ERROR); //block write Error!
      }
   }
   while(temp!=0);
      
   //Read Start Byte form MMC/SD-Card (FEh/Start Byte)
   //Now data is ready,you can read it out.
   while (Read_Byte_MMC() != 0xfe);
   readPos=0;
  SD_get_data(512,buffer) ;  //512
字节被读出到buffer
return 0;
}
其中SD_get_data函数如下:
//----------------------------------------------------------------------------
   
获取数据到buffer
//----------------------------------------------------------------------------
void SD_get_data(unsigned int Bytes,unsigned char *buffer)
{
   unsigned int j;
   for (j=0;j<Bytes;j++)
      *buffer++ = Read_Byte_SD();
}

6
扇区写
扇区写是SD卡驱动的另一目的。每次扇区写操作将向SD卡的某个扇区中写入512个字节。过程与扇区读相似,只是数据的方向相反与写入命令不同而已。
   
扇区写的时序:

扇区写的程序例程:
//--------------------------------------------------------------------------------------------
   
512个字节到SD卡的某一个扇区中去   返回0说明写入成功
//--------------------------------------------------------------------------------------------
unsigned char SD_write_sector(unsigned long addr,unsigned char *Buffer)
{  
   unsigned char tmp,retry;
   unsigned int i;
   //
命令24
   unsigned char CMD[] = {0x58,0x00,0x00,0x00,0x00,0xFF};
   addr = addr << 9; //addr = addr * 512

   CMD[1] = ((addr & 0xFF000000) >>24 );
   CMD[2] = ((addr & 0x00FF0000) >>16 );
   CMD[3] = ((addr & 0x0000FF00) >>8 );
   //
写命令24SD卡中去
   retry=0;
   do
   {  //
为了可靠写入,写100
      tmp=Write_Command_SD(CMD);
      retry++;
      if(retry==100)
      {
        return(tmp); //send commamd Error!
      }
   }
   while(tmp!=0);
   
   //
在写之前先产生100个时钟信号
   for (i=0;i<100;i++)
   {
      Read_Byte_SD();
   }

   //
写入开始字节
   Write_Byte_MMC(0xFE);

   //
现在可以写入512个字节
   for (i=0;i<512;i++)
   {
      Write_Byte_MMC(*Buffer++);
   }
   //CRC-Byte
   Write_Byte_MMC(0xFF); //Dummy CRC
   Write_Byte_MMC(0xFF); //CRC Code
   
   
   tmp=Read_Byte_MMC();   // read response
   if((tmp & 0x1F)!=0x05) //
写入的512个字节是未被接受
   {
     SPI_CS=1;
     return(WRITE_BLOCK_ERROR); //Error!
   }
   //
等到SD卡不忙为止
//
因为数据被接受后,SD卡在向储存阵列中编程数据
   while (Read_Byte_MMC()!=0xff){};

   //
禁止SD
   SPI_CS=1;
   return(0);//
写入成功
}
此上内容在笔者的实验中都已调试通过。单片机采用STC89LE单片机(SD卡的初始化电压为2.0V~3.6V,操作电压为3.1V~3.5V,因此不能用5V单片机,或进行分压处理),工作于22.1184M的时钟下,由于所采用的单片机中没硬件SPI,采用软件模拟SPI,因此读写速率都较慢。如果要半SD卡应用于音频、视频等要求高速场合,则需要选用有硬件SPI的控制器,或使用SD模式,当然这就需要各位读者对SD模式加以研究,有了SPI模式的基础,SD模式应该不是什么难事

 

PARTNER CONTENT

文章评论1条评论)

登录后参与讨论

egaiai.1989_868445655 2011-7-6 08:24

不知道为什么,word文档中的图片都没办法传上去,如有需要的朋友,我可以提供PDF文档,里面的图就不会掉啦……
相关推荐阅读
egaiai.1989_868445655 2014-03-24 14:04
苹果车载系统CarPlay能否改变汽车业?
苹果推出车载系统CarPlay 在即将开幕的日内瓦车展前夕,苹果宣布推出车载系统CarPlay,与特斯拉等互联网汽车公司、谷歌等新科技巨头一同加入了智能汽车战场。 通过CarPlay将手机和汽车连...
egaiai.1989_868445655 2014-03-24 14:04
非特定人车载音响语音控制系统
随着现代电子技术在汽车系统中的不断应用,越来越多的车载电器加入到车身电子行列中,使得汽车的各种性能都得到了极大的改善,但汽车驾驶室的开关也越来越多,这就为驾驶员行车中对车载电器的操作提出了更高的要求,...
egaiai.1989_868445655 2014-03-24 14:04
Mentor Graphics大力发展车载信息娱乐系统
随着汽车电子化程度的推进,目前每辆车拥有大约100个ECU,这导致软件开发在汽车开发中的成本占比越来越高,而之前由于缺乏可重复使用的代码或太复杂或难以处理而无法适配的开源中间件,车载信息娱乐技术一直没...
egaiai.1989_868445655 2014-03-24 13:57
车里的未来谁做主?
互联网与汽车企业争抢肥肉   在移动互联网时代,距离人身体越近的终端设备越容易产生机会,于是有了之前的智能手机爆发,当下智能穿戴产品的群英逐鹿。面对着来势汹汹的移动浪潮,汽车场景下的“中控屏”之争...
egaiai.1989_868445655 2014-03-05 09:35
搬运工之MURATA村田的EMICON-FUN(1)
这是MURATA村田官网上的基础知识,觉得很有价值,搬运过来大家共享。 (一)功率电感器的额定电流为什么有两种? 近年来,在世界规模的节能化潮流中,对电子设备的低功耗要求也在不...
egaiai.1989_868445655 2013-08-09 10:44
吐槽样机计划
公司内部约定俗成的一个完整的项目开发会经过3个阶段:A Sample——B Sample——GOLDEN Sample,最后的量试属于生产阶段,开发一般不需要关注。正常的开发周期呢一般是3个月,这...
EE直播间
更多
我要评论
1
7
关闭 站长推荐上一条 /3 下一条