您的位置:首页 > Web前端

.初学56F8366之SD卡读写(基于SPI模式)(2)

2011-04-10 01:36 351 查看
对于SD卡的初始化的时候,尽量要保持低速,尽量小于375K。而初始化的一般的步骤为先发送74个时钟,然后发送CMD0命令,等SD卡返回0x01信息之后,发送APP类命令ACMD41可初始化SD卡,所谓的APP类命令ACMD41,其实就是CMD55+ACMD41,。如果是MMC卡,则可以发送CMD1命令,等待返回0x01,即可。

之前对于8366的SPI接口初始化过,这里不再赘述。

这里要说的发送命令函数。

SD卡的命令为48位的,其格式如下:



开始的两位和最后一位变。开始的前8位去掉前2两位,剩下6位,表示了SD卡的64个CMD命令。之后32位表示命令的参数,一些命令或者地址参数,总之后面这32位和前面的CMD命令配合的。后面7位为RCR,最后一位是始终为1。了解了命令结构之后,就可以参照有关时序,得下面函数。
byte SD_SendCommand(byte u8SDCommand_0,byte u8SDCommand_1,byte u8SDCommand_2,
byte u8SDCommand_3,byte u8SDCommand_4,byte u8SDCommand_CRC,
byte u8SDResponse) //发送48位命令
{
byte u8Count;
byte u8SDTemp;

//送起始位命令
SPI_Send_Date(u8SDCommand_0+0x40);

//送命令字
SPI_Send_Date(u8SDCommand_1);
SPI_Send_Date(u8SDCommand_2);
SPI_Send_Date(u8SDCommand_3);
SPI_Send_Date(u8SDCommand_4);

//送CRC
SPI_Send_Date(u8SDCommand_CRC);

u8Count = 0x0F00; //等反馈命令

do
{
u8SDTemp = SPI_Get_Date();
u8Count--;
if(u8SDTemp == u8SDResponse) break;
}
while(u8Count<0);

if(u8Count) return(0); //成功返回0
else return(1); //超时返回1
}
现在SPI初始化成功,写命令函数齐全了,然后可以开始初始化了,要点之前也说了,其具体的函数为。

void SDCard_Init(void)
{
unsigned char Success_Flag = 0;
unsigned int Count = 0; //最高要大于600,所以选择16位
unsigned char r1 = 0xFF;

SPI_Init();
//以下时序较为严谨,注意!!!
//-----------------------复位SD卡,并选择为SPI模式-----------------------------
GPIO_E_DR |= (SD_SS); //SD卡片选拉高 ,禁能
SD_CLKDelay(12); //补80个时钟(大于74个时钟)
GPIO_E_DR&= ~(SD_SS); //SD卡片选拉低 ,使能
SD_CLKDelay(8); //补40个时钟(大于16个时钟)
do
{
GPIO_E_DR&= ~(SD_SS); //SD卡片选拉低 ,使能
SD_CLKDelay(12); //补时钟(大于16个时钟)
r1 = SD_SendCommand(SD_CMD0,0,0,0,0,0x95,SD_IDLE);
Count++;
}
while((r1 != 0x00)&&Count<20 BR> GPIO_E_DR |= (SD_SS); //SD卡禁能
(void)SPI_Get_Date(); // Dummy SPI cycle //相当于补8个时钟

//--------------------------激活SD卡------------------------------------------
//备注:此种激活使用APP类命令,首先发送CMD55然后发送ACMD41
if(Count<20 BR> {
GPIO_E_DR&= ~(SD_SS); //SD卡使能
do
{
r1 = SD_SendCommand(SD_CMD55,0,0,0,0,0x95,SD_IDLE); //写CMD55
r1 = SD_SendCommand(SD_CMD41,0,0,0,0,0x95,SD_OK ); //写ACMD41
Count++;
}
while((r1 != 0x00)&&Count<300 BR> GPIO_E_DR |= (SD_SS); //SD卡禁能
(void)SPI_Get_Date(); // Dummy SPI cycle
}
//---------------激活MMC卡(原本初始为激活MMC卡所用,但是可以激活一部分的SD卡?--------
//备注:SD卡原本没有CMD1指令,但是本人诺基亚卡可以通过CMD1卡激活
if(Count<= 300)
{
GPIO_E_DR&= ~(SD_SS); //SD卡使能
do
{
r1 = SD_SendCommand(SD_CMD1,0,0,0,0,0x95,SD_OK); //写CMD1
Count ++;
}
while((r1 != 0x00)&&Count<600 BR> GPIO_E_DR |= (SD_SS); //SD卡禁能
(void)SPI_Get_Date(); // Dummy SPI cycle

}

//SPI_High_Speed(); //开高速模式
//SPI_Send_Date(0x00);
//SPI_Send_Date(0x00);
if(Count == 20) LCD_P6x8Str(5,0,(unsigned char*)"SD Rest Fail");
if(Count<300 Count="20)" LCD_PxStrunsigned charSD Init Success SD BR> if(Count<=300&&Count<600 LCD_PxStrunsigned charSD Init Success MMCBR> if(Count<= 600) LCD_P6x8Str(5,0,(unsigned char*)"SD Init Fail");
}

这样,就可以完成SD卡的初始化与激活,还可以激活MMC卡(理论上)。这个程序已经成功激活我的诺基亚的512M的SD卡,但是激活不了其他的SD卡,测试的卡有金士顿的2G卡4G卡。均无法激活,但是我的512的卡却可以,这个原因还得再找找。话说我512M的诺基亚的卡真强,MMC卡的激活指令同样有效。

然后,可以读写SD卡内部寄存器,从而得知一些信息。其寄存器有:

1.OCR:操作条件寄存器(32位),记录上电电压范围。但SPI下,无须读写OCR寄存器。

2.CID:卡标识寄存器(128位),本SD卡生产厂商之类的。

3.CSD:卡详细数据寄存器(128位),这寄存器很重要,记录SD卡容量,块大小,以及其他重要信息。

等等。

关于读寄存器,我仅读了一个CSD寄存器,程序如下:

//读取CSD寄存器,显示容量,块大小与块数。(CSD)寄存器为128位
unsigned char SD_Read_CSD(void)
{
unsigned int count = 0;
unsigned char r1 = 0;
unsigned int buffer[16] = {0};
unsigned long C_SIZE = 0; //设备大小
unsigned long MULT = 0; //乘数
unsigned long BL_LEN = 0; //块长度
float SD_SIZE= 0; //经过计算后的SD卡大小

GPIO_E_DR&= ~(SD_SS); //SD卡使能
do
{
r1 = SD_SendCommand(SD_CMD9,0,0,0,0,0xFF,SD_OK); //发送"CMD9"指令读取寄存器CSD,不需要CRC验证(CRC位置为0xFF)
count++;
}
while((r1 != 0x00)&&count<200 BR>
if(count<=200)
{
GPIO_E_DR |= (SD_SS); //SD卡禁能
(void)SPI_Get_Date(); // Dummy SPI cycle
LCD_P6x8Str(5,2,(unsigned char*)"Read CSD Fail");
return 1; //失败
}

while((SPI_Get_Date() != 0xFE)); //等待数据起始位

for(count = 0;count<16 countBR> {
buffer[count] = SPI_Get_Date();
}

GPIO_E_DR |= (SD_SS); //SD卡禁能
(void)SPI_Get_Date(); // Dummy SPI cycle
SD_CLKDelay(3);

//-------------------------计算尺寸,块数量-------------------//

C_SIZE = (((buffer[6]&0x03)<10 buffer buffer><6));
MULT = Match_2N(((buffer[9]&0x03)<1 buffer><7)+2);
BL_LEN = Match_2N((buffer[5]&0x0f));

SD_SIZE = (float)(((C_SIZE+1)*(BL_LEN)*(MULT))/1048576);

LCD_P6x8Str(5, 1,(unsigned char*)"SD Size: MB");
LCD_P6x8Var(60,1,(unsigned int)SD_SIZE);
LCD_P6x8Str(5, 2,(unsigned char*)"Max BL: byte");
LCD_P6x8Var(60,2,BL_LEN);

return 0;
}

最后计算出了卡的容量,是有点复杂,呃,还好!关于计算的公式,就是块容量乘以块大小。这个很多技术文档上都有介绍了。

最后,就是读块与写块函数了,如下所示。

//-------------------------读块函数----------------------------------
unsigned char SD_Read_BL(unsigned char Addr_0 , unsigned char Addr_1,
unsigned char Addr_2 , unsigned char Addr_3 ) //3为最高位地址
{
unsigned int r1 = 0;
unsigned int count = 0;
unsigned int buffer[512] = {0};

GPIO_E_DR&= ~(SD_SS); //SD卡使能
do
{
r1 = SD_SendCommand(SD_CMD17,Addr_3,Addr_2,Addr_1,Addr_0,0xFF,SD_OK); //发送"CMD17"指令按地址读块
count++;
}
while((r1 != 0x00)&&count<100 BR>
if(count<=100)
{
GPIO_E_DR |= (SD_SS); //SD卡禁能
(void)SPI_Get_Date(); // Dummy SPI cycle
LCD_P6x8Str(5,4,(unsigned char*)"Read Fail");
return 1; //失败
}

while((SPI_Get_Date() != 0xFE)); //等待数据起始位

for(count = 0;count<512 countBR> {
buffer[count] = SPI_Get_Date();
}

GPIO_E_DR |= (SD_SS); //SD卡禁能
(void)SPI_Get_Date(); // Dummy SPI cycle
SD_CLKDelay(3);

if(count<100 LCD_PxStrunsigned charRead SuccessBR>
return 0;
}

 

//-------------------------写块函数----------------------------------
unsigned char SD_Write_BL(unsigned char Addr_0 , unsigned char Addr_1,
unsigned char Addr_2 , unsigned char Addr_3,
unsigned char *buffer) //3为最高位地址
{
unsigned int r1 = 0;
unsigned int count = 0;

GPIO_E_DR&= ~(SD_SS); //SD卡使能
do
{
r1 = SD_SendCommand(SD_CMD24,Addr_3,Addr_2,Addr_1,Addr_0,0xFF,SD_OK); //发送"CMD24"指令按地址写块
}
while((r1 != 0x00)&&count<100 BR>
if(count<=100)
{
GPIO_E_DR |= (SD_SS); //SD卡禁能
(void)SPI_Get_Date(); // Dummy SPI cycle
LCD_P6x8Str(5,5,(unsigned char*)"Write Fail");
return 1; //失败
}

SPI_Send_Date(0xFE); //写入数据起始位

for(count = 0;count<512 countBR> {
SPI_Send_Date(*buffer++); //写入数据起始位
}

GPIO_E_DR |= (SD_SS); //SD卡禁能
(void)SPI_Get_Date(); // Dummy SPI cycle
SD_CLKDelay(3);

if(count<100 LCD_PxStrunsigned charWrite SuccessBR>
return 0;
}

以上函数,均成功读出本人512M诺基亚的卡,且信息正确。到这里,SD卡的所有函数就基本没了,剩下的就只有FAT32文件层了。要想在SD卡上创建TXT或者是写TXT文件的话,FAT32就不可不学了。

 

 

 

 

以上程序有些设计上的不足,时序并不是非常严谨,请看我之后文章,有对其的校正。               于2011,4,17      子诺

 

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  date buffer cmd byte float rest