分享

SPI模式下对SD卡的操作

 kongzhou 2012-04-11


STM32的SPI设备简介:
STM32F107VC有3个SPI设备,SPI控制器在输出数据的同时采样输入数据,使用相同时钟线。

Master设备写操作的同时,读入寄存器同时采样填充,每次也需要清空寄存器。
Master设备的读操作,实际上是通过写数据输出时钟序列,采样MISO的信号。

SD卡简介:
SD卡的技术规范经过几次升级,与最初版本已有很大不同,本文基于Ver 3.01讨论
从容量上分
 容量命名 简称 
 0G<容量<2GStandard Capacity SD Memory Card  SDSC或SD
 2G<容量<32GHigh Capacity SD Memory Card SDHC
 32G<容量<2TExtended Capacity SD Memory Card SDXC


SD卡支持SPI的Mode0和Mode3
SD卡支持50MHz总线,STM32的APB2总线最高72MHz,SPI分频?为36Mhz,理论上所有SD卡都可以正常操作,实际上一些低版本的卡缺乏稳定性

插入信号CD:
CD线是可选的信号线,没有卡时为高电位,有卡插入时CD为低电位

CD Waiting

while ((*gpiob_idr)&0x4000) {}

电位稳定延迟:
CS线为高的状态下输出若干时钟,延迟利于电位稳定

Init Stable

sd_cs(1);
//sending dummy data few times
for (ii=0; ii<0xF; ii++) {
sd_bytedata(0xFF);
}

SD卡的准备状态,初始化操作:
SD卡从上电到可读写状态需要一定序列命令的操作,这个过程包括选择SPI模式和判断卡的版本以及供电操作
SD规范中的流程图
CMD0
SD卡上电后使用CMD0进入SPI模式,CMD0的返回值是1字节的R1,R1应该为空闲0x01

CMD0

sd_cs(0);
sd_command(cmd, cc);
res=sd_response();
sd_cs(1);

while ((rtn!=0x01)

CMD8
版本2.0以上的SD卡支持CMD8命令,包括大部分SDHC的卡和所有SDXC卡,早期的SDHC卡有可能仍属于V1.0卡
命令返回值R7,第一字节为R1,Ver1.0的卡对R1的“非法命令”位2置位,Ver2.0以上卡应返回0x01

CMD8

rtn = sd_cmd8_version_check(&sdif);
if ((rtn&0x04)==0x04) {
rtn=sd_v1_process(&sdif);
} else {
rtn=sd_v2_process(&sdif);
}

ACMD41和CMD1
ACMD41是为卡供电命令,供电前卡的状态为空闲(idle),R1的返回值为0x01,供电后为动作状态(ready)
一些早期的卡认为ACMD41是非法命令,只能用MMC的CMD1命令供电

ACMD41 Ready

if (rtn == 0x01) {
while (rtn==0x01) {
rtn = sd_send_cond_acmd41_v1();
}
}

if ((rtn&0x04)==0x04) {// invalid command
rtn = 0x01;
while (rtn==0x01) {
rtn = sd_cmd1_mmc_ready();
}
}

CMD58
CMD58读取卡的状态,一个重要的标志位CCS,会影响到读写操作中地址数据的设定
CCS为1时为高版本卡,数据地址为页单位,512字节为一页
CCS为0时,地址为以字节为单位实际地址
CCS置位与否也取决于ACMD41中对HCS:30bit的置位请求

CMD58 CCS

rtn = sd_read_ocr_cmd58(sd_inf->rx.r3);
sd_inf->ccs = (sd_inf->rx.r3[0]>>6)&0x01;


CMD9
取得卡容量等信息,CSO寄存器,CSO是16个字节的结构体,加上2字节的CRC,应读取18字节的内容

Read CSO

sd_cs(0);
sd_command(cmd, cc);
rtn = sd_response();
if (rtn == 0x00) {
if(sd_data_token()==0xFE) {
sd_data(cso, 20);
} else {
rtn = 0xFF;
}
}
sd_cs(1);

卡的信息也有版本区别,需要分别处理

CSO Version

sd_inf->csd_version = (cso[0]>>6)&0x3;
if (sd_inf->csd_version==0x0) {
sd_inf->block = cso[5]&0xF;
sd_inf->unit = cso[6]&0x3;
sd_inf->unit <<= 8;
sd_inf->unit |= cso[7];
sd_inf->unit <<= 2;
sd_inf->unit |= (cso[8]&0xC0)>>6;
sd_inf->multi = (cso[9]&0x3);
sd_inf->multi <<= 1;
sd_inf->multi |= (cso[10]&0x80)>>7;
sd_inf->page = sd_inf->unit + 1;
sd_inf->page <<= (sd_inf->multi + 2);
sd_inf->size = sd_inf->page << sd_inf->block;
} else {
sd_inf->block = cso[5]&0xF;
sd_inf->unit = cso[7]&0x3F;
sd_inf->unit <<= 8;
sd_inf->unit |= cso[8];
sd_inf->unit <<= 8;
sd_inf->unit |= cso[9];
sd_inf->page = sd_inf->unit * 1024;
sd_inf->size = sd_inf->page << sd_inf->block;
}

CMD24 CMD17
CMD24写数据
CMD17读数据
SD的读写都要以页单位进行,无论是否是新版本。
老版本的地址设定也要从页的起始处,一般为512的倍数
数据块紧接在命令块的后面。
数据块长度515个字节,起始标志字节为0xFE,实际数据512字节,2字节CRC
CRC使用CRC-16算法,不计算时可以设置为0xFF
写操作之后要等待非忙信号,忙信号使MISO为低电位,接收数据一直为0



CMD17 Read Block

sd_cs(0);
sd_command(cmd, cc);
rtn = sd_response();
if (rtn==0x00) {
rtn = sd_data_token();
if(rtn==0xFE) {
for (cc=0; cc<512; cc++) {
data[cc] = 0xAA;
data[cc] = sd_bytedata(0xFF);
}
sd_bytedata(0xFF);
sd_bytedata(0xFF);
}
rtn=0;
}
sd_cs(1);

CMD24 Write Block

sd_cs(0);
sd_command(cmd, cc);
rtn = sd_response();
if (rtn==0x00) {
sd_bytedata(0xFF);
sd_bytedata(0xFE);
for (cc=0; cc<512; cc++) {
sd_bytedata(data[cc]);
}
sd_bytedata(0xFF);
sd_bytedata(0xFF);
rtn = sd_response();
cc = 0;
while (rtn == 0x00) { //R1b busy
rtn = sd_bytedata(0xFF);
cc++;
}

}
sd_cs(1);

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多