一、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檔案系統
再續