如何使用 SPI 模式从 SD 卡读取单块(到目前为止,行为很奇怪)?
当我从 SPI 总线上的 PIC-18F4520 向我的卡发出地址为 (0x00000000) 的 cmd17 时,我从命令发出中获得了正确的返回 R1 令牌。然后,经过几次循环检查后,我得到了从发出的 SPI_Put_Char(0xFF) 返回的 0xFE 标记。然后数据应该开始,因此我将 512 字节读入 IO_Buffer 数组。当我扫描返回时,我得到了许多 0x00 字节。奇怪的是,重复地,在扇区 0 的 pos 448 处,一些数据过来了——这里有几个字节,那里有几个字节——然后最后的 32 个字节(我一次只能在液晶屏上查看 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;
};
提前致谢!
When I issue cmd17 with address (0x00000000) to my card from PIC-18F4520 on SPI bus, I get a correct return R1 token from the command issue. Then, after a few loops checking, I get a 0xFE marker returned from my issuing SPI_Put_Char(0xFF). Data should then start so I read 512 bytes into my IO_Buffer array. As I scan the returns, I got many 0x00 bytes. Oddly, and repeatedly, at about pos 448 in sector 0, some data comes over - a few bytes here and there - then the the final 32 bytes (I can only view 32 at a time on my LCD screen) are all zeroes followed by the 0x55AA marker expected at the end of the boot sector.
The odd thing, is that using disk investigator reveals the SD card has the proper sector zero information - MSDOS message, EB jump code, all sorts of stuff. My read command gives all that back as zeroes. I just don't get what's happening.
Other information: I boot with the cmd0, cmd8, cmd58 and OCR reads fine. Then acmd41 (looping cmd55 followed by APP_SEND_OP_COND). All seem to respond and give expected marker. Finally, I even use SEND_CID to get the card information. that returns MID=3 OID=SD and a verion SD017 followed by other information - seems all to be correct.
I have tried adding pull up and pull down resistors on DOUT from card but doesn't affect any results.
I am desperate for ideas to try to get this card to read correctly. I have (BTW) tried two other cards. They give different specific results, but qualitatively the same - initialization, OCR, and CID read all work okay. Data read gives mostly zeroes followed by some reproducible but sparse bytes, and a 0xAA55 marker!?!
My SanDisk 1GB SD card is running at 3.296 volts which seems stable during card reading.
Here's some code:
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;
};
Thanks ahead of time!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您的扇区 0 看起来像一个有效的分区表。如果您使用 磁盘调查器 读取驱动器盘符,您最终可能会读取以下扇区 0分区而不是 SD 卡本身。该程序似乎无法从物理设备读取,因此您不能使用它来读取分区表。
Your sector 0 looks like a vaild partition table. If you read from the drive letter using disk investigator you may ended up reading the sector 0 of the partition and not from the sd-card itself. This program does not seem to be able to read from a physical device, so you can not use it to read the partition table.
终于找到了解决这个问题的方法!
原来你正在读取MBR,它位于SD卡上的地址0处。要找到引导扇区的位置,需要读取 MBR 中的相应条目。这些条目从地址 0x01be 开始,每个条目有 16 个字节。条目中感兴趣的点位于偏移量 0x08 处,长度为 4 个字节,称为 LBA。 [Wikipedia] 要获取引导扇区位置的地址,可以将 LBA 乘以扇区的大小(512 字节)。 [Microchip 论坛]
有关示例,请参见 我的其他答案。
Finally found the solution to this!
It turns out you were reading the MBR, which is located at the address 0 on the SD card. To find the location of the boot sector, one needs to read the appropriate entry in the MBR. The entries start at the address 0x01be and are 16 bytes each. The point of interest in the entry lies at the offset 0x08, is 4 bytes long and is called an LBA. [Wikipedia] To get the address of the boot sector location, one would multiply the LBA by the size of a sector (512 bytes). [Microchip forum]
For an example, see my other answer.