精华内容
下载资源
问答
  • SD初始化步骤详解
    千次阅读
    2016-12-15 14:55:29

    硬件平台:飞思卡尔s12xep100

    开发环境:codewarrior 5.2

    结果:批量存储稳定


    最近在做文件系统的升级,先前用的是21ic论坛上面down的znfat,相信做单片机软件的兄弟们应该都知道。

    后来用的过程中发现此文件系统超时机制做的不是很好,而且不精简,会占用比较大的ram和flash,产品用了几个月后,决定升级一下文件系统,后来就发现了fatfs。


    这里先从sd卡的初始化说起吧,只说初始化流程以及一些细节问题(spi模式)。

    1、第一步,在将spi模块初始化好之后,将spi速度设为低速模式,我这里用的是250k

    2、保持select脚为高电平,并向SD卡发送不低于74个时钟脉冲,此时sd卡将进入它的native operation mode(翻译为本地操作模式了),并做好接收本地指令的准备。

    3、此时将select脚拉低并向SD卡发送cmd0指令,SD卡在检测到select脚为低并收到cmd0指令后将进入spi模式,并返回0x01空闲状态。

    4、成功进入空闲状态后,继续发送cmd8指令,携带的参数为0x000001AA,如果SD卡不能识别并返回错误码5,说明此卡非2.0卡(进入步骤6)。如果指令能被识别,则SD卡将返回R1+32位共40位数据,其中R1位0x01,32位数据为0x000001AA。此时可判断卡为2.0卡。

    5、判断为2.0卡后,紧接着发送cmd55+acmd41指令(应该是在cmd55返回1的情况下再接着发送acmd41),如果返回0,则初始化成功。此时如果你想继续判断是标准2.0卡SCSD还是大容量HCSD,则需要继续发送cmd58指令,并在cmd58指令被成功响应后连续从SD卡读取4个字节的ocr数据,若其第31位为1,表示初始化成功,其第30位为1表示是HCSD,为0表示SCSD。 此时2.0卡初始化结束。

    6、步骤4对cmd8无响应,则继续判断卡为1.0或mmc卡。此时同步骤5一样向卡发送cmd55+acmd41指令,如果返回0,则表示1.0卡初始化成功,如果返回错误,则确定卡为mmc卡。 在确定卡为mmc卡后,继续向卡发送cmd1指令,如果返回0,则mmc卡初始化成功,否则失败,判断为错卡!!

    7、初始化成功后,将spi切换为高速模式,我这里用的是4m。


    至此,SD卡初始化过程就结束了,记得几点:在发送cmd函数后,记得补偿时钟;在读寄存器位时,需要select拉低;初始化时,一定要是低速模式并且有超过74个时钟脉冲!


    注:在测试的过程中,也是遇到了些比较蛋疼的问题,我手里有3张卡,2张8ghc,1张2gsc。这两张8g卡在读ocr时,其最高字节一直返回0x80,也就是说从ocr上面判断不出是HC大容量卡,那张2g卡返回也是0x80是正常的。  于是找啊找查啊查,究竟是哪里出问题了。最后实在是差不出来,重新买了个新的HC,还好,果然成功了,ocr最高字节终于为0xc0。 所以说就是提醒大家在测试过程中,多找些,多对比现象。



    更多相关内容
  • SD卡分为SDIO模式与SPI模式,SDIO模式使用SD总线协议,使用4根数据线进行数据传输,SPI使用1收1发2根数据线数据传输,理论上SDIO模式会比SPI模式速度快4倍,但SDIO模式还牵扯到CRC校验位的计算,所以:如果使用CPU有...

    SD卡分为SDIO模式与SPI模式,SDIO模式使用SD总线协议,使用4根数据线进行数据传输,SPI使用1收1发2根数据线数据传输,理论上SDIO模式会比SPI模式速度快4倍,但SDIO模式还牵扯到CRC校验位的计算,所以:如果使用CPU有限制的话不用太纠结选取哪个模式。这里只对SPI模式进行说明:

    注:SD卡默认处于SD模式

    一、接口连线:

    接口连线给3张图吧,很直观:

     

     

     二、SD卡命令说明

            SD卡命令有6个字节构成:

            字节1:指令标号,即 0----cmd0        注:该字节第7位始终为1,即每一个命令都需 |=0x40;

            字节2,3,4,5:指令内容

            字节6:指令校验位crc,spi模式不对其进行校验,可发送任意数值,一般选取0xff,但必须发送,不可省略

            指令后为正确应答数据

            CMD0:        0x40,0x00,0x00,0x00,0x00,0x95-------0x01

            CMD1:        0x41,0x00,0x00,0x00,0x00,0x01-------0x00

                                    MMC卡使用CMD1命令初始化
            CMD8:     0x48,0x00,0x00,0x01,0xAA,0x87-------0x01(SD2.0)

                                    判断SD卡的版本,同时读取后续应答获知SD卡工作电压,一般为0x01aa,

                                    即:支持2.7~3.6v工作电压
            CMD16        0x50,0x00,0x00,0x02,0x00,0x01

                                    设置扇区大小为512字节,同时验证初始化是否成功

                                    注:设置时不可小于512字节,且为512整倍数
            CMD55        0x77,0x00,0x00,0x00,0x00,0x01

                                    用于说明后一条指令为ACMD,即:CMD55+ACMD41配合进行使用
             ACMD41(SD2.0)        0x69,0x40,0x00,0x00,0x00,0x01

                                    用于检测SD卡是否初始化完成,针对2.0的SD卡
            ACMD41(SD2.1)         0x69,0x00,0x00,0x00,0x00,0x01

                                    用于检测SD卡是否初始化完成,针对1.0的SD卡

            CMD58        0x7A,0x00,0x00,0x00,0x00,0x01

                                    用于鉴别SD2.0卡类型        SDHC或普通SD卡

                                    区别:扇区地址的寻址方式不同

                                               SDHC为扇区块进行寻址

                                               普通SD卡位字节地址寻址

            注:MMC卡使用CMD1来进行初始化,而SD卡则使用CMD55+ACMD41来进行初始化

    三、SD卡初始化步骤

    1. 设置SPI时钟为低速0~400khz-------------SD卡初始化只能在低速模式下进行
    2. 发送至少74个周期的时钟信号-------------建议适当多发送一些
    3. 发送SD卡命令CMD0-------------------------使SD卡切换至spi模式,进入IDLE状态
    4. 发送CMD8(SD2.0)----------------------------针对SD2.0进行鉴别     同时读取后续4个字节返回值,判断其是否支持工作电压(例如:2.7~3.6v支持则为0x01aa)
      1. 发送CMD55+ACMD41(SD2.0)-----检测初始化是否完成
      2. 发送CMD58------------------------------对SD2.0版本进一步确认SD卡型号
    5. 发送CMD55+ACMD41(SD1.0)-------------检测返回值进行判断为SD1.0或MMC卡
      1. 发送CMD55+ACMD41(SD1.0)------检测初始化是否完成
    6. 发送CMD1(MMC)------------------------------针对MMC卡进行初始化
    7. 发送CMD16--------------------------------------设置SD卡扇区大小(默认为512字节)
    8. 设置SPI时钟为高速,最大为25Mhz--------初始化完成

    四、补充说明

            关于CSD、OCR、CID寄存器的说明可自行百度,或参考SD协议。

            不同型号SD卡可能略有不同,可调整各步骤执行次数可解决绝大部分问题(不知名SD卡除外,可能需要更多次数的指令发送)

            以上所使用的命令并非SD卡全部命令,若有需要,请参考协议。

    展开全文
  • 宝图一张,不过话说回来,如果现在买HC高速卡以外的低速卡,还真买不到了,主要是MMC卡的扇区不是512,本来左移9位就拉倒的事还不确定了,...在京东买的最便宜的SD卡,20元8G金士顿,SPI已通过,并挂载上了FATFS 0.99。
  • U盘SD初始化工具

    热门讨论 2012-05-03 22:31:23
    很多人的u盘或者存储卡要用系统工具刻录安装系统,当你用工具刻录你的u盘或sd卡时就改写了u盘或卡的一些基本信息,导致你无法在一些数码电子设备中使用,如相机,播放器等。用这个工具可以把你的u盘或sd卡恢复到出厂...
  • SD初始化细节

    千次阅读 2018-07-27 14:10:43
    使用STM32学习板操作SD卡时,遇到了初始化中检测SD卡类型的问题,查看学习板带的源程序发现使用了CMD8命令进行判断SD卡的类型,在网上查了好久查不到有关SDV2.0中CMD8命令的详细描述,于是自己看了英文版的SDV2.0协议...

     

    V2.0版SD卡在SPI模式下使用CMD8的操作

    使用STM32学习板操作SD卡时,遇到了初始化中检测SD卡类型的问题,查看学习板带的源程序发现使用了CMD8命令进行判断SD卡的类型,在网上查了好久查不到有关SDV2.0中CMD8命令的详细描述,于是自己看了英文版的SDV2.0协议(2006),找出了CMD8命令的章节,翻译了一下,然后加上一些自己的理解,构成了这篇说明。

    首先简单说一下SD卡的初始化过程,这个过程很多文章都有叙述,此处简单摘自其他文章如下:

    (1).首先延时74CLK,然后发送CMD0命令,得到返回值0X01;

    (2).然后进入卡类型检验。为了检验卡的类型,首先发送只有V2.0版的SD卡才具有的命令CMD8,然后检测返回值:

    返回值若是0X01,则表示此卡为V2.0卡,然后再发送循环命令CMD55+ACMD41,直到返回0x00,确定SD2.0卡初始化成功;然后再发送CMD58命令,接收返回的OCR 寄存器的数据,其中第31位用于判断V2.0的卡是否为SDHC类型。

    若返回值不为0X01,则进一步判断是V1.0卡还是MMC卡:先发送循环命令

    CMD55+ACMD41进行复位,如果复位不成功则考虑是MMC卡,如果复位成功,则为V1.0卡。在复位不成功的情况下,再使用CMD1进行复位,如果复位成功,则表明是MMC卡,如果复位不成功,则表示是无法识别的卡。

    从上面的初始化过程可以看出主要涉及到4个特殊的命令:CMD8、CMD55、CMD41、CMD58。对于CMD55和CMD41的讲解,有很多文章都有,不多解释,此复位命令的返回值和CMD1的返回值相同,成功复位时返回0X00;对于CMD58,其命令码格式是标准格式,其中数据填充0即可,CRC也可省略,只要最后加上停止位‘1’即可,不作详解;其返回值为R3类型,首先接收到一个字节应该为0X00,表示SD卡响应命令成功,然后接收4字节的OCR寄存器值;OCR寄存器的第30位(CCS)指示了卡的类型是否为

    SDHC,此位为1则为SDHC,为0则为SDSC;

    重点讲一下CMD8命令:

    (1).发送的命令格式如下:

    其中需要注意的是VHS。如果单片机支持2.7-3.6v的SD卡,那么这个半字节应该写成‘0001’。STM32单片机用的是3.3V电压,当然支持,如果不支持还怎么做实验。

    然后注意CHECK PATTERN这个字节,这个字节的特点是,你在这个发送的命令中写的是什么,那么接收CMD8命令的回复时接收到的就是什么,比如如果这个字节写成“0XAA”,那么当接收CMD8命令回复的数据时接收到的也是“0XAA”;

    发送的CMD8命令数据可以如下:

    [0X48(CMD8)、0X00、0X00、0X01、0XAA、0X87];

    (2).接收SD卡响应CMD8命令后返回的数据格式:

    在SDV2.0协议中,CMD8的返回值格式为R7,如下所示:

     

    首先可以接收到第一个字节格式为R1的数据,这个数据只要判断是否为0X01即可,如果为0X01,表示SD卡响应了CMD8命令,如果不为0X01(一般会是0X09或0X05,不用关心),则表示SD卡不支持CMD8命令。

    在接收到0X01之后,随后需要接收4字节数据,其中31-28位为command version,即命令的类型,此处为CMD8;然后27-12位是保留的数据位,通常为0;然后11-8位是SD卡支持的电压范围,此处得到的应该是‘0001’;最后一个字节是我们在CMD8的命令中发送给SD卡的数据,SD卡又原模原样的返回来了,在命令中我们发送的是0XAA,此处得到的也应该是0XAA。

    最后,这样解释下来,对于一般的应用应该可以满足了,至于更多的信息,请读者参阅SD卡V2.0的英文版协议(2006)吧。以上仅仅是个人对SD卡V2.0协议中有关CMD8命令的一些理解,如有错误之处希望读者能给予指正,技术重在交流。

    作者:嵌入式奋勇前进

    2013-10-1 mingzheng0901@sina.com

     

     

     

     

     

     

    展开全文
  • sdio-sd初始化流程

    2017-11-02 14:16:13
    sd初始化流程的撒撒打算打算打算打算大美女‘;的;啊打算;阿三;的撒
  • SD初始化 1602屏显示 C51程序 欢迎下载~~~~
  • 说明: ①测试的SD卡为高容量卡,支持SD卡2.0协议,容量为16G ②采用GPIO模拟SPI时序的方式对SD卡进行驱动,很方便移植到没有硬件SPI或者SDIO的MCU,对于这类MCU,只需要将...一、 SD卡SPI初始化流程 (1)大致流程分析

    说明:
    ①测试的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()   //等待SD卡上电稳定
    Reply = 200
    for(int I = 0;I < 20;i++) SPI_SendByte(0xff);  //内容随便发,但是必须>74个时钟周期。 
    //等待SD卡复位成功,CMD0命令,没有参数,
    //且根据文档其CRC为0x95(文档P96),复位正常应该返回0x01
    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)//卡是否支持2.7~3.6V
    		{
    			Reply = 20000
    			do
    			{
    				ret = SendCmd(ACMD41, 0x40000000, 0x01);
    			} While(ret != 0x00 && Reply--);  //等待SD初始化完毕
    			ret = SendCmd(CMD58, 0x00, 0x01);
    			if(ret == 0x00)
    			{
    				for(i=0;i<4;i++)buf[i]=SPI_ ReadByte ();//得到OCR值
    			    if(buf[0]&0x40)SD_Type=SD_TYPE_V2HC;   //检查CCS 标准容量卡
    			    else SD_Type=SD_TYPE_V2;         //高容量卡
    			}
    		}
    	}
    	else//SD V1.x/ MMC V3
    	{
    	    SD_SendCmd(CMD55,0,0X01);       //发送CMD55
    	    r1=SD_SendCmd(ACMD41,0,0X01);    //发送CMD41
    	    if(r1<=1)
    	    {
    	        SD_Type=SD_TYPE_V1;
    	        retry=0XFFFE;
    	        do //等待退出IDLE模式
    	        {
    	            SD_SendCmd(CMD55,0,0X01);   //发送CMD55
    	            r1=SD_SendCmd(CMD41,0,0X01);//发送CMD41
    	        }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()   //等待SD卡上电稳定
    Reply = 200
    //等待SD卡复位成功,CMD0命令,没有参数,
    //且根据文档其CRC为0x95(文档P96),复位正常应该返回0x01
    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)//卡是否支持2.7~3.6V
    		{
    			Reply = 20000
    			do
    			{
    								SendCmd(CMD55, 0x00, 0x01)
    				ret = SendCmd(ACMD41, 0x40000000, 0x01);
    			} While(ret != 0x00 && Reply--);  //等待SD初始化完毕
    			ret = SendCmd(CMD58, 0x00, 0x01);
    			if(ret == 0x00)
    			{
    				for(i=0;i<4;i++)buf[i]=SPI_ ReadByte ();//得到OCR值
    			    if(buf[0]&0x40)SD_Type=SD_TYPE_V2HC;   //检查CCS 标准容量卡
    			    else SD_Type=SD_TYPE_V2;         //高容量卡
    			}
    		}
    	}
    	else//SD V1.x
    	{
    	    SD_SendCmd(CMD55,0,0x01);       //发送CMD55
    	    r1=SD_SendCmd(ACMD41,0,0x01);    //发送ACMD41
    	    if(r1<=1)
    	    {
    	        SD_Type=SD_TYPE_V1;
    	        retry=0XFFFE;
    	        do //等待退出IDLE模式
    	        {
    	            SD_SendCmd(CMD55,0,0x01);   //发送CMD55
    	            r1=SD_SendCmd(CMD41,0,0x01);//发送CMD41
    	        }while(r1&&retry--);
    	    }
    	}
    }
    

    (4)初始化代码分析

    这里采用的GPIO模拟SPI时序来驱动SD卡的,再次说明,有关SPI时序分析和代码的编写,不清楚的可以我之前写过的东西:
    SD/TF卡驱动(一)--------SD卡相关简介

    uint8_t SD_Init(void)
    {
        uint8_t r1;      // 存放SD卡的返回值
        uint16_t retry;  // 用来进行超时计数
        uint8_t buf[4];
        uint16_t i;
    
        SPI_GpioInit();
        for(i=0;i<20;i++)SPI_WriteReadByte(0XFF);//发送最少74个脉冲
        retry=20;
        do
        {
            r1=SD_SendCmd(CMD0,0,0x95);//进入IDLE状态
        }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)//SD V2.0
    {
    //Get trailing return value of R7 resp
                for(i=0;i<4;i++)buf[i]=SPI_WriteReadByte(0XFF);  
                if(buf[2]==0X01&&buf[3]==0XAA)//卡是否支持2.7~3.6V
                {
                    retry=0XFFFE;
                    do
                    {
                        SD_SendCmd(CMD55,0,0X01);   //发送CMD55
                        r1=SD_SendCmd(CMD41,0x40000000,0X01);//发送CMD41
                    }while(r1&&retry--);
                    if(retry&&SD_SendCmd(CMD58,0,0X01)==0)//鉴别SD2.0卡版本开始
                    {
                        printf("SD_SendCmd(CMD41,0x40000000,0X01) SUCCESS!\r\n");
                        for(i=0;i<4;i++)buf[i]=SPI_WriteReadByte(0XFF);//得到OCR值
                        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;    //检查CCS
                        else SD_Type=SD_TYPE_V2;
                        printf("SD_Type %02X\r\n", SD_Type);
                    }
                }
            }
            else//SD V1.x/ MMC V3
            {
                SD_SendCmd(CMD55,0,0X01);       //发送CMD55
                r1=SD_SendCmd(CMD41,0,0X01);    //发送CMD41
                if(r1<=1)
                {
                    SD_Type=SD_TYPE_V1;
                    retry=0XFFFE;
                    do //等待退出IDLE模式
                    {
                        SD_SendCmd(CMD55,0,0X01);   //发送CMD55
                        r1=SD_SendCmd(CMD41,0,0X01);//发送CMD41
                    }while(r1&&retry--);
                }else//MMC卡不支持CMD55+CMD41识别
                {
                    SD_Type=SD_TYPE_MMC;//MMC V3
                    retry=0XFFFE;
                    do //等待退出IDLE模式
                    {
                        r1=SD_SendCmd(CMD1,0,0X01);//发送CMD1
                    }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卡读写代码分析

    /*
     * 向sd卡写入一个数据包的内容 512字节
     * 参数:buf:数据缓存区    cmd:指令
     * 返回值:0,成功;其他,失败;
     * */
    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);//忽略crc
            SPI_WriteReadByte(0xFF);
            t=SPI_WriteReadByte(0xFF);//接收响应
            if((t&0x1F)!=0x05)return 2;//响应错误
        }
        return 0;//写入成功
    }
    
    /*
     * 读SD卡
     * 参数:buf:数据缓存区
     *      sector:扇区
     *      cnt:扇区数
     * 返回值:0,ok;其他,失败.
     * */
    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);//接收512个字节
            }
        }else
        {
            r1=SD_SendCmd(CMD18,sector,0X01);//连续读命令
            do
            {
                r1=SD_RecvData(buf,512);//接收512个字节
                buf+=512;
            }while(--cnt && r1==0);
            SD_SendCmd(CMD12,0,0X01);   //发送停止命令
        }
        SD_DisSelect();//取消片选
        return r1;//
    }
    
    /*
     * 写SD卡
     * 参数:buf:数据缓存区
     *      sector:扇区
     *      cnt:扇区数
     * 返回值:0,ok;其他,失败.
     * */
    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);//写512个字节
            }
        }
        else
        {
            if(SD_Type!=SD_TYPE_MMC)
            {
                do
                {
                    SD_SendCmd(CMD55,0,0X01);
    //            SD_SendCmd(CMD23,cnt,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);//接收512个字节
                    buf+=512;
                }while(--cnt && r1==0);
                r1=SD_SendBlock(0,0xFD);//接收512个字节
            }
        }
        SD_DisSelect();//取消片选
        return r1;//
    }
    

    三、 完整代码

    Sd_card.h

    /*
     * sd_card.h
     *
     *  Created on: 2021年11月26日
     *      Author: heng.liao
     */
    
    #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"
    
    // SD卡类型定义
    #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
    
    // SD卡指令表
    #define CMD0    0       //卡复位
    #define CMD1    1
    #define CMD8    8       //命令8 ,SEND_IF_COND
    #define CMD9    9       //命令9 ,读CSD数据
    #define CMD10   10      //命令10,读CID数据
    #define CMD12   12      //命令12,停止数据传输
    #define CMD16   16      //命令16,设置SectorSize 应返回0x00
    #define CMD17   17      //命令17,读sector
    #define CMD18   18      //命令18,读Multi sector
    #define CMD22   22
    #define CMD23   23      //命令23,设置多sector写入前预先擦除N个block
    #define CMD24   24      //命令24,写sector
    #define CMD25   25      //命令25,写Multi sector
    #define CMD41   41      //命令41,应返回0x00
    #define CMD55   55      //命令55,应返回0x01
    #define CMD58   58      //命令58,读OCR信息
    #define CMD59   59      //命令59,使能/禁止CRC,应返回0x00
    
    
    
    //数据写入回应字意义
    extern uint8_t  SD_Type;         //SD卡的类型
    
    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 /* HARDWARE_SD_CARD_H_ */
    

    Sd_card.c

    /*
     * sd_card.c
     *
     *  Created on: 2021年11月26日
     *      Author: heng.liao
     */
    
    #include "sd_card.h"
    #include <stdio.h>
    #include "spidrv.h"
    #include "sl_udelay.h"
    
    uint8_t  SD_Type;
    
    /*
     * 取消片选,释放SPI总线
     *
     * 参数:void
     * 返回值:void
     * */
    void SD_DisSelect(void)
    {
        SD_CS_SET_GPIO_OUTPUT_STATUS(1);//SD_CS=1;
        SPI_WriteReadByte(0xff);//提供额外的8个时钟
    }
    
    /*
     * 等待SD卡准备好
     * 参数:void
     * 返回值:准备好了返回0  否则返回其他
     * */
    uint8_t SD_WaitReady(void)
    {
        uint32_t t=0;
        do
        {
            if(SPI_WriteReadByte(0XFF)==0XFF)return 0;//OK
            t++;
        }while(t<0XFF);//等待
        return 1;
    }
    
    /*
     * 选择SD卡,并且等待SD卡准备好
     * 参数:void
     * 返回值:0,成功;1,失败
     * */
    uint8_t SD_Select(void)
    {
        SD_CS_SET_GPIO_OUTPUT_STATUS(0);//SD_CS=0;
        if(SD_WaitReady()==0)return 0;//等待成功
        SD_DisSelect();
        return 1;//等待失败
    }
    
    /*
     * 向SD卡发送一个命令
     * 参数: u8 cmd   命令
     *      u32 arg  命令参数
     *      u8 crc   crc校验值
     * 返回值:SD卡返回的响应
     * */
    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())//return 0XFF;//片选失效
            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);//Skip a stuff byte when stop reading
        //等待响应,或超时退出
        Retry=0X1F;
        do
        {
            r1=SPI_WriteReadByte(0xFF);
        }while((r1&0X80) && Retry--);
        //返回状态值
        return r1;
    }
    
    /*
     * 等待SD卡回应
     * 参数:Response:要得到的回应值
     * 返回值:0,成功得到了该回应值
     *      其他,得到回应值失败
     * */
    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;//正确回应
    }
    
    /*
     * 从sd卡读取一个数据包的内容
     * 参数:buf:数据缓存区
     *      len:要读取的数据长度.
     * 返回值:0,成功;其他,失败;
     * */
    uint8_t SD_RecvData(uint8_t*buf,uint16_t len)
    {
        if(SD_GetResponse(0xFE))return 1;//等待SD卡发回数据起始令牌0xFE
        while(len--)//开始接收数据
        {
            *buf=SPI_WriteReadByte(0xFF);
            buf++;
        }
        //下面是2个伪CRC(dummy CRC)
        SPI_WriteReadByte(0xFF);
        SPI_WriteReadByte(0xFF);
        return 0;//读取成功
    }
    
    /*
     * 获取SD卡的CID信息,包括制造商信息
     * 参数:u8 *cid_data(存放CID的内存,至少16Byte)
     * 返回值:0:NO_ERR   1:错误
     * */
    uint8_t SD_GetCID(uint8_t *cid_data)
    {
        uint8_t r1;
        //发CMD10命令,读CID
        r1=SD_SendCmd(CMD10,0,0x01);
        if(r1==0x00)
        {
            r1=SD_RecvData(cid_data,16);//接收16个字节的数据
        }
        SD_DisSelect();//取消片选
        if(r1)return 1;
        else return 0;
    }
    
    /*
     * 获取SD卡的CSD信息,包括容量和速度信息
     * 参数:u8 *cid_data(存放CID的内存,至少16Byte)
     *
     * 返回值:0:NO_ERR
     *      1:错误
     * */
    uint8_t SD_GetCSD(uint8_t *csd_data)
    {
        uint8_t r1;
        r1=SD_SendCmd(CMD9,0,0x01);//发CMD9命令,读CSD
        if(r1==0)
        {
            r1=SD_RecvData(csd_data, 16);//接收16个字节的数据
        }
        SD_DisSelect();//取消片选
        if(r1)return 1;
        else return 0;
    }
    
    /*
     * 获取SD卡的总扇区数(扇区数)
     *
     * 参数:void
     *
     * 返回值:0: 取容量出错
     *          其他:SD卡的容量(扇区数/512字节)
     * 每扇区的字节数必为512,因为如果不是512,则初始化不能通过.
     */
    uint32_t SD_GetSectorCount(void)
    {
        uint8_t csd[16];
        uint32_t Capacity;
        uint8_t n;
        uint16_t csize;
        //取CSD信息,如果期间出错,返回0
        if(SD_GetCSD(csd)!=0) return 0;
        //如果为SDHC卡,按照下面方式计算
        if((csd[0]&0xC0)==0x40)  //V2.00的卡
        {
            csize = csd[9] + ((uint16_t)csd[8] << 8) + 1;
            Capacity = (uint32_t)csize << 10;//得到扇区数
        }else//V1.XX的卡
        {
            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;
    }
    
    
    /*
     * SD卡初始化
     *
     * 参数:void
     *
     * 返回值:初始化成功返回0
     *      失败返回其他
     * */
    uint8_t SD_Init(void)
    {
        uint8_t r1;      // 存放SD卡的返回值
        uint16_t retry;  // 用来进行超时计数
        uint8_t buf[4];
        uint16_t i;
    
        SPI_GpioInit();
        for(i=0;i<20;i++)SPI_WriteReadByte(0XFF);//发送最少74个脉冲
        retry=20;
        do
        {
            r1=SD_SendCmd(CMD0,0,0x95);//进入IDLE状态
        }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)//SD V2.0
            {
                for(i=0;i<4;i++)buf[i]=SPI_WriteReadByte(0XFF);  //Get trailing return value of R7 resp
                if(buf[2]==0X01&&buf[3]==0XAA)//卡是否支持2.7~3.6V
                {
                    retry=0XFFFE;
                    do
                    {
                        SD_SendCmd(CMD55,0,0X01);   //发送CMD55
                        r1=SD_SendCmd(CMD41,0x40000000,0X01);//发送CMD41
                    }while(r1&&retry--);
                    if(retry&&SD_SendCmd(CMD58,0,0X01)==0)//鉴别SD2.0卡版本开始
                    {
                        printf("SD_SendCmd(CMD41,0x40000000,0X01) SUCCESS!\r\n");
                        for(i=0;i<4;i++)buf[i]=SPI_WriteReadByte(0XFF);//得到OCR值
                        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;    //检查CCS
                        else SD_Type=SD_TYPE_V2;
                        printf("SD_Type %02X\r\n", SD_Type);
                    }
                }
            }
            else//SD V1.x/ MMC V3
            {
                SD_SendCmd(CMD55,0,0X01);       //发送CMD55
                r1=SD_SendCmd(CMD41,0,0X01);    //发送CMD41
                if(r1<=1)
                {
                    SD_Type=SD_TYPE_V1;
                    retry=0XFFFE;
                    do //等待退出IDLE模式
                    {
                        SD_SendCmd(CMD55,0,0X01);   //发送CMD55
                        r1=SD_SendCmd(CMD41,0,0X01);//发送CMD41
                    }while(r1&&retry--);
                }else//MMC卡不支持CMD55+CMD41识别
                {
                    SD_Type=SD_TYPE_MMC;//MMC V3
                    retry=0XFFFE;
                    do //等待退出IDLE模式
                    {
                        r1=SD_SendCmd(CMD1,0,0X01);//发送CMD1
                    }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;//其他错误
    }
    
    /*
     * 向sd卡写入一个数据包的内容 512字节
     * 参数:buf:数据缓存区    cmd:指令
     * 返回值:0,成功;其他,失败;
     * */
    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);//忽略crc
            SPI_WriteReadByte(0xFF);
            t=SPI_WriteReadByte(0xFF);//接收响应
            if((t&0x1F)!=0x05)return 2;//响应错误
        }
        return 0;//写入成功
    }
    
    /*
     * 读SD卡
     * 参数:buf:数据缓存区
     *      sector:扇区
     *      cnt:扇区数
     * 返回值:0,ok;其他,失败.
     * */
    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);//接收512个字节
            }
        }else
        {
            r1=SD_SendCmd(CMD18,sector,0X01);//连续读命令
            do
            {
                r1=SD_RecvData(buf,512);//接收512个字节
                buf+=512;
            }while(--cnt && r1==0);
            SD_SendCmd(CMD12,0,0X01);   //发送停止命令
        }
        SD_DisSelect();//取消片选
        return r1;//
    }
    
    /*
     * 写SD卡
     * 参数:buf:数据缓存区
     *      sector:扇区
     *      cnt:扇区数
     * 返回值:0,ok;其他,失败.
     * */
    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);//写512个字节
            }
        }
        else
        {
            if(SD_Type!=SD_TYPE_MMC)
            {
                do
                {
                    SD_SendCmd(CMD55,0,0X01);
    //            SD_SendCmd(CMD23,cnt,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);//接收512个字节
                    buf+=512;
                }while(--cnt && r1==0);
                r1=SD_SendBlock(0,0xFD);//接收512个字节
            }
        }
        SD_DisSelect();//取消片选
        return r1;//
    }
    

    /*************************************************************************/

    附:SPI源码(SPI模式0)

    Spi.h

    /*
     * spi.h
     *
     *  Created on: 2021年11月27日
     *      Author: lhsmd
     */
    
    #ifndef HARDWARE_SPI_H_
    #define HARDWARE_SPI_H_
    
    #include "em_gpio.h"
    #include "em_cmu.h"
    
    
    // USART0 TX on PC00
    #define SPI_MOSI_PORT               gpioPortC
    #define SPI_MOSI_PIN                1
    
    // USART0 RX on PC01
    #define SPI_MISO_PORT               gpioPortC
    #define SPI_MISO_PIN               3
    
    // USART0 CLK on PC02
    #define SPI_CLK_PORT              gpioPortC
    #define SPI_CLK_PIN               0
    
    // USART0 CS on PC03
    #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 /* HARDWARE_SPI_H_ */
    

    Spi.c

    /*
     * spi.c
     *
     *  Created on: 2021年11月27日
     *      Author: lhsmd
     */
    
    #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时钟 */
        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)
    * 输入参数:data -> 要写的数据
    * 输出参数:无
    * 返回值:无
    * 函数作用:模拟 SPI 写一个字节
    */
    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); //SPI_CLK(0); //CPOL=0
            SD_MOSI_SET_GPIO_OUTPUT_STATUS(temp);//SPI_MOSI(temp);
            SPI_Delay(1);
            SD_CLK_SET_GPIO_OUTPUT_STATUS(1);//SPI_CLK(1); //CPHA=0
            SPI_Delay(1);
        }
        SD_CLK_SET_GPIO_OUTPUT_STATUS(0);//SPI_CLK(0);
    }
    /*
    * 函数名:uint8_t SPI_ReadByte(void)
    * 输入参数:
    * 输出参数:无
    * 返回值:读到的数据
    * 函数作用:模拟 SPI 读一个字节
    */
    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_CLK(0);
            SPI_Delay(1);
            SD_CLK_SET_GPIO_OUTPUT_STATUS(1);//SPI_CLK(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);//SPI_CLK(0);
        return read_data;
    }
    
    
    
    
    /*
    * 函数名:uint8_t SPI_WriteReadByte(uint8_t data)
    * 输入参数:data -> 要写的一个字节数据
    * 输出参数:无
    * 返回值:读到的数据
    * 函数作用:模拟 SPI 读写一个字节
    */
    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);//SPI_CLK(0);
            SD_MOSI_SET_GPIO_OUTPUT_STATUS(temp);//SPI_MOSI(temp);
            SPI_Delay(1);
            SD_CLK_SET_GPIO_OUTPUT_STATUS(1);//SPI_CLK(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); //SPI_CLK(0);
        return read_data;
    }
    
    展开全文
  • SD卡的初始化及基本应用

    千次阅读 2020-02-15 22:30:27
    在通信之前,我们需要对SD卡进行初始化操作,对SD初始化需要通过我们前面说的CMD命令,SD卡接收到相应的CMD命令后,反馈相应的数据,我们便是通过这些反馈的数据对我们SD卡的类型,大小等进行判断,从而完成初始化...
  • SD卡驱动初始化失败

    千次阅读 2020-05-26 10:31:04
    各位大佬,小弟正在做一个可穿戴设备,数据是采用SD卡存储的,移植了最小文件系统,但出现概率性出现SD卡驱动初始化失败,通过Debug发现问题如下: disk_initialize = STA_NOINIT
  • bootloader---27.uboot中SD初始化及读写分析
  • SD初始化代码

    2016-01-09 21:06:28
    SD初始化代码,这一版本仅仅完成初始化功能
  • STM32初始化SD卡流程

    千次阅读 2019-10-28 14:33:42
    1,初始化SDIO外设,关闭SDIO时钟。 2,上电,开启时钟。 3,延时最低74个时钟周期。使时钟与SD卡稳定,否则会出错。 4,发送CMD0,需要返回0x01,进入Idle状态 5,为了区别SD卡是2.0还是1.0,或是MMC卡,这里根据...
  • 初次接触使用spi接口读写sd卡,在初始化阶段一直过不去,但随着这几天的学习也有一些心得,分享出来,供大家参考使用,新手摸石头过河,多有不足,还请斧正!!
  • SD初始化流程

    2014-01-01 13:48:13
    SD初始化流程 当host上电后,使所有的卡设备处于卡识别模式,完成设置有效操作电压范围,卡识别和请求卡相对地址等操作。 1、 发送指令CMD0使卡设备处于idle状态; 2、 发送指令CMD8,如果卡设备...
  • SD初始化流程说明

    2015-03-26 21:24:30
    SD初始化流程,简单说明了SD初始化的指令与步骤。本人是基于FPGA写的。对SD初始化的指令作了说明。
  • FPGA开发之SD初始化

    万次阅读 多人点赞 2014-11-29 11:02:00
    清晰明了的MMC卡时序图(虽然这个是MMC卡的,但是在初始化的时候CMD0的时序是一样的) 电路:我用的SD卡的电路其实很简单,参考SD卡的官方资料中的电路链接就可以的。 供电问题:由于SD卡的电压是3
  • 适用于STM32F4,基于STM32CubeMX及HAL库开发,SD初始化程序,LCD显示SD卡基本信息,测试环境为正点原子探索者开发板,可用于项目开发或学习参考
  • 上传的为一个整体代码,里面有具体使用sd卡的代码,非常详细
  • 初始化 1.cmd 0 2.cmd8 3.cmd55 4.acmd41 5.cmd2 6.cmd3 7.cmd9 8.cmd13 9.cmd7 10.ACMD51 11.CMD6 12.CMD16 13.CMD17 14.CMD18 15.CMD12 tuning CMD19 DW_SDHCI的tuning流程 初始化 1.cmd 0 ...
  • 转载于:https://www.cnblogs.com/GyForever1004/p/8869027.html
  • SD卡的SPI模式的初始化顺序,对开发SD卡很有帮助
  • MMC/SD卡时序图

    2016-12-26 11:43:10
    本文档详细描述了MMC/SD卡时序图,虽然详细画出了MMC/SD的时序图,但是本文档只有时序图和时序图的说明解释,不包含标准资料。
  • SD初始化过程以及Cmd解析

    千次阅读 2012-08-22 19:03:27
    SD: 1条CMD线,所有的命令和回应都是通过这条线一位一位的传输.不同模式或不同版本下,命令有不同含义.(SD(SD[0],SD[3:0]),SPI)) 1条时钟线,时钟源是来自APH总线时钟 ...SD卡是以块为单位,初始化时有配置,一般为5
  • 新唐NUC977开发板uboot代码解析3-SD初始化及mmc相关命令
  • 调用SD库的官方例子不能初始化。 解决办法 1.就是将IDE更新到最新的,现在我用的是cc的1.6.10不能正常,更新到1.6.12就可以了,大家说1.6.10有很多问题 2.在setup函数里加上pinMode(10,OUTPUT)(10是CS引脚,片选...
  • SD SDIO MMC 初始化经典解析
  • SD初始化以及命令详解

    千次阅读 2020-09-08 21:37:36
    SD卡和MMC卡的区别在于初始化过程不同。SD卡并不是我们通常意义上的手机扩展卡,那种卡叫做TF卡,但是通讯以及驱动模式是类似的. SD卡的通信协议包括SD和SPI两类,SD卡上电之后默认处于SD状态。 SD卡使用卡内智能控制...
  • 现象:初始化4bit SDIO模式的TF卡,卡死在初始化过程中。 问题现象代码移植于野火开发板相关例程。问题现象中的TF卡在野火开发板中初始化正常。 当TF卡初始化卡死的时候,测得TF卡回路耗电200mA以上,而单独为TF卡...
  • 适用于F407,基于HAL库开发,用于初始化SD卡及获取SD卡基本信息并在串口打印,可作为STM32软硬件调试的参考

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 61,280
精华内容 24,512
关键字:

sd初始化