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


 

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