一、SDIO接口协议定义说明
1.1 介绍
SDIO 是基于SD标准开发的一种外设接口,其接口定义主要有时钟线、命令/响应线和数据线,数据线分为1位、4位和8位,半双工通信方式,可以与MMC(多媒体卡,Mutiny Media Card )卡、SD存储卡和SDIO卡通信。
与SD卡和SD IO卡的通信速率(卡时钟)可以设置在0-25MHZ,与MMC卡(V4.0以上)的通信速率可以设置在0-48MHZ,(V3.3以下的设置在0-20Mhz)
- SDIO_CK:卡时钟是用来和外部卡进行数据交互的时钟,SDIO_CK = SDIOCLK/(2+CLKDIV)
- CLKDIV:分频系数
- SDIOCLK:数据通道到以及控制单元的时钟,用来产生卡时钟
- HCLK/2:寄存器和FIFO的时钟。
1.2 通信过程
通过命令线去发送指令,同样通过命令线接收响应,这种类似于IIC通过并转串然后将数据1bit1bit发出来,先传输高位MSB,再传输低位LSB
如果是多字节传输,先传输低字节,后传输高字节,低字节在低数据总线,高字节在高数据总线。
如果是宽字节传输,同样是高位先传输,低位后传输,
如下图所示,可以看到CMD线通过移位寄存器将串行数据接收回来,同样之后会进行CRC校验(硬件),再然后会存入到寄存器中。
命令传输过程:中间会有等待状态,发送命令时是有控制器驱动,响应的时候是由卡来驱动。
数据线说明:寄存器可以配置数据总线宽度,如果位宽没有限制,只是在SDIO_D0上面有传输,其中FIFO缓存空间是128个字节(32个字,每个字为4字节),
SDIO的有响应操作和无响应操作(比如广播)
如果是读操作和写操作,则需要有数据线去参与。在进行写操作的时候需要注意判断卡处于繁忙状态,不会被写入数据,最后需要发送停止数据传输 指令。
此外也可以进行连续读和连续写操作,即流操作,数据末尾没有CRC校验码,只适用于多媒体卡
1.3 通信协议
(命令格式:发送,传输位为1)
命令格式:接收(长响应和短响应,传输位为0)
具体的命令举例说明:见指令表及程序代码。
二、SD卡/SD IO卡/MMC卡
2.1 SD卡
SD卡类型
- 标准卡,内存在2GB(2^31 bytes)以内,
- 大容量卡(2GB<X<32GB),由SD卡2.0协议定义,大容量卡有两种类型,
一种是 单状态卡:一块大容量区域
一种是 双状态卡:两块区域,一块是大容量卡区域,一块是标准容量卡区域。同一时间只能访问一种区域,可以通过一种机制来选择对应的容量卡区域
SD卡信息寄存器定义:
- CID:128位,卡识别号,无法修改
- RCA:16位,Relative card address,卡相对地址,无法修改,
- DSR:16位,驱动阶段寄存器,可修改
- CSD:128位,卡特定数据,关于卡的操作情况说明,不可修改
- SCR:64位,SD配置寄存器,SD卡的容量,不可修改
- OCR:32位,运行条件寄存器,不可修改
- SSR:512位,SD状态,SD卡的专用功能说明,不可修改
- CSR:32位,卡状态,关于卡状态的说明,不可修改
两种模式:卡识别模式和数据传输模式。
-
卡识别模式
①卡复位:CMD0可以进行软复位,使得所有卡进入空闲状态,处于不激活的卡会忽略这条指令
②操作条件验证:首先会进行电压验证,通过CMD8指令,如果可以当前电压可以与卡进行通信,则存在响应,否则无响应,ACMD41指令用来识别和否决不适应由主机提供的电压的卡片。ACMD41是应用特定指令,因此在发送其之前需要发送CMD55指令。先发送CMD8,然后再发送ACMD41。
卡识别流程图
首先进行CMD0.软复位所以状态卡,之后进行CMD8,检验电压范围是否有效,在发送ACMD41之前,需要发送CMD55,指示下一条指令是应用指令,不是通用指令。
ACMD41,识别到可以正常进行通信的卡,可以OCR里设置HCS(Host Capacity Support)即置1,表明主机(Host)支持大容量SD卡,响应里面会带有CCS位,表明卡状态,1代表是大容量卡。如果CMD8指令无响应,则说明不支持大容量SD卡,ACMD41中OCR busy位置1表明初始化完成,置0表明还处于初始化中。
主机初始化完成后,执行CMD2指令,获取CID码(unique card identification),通过CMD指令线返回,不是数据线。
之后再发送CMD3,询问RCA(Relative card address) 相对卡地址,在传输模式中使用,之后进入旁路状态。
以STM32F1代码为例,来讲解 SD卡的初始化及读写流程。
代码实现:初始化时钟
/*初始化时的时钟不能大于400KHz*/
SDIO_InitStructure.SDIO_ClockDiv = SDIO_INIT_CLK_DIV; /* HCLK = 72MHz, SDIOCLK = 72MHz, SDIO_CK = HCLK/(178 + 2) = 400 KHz */
SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising;
SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable; //不使用bypass模式,直接用HCLK进行分频得到SDIO_CK
SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable; // 空闲时不关闭时钟电源
SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_1b; //1位数据线
SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable;//硬件流
SDIO_Init(&SDIO_InitStructure);
SDIO_SetPowerState(SDIO_PowerState_ON); //上电状态,开启卡时钟
SDIO_ClockCmd(ENABLE);//SDIOCK使能
发送CMD0,进入空闲状态,循环发送,如果发送成功,则退出循环,cmderror判断发送是否成功,寄存器初始化为0。
for(i=0;i<74;i++)
{
SDIO_CmdInitStructure.SDIO_Argument = 0x0;//发送CMD0进入IDLE STAGE模式命令.
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_GO_IDLE_STATE; //cmd0
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_No; //无响应
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable; //则CPSM在开始发送命令之前等待数据传输结束。
SDIO_SendCommand(&SDIO_CmdInitStructure); //写命令进命令寄存器
errorstatus=CmdError(); //判断是否发送成功
if(errorstatus==SD_OK)break;
}
if(errorstatus)return errorstatus;//返回错误状态
SD_Error CmdError(void)
{
SD_Error errorstatus = SD_OK;
u32 timeout=SDIO_CMD0TIMEOUT;
while(timeout--)
{
if(SDIO_GetFlagStatus(SDIO_FLAG_CMDSENT) != RESET)break; //命令已发送(无需响应)
}
if(timeout==0)return SD_CMD_RSP_TIMEOUT;
SDIO_ClearFlag(SDIO_STATIC_FLAGS);//清除所有标记
return errorstatus;
}
CMD寄存器变为400,代表指令0,不需要响应。1000 0000
之后发送CMD8,检查SD卡接口电压,
SDIO_CmdInitStructure.SDIO_Argument = SD_CHECK_PATTERN; //发送CMD8,短响应,检查SD卡接口特性
SDIO_CmdInitStructure.SDIO_CmdIndex = SDIO_SEND_IF_COND; //cmd8
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short; //r7
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No; //关闭等待中断
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);
errorstatus=CmdResp7Error(); //等待R7响应
if(errorstatus==SD_OK) //R7响应正常
{
CardType=SDIO_STD_CAPACITY_SD_CARD_V2_0; //SD 2.0卡
SDType=SD_HIGH_CAPACITY; //高容量卡
}
SD_Error CmdResp7Error(void)
{
SD_Error errorstatus=SD_OK;
u32 status;
u32 timeout=SDIO_CMD0TIMEOUT;
while(timeout--)
{
status=SDIO->STA;
if(status&((1<<0)|(1<<2)|(1<<6)))break;//CRC错误/命令响应超时/已经收到响应(CRC校验成功)
}
if((timeout==0)||(status&(1<<2))) //响应超时
{
errorstatus=SD_CMD_RSP_TIMEOUT; //当前卡不是2.0兼容卡,或者不支持设定的电压范围
SDIO_ClearFlag(SDIO_FLAG_CTIMEOUT); //清除命令响应超时标志
return errorstatus;
}
if(status&1<<6) //成功接收到响应
{
errorstatus=SD_OK;
SDIO_ClearFlag(SDIO_FLAG_CMDREND); //清除响应标志
}
return errorstatus;
}
CMD寄存器变为448,10001001000,前六位代表指令8,01代表短响应,100代表使能CPSM状态机,进入空闲状态,响应时R7响应,收到响应,代表支持该电压范围,如果超时,则不是2.0卡或者不支持电压范围。ARG寄存器为发送的参数,1AA 代表检查。。。?
第6位为1,则代表接收响应成功,其他则代表失败
之后发送CMD55,代表下一条指令是应用指令。
SDIO_CmdInitStructure.SDIO_Argument = 0x00;//发送CMD55,短响应
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD;
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure); //发送CMD55,短响应
errorstatus=CmdResp1Error(SD_CMD_APP_CMD); //等待R1响应
errorstatus=CmdResp1Error(SD_CMD_APP_CMD); //等待R1响应
if(errorstatus!=SD_OK)return errorstatus; //响应错误
CMD寄存器变为477,10001 110111,前6位为55,01则为短响应,100使能CPSM状态机
接下来发送ACMD41指令,判断SD卡是否为大容量卡,不支持CMD55指令的卡为MMC卡,469,100 01 101001 41指令,短响应
SDIO_CmdInitStructure.SDIO_Argument = SD_VOLTAGE_WINDOW_SD | SDType; //发送ACMD41,短响应
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_APP_OP_COND;
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short; //r3
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);
errorstatus=CmdResp3Error(); //等待R3响应
response=SDIO->RESP1;; //得到响应
if(response&=SD_HIGH_CAPACITY) //SD_HIGH_CAPCITY = 0x400 0000
{
CardType=SDIO_HIGH_CAPACITY_SD_CARD; //支持大容量卡
}
OCR寄存器各位定义,倒数第二位是容量卡定义。
再之后是CMD2和CMD3指令,CMD2获取SD卡信息,4C2 100 11 000010 指令码为2,11为长响应。
SDIO_CmdInitStructure.SDIO_Argument = 0x0;//发送CMD2,取得CID,长响应
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_ALL_SEND_CID;
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Long;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);//发送CMD2,取得CID,长响应 128位
errorstatus=CmdResp2Error(); //等待R2响应
if(errorstatus!=SD_OK)return errorstatus; //响应错误
CID_Tab[0]=SDIO->RESP1;
CID_Tab[1]=SDIO->RESP2;
CID_Tab[2]=SDIO->RESP3;
CID_Tab[3]=SDIO->RESP4;
CID寄存器定义,Manufacture ID = 0x27 = 39.
- 数据传输模式:
通过CMD9可以获取CSD寄存器数据,比如块长度,块存储容量等。CMD4指令可以配置总线布局以及卡的数量,之后时钟频率从初始化频率的不大于400K,调节到10-25M这个数据传输频段。
RCA = rca;
SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)(rca << 16);//发送CMD9+卡RCA,取得CSD,长响应
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SEND_CSD;
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Long;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);
errorstatus=CmdResp2Error(); //等待R2响应
if(errorstatus!=SD_OK)return errorstatus; //响应错误
CSD_Tab[0]=SDIO->RESP1;
CSD_Tab[1]=SDIO->RESP2;
CSD_Tab[2]=SDIO->RESP3;
CSD_Tab[3]=SDIO->RESP4;
CMD7指令可以配置卡进入传输数据状态,但是同时只有一个卡进入传输数据状态,同时发送默认地址0x00,也可以使得卡退出传输状态,重新进入旁路状态。地址由RCA决定,因为在高16位,所以读取的RCA地址需要左移16位,才可以选中该卡 通过RCA来区分各个卡的地址。
SDIO_CmdInitStructure.SDIO_Argument = addr;//发送CMD7,选择卡,短响应
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SEL_DESEL_CARD;//cmd7
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);//发送CMD7,选择卡,短响应
return CmdResp1Error(SD_CMD_SEL_DESEL_CARD);
读写之前的一些操作指令:CMD16设置读写块的大小,一次读多少个字节,CMD32,CMD33,擦除开始和擦除结束指令,ACMD6,设置总线数据宽度,(1bit 4bit 8bit),ACMD23,设置即将擦除块的大小等,ACMD42设置或者清除50K欧姆的上拉电阻。
SDIO_CmdInitStructure.SDIO_Argument = blksize;
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN;
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);//发送CMD16+设置数据长度为blksize,短响应
errorstatus=CmdResp1Error(SD_CMD_SET_BLOCKLEN); //等待R1响应
SDIO_CmdInitStructure.SDIO_Argument = (u32)RCA<<16;//发送CMD13,查询卡的状态,短响应
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SEND_STATUS;
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);
errorstatus=CmdResp1Error(SD_CMD_SEND_STATUS); //等待R1响应
SDIO_CmdInitStructure.SDIO_Argument =nblks; //发送CMD23,设置块数量,短响应
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCK_COUNT;
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);
errorstatus=CmdResp1Error(SD_CMD_SET_BLOCK_COUNT);//等待R1响应
写数据块:CMD24(单块写入):写入一个块的大小,在标准容量卡有效,大容量卡,固定512字节。CMD25(多块写入):直到CMD12 停止写入指令。
CMD42:上锁或者解锁,设置密码或者复位密码。CMD56:主机设置读卡或者写卡。
CMD254单块写入
SDIO_CmdInitStructure.SDIO_Argument = addr;//发送CMD24,写单块指令,短响应
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_WRITE_SINGLE_BLOCK;
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);
errorstatus=CmdResp1Error(SD_CMD_WRITE_SINGLE_BLOCK);//等待R1响应
StopCondition=0; //单块写,不需要发送停止传输指令
SDIO_DataInitStructure.SDIO_DataBlockSize= power<<4; ; //blksize, 控制器到卡
SDIO_DataInitStructure.SDIO_DataLength= blksize ;
SDIO_DataInitStructure.SDIO_DataTimeOut=SD_DATATIMEOUT ;
SDIO_DataInitStructure.SDIO_DPSM=SDIO_DPSM_Enable;
SDIO_DataInitStructure.SDIO_TransferDir=SDIO_TransferDir_ToCard;
SDIO_DataInitStructure.SDIO_TransferMode=SDIO_TransferMode_Block;
SDIO_DataConfig(&SDIO_DataInitStructure);
CMD25 多块写入
SDIO_CmdInitStructure.SDIO_Argument =addr; //发送CMD25,多块写指令,短响应
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_WRITE_MULT_BLOCK;
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);
errorstatus=CmdResp1Error(SD_CMD_WRITE_MULT_BLOCK); //等待R1响应
if(errorstatus!=SD_OK)return errorstatus;
SDIO_DataInitStructure.SDIO_DataBlockSize= power<<4; ; //blksize, 控制器到卡
SDIO_DataInitStructure.SDIO_DataLength= nblks*blksize ;
SDIO_DataInitStructure.SDIO_DataTimeOut=SD_DATATIMEOUT ;
SDIO_DataInitStructure.SDIO_DPSM=SDIO_DPSM_Enable;
SDIO_DataInitStructure.SDIO_TransferDir=SDIO_TransferDir_ToCard;
SDIO_DataInitStructure.SDIO_TransferMode=SDIO_TransferMode_Block;
SDIO_DataConfig(&SDIO_DataInitStructure);
CMD12 停止写入。
SDIO_CmdInitStructure.SDIO_Argument =0;//发送CMD12+结束传输
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_STOP_TRANSMISSION;
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);
errorstatus=CmdResp1Error(SD_CMD_STOP_TRANSMISSION);//等待R1响应
if(errorstatus!=SD_OK)return errorstatus;
读数据块:CMD17读单个块,CMD18读多个块,CMD30,写保护,大容量卡 不支持改指令。ACMD13:发送卡状态,ACMD:22:发送写入块的数量,ACMD51:读取SCR寄存器内容。
SDIO_DataInitStructure.SDIO_DataBlockSize= power<<4 ;//配置读取的数据个数
SDIO_DataInitStructure.SDIO_DataLength= blksize ;
SDIO_DataInitStructure.SDIO_DataTimeOut=SD_DATATIMEOUT ;
SDIO_DataInitStructure.SDIO_DPSM=SDIO_DPSM_Enable;
SDIO_DataInitStructure.SDIO_TransferDir=SDIO_TransferDir_ToSDIO;
SDIO_DataInitStructure.SDIO_TransferMode=SDIO_TransferMode_Block;
SDIO_DataConfig(&SDIO_DataInitStructure);
SDIO_CmdInitStructure.SDIO_Argument = addr; //参数
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_READ_SINGLE_BLOCK; //指令码
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);//发送CMD17+从addr地址出读取数据,短响应
各指令定义表。
2.2 SD I/O卡
SDIO卡就是支持SDIO接口的卡,其卡的功能多种多样,比如支持WIFI或者BlueTooth等,SDIO卡和SD存储卡是兼容的,在机械、电气指令和软件上面,SDIO卡对于移动电子设备,提供了一个高速度的数据传输,可以作为SIOD设备被识别。
SDIO卡分为两种:高速和低速。
- 高速:支持SPI模式和SDIO(1bit、4bit(必须))0-25MHZ
- 低速:支持SPI模式和SDIO(1bit、4bit(可选)) 0-400KHZ
SD卡以及SDIO卡不同的指令,因为对于SDIO卡来说,SD卡的有些指令没用,比如擦除指令等。
2.3 MMC卡(多媒体卡)
再续
三、FATFS文件系统
再续