了解STM32F103ZET是高容量多管腳的芯片
了解SD讀寫線路圖
了解SD的基地址
閱讀STM32F10xx英文參考 SDIO那章,我們編寫代碼邊看文檔解析
建工程,打開包含所有包括外設庫函數的樣本工程,然后留下如下的外設
在stm32_eval_sdio_sd.c(我改名為bsp_sdio_sdcard.c)添加新的功能函數
- 宏定義:sd外設地址(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,一發現有中斷,就自動傳輸 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);//傳輸配置//外設地址,fifoDMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SDIO_FIFO_ADDRESS;//目標地址DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)BufferDST;//傳輸方向DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//把字轉為字節DMA_InitStructure.DMA_BufferSize = BufferSize / 4;//存儲地址自增DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//不循環 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;//外設數據大小為字, 32 位DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;//存儲數據大小為字, 32 位DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;//通道優先級高DMA_InitStructure.DMA_Priority = DMA_Priority_High;//外設地址不自增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,一發現有中斷,就自動傳輸 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;//外設為寫入目標DMA_InitStructure.DMA_BufferSize = BufferSize / 4;DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外設地址不自增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文件中發現很多枚舉定義,等
- 枚舉:SD_Error、 SDTransferState 和 SDCardState
- 結構體定義: SD_CSD、 SD_CID、 SD_CardStatus 以及 SD_CardInfo
- 宏定義:命令號定義、 SDIO 傳輸方式、 SD 卡插入狀態以及 SD 卡類型定義。
接下來我們就開始根據 SD 卡識別過程和數據傳輸過程理解 SD 卡驅動函數代碼。這部分代碼內容也是非常龐大,不可能全部在文檔中全部列出,對于部分函數只介紹其功能。
static void NVIC_Configuration(void)
{NVIC_InitTypeDef NVIC_InitStructure;/* Configure the NVIC Preemption Priority Bits */NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);//SDIO的中斷請求 配置好NVIC的中斷控制器和中斷來,判斷誰的優先級高(假設啟動多個中斷)。先配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卡,使卡處于就緒狀態(準備傳輸數據)*/
- 此函數原stm32_eval_sdio_sd.c有,不需添加,需要修改
SD_Error SD_Init(void)
{/*重置SD_Error狀態*/SD_Error errorstatus = SD_OK;NVIC_Configuration();/* SDIO 外設底層引腳初始化 */GPIO_Configuration();/*對SDIO的所有寄存器進行復位*/SDIO_DeInit(); /*上電并進行卡識別流程,確認卡的操作電壓 */errorstatus = SD_PowerON(); /*如果上電,識別不成功,返回“響應超時”錯誤 */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 外設,提高時鐘頻率,由卡識別模式的400khz提升到數據傳輸模式小于25MhzSDIO_InitStructure.SDIO_ClockDiv = SDIO_TRANSFER_CLK_DIV;//上升沿采集數據 SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising;// 不使能Bypass,使SDIO_CK經過SDIO_ClockDiv分頻SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable;//開啟的話,總線空閑時關閉SD_CLK 時鐘SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable;//暫時配置成lbit模式SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_1b;//硬件流。若開啟,在FIFO不能進行發送和接受數據是,數據暫停SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable;SDIO_Init(&SDIO_InitStructure);if (errorstatus == SD_OK){/*----------------- Read CSD/CID MSD registers ------------------*///用來讀取csd/cid寄存器/*調用 SD_GetCardInfo 函數獲取 SD 卡信息,它需要一個指向 SD_CardInfo 類型變
量地址的指針形參,這里賦值為 SDCardInfo 變量的地址。 SD 卡信息主要是 CID
和 CSD 寄存器內容,這兩個寄存器內容在 SD_InitializeCards 函數中都完成讀取過
程并將其分別存放在 CID_Tab 數組和 CSD_Tab 數組中,所以 SD_GetCardInfo 函
數只是簡單的把這兩個數組內容整合復制到 SDCardInfo 變量對應成員內。正確執
行 SD_GetCardInfo 函數后, SDCardInfo 變量就存放了 SD 卡的很多狀態信息,這
在之后應用中使用頻率是很高的。*/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);
}
- 調用 SD_PowerON 函數,它用于查詢卡的工作電壓和時鐘控制配置,并返回SD_Error 類型錯誤,該函數是整個 SD 識別精髓,
- 此函數原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;// 初始化的時候暫時把數據線配置成一根SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_1b;//禁止硬件流控制SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable;SDIO_Init(&SDIO_InitStructure);/*!< Set Power State to ON *///開啟外設電源SDIO_SetPowerState(SDIO_PowerState_ON);/*!< Enable SDIO Clock *///使能SDIO時鐘SDIO_ClockCmd(ENABLE);/*!< CMD0: GO_IDLE_STATE ---------------------------------------------------*//*!< No CMD response required *///發送一系列命令。開始卡的識別流程SDIO_CmdInitStructure.SDIO_Argument = 0x0;SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_GO_IDLE_STATE;//設置具體的返回類型,SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_No;//SDIO是否開啟或關閉等待中斷SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;/* CPSM 在開始發送命令之前等待數據傳輸結束 */SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;SDIO_SendCommand(&SDIO_CmdInitStructure);//檢測是否正確接收CM0 , CmdError 函數用于無需響應的命令發送errorstatus = CmdError();if (errorstatus != SD_OK){/*!< CMD Response TimeOut (wait for CMDSENT flag) *///響應超時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 *///發送CMD8檢查SD卡電壓操作 //發送 CMD8 命令,檢測 SD 卡支持的操作條件,主要就是電壓匹配, CMD8 的響//應類型是 R7,使用 CmdResp7Error 函數可獲取得到 R7 響應結果,它是通過檢測//SDIO_STA 寄 存 器 相 關 位 完 成 的 , 并 具 有 等 待 超 時 檢 測 功 能 。 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 //無響應,說明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 *///發送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);//是否響應,沒響應就是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) //響應cmd44.是sd卡,可能為1.x也可能2.x{//下面,循環發送sdio支持的電壓范圍,/*!< SD CARD *//*!< Send ACMD41 SD_APP_OP_COND with Argument 0x80100000 */while ((!validvoltage) && (count < SD_MAX_VOLT_TRIAL)){// 在發送 ACMD 命令前都要先向卡發送 CMD55 ,CMD55用于指示下一條指令是應用指令/*!< 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返回狀態,主要從OCR寄存器31位0或1來判斷那種類型* 命令參數由支持的電壓范圍及 HCS 位組成, HCS 位置一來區分卡是 SDSC 還是 SDHC* 0:SDSC* 1:SDHC* 響應: R3,對應的是 OCR 寄存器*//*使用 ACMD41 命令判斷卡的具體類型。因為是 A 類命令,所以在發送 ACMD41
之前必須先發送 CMD55, CMD55 命令的響應類型的 R1。如果 CMD55 命令都沒
有響應說明是 MMC 卡或不可用卡。在正確發送 CMD55 之后就可以發送
ACMD41,并根據響應判斷卡類型, ACMD41 的響應號為 R3, CmdResp3Error 函
數用于檢測命令正確發送并帶有超時檢測功能,但并不具備響應內容接收功能,
需要在判定命令正確發送之后調用 SDIO_GetResponse 函數才能獲取響應的內容。*/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 的供電電壓范圍內,會自動上電并標志 pwr_up 位* 讀取卡寄存器,卡狀態*/response = SDIO_GetResponse(SDIO_RESP1);/* 讀取卡的 ocr 寄存器的 pwr_up 位,看是否已工作在正常電壓 */validvoltage = (((response >> 31) == 1) ? 1 : 0);count++; //計算循環}//結束循環// 循環檢測超過一定次數還沒上電if (count >= SD_MAX_VOLT_TRIAL){// SDIO 不支持 card 的供電電壓errorstatus = SD_INVALID_VOLTRANGE;return(errorstatus);}/*檢查卡返回信息中的 HCS 位*//* 判斷 ocr 中的 ccs 位 ,如果是 sdsc 卡則不執行下面的語句 */if (response &= SD_HIGH_CAPACITY) //判斷30位是否為1{CardType = SDIO_HIGH_CAPACITY_SD_CARD;}}/*!< else MMC Card */return(errorstatus);
}
到此,程序執行SD卡的SD模式流程圖執行到如下圖、
判斷執行 SD_PowerON 函數無錯誤后,執行下面的 SD_InitializeCards 函數進行與 SD 卡相關的初始化,使得卡進入數據傳輸模式下的待機模式。
- 此函數原stm32_eval_sdio_sd.c有,不需添加
//描述 :初始化所有的卡或者單個卡進入就緒狀態
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 響應: R2,對應 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已經定義好了,不用我們自己,直接用 */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 響應:R2 對應寄存器 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);
}
;執行 SD_InitializeCards 函數無錯誤后 SD 卡就已經處于數據傳輸模式下的待機狀態,退出 SD_InitializeCards 后會返回前面的 SD_Init 函數,執行接下來代碼,以下是 SD_Init 函數的后續執行過程。執行之后,卡就從待機狀態轉變為傳輸模式,可以說數據傳輸已經是萬事俱備了。
SD 卡數據操作:包括數據讀取、數據寫入以及存儲區擦除。數據讀取和寫入都可以分為單塊操作和多塊操作。
- 擦除函數
- 此函數原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 卡中,地址參數為塊地址,每塊 512 字節,而 sdsc 卡地址為字節地址//所以若是 sdhc 卡要對地址/512 進行轉換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 函數用于向指定的目標地址寫入一個塊的數據,它有三個形參,分別為指向待寫入數據的首地址的指針變量、目標寫入地址和塊大小。塊大小一般都設置為512 字節。 (函數不用自己添加,但需要修改)
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卡,可以用來設置塊大小,* 若是sdhc卡,塊大小為512字節,不受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的寫數據寄存器SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;SDIO_DataInitStructure.SDIO_DataLength = BlockSize;SDIO_DataInitStructure.SDIO_DataBlockSize = (uint32_t) 9 << 4; //可用此參數代替SDIO_DataBlockSize_512bSDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToCard;//寫數據,SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block;SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Enable; //開啟數據通道狀態機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); //數據傳輸結束中斷SD_DMA_TxConfig((uint32_t *)writebuff, BlockSize); //配置dma,跟rx類似SDIO_DMACmd(ENABLE); // 使能sdio的dma請求
#endifreturn(errorstatus);
}
SD_WaitWriteOperation 函數用于檢測和等待數據寫入完成,在調用數據寫入函數之后一般都需要調用, SD_WaitWriteOperation 函數適用于單塊及多塊寫入函數。
/*** 上述代碼調用庫函數 SD_DMAEndOfTransferStatus 一直檢測 DMA 的傳輸完成標志,
當 DMA 傳輸結束時,該函數會返回 SET 值。另外, while 循環中的判斷條件使用的
TransferEnd 和 TransferError 是全局變量,它們會在 SDIO 的中斷服務函數根據傳輸情況被
設置, 傳輸結束后,根據 TransferError 的值來確認是否正確傳輸,若不正確則直接返回錯
誤代碼。 SD_WaitWriteOperation 函數最后是清除相關標志位并返回錯誤。由于這個函數里
的 while 循環的存在, 它會確保 DMA 的傳輸結束。*/
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函數:
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; //傳輸結束標置位,在中斷服務置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卡,可以用來設置塊大小,* 若是sdhc卡,塊大小為512字節,不受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);
}
其他讀寫操作函數雷同,不用修改。但需要把最后面4句話替換成:
到此,SD卡初始化完成了,以上只是介紹重要那部分的函數,其他有些細微的修改,先引用,請點擊:SD讀寫測試
設置SDIO 中斷服務函數(stm32f10x_it.c)
// 在 SDIO_ITConfig()這個函數開啟了 sdio 中斷 ,
void SDIO_IRQHandler(void)
{//SDIO中斷相關處理SD_ProcessIRQSrc(); //定義在bsp_sdio_sdcard.c}
/** 函數名:SD_ProcessIRQSrc* 描述 :數據傳輸結束中斷* 輸入 :無 * 輸出 :SD錯誤類型*/
SD_Error SD_ProcessIRQSrc(void)
{if (StopCondition == 1) //發送讀取、多塊讀寫命令時置1{SDIO->ARG = 0x0; //命令參數寄存器SDIO->CMD = 0x44C; // 命令寄存器: 0100 01 001100// [7:6] [5:0]// CPSMEN WAITRESP CMDINDEX// 開啟命令狀態機 短響應 cmd12 STOP_ TRANSMISSION TransferError = CmdResp1Error(SD_CMD_STOP_TRANSMISSION);}else{TransferError = SD_OK;}SDIO_ClearITPendingBit(SDIO_IT_DATAEND); //清中斷SDIO_ITConfig(SDIO_IT_DATAEND, DISABLE); //關閉sdio中斷使能TransferEnd = 1;return(TransferError);
}
至此,我們已經介紹了 SD 卡初始化、 SD 卡數據操作的基礎功能函數以及 SDIO 相關中斷服務函數內容,利用這個 SDIO 驅動,可以編寫一些簡單的 SD 卡讀寫測試程序。
測試 SD 卡部分的函數是我們自己編寫的,存放在 sdio_test.c 文件等。
最后附上我編寫的程序代碼,主要實現串口輸入內容,然后保存到sd卡中。再讀取出來,輸出到串口那里。
點擊下載:【sd卡讀取】
轉載于:https://www.cnblogs.com/guguobao/p/10123755.html
總結
以上是生活随笔為你收集整理的使用STM32F103ZET霸道主板实现SD卡的读写(非文件系统)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。