stm32 SD(SDSC)卡的学习与SPI 模式应用(卡槽介绍)

这次的sd卡内容比较多,写的比较多,关于卡的命令和寄存器方面内容较多下面只是简要题一下,我们只需要指导如何使用即可,深入研究相对比较困难。

什么是SD 卡?
SD 卡( Secure Digital Memory Card)中文翻译为安全数码卡, 它是在 MMC 的基础上发展而来, 是一种基于半导体快闪记忆器的新一代记忆设备,它被广泛地于便携式装置上使用,例如数码相机、个人数码助理(PDA)和多媒体播放器等。就是之前常用的内存卡,手机内存卡,数码相机内存卡,不过是容量和规格不一样的区别。

TF卡和SD卡?
SD卡又名安全数字卡,SD卡一般都用在大一些的电子设备上,如电脑、相机、摄像机等器材中。相比TF卡,SD卡的读写速度会更高,并且具备读写保护能力。TF卡又叫微型SD卡,一般常用于手机扩展内存或者行车记录仪,以及其他手持微型摄像设备,TF卡并不具备保护功能。

常见的SD卡如下图
在这里插入图片描述

SD卡的分类

  1. 一般SD卡 (SDSC/SD) 0—2G
  2. SDHK 高容量SD存储卡 2G—32G
  3. SDXC 容量扩大化的安全存储卡 32G–2T

通信模式

  1. SPI 模式
  2. SD模式(SDIO通信)

工作模式

  1. 卡识别模式 (主机上电复位,一直等操作命令)
  2. 数据传输模式(主机识别到卡后进入此模式)

引脚排序如下图所示
![在这里插入图片描述](https://img-blog.csdnimg.cn/a7dd9940bcea461b9c83c47ff639db10.png
在这里插入图片描述在这里插入图片描述

普通SD卡有9个引脚,使其反面朝上,最左边为9号引脚,然后从左到右依次是1号~8号

针脚123456789
SD卡模式CD/DAT3CMDVSSVCCCLKVSSDAT0DAT1DAT2
SPI模式CSMOSIVSSVCCCLKVSSMISONCNC

与单片机的连接接电路图
在这里插入图片描述

上图可以看出支持2种模式spi 和sdio
在这里插入图片描述
上图仅支持sdio 模式

SD卡总线拓扑
SD总线有6根通信线和三根电源供应线:
CMD——命令线是双向信号线。主机和卡通过push pull 模式工作。
DAT0-3——数据线是双向信号线。主机和卡通过push pull 模式工作。
CLK——时钟是从主机到卡的信号。CLK通过push pull 模式操作。
VDD—VDD是所有卡的电源供应线。
VSS[1:2]—VSS是2根地线。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
SK卡的寄存器
在这里插入图片描述

  1. SD卡操作电压范围为2~3.6V。然而从内存中访问数据的电压是2.7–3.6V(32位的操作条件寄存器存储了VDD电压范围)。CSD寄存器包含访问卡数据所需的配置信息。SD卡和MMC卡的CSD不同,
  2. SD卡分为2个区: 用户区—用户通过读写命令存储安全和非安全数据。 安全保护区(Security Protected
    Area)—版权保护应用程序用来保存安全相关数据,通过SD安全规范中定义的条件验证后,由主机使用安全的读写指令完成操作。安全保护区的大小大概是总大小的1%。
**SD卡的命令**

在这里插入图片描述
下面是几个常用的命令(卡的命令约有50多个,根据不同产品厂商会提供参考说明)
在这里插入图片描述

SD卡初始化流程图解析如下图
在这里插入图片描述

下面是对上面流程图的分解在这里插入图片描述在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

-----------------------------------下面会使用 模式1SPI模式进行卡的读取---------------------------------------------------------------------

SD卡初始化过程:
1、初始化与 SD 卡连接的硬件条件( MCU 的 SPI 配置, IO 口配置)**卡槽(后面会提)**电路连接好,直接插入sd卡即可;
2、上电延时( >74 个 CLK);
上电后,主机开始时钟并在CMD线上发送初始化序列,初始化序列由连续的逻辑“1”组成。序列长度为最大1毫秒,74个时钟或supply-ramp-up时间。额外的10个时钟(64个时钟后卡已准备就绪)用来实现同步。

3、复位卡( CMD0),进入 IDLE 状态;

4、发送 CMD8,检查是否支持 2.0 协议;

5、根据不同协议检查 SD 卡(命令包括: CMD55、 CMD41、 CMD58 和 CMD1 等);
上电后,包括热插入,卡进入idle状态。在该状态SD卡忽略所有总线操作直到接收到ACMD41命令。ACMD41命令是一个特殊的同步命令,用来协商操作电压范围,并轮询所有的卡。除了操作电压信息,ACMD41的响应还包括一个忙标志,表明卡还在power-up过程工作,还没有准备好识别操作,即告诉主机卡还没有就绪。主机等待(继续轮询)直到忙标志清除。单个卡的最大上电时间不能操作1秒。

6、取消片选,发多 8 个 CLK,结束初始化

SD 卡读取数据,这里通过 CMD17 来实现,具体过程如下:
1、发送 CMD17;
2、接收卡响应 R1;
3、接收数据起始令牌 0XFE;
4、接收数据;
5、接收 2 个字节的 CRC,如果不使用 CRC,这两个字节在读取后可以丢掉。
6、禁止片选之后,发多 8 个 CLK;

SD 卡的写与读数据差不多,写数据通过 CMD24来实现,具体过程如下
1、发送 CMD24;
2、接收卡响应 R1;
3、发送写数据起始令牌 0XFE;
4、发送数据;
5、发送 2 字节的伪 CRC;
6、禁止片选之后,发多 8 个 CLK;

控制程序流程(核心掌握的)

  1. 初始化SD卡
  2. SD卡读写块函数
  3. 编写主函数

/**************************下面上才源码,仅供参考*********************************/

spi.c 初始化IO口和SPI 的初始化以及准备工作

#include "spi.h"

//以下是SPI模块的初始化代码,配置成主机模式 						  
//SPI口初始化
//这里针是对SPI1的初始化
void SPI1_Init(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	SPI_InitTypeDef  SPI_InitStructure;
	
	/* SPI的IO口和SPI外设打开时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
	
	/* SPI的IO口设置 */
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;		//设置SPI工作模式:设置为主SPI
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;		//设置SPI的数据大小:SPI发送接收8位帧结构
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;		//串行同步时钟的空闲状态为高电平
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;	//串行同步时钟的第二个跳变沿(上升或下降)数据被采样
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;		//NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;		//定义波特率预分频的值:波特率预分频值为256
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;	//指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
	SPI_InitStructure.SPI_CRCPolynomial = 7;	//CRC值计算的多项式
	SPI_Init(SPI1, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
	
	SPI_Cmd(SPI1, ENABLE); //使能SPI外设
	
	SPI1_ReadWriteByte(0xff);//启动传输	
}

//SPI1速度设置函数
//SPI速度=fAPB2/分频系数
//@ref SPI_BaudRate_Prescaler:SPI_BaudRatePrescaler_2~SPI_BaudRatePrescaler_256  
//fAPB2时钟一般为84Mhz:
void SPI1_SetSpeed(u8 SPI_BaudRatePrescaler)
{
	SPI1->CR1&=0XFFC7;//位3-5清零,用来设置波特率
	SPI1->CR1|=SPI_BaudRatePrescaler;	//设置SPI1速度 
	SPI_Cmd(SPI1,ENABLE); //使能SPI1
} 

//SPI1 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
u8 SPI1_ReadWriteByte(u8 TxData)
{		 			 
 
	while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);//等待发送区空  
	
	SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个byte  数据
		
	while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET); //等待接收完一个byte  
 
	return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据	
 		    
}



//SPI口初始化
//这里针是对SPI2的初始化
void SPI2_Init(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	SPI_InitTypeDef  SPI_InitStructure;
	
	/* SPI的IO口和SPI外设打开时钟 */
    RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOB, ENABLE );
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
	
	/* SPI的IO口设置 */
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //PB13/14/15复用推挽输出 
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);

	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;		//设置SPI工作模式:设置为主SPI
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;		//设置SPI的数据大小:SPI发送接收8位帧结构
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;		//串行同步时钟的空闲状态为高电平
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;	//串行同步时钟的第二个跳变沿(上升或下降)数据被采样
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;		//NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;		//定义波特率预分频的值:波特率预分频值为256
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;	//指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
	SPI_InitStructure.SPI_CRCPolynomial = 7;	//CRC值计算的多项式
	SPI_Init(SPI2, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
	
	SPI_Cmd(SPI2, ENABLE); //使能SPI外设
	
	SPI2_ReadWriteByte(0xff);//启动传输	
}

//SPI2速度设置函数
//SPI速度=fAPB1/分频系数
//@ref SPI_BaudRate_Prescaler:SPI_BaudRatePrescaler_2~SPI_BaudRatePrescaler_256  
//fAPB1时钟一般为36Mhz:
void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler)
{
	SPI2->CR1&=0XFFC7;//位3-5清零,用来设置波特率
	SPI2->CR1|=SPI_BaudRatePrescaler;	//设置SPI速度 
	SPI_Cmd(SPI2,ENABLE); //使能SPI2
} 

//SPI2 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
u8 SPI2_ReadWriteByte(u8 TxData)
{		 			 
 
	while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);//等待发送区空  
	
	SPI_I2S_SendData(SPI2, TxData); //通过外设SPIx发送一个byte  数据
		
	while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET); //等待接收完一个byte  
 
	return SPI_I2S_ReceiveData(SPI2); //返回通过SPIx最近接收的数据	
 		    
}


spi.h

#ifndef _spi_H
#define _spi_H

#include "system.h"

void SPI1_Init(void);			 //初始化SPI1口
void SPI1_SetSpeed(u8 SpeedSet); //设置SPI1速度   
u8 SPI1_ReadWriteByte(u8 TxData);//SPI1总线读写一个字节

void SPI2_Init(void);			 //初始化SPI2口
void SPI2_SetSpeed(u8 SpeedSet); //设置SPI2速度   
u8 SPI2_ReadWriteByte(u8 TxData);//SPI2总线读写一个字节

#endif

下面是sd 的驱动相关内容
sd.c

#include "sd.h"
#include "spi.h"
#include "usart.h"
#include "SysTick.h"


u8 SD_Type=0;//SD卡的类型

//data:要写入的数据
//返回值:读到的数据
u8 SD_SPI_ReadWriteByte(u8 dat) 
{
	return SPI2_ReadWriteByte(dat);
}

//SD卡初始化的时候,需要低速
void SD_SPI_SpeedLow(void)
{
 	SPI2_SetSpeed(SPI_BaudRatePrescaler_256);//设置到低速模式	
}

//SD卡正常工作的时候,可以高速了
void SD_SPI_SpeedHigh(void)
{
 	SPI2_SetSpeed(SPI_BaudRatePrescaler_2);//设置到高速模式	
}

//SPI硬件层初始化
void SD_SPI_Init(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
 
	//关闭其他使用SPI2总线的片选 EN25QXX-PG13,ENC28J60-PB12,NRF24L01-PF9
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOG|RCC_APB2Periph_GPIOF, ENABLE);	 //使能PB端口时钟

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;				 //PB12 推挽 
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOB, &GPIO_InitStructure);
 	GPIO_SetBits(GPIOB,GPIO_Pin_12);						 //PB12上拉
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_14;				 //PG13 PG14推挽
	GPIO_Init(GPIOG, &GPIO_InitStructure);
	GPIO_SetBits(GPIOG,GPIO_Pin_13);
			 
	SPI2_Init();
	SD_CS=1;	
}

//取消选择,释放SPI总线
void SD_DisSelect(void)
{
	SD_CS=1;
 	SD_SPI_ReadWriteByte(0xff);//提供额外的8个时钟
}

//选择sd卡,并且等待卡准备OK
//返回值:0,成功;1,失败;
u8 SD_Select(void)
{
	SD_CS=0;
	if(SD_WaitReady()==0)return 0;//等待成功
	SD_DisSelect();
	return 1;//等待失败
}

//等待卡准备好
//返回值:0,准备好了;其他,错误代码
u8 SD_WaitReady(void)
{
	u32 t=0;
	do
	{
		if(SD_SPI_ReadWriteByte(0XFF)==0XFF)return 0;//OK
		t++;		  	
	}while(t<0XFFFFFF);//等待 
	return 1;
}

//等待SD卡回应
//Response:要得到的回应值
//返回值:0,成功得到了该回应值
//    其他,得到回应值失败
u8 SD_GetResponse(u8 Response)
{
	u16 Count=0xFFFF;//等待次数	   						  
	while ((SD_SPI_ReadWriteByte(0XFF)!=Response)&&Count)Count--;//等待得到准确的回应  	  
	if (Count==0)return MSD_RESPONSE_FAILURE;//得到回应失败   
	else return MSD_RESPONSE_NO_ERROR;//正确回应
}

//从sd卡读取一个数据包的内容
//buf:数据缓存区
//len:要读取的数据长度.
//返回值:0,成功;其他,失败;	
u8 SD_RecvData(u8*buf,u16 len)
{			  	  
	if(SD_GetResponse(0xFE))return 1;//等待SD卡发回数据起始令牌0xFE
    while(len--)//开始接收数据
    {
        *buf=SPI2_ReadWriteByte(0xFF);
        buf++;
    }
    //下面是2个伪CRC(dummy CRC)
    SD_SPI_ReadWriteByte(0xFF);
    SD_SPI_ReadWriteByte(0xFF);									  					    
    return 0;//读取成功
}


//向sd卡写入一个数据包的内容 512字节
//buf:数据缓存区
//cmd:指令
//返回值:0,成功;其他,失败;	
u8 SD_SendBlock(u8*buf,u8 cmd)
{	
	u16 t;		  	  
	if(SD_WaitReady())return 1;//等待准备失效
	SD_SPI_ReadWriteByte(cmd);
	if(cmd!=0XFD)//不是结束指令
	{
		for(t=0;t<512;t++)SPI2_ReadWriteByte(buf[t]);//提高速度,减少函数传参时间
	    SD_SPI_ReadWriteByte(0xFF);//忽略crc
	    SD_SPI_ReadWriteByte(0xFF);
		t=SD_SPI_ReadWriteByte(0xFF);//接收响应
		if((t&0x1F)!=0x05)return 2;//响应错误									  					    
	}						 									  					    
    return 0;//写入成功
}

//向SD卡发送一个命令
//输入: u8 cmd   命令 
//      u32 arg  命令参数
//      u8 crc   crc校验值	   
//返回值:SD卡返回的响应															  
u8 SD_SendCmd(u8 cmd, u32 arg, u8 crc)
{
    u8 r1;	
	u8 Retry=0; 
	SD_DisSelect();//取消上次片选
	if(SD_Select())return 0XFF;//片选失效 
	//发送
    SD_SPI_ReadWriteByte(cmd | 0x40);//分别写入命令
    SD_SPI_ReadWriteByte(arg >> 24);
    SD_SPI_ReadWriteByte(arg >> 16);
    SD_SPI_ReadWriteByte(arg >> 8);
    SD_SPI_ReadWriteByte(arg);	  
    SD_SPI_ReadWriteByte(crc); 
	if(cmd==CMD12)SD_SPI_ReadWriteByte(0xff);//Skip a stuff byte when stop reading
    //等待响应,或超时退出
	Retry=0X1F;
	do
	{
		r1=SD_SPI_ReadWriteByte(0xFF);
	}while((r1&0X80) && Retry--);	 
	//返回状态值
    return r1;
}

//获取SD卡的CID信息,包括制造商信息
//输入: u8 *cid_data(存放CID的内存,至少16Byte)	  
//返回值:0:NO_ERR
//		 1:错误														   
u8 SD_GetCID(u8 *cid_data)
{
    u8 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 *csd_data(存放CSD的内存,至少16Byte)	    
//返回值:0:NO_ERR
//		 1:错误														   
u8 SD_GetCSD(u8 *csd_data)
{
    u8 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卡的总扇区数(扇区数)   
//返回值:0: 取容量出错 
//       其他:SD卡的容量(扇区数/512字节)
//每扇区的字节数必为512,因为如果不是512,则初始化不能通过.														  
u32 SD_GetSectorCount(void)
{
    u8 csd[16];
    u32 Capacity;  
    u8 n;
	u16 csize;  					    
	//取CSD信息,如果期间出错,返回0
    if(SD_GetCSD(csd)!=0) return 0;	    
    //如果为SDHC卡,按照下面方式计算
    if((csd[0]&0xC0)==0x40)	 //V2.00的卡
    {	
		csize = csd[9] + ((u16)csd[8] << 8) + 1;
		Capacity = (u32)csize << 10;//得到扇区数	 		   
    }else//V1.XX的卡
    {	
		n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
		csize = (csd[8] >> 6) + ((u16)csd[7] << 2) + ((u16)(csd[6] & 3) << 10) + 1;
		Capacity= (u32)csize << (n - 9);//得到扇区数   
    }
    return Capacity;
}

//初始化SD卡
u8 SD_Init(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[i]=SD_SPI_ReadWriteByte(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卡版本开始
				{
					for(i=0;i<4;i++)buf[i]=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
			{
				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卡
//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;//
}

//写SD卡
//buf:数据缓存区
//sector:起始扇区
//cnt:扇区数
//返回值:0,ok;其他,失败.
u8 SD_WriteDisk(u8*buf,u32 sector,u8 cnt)
{
	u8 r1;
	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)
		{
			SD_SendCmd(CMD55,0,0X01);	
			SD_SendCmd(CMD23,cnt,0X01);//发送指令	
		}
 		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.h 定义一些值,以及指令

#ifndef _sd_H
#define _sd_H

#include "system.h"


#define SD_CS PGout(14)		  //SD_CS

// 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写入前预先擦除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

//数据写入回应字意义
#define MSD_DATA_OK                0x05
#define MSD_DATA_CRC_ERROR         0x0B
#define MSD_DATA_WRITE_ERROR       0x0D
#define MSD_DATA_OTHER_ERROR       0xFF
//SD卡回应标记字
#define MSD_RESPONSE_NO_ERROR      0x00
#define MSD_IN_IDLE_STATE          0x01
#define MSD_ERASE_RESET            0x02
#define MSD_ILLEGAL_COMMAND        0x04
#define MSD_COM_CRC_ERROR          0x08
#define MSD_ERASE_SEQUENCE_ERROR   0x10
#define MSD_ADDRESS_ERROR          0x20
#define MSD_PARAMETER_ERROR        0x40
#define MSD_RESPONSE_FAILURE       0xFF 							   						 					    	  

extern u8 SD_Type;//SD卡的类型
//函数申明区 
u8 SD_SPI_ReadWriteByte(u8 data);
void SD_SPI_SpeedLow(void);
void SD_SPI_SpeedHigh(void);
u8 SD_WaitReady(void);							//等待SD卡准备
u8 SD_GetResponse(u8 Response);					//获得相应
u8 SD_Init(void);							//初始化
u8 SD_ReadDisk(u8*buf,u32 sector,u8 cnt);		//读块
u8 SD_WriteDisk(u8*buf,u32 sector,u8 cnt);		//写块
u32 SD_GetSectorCount(void);   					//读扇区数
u8 SD_GetCID(u8 *cid_data);                     //读SD卡CID
u8 SD_GetCSD(u8 *csd_data);                     //读SD卡CSD
 

#endif

主函数的引用


int main()
{
	u8 i=0;
	u32 sd_size;
	u8 sd_buf[6];
	
	SysTick_Init(72);
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  //中断优先级分组 分2组
	LED_Init();
	USART1_Init(115200);
	TFTLCD_Init();			//LCD初始化
	
	FRONT_COLOR=RED;//设置字体为红色 
	LCD_ShowString(10,10,tftlcd_data.width,tftlcd_data.height,16,"STM32F1");	
	LCD_ShowString(10,30,tftlcd_data.width,tftlcd_data.height,16,"SD CARD TEST");	  
	
	
	while(SD_Init())//检测不到SD卡
	{
		LCD_ShowString(10,80,tftlcd_data.width,tftlcd_data.height,16,"SD Card Error!");
		printf("SD Card Error!\r\n");
		delay_ms(500);					
	}
	
	FRONT_COLOR=BLUE;	//设置字体为蓝色 
	//检测SD卡成功 			
	printf("SD Card OK!\r\n");	
	LCD_ShowString(10,80,tftlcd_data.width,tftlcd_data.height,16,"SD Card OK    ");
	
	/* 显示SD卡类型 */
    if(SD_Type == 0x06)
	{
		LCD_ShowString(10, 100,tftlcd_data.width,tftlcd_data.height,16,"SDV2HC OK!");
	}
	else if(SD_Type == 0x04)
	{
		LCD_ShowString(10, 100,tftlcd_data.width,tftlcd_data.height,16,"SDV2 OK!");
	}
	else if(SD_Type == 0x02)
	{
		LCD_ShowString(10, 100,tftlcd_data.width,tftlcd_data.height,16,"SDV1 OK!");
	}
	else if(SD_Type == 0x01)
	{
		LCD_ShowString(10, 100,tftlcd_data.width,tftlcd_data.height,16,"MMC OK!");
	}
	
	LCD_ShowString(10,120,tftlcd_data.width,tftlcd_data.height,16,"SD Card Size:     MB");
	
	sd_size=SD_GetSectorCount();//得到扇区数
	sd_size=sd_size>>11;  //显示SD卡容量   MB
	printf("\nSD卡容量为:%dMB\n", sd_size);
	
	sd_buf[0]=sd_size/10000+0x30;
	sd_buf[1]=sd_size%10000/1000+0x30;
	sd_buf[2]=sd_size%10000%1000/100+0x30;
	sd_buf[3]=sd_size%10000%1000%100/10+0x30;
	sd_buf[4]=sd_size%10000%1000%100%10+0x30;
	sd_buf[5]='\0';
	LCD_ShowString(115,120,tftlcd_data.width,tftlcd_data.height,16,sd_buf);

}

总结

  1. SD卡是基于flash的存储卡。
  2. SD卡和MMC卡的区别在于初始化过程不同。
  3. SD卡的通信协议包括SD和SPI两类。
  4. 掌握工作模式,通信模式
  5. 不同的板子主要看电路图其中的sd 卡是否支持sdio 大部分支持spi 根据不同的模式选择

注释 关于SD卡相关中文数据手册说明和文档,发消息会转发文档给
这里额外提一下卡槽(卡槽图片来源博主jiangfutao的)在这里插入图片描述

SD卡(Secure Digital Memory Card)是一种基于半导体闪存工艺的存储卡,中文名称“安全数码卡”,是一种基于半导体快闪记忆器的新一代记忆设备,它被广泛地于便携式装置上使用,例如数码相机、个人数码助理(外语缩写PDA)和多媒体播放器等。
而SD卡槽就是一种放置SD卡的装置,它是没有存储功能的,它只是为SD卡服务的,和读卡器起到的作用有些类似,但它们还是有很大区别的,比如说SD卡槽是固定在设备当中的,而读卡器则是可以移动的。
SD卡座属于一种弹跳式的装置,在SD卡座的卡槽底部一般都是有一个复位的装置结构。按一下可以将SD卡弹跳出来,它的其工作原理是,在SD卡座中卡槽的底部有个小直径和小线径的弹簧或者是一种切口式弹片,我们对其进行按压SD卡就嵌在卡座里面,相反在按一下就可以弹跳出来。因此SD卡座不但可以临时锁死结构,还可以临时弹出等,其实主要的就是靠这种复位的装置进行操作。
除此之外,还有SD储存卡座的工作原理,在卡槽有相对应的凸起开口,将其SD卡插入到SD卡座的卡槽内,然后我们可以通过SD卡座以命令形式的传输来控制SD卡的读写操作,并且能够根据命令对多块SD卡或单块SD卡进行读写操作。

相信大家都知道SD卡座在日常生活与工作中使用非常广泛,目前已经成为了最通用的数据存储卡、储存记忆卡。比如通讯数码产品、安防产品、带储存类产品等设备上都是使用SD卡来进行数据储存。如今的SD卡之所以能德到大家如此广泛的使用,是因为SD卡的优点诸多,比如性价比高、存储容量大、使用便捷、通用性以及安全性强等。如果将它加入到单片机应用开发系统中来,将使系统变得更加出色。这就需要对SD卡的硬件与读写时序进行研究。

上一节 学习STM32 RS485 原理与应用

  • 5
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

闰土小蒋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值