日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

使用STM32F103ZET霸道主板实现SD卡的读写(非文件系统)

發布時間:2025/3/14 windows 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 使用STM32F103ZET霸道主板实现SD卡的读写(非文件系统) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

了解STM32F103ZET是高容量多管腳的芯片

了解SD讀寫線路圖

了解SD的基地址

閱讀STM32F10xx英文參考 SDIO那章,我們編寫代碼邊看文檔解析

建工程,打開包含所有包括外設庫函數的樣本工程,然后留下如下的外設

  • 官方給的E:\2018-9-3\stm32-奮斗者\STM32 官方庫3.5版本\stm32f10x_stdperiph_lib35\STM32F10x_StdPeriph_Lib_V3.5.0\Utilities\STM32_EVAL\Common下的文件只是用于他們的測試版,因此需要修改stm32_eval_sdio_sd.h中的include,由原來的#include "stm32_eval.h"改為#include "stm32f10x.h"

在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)
  • GPIO 初始化
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 卡驅動函數代碼。這部分代碼內容也是非常龐大,不可能全部在文檔中全部列出,對于部分函數只介紹其功能。

  • SD卡初始化
  • NVIC初始化
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卡
/**描述 :初始化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卡的读写(非文件系统)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 亚洲丝袜天堂 | 久久这里只有精品99 | 99久久久久久久 | 假日游船 | 丁香婷婷网| 一个人看的www视频在线观看 | 日本强好片久久久久久aaa | 亚洲精品www久久久 一级aaa毛片 | 国产91精品露脸国语对白 | 综合色av| 黑人巨大精品人妻一区二区 | 捆绑中国女人hd视频 | 99在线视频播放 | chinese xxxx videos andvr | 国产欧美精品在线 | 日本黄色大片免费 | 国产视频福利在线观看 | 秋霞毛片少妇激情免费 | 欧美日韩激情网 | 后进极品美女圆润翘臀 | 国产特级黄色片 | 国产av电影一区二区 | 丁香六月综合 | 大象传媒成人在线观看 | 久久久久久久偷拍 | 99久视频 | 中文字幕日韩久久 | 91久久精品国产91久久性色tv | 天堂av网站| 欧美午夜免费 | 寡妇高潮一级视频免费看 | 欧美三级久久久 | 亚州欧美| 最新亚洲精品 | 99国产免费| 精品一区二区免费视频 | 午夜肉体高潮免费毛片 | 日韩一区欧美一区 | 国偷自拍| 美国成人av| 叼嘿视频91 | 欧美特级黄色录像 | 午夜一区二区三区免费 | 香蕉视频一区 | 亚洲黄色a | 丁香激情婷婷 | 日本免费高清视频 | 亚洲国产精品久久久久久 | www.成人在线观看 | 国产日韩欧美激情 | 日本韩国中文字幕 | 久久91久久| 美女高潮网站 | 久久老司机| 求av网站 | 欧美一级免费大片 | 国产亚洲一区二区三区在线观看 | 黄色字幕网 | 毛片小视频| 一区二区亚洲精品 | 久久精品伊人 | 一本综合久久 | 欧美国产一二三区 | 肉大榛一进一出免费视频 | 亚洲影院在线 | 99热成人| 国产精品久久久爽爽爽麻豆色哟哟 | 丁香花免费高清完整在线播放 | 亚欧在线视频 | 国产在线播 | 麻豆av在线看 | 后进极品白嫩翘臀在线视频 | 久久精品综合网 | 中文字幕第六页 | 97色婷婷 | 午夜伦理一区二区 | 久久久久成人片免费观看蜜芽 | 亚洲欧美综合久久 | 亚洲综合第一页 | 亚洲一级黄色 | 亚洲精品国产精品乱码不66 | 伊人久久免费视频 | 日韩首页 | 奇米影视一区二区三区 | 激情av在线 | www.97视频| 奇米影视亚洲 | 日韩一级片视频 | 99久久久久久久久久 | 日本一区不卡在线 | 国产成人99久久亚洲综合精品 | 67194在线免费观看 | xxxx国产精品 | 少妇人妻好深好紧精品无码 | 在线观看一区二区视频 | 日韩欧美视频免费观看 | 免费观看的av | 大战熟女丰满人妻av | 亚洲一级片在线观看 |