【ALIENTEK 战舰STM32开发板例程系列连载+教学】第四十四章 SD卡实验

2019-10-15 01:59发布

第四十四章  SD卡实验

       很多单片机系统都需要大容量存储设备,以存储数据。目前常用的有U盘,FLASH芯片,SD卡等。他们各有优点,综合比较,最适合单片机系统的莫过于SD卡了,它不仅容量可以做到很大(32Gb以上),而且支持SPI接口,方便移动,并且有几种体积的尺寸可供选择(标准的SD卡尺寸,以及TF卡尺寸等),能满足不同应用的要求。 只需要4IO口即可外扩一个最大达32GB以上的外部存储器,容量从几十M到几十G选择尺度很大,更换也很方便,编程也简单,是单片机大容量外部存储器的首选。 ALIENTKE 战舰STM32开发板自带了标准的SD卡接口,可使用STM32自带的SPI/SDIO接口驱动(通过跳线帽选择驱动方式),本章我们使用SPI驱动,最高通信速度可达18Mbps,每秒可传输数据2M字节以上,对于一般应用足够了。在本章中,我们将向大家介绍,如何在ALIENTEK战舰STM32开发板上实现SD卡的读取。本章分为如下几个部分: 44.1 SD卡简介 44.2 硬件设计 44.3 软件设计 44.4 下载验证

44.1 SD卡简介

SD卡(Secure Digital Memory Card)中文翻译为安全数码卡,它是在MMC的基础上发展而来,是一种基于半导体快闪记忆器的新一代记忆设备,它被广泛地于便携式装置上使用,例如数码相机、个人数码助理(PDA)和多媒体播放器等。SD卡由日本松下、东芝及美国SanDisk公司于19998月共同开发研制。大小犹如一张邮票的SD记忆卡,重量只有2,但却拥有高记忆容量、快速数据传输率、极大的移动灵活性以及很好的安全性。按容量分类,可以将SD卡分为3类:SD卡、SDHC卡、SDXC卡。如表44.1.1所示: 容量 命名 简称 0~2G Standard Capacity SD Memory Card SDSCSD 2G~32G High Capacity SD Memory Card SDHC 32G~2T Extended Capacity SD Memory Card SDXC 44.1.1 SD卡按容量分类 SD卡和SDHC卡协议基本兼容,但是SDXC卡,同这两者区别就比较大了,本章我们讨论的主要是SD/SDHC卡(简称SD卡)。 SD卡一般支持2种操作模式: 1SD卡模式(通过SDIO通信); 2SPI模式; 主机可以选择以上任意一种模式同SD卡通信,SD卡模式允许4线的高速数据传输。SPI模式允许简单的通过SPI接口来和SD卡通信,这种模式同SD卡模式相比就是丧失了速度。 SD卡的引脚排序如下图44.1.1所示:
44.1.1 SD卡引脚排序图 SD卡引脚功能描述如表45.1.2所示:
45.1.2 SD卡引脚功能表 SD卡只能使用3.3VIO电平,所以,MCU一定要能够支持3.3VIO端口输出。注意:在SPI模式下,CS/MOSI/MISO/CLK都需要加10~100K左右的上拉电阻。 SD卡有5个寄存器,如表45.1.3所示: 名称 宽度 描述 CID 128 卡标识寄存器 RCA 16 相对卡地址(Relative card address)寄存器:本地系统中卡的地址,动态变化,在主机初始化的时候确定 *SPI模式中没有 CSD 128 卡描述数据:卡操作条件相关的信息数据 SCR 64 SD配置寄存器:SD卡特定信息数据 OCR 32 操作条件寄存器 45.1.3 SD卡相关寄存器 关于这些寄存器的详细描述,请参考光盘相关SD卡资料。我们在这里就不描述了。接下来,我们看看SD卡的命令格式,如表45.1.4所示: 字节1 字节2--5 字节6 7 6 5      0 31       0 7       1 0 0 1 command 命令参数 CRC 1 45.1.4 SD卡命令格式 SD卡的指令由6个字节组成,字节1的最高2位固定为01,低6位为命令号(比如CMD16,为1000016进制的0X10,完整的CMD16,第一个字节为01010000,即0X10+0X40)。 字节2~5为命令参数,有些命令是没有参数的。 字节6的高七位为CRC值,最低位恒定为1 SD卡的命令总共有12类,分为Class0~Class11,本章,我们仅介绍几个比较重要的命令,如表45.1.5所示: 命令 参数 回应 描述 CMD0(0X00) NONE R1 复位SD CMD8(0X08) VHS+Check pattern R7 发送接口状态命令 CMD9(0X09) NONE R1 读取卡特定数据寄存器 CMD10(0X0A) NONE R1 读取卡标志数据寄存器 CMD16(0X10) 块大小 R1 设置块大小(字节数) CMD17(0X11) 地址 R1 读取一个块的数据 CMD24(0X18) 地址 R1 写入一个块的数据 CMD41(0X29) NONE R3 发送给主机容量支持信息和激活
卡初始化过程
CMD55(0X37) NONE R1 告诉SD卡,下一个是特定应用命令 CMD58(0X3A) NONE R3 读取OCR寄存器 45.1.5 SD卡部分命令 上表中,大部分的命令是初始化的时候用的。表中的R1R3R7等是SD卡的回应,SD卡和单片机的通信采用发送应答机制,如图45.1.2所示:  45.1.2 SD卡命令传输过程        每发送一个命令,SD卡都会给出一个应答,以告知主机该命令的执行情况,或者返回主机需要获取的数据。SPI模式下,SD卡针对不同的命令,应答可以使R1~R7R1的应答,各位描述如表45.1.6所示:
R1响应格式 7 6 5 4 3 2 1 0 含义 开始位
始终为0
参数
错误
地址
错误
擦除序列
错误
CRC错误 非法
命令
擦除
复位
闲置
状态
45.1.6 R1响应各位描述        R2~R7的响应,我们就不介绍了,请的大家参考SD2.0协议。接下来,我们看看SD卡初始化过程。因为我们使用的是SPI模式,所以先得让SD卡进入SPI模式。方法如下:SD卡收到复位命令(CMD0)时,CS为有效电平(低电平)则SPI模式被启用。不过在发送CMD0之前,要发送>74个时钟,这是因为SD卡内部有个供电电压上升时间,大概为64CLK,剩下的10CLK用于SD卡同步,之后才能开始CMD0的操作,在卡初始化的时候,CLK时钟最大不能超过400Khz!。 接着我们看看SD卡的初始化,SD卡的典型初始化过程如下: 1、初始化与SD卡连接的硬件条件(MCUSPI配置,IO口配置); 2、上电延时(>74CLK); 3、复位卡(CMD0),进入IDLE状态; 4、发送CMD8,检查是否支持2.0协议; 5、根据不同协议检查SD卡(命令包括:CMD55CMD41CMD58CMD1等); 6、取消片选,发多8CLK,结束初始化 这样我们就完成了对SD卡的初始化,注意末尾发送的8CLK是提供SD卡额外的时钟,完成某些操作。通过SD卡初始化,我们可以知道SD卡的类型(V1V2V2HC或者MMC),在完成了初始化之后,就可以开始读写数据了。 SD卡读取数据,这里通过CMD17来实现,具体过程如下: 1、发送CMD17 2、接收卡响应R1 3、接收数据起始令牌0XFE 4、接收数据; 5、接收2个字节的CRC,如果不使用CRC,这两个字节在读取后可以丢掉。 6、禁止片选之后,发多8CLK 以上就是一个典型的读取SD卡数据过程,SD卡的写于读数据差不多,写数据通过CMD24来实现,具体过程如下: 1、发送CMD24; 2、接收卡响应R1 3、发送写数据起始令牌0XFE 4、发送数据; 5、发送2字节的伪CRC 6、禁止片选之后,发多8CLK 以上就是一个典型的写SD卡过程。关于SD卡的介绍,我们就介绍到这里,更详细的介绍请参考光盘SD卡的参考资料(SD2.0协议)。

44.2 硬件设计 

本章实验功能简介:开机的时候先初始化SD卡,如果SD卡初始化完成,则提示LCD初始化成功。按下KEY0,读取SD卡扇区0的数据,然后通过串口发送到电脑。如果没初始化通过,则在LCD上提示初始化失败。 同样用DS0来指示程序正在运行。 本实验用到的硬件资源有: 1)  指示灯DS0 2)  KEY0按键 3)  串口 4)  TFTLCD模块 5)  SD 前面四部分,在之前的实例已经介绍过了,这里我们介绍一下战舰STM32开发板板载的SD卡接口和STM32的连接关系,如图44.2.1所示:

44.2.1 SD卡接口与STM32连接原理图        我们用跳线帽将P10SD_DT3SD_CMDSD_SCKSD_DT0分别同P12SD_CSSPI2_MOSISPI2_SCKSPI2_MISO连接起来,即实现SD卡的SPI模式连接。硬件连接示意图如图44.2.2所示:
44.2.2 SDSPI方式硬件连接示意图        将图中所示的4处,用跳线帽短接,接口实现SD卡与STM32SPI连接。最后,你还得自备一个SD卡,将其插入板子下面的SD卡接口。

44.3 软件设计

打开上一章的工程,首先在HARDWARE文件夹下新建一个SD的文件夹。然后新建一个MMC_SD.CMMC_SD.H的文件保存在SD文件夹下,并将这个文件夹加入头文件包含路径。 打开MMC_SD.C文件,在该文件里面,我们输入与SD卡相关的操作代码,这里由于篇幅限制,我们不贴出所有代码,仅介绍两个最重要的函数,第一个是SD_Initialize函数,该函数源码如下:                                               //初始化SD u8 SD_Initialize(void) {     u8 r1;      // 存放SD卡的返回值     u16 retry;  // 用来进行超时计数     u8 buf[4];         u16 i;        SD_SPI_Init();             //初始化IO       SD_SPI_SpeedLow();    //设置到低速模式       for(i=0;i<10;i++)SD_SPI_ReadWriteByte(0XFF);//发送最少74个脉冲        retry=20;        do        {               r1=SD_SendCmd(CMD0,0,0x95);//进入IDLE状态        }while((r1!=0X01) && retry--);       SD_Type=0;//默认无卡        if(r1==0X01)        {               if(SD_SendCmd(CMD8,0x1AA,0x87)==1)//SD V2.0               {                      for(i=0;i<4;i++)buf=SD_SPI_ReadWriteByte(0XFF);//得到R7相应值                            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卡版本开始                             {                                    for(i=0;i<4;i++)buf=SD_SPI_ReadWriteByte(0XFF);//得到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(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();//取消片选        SD_SPI_SpeedHigh();//高速        if(SD_Type)return 0;        else if(r1)return r1;                  return 0xaa;//其他错误 }                                                                                                                                 该函数先设置与SD相关的IO口及SPI初始化,然后发送CMD0,进入IDLE状态,并设置SD卡为SPI模式通信,然后判断SD卡类型,完成SD卡的初始化,注意该函数调用的SD_SPI_Init等函数,实际是对SPI2的相关函数进行了一层封装,方便移植。另外一个要介绍的函数是 SD_ReadDisk,该函数用于从SD卡读取一个扇区的数据(这里一般为512字节),该函数代码如下:                                                                          //SD //buf:数据缓存区 //sector:扇区 //cnt:扇区数 //返回值:0,ok;其他,失败. u8 SD_ReadDisk(u8*buf,u32 sector,u8 cnt) {        u8 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;// } 此函数先发送CMD17命令,然后读取一个扇区的数据,详细见代码,这里我们就不多介绍了。保存MMC_SD.C文件,并加入到HARDWARE组下,然后打开MMC_SD.H,在该文件里面输入如下代码: #ifndef _MMC_SD_H_ #define _MMC_SD_H_        #include "sys.h"     #include <stm32f10x_map.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 CMD23   23      //命令23,设置多sector写入前预先擦除Nblock #define CMD24  
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。