SDIO学习
内容提要
摸索SD操作许久,发现很多资料都是基于库函数开发,为真正理解SDIO的操作流程,本文引导读者一起去阅读Spec文件,深入理解SDIO操作的来龙去脉。(注意:本文主要是自己的学习心得,记录学习中感觉重要的知识点,细节还需参考Spec)
协议手册
SDIO操作协议主要包括两个部分
- SDIO接口协议 SDIO Card Specification:SDIO接口操作SD/MMC等的规范
- SD物理层协议 SD Specifications Physical Layer Spec:SD卡的操作规范
SD卡结构
如上图所示,SD卡主要包括输入输出引脚、卡接口控制器、电源管理、内部寄存器、Memory接口和Memory组成。
协议理解
总线数据传输协议
command和response通过双向传输线CMD传输
command表示SDIO接口传输给SD卡的指令,response表示SD卡传输个SDIO接口的信息
数据可以是1bit或4bit通过DAT传输(data0/data0-3)
数据线DAT是双向的,SDIO通过它进行SD卡的读写操作
CMD结构
CMD命令结构
每条指令由起始位(1bit)、传输位(1bit)、命令索引(6bit)、CRC校验(7bit)和结束位(1bit)组成,总长48bit。
传输位标识传输的方向,SDIO(host)传输的为command,transmmitter bit = 1
Responds类型
SD卡的response有多种类型,请参考Spec文档。
response主要包含SD卡的内部寄存器内容和状态信息,在操作过程中需密切关注。
传输位标识传输的方向,SD(host)传输的为response,transmmitter bit = 0
数据结构
SDIO支持1bit和4bit数据传输,数据发送通过移位寄存器进行串行处理
SD读写操作
卡状态与操作模式
卡状态可以通过response或CMD13得到,标识SD操作的状态
操作模式是上电操作一般的过程
初始化
上电后,需要识别SD卡的类型,得到SD卡的状态信息,并将SD置于stand-by模式,故需要进行初始化
初始化的步骤(SDHC卡为例)
- 发送CMD0,将卡状态复位到idle状态。
- 发送CMD8,得到response。
- 持续发送ACMD41(CMD55+CMD41),检测response的CCS(30bit),若CCS = 1,则为高容量卡,检测识别是否完成response的finish(31bit)= 1执行下一步。
- 发送CMD2得到CID信息
- 发送CMD3得到SD卡地址信息(RCA)初始化完成,进入到stand-by模式
传输状态
- 如图所示,通过发送不同的CMD,切换SD的状态
- 注意状态的转移情况
卡状态确认
- 卡状态可以通过CMD13查看SD卡所处的状态,确认操作正确与否
参考代码
void SDIORead_Test(){
int rca;
int complete;
int current_status;
int error_status;
int i;
int n = 1000;
int temp = 0;
int rxdata0 = 0;
int rxdata1 = 0;
int rxdata2 = 0;
int rxdata3 = 0;
uart_printf("Start testing SDIO tranfer...\n");
SDIO1_GPIOInitRemap(); //配置GPIO
//======================================================
// set up
// Test: Init sequence, With response check
// CMD 0 Reset Card
// CMD 8 Get voltage (Only 2.0 Card response to this)
// CMD55 Indicate Next Command are Application specific
// ACMD44 Get Voltage windows
// CMD 2 CID reg
// CMD 3 Get RCA.
//======================================================
//配置MCU的SDIO(根据不同的SDIO而定)
SDIO1->MMC_CARDSEL = 0xdf; //enable module, enable mmcclk
SDIO1->MMC_CTRL = 0x83; //4bit,low speed,1/16 divider
SDIO1->MMC_INT_MASK = 0x01; //unmask interrupt
SDIO1->MMC_CRCCTL = 0xC0;
//======================================================
//reset card :CMD0
//======================================================
CMD_Send(0,0);
n = 100;
while(n--);
//======================================================
//cmd 8
//======================================================
CMD_Send(8,0x1AA);
n = 1000;
while(n--);
SDIO1->MMC_IO = 0x0c; //auto only response transfer (48bit)
while(1){
if(SDIO1->CLR_MMC_INT & 0x1){ //judge which interrupt generation
uart_printf("Recieve CMD8 response OK.\n");
SDIO1->CLR_MMC_INT = 0x1; //write 1 clear interrup
break;
}
}
rxdata0 = 0;
rxdata1 = 0;
rxdata2 = 0;
rxdata3 = 0;
rxdata0 = SDIO1->CMD_BUF3<<24 |SDIO1->CMD_BUF2<<16 |SDIO1->CMD_BUF1<<8 | SDIO1->CMD_BUF0;
rxdata1 = SDIO1->CMD_BUF4;
uart_printf("rxdata0 = %x.\n",rxdata0);
uart_printf("rxdata1 = %x.\n",rxdata1);
n = 100;
while(n--);
while(1)
{
//======================================================
//cmd 55
//======================================================
CMD_Send(55,0);
n = 1000;
while(n--);
SDIO1->MMC_IO = 0x0c; //auto only response transfer (48bit)
while(1){
if(SDIO1->CLR_MMC_INT & 0x1){ //judge which interrupt generation
uart_printf("Recieve CMD55 response OK.\n");
SDIO1->CLR_MMC_INT = 0x1; //write 1 clear interrup
break;
}
}
rxdata0 = 0;
rxdata1 = 0;
rxdata2 = 0;
rxdata3 = 0;
rxdata0 = SDIO1->CMD_BUF3<<24 |SDIO1->CMD_BUF2<<16 |SDIO1->CMD_BUF1<<8 | SDIO1->CMD_BUF0;
rxdata1 = SDIO1->CMD_BUF4;
uart_printf("rxdata0 = %x.\n",rxdata0);
uart_printf("rxdata1 = %x.\n",rxdata1);
n = 100;
while(n--);
//======================================================
//acmd 41
//======================================================
CMD_Send(41,0xC0100000);
n = 1000;
while(n--);
SDIO1->MMC_IO = 0x0c; //auto only response transfer (48bit)
while(1){
if(SDIO1->CLR_MMC_INT & 0x1){ //judge which interrupt generation
uart_printf("Recieve CMD41 response OK.\n");
SDIO1->CLR_MMC_INT = 0x1; //write 1 clear interrup
break;
}
}
rxdata0 = 0;
rxdata1 = 0;
rxdata2 = 0;
rxdata3 = 0;
rxdata0 = SDIO1->CMD_BUF3<<24 |SDIO1->CMD_BUF2<<16 |SDIO1->CMD_BUF1<<8 | SDIO1->CMD_BUF0;
rxdata1 = SDIO1->CMD_BUF4;
uart_printf("rxdata0 = %x.\n",rxdata0);
uart_printf("rxdata1 = %x.\n",rxdata1);
if(!(rxdata0>>30 & 0x1)){ //判断CCS,CCS=1为高容量卡
uart_printf("CCS = 0\n");
}
else{
uart_printf("CCS = 1 High Capacity SD Memory Card\n");
}
if(!(rxdata0>>31 & 0x1)){ //判断电压设置,上电是否完成
uart_printf("Finished = 0\n");
}
else{
uart_printf("Finished = 1\n");
}
n = 100;
while(n--);
if((rxdata0>>31 & 0x1)){
break; //上电完成退出循环
}
}
//======================================================
//cmd 2 CID
//======================================================
CMD_Send(2,0);
SDIO1->MMC_IO = 0x1c; //auto only response transfer (136bit)
while(1){
if(SDIO1->CLR_MMC_INT & 0x1){ //judge which interrupt generation
uart_printf("Recieve CMD2 response OK.\n");
SDIO1->CLR_MMC_INT = 0x1; //write 1 clear interrup
break;
}
}
rxdata0 = 0;
rxdata1 = 0;
rxdata2 = 0;
rxdata3 = 0;
rxdata0 = SDIO1->CMD_BUF3<<24 |SDIO1->CMD_BUF2<<16 |SDIO1->CMD_BUF1<<8 | SDIO1->CMD_BUF0;
rxdata1 = SDIO1->CMD_BUF7<<24 |SDIO1->CMD_BUF6<<16 |SDIO1->CMD_BUF5<<8 | SDIO1->CMD_BUF4;
rxdata2 = SDIO1->CMD_BUF11<<24 |SDIO1->CMD_BUF10<<16 |SDIO1->CMD_BUF9<<8 | SDIO1->CMD_BUF8;
rxdata3 = SDIO1->CMD_BUF15<<24 |SDIO1->CMD_BUF14<<16 |SDIO1->CMD_BUF13<<8 | SDIO1->CMD_BUF12;
uart_printf("rxdata0 = %x.\n",rxdata0);
uart_printf("rxdata1 = %x.\n",rxdata1);
uart_printf("rxdata2 = %x.\n",rxdata2);
uart_printf("rxdata3 = %x.\n",rxdata3);
n = 100;
while(n--);
//======================================================
//cmd 3 RCA :得到RCA,后续传输需要
//======================================================
CMD_Send(3,0);
SDIO1->MMC_IO = 0x0c; //auto only response transfer (48bit)
while(1){
if(SDIO1->CLR_MMC_INT & 0x1){ //judge which interrupt generation
uart_printf("Recieve CMD3 response OK.\n");
SDIO1->CLR_MMC_INT = 0x1; //write 1 clear interrup
break;
}
}
rxdata0 = 0;
rxdata1 = 0;
rxdata2 = 0;
rxdata3 = 0;
rxdata0 = SDIO1->CMD_BUF3<<24 |SDIO1->CMD_BUF2<<16 |SDIO1->CMD_BUF1<<8 | SDIO1->CMD_BUF0;
rxdata1 = SDIO1->CMD_BUF4;
uart_printf("rxdata0 = %x.\n",rxdata0);
uart_printf("rxdata1 = %x.\n",rxdata1);
uart_printf("RCA = %x.\n",(unsigned int)rxdata0>>16);
rca = (unsigned int)rxdata0>>16;
n = 100;
while(n--);
//======================================================
//cmd 9 + RCA -> CSD
//======================================================
CMD_Send(9,rca<<16);
uart_printf("\nRCA << 16 = %x\n",(unsigned int)rca<<16);
SDIO1->MMC_IO = 0x0c; //auto only response transfer (48bit)
while(1){
if(SDIO1->CLR_MMC_INT & 0x1){ //judge which interrupt generation
uart_printf("Recieve CMD9 response OK.\n");
SDIO1->CLR_MMC_INT = 0x1; //write 1 clear interrup
break;
}
}
rxdata0 = 0;
rxdata1 = 0;
rxdata2 = 0;
rxdata3 = 0;
rxdata0 = SDIO1->CMD_BUF3<<24 |SDIO1->CMD_BUF2<<16 |SDIO1->CMD_BUF1<<8 | SDIO1->CMD_BUF0;
rxdata1 = SDIO1->CMD_BUF7<<24 |SDIO1->CMD_BUF6<<16 |SDIO1->CMD_BUF5<<8 | SDIO1->CMD_BUF4;
rxdata2 = SDIO1->CMD_BUF11<<24 |SDIO1->CMD_BUF10<<16 |SDIO1->CMD_BUF9<<8 | SDIO1->CMD_BUF8;
rxdata3 = SDIO1->CMD_BUF15<<24 |SDIO1->CMD_BUF14<<16 |SDIO1->CMD_BUF13<<8 | SDIO1->CMD_BUF12;
uart_printf("rxdata0 = %x.\n",rxdata0);
uart_printf("rxdata1 = %x.\n",rxdata1);
uart_printf("rxdata2 = %x.\n",rxdata2);
uart_printf("rxdata3 = %x.\n",rxdata3);
n = 100;
while(n--);
//======================================================
//cmd 13 status stand-by
//======================================================
while(1){
CMD_Send(13,rca<<16);
SDIO1->MMC_IO = 0x0c; //auto only response transfer (48bit)
while(1){
if(SDIO1->CLR_MMC_INT & 0x1){ //judge which interrupt generation
uart_printf("Recieve CMD13 response OK.\n");
SDIO1->CLR_MMC_INT = 0x1; //write 1 clear interrup
break;
}
}
rxdata0 = 0;
rxdata1 = 0;
rxdata2 = 0;
rxdata3 = 0;
rxdata0 = SDIO1->CMD_BUF3<<24 |SDIO1->CMD_BUF2<<16 |SDIO1->CMD_BUF1<<8 | SDIO1->CMD_BUF0;
rxdata1 = SDIO1->CMD_BUF4;
uart_printf("rxdata0 = %x.\n",rxdata0);
uart_printf("rxdata1 = %x.\n",rxdata1);
n = 100;
while(n--);
current_status = (rxdata0>>9) & 0xf;
error_status = (rxdata0>>19) & 0x1;
uart_printf("\r\n current_status = %x.\r\n",current_status);
uart_printf("\r\n error_status = %x.\r\n",error_status);
if(current_status == 3){
break;
}
}
//======================================================
//cmd 4 设置频率
//======================================================
CMD_Send(4,0x04040000);
SDIO1->MMC_IO = 0x0c; //auto only response transfer (48bit)
while(1){
if(SDIO1->CLR_MMC_INT & 0x1){ //judge which interrupt generation
uart_printf("Recieve CMD4 response OK.\n");
SDIO1->CLR_MMC_INT = 0x1; //write 1 clear interrup
break;
}
}
rxdata0 = 0;
rxdata1 = 0;
rxdata2 = 0;
rxdata3 = 0;
rxdata0 = SDIO1->CMD_BUF3<<24 |SDIO1->CMD_BUF2<<16 |SDIO1->CMD_BUF1<<8 | SDIO1->CMD_BUF0;
rxdata1 = SDIO1->CMD_BUF4;
uart_printf("rxdata0 = %x.\n",rxdata0);
uart_printf("rxdata1 = %x.\n",rxdata1);
n = 100;
while(n--);
SDIO1->MMC_CTRL = 0xc3; //4bit,high speed,1/2 divider
//======================================================
//cmd 7
//======================================================
CMD_Send(7,rca<<16);
SDIO1->MMC_IO = 0x0c; //auto only response transfer (48bit)
while(1){
if(SDIO1->CLR_MMC_INT & 0x1){ //judge which interrupt generation
uart_printf("Recieve CMD7 response OK.\n");
SDIO1->CLR_MMC_INT = 0x1; //write 1 clear interrup
break;
}
}
rxdata0 = 0;
rxdata1 = 0;
rxdata2 = 0;
rxdata3 = 0;
rxdata0 = SDIO1->CMD_BUF3<<24 |SDIO1->CMD_BUF2<<16 |SDIO1->CMD_BUF1<<8 | SDIO1->CMD_BUF0;
rxdata1 = SDIO1->CMD_BUF4;
uart_printf("rxdata0 = %x.\n",rxdata0);
uart_printf("rxdata1 = %x.\n",rxdata1);
n = 100;
while(n--);
//======================================================
//cmd 13 status tran mode
//======================================================
while(1){
CMD_Send(13,rca<<16);
SDIO1->MMC_IO = 0x0c; //auto only response transfer (48bit)
while(1){
if(SDIO1->CLR_MMC_INT & 0x1){ //judge which interrupt generation
uart_printf("Recieve CMD13 response OK.\n");
SDIO1->CLR_MMC_INT = 0x1; //write 1 clear interrup
break;
}
}
rxdata0 = 0;
rxdata1 = 0;
rxdata2 = 0;
rxdata3 = 0;
rxdata0 = SDIO1->CMD_BUF3<<24 |SDIO1->CMD_BUF2<<16 |SDIO1->CMD_BUF1<<8 | SDIO1->CMD_BUF0;
rxdata1 = SDIO1->CMD_BUF4;
uart_printf("rxdata0 = %x.\n",rxdata0);
uart_printf("rxdata1 = %x.\n",rxdata1);
n = 100;
while(n--);
current_status = (rxdata0>>9) & 0xf;
uart_printf("current_status = %x.\n",current_status);
if(current_status == 4){
break;
}
}
//======================================================
//cmd 55
//======================================================
CMD_Send(55,rca<<16);
n = 1000;
while(n--);
SDIO1->MMC_IO = 0x0c; //auto only response transfer (48bit)
while(1){
if(SDIO1->CLR_MMC_INT & 0x1){ //judge which interrupt generation
// uart_printf("Recieve CMD55 response OK.\n");
SDIO1->CLR_MMC_INT = 0x1; //write 1 clear interrup
break;
}
}
rxdata0 = 0;
rxdata1 = 0;
rxdata2 = 0;
rxdata3 = 0;
rxdata0 = SDIO1->CMD_BUF3<<24 |SDIO1->CMD_BUF2<<16 |SDIO1->CMD_BUF1<<8 | SDIO1->CMD_BUF0;
rxdata1 = SDIO1->CMD_BUF4;
// uart_printf("rxdata0 = %x.\n",rxdata0);
// uart_printf("rxdata1 = %x.\n",rxdata1);
n = 100;
while(n--);
//
//======================================================
//acmd 6 设置bus宽度
//======================================================
CMD_Send(6,0x2); //4bit
SDIO1->MMC_IO = 0x0c; //auto only response transfer (48bit)
while(1){
if(SDIO1->CLR_MMC_INT & 0x1){ //judge which interrupt generation
uart_printf("Recieve CMD6 response OK.\n");
SDIO1->CLR_MMC_INT = 0x1; //write 1 clear interrup
break;
}
}
rxdata0 = 0;
rxdata1 = 0;
rxdata2 = 0;
rxdata3 = 0;
rxdata0 = SDIO1->CMD_BUF3<<24 |SDIO1->CMD_BUF2<<16 |SDIO1->CMD_BUF1<<8 | SDIO1->CMD_BUF0;
rxdata1 = SDIO1->CMD_BUF4;
uart_printf("rxdata0 = %x.\n",rxdata0);
uart_printf("rxdata1 = %x.\n",rxdata1);
n = 100;
while(n--);
//======================================================
//cmd 16
//======================================================
CMD_Send(16,0x200);
SDIO1->MMC_IO = 0x0c; //auto only response transfer (48bit)
while(1){
if(SDIO1->CLR_MMC_INT & 0x1){ //judge which interrupt generation
uart_printf("Recieve CMD16 response OK.\n");
SDIO1->CLR_MMC_INT = 0x1; //write 1 clear interrup
break;
}
}
rxdata0 = 0;
rxdata1 = 0;
rxdata2 = 0;
rxdata3 = 0;
rxdata0 = SDIO1->CMD_BUF3<<24 |SDIO1->CMD_BUF2<<16 |SDIO1->CMD_BUF1<<8 | SDIO1->CMD_BUF0;
rxdata1 = SDIO1->CMD_BUF4;
uart_printf("rxdata0 = %x.\n",rxdata0);
uart_printf("rxdata1 = %x.\n",rxdata1);
n = 100;
while(n--);
//======================================================
//cmd 17 read data
//======================================================
// CMD_Send(17,0x300);
CMD_Send(17,0x0);
SDIO1->MMC_IO = 0x0c; //auto only response transfer (48bit)
while(1){
if(SDIO1->CLR_MMC_INT & 0x1){ //judge which interrupt generation
uart_printf("Recieve CMD17 response OK.\n");
SDIO1->CLR_MMC_INT = 0x1; //write 1 clear interrup
break;
}
}
rxdata0 = 0;
rxdata1 = 0;
rxdata2 = 0;
rxdata3 = 0;
rxdata0 = SDIO1->CMD_BUF3<<24 |SDIO1->CMD_BUF2<<16 |SDIO1->CMD_BUF1<<8 | SDIO1->CMD_BUF0;
rxdata1 = SDIO1->CMD_BUF4;
uart_printf("rxdata0 = %x.\n",rxdata0);
uart_printf("rxdata1 = %x.\n",rxdata1);
n = 100;
while(n--);
complete = SDIO1->BUF_CTL & 0x1;
uart_printf("\r\n\ncomplete = %d.\r\n\n",complete);
//======================================================
//read data
//======================================================
SDIO1->BUF_CTL = 0x020; //disable dma, read sd card
SDIO1->MMC_IO = 0x3; //!!!read data, auto transfer
uart_printf("Wait read data from sd card.\n");
while(1){ //wait FIFO full interrupt
n = 100;
while(n--);
if((SDIO1->BUF_CTL & 0x1)){ //judge which interrupt generation
uart_printf("Data transmission is completed.\n");
break;
}
}
complete = SDIO1->BUF_CTL & 0x1;
uart_printf("\r\n\ncomplete = %d.\r\n\n",complete);
//======================================================
//cmd 12
//======================================================
CMD_Send(12,0);
SDIO1->MMC_IO = 0x0c; //auto only response transfer (48bit)
while(1){
if(SDIO1->CLR_MMC_INT & 0x1){ //judge which interrupt generation
uart_printf("Recieve CMD12 response OK.\n");
SDIO1->CLR_MMC_INT = 0x1; //write 1 clear interrup
break;
}
}
rxdata0 = 0;
rxdata1 = 0;
rxdata2 = 0;
rxdata3 = 0;
rxdata0 = SDIO1->CMD_BUF3<<24 |SDIO1->CMD_BUF2<<16 |SDIO1->CMD_BUF1<<8 | SDIO1->CMD_BUF0;
rxdata1 = SDIO1->CMD_BUF4;
uart_printf("rxdata0 = %x.\n",rxdata0);
uart_printf("rxdata1 = %x.\n",rxdata1);
uart_printf("Read data from data_buf\n");
SDIO1->BUF_CTL = 0x000; //read buf
uart_printf("\r\n\r\n");
for(i = 0;i<128;i++){
uart_printf("%x ",SDIO1->DATA_BUF0);
}
uart_printf("\r\n\r\n");
uart_printf("Read data OK\n");
n = 100;
while(n--);
uart_printf("Finish.\n");
}
参考资料
[1]. SD_Physical_Layer_Spec_Version 2.00.pdf
[2]. Simplified_SDIO_Card_Spec_Version 2.00.pdf