STM32个人笔记:SDIO接口详解
目录
SDIO接口常用设备
SD卡的总线拓扑
总线协议
SD卡数据包格式
SD命令
SD命令类型
响应
SD卡操作模式
SD卡系统(包括主机和CD卡)定义了两种操作模式:卡识别模式和数据传输模式。
卡识别模式
数据传输模式
STM32功能框图
控制单元
命令路径
数据路径
数据FIFO
适配器寄存器
HAL库_SDIO
SDIO配置
SDIO初始化结构体
SDIO函数
SD卡常用接口:SPI、SDIO。
SDIO接口常用设备
SD卡组成:存储单元、存储单元接口、电源检测、卡及接口控制器和接口驱动器。
SD卡的总线拓扑
虽然可以共用总线,但不推荐多卡槽共用总线信号,要求一个单独总线应该连接一个单独的SD卡。
CLK:时钟线,由SDIO主机产生,即由STM32控制器输出;
CMD:命令线,SDIO主机通过该线发送命令控制SD卡。如果命令要求SD卡提供响应,SD卡也是通过该线传输响应信息。
D0-D3:数据线,传输读写数据。D0拉低表示忙碌状态。
主机的SDIO接口对SD卡的读写都只以CLK时钟线的上身沿为有效。
SD卡操作过程会使用两种不同频率的时钟来同步数据。一个是识别卡阶段时钟频率FOD,最高为400kHz;另一个是数据传输阶段时钟频率FPP,默认最高为25MHz,如果通过相关寄存器配置使SDIO工作在高速模式,此时数据传输最高频率为50MHz。
总线协议
SD总线通信是基于命令和数据传输的。通讯开始:0;通讯结束:1。
SD总线通信一般是主机发送一个命令,从机在接收到命令后做出响应。如有需要会有数据传输参与。数据以数据块形式传输,SDHC卡数据块长度一般为 512 字节。
数据块需要CRC校验。CRC位由SD卡系统硬件自动生成。
STM32控制器可以通过控制单线或四线传输(D0~D3)。SD数据传输支持单块和多块写入,写入前需要检测SD卡忙状态,因为SD卡在接收到数据后编程到存储器过程需要一定操作时间。SD卡忙状态通过把D0线拉低表示。SD数据传输也支持单块和多块读出,只是无需忙状态检测。
SD卡数据包格式
使用4根数据线传输时,每次传输4bit数据,每根数据线都必须有起始位、终止位、CRC位,CRC位每根数据线都要分别检查,并把检查结果汇总,然后在数据传输完成后通过D0线反馈给主机。
常规数据包格式(8bit宽)
先低字节再高字节,先高位再低位。
宽位数据包格式(针对SSR寄存器内容发送)
SSR寄存器共512bit,在主机发出ACMD13命令后,SD卡将SSR寄存器内容通过DAT线发送给主机。
SD命令
SD命令由主机发出,以广播命令和寻址命令为主。
SD命令固定格式为48bit,都是通过CMD线连续传输的。
起始位和终止位:0和1。
传输标志:1-传输(主机===>CD卡),0-响应(CD卡===>主机)。
命令号:固定6bit(CMD0~CMD63)。
地址信息/参数:每个命令有32bit地址信息/参数用于命令附加内容。例如,广播命令没有地址信息,这32bit用于指定参数。而寻址命令的32bit用于指定目标SD卡的地址。
CRC7校验:7bit用于验证命令传输内容正确性。如果发生外部干扰导致传输数据个别位状态改变将导致校准失败,也意味着命令传输失败,SD卡不执行命令。
SD命令类型
无响应广播命令(bc):发送到所有卡,不返回任务响应。
带响应广播命令(bcr):发送到所有卡,同时接收来自所有卡响应。
寻址命令(ac):发送到选定卡,数据线无数据传输。
寻址数据传输命令(adtc):发送到选定卡,数据线有数据传输。
在标准中定义了两种类型的通用命令:特定应用命令 (ACMD) 和常规命令 (GEN_CMD)。
要使用 SD 卡制造商特定的 ACMD 命令如 ACMD6,需要在发送该命令之前无发送 CMD55 命令,告知 SD 卡接下来的命令为特定应用命令。 CMD55 命令只对紧接的第一个命令有效, SD 卡如果检测到 CMD55 之后的第一条命令为 ACMD 则执行其特定应用功能,如果检测发现不是 ACMD 命令,则执行标准命令。
响应(短响应、长响应)
SDIO总共有7个响应类型(R1~R7),其中SD卡没有R4、R5类型响应。响应也是通过CMD线连续传输。
短响应是48bit,长响应是136bit。只有R2类型是长响应。除了R3类型之外,其他响应都使用CRC7校验(R2类型是使用CID和CSD寄存器的内部CRC7)。
SD卡操作模式
SD 卡有多个版本, STM32 控制器目前最高支持《Physical Layer Simplified Specification V2.0》定义的 SD 卡, STM32 控制器对 SD 卡进行数据读写之前需要识别卡的种类: V1.0 标准卡、 V2.0 标准卡、 V2.0 高容量卡或者不被识别卡。
SD卡系统(包括主机和CD卡)定义了两种操作模式:卡识别模式和数据传输模式。
在系统复位后,主机处于卡识别模式,寻找总线上可用的SDIO设备;同时,SD卡也处于卡识别模式。直到被主机识别到,即当SD卡接收到SEND_RCA(CMD3)命令后,SD卡就进入数据传输模式,而主机在总线上所有卡被识别后也进入数据传输模式。在每个操作模式下,SD卡都有几种状态,通过命令控制实现卡状态的切换。
操作模式 | SD卡状态 |
无效模式 | 无效状态 |
卡识别模式 | 空闲状态 |
准备状态 | |
识别状态 | |
数据传输模式 | 待机状态 |
传输状态 | |
发送数据状态 | |
接收数据状态 | |
编程状态 | |
断开连接状态 |
卡识别模式
在卡识别模式下,主机会复位所有处于“卡识别模式”的 SD 卡,确认其工作电压范围,识别 SD卡类型,并且获取 SD 卡的相对地址 (卡相对地址较短,便于寻址)。
在卡识别过程中,要求 SD 卡工作在识别时钟频率 FOD 的状态下。
空闲状态:主机上电后,所有卡处于空闲状态(包括当前处于无效状态的卡)。主机也可以发送 CMD0 让所有卡软复位从而进入空闲状态(但当前处于无效状态的卡并不会复位)。
主机在开始与卡通信前,需要先确定双方在互相支持的电压范围内。 SD 卡有一个电压支持范围,主机当前电压必须在该范围才能与卡正常通信。 CMD8 命令就是用于验证卡接口操作条件的(主要是电压支持)。卡会根据命令的参数来检测操作条件匹配性,如果卡支持主机电压就产生响应,否则不响应。而主机则根据响应内容确定卡的电压匹配性。 CMD8 是 SD卡标准 V2.0 版本才有的新命令,所以如果主机有接收到响应,可以判断卡为 V2.0 或更高版本 SD 卡。
ACMD41 命令可以识别或拒绝不匹配它的电压范围的卡。 ACMD41 命令的 VDD 电压参数用于设置主机支持电压范围,卡响应会返回卡支持的电压范围。对于 CMD8 有响应的卡,把ACMD41 命令的 HCS 位设置为 1。也可以测试卡的容量类型,如果卡响应的 CCS 位为 1 说明为高容量 SD 卡,否则为标准卡。卡在响应 ACMD41 之后进入准备状态,不响应 ACMD41 的卡为不可用卡,进入无效状态。 ACMD41 是应用特定命令,发送该命令之前必须先发 CMD55。
CMD2 用来控制所有卡返回它们的卡识别号 (CID),处于准备状态的卡在发送 CID 之后就进入识别状态。之后主机就发送 CMD3 命令,让卡自己推荐一个相对地址 (RCA) 并响应命令。这个 RCA 是 16bit 地址,而 CID 是 128bit 地址,使用 RCA简化通信。卡在接收到 CMD3 并发出响应后就进入数据传输模式,并处于待机状态,主机在获取所有卡 RCA 之后也进入数据传输模式。
数据传输模式
只有SD卡系统处于数据传输模式下才可以进行数据读写操作。数据传输模式下可以将主机SD时钟频率设置为FPP,默认最高为25MHz,频率切换可以通过 CMD4 命令实现。
CMD7 用来选定和取消指定的卡,卡在待机状态下还不能进行数据通信,因为总线上可能有多个卡都是出于待机状态,必须选择一个 RCA 地址目标卡使其进入传输状态才可以进行数据通信。
同时通过 CMD7 命令也可以让已经被选择的目标卡返回到待机状态。
数据传输模式下的数据通信都是主机和目标卡之间通过寻址命令点对点进行的。 CMD12 可以中断正在进行的数据通信,让卡返回到传输状态。 CMD0 和 CMD15 会中止任何数据编程操作,返回卡识别模式,这可能导致卡数据被损坏。
SDIO接口功能框图(SDIO适配器、AHB总线接口)
SDIO适配器:提供SDIO主机功能,可以提供SD时钟,发送命令和数据传输。
AHB接口:用于控制器访问SDIO适配器寄存器并且可以产生中断和DMA请求信号。
SDIO使用两个时钟信号,SDIO适配器时钟(SDIOCLK = HCLK = 72MHz)、AHB总线时钟的二分频(HCLK/2,一般为36MHz)。
SDIO_CK 是 SDIO 接口与 SD 卡用于同步的时钟信号。它使用 SDIOCLK 作为 SDIO_CK 的时钟来源,可以通过设置 BYPASS 模式直接得到,这时 SDIO_CK = SDIOCLK = HCLK。若禁止 BYPASS 模式,可以通过配置时钟寄存器的 CLKDIV 位控制分频因子,即 SDIO_CK = SDIOCLK/(2+CLKDIV)= HCLK/(2+CLKDIV)。配置时钟时要注意, SD 卡普遍要求 SDIO_CK 时钟频率不能超过 25MHz。
STM32 控制器的 SDIO 是针对 MMC 卡和 SD 卡的主设备,所以预留有 8 根数据线,对于 SD 卡最多用四根数据线。
SDIO适配器(SD卡系统主机部分,是STM32控制器与SD卡数据通信中间设备)
组成:控制单元、命令路径单元、数据路径单元、适配器寄存器单元和FIFO。
适配器寄存器和 FIFO 使用 AHB 总线一侧的时钟(HCLK/2),控制单元、命令通道和数据通道使用 SDIO 适配器一侧的时钟 (SDIOCLK)。
控制单元(电源管理、时钟管理)
命令路径(控制命令发送,控制卡的响应)
数据路径(负责与SD卡相互数据传输)
数据FIFO(先进先出数据缓冲器)
控制器的 FIFO 包含宽度为32bit、深度为 32 字的数据缓冲器和发送/接收逻辑。其中 SDIO 状态寄存器 (SDIO_STA) 的 TXACT位用于指示当前正在发送数据, RXACT 位指示当前正在接收数据,这两个位不可能同时为 1。
• 当 TXACT 为 1 时,可以通过 AHB 接口将数据写入到传输 FIFO。
• 当 RXACT 为 1 时,接收 FIFO 存放从数据路径部件接收到的数据。
根据 FIFO 空或满状态会把 SDIO_STA 寄存器位值 1,并可以产生中断和 DMA 请求。
适配器寄存器
适配器寄存器包含了控制 SDIO 外设的各种控制寄存器及状态寄存器。
HAL库_SDIO
SDIO配置
SD_HandleTypeDef uSdHandle;
uint8_t BSP_SD_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
uint8_t state = MSD_OK;
__HAL_RCC_SDIO_CLK_ENABLE();
__HAL_RCC_DMA2_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOF_CLK_ENABLE();
uSdHandle.Instance = SDIO;
uSdHandle.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;
uSdHandle.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;
uSdHandle.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;
uSdHandle.Init.BusWide = SDIO_BUS_WIDE_1B;
uSdHandle.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
uSdHandle.Init.ClockDiv = SDIO_TRANSFER_CLK_DIV;
GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_2;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
if(HAL_SD_Init(&uSdHandle) != HAL_OK)
{
state = MSD_ERROR;
}
if(state == MSD_OK)
{
if(HAL_SD_ConfigWideBusOperation(&uSdHandle, SDIO_BUS_WIDE_4B) != HAL_OK)
{
state = MSD_ERROR;
}
else
{
state = MSD_OK;
}
}
return state;
}
SDIO初始化结构体
typedef struct
{
uint32_t CardType;
uint32_t CardVersion;
uint32_t Class;
uint32_t RelCardAdd;
uint32_t BlockNbr;
uint32_t BlockSize;
uint32_t LogBlockNbr;
uint32_t LogBlockSize;
}HAL_SD_CardInfoTypeDef;
typedef struct
{
SD_TypeDef *Instance;
SD_InitTypeDef Init;
HAL_LockTypeDef Lock;
uint32_t *pTxBuffPtr;
uint32_t TxXferSize;
uint32_t *pRxBuffPtr;
uint32_t RxXferSize;
__IO uint32_t Context;
__IO HAL_SD_StateTypeDef State;
__IO uint32_t ErrorCode;
DMA_HandleTypeDef *hdmarx;
DMA_HandleTypeDef *hdmatx;
HAL_SD_CardInfoTypeDef SdCard;
uint32_t CSD[4];
uint32_t CID[4];
}SD_HandleTypeDef;
typedef struct
{
__IO uint8_t CSDStruct;
__IO uint8_t SysSpecVersion;
__IO uint8_t Reserved1;
__IO uint8_t TAAC;
__IO uint8_t NSAC;
__IO uint8_t MaxBusClkFrec;
__IO uint16_t CardComdClasses;
__IO uint8_t RdBlockLen;
__IO uint8_t PartBlockRead;
__IO uint8_t WrBlockMisalign;
__IO uint8_t RdBlockMisalign;
__IO uint8_t DSRImpl;
__IO uint8_t Reserved2;
__IO uint32_t DeviceSize;
__IO uint8_t MaxRdCurrentVDDMin;
__IO uint8_t MaxRdCurrentVDDMax;
__IO uint8_t MaxWrCurrentVDDMin;
__IO uint8_t MaxWrCurrentVDDMax;
__IO uint8_t DeviceSizeMul;
__IO uint8_t EraseGrSize;
__IO uint8_t EraseGrMul;
__IO uint8_t WrProtectGrSize;
__IO uint8_t WrProtectGrEnable;
__IO uint8_t ManDeflECC;
__IO uint8_t WrSpeedFact;
__IO uint8_t MaxWrBlockLen;
__IO uint8_t WriteBlockPaPartial;
__IO uint8_t Reserved3;
__IO uint8_t ContentProtectAppli;
__IO uint8_t FileFormatGrouop;
__IO uint8_t CopyFlag;
__IO uint8_t PermWrProtect;
__IO uint8_t TempWrProtect;
__IO uint8_t FileFormat;
__IO uint8_t ECC;
__IO uint8_t CSD_CRC;
__IO uint8_t Reserved4;
}HAL_SD_CardCSDTypeDef;
typedef struct
{
__IO uint8_t ManufacturerID;
__IO uint16_t OEM_AppliID;
__IO uint32_t ProdName1;
__IO uint8_t ProdName2;
__IO uint8_t ProdRev;
__IO uint32_t ProdSN;
__IO uint8_t Reserved1;
__IO uint16_t ManufactDate;
__IO uint8_t CID_CRC;
__IO uint8_t Reserved2;
}HAL_SD_CardCIDTypeDef;
typedef struct
{
__IO uint8_t DataBusWidth;
__IO uint8_t SecuredMode;
__IO uint16_t CardType;
__IO uint32_t ProtectedAreaSize;
__IO uint8_t SpeedClass;
__IO uint8_t PerformanceMove;
__IO uint8_t AllocationUnitSize;
__IO uint16_t EraseSize;
__IO uint8_t EraseTimeout;
__IO uint8_t EraseOffset;
}HAL_SD_CardStatusTypeDef;
SDIO函数
HAL_StatusTypeDef HAL_SD_Init(SD_HandleTypeDef *hsd);
HAL_StatusTypeDef HAL_SD_InitCard(SD_HandleTypeDef *hsd);
HAL_StatusTypeDef HAL_SD_DeInit (SD_HandleTypeDef *hsd);
void HAL_SD_MspInit(SD_HandleTypeDef *hsd);
void HAL_SD_MspDeInit(SD_HandleTypeDef *hsd);
/* Blocking mode: Polling */
HAL_StatusTypeDef HAL_SD_ReadBlocks(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks, uint32_t Timeout);
HAL_StatusTypeDef HAL_SD_WriteBlocks(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks, uint32_t Timeout);
HAL_StatusTypeDef HAL_SD_Erase(SD_HandleTypeDef *hsd, uint32_t BlockStartAdd, uint32_t BlockEndAdd);
/* Non-Blocking mode: IT */
HAL_StatusTypeDef HAL_SD_ReadBlocks_IT(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks);
HAL_StatusTypeDef HAL_SD_WriteBlocks_IT(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks);
/* Non-Blocking mode: DMA */
HAL_StatusTypeDef HAL_SD_ReadBlocks_DMA(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks);
HAL_StatusTypeDef HAL_SD_WriteBlocks_DMA(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks);
void HAL_SD_IRQHandler(SD_HandleTypeDef *hsd);
/* Callback in non blocking modes (DMA) */
void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd);
void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd);
void HAL_SD_ErrorCallback(SD_HandleTypeDef *hsd);
void HAL_SD_AbortCallback(SD_HandleTypeDef *hsd);
HAL_StatusTypeDef HAL_SD_ConfigWideBusOperation(SD_HandleTypeDef *hsd, uint32_t WideMode);
HAL_StatusTypeDef HAL_SD_SendSDStatus(SD_HandleTypeDef *hsd, uint32_t *pSDstatus);
HAL_SD_CardStateTypeDef HAL_SD_GetCardState(SD_HandleTypeDef *hsd);
HAL_StatusTypeDef HAL_SD_GetCardCID(SD_HandleTypeDef *hsd, HAL_SD_CardCIDTypeDef *pCID);
HAL_StatusTypeDef HAL_SD_GetCardCSD(SD_HandleTypeDef *hsd, HAL_SD_CardCSDTypeDef *pCSD);
HAL_StatusTypeDef HAL_SD_GetCardStatus(SD_HandleTypeDef *hsd, HAL_SD_CardStatusTypeDef *pStatus);
HAL_StatusTypeDef HAL_SD_GetCardInfo(SD_HandleTypeDef *hsd, HAL_SD_CardInfoTypeDef *pCardInfo);
HAL_SD_StateTypeDef HAL_SD_GetState(SD_HandleTypeDef *hsd);
uint32_t HAL_SD_GetError(SD_HandleTypeDef *hsd);
HAL_StatusTypeDef HAL_SD_Abort(SD_HandleTypeDef *hsd);
HAL_StatusTypeDef HAL_SD_Abort_IT(SD_HandleTypeDef *hsd);