寫在前面:
本文章旨在總結備份、方便以後查詢,由於是個人總結,如有不對,歡迎指正;另外,內容大部分來自網絡、書籍、和各類手冊,如若侵權請告知,馬上刪帖致歉。
目錄
一、SDIO接口
SDIO 就是 SD 的 I/O 接口的意思;用來解決和模塊間的高速通信問題,常見於嵌入式系統。速度可達到 25Mbps
在說 SDIO過程中,往往還涉及到 MMC跟 SD,以至於它們以 MMC/SD/SDIO方式出現
MMC、SD、SDIO的技術本質是一樣的(使用相同的總線規範,等等),都是從MMC規範演化而來;
MMC強調的是多媒體存儲(MM,MultiMedia);
SD強調的是安全和數據保護(S,Secure);
SDIO是從SD演化出來的,強調的是接口(IO,Input/Output),不再關注另一端的具體形態(可以是WIFI設備、Bluetooth設備、GPS等等)。
MMC/SD/SDIO evolution 如下圖:
在 STM32中,SDIO包含 2個部分:
● SDIO適配器模塊:實現所有 MMC/SD/SD I/O卡的相關功能,如時鐘的產生、命令和數據的傳送。
● AHB總線接口:操作SDIO適配器模塊中的寄存器,併產生中斷和 DMA請求信號。
二、SD Card
1、SD卡構架
2、接口定義
3、流程圖
4、SD卡寄存器
卡的接口中定義了6 個寄存器:OCR,CID,CSD,RCA,DSR,SCR。這些寄存器只能通過對應的命令訪問
CR,CID,CSD,SCR 寄存器保存了卡/內容的特定信息,RCA和 DSR 寄存器是配置寄存器,存儲目前的配置參數
然後簡單瞭解一下各寄存器
- OCR寄存器
- CID寄存器
- CSD寄存器
- RCA寄存器
可寫的 16位卡相對地址寄存器,在卡的初始化期間,由卡向外發佈的卡地址。這個地址用於卡初始化進程之後,主機同卡之間的交互尋址。默認的 RCA 寄存器值是 0x0000,這個值保留着,用來通過 CMD7 設置所有卡到 stand-by 狀態
- DSR寄存器
16 位驅動階段寄存器,是可選的;可以用來在擴展操作條件中,提高總線性能(受總線長度,傳輸速率和卡數目的影響)。CSD 寄存器中有 DSR 寄存器是否使用的標誌。DSR 默認值是 0x404
- SCR 寄存器
三、代碼實驗操作
是否給上面的信息搞暈了呢?寫它的驅動是不可能的,看看還行;在 STM32標準庫中的 ...\STM32F10x_StdPeriph_Lib_V3.5.0\Utilities\STM32_EVAL\Common路徑中,是有提供 SD Card庫操作的,所以我們只需要移植修改一下,並且爲它提供底層端口操作就行了
在 stm32_eval_sdio_sd.c源文件中,我們需要提供兩個接口給它, SD_LowLevel_DeInit();跟 SD_LowLevel_Init();函數的實現,所以我們另外創建一個專門提供給 SD Card庫的底層源文件 sdcard_base.c
sdcard_base.c 源文件
#include "sdcard_base.h"
#include "stm32_eval_sdio_sd.h"
/************************************************
函數名稱 : SD_DMAEndOfTransferStatus
功 能 : SD DMA結束傳輸狀態
參 數 : 無
返 回 值 : 無
*************************************************/
uint32_t SD_DMAEndOfTransferStatus(void)
{
return (uint32_t)DMA_GetFlagStatus(DMA2_FLAG_TC4);
}
/************************************************
函數名稱 : SD_LowLevel_DMA_TxConfig
功 能 : 配置 SDIO DMA發送請求
參 數 : BufferDST ---- 指向目標緩衝區的指針
BufferSize ---- 緩衝區大小
返 回 值 : 無
*************************************************/
void SD_LowLevel_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);
}
/************************************************
函數名稱 : SD_LowLevel_DMA_RxConfig
功 能 : 配置 SDIO DMA接收請求
參 數 : BufferDST ---- 指向目標緩衝區的指針
BufferSize ---- 緩衝區大小
返 回 值 : 無
*************************************************/
void SD_LowLevel_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);
/*!< 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)BufferDST;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
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);
}
/************************************************
函數名稱 : SD_LowLevel_Init
功 能 : SD底層初始化
參 數 : 無
返 回 值 : 無
*************************************************/
void SD_LowLevel_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/*!< Dx /CLK and CMD clock enable */
SD_SDIO_IO_APBxClock_FUN(SD_SDIO_Dx_CLK | SD_SDIO_CLK_CLK | SD_SDIO_CMD_CLK, ENABLE);
#if _SD_SPECCY
/*!< sd detect clock enable */
SD_DETECT_APBxClock_FUN(SD_DETECT_GPIO_CLK, ENABLE);
#endif /* _SD_SPECCY */
/*!< Configure PC.08, PC.09, PC.10, PC.11, PC.12 pin: D0, D1, D2, D3, CLK pin */
GPIO_InitStructure.GPIO_Pin = SD_SDIO_D0_PINS | SD_SDIO_D1_PINS | SD_SDIO_D2_PINS \
| SD_SDIO_D3_PINS | SD_SDIO_CLK_PINS;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(SD_SDIO_IO_PORT, &GPIO_InitStructure);
/*!< Configure PD.02 CMD line */
GPIO_InitStructure.GPIO_Pin = SD_SDIO_CMD_PINS;
GPIO_Init(SD_SDIO_CMD_PORT, &GPIO_InitStructure);
#if _SD_SPECCY
/*!< Configure SD_SPI_DETECT_PIN pin: SD Card detect pin */
GPIO_InitStructure.GPIO_Pin = SD_DETECT_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(SD_DETECT_GPIO_PORT, &GPIO_InitStructure);
#endif /* _SD_SPECCY */
/* Enable the SDIO AHB Clock */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_SDIO, ENABLE);
/* Enable the DMA2 Clock */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);
/* Configure the NVIC Preemption Priority Bits */
NVIC_InitStructure.NVIC_IRQChannel = SDIO_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/************************************************
函數名稱 : SD_LowLevel_DeInit
功 能 : SD底層復位
參 數 : 無
返 回 值 : 無
*************************************************/
void SD_LowLevel_DeInit(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/*!< Disable SDIO Clock */
SDIO_ClockCmd(DISABLE);
/*!< Set Power State to OFF */
SDIO_SetPowerState(SDIO_PowerState_OFF);
/*!< DeInitializes the SDIO peripheral */
SDIO_DeInit();
/*!< Disable the SDIO AHB Clock */
SD_SDIO_APBxClock_FUN(RCC_AHBPeriph_SDIO, DISABLE);
/*!< Configure PC.08, PC.09, PC.10, PC.11, PC.12 pin: D0, D1, D2, D3, CLK pin */
GPIO_InitStructure.GPIO_Pin = SD_SDIO_D0_PINS | SD_SDIO_D1_PINS | SD_SDIO_D2_PINS \
| SD_SDIO_D3_PINS | SD_SDIO_CLK_PINS;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(SD_SDIO_IO_PORT, &GPIO_InitStructure);
/*!< Configure PD.02 CMD line */
GPIO_InitStructure.GPIO_Pin = SD_SDIO_CMD_PINS;
GPIO_Init(SD_SDIO_CMD_PORT, &GPIO_InitStructure);
}
/************************************************************************/
/* STM32F10x SDIO Interrupt Handlers */
/************************************************************************/
/**
* @brief This function handles SDIO global interrupt request.
* @param None
* @retval None
*/
void SDIO_IRQHandler(void)
{
/* Process All SDIO Interrupt Sources */
SD_ProcessIRQSrc();
}
/*---------------------------- END OF FILE ----------------------------*/
上面的代碼就不用怎麼說了,SDIO + DMA + 中斷;其中,值得注意的是,我們需要在 SDIO中斷中,放入 SD Card庫提供的中斷處理函數 SD_ProcessIRQSrc();
然後,有時候我們在設計 SD Card硬件接口時,不一定都加上 SD Card硬件檢測功能(即是否接上 SD Card檢測引腳),那麼,我們就修改一下它的 SD Card庫裏的函數,對應的是 SD_Detect();函數,我們只需加個宏來作決定就好了,更改如下:
uint8_t SD_Detect(void)
{
#if _SD_SPECCY
__IO uint8_t status = SD_PRESENT;
/*!< Check GPIO to detect SD */
if (GPIO_ReadInputDataBit(SD_DETECT_GPIO_PORT, SD_DETECT_PIN) != Bit_RESET)
{
status = SD_NOT_PRESENT;
}
return status;
#else
return SD_PRESENT;
#endif /* _SD_SPECCY */
}
最後我們只需在宏這裏選擇
/* 無硬件 IO檢測則定義爲 0 */
#define _SD_SPECCY 0
接口補充完成了,我們可以編寫一下測試程序來測試一下,而 ST官方也剛好有提供模版,那我們可以模仿一下它的代碼,隨便了解基本操作的流程關係
#include "user_sdcard.h"
#include <string.h>
#include "bsp_uart.h"
/* 測試功能宏選擇 */
#define _SDCARD_TEST 1
/* User defined variables ----------------------------------------------------*/
#if _SDCARD_TEST
/* Private typedef -----------------------------------------------------------*/
typedef enum {FAILED = 0, PASSED = !FAILED} TestStatus;
/* Private macro -------------------------------------------------------------*/
#define NUMBER_OF_BLOCKS 32 /* For Multi Blocks operation (Read/Write) */
#define MULTI_BUFFER_SIZE (SD_BLOCK_SIZE * NUMBER_OF_BLOCKS)
#define SD_OPERATION_ERASE 0
#define SD_OPERATION_BLOCK 1
#define SD_OPERATION_MULTI_BLOCK 2
#define SD_OPERATION_END 3
/* Private variables ---------------------------------------------------------*/
uint8_t Buffer_Block_Tx[SD_BLOCK_SIZE], Buffer_Block_Rx[SD_BLOCK_SIZE];
uint8_t Buffer_MultiBlock_Tx[MULTI_BUFFER_SIZE], Buffer_MultiBlock_Rx[MULTI_BUFFER_SIZE];
volatile TestStatus EraseStatus = FAILED, TransferStatus1 = FAILED, TransferStatus2 = FAILED;
SD_Error Status = SD_OK;
__IO uint32_t SDCardOperation = SD_OPERATION_ERASE;
/* Private function prototypes -----------------------------------------------*/
void SD_EraseTest(void);
void SD_SingleBlockTest(void);
void SD_MultiBlockTest(void);
void Fill_Buffer(uint8_t *pBuffer, uint32_t BufferLength, uint32_t Offset);
TestStatus Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint32_t BufferLength);
TestStatus eBuffercmp(uint8_t* pBuffer, uint32_t BufferLength);
#endif /* _SDCARD_TEST */
/* Test functions ------------------------------------------------------------*/
#if _SDCARD_TEST
/************************************************
函數名稱 : SD_test
功 能 : SD測試
參 數 : 無
返 回 值 : 無
*************************************************/
void SD_test(void)
{
/*------------------------------ SD Init ---------------------------------- */
if((Status = SD_Init()) != SD_OK)
{
DEBUG_PRINTF("SD卡初始化失敗,請確保SD卡已正確接入,或換一張SD卡測試!\n");
}
while((Status == SD_OK) && (SDCardOperation != SD_OPERATION_END) && (SD_Detect()== SD_PRESENT))
{
switch(SDCardOperation)
{
/*-------------------------- SD Erase Test ---------------------------- */
case (SD_OPERATION_ERASE):
{
SD_EraseTest();
SDCardOperation = SD_OPERATION_BLOCK;
break;
}
/*-------------------------- SD Single Block Test --------------------- */
case (SD_OPERATION_BLOCK):
{
SD_SingleBlockTest();
SDCardOperation = SD_OPERATION_MULTI_BLOCK;
break;
}
/*-------------------------- SD Multi Blocks Test --------------------- */
case (SD_OPERATION_MULTI_BLOCK):
{
SD_MultiBlockTest();
SDCardOperation = SD_OPERATION_END;
break;
}
}
}
}
/**
* @brief Tests the SD card erase operation.
* @param None
* @retval None
*/
void SD_EraseTest(void)
{
/*------------------- Block Erase ------------------------------------------*/
if (Status == SD_OK)
{
/* Erase NumberOfBlocks Blocks of WRITE_BL_LEN(512 Bytes) */
Status = SD_Erase(0x00, (SD_BLOCK_SIZE * NUMBER_OF_BLOCKS));
}
if (Status == SD_OK)
{
Status = SD_ReadMultiBlocks(Buffer_MultiBlock_Rx, 0x00, SD_BLOCK_SIZE, NUMBER_OF_BLOCKS);
/* Check if the Transfer is finished */
Status = SD_WaitReadOperation();
/* Wait until end of DMA transfer */
while(SD_GetStatus() != SD_TRANSFER_OK);
}
/* Check the correctness of erased blocks */
if (Status == SD_OK)
{
EraseStatus = eBuffercmp(Buffer_MultiBlock_Rx, MULTI_BUFFER_SIZE);
}
if(EraseStatus == PASSED)
{
DEBUG_PRINTF("SD卡擦除測試成功!\n");
}
else
{
DEBUG_PRINTF("SD卡擦除測試失敗!\n");
}
}
/**
* @brief Tests the SD card Single Blocks operations.
* @param None
* @retval None
*/
void SD_SingleBlockTest(void)
{
/*------------------- Block Read/Write --------------------------*/
/* Fill the buffer to send */
Fill_Buffer(Buffer_Block_Tx, SD_BLOCK_SIZE, 0x320F);
if (Status == SD_OK)
{
/* Write block of 512 bytes on address 0 */
Status = SD_WriteBlock(Buffer_Block_Tx, 0x00, SD_BLOCK_SIZE);
/* Check if the Transfer is finished */
Status = SD_WaitWriteOperation();
while(SD_GetStatus() != SD_TRANSFER_OK);
}
if (Status == SD_OK)
{
/* Read block of 512 bytes from address 0 */
Status = SD_ReadBlock(Buffer_Block_Rx, 0x00, SD_BLOCK_SIZE);
/* Check if the Transfer is finished */
Status = SD_WaitReadOperation();
while(SD_GetStatus() != SD_TRANSFER_OK);
}
/* Check the correctness of written data */
if (Status == SD_OK)
{
TransferStatus1 = Buffercmp(Buffer_Block_Tx, Buffer_Block_Rx, SD_BLOCK_SIZE);
}
if(TransferStatus1 == PASSED)
{
DEBUG_PRINTF("Single block 測試成功!\n");
}
else
{
DEBUG_PRINTF("Single block 測試失敗,請確保SD卡正確接入,或換一張SD卡測試!\n");
}
}
/**
* @brief Tests the SD card Multiple Blocks operations.
* @param None
* @retval None
*/
void SD_MultiBlockTest(void)
{
/*--------------- Multiple Block Read/Write ---------------------*/
/* Fill the buffer to send */
Fill_Buffer(Buffer_MultiBlock_Tx, MULTI_BUFFER_SIZE, 0x0);
if (Status == SD_OK)
{
/* Write multiple block of many bytes on address 0 */
Status = SD_WriteMultiBlocks(Buffer_MultiBlock_Tx, 0x00, SD_BLOCK_SIZE, NUMBER_OF_BLOCKS);
/* Check if the Transfer is finished */
Status = SD_WaitWriteOperation();
while(SD_GetStatus() != SD_TRANSFER_OK);
}
if (Status == SD_OK)
{
/* Read block of many bytes from address 0 */
Status = SD_ReadMultiBlocks(Buffer_MultiBlock_Rx, 0x00, SD_BLOCK_SIZE, NUMBER_OF_BLOCKS);
/* Check if the Transfer is finished */
Status = SD_WaitReadOperation();
while(SD_GetStatus() != SD_TRANSFER_OK);
}
/* Check the correctness of written data */
if (Status == SD_OK)
{
TransferStatus2 = Buffercmp(Buffer_MultiBlock_Tx, Buffer_MultiBlock_Rx, MULTI_BUFFER_SIZE);
}
if(TransferStatus2 == PASSED)
{
DEBUG_PRINTF("Multi block 測試成功!\n");
}
else
{
DEBUG_PRINTF("Multi block 測試失敗,請確保SD卡正確接入,或換一張SD卡測試!\n");
}
}
/**
* @brief Compares two buffers.
* @param pBuffer1, pBuffer2: buffers to be compared.
* @param BufferLength: buffer's length
* @retval PASSED: pBuffer1 identical to pBuffer2
* FAILED: pBuffer1 differs from pBuffer2
*/
TestStatus Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint32_t BufferLength)
{
while (BufferLength--)
{
if (*pBuffer1 != *pBuffer2)
{
return FAILED;
}
pBuffer1++;
pBuffer2++;
}
return PASSED;
}
/**
* @brief Fills buffer with user predefined data.
* @param pBuffer: pointer on the Buffer to fill
* @param BufferLength: size of the buffer to fill
* @param Offset: first value to fill on the Buffer
* @retval None
*/
void Fill_Buffer(uint8_t *pBuffer, uint32_t BufferLength, uint32_t Offset)
{
uint16_t index = 0;
/* Put in global buffer same values */
for (index = 0; index < BufferLength; index++)
{
pBuffer[index] = index + Offset;
}
}
/**
* @brief Checks if a buffer has all its values are equal to zero.
* @param pBuffer: buffer to be compared.
* @param BufferLength: buffer's length
* @retval PASSED: pBuffer values are zero
* FAILED: At least one value from pBuffer buffer is different from zero.
*/
TestStatus eBuffercmp(uint8_t* pBuffer, uint32_t BufferLength)
{
while (BufferLength--)
{
/* In some SD Cards the erased state is 0xFF, in others it's 0x00 */
if ((*pBuffer != 0xFF) && (*pBuffer != 0x00))
{
return FAILED;
}
pBuffer++;
}
return PASSED;
}
#endif /* _SDCARD_TEST */
/*---------------------------- END OF FILE ----------------------------*/