STM32筆記之 SDIO(SD Card操作)

寫在前面:
本文章旨在總結備份、方便以後查詢,由於是個人總結,如有不對,歡迎指正;另外,內容大部分來自網絡、書籍、和各類手冊,如若侵權請告知,馬上刪帖致歉。

 

目錄

一、SDIO接口

二、SD Card

三、代碼實驗操作


 

一、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 ----------------------------*/


 

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