写在前面:
本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。
目录
一、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 ----------------------------*/