SDIO应用SD卡
一、SD卡介紹
SD卡(Secure Digital Memory Card)即:安全數碼卡,它是在MMC的基礎上發展而來,是一種基于半導體快閃記憶器的新一代記憶設備,它被廣泛地于便攜式裝置上使用,例如數碼相機、個人數碼助理(PDA)和多媒體播放器等。SD卡由日本松下、東芝及美國SanDisk公司于1999年8月共同開發研制。
分類:SD卡外形和接口圖如下圖所示:
SD卡由9個引腳與外部通信,支持SPI和SDIO兩種模式,不同模式下,SD卡引腳功能描述如下表所示:
特點
①高容量,最大可達2TB(目前已有512GB的SD卡)。
②高安全性。
③高速,目前已有讀取速度近100MB/S的SD卡。
④體積小巧,標準SD卡大小只有一張郵票大小,重量僅2g。
⑤接口簡單,支持SPI和SDIO兩種訪問模式。
注意:TF卡+卡套,組合起來也可以當SD卡用,不過,很大一部分TF卡,不支持SPI訪問模式。所以,SPI驅動SD卡的時候,盡量選擇大卡(SD卡),而不要選擇TF卡。
二、SD卡寄存器
三、SD卡初始化
初始化流程圖
從SD卡初始化流程可知,不管什么卡(這里我們將卡分為4類:SD2.0高容量卡(SDHC,最大32G),SD2.0標準容量卡(SDSC,最大2G),SD1.x卡和MMC卡),首先我們要執行的是卡上電(設置SDIO_POWER
[1:0]=11),上電后發送CMD0,對卡進行軟復位,之后發送CMD8命令,用于區分SD卡2.0,只有2.0及以后的卡才支持CMD8命令,MMC卡和V1.x的卡,是不支持該命令的。 CMD8命令格式如下表:
在發送CMD8的時候,通過其帶的參數我們可以設置VHS位,以告訴SD卡,主機的供電情況,讓SD卡知道主機的供電范圍。
這里使用參數0X1AA,即告訴SD卡,主機供電為2.7~3.6V之間,如果SD卡支持CMD8,且支持該電壓范圍,則會通過CMD8的響應(R7)。在發送CMD8后,發送ACMD41(注意:發送ACMD41之前,要先發送CMD55),來進一步確認卡的操作電壓范圍,并通過HCS位來告訴SD卡,主機是不是支持高容量卡(SDHC)。
ACMD41命令格式如下表所示:
ACMD41指令響應(R3),包含了SD卡OCR寄存器內容,其定義如下表所示:對于支持CMD8的卡,主機設置ACMD41的參數HCS=1,告訴SD卡,主機支持SDHC卡。
對2.0的卡,OCR的CCS位用于表示SDHC還是SDSC;對1.x的卡,則忽略該位;
對MMC卡,則不支持ACMD41,MMC卡只需要發送:CMD0和CMD1即可完成初始化。
CMD2用于獲取CID寄存器數據,CID寄存器各位定義如下表:
SD卡在收到CMD2后,將返回R2長響應(136位),其中包含128位有效數據(CID寄存器內容),存放在SDIO_RESP1~4等4個寄存器里面。通過讀取這四個寄存器,就可以獲得SD卡的CID信息。
CMD3,用于設置卡相對地址(RCA,必須為非0),對于SD卡(非MMC卡),在收到CMD3后,將返回一個新的RCA給主機,方便主機尋址。RCA的存在允許一個SDIO接口掛多個SD卡,通過RCA來區分主機要操作的是哪個卡。對于MMC卡,則不是由SD卡自動返回RCA,而是主機主動設置MMC卡的RCA,即通過CMD3帶參數(高16位用于RCA設置),實現RCA設置。同樣MMC卡也支持一個SDIO接口掛多個MMC卡,不同于SD卡的是所有的RCA都是由主機主動設置的,而SD卡的RCA則是SD卡發給主機的。
在獲得卡RCA之后,我們便可以發送CMD9(帶RCA參數),獲得SD卡的CSD寄存器內容,從CSD寄存器,我們可以得到SD卡的容量和扇區大小等十分重要的信息。
至此,我們的SD卡初始化基本就結束了,最后通過CMD7命令,選中我們要操作的SD卡,即可開始對SD卡的讀寫操作了。
四、SD卡讀寫數據
1、SD卡單塊數據塊讀取流程
注意:CMD16設置的數據塊大小,一般為512字節,此設置直接決定SD卡的塊大小,SD卡默認的塊大小自動失效。
2、SD卡多塊數據塊讀取流程
①、CMD16指令說明:
②、 CMD17指令說明:
③、 CMD18指令說明:
④、CMD11指令說明:
3、SD卡單塊數據塊寫入流程
4、SD卡多塊數據寫入流程
注意:該指令僅對SD卡有效。另外,在ACMD指令發送之前,需先發送CMD55。
①、CMD13指令說明:
R1響應:
②、CMD24指令說明:
③、ACMD23指令說明:
注意:發送ACMD之前,必須先發送CMD55,通知SD卡,接下來要發送的是應用命令(APP CMD),而非標準命令
④、CMD55指令說明:
⑤、CMD25指令說明:
五、代碼
1、SD初始化
//初始化SD卡 //返回值:錯誤代碼;(0,無錯誤) SD_Error SD_Init(void) {GPIO_InitTypeDef GPIO_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;SD_Error errorstatus=SD_OK; u8 clkdiv=0;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC|RCC_AHB1Periph_GPIOD|RCC_AHB1Periph_DMA2, ENABLE);//使能GPIOC,GPIOD DMA2時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_SDIO, ENABLE);//SDIO時鐘使能RCC_APB2PeriphResetCmd(RCC_APB2Periph_SDIO, ENABLE);//SDIO復位GPIO_InitStructure.GPIO_Pin =GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12; //PC8,9,10,11,12復用功能輸出 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//復用功能GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//100MGPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉GPIO_Init(GPIOC, &GPIO_InitStructure);// PC8,9,10,11,12復用功能輸出GPIO_InitStructure.GPIO_Pin =GPIO_Pin_2;GPIO_Init(GPIOD, &GPIO_InitStructure);//PD2復用功能輸出//引腳復用映射設置GPIO_PinAFConfig(GPIOC,GPIO_PinSource8,GPIO_AF_SDIO); //PC8,AF12GPIO_PinAFConfig(GPIOC,GPIO_PinSource9,GPIO_AF_SDIO);GPIO_PinAFConfig(GPIOC,GPIO_PinSource10,GPIO_AF_SDIO);GPIO_PinAFConfig(GPIOC,GPIO_PinSource11,GPIO_AF_SDIO);GPIO_PinAFConfig(GPIOC,GPIO_PinSource12,GPIO_AF_SDIO); GPIO_PinAFConfig(GPIOD,GPIO_PinSource2,GPIO_AF_SDIO); RCC_APB2PeriphResetCmd(RCC_APB2Periph_SDIO, DISABLE);//SDIO結束復位//SDIO外設寄存器設置為默認值 SDIO_Register_Deinit();NVIC_InitStructure.NVIC_IRQChannel = SDIO_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;//搶占優先級3NVIC_InitStructure.NVIC_IRQChannelSubPriority =0; //子優先級3NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能NVIC_Init(&NVIC_InitStructure); //根據指定的參數初始化VIC寄存器、errorstatus=SD_PowerON(); //SD卡上電if(errorstatus==SD_OK)errorstatus=SD_InitializeCards(); //初始化SD卡 if(errorstatus==SD_OK)errorstatus=SD_GetCardInfo(&SDCardInfo); //獲取卡信息if(errorstatus==SD_OK)errorstatus=SD_SelectDeselect((u32)(SDCardInfo.RCA<<16));//選中SD卡 if(errorstatus==SD_OK)errorstatus=SD_EnableWideBusOperation(SDIO_BusWide_4b); //4位寬度,如果是MMC卡,則不能用4位模式 if((errorstatus==SD_OK)||(SDIO_MULTIMEDIA_CARD==CardType)){ if(SDCardInfo.CardType==SDIO_STD_CAPACITY_SD_CARD_V1_1||SDCardInfo.CardType==SDIO_STD_CAPACITY_SD_CARD_V2_0){clkdiv=SDIO_TRANSFER_CLK_DIV+2; //V1.1/V2.0卡,設置最高48/4=12Mhz}else clkdiv=SDIO_TRANSFER_CLK_DIV; //SDHC等其他卡,設置最高48/2=24MhzSDIO_Clock_Set(clkdiv); //設置時鐘頻率,SDIO時鐘計算公式:SDIO_CK時鐘=SDIOCLK/[clkdiv+2];其中,SDIOCLK固定為48Mhz //errorstatus=SD_SetDeviceMode(SD_DMA_MODE); //設置為DMA模式errorstatus=SD_SetDeviceMode(SD_POLLING_MODE);//設置為查詢模式}return errorstatus; }2、卡上電
//卡上電 //查詢所有SDIO接口上的卡設備,并查詢其電壓和配置時鐘 //返回值:錯誤代碼;(0,無錯誤) SD_Error SD_PowerON(void) {u8 i=0;SD_Error errorstatus=SD_OK;u32 response=0,count=0,validvoltage=0;u32 SDType=SD_STD_CAPACITY;/*初始化時的時鐘不能大于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_CKSDIO_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->CLKCR|=1<<8; //SDIOCK使能 for(i=0;i<74;i++){SDIO_CmdInitStructure.SDIO_Argument = 0x0;//發送CMD0進入IDLE STAGE模式命令.SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_GO_IDLE_STATE; //cmd0SDIO_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;//返回錯誤狀態SDIO_CmdInitStructure.SDIO_Argument = SD_CHECK_PATTERN; //發送CMD8,短響應,檢查SD卡接口特性SDIO_CmdInitStructure.SDIO_CmdIndex = SDIO_SEND_IF_COND; //cmd8SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short; //r7SDIO_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; //高容量卡}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響應 if(errorstatus==SD_OK)//SD2.0/SD 1.1,否則為MMC卡{ //SD卡,發送ACMD41 SD_APP_OP_COND,參數為:0x80100000 while((!validvoltage)&&(count<SD_MAX_VOLT_TRIAL)){ SDIO_CmdInitStructure.SDIO_Argument = 0x00;//發送CMD55,短響應SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD; //CMD55SDIO_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響應 if(errorstatus!=SD_OK)return errorstatus; //響應錯誤//acmd41,命令參數由支持的電壓范圍及HCS位組成,HCS位置一來區分卡是SDSc還是sdhcSDIO_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; //r3SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;SDIO_SendCommand(&SDIO_CmdInitStructure);errorstatus=CmdResp3Error(); //等待R3響應 if(errorstatus!=SD_OK)return errorstatus; //響應錯誤 response=SDIO->RESP1;; //得到響應validvoltage=(((response>>31)==1)?1:0); //判斷SD卡上電是否完成count++;}if(count>=SD_MAX_VOLT_TRIAL){errorstatus=SD_INVALID_VOLTRANGE;return errorstatus;} if(response&=SD_HIGH_CAPACITY){CardType=SDIO_HIGH_CAPACITY_SD_CARD;}}else//MMC卡{//MMC卡,發送CMD1 SDIO_SEND_OP_COND,參數為:0x80FF8000 while((!validvoltage)&&(count<SD_MAX_VOLT_TRIAL)){ SDIO_CmdInitStructure.SDIO_Argument = SD_VOLTAGE_WINDOW_MMC;//發送CMD1,短響應 SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SEND_OP_COND;SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short; //r3SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;SDIO_SendCommand(&SDIO_CmdInitStructure);errorstatus=CmdResp3Error(); //等待R3響應 if(errorstatus!=SD_OK)return errorstatus; //響應錯誤 response=SDIO->RESP1;; //得到響應validvoltage=(((response>>31)==1)?1:0);count++;}if(count>=SD_MAX_VOLT_TRIAL){errorstatus=SD_INVALID_VOLTRANGE;return errorstatus;} CardType=SDIO_MULTIMEDIA_CARD; } return(errorstatus); }3、設置時鐘頻率
//SDIO時鐘初始化設置 //clkdiv:時鐘分頻系數 //CK時鐘=SDIOCLK/[clkdiv+2];(SDIOCLK時鐘固定為48Mhz) void SDIO_Clock_Set(u8 clkdiv) {u32 tmpreg=SDIO->CLKCR; tmpreg&=0XFFFFFF00; tmpreg|=clkdiv; SDIO->CLKCR=tmpreg; }4、設置為DMA模式還是查詢模式
//設置SD卡工作模式 //Mode: //返回值:錯誤狀態 SD_Error SD_SetDeviceMode(u32 Mode) {SD_Error errorstatus = SD_OK;if((Mode==SD_DMA_MODE)||(Mode==SD_POLLING_MODE))DeviceMode=Mode;else errorstatus=SD_INVALID_PARAMETER;return errorstatus; }5、SD卡讀操作
//讀SD卡 //buf:讀數據緩存區 //sector:扇區地址 //cnt:扇區個數 //返回值:錯誤狀態;0,正常;其他,錯誤代碼; u8 SD_ReadDisk(u8*buf,u32 sector,u8 cnt) {u8 sta=SD_OK;long long lsector=sector;u8 n;lsector<<=9;if((u32)buf%4!=0){for(n=0;n<cnt;n++){sta=SD_ReadBlock(SDIO_DATA_BUFFER,lsector+512*n,512);//單個sector的讀操作memcpy(buf,SDIO_DATA_BUFFER,512);buf+=512;} }else{if(cnt==1)sta=SD_ReadBlock(buf,lsector,512); //單個sector的讀操作else sta=SD_ReadMultiBlocks(buf,lsector,512,cnt);//多個sector }return sta; }6、SD卡寫操作
//寫SD卡 //buf:寫數據緩存區 //sector:扇區地址 //cnt:扇區個數 //返回值:錯誤狀態;0,正常;其他,錯誤代碼; u8 SD_WriteDisk(u8*buf,u32 sector,u8 cnt) {u8 sta=SD_OK;u8 n;long long lsector=sector;lsector<<=9;if((u32)buf%4!=0){for(n=0;n<cnt;n++){memcpy(SDIO_DATA_BUFFER,buf,512);sta=SD_WriteBlock(SDIO_DATA_BUFFER,lsector+512*n,512);//單個sector的寫操作buf+=512;} }else{if(cnt==1)sta=SD_WriteBlock(buf,lsector,512); //單個sector的寫操作else sta=SD_WriteMultiBlocks(buf,lsector,512,cnt); //多個sector }return sta; }SD卡的應用就講到這里啦!
感謝觀看!!!
總結
- 上一篇: Java基础书籍推荐
- 下一篇: 连接SSL证书文件