STM32之CubeL4(四)--- SD/MMC + SDIO + HAL

我們進行嵌入式開發時,常會遇到SD卡驅動或者擴展SDIO模塊,ST提供的SD卡HAL標準庫中常出現SDMMC,爲什麼SD經常與MMC一塊出現,SD與SDIO又有什麼關係呢?

一、SD/MMC/SDIO概念區分

MMC(MultiMediaCard)從本質上看,是一種用於固態非易失性存儲的內存卡(memory card)規範,定義了諸如卡的形態、尺寸、容量、電氣信號、和主機之間的通信協議等方方面面的內容。

從1997年MMC規範發佈至今,基於不同的考量(物理尺寸、電壓範圍、管腳數量、最大容量、數據位寬、clock頻率、安全特性、是否支持SPI mode、是否支持DDR mode、等等),進化出了MMC、SD、microSD、SDIO、eMMC等不同的規範,如下圖所示(圖片取自博客:MMC/SD/SDIO介紹):
MMC進化到SD與SDIO過程圖
SD卡(Secure Digital Memory Card)是在MMC的基礎上發展而來的,相比MMC增加了兩個主要特色:SD卡強調數據的安全,可以設定所儲存的使用權限,防止數據被他人複製;SD卡的傳輸速度比MMC卡快。在數據傳輸和物理規範上,SD卡向前兼容了MMC卡,所有支持SD卡的設備也支持MMC卡,這也是我們在SD卡驅動文件中常見到SDMMC的原因。
SD容量與速度
SDIO(Secure Digital Input Output)是從SD規範演化來的,強調的是接口 I/O功能,不再關注另一端的具體形態(可以是Wi-Fi card、Bluetooth card、GPS card、GSM/GPRS card 、Camera card、Radio/TV card等)。SDIO和SD卡規範間的一個重要區別是增加了低速標準,低速卡的目標應用是以最小的硬件開銷支持低速IO能力。因此,SDIO接口兼容SD卡,或者說SD卡就是其中一種具有安全存儲功能的SDIO Card,SDIO Card也可以是兼具多種功能的組合卡Combo Card(比如 I/O + Memory)。
SDIO標準規範

二、SDIO Bus簡介

SDIO/SD協議規範包含三部分:Host Controller、SDIO Bus、SDIO Card,三者的結構框圖如下所示:
SDIO結構框圖
SDIO總線和USB總線類似,都是採用主從模式通信,其中一端是主機端(Host Controller / Adapter),另一端是設備端(SDIO Card),採用 Host - Device 這樣的設計是爲了簡化 Device 的設計,所有的通信都是由 Host 端發出命令開始的。在 Device 端只要能解析 Host 的命令,就可以同 Host 進行通信了,SDIO 的 Host 可以連接多個 Device。

2.1 SDIO Bus物理層

SDIO Bus的物理信號有 CK、CMD、DATA三類(SDIO適配器時鐘SDIOCLK用於驅動SDIO Host,並用於產生SDIO_CK時鐘):

  • SDIO_CK:Host給Device的時鐘信號,SDIO卡時鐘頻率全速模式可達0-25MHZ(低速模式爲0-400KHZ),SD卡或MMC卡的高速版本時鐘頻率可達上百兆赫茲;
  • SDIO_CMD:雙向的命令、響應信號線,Command由Host發送給Device,Response由Device 返回給Host;
  • SDIO_D[7:0]: 雙向的數據傳輸線,數據位寬允許動態配置爲 1-bit(默認)、4-bit、8-bit模式(數據線與命令線通常要保持上拉狀態,以保護數據免受總線浮動影響);
  • VCC/VDD:按照工作電壓的不同,SD卡可分爲高電壓版(3.3V)和雙電壓版(1.8V和3.3V)。

SDIO的信號傳輸模式有SD 4-bit、SD 1-bit、SPI三種(8-bit模式一般用於MMC),以最常見的SD卡9 pin引腳(3根電源線 + 6根信號線)爲例,分別工作在這三種模式下的引腳功能定義如下表所示:
SD卡傳輸模式
依據 SD 標準,所有的 SD(記憶卡)與 SDIO(外設)都必須支持 SPI mode,因此 SPI mode 是「required」。SDIO新增的低速標準本身就爲了減少硬件開支,因此不支持上面的SD 4-bit模式。

2.2 SDIO Bus協議層

SDIO 總線的通信是基於命令和數據流的,由一個起始位開始,由一個停止位終止。

  • 命令(Command):就是一個標記,用於發起(或結束)一個操作,由主機發送到單個卡(尋址命令)或者所有卡(廣播命令);
  • 響應(Response):也是一個標記,從尋址的卡或者所有卡(同步)發送給主機,作爲向先前接收到的命令的回答;
  • 數據(Data):可以從主機到卡,也可以從卡到主機,數據線的個數可以是 1、4、8。

SDIO總線上的基本交互是命令/響應交互,這種總線交互直接在命令或者響應的結構裏面傳輸它們的信息。SDIO的每次操作都是由Host 在CMD線上發起一個命令,對於有的Command,Device 需要返回 Response,有的則不需要。

SDIO總線上傳輸的數據可以是數據塊(比如SD memory card 這類塊設備),也可以是數據流(比如 I/O function card 這類字符設備),數據塊需附加CRC(Cyclic Redundancy Check)位來保證數據準確傳輸。主機端可以配置單線或者多線傳輸,數據塊可以單塊操作或多塊操作,數據塊寫操作使用busy來表示DAT0數據線上的持續寫操作,不管使用幾線傳輸。SDIO總線上命令、響應、數據塊、數據流的交互示意圖如下:
SDIO命令/響應交互過程

2.2.1 SDIO Command

Command是一次操作開始的令牌,從主機發送到一個卡設備(尋址命令)或者連接到主機的所有卡設備(廣播命令)。每個命令都有一個起始位和結束位,總長度爲48 bits,並且每個命令都有 7 bits 的CRC 校驗碼。命令只能通過CMD線傳輸,並且MSB(Most Significant Bit)爲先(也即最高有效位在前),Command的編碼格式如下圖所示:
SDIO命令格式
命令的開始位(始終爲0)、傳輸位(始終爲1,表示Host–>Device的傳輸方向)、 CRC7 和結束位(始終爲1)由 SDIO 硬件控制,我們需要設置的就只有命令索引和參數部分。其中命令索引(CMD0 ~ CMD63共64個命令)在 SDIO_CMD 寄存器裏面設置,命令參數則由寄存器 SDIO_ARG 設置。

SDIO的命令分爲應用相關命令ACMD 和通用命令CMD 兩部分,應用相關命令ACMD 的發送,必須先發送通用命令 CMD55 ,然後才能發送應用相關命令 ACMD 。 SDIO的命令根據發送範圍和是否帶響應可以分爲下面四種不同的類型:

  • Broadcast Commands(BC):發送到所有卡,沒有響應返回;
  • Broadcast Commands with Response(BCR):發送到所有卡,同時收到從所有卡返回的響應;
  • Addressed(point-to-point) Commands(AC):由Host 發送到指定的卡設備,沒有數據的傳輸;
  • Address(point-to-point) Data Transfer Commands(ADTC):由Host 發送到指定的卡設備且伴隨有數據傳輸。

SD卡與SDIO卡支持的命令共分爲12類(class 0 ~ class 11),下面按命令類別class和命令類型type,將SD/SDIO支持的常用命令及其功能描述彙總如下(保留命令、SPI模式命令等沒有列出):
SD與SDIO卡支持的命令列表
上面的命令中出現的CID、CSD、OCR、SCR等都是卡設備寄存器(SD/SDIO Card寄存器信息下文再單獨介紹),實際上Host 與Card Device 之間的命令/響應信息交互,都是通過Host 讀寫Card Device 的寄存器信息實現的。類似於,CPU控制SDIO Host 發送命令、解析響應、傳輸數據等操作,都是通過CPU 讀寫 SDIO Host 的寄存器信息實現的。

2.2.2 SDIO Response

Response從卡設備發回數據的令牌,迴應主機之前發送的命令。每個響應也都有一個起始位(始終爲0)、傳輸位(始終爲0,表示Device --> Host 方向)和結束位(始終爲1),總長度爲48 bits(短響應)或者136 bits(長響應),有相應的 CRC 校驗碼(除了R3/R4沒有CRC)。響應信號也只在 CMD 線上傳輸,並且 MSB(Most Significant Bit) 爲先,Response的編碼格式如下圖所示:
SDIO響應格式
SD存儲卡有5種類型的Response:R1/R1b、R2、R3、R6、R7,SDIO卡還支持另外兩種Response 類型:R4、R5。只有R2 爲136 bits的長響應(爲了返回 CID 或 CSD 這類長達 128 bits的數據),其餘的響應均爲48 bits的短響應。從前面SD/SDIO命令列表中可以看到,每個命令對應哪種響應格式,這七種Response的內容及描述如下:
SD與SDIO卡響應類型及結構
上面這些響應返回的內容,多數也是卡設備自身的寄存器信息,比如CID、CSD、OCR、CSR等,這些寄存器在下文單獨介紹。

2.2.3 SDIO Data

Data可以從主機Host 到卡設備,也可以從卡設備到主機,通過數據線傳輸,SDIO總線定義了3種數據模式——1bit、4bit、8bit,其中SD/SDIO卡只用到1bit和4bit模式(MMC卡會使用8-bit 模式),SD卡傳輸數據時使用的是4bit模式。

在SDIO_DATA線上傳輸的數據包格式跟Command和Response類似的是,在每根數據線上傳輸的每個數據包也都包含一個起始位和一個結束位,而且每個數據包內也都包含CRC校驗位。CRC狀態反饋和busy 標識只會通過DATA0 從卡設備Device 發送到主機Host,跟DATA1 ~ DATA3無關。SD有兩種數據包格式:

  • Usual data(8-bit width):常規數據是先發送低字節,再發送高字節,但每個字節則是先高位後低位的順序發送;
    常規數據包格式
  • Wide width data(SD memory register):寬位數據從高位開始傳輸。
    寬位數據包格式
    從上面兩幅圖的對比可以看出,寬位數據包主要用於傳輸SD Status和SCR(SD Configuration Register)等信息,正常的數據傳輸主要還是使用常規數據包格式。

三、SDIO Card簡介

3.1 SD memory card簡介

前面介紹SDIO bus傳輸模式時,拿SD存儲卡作爲SDIO Card示例,我們都見過SD卡長啥樣,但你知道SD存儲卡的內部結構嗎?
SD存儲卡結構

3.1.1 SD卡寄存器簡介

SD卡內部跟SDIO bus直接相連的是Card interface controller,與Card interface controller相連的除了memory core interface用於訪問存儲顆粒外,就是SD卡的各個寄存器了。從上圖也可以看到SD卡主要的寄存器位寬,各個寄存器的功能描述如下:
SDIO寄存器功能簡介
限於篇幅,SD卡各個寄存器內部每一位的作用及設置就不展開介紹了,感興趣可以到網站:https://www.sdcard.org/downloads/pls/archive/index.html下載相關文檔查閱。

3.1.2 SD卡操作模式簡介

SD卡系統(Host & Card)定義了兩種操作模式:

  • 卡識別模式:卡在復位後會處於“識別模式”,直到收到 SEND_RCA(CMD3)命令(主機復位所有卡片後將進入該模式);
  • 數據傳輸模式:當 RCA 第一次發佈後,卡會處於“數據傳輸模式”(主機會在總線上所有的卡都被識別後進入該模式)。
    SD卡狀態與操作模式
  • SD卡識別模式

當主機復位所有卡片後將進入卡片識別模式,在識別模式下將會確認卡片的操作電壓並要求卡片發佈自己的相對地址(默認地址爲0x0000),在此操作模式下只會用到 CMD 線並且工作在專門的時鐘頻率 F-od(400 KHz)。SD卡識別模式的流程圖如下:
SD卡初始化與識別流程

  1. 上電後發送命令CMD0(GO_IDLE_STATE),使所有的卡設備進入“Idle”狀態;
  2. 發送命令CMD8(只有V2.0以上版本的SD卡才支持該命令,MMC卡和V1.x的SD卡不支持該命令),如果卡設備有Response,說明該卡爲SD V2.0以上版本;
  3. 發送命令CMD55+ACMD41(在發送ACMD41這類應用指令之前需要先發送CMD55指令),探測卡設備的工作電壓是否符合host 端的要求,並通過HCS位告訴SD卡,主機Host 是否支持高容量卡SDHC(HCS置1 表示Host 支持SDHC),ACMD41的命令與響應格式如下:
    ACMD41命令及響應格式
    命令ACMD41的響應類型是R3,R3主要是向Host 返回OCR寄存器的內容,除了告知Host 自己支持的工作電壓外,還會通過CCS位告訴Host 自己是否爲高容量卡(CCS爲1 表示自己是高容量SD卡)。OCR的bit 31(busy)用來告知Host 自己的上電狀態,ACMD41命令是Host 向卡設備循環發送的,當SD卡按照ACMD41命令裏面的工作電壓完成上電初始化流程後,busy 會置 1,通知Host 已ready,可以繼續接收命令了。不能支持Host 指定電壓範圍的卡設備在收到ACMD41命令後會進入“Inactive”狀態。
  4. 當卡設備按照Host 要求的工作電壓,上電初始化完成後,Host 發送CMD2命令,獲取卡設備的CID信息;
  5. 發送CMD3命令,要求卡設備發佈自己的相對地址RCA,一旦主機接收到相對地址,卡片就進入數據傳輸模式的待機狀態(這時候主機可以通過 CMD3 命令重複要求卡片發佈相對地址)。

上面是SD V2.0的卡初始化與識別流程,並不包括SD I/O卡,在Host 收到ACMD41命令的響應後,就能識別出卡設備是MMC卡、SD V1.0標準容量卡(V1.0不支持高容量卡)、SD V2.0標準容量卡、SD V2.0高容量卡 這四類中的哪一類。在SD更新的標準中,又新增了SDXC和SDUC等更高容量的SD卡或UHS卡,這類卡的初始化與識別流程可以參考更新的標準。

  • SD卡數據傳輸模式

當相對卡地址(RCA)第一次發佈後,卡會處於“數據傳輸模式”,主機會在總線上所有的卡都被識別後進入這個模式,數據傳輸模式下可以讀寫SD卡的相關數據。當沒有數據傳輸時,DAT線被拉高。當有數據傳輸時,主機通過命令CMD7先選中卡,然後發送CMD9命令獲得卡設備的CSD信息,發送ACMD6命令設置總線位寬,此時可以對SD卡進行讀寫操作。SD卡數據傳輸模式的流程圖如下:
SD卡數據傳輸模式
上圖中SD卡的Sending-data狀態對於Host 來說就是從SD卡讀取數據的過程,SD卡的Receiving-data狀態對於Host 來說則是向SD卡寫入數據的過程,SD卡的Programming狀態則是SD卡將接收到的數據寫入內部存儲介質的過程。

數據傳輸時,發送方先拉低所有數據線,發送起始位;然後發送連續的數據流,數據流包含了有效數據;最後拉高所有數據線發送結束位。數據傳輸是和時鐘信號同時進行的,數據傳輸的有效性通過CRC校驗值驗證。主機Host 讀取、寫入、擦除數據的過程,都是通過前面介紹的Command/Response完成的,上圖中出現的每個命令的功能可以參考前面的命令列表。

3.2 SD I/O Card簡介

3.2.1 SD I/O Card寄存器簡介

SDIO本身只是一種接口技術,類似於SPI接口,通過 I/O 引腳來連接外部設備,並且與外圍設備之間傳輸數據,所以這些外圍設備又被稱爲SD I/O卡。SD I/O Card與SD Card提供的功能不同,卡內部的寄存器自然也不一樣(支持的Command/ Response也有差異,詳見前面的命令與響應類型列表),比如單純的SD I/O Card不支持CID、CSD、SCR、SSR、DSR等寄存器(Combo組合卡中的memory card依然支持SD卡的寄存器,這裏只討論單純的 I/O function card)。SD I/O Card有哪些寄存器來支持Command/Response呢?

SD I/O Card訪問與SD Memory Card訪問的不同之處在於:I/O Card無需FAT文件結構或塊的概念,就可以基於數據字節數直接寫入或讀取寄存器( I/O Card 可以看作字符設備);SD Memory Card 則依賴於固定塊長度的概念,命令讀取或寫入這些固定大小塊的倍數( Memory Card 可以看作塊設備)。

每個SDIO卡可能具有1到7個功能以及一個內置的存儲功能(Combo Card),功能是自包含的 I/O 設備,I/O 功能可能彼此相同或完全不同。所有 I/O 功能都組織爲寄存器的集合,每個 I/O 功能最多可以有131072(即217)個寄存器,這些寄存器在 I/O 卡內的寬度可以爲8、16或32位,所有尋址都基於字節訪問。單個寄存器讀寫訪問通常用於初始化 I/O 功能或讀取單個狀態或數據值(CMD52命令),對固定地址的多次訪問用於從卡中的數據 FIFO 寄存器讀取或寫入數據(CMD53命令),讀到遞增地址用於從卡內部的RAM區域讀取數據或向其中寫入數據集合。

SDIO卡具有固定的內部寄存器空間和功能專有區域,這些固定區域包含有關卡的信息以及某些強制和可選寄存器的信息,允許任何主機訪問。功能專有區域是每個功能專屬的區域,由應用程序定義標準SDIO功能的規範,由供應商定義非標準功能的規範,具有多種 I/O 功能的SDIO卡內部CIA(Common I/O Area)與可選CSA(Code Storage Area)映射圖如下:
SDIO卡內部結構
通用 I/O 區域(CIA)必須在所有SDIO卡上實現,主機通過對功能 0 的 I/O 讀取和寫入來訪問CIA。CIA中的寄存器提供了啓用或禁用 I/O 功能、控制中斷的產生以及選擇性加載支持 I/O 功能的軟件等操作,CIA中的寄存器還提供有關 I/O 功能的能力和需求信息。 CIA支持三種不同的寄存器結構:

  1. Card Common Control Registers (CCCR):可用於主機快速檢查並控制每個 I/O 功能的啓用和中斷;
  2. Function Basic Registers (FBR):每個受支持的 I/O 功能都有一個256字節的區域,用於主機快速確定每個功能的能力和需求,並啓用軟件加載以支持 I/O 功能;
  3. Card Information Structure (CIS):提供有關卡和各個功能的更完整信息。

SDIO CIA寄存器結構
爲了支持SDIO卡的“即插即用”概念,I/O 卡中包含的每個功能可能都需要包含一塊內存,用於存儲驅動程序或應用程序。另外,由於相同的SDIO卡可能會在多個不同的主機平臺上使用,因此可能需要爲每個功能提供幾個不同版本的代碼。一種選擇是將這些程序存儲在組合卡(Combo card)的標準SD memory部分中。另一種選擇是,將這些代碼加載到 I/O card 可選的CSA(Code Storage Area)中,CSA是一個獨立的16MB內存區域,可使用CSA地址指針或包含在FBR中的CSA窗口寄存器來訪問它。

3.2.2 SD I/O Card初始化流程

SD I/O Card與SD Memory Card類似,也支持兩種操作模式:卡識別模式與數據傳輸模式。SD I/O Card數據傳輸模式與SD Memory Card類似,只是交互的Command/Response不一樣,可對照SD Memory Card數據傳輸模式圖和SDIO卡支持的Command/Response列表理解,這裏就不再贅述了。

SD I/O Card的卡識別模式與SD Memory Card有較大差異,主要也是因爲兩者在卡識別與初始化過程中交互的Command/Response不一樣。SD I/O Card的卡識別模式不僅包括單純 I/O Card 或Memory Card 的識別,還包括組合卡Combo Card的識別,因此SD I/O Card的識別與初始化流程比較複雜:
SDIO初始化流程

  1. 上電或重新初始化時,memory card發送CMD0命令使卡進入“idle”狀態,I/O card則發送CMD52 (write to I/O Reset in CCCR)命令使卡進入“idle”狀態;
  2. Memory card發送CMD8命令識別卡支持的版本,I/O card則不需要分辨卡的版本;
  3. Memory card發送ACMD41命令探測卡的工作電壓是否符合Host 端的要求,並通過HCS和CCS交換所支持的容量信息;I/O card則發送CMD5命令探測卡的工作電壓是否符合Host 端的要求,單純的 I/O card沒有容量區分,響應也應包含 I/O 功能設備的信息,CMD5命令及響應格式如下:
    CMD5命令及響應格式
    命令CMD5的響應除了包含 I/O OCR(只有24位,沒有CCS與busy位)信息外,還包含 I/O 上電狀態位(當 I/O card按照CMD5命令裏面的工作電壓完成上電初始化後,該C位置1)、I/O 功能數量、是否附帶memory功能(也即是單純 I/O卡還是Combo卡)等信息。主機Host 會向 I/O card循環發送CMD5命令,直到收到的 R4 中 C 位置 1,不能支持Host 知道電壓的卡設備在收到CMD5命令後會進入“inactive”狀態;
  4. 當memory card或combo card按照Host 要求的工作電壓,上電初始化完成後,Host 發送CMD2命令,獲取卡設備的CID信息,I/O card沒有CID寄存器不需要該操作(會讀取CIA獲得 I/O card信息);
  5. 所有完成上電初始化後的卡設備發送CMD3命令,要求卡設備發佈自己的相對地址RCA,一旦主機接收到相對地址,卡片就進入數據傳輸模式的待機狀態(這時候主機可以通過 CMD3 命令重複要求卡片發佈相對地址)。

上面是SDIO V2.0 SD mode的卡初始化與識別流程,並不包括SPI mode,SPI mode的Command/Response格式與SD mode有所區別,SPI mode下的SDIO卡初始化流程自然也跟SD mode有所區別,想了解更多SPI mode的信息,可以參考《SDIO Simplified Specification》

四、SDIO Host 簡介

我們在外設驅動開發中常接觸的是SDIO Host controller(Adapter)端,我們編寫的驅動程序或應用程序被CPU取指、譯碼、執行,訪問外設實際是通過操作 Host Adapter 的寄存器,控制Host 端通過外設總線接口向Device 端發送符合協議規範的命令實現的。前面已經介紹了 Host 端訪問或控制 Device 端也是通過讀寫 Device 端的寄存器實現的,在對Device 端進行識別、初始化、數據傳輸時,也要按照 Device 的要求進行。下面介紹 CPU 如何通過系統總線AHB訪問或控制 Host 端,HAL 標準庫(芯片廠商ST提供的)爲我們提供了哪些訪問Host 端的接口,供我們進行驅動或應用開發呢?

前面介紹了SDIO結構框圖,包含了Host Controller、SDIO Bus、SDIO Card三部分,下面展示SDIO Host Controller(Adapter)的內部結構:
SDIO適配器結構
SDIO適配器主要包含四個模塊:寄存器、控制單元、命令通道、數據通道,HAL標準庫則爲這四個模塊分別提供了數據描述結構:

  • SDIO適配器寄存器

前面介紹SDIO Command時提到過,命令索引在 SDIO_CMD 寄存器裏面設置,命令參數則由寄存器 SDIO_ARG 設置,我們以STM32L475芯片爲例,看下SDIO Host 總共有哪些寄存器:

// libraries\STM32L4xx_HAL\CMSIS\Device\ST\STM32L4xx\Include\stm32l475xx.h

/* @brief Secure digital input/output Interface */
typedef struct
{
  __IO uint32_t POWER;          /*!< SDMMC power control register,    Address offset: 0x00 */
  __IO uint32_t CLKCR;          /*!< SDMMC clock control register,    Address offset: 0x04 */
  __IO uint32_t ARG;            /*!< SDMMC argument register,         Address offset: 0x08 */
  __IO uint32_t CMD;            /*!< SDMMC command register,          Address offset: 0x0C */
  __I uint32_t  RESPCMD;        /*!< SDMMC command response register, Address offset: 0x10 */
  __I uint32_t  RESP1;          /*!< SDMMC response 1 register,       Address offset: 0x14 */
  __I uint32_t  RESP2;          /*!< SDMMC response 2 register,       Address offset: 0x18 */
  __I uint32_t  RESP3;          /*!< SDMMC response 3 register,       Address offset: 0x1C */
  __I uint32_t  RESP4;          /*!< SDMMC response 4 register,       Address offset: 0x20 */
  __IO uint32_t DTIMER;         /*!< SDMMC data timer register,       Address offset: 0x24 */
  __IO uint32_t DLEN;           /*!< SDMMC data length register,      Address offset: 0x28 */
  __IO uint32_t DCTRL;          /*!< SDMMC data control register,     Address offset: 0x2C */
  __I uint32_t  DCOUNT;         /*!< SDMMC data counter register,     Address offset: 0x30 */
  __I uint32_t  STA;            /*!< SDMMC status register,           Address offset: 0x34 */
  __IO uint32_t ICR;            /*!< SDMMC interrupt clear register,  Address offset: 0x38 */
  __IO uint32_t MASK;           /*!< SDMMC mask register,             Address offset: 0x3C */
  uint32_t      RESERVED0[2];   /*!< Reserved, 0x40-0x44                                  */
  __I uint32_t  FIFOCNT;        /*!< SDMMC FIFO counter register,     Address offset: 0x48 */
  uint32_t      RESERVED1[13];  /*!< Reserved, 0x4C-0x7C                                  */
  __IO uint32_t FIFO;           /*!< SDMMC data FIFO register,        Address offset: 0x80 */
} SDMMC_TypeDef;

上面的CMD寄存器設置命令索引,ARG寄存器設置命令參數,RESPCMD與RESP1 ~ RESP4 則是用來處理響應的。其餘的寄存器有負責電源管理、時鐘控制、數據傳輸、狀態查詢、中斷管理等的,想有更詳細的瞭解,可以參閱STM32L475參考手冊。

  • SDIO控制單元

SDIO控制單元
SDIO控制單元包括電源管理與時鐘管理兩部分,控制SDIO_CK引腳的時鐘輸出和數據總線寬度,HAL標準庫提供了初始化配置結構體來實現控制單元的配置:

// libraries\STM32L4xx_HAL\STM32L4xx_HAL_Driver\Inc\stm32l4xx_ll_sdmmc.h

/* @brief  SDMMC Configuration Structure definition */
typedef struct
{
  uint32_t ClockEdge;            /*!< Specifies the clock transition on which the bit capture is made.
                                      This parameter can be a value of @ref SDMMC_LL_Clock_Edge                 */

#if !defined(STM32L4R5xx) && !defined(STM32L4R7xx) && !defined(STM32L4R9xx) && !defined(STM32L4S5xx) && !defined(STM32L4S7xx) && !defined(STM32L4S9xx)
  uint32_t ClockBypass;          /*!< Specifies whether the SDMMC Clock divider bypass is
                                      enabled or disabled.
                                      This parameter can be a value of @ref SDMMC_LL_Clock_Bypass               */
#endif /* !STM32L4R5xx && !STM32L4R7xx && !STM32L4R9xx && !STM32L4S5xx && !STM32L4S7xx && !STM32L4S9xx */

  uint32_t ClockPowerSave;       /*!< Specifies whether SDMMC Clock output is enabled or
                                      disabled when the bus is idle.
                                      This parameter can be a value of @ref SDMMC_LL_Clock_Power_Save           */

  uint32_t BusWide;              /*!< Specifies the SDMMC bus width.
                                      This parameter can be a value of @ref SDMMC_LL_Bus_Wide                   */

  uint32_t HardwareFlowControl;  /*!< Specifies whether the SDMMC hardware flow control is enabled or disabled.
                                      This parameter can be a value of @ref SDMMC_LL_Hardware_Flow_Control      */

  uint32_t ClockDiv;             /*!< Specifies the clock frequency of the SDMMC controller.
                                      This parameter can be a value between Min_Data = 0 and Max_Data = 1023   */
}SDMMC_InitTypeDef;

/* @defgroup SDMMC_LL_Clock_Edge Clock Edge */
#define SDMMC_CLOCK_EDGE_RISING               ((uint32_t)0x00000000U)
#define SDMMC_CLOCK_EDGE_FALLING              SDMMC_CLKCR_NEGEDGE

/* @defgroup SDMMC_LL_Clock_Bypass Clock Bypass */
#define SDMMC_CLOCK_BYPASS_DISABLE             ((uint32_t)0x00000000U)
#define SDMMC_CLOCK_BYPASS_ENABLE              SDMMC_CLKCR_BYPASS

/* @defgroup SDMMC_LL_Clock_Power_Save Clock Power Saving */
#define SDMMC_CLOCK_POWER_SAVE_DISABLE         ((uint32_t)0x00000000U)
#define SDMMC_CLOCK_POWER_SAVE_ENABLE          SDMMC_CLKCR_PWRSAV

/* @defgroup SDMMC_LL_Bus_Wide Bus Width */
#define SDMMC_BUS_WIDE_1B                      ((uint32_t)0x00000000U)
#define SDMMC_BUS_WIDE_4B                      SDMMC_CLKCR_WIDBUS_0
#define SDMMC_BUS_WIDE_8B                      SDMMC_CLKCR_WIDBUS_1

/* @defgroup SDMMC_LL_Hardware_Flow_Control Hardware Flow Control */
#define SDMMC_HARDWARE_FLOW_CONTROL_DISABLE    ((uint32_t)0x00000000U)
#define SDMMC_HARDWARE_FLOW_CONTROL_ENABLE     SDMMC_CLKCR_HWFC_EN
  • SDIO命令通道

SDIO命令通道
SDIO命令通道控制命令發送與響應接收,HAL標準庫提供了命令初始化結構體來實現命令通道的配置:

// libraries\STM32L4xx_HAL\STM32L4xx_HAL_Driver\Inc\stm32l4xx_ll_sdmmc.h

/* @brief  SDMMC Command Control structure */
typedef struct
{
  uint32_t Argument;            /*!< Specifies the SDMMC command argument which is sent
                                     to a card as part of a command message. If a command
                                     contains an argument, it must be loaded into this register
                                     before writing the command to the command register.              */

  uint32_t CmdIndex;            /*!< Specifies the SDMMC command index. It must be Min_Data = 0 and
                                     Max_Data = 64                                                    */

  uint32_t Response;            /*!< Specifies the SDMMC response type.
                                     This parameter can be a value of @ref SDMMC_LL_Response_Type         */

  uint32_t WaitForInterrupt;    /*!< Specifies whether SDMMC wait for interrupt request is
                                     enabled or disabled.
                                     This parameter can be a value of @ref SDMMC_LL_Wait_Interrupt_State  */

  uint32_t CPSM;                /*!< Specifies whether SDMMC Command path state machine (CPSM)
                                     is enabled or disabled.
                                     This parameter can be a value of @ref SDMMC_LL_CPSM_State            */
}SDMMC_CmdInitTypeDef;

/* @defgroup SDMMC_LL_Response_Type Response Type */
#define SDMMC_RESPONSE_NO                    ((uint32_t)0x00000000U)
#define SDMMC_RESPONSE_SHORT                 SDMMC_CMD_WAITRESP_0
#define SDMMC_RESPONSE_LONG                  SDMMC_CMD_WAITRESP

/* @defgroup SDMMC_LL_Wait_Interrupt_State Wait Interrupt */
#define SDMMC_WAIT_NO                        ((uint32_t)0x00000000U)
#define SDMMC_WAIT_IT                        SDMMC_CMD_WAITINT
#define SDMMC_WAIT_PEND                      SDMMC_CMD_WAITPEND

/* @defgroup SDMMC_LL_CPSM_State CPSM State */
#define SDMMC_CPSM_DISABLE                   ((uint32_t)0x00000000U)
#define SDMMC_CPSM_ENABLE                    SDMMC_CMD_CPSMEN

結構體SDMMC_CmdInitTypeDef 中的CPSM(Command path state machine)是由命令通道的狀態標誌和控制邏輯共同組成的一個控制中心,它能夠根據控制信號按照預先設定的狀態進行狀態轉移,協調相關信號動作,完成特定操作,命令通道狀態機的狀態遷移圖如下:
SDIO命令通道狀態機
當寫入命令寄存器並設置了CPSM使能位,CPSM 進入Send 狀態,開始發送命令。命令發送完成時,CPSM 設置狀態標誌並在不需要響應時進入 Idle 狀態,當需要收到響應時則進入 Wait 狀態,命令定時器開始運行。待Device開始返回響應,則CPSM從Wait狀態進入Receive狀態,響應接收完成後進入Idle狀態,當CPSM進入 Receive 狀態之前產生了超時,則設置超時標誌並進入 Idle 狀態。一個正常的需要返回響應的命令傳輸狀態遷移爲:Idle --> Send --> Wait --> Receive --> Idle。

  • SDIO數據通道

SDIO數據通道
SDIO數據通道控制數據的發送與接收,FIFO(first in first out)是一個先進先出數據緩衝區,內部按照32位對齊,共128 Bytes,同時具有發送和接收單元,HAL標準庫提供了數據初始化結構體來實現數據通道的配置:

// libraries\STM32L4xx_HAL\STM32L4xx_HAL_Driver\Inc\stm32l4xx_ll_sdmmc.h

/* @brief  SDMMC Data Control structure */
typedef struct
{
  uint32_t DataTimeOut;         /*!< Specifies the data timeout period in card bus clock periods.  */

  uint32_t DataLength;          /*!< Specifies the number of data bytes to be transferred.         */

  uint32_t DataBlockSize;       /*!< Specifies the data block size for block transfer.
                                     This parameter can be a value of @ref SDMMC_LL_Data_Block_Size    */

  uint32_t TransferDir;         /*!< Specifies the data transfer direction, whether the transfer
                                     is a read or write.
                                     This parameter can be a value of @ref SDMMC_LL_Transfer_Direction */

  uint32_t TransferMode;        /*!< Specifies whether data transfer is in stream or block mode.
                                     This parameter can be a value of @ref SDMMC_LL_Transfer_Type      */

  uint32_t DPSM;                /*!< Specifies whether SDMMC Data path state machine (DPSM)
                                     is enabled or disabled.
                                     This parameter can be a value of @ref SDMMC_LL_DPSM_State         */
}SDMMC_DataInitTypeDef;

/* @defgroup SDMMC_LL_Data_Block_Size  Data Block Size */
#define SDMMC_DATABLOCK_SIZE_1B               ((uint32_t)0x00000000U)
#define SDMMC_DATABLOCK_SIZE_2B               SDMMC_DCTRL_DBLOCKSIZE_0
#define SDMMC_DATABLOCK_SIZE_4B               SDMMC_DCTRL_DBLOCKSIZE_1
#define SDMMC_DATABLOCK_SIZE_8B               (SDMMC_DCTRL_DBLOCKSIZE_0|SDMMC_DCTRL_DBLOCKSIZE_1)
#define SDMMC_DATABLOCK_SIZE_16B              SDMMC_DCTRL_DBLOCKSIZE_2
#define SDMMC_DATABLOCK_SIZE_32B              (SDMMC_DCTRL_DBLOCKSIZE_0|SDMMC_DCTRL_DBLOCKSIZE_2)
#define SDMMC_DATABLOCK_SIZE_64B              (SDMMC_DCTRL_DBLOCKSIZE_1|SDMMC_DCTRL_DBLOCKSIZE_2)
#define SDMMC_DATABLOCK_SIZE_128B             (SDMMC_DCTRL_DBLOCKSIZE_0|SDMMC_DCTRL_DBLOCKSIZE_1|SDMMC_DCTRL_DBLOCKSIZE_2)
#define SDMMC_DATABLOCK_SIZE_256B             SDMMC_DCTRL_DBLOCKSIZE_3
#define SDMMC_DATABLOCK_SIZE_512B             (SDMMC_DCTRL_DBLOCKSIZE_0|SDMMC_DCTRL_DBLOCKSIZE_3)
#define SDMMC_DATABLOCK_SIZE_1024B            (SDMMC_DCTRL_DBLOCKSIZE_1|SDMMC_DCTRL_DBLOCKSIZE_3)
#define SDMMC_DATABLOCK_SIZE_2048B            (SDMMC_DCTRL_DBLOCKSIZE_0|SDMMC_DCTRL_DBLOCKSIZE_1|SDMMC_DCTRL_DBLOCKSIZE_3)
#define SDMMC_DATABLOCK_SIZE_4096B            (SDMMC_DCTRL_DBLOCKSIZE_2|SDMMC_DCTRL_DBLOCKSIZE_3)
#define SDMMC_DATABLOCK_SIZE_8192B            (SDMMC_DCTRL_DBLOCKSIZE_0|SDMMC_DCTRL_DBLOCKSIZE_2|SDMMC_DCTRL_DBLOCKSIZE_3)
#define SDMMC_DATABLOCK_SIZE_16384B           (SDMMC_DCTRL_DBLOCKSIZE_1|SDMMC_DCTRL_DBLOCKSIZE_2|SDMMC_DCTRL_DBLOCKSIZE_3)

/* @defgroup SDMMC_LL_Transfer_Direction Transfer Direction */
#define SDMMC_TRANSFER_DIR_TO_CARD            ((uint32_t)0x00000000U)
#define SDMMC_TRANSFER_DIR_TO_SDMMC            SDMMC_DCTRL_DTDIR

/* @defgroup SDMMC_LL_Transfer_Type Transfer Type */
#define SDMMC_TRANSFER_MODE_BLOCK             ((uint32_t)0x00000000U)
#define SDMMC_TRANSFER_MODE_STREAM            SDMMC_DCTRL_DTMODE

/* @defgroup SDMMC_LL_DPSM_State DPSM State */
#define SDMMC_DPSM_DISABLE                    ((uint32_t)0x00000000U)
#define SDMMC_DPSM_ENABLE                     SDMMC_DCTRL_DTEN

結構體SDMMC_DataInitTypeDef 中的DPSM(Data path state machine)可以控制數據的發送、接收流程,數據通道狀態機的狀態遷移圖如下:
SDIO數據通道狀態機
發送流程:設置數據控制寄存器的數據使能位以及數據方向爲“Host to Card”,此時DPSM 進入Wait_S 狀態,一旦FIFO中有數據寫入時,DPSM 進入 Send 狀態,開始發送數據到卡設備。根據數據控制寄存器中傳輸模式位的設置,可以是數據塊傳輸或數據流傳輸:在塊模式下,當數據塊計數器爲0,DPSM發送CRC校驗碼,然後發送結束位,並進入 Busy 狀態;在流模式下,當使能位爲高同時數據計數器不爲0,DPSM向卡發送數據,然後進入 Idle 狀態。

接收流程:設置數據控制寄存器的數據使能位以及數據方向爲“Card to Host”,此時DPSM 進入Wait_R 狀態並等待開始位,當收到開始位時, DPSM進入 Receive 狀態,同時數據通道子單元開始從卡接收數據,接收到的串行數據被組合爲字節並寫入數據FIFO。根據數據控制寄存器中傳輸模式位的設置,也可以是數據塊傳輸或數據流傳輸:在塊模式下,當數據塊計數器爲0,DPSM等待接收CRC碼,如果接收到的代碼與內部產生的CRC碼匹配,則DPSM進入Wait_R 狀態,否則設置CRC失敗狀態標誌同時DPSM進入到 Idle 狀態;在流模式下,當數據計數器不爲0,DPSM接收數據,當計數器爲0,將移位寄存器中的剩餘數據寫入數據FIFO,同時DPSM進入Wait_R 狀態。

  • HAL提供的SDIO配置接口函數

前面HAL標準庫爲我們提供了四個結構體,方便我們對SDIO Host controller 的四個模塊分別進行配置,既然有結構體自然有接口函數。我們在進行程序開發時,只需要調用HAL提供的接口函數就可以控制這幾個模塊實現我們想要的功能,HAL爲我們提供的接口函數如下:

// libraries\STM32L4xx_HAL\STM32L4xx_HAL_Driver\Inc\stm32l4xx_ll_sdmmc.h

/* Initialization/de-initialization functions  **********************************/
HAL_StatusTypeDef SDMMC_Init(SDMMC_TypeDef *SDMMCx, SDMMC_InitTypeDef Init);

/* I/O operation functions  *****************************************************/
uint32_t          SDMMC_ReadFIFO(SDMMC_TypeDef *SDMMCx);
HAL_StatusTypeDef SDMMC_WriteFIFO(SDMMC_TypeDef *SDMMCx, uint32_t *pWriteData);

/* Peripheral Control functions  ************************************************/
HAL_StatusTypeDef SDMMC_PowerState_ON(SDMMC_TypeDef *SDMMCx);
HAL_StatusTypeDef SDMMC_PowerState_OFF(SDMMC_TypeDef *SDMMCx);
uint32_t          SDMMC_GetPowerState(SDMMC_TypeDef *SDMMCx);

/* Command path state machine (CPSM) management functions */
HAL_StatusTypeDef SDMMC_SendCommand(SDMMC_TypeDef *SDMMCx, SDMMC_CmdInitTypeDef *Command);
uint8_t           SDMMC_GetCommandResponse(SDMMC_TypeDef *SDMMCx);
uint32_t          SDMMC_GetResponse(SDMMC_TypeDef *SDMMCx, uint32_t Response);

/* Data path state machine (DPSM) management functions */
HAL_StatusTypeDef SDMMC_ConfigData(SDMMC_TypeDef *SDMMCx, SDMMC_DataInitTypeDef* Data);
uint32_t          SDMMC_GetDataCounter(SDMMC_TypeDef *SDMMCx);
uint32_t          SDMMC_GetFIFOCount(SDMMC_TypeDef *SDMMCx);

/* SDMMC Cards mode management functions */
HAL_StatusTypeDef SDMMC_SetSDMMCReadWaitMode(SDMMC_TypeDef *SDMMCx, uint32_t SDMMC_ReadWaitMode);

/* SDMMC Commands management functions */
uint32_t SDMMC_CmdBlockLength(SDMMC_TypeDef *SDMMCx, uint32_t BlockSize);
uint32_t SDMMC_CmdReadSingleBlock(SDMMC_TypeDef *SDMMCx, uint32_t ReadAdd);
uint32_t SDMMC_CmdReadMultiBlock(SDMMC_TypeDef *SDMMCx, uint32_t ReadAdd);
uint32_t SDMMC_CmdWriteSingleBlock(SDMMC_TypeDef *SDMMCx, uint32_t WriteAdd);
uint32_t SDMMC_CmdWriteMultiBlock(SDMMC_TypeDef *SDMMCx, uint32_t WriteAdd);
uint32_t SDMMC_CmdEraseStartAdd(SDMMC_TypeDef *SDMMCx, uint32_t StartAdd);
uint32_t SDMMC_CmdSDEraseStartAdd(SDMMC_TypeDef *SDMMCx, uint32_t StartAdd);
uint32_t SDMMC_CmdEraseEndAdd(SDMMC_TypeDef *SDMMCx, uint32_t EndAdd);
uint32_t SDMMC_CmdSDEraseEndAdd(SDMMC_TypeDef *SDMMCx, uint32_t EndAdd);
uint32_t SDMMC_CmdErase(SDMMC_TypeDef *SDMMCx);
uint32_t SDMMC_CmdStopTransfer(SDMMC_TypeDef *SDMMCx);
uint32_t SDMMC_CmdSelDesel(SDMMC_TypeDef *SDMMCx, uint64_t Addr);
uint32_t SDMMC_CmdGoIdleState(SDMMC_TypeDef *SDMMCx);
uint32_t SDMMC_CmdOperCond(SDMMC_TypeDef *SDMMCx);
uint32_t SDMMC_CmdAppCommand(SDMMC_TypeDef *SDMMCx, uint32_t Argument);
uint32_t SDMMC_CmdAppOperCommand(SDMMC_TypeDef *SDMMCx, uint32_t Argument);
uint32_t SDMMC_CmdBusWidth(SDMMC_TypeDef *SDMMCx, uint32_t BusWidth);
uint32_t SDMMC_CmdSendSCR(SDMMC_TypeDef *SDMMCx);
uint32_t SDMMC_CmdSendCID(SDMMC_TypeDef *SDMMCx);
uint32_t SDMMC_CmdSendCSD(SDMMC_TypeDef *SDMMCx, uint32_t Argument);
uint32_t SDMMC_CmdSetRelAdd(SDMMC_TypeDef *SDMMCx, uint16_t *pRCA);
uint32_t SDMMC_CmdSendStatus(SDMMC_TypeDef *SDMMCx, uint32_t Argument);
uint32_t SDMMC_CmdStatusRegister(SDMMC_TypeDef *SDMMCx);
uint32_t SDMMC_CmdOpCondition(SDMMC_TypeDef *SDMMCx, uint32_t Argument);
uint32_t SDMMC_CmdSwitch(SDMMC_TypeDef *SDMMCx, uint32_t Argument);

想了解SDIO總線設備驅動模型或SDIO WIFI 驅動開發的讀者,可以參考博客:SDIO設備對象管理 + AP6181(BCM43362) WiFi模塊

更多文章:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章