wshini7316 发表于 2012-5-11 17:58:07

spi初始化sd卡成功为什么读到的0扇区的数据都是0

初始化成功,读寄存器也正确,当读扇区的内容的时候读到的都是0 ,这是问什么呢?

hefanghua 发表于 2012-5-11 18:17:49

你读的是物理扇区。用WINHEX看下,注意上面显示的逻辑扇区与物理扇区的值,如果两者相同就只显示一个。

hhxb 发表于 2012-5-11 18:45:11

有可能你初始化没成功,所以全是0

byrin219 发表于 2012-5-11 19:00:55

2 楼正解..因为你的SD卡在电脑上格式化后是带FAT系统的吧? 带系统的是不能这样读了..用winhex看下 对比一下..建议先熟悉一下fat

xiaoziwen 发表于 2012-5-11 20:53:03

确定全是0。正常是只有16字节内容 其他是0

wshini7316 发表于 2012-5-11 21:22:02

hefanghua 发表于 2012-5-11 18:17 static/image/common/back.gif
你读的是物理扇区。用WINHEX看下,注意上面显示的逻辑扇区与物理扇区的值,如果两者相同就只显示一个。 ...

我是读卡的0扇区,是文件系统的启动扇区,可不应该都是0啊?

wshini7316 发表于 2012-5-11 21:24:11

byrin219 发表于 2012-5-11 19:00 static/image/common/back.gif
2 楼正解..因为你的SD卡在电脑上格式化后是带FAT系统的吧? 带系统的是不能这样读了..用winhex看下 对比一下 ...

不管带不带系统文件,启动扇区也是在0地址吧,我能读到其他一张sandisk的启动扇区,可是读这个卡就都是0.

wshini7316 发表于 2012-5-11 21:25:58

hhxb 发表于 2012-5-11 18:45 static/image/common/back.gif
有可能你初始化没成功,所以全是0

可是我初始化显示都成功了,而且能正确的读到寄存器的数值啊!那怎么才能确定我卡正常初始化了?

wshini7316 发表于 2012-5-11 21:30:19

我读的是地址0,物理地址的开始。

hefanghua 发表于 2012-5-12 08:35:15

我的一张2G卡,0扇区基本全为0,注意是基本为0,它在扇区快结束的字节有少量几个非0的数据,然后以55 AA结束该扇区。说明实际的引导扇区在刚才那几个字节所指的另一个扇区处。
如果还是调不出,建议用proteus仿真下SD(MMC)卡,并用SPI debugger查看通信过程。

dudududu 发表于 2012-5-12 11:23:16

楼上正解,SD卡的0扇区,如果你只把它分成一个区(就是没有多余的分区),那么0扇区前面的446字节的MBR基本上都是0x00,后面的DPT也只有第一个分区表项里面有些非零内容,然后结尾时0x55,0xAA;
这个也和不同的格式化软件有关系,有时候甚至没有MBR扇区,这时候0扇区就是DBR,前三个扇区通常是0xEB,0x58,0x90

wye11083 发表于 2012-5-12 14:31:22

0很正常。Windows格式化的MMC卡的第0扇区也是全0,我也读过。当我继续往后读约摸着十几个扇区才开始有非零值(好象是这样)。,

wshini7316 发表于 2012-5-13 22:00:00

wye11083 发表于 2012-5-12 14:31 static/image/common/back.gif
0很正常。Windows格式化的MMC卡的第0扇区也是全0,我也读过。当我继续往后读约摸着十几个扇区才开始有非零 ...

我记得上周5我测的时候不管读多少扇区一直都是0。

wshini7316 发表于 2012-5-13 22:16:36

dudududu 发表于 2012-5-12 11:23 static/image/common/back.gif
楼上正解,SD卡的0扇区,如果你只把它分成一个区(就是没有多余的分区),那么0扇区前面的446字节的MBR基本 ...

我用WinHEX查看过了,没有MBR,直接就是DBR。

wshini7316 发表于 2012-5-13 22:43:15

谁能提供我一份程序参考一下吗?

还有就是怎么做才能做到初始化、读sd卡的兼容性,才能保证我能识别所有的sd卡?

dudududu 发表于 2012-5-14 21:26:04

给你一个我正在用的程序,是在LM3S9B95上面用的,调用的是硬件SPI,所以你需要换用你自己的SPI函数,我实际使用,读扇区,写扇区,以及连续读扇区都没有问题,注释的话,有些地方可能还不是很准确,因为也是才写的,还没有完善到家

#define CMD0   (0x40+0)    /* GO_IDLE_STATE */
#define CMD1   (0x40+1)    /* SEND_OP_COND */
#define CMD8   (0x40+8)    /* SEND_IF_COND */
#define CMD9   (0x40+9)    /* SEND_CSD */
#define CMD10    (0x40+10)    /* SEND_CID */
#define CMD12    (0x40+12)    /* STOP_TRANSMISSION */
#define CMD16    (0x40+16)    /* SET_BLOCKLEN */
#define CMD17    (0x40+17)    /* READ_SINGLE_BLOCK */
#define CMD18    (0x40+18)    /* READ_MULTIPLE_BLOCK */
#define CMD23    (0x40+23)    /* SET_BLOCK_COUNT */
#define CMD24    (0x40+24)    /* WRITE_BLOCK */
#define CMD25    (0x40+25)    /* WRITE_MULTIPLE_BLOCK */
#define CMD41    (0x40+41)    /* SEND_OP_COND (ACMD) */
#define CMD55    (0x40+55)    /* APP_CMD */
#define CMD58    (0x40+58)    /* READ_OCR */

UCHAR DISK_TYPE=0;


// 选择SD卡, SFLASH_CS_PIN置高,SDCARD_CS_PIN置低
static void SD_CS_ON (void)
{
    GPIOPinWrite(SFLASH_CS_BASE, SFLASH_CS_PIN, SFLASH_CS_PIN);
    GPIOPinWrite(SDCARD_CS_BASE, SDCARD_CS_PIN, 0);
}

// 禁用SD卡,SDCARD_CS_PIN置高
static void SD_CS_OFF (void)
{
    GPIOPinWrite(SDCARD_CS_BASE, SDCARD_CS_PIN, SDCARD_CS_PIN);
}


// 设置SPI运行速度
static void set_spi_speed(UINT speed)
{
    unsigned long i;
    SSIDisable(SDC_SSI_BASE);
        if(speed > (SysCtlClockGet()/2000) ) i = SysCtlClockGet() / 2;
        else i = speed;
    if(i > 12500)
          { i = 12500; }
        i=i*1000;
    SSIConfigSetExpClk(SDC_SSI_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_0,
                     SSI_MODE_MASTER, i, 8);
    SSIEnable(SDC_SSI_BASE);
}


//-----------------------------------------------------------------------
// 通过SPI向MMC发送一个字节数据                                          
//-----------------------------------------------------------------------
static void spi_write_byte(BYTE dat)
{
    DWORD rcvdat;

    SSIDataPut(SDC_SSI_BASE, dat); /* 写一个字节到SPI的发送缓存 tx fifo */

    SSIDataGet(SDC_SSI_BASE, &rcvdat); /* 写的同时,读取flush data */
}


//-----------------------------------------------------------------------
// 通过SPI接受来自MMC的一个字节数据                                    
//-----------------------------------------------------------------------

static BYTE spi_read_byte (void)
{
    DWORD rcvdat;

    SSIDataPut(SDC_SSI_BASE, 0xFF); /* 写虚假数据 */

    SSIDataGet(SDC_SSI_BASE, &rcvdat); /* 从SPI接受缓存中读取数据 rx fifo */

    return (BYTE)rcvdat;
}

//等待disk准备就绪,超过50次则等待超时
static BYTE spi_wait_ready (void)
{
    BYTE res;
        UINT Timer = 20000;
    do
      res = spi_read_byte();
    while ((res != 0xFF) && Timer--);
    return res;
}

//-----------------------------------------------------------------------
// 发送80个时钟,CS和DI保持高电平
// SD卡上电后,转入SPI模式的时序要求               
//-----------------------------------------------------------------------
static void write_initial_80_clock (void)
{
    unsigned int i;
    DWORD dat;
    SD_CS_OFF();

    //切换SPI_TX端口为普通GPIO,且输出高电平
    GPIOPinTypeGPIOOutput(SDC_GPIO_PORT_BASE, SDC_SSI_TX);
    GPIOPinWrite(SDC_GPIO_PORT_BASE, SDC_SSI_TX, SDC_SSI_TX);

    //输出10字节0xff,使clock端口输出80个时钟信号
    for(i = 0 ; i < 20 ; i++)
    {
      SSIDataPut(SDC_SSI_BASE, 0xFF);                                //传输虚拟数据
      SSIDataGet(SDC_SSI_BASE, &dat);                                //结收虚拟数据
    }

    //切换SPI_TX端口为SPI模式
    GPIOPinTypeSSI(SDC_GPIO_PORT_BASE, SDC_SSI_TX);
}



//---------------------------------------------------
// 函数名:spi_write_cmd
// 功能:发送卡命令
// 参数:uint8 Cmd: SD指令
// 参数:uint32 arg
// 返回值:uint8
//---------------------------------------------------

UINT8 spi_write_cmd(UINT8 Cmd, UINT32 arg, UINT8 CRC)
{
         UINT8 r1;
         UINT8 retry=0;

         if (spi_wait_ready() != 0xFF) return 0xFF;
         spi_write_byte(Cmd);                     //写入命令
         spi_write_byte(arg>>24);                                   //写入命令参数,有些命令没有参数,写00
         spi_write_byte(arg>>16);
         spi_write_byte(arg>>8);
         spi_write_byte(arg);
         spi_write_byte(CRC);                     //写入有效的 CRC值
               retry = 10;
               do
                r1 = spi_read_byte();
                  while ((r1 & 0x80) && --retry);          //等待响应, 超时退出         
         return r1;                               //返回状态值
}

//-----------------------------------------------------------------------
// 函数名:disk_read_sectors
// 功能:读扇区
// 参数:UINT32 start_sector 起始扇区地址
// 参数:BYTE sector_number连续读出的扇区数,有效值1-255
// 参数:BYTE *buff读出数据缓存区
// 返回值:TRUE/FALSE                                                                 
//-----------------------------------------------------------------------

BYTE disk_read_sectors ( UINT32 sector, BYTE sector_number, BYTE *buff )
{
        UINT8 r1;
        UINT16 i;
        SD_CS_ON ();                                                                                   //开片选
        if(sector_number==1)
               {
             r1 = spi_write_cmd(CMD17, sector<<9, 0xff);      // 发读扇区命令,读取有效返回值(0x00)
             if(r1 != 0x00) return (0);                                           // 如果写命令返回值无效,则返回失败
             while(spi_read_byte() != 0xFE);                  // 等待数据的起始令牌号
             for(i=0; i<512; i++)                                                         // 读512个数据
                     {
                 *buff = spi_read_byte();
                       buff++;
                     }
             spi_read_byte();                                                        // 丢弃两字节CRC校验位
             spi_read_byte();
               }
        else if(sector_number>1)
                {
             r1 = spi_write_cmd(CMD18, sector<<9, 0xff);      // 发读扇区命令,读取有效返回值(0x00)
             if(r1 != 0x00) return (0);                                           // 如果写命令返回值无效,则返回失败
          do
                  {
                     while(spi_read_byte() != 0xFE);                  // 等待数据的起始令牌号
                     for(i=0; i<512; i++)                                                         // 读512个数据
                             {
                         *buff = spi_read_byte();
                               buff++;
                             }
                     spi_read_byte();                                                        // 丢弃两字节CRC校验位
                     spi_read_byte();
          } while(--sector_number);
          spi_write_cmd(CMD12, 0, 0xff);      // 发读扇区命令,读取有效返回值(0x00)
                spi_read_byte();                                                        // 丢弃两字节CRC校验位
          spi_read_byte();
                }
        SD_CS_OFF();                                                               // 关闭SD Card
        return (1);
}

//-----------------------------------------------------------------------
// 函数名:disk_write_sectors
// 功能:写扇区
// 参数:UINT32 start_sector 起始扇区地址
// 参数:BYTE sector_number连续写入的扇区数,有效值1-255
// 参数:BYTE *buff等待写入的数据
// 返回值:TRUE/FALSE
//-----------------------------------------------------------------------
#ifREADONLY == 0
BYTE disk_write_sectors ( UINT32 sector, BYTE sector_number, BYTE *buff)
{
   UINT8 r1=0;
   UINT16 i=0;
       SD_CS_ON ();                                                           //开片选
   r1 = spi_write_cmd(CMD24, sector<<9, 0xff);      // 发写扇区命令,读取有效返回值(0x00)
   if(r1 != 0x00) return 0x00;                                                 // 如果写命令返回值无效,则返回失败
   if(spi_wait_ready() != 0xFF) return 0x00;

   spi_write_byte(0xFE);                                                         // 发送数据起始令牌号
          
   for(i=0; i<512; i++)                                                         // 以扇区为单位写入数据
          {
      spi_write_byte(*buff);
                buff++;
          }
   spi_write_byte(0xFF);                                                         // 发送两字节伪CRC
   spi_write_byte(0xFF);
   r1 = spi_read_byte();                                                         // 读数据应答令牌号(有效令牌xxx00101B)
   if( (r1&0x1F) != 0x05)                                                 // 判断令牌是否正确
             {
          SD_CS_OFF();                                                       // 如果令牌错误,关闭SD Card
         return 0x00;                                                                         // 如果令牌错误,返回失败
             }
   while(spi_read_byte()==0);                                                 //--等待操作完成--
   SD_CS_OFF();                                                               // 关闭SD Card
   return 0x01;        
}
#endif

//SPI初始化
void spi_Interface_Init(void)
{

    SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0);                   //使能SSI模块

    SysCtlPeripheralEnable(SDC_SSI_SYSCTL_PERIPH);                                  //使能SSI0接口所在的GPIO端口
    SysCtlPeripheralEnable(SDC_GPIO_SYSCTL_PERIPH);
    SysCtlPeripheralEnable(SDCARD_CS_PERIPH);
    SysCtlPeripheralEnable(SFLASH_CS_PERIPH);


    GPIOPinTypeSSI(SDC_GPIO_PORT_BASE, SDC_SSI_TX | SDC_SSI_RX | SDC_SSI_CLK);//定义SPI端口
    GPIOPinTypeGPIOOutput(SDCARD_CS_BASE, SDCARD_CS_PIN);                                                //设置SDCARD_CS_PIN
    GPIOPinTypeGPIOOutput(SFLASH_CS_BASE, SFLASH_CS_PIN);                                                //设置SFLASH_CS_PIN
    GPIOPadConfigSet(SDC_GPIO_PORT_BASE, SDC_SSI_PINS | SDC_SSI_TX | SDC_SSI_RX | SDC_SSI_CLK,
                                       GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD_WPU);
    GPIOPadConfigSet(SDCARD_CS_BASE, SDCARD_CS_PIN, GPIO_STRENGTH_4MA,
                     GPIO_PIN_TYPE_STD_WPU);
    GPIOPadConfigSet(SFLASH_CS_BASE, SFLASH_CS_PIN, GPIO_STRENGTH_4MA,
                     GPIO_PIN_TYPE_STD_WPU);

    GPIOPinWrite(SDCARD_CS_BASE, SDCARD_CS_PIN, SDCARD_CS_PIN);                                        //SDCARD_CS_PIN 置1
    GPIOPinWrite(SFLASH_CS_BASE, SFLASH_CS_PIN, SFLASH_CS_PIN);                                        //SFLASH_CS_PIN        置1

    //SSI配置:基址,协议格式,主/从模式,位速率,数据宽度
    SSIConfigSetExpClk(SDC_SSI_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_0,
                     SSI_MODE_MASTER, 100000, 8);

    SSIEnable(SSI0_BASE);                                 //使能SSI收发
}


//---------------------------------------------------
// 函数名:MMC_SD_Init
// 功能:sd卡初始化
// 参数:无
// 返回值:uint8 type         // 卡类型返回值: 0x10 SD, 0x20 MMC
//---------------------------------------------------
BYTE SD_Init(void)
{
        UINT8 retry;
        UINT8 r1=0;
        spi_Interface_Init();
        set_spi_speed(100);                                         //低速 SPI=100k
        retry = 0;
       
        do
                {
                write_initial_80_clock();                                    // 发送80个时钟,使卡同步
                SD_CS_ON();                                               // 使能Card
                r1 = spi_write_cmd(CMD0, 0, 0x95);         // 发Cmd0(复位)命令
                retry++;
                if(retry > 200) return (ERROR);                         //超时退出,个别卡需要更多次循环才有反应
        } while(r1 != 0x01);                               // MMC、SD卡成功转到SPI模式
        retry = 2;
               
        while(retry--)spi_write_byte(0xFF);               
               
        do
               {
               r1 = spi_write_cmd(CMD55, 0, 0xff);      // 先发送 Cmd55
               if(r1 == 0x01)                                                                // 如果有反应
                     {
                     r1 = spi_write_cmd(CMD41,0, 0xff);   // 再发送 Cmd41 进行激活
                     if(r1 == 0x00) DISK_TYPE = SD_CARD;                 // 激活成功就是SD卡
                     }
               else {                                     // 如果发送 Cmd55无反应,改发送 Cmd1
                     r1 = spi_write_cmd(CMD1,0, 0xff);
                     if(r1 == 0x00) DISK_TYPE = MMC_CARD;                 // 激活成功就是MMC卡
                     }
               
               retry++;
               if(retry > 255) return (TIME_OUT);      // 超时退出, 个别卡需要更多次循环才有反应
        } while(r1 != 0x00);                                       // MMC、SD卡激活后的返回值均为0x00
       
        r1 = spi_write_cmd(CMD16, 512, 0xff);          //设置读取一次的字节数
        if(r1!=0x00)return (TIME_OUT);
       
        SD_CS_OFF();                         // 关闭Card
        spi_write_byte(0xFF);                                              // 高速SPI前先发送8个时钟
        if(DISK_TYPE!=0) set_spi_speed(12500);                      // 初始化成功,切换到高速 SPI 12.5M
       
        return SUCCESS;         //参数返回,1为初始化错误,10为SD卡,20为MMC卡
}


wshini7316 发表于 2012-5-15 21:26:59

dudududu 发表于 2012-5-14 21:26 static/image/common/back.gif
给你一个我正在用的程序,是在LM3S9B95上面用的,调用的是硬件SPI,所以你需要换用你自己的SPI函数,我实际 ...

谢谢!我先参考一下。我现在的程序能完成sandisk2G卡的初始化和数据扇区的读取,但是其他的卡就很难初始化。我现在使用飞线的方式连接的会有问题吗?

dudududu 发表于 2012-5-16 11:04:30

飞线??
木有问题,我的也是飞线,然后目前只能读取小于4G的卡,因为32位的寻址限制(还不知道SD卡更大空间的寻址指令)

wshini7316 发表于 2012-5-17 12:55:30

dudududu 发表于 2012-5-16 11:04 static/image/common/back.gif
飞线??
木有问题,我的也是飞线,然后目前只能读取小于4G的卡,因为32位的寻址限制(还不知道SD卡更大空 ...

整合你的程序之后还是不能把4张卡都初始化,也只能完成2张。我将stm32开发板上用的一个程序(在开发板上可以识别4张卡)移植过来,出现同样的问题。
spi发送接收数据的过程如下正确吗?
u8 _spi_read_write(u8 data)
{
        u8 temp;
           
        SSI_DR = data;                /* 发送数据放入SPI数据寄存器 */
   
        while(0 == (SSI_SR & (1<<2)));        /* 等待数据发送完毕 */
       
        while(0 == (SSI_SR & (1<<3)));/* 等待数据接收完毕*/

        temp = SSI_DR;                        /* 读取接收数据*/
       
        while(SSI_SR&1);                      /* 等待spi操作完成毕*/
       
        return temp;
}

wshini7316 发表于 2012-5-17 12:56:33

对于sd卡的spi配置需要注意什么东西吗?

dudududu 发表于 2012-5-17 17:34:11

我勒个去模拟要同时搞四张SD卡,我就搞定一张而已哈。
关于SPI发送和接受数据,因为我用的LM3S9B96,有硬件SPI,只要把要发送的数据写到发送寄存器就可以发送,接受的话,只需要读取接受寄存器就可以,很简单的,你这个就不是很清楚了;
不过,好像SPI是同时收发数据的,所以我的程序里,接受的时候,也在发送无效的数据,而发送的时候,也是在接受数据的,只不过丢弃不用而已,
SD卡的配置,看我程序里面的初始化就可以,不过可能还不是很完善,可能有些卡不能很好的识别和兼容,我水平也有限哈,找找其他高手的例程,兼容性可能更好些!!

wshini7316 发表于 2012-5-17 17:36:22

今天又测到,当我CMD0返回0x01------CMD8返回0x01(得到对应卡的类型)------发送CMD55返回0x01------发送ACMD41没有反应(命令超时,当我把时钟提高到400k以上之后可以得到响应但是数据不正确),如果ACMD41超时之后再去发送CMD55也回超时没有响应,谁知道这是为什么吗?

wshini7316 发表于 2012-5-17 17:37:59

我对ADATA卡初始化正确,之后读任何扇区的数据都是0,能够正确得到数据的起始令牌0xfe,之后接受到的数据都是0,是什么原因?

wshini7316 发表于 2012-5-17 17:43:21

dudududu 发表于 2012-5-17 17:34 static/image/common/back.gif
我勒个去模拟要同时搞四张SD卡,我就搞定一张而已哈。
关于SPI发送和接受数据,因为我用的LM3S9B96,有硬件 ...

做芯片测试,做方案,当然要兼容所有的卡了,不能指定客户用什么卡啊。


我用的也是硬件spi,我的理解也是这样的,在发送的时候同时读取接受数据丢弃,在接受的时候同时发送无效数据。

我这有其他的程序在stm32开发板上就能识别这4张卡,但是同样的程序到我这里就只能识别2张,还有一张读出数据都是0.

canaan 发表于 2012-6-3 00:56:32

{:dizzy:}郁闷啊,搞了好久

llf021421 发表于 2012-6-3 11:49:43

注意在SDHC卡中的寻址方式,与一般的SD卡不同,一般的SD卡的方式是字节寻址,而SDHC寻址方式是块寻址,所以在SDHC中,32位地址线寻址范围可以达到(2^32)*512=2TB

llf021421 发表于 2012-6-3 11:53:18

请参考atmel的SD卡的初始化程序,它能够初始化各种类型的SD卡

zsmj0513 发表于 2012-6-6 11:13:34

学习了!!!!!!!!!!!!!!!!!!!!1

stirwl 发表于 2012-11-3 21:20:05

我读SD卡读了很多个扇区也全是0,郁闷,不知道是什么原因,
楼主解决了吗?

xiaosuo521 发表于 2014-6-20 15:41:28

楼主,我的也是同样问题,初始化成功但读0扇区的数据全是0x00,用winhex看的0扇区不全是0啊,有正常的显示信息值,为什么呢
页: [1]
查看完整版本: spi初始化sd卡成功为什么读到的0扇区的数据都是0