了解STM32F103ZET是高容量多管腳的芯片
了解SD讀寫線路圖
了解SD的基地址
閱讀STM32F10xx英文參考 SDIO那章,我們編寫代碼邊看文檔解析
建工程,打開包含所有包括外設(shè)庫函數(shù)的樣本工程,然后留下如下的外設(shè)
在stm32_eval_sdio_sd.c(我改名為bsp_sdio_sdcard.c)添加新的功能函數(shù)
- 宏定義:sd外設(shè)地址(stm32_eval_sdio_sd.h)
/** @defgroup STM32_EVAL_SDIO_SD_Exported_Constants* @{*/ /*宏定義*/
#define SDIO_FIFO_ADDRESS ((uint32_t)0x40018080) //SDIO_FIOF地址=SDIO地址+0x80至 sdio地址+0xfc
/** * @brief SDIO Intialization Frequency (400KHz max)*/
#define SDIO_INIT_CLK_DIV ((uint8_t)0xB2)
/** * @brief SDIO Data Transfer Frequency (25MHz max) */
/*!< SDIOCLK = HCLK, SDIO_CK = HCLK/(2 + SDIO_TRANSFER_CLK_DIV) */
#define SDIO_TRANSFER_CLK_DIV ((uint8_t)0x01)
static void GPIO_Configuration(void)
{GPIO_InitTypeDef GPIO_InitStructure;//使能gpio時鐘,判斷APB還是AHB,看System architecture圖(PDF搜)RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOC,ENABLE);//配置pc8,pc9,pc10,pc11,pc12為D0,D0,D2,D3,D4,CLK,看電路線路圖GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 |GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF_PP;GPIO_Init(GPIOC,&GPIO_InitStructure)//配置PD2 CMD引腳GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;GPIO_Init(GPIOD,&GPIO_InitStructure);//使能SDIO AHB時鐘RCC_AHBPeriphClockCmd(RCC_AHBPeriph_SDIO,ENABLE);//使能DMARCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2,ENABLE);
}
- DMA 配置(為啥選擇DMA2的channel4,DMA2的選擇是看System architecture,channel4看DMA2 request mapping)
/**
配置好dma2,一發(fā)現(xiàn)有中斷,就自動傳輸 Rx
**/
void SD_DMA_RxConfig(uint32_t *BufferDST, uint32_t BufferSize)
{DMA_InitTypeDef DMA_InitStructure;//清除標志位DMA_ClearFlag(DMA2_FLAG_TC4 | DMA2_FLAG_TE4 |DMA2_FLAG_HT4 | DMA2_FLAG_GL4);//禁止DMADMA_Cmd(DMA2_Channel4,DISABLE);//傳輸配置//外設(shè)地址,fifoDMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SDIO_FIFO_ADDRESS;//目標地址DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)BufferDST;//傳輸方向DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//把字轉(zhuǎn)為字節(jié)DMA_InitStructure.DMA_BufferSize = BufferSize / 4;//存儲地址自增DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//不循環(huán) When circular mode is activated, the number of data to be transferred is automatically reloaded with the initial value programmed during the channel configuration phase, and the DMA requests continue to be served.DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//外設(shè)數(shù)據(jù)大小為字, 32 位DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;//存儲數(shù)據(jù)大小為字, 32 位DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;//通道優(yōu)先級高DMA_InitStructure.DMA_Priority = DMA_Priority_High;//外設(shè)地址不自增DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//非 存儲器至存儲器模式 The DMA channels can also work without being triggered by a request from a peripheral. This mode is called Memory to Memory mode.DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA4_Channel4,&DMA_InitStructure); /*!< 使能 DMA 通道 */DMA_Cmd(DMA2_Channel4, ENABLE);
}
/**
配置好dma2,一發(fā)現(xiàn)有中斷,就自動傳輸 Tx
**/
void SD_DMA_TxConfig(uint32_t *BufferSRC, uint32_t BufferSize)
{DMA_InitTypeDef DMA_InitStructure;DMA_ClearFlag(DMA2_FLAG_TC4 | DMA2_FLAG_TE4 | DMA2_FLAG_HT4 | DMA2_FLAG_GL4);/*!< DMA2 Channel4 disable */DMA_Cmd(DMA2_Channel4, DISABLE);/*!< DMA2 Channel4 Config */DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SDIO_FIFO_ADDRESS;DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)BufferSRC;DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;//外設(shè)為寫入目標DMA_InitStructure.DMA_BufferSize = BufferSize / 4;DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外設(shè)地址不自增DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;DMA_InitStructure.DMA_Priority = DMA_Priority_High;DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;DMA_Init(DMA2_Channel4, &DMA_InitStructure);/*!< DMA2 Channel4 enable */DMA_Cmd(DMA2_Channel4, ENABLE);
}
- 打開stm32_eval_sdio_sd.h文件中發(fā)現(xiàn)很多枚舉定義,等
- 枚舉:SD_Error、 SDTransferState 和 SDCardState
- 結(jié)構(gòu)體定義: SD_CSD、 SD_CID、 SD_CardStatus 以及 SD_CardInfo
- 宏定義:命令號定義、 SDIO 傳輸方式、 SD 卡插入狀態(tài)以及 SD 卡類型定義。
接下來我們就開始根據(jù) SD 卡識別過程和數(shù)據(jù)傳輸過程理解 SD 卡驅(qū)動函數(shù)代碼。這部分代碼內(nèi)容也是非常龐大,不可能全部在文檔中全部列出,對于部分函數(shù)只介紹其功能。
static void NVIC_Configuration(void)
{NVIC_InitTypeDef NVIC_InitStructure;/* Configure the NVIC Preemption Priority Bits */NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);//SDIO的中斷請求 配置好NVIC的中斷控制器和中斷來,判斷誰的優(yōu)先級高(假設(shè)啟動多個中斷)。先配NVIC,在配外部中斷器來屏蔽--硬件或軟件(事件或中斷)NVIC_InitStructure.NVIC_IRQChannel = SDIO_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;NVIC_Init(&NVIC_InitStructure);//SDIO_IRQ不需要外部中斷/事件或軟件中斷/事件,因此不需要初始化EXIT控制器的全部寄存器,比如中斷屏蔽寄存器、事件屏蔽寄存器,看圖External interrupt/event controller block diagram
}
/**描述 :初始化SD卡,使卡處于就緒狀態(tài)(準備傳輸數(shù)據(jù))*/
- 此函數(shù)原stm32_eval_sdio_sd.c有,不需添加,需要修改
SD_Error SD_Init(void)
{/*重置SD_Error狀態(tài)*/SD_Error errorstatus = SD_OK;NVIC_Configuration();/* SDIO 外設(shè)底層引腳初始化 */GPIO_Configuration();/*對SDIO的所有寄存器進行復(fù)位*/SDIO_DeInit(); /*上電并進行卡識別流程,確認卡的操作電壓 */errorstatus = SD_PowerON(); /*如果上電,識別不成功,返回“響應(yīng)超時”錯誤 */if (errorstatus != SD_OK){/*!< CMD Response TimeOut (wait for CMDSENT flag) */return(errorstatus); }/*卡識別成功,進行卡初始化 */errorstatus = SD_InitializeCards(); if (errorstatus != SD_OK) //失敗返回{/*!< CMD Response TimeOut (wait for CMDSENT flag) */return(errorstatus);}/*!< Configure the SDIO peripheral *//*!< on STM32F2xx devices, SDIOCLK is fixed to 48MHz */ /*!< SDIOCLK = HCLK, SDIO_CK = HCLK/(2 + SDIO_TRANSFER_CLK_DIV) *///重新配置 SDIO 外設(shè),提高時鐘頻率,由卡識別模式的400khz提升到數(shù)據(jù)傳輸模式小于25MhzSDIO_InitStructure.SDIO_ClockDiv = SDIO_TRANSFER_CLK_DIV;//上升沿采集數(shù)據(jù) SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising;// 不使能Bypass,使SDIO_CK經(jīng)過SDIO_ClockDiv分頻SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable;//開啟的話,總線空閑時關(guān)閉SD_CLK 時鐘SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable;//暫時配置成lbit模式SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_1b;//硬件流。若開啟,在FIFO不能進行發(fā)送和接受數(shù)據(jù)是,數(shù)據(jù)暫停SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable;SDIO_Init(&SDIO_InitStructure);if (errorstatus == SD_OK){/*----------------- Read CSD/CID MSD registers ------------------*///用來讀取csd/cid寄存器/*調(diào)用 SD_GetCardInfo 函數(shù)獲取 SD 卡信息,它需要一個指向 SD_CardInfo 類型變
量地址的指針形參,這里賦值為 SDCardInfo 變量的地址。 SD 卡信息主要是 CID
和 CSD 寄存器內(nèi)容,這兩個寄存器內(nèi)容在 SD_InitializeCards 函數(shù)中都完成讀取過
程并將其分別存放在 CID_Tab 數(shù)組和 CSD_Tab 數(shù)組中,所以 SD_GetCardInfo 函
數(shù)只是簡單的把這兩個數(shù)組內(nèi)容整合復(fù)制到 SDCardInfo 變量對應(yīng)成員內(nèi)。正確執(zhí)
行 SD_GetCardInfo 函數(shù)后, SDCardInfo 變量就存放了 SD 卡的很多狀態(tài)信息,這
在之后應(yīng)用中使用頻率是很高的。*/errorstatus = SD_GetCardInfo(&SDCardInfo);}if (errorstatus == SD_OK){/*----------------- Select Card --------------------------------*///通過cm7,rca選擇要操作的卡errorstatus = SD_SelectDeselect((uint32_t) (SDCardInfo.RCA << 16));}if (errorstatus == SD_OK){//提高讀寫,開啟4bit模式errorstatus = SD_EnableWideBusOperation(SDIO_BusWide_4b);} return(errorstatus);
}
- 調(diào)用 SD_PowerON 函數(shù),它用于查詢卡的工作電壓和時鐘控制配置,并返回SD_Error 類型錯誤,該函數(shù)是整個 SD 識別精髓,
- 此函數(shù)原stm32_eval_sdio_sd.c有,不需添加
//確保 SD 卡的工作電壓和配置控制時鐘
SD_Error SD_PowerON(void)
{SD_Error errorstatus = SD_OK;uint32_t response = 0, count = 0, validvoltage = 0;uint32_t SDType = SD_STD_CAPACITY;/*!< Power ON Sequence -----------------------------------------------------*//*!< Configure the SDIO peripheral *//*!< SDIOCLK = HCLK, SDIO_CK = HCLK/(2 + SDIO_INIT_CLK_DIV) *//*!< on STM32F2xx devices, SDIOCLK is fixed to 48MHz *//*!< SDIO_CK for initialization should not exceed 400 KHz */ SDIO_InitStructure.SDIO_ClockDiv = SDIO_INIT_CLK_DIV;SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising;SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable;SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable;// 初始化的時候暫時把數(shù)據(jù)線配置成一根SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_1b;//禁止硬件流控制SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable;SDIO_Init(&SDIO_InitStructure);/*!< Set Power State to ON *///開啟外設(shè)電源SDIO_SetPowerState(SDIO_PowerState_ON);/*!< Enable SDIO Clock *///使能SDIO時鐘SDIO_ClockCmd(ENABLE);/*!< CMD0: GO_IDLE_STATE ---------------------------------------------------*//*!< No CMD response required *///發(fā)送一系列命令。開始卡的識別流程SDIO_CmdInitStructure.SDIO_Argument = 0x0;SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_GO_IDLE_STATE;//設(shè)置具體的返回類型,SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_No;//SDIO是否開啟或關(guān)閉等待中斷SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;/* CPSM 在開始發(fā)送命令之前等待數(shù)據(jù)傳輸結(jié)束 */SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;SDIO_SendCommand(&SDIO_CmdInitStructure);//檢測是否正確接收CM0 , CmdError 函數(shù)用于無需響應(yīng)的命令發(fā)送errorstatus = CmdError();if (errorstatus != SD_OK){/*!< CMD Response TimeOut (wait for CMDSENT flag) *///響應(yīng)超時return(errorstatus);}/*!< CMD8: SEND_IF_COND ----------------------------------------------------*//*!< Send CMD8 to verify SD card interface operating condition *//*!< Argument: - [31:12]: Reserved (shall be set to '0')- [11:8]: Supply Voltage (VHS) 0x1 (Range: 2.7-3.6 V)- [7:0]: Check Pattern (recommended 0xAA) *//*!< CMD Response: R7 *///發(fā)送CMD8檢查SD卡電壓操作 //發(fā)送 CMD8 命令,檢測 SD 卡支持的操作條件,主要就是電壓匹配, CMD8 的響//應(yīng)類型是 R7,使用 CmdResp7Error 函數(shù)可獲取得到 R7 響應(yīng)結(jié)果,它是通過檢測//SDIO_STA 寄 存 器 相 關(guān) 位 完 成 的 , 并 具 有 等 待 超 時 檢 測 功 能 。 SDIO_CmdInitStructure.SDIO_Argument = SD_CHECK_PATTERN;SDIO_CmdInitStructure.SDIO_CmdIndex = SDIO_SEND_IF_COND;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);//檢測是否正確接收 ,Checks for error conditions for R7 response. 搜索R7 (Card interface condition)errorstatus = CmdResp7Error();if (errorstatus == SD_OK){CardType = SDIO_STD_CAPACITY_SD_CARD_V2_0; /*!< SD Card 2.0 */SDType = SD_HIGH_CAPACITY;}else //無響應(yīng),說明1.x{/*!< CMD55 */SDIO_CmdInitStructure.SDIO_Argument = 0x00;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);errorstatus = CmdResp1Error(SD_CMD_APP_CMD);}/*!< CMD55 *///發(fā)送CMD55,用于檢測sd卡還是mmc卡,或者不支持的卡SDIO_CmdInitStructure.SDIO_Argument = 0x00;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);//是否響應(yīng),沒響應(yīng)就是mmc或不支持的卡errorstatus = CmdResp1Error(SD_CMD_APP_CMD);/*!< If errorstatus is Command TimeOut, it is a MMC card *//*!< If errorstatus is SD_OK it is a SD card: SD card 2.0 (voltage range mismatch)or SD card 1.x */if (errorstatus == SD_OK) //響應(yīng)cmd44.是sd卡,可能為1.x也可能2.x{//下面,循環(huán)發(fā)送sdio支持的電壓范圍,/*!< SD CARD *//*!< Send ACMD41 SD_APP_OP_COND with Argument 0x80100000 */while ((!validvoltage) && (count < SD_MAX_VOLT_TRIAL)){// 在發(fā)送 ACMD 命令前都要先向卡發(fā)送 CMD55 ,CMD55用于指示下一條指令是應(yīng)用指令/*!< SEND CMD55 APP_CMD with RCA as 0 */SDIO_CmdInitStructure.SDIO_Argument = 0x00;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);errorstatus = CmdResp1Error(SD_CMD_APP_CMD);if (errorstatus != SD_OK){return(errorstatus);}/* ACMD41 ,確定卡是不是 SDSC 還是 SDHC,返回R3(就是OCR寄存器),需要用CmdResp3Error返回狀態(tài),主要從OCR寄存器31位0或1來判斷那種類型* 命令參數(shù)由支持的電壓范圍及 HCS 位組成, HCS 位置一來區(qū)分卡是 SDSC 還是 SDHC* 0:SDSC* 1:SDHC* 響應(yīng): R3,對應(yīng)的是 OCR 寄存器*//*使用 ACMD41 命令判斷卡的具體類型。因為是 A 類命令,所以在發(fā)送 ACMD41
之前必須先發(fā)送 CMD55, CMD55 命令的響應(yīng)類型的 R1。如果 CMD55 命令都沒
有響應(yīng)說明是 MMC 卡或不可用卡。在正確發(fā)送 CMD55 之后就可以發(fā)送
ACMD41,并根據(jù)響應(yīng)判斷卡類型, ACMD41 的響應(yīng)號為 R3, CmdResp3Error 函
數(shù)用于檢測命令正確發(fā)送并帶有超時檢測功能,但并不具備響應(yīng)內(nèi)容接收功能,
需要在判定命令正確發(fā)送之后調(diào)用 SDIO_GetResponse 函數(shù)才能獲取響應(yīng)的內(nèi)容。*/SDIO_CmdInitStructure.SDIO_Argument = SD_VOLTAGE_WINDOW_SD | SDType;//0x80100000SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_APP_OP_COND;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 = CmdResp3Error();if (errorstatus != SD_OK){return(errorstatus);}/* 若卡需求電壓在 SDIO 的供電電壓范圍內(nèi),會自動上電并標志 pwr_up 位* 讀取卡寄存器,卡狀態(tài)*/response = SDIO_GetResponse(SDIO_RESP1);/* 讀取卡的 ocr 寄存器的 pwr_up 位,看是否已工作在正常電壓 */validvoltage = (((response >> 31) == 1) ? 1 : 0);count++; //計算循環(huán)}//結(jié)束循環(huán)// 循環(huán)檢測超過一定次數(shù)還沒上電if (count >= SD_MAX_VOLT_TRIAL){// SDIO 不支持 card 的供電電壓errorstatus = SD_INVALID_VOLTRANGE;return(errorstatus);}/*檢查卡返回信息中的 HCS 位*//* 判斷 ocr 中的 ccs 位 ,如果是 sdsc 卡則不執(zhí)行下面的語句 */if (response &= SD_HIGH_CAPACITY) //判斷30位是否為1{CardType = SDIO_HIGH_CAPACITY_SD_CARD;}}/*!< else MMC Card */return(errorstatus);
}
到此,程序執(zhí)行SD卡的SD模式流程圖執(zhí)行到如下圖、
判斷執(zhí)行 SD_PowerON 函數(shù)無錯誤后,執(zhí)行下面的 SD_InitializeCards 函數(shù)進行與 SD 卡相關(guān)的初始化,使得卡進入數(shù)據(jù)傳輸模式下的待機模式。
- 此函數(shù)原stm32_eval_sdio_sd.c有,不需添加
//描述 :初始化所有的卡或者單個卡進入就緒狀態(tài)
SD_Error SD_InitializeCards(void)
{SD_Error errorstatus = SD_OK;uint16_t rca = 0x01;if (SDIO_GetPowerState() == SDIO_PowerState_OFF){errorstatus = SD_REQUEST_NOT_APPLICABLE;return(errorstatus);}//判斷卡的類型if (SDIO_SECURE_DIGITAL_IO_CARD != CardType){/*!< Send CMD2 ALL_SEND_CID 響應(yīng): R2,對應(yīng) CID 寄存器*/SDIO_CmdInitStructure.SDIO_Argument = 0x0;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);errorstatus = CmdResp2Error();if (SD_OK != errorstatus){return(errorstatus);}/* 將返回的 CID 信息存儲起來 CID_Tab已經(jīng)定義好了,不用我們自己,直接用 */CID_Tab[0] = SDIO_GetRespon se(SDIO_RESP1);CID_Tab[1] = SDIO_GetResponse(SDIO_RESP2);CID_Tab[2] = SDIO_GetResponse(SDIO_RESP3);CID_Tab[3] = SDIO_GetResponse(SDIO_RESP4);}if ((SDIO_STD_CAPACITY_SD_CARD_V1_1 == CardType) || (SDIO_STD_CAPACITY_SD_CARD_V2_0 == CardType) || (SDIO_SECURE_DIGITAL_IO_COMBO_CARD == CardType)|| (SDIO_HIGH_CAPACITY_SD_CARD == CardType)){/*!< Send CMD3 SET_REL_ADDR with argument 0* 要求各個 SD 卡返回自身的 RCA 地址. *//*!< SD Card publishes its RCA. */SDIO_CmdInitStructure.SDIO_Argument = 0x00;SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_REL_ADDR;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 = CmdResp6Error(SD_CMD_SET_REL_ADDR, &rca);if (SD_OK != errorstatus){return(errorstatus);}}/*******************************************************************/if (SDIO_SECURE_DIGITAL_IO_CARD != CardType){RCA = rca;/*!< Send CMD9 SEND_CSD with argument as card's RCA 響應(yīng):R2 對應(yīng)寄存器 CSD(Card-Specific Data)*/SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)(rca << 16);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();if (SD_OK != errorstatus){return(errorstatus);}CSD_Tab[0] = SDIO_GetResponse(SDIO_RESP1);CSD_Tab[1] = SDIO_GetResponse(SDIO_RESP2);CSD_Tab[2] = SDIO_GetResponse(SDIO_RESP3);CSD_Tab[3] = SDIO_GetResponse(SDIO_RESP4);}/*全部卡初始化成功 */errorstatus = SD_OK; /*!< All cards get intialized */return(errorstatus);
}
;執(zhí)行 SD_InitializeCards 函數(shù)無錯誤后 SD 卡就已經(jīng)處于數(shù)據(jù)傳輸模式下的待機狀態(tài),退出 SD_InitializeCards 后會返回前面的 SD_Init 函數(shù),執(zhí)行接下來代碼,以下是 SD_Init 函數(shù)的后續(xù)執(zhí)行過程。執(zhí)行之后,卡就從待機狀態(tài)轉(zhuǎn)變?yōu)閭鬏斈J?#xff0c;可以說數(shù)據(jù)傳輸已經(jīng)是萬事俱備了。
SD 卡數(shù)據(jù)操作:包括數(shù)據(jù)讀取、數(shù)據(jù)寫入以及存儲區(qū)擦除。數(shù)據(jù)讀取和寫入都可以分為單塊操作和多塊操作。
- 擦除函數(shù)
- 此函數(shù)原stm32_eval_sdio_sd.c有,不需添加
SD_Error SD_Erase(uint32_t startaddr, uint32_t endaddr)
{SD_Error errorstatus = SD_OK;uint32_t delay = 0;__IO uint32_t maxdelay = 0;uint8_t cardstate = 0;/*!< Check if the card coomnd class supports erase command */if (((CSD_Tab[1] >> 20) & SD_CCCC_ERASE) == 0){errorstatus = SD_REQUEST_NOT_APPLICABLE;return(errorstatus);}maxdelay = 120000 / ((SDIO->CLKCR & 0xFF) + 2);if (SDIO_GetResponse(SDIO_RESP1) & SD_CARD_LOCKED) //卡已上鎖{errorstatus = SD_LOCK_UNLOCK_FAILED;return(errorstatus);}if (CardType == SDIO_HIGH_CAPACITY_SD_CARD){//在 sdhc 卡中,地址參數(shù)為塊地址,每塊 512 字節(jié),而 sdsc 卡地址為字節(jié)地址//所以若是 sdhc 卡要對地址/512 進行轉(zhuǎn)換startaddr /= 512;endaddr /= 512;}/*!< According to sd-card spec 1.0 ERASE_GROUP_START (CMD32) and erase_group_end(CMD33) */if ((SDIO_STD_CAPACITY_SD_CARD_V1_1 == CardType) || (SDIO_STD_CAPACITY_SD_CARD_V2_0 == CardType) || (SDIO_HIGH_CAPACITY_SD_CARD == CardType)){/*!< Send CMD32 SD_ERASE_GRP_START with argument as addr */SDIO_CmdInitStructure.SDIO_Argument = startaddr;SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_ERASE_GRP_START;SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short; //R1SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;SDIO_SendCommand(&SDIO_CmdInitStructure);errorstatus = CmdResp1Error(SD_CMD_SD_ERASE_GRP_START);if (errorstatus != SD_OK){return(errorstatus);}/*!< Send CMD33 SD_ERASE_GRP_END with argument as addr */SDIO_CmdInitStructure.SDIO_Argument = endaddr;SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_ERASE_GRP_END;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_SD_ERASE_GRP_END);if (errorstatus != SD_OK){return(errorstatus);}}/*!< Send CMD38 ERASE */SDIO_CmdInitStructure.SDIO_Argument = 0;SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_ERASE;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_ERASE);if (errorstatus != SD_OK){return(errorstatus);}for (delay = 0; delay < maxdelay; delay++){}/*!< Wait till the card is in programming state */errorstatus = IsCardProgramming(&cardstate);while ((errorstatus == SD_OK) && ((SD_CARD_PROGRAMMING == cardstate) || (SD_CARD_RECEIVING == cardstate))){errorstatus = IsCardProgramming(&cardstate);}return(errorstatus);
}
SD_WriteBlock 函數(shù)用于向指定的目標地址寫入一個塊的數(shù)據(jù),它有三個形參,分別為指向待寫入數(shù)據(jù)的首地址的指針變量、目標寫入地址和塊大小。塊大小一般都設(shè)置為512 字節(jié)。 (函數(shù)不用自己添加,但需要修改)
SD_Error SD_WriteBlock(uint8_t *writebuff, uint64_t WriteAddr, uint16_t BlockSize)
{SD_Error errorstatus = SD_OK;#if defined (SD_POLLING_MODE)uint32_t bytestransferred = 0, count = 0, restwords = 0;uint32_t *tempbuff = (uint32_t *)writebuff;
#endifTransferError = SD_OK;TransferEnd = 0;StopCondition = 0;SDIO->DCTRL = 0x0;if (CardType == SDIO_HIGH_CAPACITY_SD_CARD){BlockSize = 512;WriteAddr /= 512;}/*-------------- add , 沒有這一段容易卡死在DMA檢測中 -------------------*//* Set Block Size for Card,cmd16,* 若是sdsc卡,可以用來設(shè)置塊大小,* 若是sdhc卡,塊大小為512字節(jié),不受cmd16影響 */SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) BlockSize;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);errorstatus = CmdResp1Error(SD_CMD_SET_BLOCKLEN);if (SD_OK != errorstatus){return(errorstatus);}/*********************************************************************************//*!< Send CMD24 WRITE_SINGLE_BLOCK */SDIO_CmdInitStructure.SDIO_Argument = WriteAddr; //寫入地址SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_WRITE_SINGLE_BLOCK;SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short; //r1SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;SDIO_SendCommand(&SDIO_CmdInitStructure);errorstatus = CmdResp1Error(SD_CMD_WRITE_SINGLE_BLOCK);if (errorstatus != SD_OK){return(errorstatus);}//配置sdio的寫數(shù)據(jù)寄存器SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;SDIO_DataInitStructure.SDIO_DataLength = BlockSize;SDIO_DataInitStructure.SDIO_DataBlockSize = (uint32_t) 9 << 4; //可用此參數(shù)代替SDIO_DataBlockSize_512bSDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToCard;//寫數(shù)據(jù),SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block;SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Enable; //開啟數(shù)據(jù)通道狀態(tài)機SDIO_DataConfig(&SDIO_DataInitStructure);/*!< In case of single data block transfer no need of stop command at all */
#if defined (SD_POLLING_MODE) //普通模式while (!(SDIO->STA & (SDIO_FLAG_DBCKEND | SDIO_FLAG_TXUNDERR | SDIO_FLAG_DCRCFAIL | SDIO_FLAG_DTIMEOUT | SDIO_FLAG_STBITERR))){if (SDIO_GetFlagStatus(SDIO_FLAG_TXFIFOHE) != RESET){if ((512 - bytestransferred) < 32){restwords = ((512 - bytestransferred) % 4 == 0) ? ((512 - bytestransferred) / 4) : (( 512 - bytestransferred) / 4 + 1);for (count = 0; count < restwords; count++, tempbuff++, bytestransferred += 4){SDIO_WriteData(*tempbuff);}}else{for (count = 0; count < 8; count++){SDIO_WriteData(*(tempbuff + count));}tempbuff += 8;bytestransferred += 32;}}}if (SDIO_GetFlagStatus(SDIO_FLAG_DTIMEOUT) != RESET){SDIO_ClearFlag(SDIO_FLAG_DTIMEOUT);errorstatus = SD_DATA_TIMEOUT;return(errorstatus);}else if (SDIO_GetFlagStatus(SDIO_FLAG_DCRCFAIL) != RESET){SDIO_ClearFlag(SDIO_FLAG_DCRCFAIL);errorstatus = SD_DATA_CRC_FAIL;return(errorstatus);}else if (SDIO_GetFlagStatus(SDIO_FLAG_TXUNDERR) != RESET){SDIO_ClearFlag(SDIO_FLAG_TXUNDERR);errorstatus = SD_TX_UNDERRUN;return(errorstatus);}else if (SDIO_GetFlagStatus(SDIO_FLAG_STBITERR) != RESET){SDIO_ClearFlag(SDIO_FLAG_STBITERR);errorstatus = SD_START_BIT_ERR;return(errorstatus);}
#elif defined (SD_DMA_MODE) //dma模式SDIO_ITConfig(SDIO_IT_DATAEND, ENABLE); //數(shù)據(jù)傳輸結(jié)束中斷SD_DMA_TxConfig((uint32_t *)writebuff, BlockSize); //配置dma,跟rx類似SDIO_DMACmd(ENABLE); // 使能sdio的dma請求
#endifreturn(errorstatus);
}
SD_WaitWriteOperation 函數(shù)用于檢測和等待數(shù)據(jù)寫入完成,在調(diào)用數(shù)據(jù)寫入函數(shù)之后一般都需要調(diào)用, SD_WaitWriteOperation 函數(shù)適用于單塊及多塊寫入函數(shù)。
/*** 上述代碼調(diào)用庫函數(shù) SD_DMAEndOfTransferStatus 一直檢測 DMA 的傳輸完成標志,
當 DMA 傳輸結(jié)束時,該函數(shù)會返回 SET 值。另外, while 循環(huán)中的判斷條件使用的
TransferEnd 和 TransferError 是全局變量,它們會在 SDIO 的中斷服務(wù)函數(shù)根據(jù)傳輸情況被
設(shè)置, 傳輸結(jié)束后,根據(jù) TransferError 的值來確認是否正確傳輸,若不正確則直接返回錯
誤代碼。 SD_WaitWriteOperation 函數(shù)最后是清除相關(guān)標志位并返回錯誤。由于這個函數(shù)里
的 while 循環(huán)的存在, 它會確保 DMA 的傳輸結(jié)束。*/
SD_Error SD_WaitWriteOperation(void)
{SD_Error errorstatus = SD_OK;//等待dma是否傳輸while ((SD_DMAEndOfTransferStatus() == RESET) && (TransferEnd == 0) && (TransferError == SD_OK)){}if (TransferError != SD_OK){return(TransferError);}/*!< Clear all the static flags */SDIO_ClearFlag(SDIO_STATIC_FLAGS);return(errorstatus);
}
SD_ReadBlock函數(shù):
SD_Error SD_ReadBlock(uint8_t *readbuff, uint64_t ReadAddr, uint16_t BlockSize)
{SD_Error errorstatus = SD_OK;
#if defined (SD_POLLING_MODE) uint32_t count = 0, *tempbuff = (uint32_t *)readbuff;
#endifTransferError = SD_OK;TransferEnd = 0; //傳輸結(jié)束標置位,在中斷服務(wù)置1StopCondition = 0; SDIO->DCTRL = 0x0;if (CardType == SDIO_HIGH_CAPACITY_SD_CARD){BlockSize = 512;ReadAddr /= 512;}/*******************add,沒有這一段容易卡死在DMA檢測中*************************************//* Set Block Size for Card,cmd16,* 若是sdsc卡,可以用來設(shè)置塊大小,* 若是sdhc卡,塊大小為512字節(jié),不受cmd16影響 */SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) BlockSize;SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN;SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short; //r1SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;SDIO_SendCommand(&SDIO_CmdInitStructure);errorstatus = CmdResp1Error(SD_CMD_SET_BLOCKLEN);if (SD_OK != errorstatus){return(errorstatus);}/*********************************************************************************/SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;SDIO_DataInitStructure.SDIO_DataLength = BlockSize;SDIO_DataInitStructure.SDIO_DataBlockSize = (uint32_t) 9 << 4;SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToSDIO;SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block;SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Enable;SDIO_DataConfig(&SDIO_DataInitStructure);/*!< Send CMD17 READ_SINGLE_BLOCK */SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)ReadAddr;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);errorstatus = CmdResp1Error(SD_CMD_READ_SINGLE_BLOCK);if (errorstatus != SD_OK){return(errorstatus);}#if defined (SD_POLLING_MODE) /*!< In case of single block transfer, no need of stop transfer at all.*//*!< Polling mode */while (!(SDIO->STA &(SDIO_FLAG_RXOVERR | SDIO_FLAG_DCRCFAIL | SDIO_FLAG_DTIMEOUT | SDIO_FLAG_DBCKEND | SDIO_FLAG_STBITERR))){if (SDIO_GetFlagStatus(SDIO_FLAG_RXFIFOHF) != RESET){for (count = 0; count < 8; count++){*(tempbuff + count) = SDIO_ReadData();}tempbuff += 8;}}if (SDIO_GetFlagStatus(SDIO_FLAG_DTIMEOUT) != RESET){SDIO_ClearFlag(SDIO_FLAG_DTIMEOUT);errorstatus = SD_DATA_TIMEOUT;return(errorstatus);}else if (SDIO_GetFlagStatus(SDIO_FLAG_DCRCFAIL) != RESET){SDIO_ClearFlag(SDIO_FLAG_DCRCFAIL);errorstatus = SD_DATA_CRC_FAIL;return(errorstatus);}else if (SDIO_GetFlagStatus(SDIO_FLAG_RXOVERR) != RESET){SDIO_ClearFlag(SDIO_FLAG_RXOVERR);errorstatus = SD_RX_OVERRUN;return(errorstatus);}else if (SDIO_GetFlagStatus(SDIO_FLAG_STBITERR) != RESET){SDIO_ClearFlag(SDIO_FLAG_STBITERR);errorstatus = SD_START_BIT_ERR;return(errorstatus);}while (SDIO_GetFlagStatus(SDIO_FLAG_RXDAVL) != RESET){*tempbuff = SDIO_ReadData();tempbuff++;}/*!< Clear all the static flags */SDIO_ClearFlag(SDIO_STATIC_FLAGS);#elif defined (SD_DMA_MODE)SDIO_ITConfig(SDIO_IT_DATAEND, ENABLE);SDIO_DMACmd(ENABLE);SD_DMA_RxConfig((uint32_t *)readbuff, BlockSize);
#endifreturn(errorstatus);
}
其他讀寫操作函數(shù)雷同,不用修改。但需要把最后面4句話替換成:
到此,SD卡初始化完成了,以上只是介紹重要那部分的函數(shù),其他有些細微的修改,先引用,請點擊:SD讀寫測試
設(shè)置SDIO 中斷服務(wù)函數(shù)(stm32f10x_it.c)
// 在 SDIO_ITConfig()這個函數(shù)開啟了 sdio 中斷 ,
void SDIO_IRQHandler(void)
{//SDIO中斷相關(guān)處理SD_ProcessIRQSrc(); //定義在bsp_sdio_sdcard.c}
/** 函數(shù)名:SD_ProcessIRQSrc* 描述 :數(shù)據(jù)傳輸結(jié)束中斷* 輸入 :無 * 輸出 :SD錯誤類型*/
SD_Error SD_ProcessIRQSrc(void)
{if (StopCondition == 1) //發(fā)送讀取、多塊讀寫命令時置1{SDIO->ARG = 0x0; //命令參數(shù)寄存器SDIO->CMD = 0x44C; // 命令寄存器: 0100 01 001100// [7:6] [5:0]// CPSMEN WAITRESP CMDINDEX// 開啟命令狀態(tài)機 短響應(yīng) cmd12 STOP_ TRANSMISSION TransferError = CmdResp1Error(SD_CMD_STOP_TRANSMISSION);}else{TransferError = SD_OK;}SDIO_ClearITPendingBit(SDIO_IT_DATAEND); //清中斷SDIO_ITConfig(SDIO_IT_DATAEND, DISABLE); //關(guān)閉sdio中斷使能TransferEnd = 1;return(TransferError);
}
至此,我們已經(jīng)介紹了 SD 卡初始化、 SD 卡數(shù)據(jù)操作的基礎(chǔ)功能函數(shù)以及 SDIO 相關(guān)中斷服務(wù)函數(shù)內(nèi)容,利用這個 SDIO 驅(qū)動,可以編寫一些簡單的 SD 卡讀寫測試程序。
測試 SD 卡部分的函數(shù)是我們自己編寫的,存放在 sdio_test.c 文件等。
最后附上我編寫的程序代碼,主要實現(xiàn)串口輸入內(nèi)容,然后保存到sd卡中。再讀取出來,輸出到串口那里。
點擊下載:【sd卡讀取】
轉(zhuǎn)載于:https://www.cnblogs.com/guguobao/p/10123755.html
總結(jié)
以上是生活随笔為你收集整理的使用STM32F103ZET霸道主板实现SD卡的读写(非文件系统)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。