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