使用SPI模式从SD卡中读取单个块的方法(目前出现奇怪的行为)

3
当我从SPI总线上的PIC-18F4520向卡片发出地址为(0x00000000)的cmd17命令时,我会从命令发出处得到一个正确的R1标记。然后,在几次循环检查后,我会从我的发行SPI_Put_Char(0xFF)中获得一个0xFE标记。接下来应该是数据,所以我将512字节读入我的IO_Buffer数组。当我扫描返回时,我得到许多0x00字节。奇怪的是,在第0个扇区的位置448左右,一些数据会被传输过来 - 有一些字节 - 然后最后的32个字节(我只能在我的LCD屏幕上一次查看32个字节)都是零,后面是期望在引导扇区结尾的0x55AA标记。
奇怪的是,使用磁盘调查程序可以发现SD卡具有正确的零扇区信息 - MSDOS消息,EB跳转代码等等。但我的读取命令将所有这些都返回为零。我真的不知道发生了什么。
其他信息:我使用cmd0,cmd8,cmd58和OCR启动读取正常。然后是acmd41(循环cmd55后跟APP_SEND_OP_COND)。所有似乎都有响应并给出预期的标记。最后,我甚至使用SEND_CID获取卡片信息。返回MID = 3 OID = SD和版本SD017,后跟其他信息 - 所有似乎都是正确的。
我尝试在来自卡的DOUT上添加上拉和下拉电阻,但没有影响任何结果。
我渴望尝试一些想法来使这张卡正确读取。顺便说一句,我已经尝试了另外两张卡。它们给出不同的具体结果,但定性相同 - 初始化,OCR和CID读取都可以正常工作。数据读取大部分为零,然后是一些可重现但稀疏的字节,以及一个0xAA55标记!?
我的SanDisk 1GB SD卡正在以3.296伏特运行,在读卡过程中似乎很稳定。
以下是一些代码:
    bit MMC_Command(unsigned char cmd, unsigned short AdrH, unsigned short AdrL, unsigned char *response)
{
    unsigned char response_length;
    unsigned char MMC_Counter_Byte = 255;
    unsigned char current_response;

    switch (cmd)
    {
        case MMC_SEND_IF_COND:
        case MMC_READ_OCR:
            response_length = 5;
            break;
        case MMC_SEND_STATUS:
            response_length = 2;
            break;
        default:
            response_length = 1;
    };

    DEV_xSELECT = DEV_MMC;

    SPI_Put_Char(cmd);
    SPI_Put_Char(AdrH >> 8);
    SPI_Put_Char(AdrH & 0x00FFU);
    SPI_Put_Char(AdrL >> 8);
    SPI_Put_Char(AdrL & 0x00FFU);
    SPI_Put_Char(0x95U); //CRC = 0x95 to get to SPI, then value not important, so always use this for convenience

    do
    {
        response[0] = SPI_Put_Char(0xFF);
    } while ((response[0] & 0x80) && --MMC_Counter_Byte);
    if (!MMC_Counter_Byte)
    {
        //SPI_Put_Char(0xFF); //some say is necessary
        DEV_xSELECT = DEV_NONE;
        return FALSE;
    };

    for (current_response = 1; current_response < response_length; current_response++)
    {
        response[current_response] = SPI_Put_Char(0xFF);
    };

    SPI_Put_Char(0xFF); //some say is necessary
    DEV_xSELECT = DEV_NONE;
    return TRUE;
};

    unsigned char MMC_Init_SD(void)
{
    unsigned long MMC_Counter_Word;
    unsigned char response[5];

    DEV_xSELECT = DEV_MMC;

    for (MMC_Counter_Word = 0; MMC_Counter_Word < 20; MMC_Counter_Word++)
    {
        SPI_Put_Char(0xFFU);
    };

    DEV_xSELECT = DEV_NONE;

    for (MMC_Counter_Word = 0; MMC_Counter_Word < 10; MMC_Counter_Word++)
    {
        SPI_Put_Char(0xFFU);
    };

    MMC_Counter_Word = 255;
    do
    {
        MMC_Command(MMC_GO_IDLE_STATE, 0x0000, 0x0000, response); //cmd0
    } while (--MMC_Counter_Word && (response[0] != 0x01));
    if (!MMC_Counter_Word) //if counter timed out, error
    {
        return FALSE;
    };

    MMC_Command(MMC_SEND_IF_COND, 0x0000, 0x01AA, response); //cmd8
    if (response[0] != 0x05)
    {
        return FALSE; //other card type
    };

    MMC_Command(MMC_READ_OCR, 0x0000, 0x0000, response); //cmd58

    MMC_Counter_Word = 0xFFFFU;
    do
    {
        if (MMC_Command(MMC_APP_CMD, 0x0000, 0x0000, response)) //cmd55
        {
            MMC_Command(MMC_APP_SEND_OP_COND, 0x4001, 0x0000, response); //acmd41
            SPI_Put_Char(0xFF);
        }
        else
        {
            return FALSE;
        };
    } while (--MMC_Counter_Word && ((response[0] & 1) == 1));
    if (!MMC_Counter_Word) 
    {
        return FALSE;
    };  

    if (MMC_Command(MMC_SEND_CID, 0x0000, 0x0000, response)) //cmd10
    {
        DEV_xSELECT = DEV_MMC;

        MMC_Counter_Word = 255;
        while (--MMC_Counter_Word && (SPI_Put_Char(0xFF) != 0xFE));
        if (!MMC_Counter_Word)
        {
            DEV_xSELECT = DEV_NONE;
            return FALSE;
        };

                //code for reading 16 byte OCR goes here

        SPI_Put_Char(0xFFU);
        SPI_Put_Char(0xFFU); //cycle through 16-bit CRC
        SPI_Put_Char(0xFFU); //1GB Sandisk SD seems to require another dummy

        DEV_xSELECT = DEV_NONE;
        Delay_Sec(2);
        LCD_CLS();
    }
    else
    {
        return FALSE;
    };

    return TRUE;
};

    bit MMC_Fill_IO_Buffer(unsigned long sector)
{
    unsigned short MMC_Fill_Index_Byte;
    unsigned char MMC_Counter_Byte = 255;
    unsigned char response[1];  

    if (MMC_Command(MMC_READ_SINGLE_BLOCK, 0x0000, 0x0000, response)) //cmd10
    {
        DEV_xSELECT = DEV_MMC;

        MMC_Counter_Byte = 255;
        while (--MMC_Counter_Byte && (SPI_Put_Char(0xFF) != 0xFE));
        if (!MMC_Counter_Byte)
        {
            DEV_xSELECT = DEV_NONE;
            return FALSE;
        };
    }
    else
    {
        return FALSE;
    };

    for (MMC_Fill_Index_Byte = 0; MMC_Fill_Index_Byte < 512 ; MMC_Fill_Index_Byte++)
    {
        IO_Buffer[MMC_Fill_Index_Byte] = SPI_Put_Char(0xFF);
    };
    SPI_Put_Char(0xFFU);
    SPI_Put_Char(0xFFU); //cycle through 16-bit CRC
    SPI_Put_Char(0xFFU); //1GB Sandisk SD seems to require another dummy
    DEV_xSELECT = DEV_NONE;

    //following is IO_Buffer displaying code.
    //LCD_CLS();
    //for (MMC_Counter_Byte = 0; MMC_Counter_Byte < 42; MMC_Counter_Byte++)
    //{
    //  LCD_Draw_Byte_Hex(IO_Buffer[MMC_Counter_Byte + 448]);
    //};
    //while (1);

    return TRUE;
};

提前感谢你!

(这是需要翻译的内容)

我有与此处描述的问题完全相同的问题:http://electronics.stackexchange.com/questions/23053/problems-reading-data-from-a-microsd-card 想不出任何可能的原因... - Armandas
2个回答

2

您的0扇区看起来像是一个有效的分区表。如果您使用磁盘检查器从驱动器字母读取,您可能会读取到分区的0扇区而不是SD卡本身。这个程序似乎不能从物理设备读取,因此您无法使用它来读取分区表。


1

终于找到解决办法了!

原来你正在读取位于SD卡地址0处的MBR。要找到引导扇区的位置,需要读取MBR中相应条目。 条目从地址0x01be开始,每个条目有16字节。 条目中感兴趣的点位于偏移量0x08处,长度为4字节,称为LBA。 [维基百科] 要获取引导扇区位置的地址,需要将LBA乘以扇区大小(512字节)。 [Microchip论坛]

例如,请参见我的其他回答


这大致就是Turbo J所说的(分区表部分)。 :) -注意:你应该检查的第一件事是刚刚读取的扇区是否在末尾有0x55,0xaa签名。如果有,那么它可能是分区表或引导扇区。现在你必须检查所有可能的字段以获取合理的值。记得允许在同一个扇区中同时存在分区表和引导扇区! - user1985657

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接