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