SD/MMC Cardmode BSP
The SD/MMC cardmode protocol is unique to SD- and MMC-compliant devices. The generic driver handles the peculiarities for initializing, reading and writing a card (including state transitions and error handling), but each CPU has a different host controller that must be individually ported. To that end, a BSP, supplementary to the general µC/FS BSP, is required that abstracts the SD/MMC interface. The port includes one code file:
FS_DEV_SD_CARD_BSP.C
This file is generally placed with other BSP files in a directory named according to the following rubric:
\Micrium\Software\EvalBoards\<manufacturer>\<board_name>
\<compiler>\BSP\
Several example ports are included in the µC/FS distribution in files named according to the following rubric:
\Micrium\Software\uC-FS\Examples\BSP\Dev\SD\Card\<cpu_name>
Function | Description |
---|---|
FSDev_SD_Card_BSP_Open() | Open (initialize) SD/MMC card interface. |
FSDev_SD_Card_BSP_Close() | Close (uninitialize) SD/MMC card interface. |
FSDev_SD_Card_BSP_Lock() | Acquire SD/MMC card bus lock. |
FSDev_SD_Card_BSP_Unlock() | Release SD/MMC card bus lock. |
FSDev_SD_Card_BSP_CmdStart() | Start a command. |
FSDev_SD_Card_BSP_CmdWaitEnd() | Wait for a command to end and get response. |
FSDev_SD_Card_BSP_CmdDataRd() | Read data following command. |
FSDev_SD_Card_BSP_CmdDataWr() | Write data following command. |
FSDev_SD_Card_BSP_GetBlkCntMax() | Get max block count. |
FSDev_SD_Card_BSP_GetBusWidthMax() | Get maximum bus width, in bits. |
FSDev_SD_Card_BSP_SetBusWidth() | Set bus width. |
FSDev_SD_Card_BSP_SetClkFreq() | Set clock frequency. |
FSDev_SD_Card_BSP_SetTimeoutData() | Set data timeout. |
FSDev_SD_Card_BSP_SetTimeoutResp() | Set response timeout. |
Each BSP must implement the functions in the table above. (For information about creating a port for a platform accessing a SD/MMC device in SPI mode, see SD/MMC SPI Mode BSP. This software interface was designed by reviewing common host implementations as well as the SD card association’s SD Specification Part A2 – SD Host Controller Simplified Specification, Version 2.00, which recommends a host architecture and provides the state machines that would guide operations. Example function implementations for a theoretical compliant host are provided in this chapter. Common advanced requirements (such as multiple cards per slot) and optimizations (such as DMA) are possible. No attempt has been made, however, to accommodate non-storage devices that are accessed on a SD/MMC cardmode, including SDIO devices.
The core operation being abstracted is the command/response sequence for high-level card transactions. The key functions, CmdStart()
, CmdWaitEnd()
, CmdDataRd()
and CmdDataWr()
, are called within the state machine of the figure below. If return error from one of the functions will abort the state machine, so the requisite considerations, such as preparing for the next command or preventing further interrupts, must be handled if an operation cannot be completed.
The remaining functions either investigate host capabilities (GetBlkCntMax()
, GetBusWidthMax()
) or set operational parameters (SetBusWidth()
, SetClkFreq()
, SetTimeoutData()
, SetTimeoutResp()
). Together, these function sets help configure a new card upon insertion. Note that the parameters configured by the ‘set’ functions belong to the card, not the slot; if multiple cards may be multiplexed in a single slot, these must be saved when set and restored whenever Lock()
is called.
Two elements of host behavior routinely influence implementation and require design choices. First, block data can typically be read/written either directly from a FIFO or transferred automatically by the peripheral to/from a memory buffer with DMA. While the former approach may be simpler—no DMA controller need be setup—it may not be reliable. Unless the host can stop the host clock upon FIFO underrun (for write) or overrun (for read), effectively pausing the operation from the card’s perspective, transfers at high clock frequency or multiple-bus configurations will probably fail. Interrupts or other tasks can interrupt the operation, or the CPU just may be unable to fill the FIFO fast enough. DMA avoids those pitfalls by offloading the responsibility for moving data directly to the CPU.
Second, the completion of operations such as command execution and data read/write are often signaled via interrupts (unless some error occurs, whereupon a different interrupt is triggered). During large transfers, these operations occur frequently and the typical wait between initiation and completion is measured in microseconds. On most platforms, polling the interrupt status register within the task performs better (i.e., results in faster reads and writes) than waiting on a semaphore for an asynchronous notification from the ISR, because the penalty of extra context switches is not incurred.