说明: ①测试的SD卡为高容量卡,支持SD卡2.0协议,容量为16G ②采用GPIO模拟SPI时序的方式对SD卡进行驱动,很方便移植到没有硬件SPI或者SDIO的MCU,对于这类MCU,只需要将对应的延时函数和GPIO配置换成自己的就可以,其他的都无需变动。 ③对SPI有疑问或者的问题的,请移步之前写过的博文: SD/TF卡驱动(一)--------SD卡相关简介 ④如果内容有任何问题,恳请大家批评指正,谢谢。
一、 SD卡SPI初始化流程
(1)大致流程分析
流程图来自《SD卡2.0协议》P95页,SPI模式下的SD卡初始化流程,虚线框起来的部分不是必须要的流程。根据上面流程图,可以得到以下流程:
①上电后,将CS片选拉低,然后发送CMD0命令复位SD卡;
②主机发送CMD8命令,确认SD是否支持的协议版本;
③如果支持V1.X版本协议,则发送CMD58指令,确认电压是否兼容,兼容情况下发送ACMD41指令,确认是否已经准备好;
④如果支持V2.0协议,同样发送CMD58指令,确认电压是否兼容,兼容情况下发送ACMD41指令,确认是否已经准备好,待SD准备好之后,发送CMD58命令,确认SD卡是标准容量卡(2G或者2G以下)还是大容量卡(2G至32G(含))。
循环结束。
(2)初始化流程完整指令分析
前文已经说过,SD卡的每一条控制命令都是6个byte,且每一次发送完命令后,SD卡都会有相应的回复,那么接下来,我们需要确定两点:
①流程中命令的参数和CRC是多少?
②每条命令执行成功回复是什么?
下面就来解决这两个问题。上篇博文提到: 由此可以知道:
CMD0 回复R1
ACMD41 回复R3
CMD8/CMD58 回复R7
再来看看什么是R1、R3和R7:
如果看不清楚,请回到前面一篇博文,上面截图均来自上篇博文。
①R1比较简单,如果主机发送的时序和指令没有问题,不是返回0x00就是返回0x01
②R3 5个byte,第一个字节为R1,不是0x00,就是0x01,剩下的四个字节为OCR寄存器的值(OCR寄存器请看前文),事实上有关CMD8命令的回复,在协议文档中已经有说明,如下
③R7 也是5个byte,第一个字节为R1,不是0x00,就是0x01。第二个字节为命令版本、第三个字节保留、第四个字节为被接受的电压范围、第五个字节为模式检查(我也不晓得这是个啥东西,只知道它和CRC都是用来检查主机和卡之间通信的有效性)
主机发送CMD0命令,复位成功,SD卡应该回复0x01,我们将CMD0在协议文档中进行全局搜索,可以在P96页发现:
所以,最终CMD0的完整命令为:
0x00|0x40,0x00,0x00,0x00,0x00,0x95----->0x01(复位成功)
还剩下CMD8/CMD58/和ACMD41有待进一步确定回复值
CMD8和CMD58都是回复R7数据的格式,在文档P108页中,有如下说明:
由文档可以知道,如果CMD8发送成功,SD卡回复的五个字节应该是
0x01 R1
0x00 command version
0x00 Reserved bit
0x01 voltage range(VCA)
(Echo Back) check pattern
这个Echo Back是个什么鬼 复制这个Echo Back进行搜索,在文档的P40页,可以找到下面说明: 显而易见了,这个(Echo Back) check pattern就是0xAA了,事实上,从上面的文档说明里也可以得到CMD8的完整命令:0x80 | 0x40,0x00,0x00,0x01,0xAA,0x87(0x87为CRC,怎么算的自己找资料,实话实话我没找,哈哈哈)。 于是就有了CMD8的完整命令及回复:
0x08 | 0x40,0x00,0x00,0x01,0xAA,0x87---->0x01,0x00,0x00,0x01,0xAA
还剩下两个:CMD58和ACMD41 CMD58是READ_OCA,文档P106:
前面已经简单介绍过,这个命令用来确定电压范围和卡容量类型的,直接找到这个寄存器:
如果你的卡接接受电压范围为2.7-3.6V,发送CMD58命令,应该收到的回复是这样的:
0xC0
0xFF
0x80
0x00
所以,完整的CMD58命令及回复应该是:
0x3A|0x40,0x00,0x00,0x00,0x00,0x01--->0x00,0xC0,0xFF,0x80,0x00
最后一个ACMD41,这个命令用来启动初始化并检测初始化是否通过。必须要在发送第一个ACMD41命令之前发送CMD8命令。接收CMD8命令会拓展CMD58和ACMD41功能。 ACMD41参数中的HCS(高容量支持)和CMD58响应中的CCS(卡容量状态),只有在卡接收了CMD8后才能被激活。 ACMD41 R1响应中的“处于空闲状态”位通知主机ACMD41的初始化是否完成。将该位设置为“1”表示卡仍在初始化。将该位设置为“0”表示初始化完成。主机反复发出ACMD41,直到该位设置为“0”。
那么这个命令由SD卡初始化流程图可以知道,当SD卡为标准容量卡的时候,其参数为0,为高容量SD卡时,其HCS位为1,即此时其命令参数为:0x40,0x00,0x00,0x00, 所以,最后ACMD41的完整命令参数及回复为:
标准容量卡:
0x29|0x40,0x00,0x00,0x00,0x00,0x01---->0x00(初始化完成)
高容量卡:
0x29|0x40,0x40,x000,0x00,0x00,0x01----> 0x00(初始化完成)
最后,总结一下初始化流程中用到的完整指令以及回复:
CMD0:
0x00|0x40,0x00,0x00,0x00,0x00,0x95----->0x01(复位成功)
CMD8:
0x08 | 0x40,0x00,0x00,0x01,0xAA,0x87---->0x01,0x00,0x00,0x01,0xAA
ACMD41:
标准容量卡:
0x29|0x40,0x00,0x00,0x00,0x00,0x01---->0x00(初始化完成)
高容量卡:
0x29|0x40,0x40,x000,0x00,0x00,0x01----> 0x00(初始化完成)
CMD58:
0x3A|0x40,0x00,0x00,0x00,0x00,0x01--->0x00,0xC0,0xFF,0x80,0x00
(3)SD卡SPI初始化代码思路梳理
这里需要说明一点,应该养成意识,在驱动一款设备时,一般设备上电后的一小段时间内都需要完成某些操作后才能接收指令,SD卡2.0协议中的P91页中,有如下说明:
现在整个初始化流程就出来了,用伪代码,表示如下:
Delay()
Reply = 200
for(int I = 0;I < 20;i++) SPI_SendByte(0xff);
CS = 0
while((ret = SendCmd(CMD0, 0, 0x95)) != 0x01 && Reply--);
if(ret == 0x01)
{
Reply = 200
While((ret = SendCmd(CMD8, 0x1AA, 0x87)) != 0x01 && Reply--);
if(ret == 0x01)
{
for(int i = 0;i < 4;i++) buf[i] = SPI_ReadByte();
if(buf[2]==0X01&&buf[3]==0XAA)
{
Reply = 20000
do
{
ret = SendCmd(ACMD41, 0x40000000, 0x01);
} While(ret != 0x00 && Reply--);
ret = SendCmd(CMD58, 0x00, 0x01);
if(ret == 0x00)
{
for(i=0;i<4;i++)buf[i]=SPI_ ReadByte ();
if(buf[0]&0x40)SD_Type=SD_TYPE_V2HC;
else SD_Type=SD_TYPE_V2;
}
}
}
else
{
SD_SendCmd(CMD55,0,0X01);
r1=SD_SendCmd(ACMD41,0,0X01);
if(r1<=1)
{
SD_Type=SD_TYPE_V1;
retry=0XFFFE;
do
{
SD_SendCmd(CMD55,0,0X01);
r1=SD_SendCmd(CMD41,0,0X01);
}while(r1&&retry--);
}
}
}
OK,到这里,基本上就剩下写代码了,有关于SPI,你可以用GPIO模拟,也可以用MCU硬件自带的SPI,毫无疑问,同一个芯片硬件的SPI肯定是要快过GPIO模拟的SPI的,如果你对自己使用芯片不太了解,而且时间又很赶,在不影响用户使用的情况下,用GPIO模拟的SPI也没关系。
事实上,如果你按照上面的思路完成代码,在保证SPI时序不出问题的情况下,有大概率是卡死在SendCmd(ACMD41, 0x40000000, 0x01),细心的你可能会发现,别的地方是都CMD,为啥这里是ACMD,有啥区别?带着疑问,还是需要回到协议文档看看,复制ACMD41,搜索看看: 在文档P107页,有以下说明:
问题的答案就在这里,凡是以ACMD开头的都是特定应用程序命令,在发这些命令之前,需要发送CMD55,告知SD卡,接下来主机要发送ACMD命令,这里就不再分析CMD55命令了,直接在发ACMD41之前发送即可:SendCmd(CMD55, 0x00, 0x01)
所以,伪代码应该是下面这样:
Delay()
Reply = 200
While((ret = SendCmd(CMD0, 0, 0x95)) != 0x01 && Reply--);
if(ret == 0x01)
{
Reply = 200
while((ret = SendCmd(CMD8, 0x1AA, 0x87)) != 0x01 && Reply--);
if(ret == 0x01)
{
for(int i = 0;i < 4;i++) buf[i] = SPI_ReadByte();
if(buf[2]==0X01&&buf[3]==0XAA)
{
Reply = 20000
do
{
SendCmd(CMD55, 0x00, 0x01)
ret = SendCmd(ACMD41, 0x40000000, 0x01);
} While(ret != 0x00 && Reply--);
ret = SendCmd(CMD58, 0x00, 0x01);
if(ret == 0x00)
{
for(i=0;i<4;i++)buf[i]=SPI_ ReadByte ();
if(buf[0]&0x40)SD_Type=SD_TYPE_V2HC;
else SD_Type=SD_TYPE_V2;
}
}
}
else
{
SD_SendCmd(CMD55,0,0x01);
r1=SD_SendCmd(ACMD41,0,0x01);
if(r1<=1)
{
SD_Type=SD_TYPE_V1;
retry=0XFFFE;
do
{
SD_SendCmd(CMD55,0,0x01);
r1=SD_SendCmd(CMD41,0,0x01);
}while(r1&&retry--);
}
}
}
(4)初始化代码分析
这里采用的GPIO模拟SPI时序来驱动SD卡的,再次说明,有关SPI时序分析和代码的编写,不清楚的可以我之前写过的东西: SD/TF卡驱动(一)--------SD卡相关简介
uint8_t SD_Init(void)
{
uint8_t r1;
uint16_t retry;
uint8_t buf[4];
uint16_t i;
SPI_GpioInit();
for(i=0;i<20;i++)SPI_WriteReadByte(0XFF);
retry=20;
do
{
r1=SD_SendCmd(CMD0,0,0x95);
}while((r1!=0X01) && retry--);
SD_Type=0;
if(r1==0X01)
{
printf("SD_SendCmd(CMD0,0,0x95) SUCCESS!\r\n");
if(SD_SendCmd(CMD8,0x1AA,0x87)==1)
{
for(i=0;i<4;i++)buf[i]=SPI_WriteReadByte(0XFF);
if(buf[2]==0X01&&buf[3]==0XAA)
{
retry=0XFFFE;
do
{
SD_SendCmd(CMD55,0,0X01);
r1=SD_SendCmd(CMD41,0x40000000,0X01);
}while(r1&&retry--);
if(retry&&SD_SendCmd(CMD58,0,0X01)==0)
{
printf("SD_SendCmd(CMD41,0x40000000,0X01) SUCCESS!\r\n");
for(i=0;i<4;i++)buf[i]=SPI_WriteReadByte(0XFF);
for(int i = 0;i < 4;i++)
{
printf("buf[%d] %02X\r\n",i, buf[i]);
}
if(buf[0]&0x40)SD_Type=SD_TYPE_V2HC;
else SD_Type=SD_TYPE_V2;
printf("SD_Type %02X\r\n", SD_Type);
}
}
}
else
{
SD_SendCmd(CMD55,0,0X01);
r1=SD_SendCmd(CMD41,0,0X01);
if(r1<=1)
{
SD_Type=SD_TYPE_V1;
retry=0XFFFE;
do
{
SD_SendCmd(CMD55,0,0X01);
r1=SD_SendCmd(CMD41,0,0X01);
}while(r1&&retry--);
}else
{
SD_Type=SD_TYPE_MMC;
retry=0XFFFE;
do
{
r1=SD_SendCmd(CMD1,0,0X01);
}while(r1&&retry--);
}
if(retry==0||SD_SendCmd(CMD16,512,0X01)!=0)SD_Type=SD_TYPE_ERR;
}
}
SD_DisSelect();
if(SD_Type)return 0;
else if(r1)return r1;
return 0xFF;
}
二、 SD卡读写流程分析
初始化SD卡过了,下面就是读写SD卡了,一样的,我们围绕文档,在文档P96页以及后面几页的内容里,详细介绍了如何读写SD卡,请仔细阅读文档。对命令的分析方式,和上面的初始化时候的思路一样,这里就不再详细叙述了,如果你初始化没问题,读写大概率是不会有问题的。
(1)SD卡读写
1.SD卡读 读SD卡有单块读,也有多块读, 单块读的流程如下:
流程总结如下:
①发送 CMD17;
②接收卡响应 R1;
③接收数据;
④接收 2 个字节的 CRC,如果不使用 CRC,这两个字节在读取后可以丢掉。
多块读的流程如下:
总结起来流程如下:
①发送 CMD18;
②接收卡响应 R1;
③接收数据;
④接收 2 个字节的 CRC,如果不使用 CRC,这两个字节在读取后可以丢掉。
⑤数据接收完成后发送 CMD12,停止数据接收,等待回应,结束
2.SD卡写 SD卡写和读类似,也有单块写,也有多块写,如下: 单块写:
①发送 CMD24;
②接收卡响应 R1;
③发送写数据起始令牌 0XFE;
④发送数据;
⑤发送 2 字节的伪 CRC;
多块写:
①发送 CMD25;
②接收卡响应 R1;
③发送写数据起始令牌 0XFE;
④发送数据;
⑤发送 2 字节的伪 CRC;
⑥判断SD卡响应
⑦结束指令
(2)SD卡读写代码分析
uint8_t SD_SendBlock(uint8_t*buf,uint8_t cmd)
{
uint16_t t;
if(SD_WaitReady())return 1;
SPI_WriteReadByte(cmd);
if(cmd!=0XFD)
{
for(t=0;t<512;t++)SPI_WriteReadByte(buf[t]);
SPI_WriteReadByte(0xFF);
SPI_WriteReadByte(0xFF);
t=SPI_WriteReadByte(0xFF);
if((t&0x1F)!=0x05)return 2;
}
return 0;
}
uint8_t SD_ReadDisk(uint8_t*buf,uint32_t sector,uint8_t cnt)
{
uint8_t r1;
if(SD_Type!=SD_TYPE_V2HC)sector <<= 9;
if(cnt==1)
{
r1=SD_SendCmd(CMD17,sector,0X01);
if(r1==0)
{
r1=SD_RecvData(buf,512);
}
}else
{
r1=SD_SendCmd(CMD18,sector,0X01);
do
{
r1=SD_RecvData(buf,512);
buf+=512;
}while(--cnt && r1==0);
SD_SendCmd(CMD12,0,0X01);
}
SD_DisSelect();
return r1;
}
uint8_t SD_WriteDisk(uint8_t*buf,uint32_t sector,uint8_t cnt)
{
uint8_t r1;
uint8_t reply = 200;
if(SD_Type!=SD_TYPE_V2HC)sector *= 512;
if(cnt==1)
{
r1=SD_SendCmd(CMD24,sector,0X01);
if(r1==0)
{
r1=SD_SendBlock(buf,0xFE);
}
}
else
{
if(SD_Type!=SD_TYPE_MMC)
{
do
{
SD_SendCmd(CMD55,0,0X01);
r1 = SD_SendCmd(CMD22,0x00,0X01);
}while(r1 != 0x00 && reply--);
for(int i = 0;i < 4;i++)SPI_WriteReadByte(0xff);
}
r1=SD_SendCmd(CMD25,sector,0X01);
if(r1==0)
{
do
{
r1=SD_SendBlock(buf,0xFC);
buf+=512;
}while(--cnt && r1==0);
r1=SD_SendBlock(0,0xFD);
}
}
SD_DisSelect();
return r1;
}
三、 完整代码
Sd_card.h
#ifndef HARDWARE_SD_CARD_H_
#define HARDWARE_SD_CARD_H_
#include "sl_spidrv.h"
#include "em_gpio.h"
#include "sl_spidrv_spiflash_config.h"
#include "spi.h"
#define SD_TYPE_ERR 0X00
#define SD_TYPE_MMC 0X01
#define SD_TYPE_V1 0X02
#define SD_TYPE_V2 0X04
#define SD_TYPE_V2HC 0X06
#define CMD0 0
#define CMD1 1
#define CMD8 8
#define CMD9 9
#define CMD10 10
#define CMD12 12
#define CMD16 16
#define CMD17 17
#define CMD18 18
#define CMD22 22
#define CMD23 23
#define CMD24 24
#define CMD25 25
#define CMD41 41
#define CMD55 55
#define CMD58 58
#define CMD59 59
extern uint8_t SD_Type;
uint8_t SD_WaitReady(void);
uint8_t SD_Init(void);
uint32_t SD_GetSectorCount(void);
uint8_t SD_ReadDisk(uint8_t*buf,uint32_t sector,uint8_t cnt);
uint8_t SD_WriteDisk(uint8_t*buf,uint32_t sector,uint8_t cnt);
#endif
Sd_card.c
#include "sd_card.h"
#include <stdio.h>
#include "spidrv.h"
#include "sl_udelay.h"
uint8_t SD_Type;
void SD_DisSelect(void)
{
SD_CS_SET_GPIO_OUTPUT_STATUS(1);
SPI_WriteReadByte(0xff);
}
uint8_t SD_WaitReady(void)
{
uint32_t t=0;
do
{
if(SPI_WriteReadByte(0XFF)==0XFF)return 0;
t++;
}while(t<0XFF);
return 1;
}
uint8_t SD_Select(void)
{
SD_CS_SET_GPIO_OUTPUT_STATUS(0);
if(SD_WaitReady()==0)return 0;
SD_DisSelect();
return 1;
}
uint8_t SD_SendCmd(uint8_t cmd, uint32_t arg, uint8_t crc)
{
uint8_t r1;
uint8_t Retry=0;
SD_DisSelect();
if(SD_Select())
SD_CS_SET_GPIO_OUTPUT_STATUS(0);
SPI_WriteReadByte(cmd | 0x40);
SPI_WriteReadByte(arg >> 24);
SPI_WriteReadByte(arg >> 16);
SPI_WriteReadByte(arg >> 8);
SPI_WriteReadByte(arg);
SPI_WriteReadByte(crc);
if(cmd==CMD12)SPI_WriteReadByte(0xff);
Retry=0X1F;
do
{
r1=SPI_WriteReadByte(0xFF);
}while((r1&0X80) && Retry--);
return r1;
}
uint8_t SD_GetResponse(uint8_t Response)
{
uint16_t Count=0xFFFF;
while ((SPI_WriteReadByte(0XFF)!=Response)&&Count)Count--;
if (Count==0)return 1;
else return 0;
}
uint8_t SD_RecvData(uint8_t*buf,uint16_t len)
{
if(SD_GetResponse(0xFE))return 1;
while(len--)
{
*buf=SPI_WriteReadByte(0xFF);
buf++;
}
SPI_WriteReadByte(0xFF);
SPI_WriteReadByte(0xFF);
return 0;
}
uint8_t SD_GetCID(uint8_t *cid_data)
{
uint8_t r1;
r1=SD_SendCmd(CMD10,0,0x01);
if(r1==0x00)
{
r1=SD_RecvData(cid_data,16);
}
SD_DisSelect();
if(r1)return 1;
else return 0;
}
uint8_t SD_GetCSD(uint8_t *csd_data)
{
uint8_t r1;
r1=SD_SendCmd(CMD9,0,0x01);
if(r1==0)
{
r1=SD_RecvData(csd_data, 16);
}
SD_DisSelect();
if(r1)return 1;
else return 0;
}
uint32_t SD_GetSectorCount(void)
{
uint8_t csd[16];
uint32_t Capacity;
uint8_t n;
uint16_t csize;
if(SD_GetCSD(csd)!=0) return 0;
if((csd[0]&0xC0)==0x40)
{
csize = csd[9] + ((uint16_t)csd[8] << 8) + 1;
Capacity = (uint32_t)csize << 10;
}else
{
n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
csize = (csd[8] >> 6) + ((uint16_t)csd[7] << 2) + ((uint16_t)(csd[6] & 3) << 10) + 1;
Capacity= (uint32_t)csize << (n - 9);
}
return Capacity;
}
uint8_t SD_Init(void)
{
uint8_t r1;
uint16_t retry;
uint8_t buf[4];
uint16_t i;
SPI_GpioInit();
for(i=0;i<20;i++)SPI_WriteReadByte(0XFF);
retry=20;
do
{
r1=SD_SendCmd(CMD0,0,0x95);
}while((r1!=0X01) && retry--);
SD_Type=0;
if(r1==0X01)
{
printf("SD_SendCmd(CMD0,0,0x95) SUCCESS!\r\n");
if(SD_SendCmd(CMD8,0x1AA,0x87)==1)
{
for(i=0;i<4;i++)buf[i]=SPI_WriteReadByte(0XFF);
if(buf[2]==0X01&&buf[3]==0XAA)
{
retry=0XFFFE;
do
{
SD_SendCmd(CMD55,0,0X01);
r1=SD_SendCmd(CMD41,0x40000000,0X01);
}while(r1&&retry--);
if(retry&&SD_SendCmd(CMD58,0,0X01)==0)
{
printf("SD_SendCmd(CMD41,0x40000000,0X01) SUCCESS!\r\n");
for(i=0;i<4;i++)buf[i]=SPI_WriteReadByte(0XFF);
for(int i = 0;i < 4;i++)
{
printf("buf[%d] %02X\r\n",i, buf[i]);
}
if(buf[0]&0x40)SD_Type=SD_TYPE_V2HC;
else SD_Type=SD_TYPE_V2;
printf("SD_Type %02X\r\n", SD_Type);
}
}
}
else
{
SD_SendCmd(CMD55,0,0X01);
r1=SD_SendCmd(CMD41,0,0X01);
if(r1<=1)
{
SD_Type=SD_TYPE_V1;
retry=0XFFFE;
do
{
SD_SendCmd(CMD55,0,0X01);
r1=SD_SendCmd(CMD41,0,0X01);
}while(r1&&retry--);
}else
{
SD_Type=SD_TYPE_MMC;
retry=0XFFFE;
do
{
r1=SD_SendCmd(CMD1,0,0X01);
}while(r1&&retry--);
}
if(retry==0||SD_SendCmd(CMD16,512,0X01)!=0)SD_Type=SD_TYPE_ERR;
}
}
SD_DisSelect();
if(SD_Type)return 0;
else if(r1)return r1;
return 0xaa;
}
uint8_t SD_SendBlock(uint8_t*buf,uint8_t cmd)
{
uint16_t t;
if(SD_WaitReady())return 1;
SPI_WriteReadByte(cmd);
if(cmd!=0XFD)
{
for(t=0;t<512;t++)SPI_WriteReadByte(buf[t]);
SPI_WriteReadByte(0xFF);
SPI_WriteReadByte(0xFF);
t=SPI_WriteReadByte(0xFF);
if((t&0x1F)!=0x05)return 2;
}
return 0;
}
uint8_t SD_ReadDisk(uint8_t*buf,uint32_t sector,uint8_t cnt)
{
uint8_t r1;
if(SD_Type!=SD_TYPE_V2HC)sector <<= 9;
if(cnt==1)
{
r1=SD_SendCmd(CMD17,sector,0X01);
if(r1==0)
{
r1=SD_RecvData(buf,512);
}
}else
{
r1=SD_SendCmd(CMD18,sector,0X01);
do
{
r1=SD_RecvData(buf,512);
buf+=512;
}while(--cnt && r1==0);
SD_SendCmd(CMD12,0,0X01);
}
SD_DisSelect();
return r1;
}
uint8_t SD_WriteDisk(uint8_t*buf,uint32_t sector,uint8_t cnt)
{
uint8_t r1;
uint8_t reply = 200;
if(SD_Type!=SD_TYPE_V2HC)sector *= 512;
if(cnt==1)
{
r1=SD_SendCmd(CMD24,sector,0X01);
if(r1==0)
{
r1=SD_SendBlock(buf,0xFE);
}
}
else
{
if(SD_Type!=SD_TYPE_MMC)
{
do
{
SD_SendCmd(CMD55,0,0X01);
r1 = SD_SendCmd(CMD22,0x00,0X01);
}while(r1 != 0x00 && reply--);
for(int i = 0;i < 4;i++)SPI_WriteReadByte(0xff);
}
r1=SD_SendCmd(CMD25,sector,0X01);
if(r1==0)
{
do
{
r1=SD_SendBlock(buf,0xFC);
buf+=512;
}while(--cnt && r1==0);
r1=SD_SendBlock(0,0xFD);
}
}
SD_DisSelect();
return r1;
}
/*************************************************************************/
附:SPI源码(SPI模式0)
Spi.h
#ifndef HARDWARE_SPI_H_
#define HARDWARE_SPI_H_
#include "em_gpio.h"
#include "em_cmu.h"
#define SPI_MOSI_PORT gpioPortC
#define SPI_MOSI_PIN 1
#define SPI_MISO_PORT gpioPortC
#define SPI_MISO_PIN 3
#define SPI_CLK_PORT gpioPortC
#define SPI_CLK_PIN 0
#define SPI_CS_PORT gpioPortC
#define SPI_CS_PIN 2
#define SD_CS_SET_GPIO_OUTPUT_STATUS(status) {if(status == 1) GPIO_PinOutSet(SPI_CS_PORT, SPI_CS_PIN);\
else if(status == 0) GPIO_PinOutClear(SPI_CS_PORT, SPI_CS_PIN);}
#define SD_CLK_SET_GPIO_OUTPUT_STATUS(status) {if(status == 1) GPIO_PinOutSet(SPI_CLK_PORT, SPI_CLK_PIN);\
else if(status == 0) GPIO_PinOutClear(SPI_CLK_PORT, SPI_CLK_PIN);}
#define SD_MOSI_SET_GPIO_OUTPUT_STATUS(status) {if(status == 1) GPIO_PinOutSet(SPI_MOSI_PORT, SPI_MOSI_PIN);\
else if(status == 0) GPIO_PinOutClear(SPI_MOSI_PORT, SPI_MOSI_PIN);}
void SPI_GpioInit(void);
void SPI_WriteByte(uint8_t data);
uint8_t SPI_ReadByte(void);
uint8_t SPI_WriteReadByte(uint8_t data);
#endif
Spi.c
#include "spi.h"
#include "sl_udelay.h"
static void SPI_Delay(uint32_t us)
{
sl_udelay_wait(us);
}
void SPI_GpioInit(void)
{
CMU_ClockEnable(cmuClock_GPIO, true);
GPIO_PinModeSet(SPI_CS_PORT, SPI_CS_PIN, gpioModePushPull, 0);
GPIO_PinModeSet(SPI_MOSI_PORT, SPI_MOSI_PIN, gpioModePushPull, 0);
GPIO_PinModeSet(SPI_CLK_PORT, SPI_CLK_PIN, gpioModePushPull, 0);
GPIO_PinModeSet(SPI_MISO_PORT, SPI_MISO_PIN, gpioModeInputPull, 0);
SD_CS_SET_GPIO_OUTPUT_STATUS(1);
SD_CLK_SET_GPIO_OUTPUT_STATUS(0);
}
void SPI_WriteByte(uint8_t data)
{
uint8_t i = 0;
uint8_t temp = 0;
for(i=0; i<8; i++)
{
temp = ((data&0x80)==0x80)? 1:0;
data = data<<1;
SD_CLK_SET_GPIO_OUTPUT_STATUS(0);
SD_MOSI_SET_GPIO_OUTPUT_STATUS(temp);
SPI_Delay(1);
SD_CLK_SET_GPIO_OUTPUT_STATUS(1);
SPI_Delay(1);
}
SD_CLK_SET_GPIO_OUTPUT_STATUS(0);
}
uint8_t SPI_ReadByte(void)
{
uint8_t i = 0;
uint8_t read_data = 0xFF;
for(i=0; i<8; i++)
{
read_data = read_data << 1;
SD_CLK_SET_GPIO_OUTPUT_STATUS(0);
SPI_Delay(1);
SD_CLK_SET_GPIO_OUTPUT_STATUS(1);
SPI_Delay(1);
if(GPIO_PinInGet(SPI_MISO_PORT, SPI_MISO_PIN)==1)
{
read_data = read_data + 1;
}
}
SD_CLK_SET_GPIO_OUTPUT_STATUS(0);
return read_data;
}
uint8_t SPI_WriteReadByte(uint8_t data)
{
uint8_t i = 0;
uint8_t temp = 0;
uint8_t read_data = 0xFF;
for(i=0;i<8;i++)
{
temp = ((data&0x80)==0x80)? 1:0;
data = data<<1;
read_data = read_data<<1;
SD_CLK_SET_GPIO_OUTPUT_STATUS(0);
SD_MOSI_SET_GPIO_OUTPUT_STATUS(temp);
SPI_Delay(1);
SD_CLK_SET_GPIO_OUTPUT_STATUS(1);
SPI_Delay(1);
if(GPIO_PinInGet(SPI_MISO_PORT, SPI_MISO_PIN)==1)
{
read_data = read_data + 1;
}
}
SD_CLK_SET_GPIO_OUTPUT_STATUS(0);
return read_data;
}
|