【SD卡】关于DJYOS下SD卡驱动开发详解

关于DJYOS下SD卡驱动开发详解

                                                                             王建忠 2011/6/21

 

1      开发环境及说明

硬件平台:tq2440(CPU: s3c2440)

操作系统:DJYOS1.0.0

 

1.1    说明

Tq2440开发板用的cpu是S3C2440,由于s3c2440自带SD卡控制器,故我们选择操作SD卡的模式是SD卡模式。对SPI模式的SD卡驱动,这里我就不说明了。等以后在djyos下有移植spi模式的SD卡驱动,我就再写一篇技术文章。

网上关于SD卡得文章非常多,在这里关于SD卡协议等等细节的,我就不介绍了。大家去网上搜索几篇看看,在这里,我就简单介绍一点以及主要还是讲DJYOS下的SD卡驱动。

注意:这文档附带的sd卡驱动,不支持sdv2.0以上的sd卡(即2G以上sd卡不支持)

1.2    SD卡电路图

下面是tq2440开发板上面的SD卡模块电路图。

2      SD卡简介

2.1    SD卡引脚解析

 

2.2    SD卡命令解析

2.2.1  SD卡的命令格式:

SD卡的指令由6字节(Byte)组成,如下:

Byte1:0 1 x x x x x x(命令号,由指令标志定义,如CMD39为100111即16进制0x27,那么完整的CMD39第一字节为01100111,即0x27+0x40)

Byte2-5:Command Arguments,命令参数,有些命令没有参数

Byte6:前7位为CRC(CyclicRedundacy Check,循环冗余校验)校验位,最后一位为停止位0

2.2.2  SD卡的命令

SD卡命令共分为12类,分别为class0到class11,

不同的SDd卡,主控根据其功能,支持不同的命令集 如下:

Class0 :(卡的识别、初始化等基本命令集)

CMD0:复位SD 卡.

CMD1:读OCR寄存器.

CMD9:读CSD寄存器.

CMD10:读CID寄存器.

CMD12:停止读多块时的数据传输

CMD13:读 Card_Status 寄存器

Class2 (读卡命令集):

CMD16:设置块的长度

CMD17:读单块.

CMD18:读多块,直至主机发送CMD12为止 .

Class4(写卡命令集) :

CMD24:写单块.

CMD25:写多块.

CMD27:写CSD寄存器 .

Class5 (擦除卡命令集):

CMD32:设置擦除块的起始地址.

CMD33:设置擦除块的终止地址.

 

CMD38: 擦除所选择的块.

Class6(写保护命令集):

CMD28:设置写保护块的地址.

CMD29:擦除写保护块的地址.

CMD30: Ask the card for the status of thewrite protection bits

class7:卡的锁定,解锁功能命令集

class8:申请特定命令集。

class10 -11 :保留

 

 

3      Djyos下开发SD卡驱动

Tq2440,自带的sd卡程序。只是从0地址开始。简单的测试,可以读写就行。整个代码比较乱,sd卡驱动接口没有规范好。我在这里,根据DJYOS的需求,将tq2440自带的代码进行修改,变成只支持SD卡驱动,不支持MMC卡驱动。并且提供可以供上层使用的SD卡读写接口。

3.1    SD卡初始化

3.1.1  SD卡初始化流程图

图3-1,是SD卡初始化的流程图。

图3-1 SD卡初始化流程图

3.1.2  Sd卡初始化函数module_init_SD

module_init_SD(),是SD卡模块初始化函数。本函数,在djyos源码main.c文件djy_main()函数中调用。只要被调用了,这个时候上层就可以调用SD卡的读写函数了。

       module_init_SD的源码,在3.1.1节的流程图介绍的已经比较清楚了。下面的代码里,也带有详细的注释。就不做很多解释了。下面代码中,有些带有MMC的代码,是因为MMC和SD卡类似,为了以后兼容MMC卡。

 

int module_init_SD(void)

{

 

       int i;

 

       RCA=0;

       MMC=0;

       //GPIO初始化

       rGPEUP  = 0xf83f;     // SDCMD, SDDAT[3:0] => PU En.

       rGPECON = 0xaaaaaaaa;      //SDCMD, SDDAT[3:0]

    //设置频率为400KHz

       rSDIPRE=cn_pclk/(INICLK)-1;

       rSDICON=(1<<4)|1;     // Type B, clk enable

       rSDIFSTA=rSDIFSTA|(1<<16);   //YH 040223 FIFO reset

       rSDIBSIZE=0x200;              // 512byte(128word)

       rSDIDTIMER=0x7fffff;              // Set timeout count

    // Wait 74SDCLK for MMC card

       for(i=0;i<0x1000;i++); 

    //发送命令0,使sd卡进入idle状态

       cmd0();

 

       //检查是否是MMC卡,并且设置目标工作电压

       //设置MMC,为了以后兼容MMC

       if(chk_MMC_OCR())

       {

              MMC=1;

              goto RECMD2;

       }

       //检查是否是SD卡,并且设置目标工作电压

       if(!chk_SD_OCR())

              return 0;

RECMD2:

       rSDICARG=0x0;   // CMD2(stuff bit)

       rSDICCON=(0x1<<10)|(0x1<<9)|(0x1<<8)|0x42; //lng_resp, wait_resp, start, CMD2

       //发送CMD2,获取SD卡身份信息,并且使卡进入identification模式

       if(!chk_cmd_end(2, 1))

              goto RECMD2;

       rSDICSTA=0xa00; // Clear cmd_end(with rsp)

RECMD3:

       //--Send RCA

       rSDICARG=MMC<<16;           // CMD3(MMC:Set RCA, SD:Ask RCA-->SBZ)

       rSDICCON=(0x1<<9)|(0x1<<8)|0x43; // sht_resp, wait_resp, start, CMD3

       //发送CMD3,SD卡获取RCA,进入stand_by状态

       if(!chk_cmd_end(3, 1))

              goto RECMD3;

   

       rSDICSTA=0xa00; // Clear cmd_end(with rsp)

    RCA=( rSDIRSP0 & 0xffff0000 )>>16;

    rSDIPRE=cn_pclk/(SDCLK)-1;    // Normal clock=25MHz

    //--State(stand-by) check

       if( rSDIRSP0 & 0x1e00!=0x600 )  // CURRENT_STATE check

              goto RECMD3;

       card_sel_desel(1);   // Select

       set_4bit_bus();

       return 1;

}

3.2    SD卡接口

SD卡接口,就是指SD卡得读写函数。在这里,我们将使用读写一扇区,作为对应用层的接口,所以读写SD卡,都是一次性一个扇区的读写。没有精确到某个地址读写。

Tq2440下的SD卡,是直接由CPU控制的。CPU,拥有自己的SD卡控制器。对SD卡读写接口,在这里涉及到一个比较重要的寄存器,对其配置比较多。就是SDI数据控制寄存器(rSDIDCON)。

       SD卡读写,基本上都是在该寄存器进行配置,所以配置这个寄存器的时候要特别注意。下面是该寄存器每比特位代表的含义。想看其他更多有关于SD卡控制器的寄存器,请看S3C2440A的芯片资料。

保留 [31:25]

Burst4 enable   [24]    在DMA模式下使能Burst4。仅当数据大小是字时该位被

 (Burst4)               置位。0:无效 1:Burst4使能

    

Data Size       [23:22] 指出用FIFO传输的大小,哪个类型,半字或字。00 = 字节

(DataSize)              传输,01 =半字传输 10 = 字传输, 11 = 保留

 

SDIO Interrupt  [21]    决定SDIO的中断周期是 2个周期还是外部更多周期,当数据块

Period Type(PrdType)    最后被发送(对SDIO)。0=正好两个周期 1=更多周期(像单周期)

   

Transmit After  [20]    决定数据传输在响应收到后开始或不开始。0= 在DatMode设置后直接

Response(TARSP)         1= 在响应收到后(假定设置DatMode设为 2b11)

 

Receive After   [19]    决定数据传输在命令发出后开始或不开始 0= 在DatMode设置后直接

Command(RACMD)          1= 在命令发出后(假定设置DatMode设为2b10)

 

Busy After      [18]    决定忙接收在命令发出后开始或不开始 0= 在DatMode设置后直接

Command(BACMD)          1= 在命令发出后(假定设置DatMode设为2b01)

 

Block mode      [17]    数据传输模式 0=流数据传输 1=模块数据传输

 (BlkMode)

 

Wide bus        [16]    决定使能宽总线模式 0:标准总线模式(仅使用SDIDAT[0])

enable(WideBus)         1:宽总线模式(使用SDIDAT[3])

 

DMA Enable      [15]    使能DMA(当DMA操作完成时,该位应该无效)

(EnDMA)                 0:无效(查询) 1:DMA使能

 

Data Transfer   [14]    决定数据传输是否开始,该位自动清除。

Start(DTST)             0:数据准备好, 1:数据开始

 

Data Transfer   [13:12] 决定数据传输的方向 00 =无操作, 01 = 仅忙检测模式

Mode (DatMode)          10 =数据接收模式,11 =数据发送模式

 

BlkNum          [11:0] 模块数(0~4095),当流模式时不考虑

 

3.2.1  读一扇区read_one_sector

下面代码,是读一扇区的代码。其实整个代码的核心部分。是上面一开始就说的rSDIDCON寄存器的配置。现在我们就大概的讲一下读一个扇区的过程

1、 复位FIFO

2、 配置rSDIDCON寄存器

(2<<22)|(1<<19)|(1<<17)|(1<<16)|(1<<14)|(2<<12)|(1<<0)

分别代表:使用一字传输、数据传输在命令开始后、使用模块数据传输、使用宽总线模式(使用4个数据地址线引脚)、数据开始,该位自动清零、启动数据接收模式(用户读取SD卡的意思)、模块数为1。

3、 设置sector_addr,这里左移9位。代表的是扇区的意思,扇区是512字节大小(1左移9位,是512)。

4、 通过配置rSDICCON寄存器,开始读取数据。数据是一个字一个字的读。所以512字节,读取128次。

5、 通过chk_dat_end,检查数据是否结束

6、 一些清除操作,见代码

 

void read_one_sector(u32 sector_addr,unsigned int *buf)

{

    unsigned int value;

       int status;

 

       rd_cnt=0;   

       rSDIFSTA=rSDIFSTA|(1<<16);   // FIFO reset  

    =(2<<22)|(1<<19)|(1<<17)|(1<<16)|(1<<14)|(2<<12)|(1<<0);   //YH 040220

       //rSDIDCON,[11:0] 我设置是1,所以这里block_addr,必须是地址,而不是扇区号

       //移动9位,512字节。这样,block_addr就变成了"扇区号"了

       rSDICARG= sector_addr <<9;      // CMD17/18(addr)

 

    //开始准备读取数据

   

    rSDICCON=(0x1<<9)|(0x1<<8)|0x51;    // sht_resp, wait_resp, dat, start, CMD17

    if(!chk_cmd_end(17, 1))       //-- Check end of CMD17

        return ;

    rSDICSTA=0xa00; // Clear cmd_end(with rsp)       

    while(rd_cnt<128)  // 128word=512byte

    {

        if((rSDIDSTA&0x20)==0x20) // Check timeout

        {

            rSDIDSTA=(0x1<<0x5);  // Clear timeout flag

            break;

        }

        status=rSDIFSTA;

        if((status&0x1000)==0x1000)      // Is Rx data?

        {

            value = rSDIDAT;

            byte_conversion(&value,buf);

            *buf++;

            rd_cnt++;

        }

    }

    //-- Check end of DATA

       if(!chk_dat_end())

              return ;

 

       rSDIDCON=rSDIDCON&~(7<<12);         

       rSDIFSTA=rSDIFSTA&0x200;    //Clear Rx FIFO Last data Ready

       rSDIDSTA=0x10;  // Clear data Tx/Rx end detect

 

}

 

3.2.2  写一扇区write_one_sector

下面代码,是写一扇区的代码。写一个扇区的大概过程如下:

1、 复位FIFO

2、 配置rSDIDCON寄存器

 (2<<22)|(1<<20)|(1<<17)|(1<<16)|(1<<14)|(3<<12)|(1<<0)

分别代表:使用一字传输、数据传输在命令开始后、使用模块数据传输、使用宽总线模式(使用4个数据地址线引脚)、数据开始,该位自动清零、启动数据发送模式(用户写入SD卡的意思)、模块数为1。

3、 设置sector_addr,这里左移9位。代表的是扇区的意思,扇区是512字节大小(1左移9位,是512)。

4、 通过配置rSDICCON寄存器,开始写入数据。数据是一个字一个字的写入。所以512字节,写入128次。

5、 通过chk_dat_end,检查数据是否结束

6、 一些清除操作,见代码

 

void write_one_sector(u32 sector_addr,unsigned int *buf)

{

       u32 value;

       int status;

 

    buf=(u32*)buf;//由于FAT是是以u8传入。不知道上面是否强制转换了

       wt_cnt=0;   

       rSDIFSTA=rSDIFSTA|(1<<16);   // FIFO reset

  

    rSDIDCON=(2<<22)|(1<<20)|(1<<17)|(1<<16)|(1<<14)|(3<<12)|(1<<0);       //YH 040220

    //扇区号转为地址,移动9位,是512字节的意思

       rSDICARG= sector_addr <<9;          // CMD24/25(addr)

    rSDICCON=(0x1<<9)|(0x1<<8)|0x58; //sht_resp, wait_resp, dat, start, CMD24

    if(!chk_cmd_end(24, 1))       //-- Check end of CMD24

        return;

    rSDICSTA=0xa00; // Clear cmd_end(with rsp)

    while(wt_cnt<128)

    {

        status=rSDIFSTA;

        if((status&0x2000)==0x2000)

        {

            byte_conversion(buf,&value);

            rSDIDAT=value;

            *buf++;

            wt_cnt++;

        }

    }

       if(!chk_dat_end())

              return;

    // Clear Data Transfer mode => no operation,Cleata

    //Data Transfer start

       rSDIDCON=rSDIDCON&~(7<<12);         

       rSDIDSTA=0x10;  // Clear data Tx/Rx end

}

 

你可能感兴趣的:(工作,cmd,command,Module,Class,byte)