一、SD卡
SD卡为移动设备提供了安全的,大容量存储解决方法。它本身可以通过两种总线模式和MCU进行数据传输,一种是称为SD BUS的4位串行数据模式,另一种就是大家熟知的4线SPI Bus模式。一些廉价,低端的MCU,通过硬件(或软件)SPI就能和SD卡进行通信,实现大容量存储的要求,这也是SD卡的魅力所在。
1.SD卡与TF卡区别:
(1)卡体尺寸不同:TF卡偏小一点,SD卡大得多(TF卡称作:Micro SD card);
TF卡:
SD卡:
(2)管脚定义不同:SD卡管脚比TF卡多的多;
TF卡:
SD卡:
(3)TF卡插入卡套可以作为SD卡使用;反之不行;
2.容量分配:
SDSC卡 < 2GB;
2GB < SDHC卡 < 32GB;
32GB < SDXC < 2TB;
其中SDSC卡和SDHC卡协议兼容,而与SDXC卡有很大的差异。本文中主要讨论前者。
3.操作模式:
SD卡通讯模式分为SD卡模式(使用SDIO接口)和SPI模式(使用SPI接口):
本文主要介绍SPI模式;
二、SD卡初始化
SD 卡在正常读写操作之前,必须先对 SD 卡进行初始化,SD 卡的初始化过程就是向 SD 中写入命令。在对 SD 卡进行读写操作时同样需要先发送写命令和读命令,SD 卡的命令格式由 6 个字节组成(先发送高位再发送低位)。
写入命令:
byte1:命令号,格式为01xx_xxx;
byte2~byte5:命令参数;
bute6:前 7 位为 CRC(循环冗余校验)校验位,最后一位为停止位 0. 在 SPI 模式下默认不开启CRC 校验(校验位全部设为1),在 SDIO 模式下开启 CRC 校验.SD 卡上电默认是 SDIO模式,在接收 SD 卡返回 CMD0 的响应命令时,拉低片选 CS,进入 SPI 模式。
SD 卡的命令号(byte1)分为标准命令(如 CMD0)和应用相关命令,在发送应用相关命令之前,必须先发送 CMD55 命令。
1.初始化流程:
1. 初始化与 SD卡连接的硬件条件。
(MCU的 SPI配置,IO口配置等等)
2. 向总线最少发送74个脉冲,为了让SD卡正常启动。 (唤醒SD卡)
(时钟线至少需要74个跳变,向MOSI发送0xFF数据即可,这是无效数据)
3. 复位卡(CMD0),进入 IDLE(闲置)状态。
(最后的返回值等于0x01就表示复位成功)
4. 发送 CMD8,检查是否支持 2.0协议,因为这个命令是在2.0的协议里面才添加的。
(发送 CMD8命令之后,返回值等于0x01表示就是2.0版本的SD卡)
5. 如果是2.0版本的SD卡,就需要循环发送CMD55+ CMD41命令等待2.0卡初始化成功,如果CMD41命令的返回值等于0就表示卡复位成功。
(先发CMD55,再发CMD41)
6. 2.0卡初始化成功后,再发送CMD58命令,继续判断是否是高速卡。
(CMD58命令返回值等于0,表示执行成功。如果只是为了判断是否是高速卡,可以只读取1个字节数据即可,因为SD返回的数据先返回的是高位数据(24~31),后面的数据可以不读取。)
7. 取消片选,结束初始化。
(取消片选之后,需要再额外发送8个时钟信号,结束本次操作。)
2.发送数据包流程:
1. 等待SD卡忙状态。
(向SD卡发送一个0xFF数据,如果SD卡也原路返回0xFF就表示SD卡处于闲置状态。)
2. 发送(开始传输)/(结束传输)的标志。
(写一个扇区的情况下发送0xFE开始传输数据;
写多个扇区的情况下发送0xFC开始传输数据;
写多个扇区的情况下,连续发送数据完成之后,发送0xFD结束数据发送;)
3. 如果不是结束标志(0xFD),就是表示发送的是正常的数据包,就进行循环发送512字节的数据。
(每次发送数据包的单位是按扇区为单位的,也就是512字节,一包数据长度固定为512字节。)
4. 数据发送完之后,再接着发送0xFF忽略CRC校验(连续发送3个0xFF)。
3.从SD卡读取数据包流程:
1、等待SD卡发回数据起始令牌0xFE
向SD卡发送0xFF,如果SD卡返回0xFE就表示等待成功。
2、收到返回的数据起始令牌之后就可以连续读取数据了(接收的数量以传入的cnt为准),读完数据发送两个伪CRC
4.向SD卡指定扇区写数据:
写一个扇区步骤:
1、发送CMD24命令,设置要写的扇区。(写单个扇区使用CMD24命令)
2、接着向SD卡写数据包(参考5.3小节)。
写多个扇区的步骤:
1、发送CMD55命令(正常应返回0x01)
2、 发送CMD23命令(设置多扇区写入前预先擦除N个block)---写入的数量
3、 发送CMD25命令,设置写入的扇区位置。(设置写多个扇区)
4、 接着向SD卡写数据包(参考5.3小节)。
5、写结束指令0xFD,完成写入。
6、 取消片选。
5.向SD卡指定扇区读数据:
读取一个扇区的步骤:
1、 发送CM17命令,设置读取的扇区
2、 接着进行接收SD卡返回的数据包。(参考5.4小节)
每次固定接收512字节,以扇区为单位。
3、 取消片选,完成数据读取
说明: 取消片选之后,需要再额外发送8个时钟信号,结束本次操作。
读取多个扇区的步骤:
1、 发送CMD18命令,设置读取的扇区(连续读多个扇区使用)
2、 接着循环接收SD卡返回的数据包。(参考5.4小节)
每次固定接收512字节,以扇区为单位。
3、 发送CMD12指令,停止数据传输
4、 取消片选,完成数据读取
说明: 取消片选之后,需要再额外发送8个时钟信号,结束本次操作。
6.获取SD卡的总扇区数
1、 发送CMD9命令,读取CSD信息
2、 连续接收16个字节数据包
3、 取消片选,完成读取
4、 判断是否是v2.0 SDHC高速卡。
(使用读取的第一个字节数据csd[0]&0xC0判断是否等于0x40,如果等于0x40就是v2.0高速卡。)
5、 如果是v2.0 SDHC高速卡就按照以下公式计算得到扇区数量
三、工程源码
SPI.c
#include "SPI.h"
/*外设驱动*/
//VCC:5V
//GND:GND
//MISO:PA6
//MOSI:PA7
//SCK:PA5
//CS:PA8
/*.c*/
/*以下是SPI1口初始化模块的初始化代码,访问SD Card这里针是对SPI1的初始化*/
void SPI1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA|RCC_APB2Periph_SPI1, ENABLE );//PORTA与SPI1时钟使能
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);//初始化GPIOA
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// CS_IN=1;
//
// GPIO_SetBits(GPIOA,GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7);
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);//启动传输
}
//SPI 速度设置函数
//SpeedSet:
//SPI_BaudRatePrescaler_2 2分频
//SPI_BaudRatePrescaler_8 8分频
//SPI_BaudRatePrescaler_16 16分频
//SPI_BaudRatePrescaler_256 256分频
void SPI1_SetSpeed(u8 SPI_BaudRatePrescaler)
{
assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));
SPI1->CR1&=0XFFC7;
SPI1->CR1|=SPI_BaudRatePrescaler; //设置SPI1速度
SPI_Cmd(SPI1,ENABLE);
}
//SPIx 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
u8 SPI1_ReadWriteByte(u8 TxData)
{
u8 retry=0;
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位
{
retry++;
if(retry>200)return 0;
}
SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个数据
retry=0;
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET) //检查指定的SPI标志位设置与否:接受缓存非空标志位
{
retry++;
if(retry>200)return 0;
}
return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据
}
SPI.h
/*.h*/
#ifndef __SPI_H
#define __SPI_H
#include "sys.h"
#define CS_IN PAout(4)
void SPI1_Init(void); //初始化SPI口
void SPI1_SetSpeed(u8 SpeedSet); //设置SPI速度
u8 SPI1_ReadWriteByte(u8 TxData);//SPI总线读写一个字节
#endif
SD.c
#include "stm32f10x.h" // 或者根据你的 MCU 系列包含相应的头文件
#include "SD.h"
#include "SPI.h"
#include "usart.h"
#include "delay.h"
/*SD卡驱动*/
uint8_t DFF=0xFF;
uint8_t test;
uint8_t SD_TYPE=0x00;
MSD_CARDINFO SD0_CardInfo;
//
//片选
//
void SD_CS(uint8_t p)
{
if (p == 0)
{
GPIO_SetBits(GPIOA, GPIO_Pin_4); // 设置 GPIO 引脚高电平
}
else {
GPIO_ResetBits(GPIOA, GPIO_Pin_4); // 设置 GPIO 引脚低电平
}
}
///
//发送命令,发完释放
//
int SD_sendcmd(uint8_t cmd,uint32_t arg,uint8_t crc){
uint8_t r1;
uint8_t retry;
SD_CS(0);;
delay_ms(20);
SD_CS(1);;
do{
retry=SPI1_ReadWriteByte(DFF);
}while(retry!=0xFF);
SPI1_ReadWriteByte(cmd | 0x40);
SPI1_ReadWriteByte(arg >> 24);
SPI1_ReadWriteByte(arg >> 16);
SPI1_ReadWriteByte(arg >> 8);
SPI1_ReadWriteByte(arg);
SPI1_ReadWriteByte(crc);
if(cmd==CMD12)SPI1_ReadWriteByte(DFF);
do
{
r1=SPI1_ReadWriteByte(0xFF);
//printf("r1 = %d\r\n",r1);
}while(r1&0X80);
return r1;
}
/
//SD卡初始化
uint8_t SD_Init(void)
{
uint8_t r1;
uint8_t buff[6] = {0};
uint16_t retry;
uint8_t i;
SPI1_Init();
SPI1_SetSpeed(SPI_BaudRatePrescaler_256);
SD_CS(0);;
for(retry=0;retry<10;retry++){
SPI1_ReadWriteByte(DFF);
}
//SD卡进入IDLE状态
do{
r1 = SD_sendcmd(CMD0 ,0, 0x95);
}while(r1!=0x01);
//查看SD卡的类型
SD_TYPE=0;
r1 = SD_sendcmd(CMD8, 0x1AA, 0x87);
if(r1==0x01){
for(i=0;i<4;i++)buff[i]=SPI1_ReadWriteByte(DFF); //Get trailing return value of R7 resp
if(buff[2]==0X01&&buff[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++)buff[i]=SPI1_ReadWriteByte(0XFF);//得到OCR值
if(buff[0]&0x40){
SD_TYPE=V2HC;
}else {
SD_TYPE=V2;
}
}
}else{
SD_sendcmd(CMD55,0,0X01); //发送CMD55
r1=SD_sendcmd(CMD41,0,0X01); //发送CMD41
if(r1<=1)
{
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=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=ERR;//错误的卡
}
}
SD_CS(0);
SPI1_SetSpeed(SPI_BaudRatePrescaler_256);
if(SD_TYPE)return 0;
else return 1;
}
//读取指定长度数据
uint8_t SD_ReceiveData(uint8_t *data, uint16_t len)
{
uint8_t r1;
SD_CS(1);
do
{
r1 = SPI1_ReadWriteByte(0xFF);
delay_ms(100);
}while(r1 != 0xFE);
while(len--)
{
*data = SPI1_ReadWriteByte(0xFF);
data++;
}
SPI1_ReadWriteByte(0xFF);
SPI1_ReadWriteByte(0xFF);
return 0;
}
//向sd卡写入一个数据包的内容 512字节
uint8_t SD_SendBlock(uint8_t*buf,uint8_t cmd)
{
uint16_t t;
uint8_t r1;
do{
r1=SPI1_ReadWriteByte(0xFF);
}while(r1!=0xFF);
SPI1_ReadWriteByte(cmd);
if(cmd!=0XFD)//不是结束指令
{
for(t=0;t<512;t++)SPI1_ReadWriteByte(buf[t]);//提高速度,减少函数传参时间
SPI1_ReadWriteByte(0xFF);//忽略crc
SPI1_ReadWriteByte(0xFF);
t=SPI1_ReadWriteByte(0xFF);//接收响应
if((t&0x1F)!=0x05)return 2;//响应错误
}
return 0;//写入成功
}
//获取CID信息
uint8_t SD_GETCID (uint8_t *cid_data)
{
uint8_t r1;
r1=SD_sendcmd(CMD10,0,0x01); //读取CID寄存器
if(r1==0x00){
r1=SD_ReceiveData(cid_data,16);
}
SD_CS(0);
if(r1)return 1;
else return 0;
}
//获取CSD信息
uint8_t SD_GETCSD(uint8_t *csd_data){
uint8_t r1;
r1=SD_sendcmd(CMD9,0,0x01);//发CMD9命令,读CSD寄存器
if(r1==0)
{
r1=SD_ReceiveData(csd_data, 16);//接收16个字节的数据
}
SD_CS(0);//取消片选
if(r1)return 1;
else return 0;
}
//获取SD卡的总扇区数
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;
}
int MSD0_GetCardInfo(PMSD_CARDINFO SD0_CardInfo)
{
uint8_t r1;
uint8_t CSD_Tab[16];
uint8_t CID_Tab[16];
/* Send CMD9, Read CSD */
r1 = SD_sendcmd(CMD9, 0, 0xFF);
if(r1 != 0x00)
{
return r1;
}
if(SD_ReceiveData(CSD_Tab, 16))
{
return 1;
}
/* Send CMD10, Read CID */
r1 = SD_sendcmd(CMD10, 0, 0xFF);
if(r1 != 0x00)
{
return r1;
}
if(SD_ReceiveData(CID_Tab, 16))
{
return 2;
}
/* Byte 0 */
SD0_CardInfo->CSD.CSDStruct = (CSD_Tab[0] & 0xC0) >> 6;
SD0_CardInfo->CSD.SysSpecVersion = (CSD_Tab[0] & 0x3C) >> 2;
SD0_CardInfo->CSD.Reserved1 = CSD_Tab[0] & 0x03;
/* Byte 1 */
SD0_CardInfo->CSD.TAAC = CSD_Tab[1] ;
/* Byte 2 */
SD0_CardInfo->CSD.NSAC = CSD_Tab[2];
/* Byte 3 */
SD0_CardInfo->CSD.MaxBusClkFrec = CSD_Tab[3];
/* Byte 4 */
SD0_CardInfo->CSD.CardComdClasses = CSD_Tab[4] << 4;
/* Byte 5 */
SD0_CardInfo->CSD.CardComdClasses |= (CSD_Tab[5] & 0xF0) >> 4;
SD0_CardInfo->CSD.RdBlockLen = CSD_Tab[5] & 0x0F;
/* Byte 6 */
SD0_CardInfo->CSD.PartBlockRead = (CSD_Tab[6] & 0x80) >> 7;
SD0_CardInfo->CSD.WrBlockMisalign = (CSD_Tab[6] & 0x40) >> 6;
SD0_CardInfo->CSD.RdBlockMisalign = (CSD_Tab[6] & 0x20) >> 5;
SD0_CardInfo->CSD.DSRImpl = (CSD_Tab[6] & 0x10) >> 4;
SD0_CardInfo->CSD.Reserved2 = 0; /* Reserved */
SD0_CardInfo->CSD.DeviceSize = (CSD_Tab[6] & 0x03) << 10;
/* Byte 7 */
SD0_CardInfo->CSD.DeviceSize |= (CSD_Tab[7]) << 2;
/* Byte 8 */
SD0_CardInfo->CSD.DeviceSize |= (CSD_Tab[8] & 0xC0) >> 6;
SD0_CardInfo->CSD.MaxRdCurrentVDDMin = (CSD_Tab[8] & 0x38) >> 3;
SD0_CardInfo->CSD.MaxRdCurrentVDDMax = (CSD_Tab[8] & 0x07);
/* Byte 9 */
SD0_CardInfo->CSD.MaxWrCurrentVDDMin = (CSD_Tab[9] & 0xE0) >> 5;
SD0_CardInfo->CSD.MaxWrCurrentVDDMax = (CSD_Tab[9] & 0x1C) >> 2;
SD0_CardInfo->CSD.DeviceSizeMul = (CSD_Tab[9] & 0x03) << 1;
/* Byte 10 */
SD0_CardInfo->CSD.DeviceSizeMul |= (CSD_Tab[10] & 0x80) >> 7;
SD0_CardInfo->CSD.EraseGrSize = (CSD_Tab[10] & 0x7C) >> 2;
SD0_CardInfo->CSD.EraseGrMul = (CSD_Tab[10] & 0x03) << 3;
/* Byte 11 */
SD0_CardInfo->CSD.EraseGrMul |= (CSD_Tab[11] & 0xE0) >> 5;
SD0_CardInfo->CSD.WrProtectGrSize = (CSD_Tab[11] & 0x1F);
/* Byte 12 */
SD0_CardInfo->CSD.WrProtectGrEnable = (CSD_Tab[12] & 0x80) >> 7;
SD0_CardInfo->CSD.ManDeflECC = (CSD_Tab[12] & 0x60) >> 5;
SD0_CardInfo->CSD.WrSpeedFact = (CSD_Tab[12] & 0x1C) >> 2;
SD0_CardInfo->CSD.MaxWrBlockLen = (CSD_Tab[12] & 0x03) << 2;
/* Byte 13 */
SD0_CardInfo->CSD.MaxWrBlockLen |= (CSD_Tab[13] & 0xc0) >> 6;
SD0_CardInfo->CSD.WriteBlockPaPartial = (CSD_Tab[13] & 0x20) >> 5;
SD0_CardInfo->CSD.Reserved3 = 0;
SD0_CardInfo->CSD.ContentProtectAppli = (CSD_Tab[13] & 0x01);
/* Byte 14 */
SD0_CardInfo->CSD.FileFormatGrouop = (CSD_Tab[14] & 0x80) >> 7;
SD0_CardInfo->CSD.CopyFlag = (CSD_Tab[14] & 0x40) >> 6;
SD0_CardInfo->CSD.PermWrProtect = (CSD_Tab[14] & 0x20) >> 5;
SD0_CardInfo->CSD.TempWrProtect = (CSD_Tab[14] & 0x10) >> 4;
SD0_CardInfo->CSD.FileFormat = (CSD_Tab[14] & 0x0C) >> 2;
SD0_CardInfo->CSD.ECC = (CSD_Tab[14] & 0x03);
/* Byte 15 */
SD0_CardInfo->CSD.CSD_CRC = (CSD_Tab[15] & 0xFE) >> 1;
SD0_CardInfo->CSD.Reserved4 = 1;
if(SD0_CardInfo->CardType == V2HC)
{
/* Byte 7 */
SD0_CardInfo->CSD.DeviceSize = (uint16_t)(CSD_Tab[8]) *256;
/* Byte 8 */
SD0_CardInfo->CSD.DeviceSize += CSD_Tab[9] ;
}
SD0_CardInfo->Capacity = SD0_CardInfo->CSD.DeviceSize * MSD_BLOCKSIZE * 1024;
SD0_CardInfo->BlockSize = MSD_BLOCKSIZE;
/* Byte 0 */
SD0_CardInfo->CID.ManufacturerID = CID_Tab[0];
/* Byte 1 */
SD0_CardInfo->CID.OEM_AppliID = CID_Tab[1] << 8;
/* Byte 2 */
SD0_CardInfo->CID.OEM_AppliID |= CID_Tab[2];
/* Byte 3 */
SD0_CardInfo->CID.ProdName1 = CID_Tab[3] << 24;
/* Byte 4 */
SD0_CardInfo->CID.ProdName1 |= CID_Tab[4] << 16;
/* Byte 5 */
SD0_CardInfo->CID.ProdName1 |= CID_Tab[5] << 8;
/* Byte 6 */
SD0_CardInfo->CID.ProdName1 |= CID_Tab[6];
/* Byte 7 */
SD0_CardInfo->CID.ProdName2 = CID_Tab[7];
/* Byte 8 */
SD0_CardInfo->CID.ProdRev = CID_Tab[8];
/* Byte 9 */
SD0_CardInfo->CID.ProdSN = CID_Tab[9] << 24;
/* Byte 10 */
SD0_CardInfo->CID.ProdSN |= CID_Tab[10] << 16;
/* Byte 11 */
SD0_CardInfo->CID.ProdSN |= CID_Tab[11] << 8;
/* Byte 12 */
SD0_CardInfo->CID.ProdSN |= CID_Tab[12];
/* Byte 13 */
SD0_CardInfo->CID.Reserved1 |= (CID_Tab[13] & 0xF0) >> 4;
/* Byte 14 */
SD0_CardInfo->CID.ManufactDate = (CID_Tab[13] & 0x0F) << 8;
/* Byte 15 */
SD0_CardInfo->CID.ManufactDate |= CID_Tab[14];
/* Byte 16 */
SD0_CardInfo->CID.CID_CRC = (CID_Tab[15] & 0xFE) >> 1;
SD0_CardInfo->CID.Reserved2 = 1;
return 0;
}
//写SD卡
//buf:数据缓存区
//sector:起始扇区
//cnt:扇区数
//返回值:0,ok;其他,失败.
uint8_t SD_WriteDisk(uint8_t*buf,uint32_t sector,uint8_t cnt)
{
uint8_t r1;
if(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!=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_CS(0);;//取消片选
return r1;//
}
//读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!=V2HC)sector <<= 9;//转换为字节地址
if(cnt==1)
{
r1=SD_sendcmd(CMD17,sector,0X01);//读命令
if(r1==0)//指令发送成功
{
r1=SD_ReceiveData(buf,512);//接收512个字节
}
}else
{
r1=SD_sendcmd(CMD18,sector,0X01);//连续读命令
do
{
r1=SD_ReceiveData(buf,512);//接收512个字节
buf+=512;
}while(--cnt && r1==0);
SD_sendcmd(CMD12,0,0X01); //发送停止命令
}
SD_CS(0);;//取消片选
return r1;//
}
SD.h
#ifndef __SD_H
#define __SD_H
#include "sys.h"
extern uint8_t SD_TYPE;
//SD卡类型
#define ERR 0x00
#define MMC 0x01
#define V1 0x02
#define V2 0x04
#define V2HC 0x06
#define DUMMY_BYTE 0xFF
#define MSD_BLOCKSIZE 512
//CMD定义
#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
enum _CD_HOLD
{
HOLD = 0,
RELEASE = 1,
};
typedef struct /* Card Specific Data */
{
uint8_t CSDStruct; /* CSD structure */
uint8_t SysSpecVersion; /* System specification version */
uint8_t Reserved1; /* Reserved */
uint8_t TAAC; /* Data read access-time 1 */
uint8_t NSAC; /* Data read access-time 2 in CLK cycles */
uint8_t MaxBusClkFrec; /* Max. bus clock frequency */
uint16_t CardComdClasses; /* Card command classes */
uint8_t RdBlockLen; /* Max. read data block length */
uint8_t PartBlockRead; /* Partial blocks for read allowed */
uint8_t WrBlockMisalign; /* Write block misalignment */
uint8_t RdBlockMisalign; /* Read block misalignment */
uint8_t DSRImpl; /* DSR implemented */
uint8_t Reserved2; /* Reserved */
uint32_t DeviceSize; /* Device Size */
uint8_t MaxRdCurrentVDDMin; /* Max. read current @ VDD min */
uint8_t MaxRdCurrentVDDMax; /* Max. read current @ VDD max */
uint8_t MaxWrCurrentVDDMin; /* Max. write current @ VDD min */
uint8_t MaxWrCurrentVDDMax; /* Max. write current @ VDD max */
uint8_t DeviceSizeMul; /* Device size multiplier */
uint8_t EraseGrSize; /* Erase group size */
uint8_t EraseGrMul; /* Erase group size multiplier */
uint8_t WrProtectGrSize; /* Write protect group size */
uint8_t WrProtectGrEnable; /* Write protect group enable */
uint8_t ManDeflECC; /* Manufacturer default ECC */
uint8_t WrSpeedFact; /* Write speed factor */
uint8_t MaxWrBlockLen; /* Max. write data block length */
uint8_t WriteBlockPaPartial; /* Partial blocks for write allowed */
uint8_t Reserved3; /* Reserded */
uint8_t ContentProtectAppli; /* Content protection application */
uint8_t FileFormatGrouop; /* File format group */
uint8_t CopyFlag; /* Copy flag (OTP) */
uint8_t PermWrProtect; /* Permanent write protection */
uint8_t TempWrProtect; /* Temporary write protection */
uint8_t FileFormat; /* File Format */
uint8_t ECC; /* ECC code */
uint8_t CSD_CRC; /* CSD CRC */
uint8_t Reserved4; /* always 1*/
}
MSD_CSD;
typedef struct /*Card Identification Data*/
{
uint8_t ManufacturerID; /* ManufacturerID */
uint16_t OEM_AppliID; /* OEM/Application ID */
uint32_t ProdName1; /* Product Name part1 */
uint8_t ProdName2; /* Product Name part2*/
uint8_t ProdRev; /* Product Revision */
uint32_t ProdSN; /* Product Serial Number */
uint8_t Reserved1; /* Reserved1 */
uint16_t ManufactDate; /* Manufacturing Date */
uint8_t CID_CRC; /* CID CRC */
uint8_t Reserved2; /* always 1 */
}
MSD_CID;
typedef struct
{
MSD_CSD CSD;
MSD_CID CID;
uint32_t Capacity; /* Card Capacity */
uint32_t BlockSize; /* Card Block Size */
uint16_t RCA;
uint8_t CardType;
uint32_t SpaceTotal; /* Total space size in file system */
uint32_t SpaceFree; /* Free space size in file system */
}
MSD_CARDINFO, *PMSD_CARDINFO;
extern MSD_CARDINFO SD0_CardInfo;
uint8_t SD_Init(void);
void SD_CS(uint8_t p);
uint32_t SD_GetSectorCount(void);
uint8_t SD_GETCID (uint8_t *cid_data);
uint8_t SD_GETCSD(uint8_t *csd_data);
int MSD0_GetCardInfo(PMSD_CARDINFO SD0_CardInfo);
uint8_t SD_ReceiveData(uint8_t *data, uint16_t len);
uint8_t SD_SendBlock(uint8_t*buf,uint8_t cmd);
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