【STM32】STM32 SDIO SD卡读写测试(二)-- SD_Init之Power On阶段
相關文章
《【SDIO】SDIO、SD卡、FatFs文件系統相關文章索引》
1. 前言
本篇文章主要是介紹stm324x9i_eval_sdio_sd.c里面SD_Init()函數完整的過程。它主要是實現了SDIO的初始化、SD卡的Power UP、SD卡的初始化和獲取SD卡的相關信息等,下面會詳細介紹SDIO的初始化和SD卡的Power UP的分析。
2. SD_LowLevel_Init()
SD_LowLevel_Init()主要功能是初始化使用的IO和相關的Clock,具體如下:
3. SD_PowerON()
SD_PowerON()主要功能是詢問SD卡的工作電壓和配置工作時鐘。主要涉及到的函數如下:
- SDIO_Init()
- SDIO_SetPowerState()
- SDIO_ClockCmd()
- CMD0: GO_IDLE_STATE
- CMD8: SEND_IF_COND
- CMD55: SD_CMD_APP_CMD
- ACMD41: SD_CMD_SD_APP_OP_COND
3.1 SDIO_Init()
SDIO_Init()主要是配置SDIO時鐘控制寄存器(SDIO_CLKCR)。下面通過Code、Register Map和Table來介紹對SDIO_CLKCR寄存器的設置如下:
/** * @brief SDIO Intialization Frequency (400KHz max)*/ #define SDIO_INIT_CLK_DIV ((uint8_t)0x76)/*!< Power ON Sequence -----------------------------------------------------*/ /*!< Configure the SDIO peripheral */ /*!< SDIO_CK = SDIOCLK / (SDIO_INIT_CLK_DIV + 2) */ /*!< on STM32F4xx 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);| 名稱 | 描述 | Value | 備注 |
| HWFC_EN | 硬件流控制使能 (HW Flow Control enable) 0:禁止硬件流控制 1:使能硬件流控制 | 0 | SDIO_HardwareFlowControl_Disable |
| NEGEDGE | SDIO_CK 移相選擇位 (SDIO_CK dephasing selection bit) 0:在主時鐘 SDIOCLK 的上升沿產生 SDIO_CK 1:在主時鐘 SDIOCLK 的下降沿產生 SDIO_CK | 0 | SDIO_ClockEdge_Rising |
| WIDBUS | 寬總線模式使能位 (Wide bus mode enable bit) 00:默認總線模式:使用 SDIO_D0 01:4 位寬總線模式:使用 SDIO_D[3:0] 10:8 位寬總線模式:使用 SDIO_D[7:0] | 0 | SDIO_BusWide_1b //在SD卡初始化階段只用到CLK和CMD, SDIO_D0作為指示SD卡的繁忙狀態,所以這里被設置0。 |
| BYPASS | 時鐘分頻器旁路使能位 (Clock divider bypass enable bit) 0:禁止旁路:在驅動 SDIO_CK 輸出信號前,根據 CLKDIV 值對 SDIOCLK 進行分頻。 1:使能旁路:SDIOCLK 直接驅動 SDIO_CK 輸出信號。 | 0 | SDIO_ClockBypass_Disable |
| PWRSAV | 節能模式配置位 (Power saving configuration bit) 0:始終使能 SDIO_CK 時鐘 1:僅在總線激活時使能 SDIO_CK | 0 | SDIO_ClockPowerSave_Disable |
| CLKEN | 時鐘使能位 (Clock enable bit) 0:禁止 SDIO_CK 1:使能 SDIO_CK | 0 | |
| CLKDIV | 時鐘分頻系數 (Clock divide factor) 該字段定義輸入時鐘 (SDIOCLK) 與輸出時鐘 (SDIO_CK) 之間的分頻系數: SDIO_CK 頻率 = SDIOCLK / [CLKDIV + 2] | 0x76 | SDIO_CK 頻率 = SDIOCLK / [CLKDIV + 2] 400K = 48M / [0x76 + 2] |
3.2 SDIO_SetPowerState()
SDIO_SetPowerState()主要是配置SDIO電源控制寄存器 (SDIO_POWER)為ON。下面通過Code、Register Map和Table來介紹對SDIO_POWER寄存器的設置如下:
/** @defgroup SDIO_Power_State * @{*/ #define SDIO_PowerState_ON ((uint32_t)0x00000003)/*!< Set Power State to ON */ SDIO_SetPowerState(SDIO_PowerState_ON);| 名稱 | 描述 | Value | 備注 |
| PWRCTRL | 電源控制位 (Power supply control bits)。 00:掉電:停止為卡提供時鐘 01:保留 10:保留,上電 11:通電:為卡提供時鐘。 | 3 | SDIO_PowerState_ON |
3.3 SDIO_ClockCmd()
SDIO_ClockCmd()主要是配置SDIO時鐘控制寄存器(SDIO_CLKCR)的CLKEN為使能 SDIO_CK。下面通過Code、Register Map和Table來介紹對SDIO_POWER寄存器的設置如下:
/*!< Enable SDIO Clock */ SDIO_ClockCmd(ENABLE);-------------------------------------------->void SDIO_ClockCmd(FunctionalState NewState) {*(__IO uint32_t *) CLKCR_CLKEN_BB = (uint32_t)NewState; }-------------------------------------------->/* ------------ SDIO registers bit address in the alias region ----------- */ #define SDIO_OFFSET (SDIO_BASE - PERIPH_BASE) // (0x40012C00 - 0x40000000) = 0x12C00/* --- CLKCR Register ---*/ /* Alias word address of CLKEN bit */ #define CLKCR_OFFSET (SDIO_OFFSET + 0x04) // (0x12C00 + 0x04) = 0x12C04 #define CLKEN_BitNumber 0x08 #define CLKCR_CLKEN_BB (PERIPH_BB_BASE + (CLKCR_OFFSET * 32) + (CLKEN_BitNumber * 4))//(0x42000000 + ( 0x12C04 * 32) + (0x08 * 4)) = 0x422580A0這里會有一個疑問:為什么CLKCR_CLKEN_BB(0x422580A0)這個地址可以操作到SDIO_CLKCR(0x40012C04)寄存器的CLKEN時鐘使能位?
解釋這個問題我們需要了解一下Cortex-M4內核的Memory Map(如下圖),Cortex-M4的存儲器系統支持所謂的“位帶”(bit-band)操作。通過它,實現了對單一Bit的原子操作。
下面的公式顯示別名區域( the alias region)如何映射到位帶區域(the bit-band region):
bit_word_offset = (byte_offset x 32) + (bit_number x 4)
bit_word_addr = bit_band_base + bit_word_offset
- bit_word_offset:目標Bit在位帶區域(the bit-band region)中的位置。
- bit_word_addr:目標Bit 映射到 別名區域( the alias region)中 字的地址 (就是32Bit地址)。
- bit_band_base:別名區域( the alias region)的起始地址。// Peripheral: 0x42000000 SRAM: 0x22000000
- byte_offset:目標Bit在位帶區域(the bit-band region)中的字節數。
- bit_number:目標Bit的位置,0-7。
例如:SRAM的寄存器地址0x200FFFFF的bit[7]映射到別名區域( the alias region)的0x23FFFFFC,計算公式如下:
0x23FFFFFC = 0x22000000 + (0xFFFFF*32) + (7*4).
3.4 CMD0: GO_IDLE_STATE
在上電的階段,發送的第一個命令是CMD0,使SD卡進入到IDLE狀態。如下圖所示:
使用SDIO_SendCommand發送CMD,涉及到2個寄存器:SDIO 參數寄存器 (SDIO_ARG) 和 SDIO 命令寄存器 (SDIO_CMD) 。
| 名稱 | 描述 | Value | 備注 |
| CMDARG | 命令參數 (Command argument) 作為命令消息的一部分發送給卡的命令參數。如果命令包含參數,則在將命令寫入到命令寄存器之前,必須將參數加載到此寄存器中。 | 0 |
| 名稱 | 描述 | Value | 備注 |
| ATACMD | CE-ATA 命令 (CE-ATA command) 如果 ATACMD 置 1,則 CPSM 將傳輸 CMD61。 | 0 | |
| nIEN | 非中斷使能 (not Interrupt Enable) 如果該位為 0,則使能 CE-ATA 設備中的中斷。 | 0 | |
| ENCMDcompl | 使能 CMD 完成 (Enable CMD completion) 如果此位置 1,則使能命令完成信號。 | 0 | |
| SDIOSuspend | SD I/O 掛起命令 (SD I/O suspend command) 如果此位置 1,則要發送的命令為掛起命令(僅用于 SDIO 卡)。 | 0 | |
| CPSMEN | 命令路徑狀態機 (CPSM) 使能位 (Command path state machine (CPSM) Enable bit) 如果此位置 1,則使能 CPSM。 | 1 | SDIO_CPSM_Enable |
| WAITPEND | CPSM 等待數據傳輸結束(CmdPend 內部信號) (CPSM Waits for ends of data transfer (CmdPend internal signal))。 如果此位置 1,則 CPSM 將等到數據傳輸結束后才開始發送命令。 | 0 | SDIO_Wait_No |
| WAITINT | CPSM 等待中斷請求 (CPSM waits for interrupt request) 如果此位置 1,則 CPSM 禁止命令超時并等待中斷請求。 | 0 | SDIO_Wait_No |
| WAITRESP | 等待響應位 (Wait for response bits) 00:無響應,但 CMDSENT 標志除外 01:短響應,但 CMDREND 或 CCRCFAIL 標志除外 10:無響應,但 CMDSENT 標志除外 11:長響應,但 CMDREND 或 CCRCFAIL 標志除外 | 0x00000000 | SDIO_Response_No |
| CMDINDEX | 命令索引 (Command index) 命令索引作為命令消息的一部分發送給卡。 | 0 | SD_CMD_GO_IDLE_STATE |
使用邏輯分析儀抓取實際發送出來的波形如下:
發送CMD0后,還需要判斷是否發送成功。由于CMD0是無需響應的CMD,所以這里只需要判斷SDIO 狀態寄存器 (SDIO_STA)是否發送完成。
| 名稱 | 描述 | Value | 備注 |
| CEATAEND | 針對 CMD61 收到了 CE-ATA 命令完成信號 (CE-ATA command completion signal received for CMD61) | ||
| SDIOIT | 收到了 SDIO 中斷 (SDIO interrupt received) | ||
| RXDAVL | 接收 FIFO 中有數據可用 (Data available in receive FIFO) | ||
| TXDAVL | 傳輸 FIFO 中有數據可用 (Data available in transmit FIFO) | ||
| RXFIFOE | 接收 FIFO 為空 (Receive FIFO empty) | ||
| TXFIFOE | 發送 FIFO 為空 (Transmit FIFO empty) 如果使能了硬件流控制,則 TXFIFOE 信號在 FIFO 包含 2 個字時激活。 | ||
| RXFIFOF | 接收 FIFO 已滿 (Receive FIFO full) 如果使能了硬件流控制,則 RXFIFOF 信號在 FIFO 差 2 個字便變滿之前激活。 | ||
| TXFIFOF | 傳輸 FIFO 已滿 (Transmit FIFO full) | ||
| RXFIFOHF | 接收 FIFO 半滿:FIFO 中至少有 8 個字 (Receive FIFO half full: there are at least 8 words in the FIFO) | ||
| TXFIFOHE | 傳輸 FIFO 半空:至少可以寫入 8 個字到 FIFO (Transmit FIFO half empty: at least 8 words can be written into the FIFO) | ||
| RXACT | 數據接收正在進行中 (Data receive in progress) | ||
| TXACT | 數據傳輸正在進行中 (Data transmit in progress) | ||
| CMDACT | 命令傳輸正在進行中 (Command transfer in progress) | ||
| DBCKEND | 已發送/ 接收數據塊(CRC 校驗通過) (Data block sent/received (CRC check passed)) | ||
| STBITERR | 在寬總線模式下,并非在所有數據信號上都檢測到了起始位 (Start bit not detected on all data signals in wide bus mode) | ||
| DATAEND | 數據結束(數據計數器 SDIDCOUNT 為零) (Data end (data counter, SDIDCOUNT, is zero)) | ||
| CMDSENT | 命令已發送(不需要響應)(Command sent (no response required)) | 0x00000080 | SDIO_FLAG_CMDSENT |
| CMDREND | 已接收命令響應(CRC 校驗通過)(Command response received (CRC check passed)) | ||
| RXOVERR | 收到了 FIFO 上溢錯誤 (Received FIFO overrun error) | ||
| TXUNDERR | 傳輸 FIFO 下溢錯誤 (Transmit FIFO underrun error) | ||
| DTIMEOUT | 數據超時 (Data timeout) | ||
| CTIMEOUT | 命令響應超時 (Command response timeout) 命令超時周期為固定值 64 個 SDIO_CK 時鐘周期。 | ||
| DCRCFAIL | 已發送/ 接收數據塊(CRC 校驗失敗) (Data block sent/received (CRC check failed)) | ||
| CCRCFAIL | 已接收命令響應(CRC 校驗失敗) (Command response received (CRC check failed)) |
3.5 CMD8: SEND_IF_COND
CMD8: SEND_IF_COND這個命令定義在《Physical Specification Version 2.00》以上版本,它有2個作用:
- 電壓檢測:檢查卡是否能在主機供電電壓下工作。
- 擴展現有命令:啟用CMD8可以擴展一些現有命令保留位的新功能。ACMD41被擴展以支持高容量SD存儲卡。
通過下面CMD8命令格式和SD_CHECK_PATTERN(0x000001AA)可以了解到這里選擇的操作電壓是2.7~3.6V。
實際使用邏輯分析儀抓取主機發送CMD8時的波形如下:
CMD8命令發送成功后,我們需要檢測SD卡是否有響應。通過它來判斷SD卡是否支持SD2.0版本以上的協議。所以,從代碼上來看這里只是判斷了SDIO 狀態寄存器 (SDIO_STA)是否已接收命令響應(CMDREND)。因為STM32F429支持SD2.0,所以只能支持大容量SD存儲卡(32GB)。
為什么判斷的是Response7?因為在SD2.0協議是已經定義好的,無論是host還是SD卡都必須按照這個協議來。
從邏輯分析儀抓取的波形來看,SD卡有回復,說明它支持SD2.0以上協議,并且支持主機提供的電壓2.7~3.6V。波形如下:
3.6 CMD55: SD_CMD_APP_CMD
CMD55: SD_CMD_APP_CMD指定下個命令為特定應用命令,不是標準命令。SD卡主機模塊系統旨在為各種應用程序類型提供一個標準接口。在此環境中,需要有特定的客戶/應用程序功能。為實現這些功能,在標準中定義了兩種類型的通用命令:特定應用命令(ACMD)和常規命令(GEN_CMD)。要使用 SD卡制造商特定的 ACMD命令如ACMD41,需要在發送該命令之前發送 CMD55 命令,告知 SD卡接下來的命令為特定應用命令。CMD55 命令只對緊接的第一個命令有效,SD卡如果檢測到 CMD55 之后的第一條命令為 ACMD 則執行其特定應用功能,如果檢測發現不是 ACMD 命令,則執行標準命令。
#define SD_CMD_APP_CMD ((uint8_t)55)/*!< 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命令后,通過SDIO 狀態寄存器 (SDIO_STA)來判斷命令響應是否已經正確被接收。然后,通過函數SDIO_GetCommandResponse獲取SDIO 命令響應寄存器 (SDIO_RESPCMD) Value來判斷Host接收到的響應命令是否是剛剛發送的命令。最后,通過函數SDIO_GetResponse獲取SDIO 響應 1寄存器 (SDIO_RESP1) SD卡的狀態。
從SD2.0協議里面可以了解到CMD55的response是R1,R1的主要獲取的Card Status。
這里只是展示了一部分的Card Status,需要找參考完整的可以參考《Physical Specification Version 2.00》,我在下面的參考資料里面有發相關的鏈接。
通過邏輯分析儀抓取的波形,Card Status為SD卡已經準備好接收ACMD命令。如下:
3.7 ACMD41: SD_CMD_SD_APP_OP_COND
ACMD41: SD_CMD_SD_APP_OP_COND 被設計為為主機提供一種機制來識別和拒絕與主機所提供的VDD范圍不匹配的卡。不能在指定范圍內進行數據傳輸的SD卡,應放棄總線操作,進入Inactive 狀態。OCR寄存器定義了相關的電壓等級。
#define SD_VOLTAGE_WINDOW_SD ((uint32_t)0x80100000) #define SD_HIGH_CAPACITY ((uint32_t)0x40000000) #define SD_CMD_SD_APP_OP_COND ((uint8_t)41) /*!< For SD Card only */while ((!validvoltage) && (count < SD_MAX_VOLT_TRIAL)) {...SDIO_CmdInitStructure.SDIO_Argument = SD_VOLTAGE_WINDOW_SD | SD_HIGH_CAPACITY;SDIO_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);}response = SDIO_GetResponse(SDIO_RESP1);validvoltage = (((response >> 31) == 1) ? 1 : 0);count++; }ACMD41主要設置的是OCR寄存器,希望設置的電壓范圍在3.2-3.3和高容量的SD卡(參考OCR寄存器),實際發送的波形如下:
發送ACMD41后,主要是通過Response R3來獲取OCR寄存器的狀態,判斷Card power up status bit (31bit)是否為高電平,如果為低電平那么SD卡還在power up階段未完成。
重復發送ACMD41獲取OCR寄存器Power up status bit的狀態,一直發送到該bit為高電平為止,此時說明power on這個過程已經完成。
4. 參考資料
SDIO參考的資料如下:
下載地址如下:
https://download.csdn.net/download/ZHONGCAI0901/14975835
移植成功的完整代碼下載地址如下:
https://download.csdn.net/download/ZHONGCAI0901/15265756
總結
以上是生活随笔為你收集整理的【STM32】STM32 SDIO SD卡读写测试(二)-- SD_Init之Power On阶段的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Openldap2.5.13编译安装
- 下一篇: 华为linux版本wifi驱动,HuaW