SDIO学习

SDIO学习

内容提要


摸索SD操作许久,发现很多资料都是基于库函数开发,为真正理解SDIO的操作流程,本文引导读者一起去阅读Spec文件,深入理解SDIO操作的来龙去脉。(注意:本文主要是自己的学习心得,记录学习中感觉重要的知识点,细节还需参考Spec)

协议手册


SDIO操作协议主要包括两个部分

  • SDIO接口协议 SDIO Card Specification:SDIO接口操作SD/MMC等的规范
  • SD物理层协议 SD Specifications Physical Layer Spec:SD卡的操作规范

847278-20170610114346872-1092793625.png

SD卡结构


847278-20170610114402700-727846902.png

如上图所示,SD卡主要包括输入输出引脚、卡接口控制器、电源管理、内部寄存器、Memory接口和Memory组成。

协议理解


总线数据传输协议


847278-20170610114416918-219912657.png
847278-20170610114424106-1996863251.png
847278-20170610114433825-512411042.png

command和response通过双向传输线CMD传输
command表示SDIO接口传输给SD卡的指令,response表示SD卡传输个SDIO接口的信息
数据可以是1bit或4bit通过DAT传输(data0/data0-3)
数据线DAT是双向的,SDIO通过它进行SD卡的读写操作

CMD结构


CMD命令结构

847278-20170610114449543-1774480459.png

每条指令由起始位(1bit)、传输位(1bit)、命令索引(6bit)、CRC校验(7bit)和结束位(1bit)组成,总长48bit。
传输位标识传输的方向,SDIO(host)传输的为command,transmmitter bit = 1

Responds类型

847278-20170610114457747-962237407.png

SD卡的response有多种类型,请参考Spec文档。
response主要包含SD卡的内部寄存器内容和状态信息,在操作过程中需密切关注。
传输位标识传输的方向,SD(host)传输的为response,transmmitter bit = 0

数据结构


847278-20170610114506106-2063733716.png

SDIO支持1bit和4bit数据传输,数据发送通过移位寄存器进行串行处理

SD读写操作


卡状态与操作模式


847278-20170610114538575-1469839611.png

卡状态可以通过response或CMD13得到,标识SD操作的状态
操作模式是上电操作一般的过程

初始化


847278-20170610114547153-2048330336.png

上电后,需要识别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模式

传输状态


847278-20170610114611512-1958720884.png

  • 如图所示,通过发送不同的CMD,切换SD的状态
  • 注意状态的转移情况

卡状态确认


847278-20170610114623747-1733101358.png

  • 卡状态可以通过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

转载于:https://www.cnblogs.com/OneFri/p/6978283.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值