zynq sd卡初始化流程
1 sd控制器的初始化 :XSdPs_CfgInitialize();
s32 XSdPs_CfgInitialize(XSdPs *InstancePtr, XSdPs_Config *ConfigPtr,
u32 EffectiveAddr)
{
s32 Status;
u8 PowerLevel;
u8 ReadReg;
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(ConfigPtr != NULL);
/* Set some default values. */
InstancePtr->Config.DeviceId = ConfigPtr->DeviceId;
InstancePtr->Config.BaseAddress = EffectiveAddr;
InstancePtr->Config.InputClockHz = ConfigPtr->InputClockHz;
InstancePtr->IsReady = XIL_COMPONENT_IS_READY;
InstancePtr->Config.CardDetect = ConfigPtr->CardDetect;
InstancePtr->Config.WriteProtect = ConfigPtr->WriteProtect;
InstancePtr->Config.BusWidth = ConfigPtr->BusWidth;
InstancePtr->Config.BankNumber = ConfigPtr->BankNumber;
InstancePtr->Config.HasEMIO = ConfigPtr->HasEMIO;
InstancePtr->Config.IsCacheCoherent = ConfigPtr->IsCacheCoherent;
InstancePtr->SectorCount = 0;
InstancePtr->Mode = XSDPS_DEFAULT_SPEED_MODE;
InstancePtr->Config_TapDelay = NULL;
InstancePtr->Dma64BitAddr = 0U;
/* Disable bus power and issue emmc hw reset */
if ((XSdPs_ReadReg16(InstancePtr->Config.BaseAddress,
XSDPS_HOST_CTRL_VER_OFFSET) & XSDPS_HC_SPEC_VER_MASK) ==
XSDPS_HC_SPEC_V3)
XSdPs_WriteReg8(InstancePtr->Config.BaseAddress,
XSDPS_POWER_CTRL_OFFSET, XSDPS_PC_EMMC_HW_RST_MASK);
else
XSdPs_WriteReg8(InstancePtr->Config.BaseAddress,
XSDPS_POWER_CTRL_OFFSET, 0x0);
//对eMMC设备执行硬件复位(通过拉高EMMC_HW_RST信号),确保设备进入初始状态,
/* Delay to poweroff card */
(void)usleep(1000U);
//eMMC JEDEC标准要求硬件复位后需等待至少1ms电源稳定时间
/* "Software reset for all" is initiated */
XSdPs_WriteReg8(InstancePtr->Config.BaseAddress, XSDPS_SW_RST_OFFSET,
XSDPS_SWRST_ALL_MASK);
/* Proceed with initialization only after reset is complete */
ReadReg = XSdPs_ReadReg8(InstancePtr->Config.BaseAddress,
XSDPS_SW_RST_OFFSET);
while ((ReadReg & XSDPS_SWRST_ALL_MASK) != 0U) {
ReadReg = XSdPs_ReadReg8(InstancePtr->Config.BaseAddress,
XSDPS_SW_RST_OFFSET);
}
//等待软复位完成
/* Host Controller version is read. */
InstancePtr->HC_Version =
(u8)(XSdPs_ReadReg16(InstancePtr->Config.BaseAddress,
XSDPS_HOST_CTRL_VER_OFFSET) & XSDPS_HC_SPEC_VER_MASK);
/*
* Read capabilities register and update it in Instance pointer.
* It is sufficient to read this once on power on.
*/
InstancePtr->Host_Caps = XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
XSDPS_CAPS_OFFSET);
/* Select voltage and enable bus power. */
if (InstancePtr->HC_Version == XSDPS_HC_SPEC_V3)
XSdPs_WriteReg8(InstancePtr->Config.BaseAddress,
XSDPS_POWER_CTRL_OFFSET,
(XSDPS_PC_BUS_VSEL_3V3_MASK | XSDPS_PC_BUS_PWR_MASK) &
~XSDPS_PC_EMMC_HW_RST_MASK);
else
XSdPs_WriteReg8(InstancePtr->Config.BaseAddress,
XSDPS_POWER_CTRL_OFFSET,
XSDPS_PC_BUS_VSEL_3V3_MASK | XSDPS_PC_BUS_PWR_MASK);
/* Delay before issuing the command after emmc reset */
if (InstancePtr->HC_Version == XSDPS_HC_SPEC_V3)
if ((InstancePtr->Host_Caps & XSDPS_CAPS_SLOT_TYPE_MASK) ==
XSDPS_CAPS_EMB_SLOT)
usleep(200);
//根据主机能力寄存器(Host_Caps)选择3.3V/3.0V/1.8V供电(优先支持3.3V)。
/* Change the clock frequency to 400 KHz */
Status = XSdPs_Change_ClkFreq(InstancePtr, XSDPS_CLK_400_KHZ);
if (Status != XST_SUCCESS) {
Status = XST_FAILURE;
goto RETURN_PATH ;
}
//根据主机能力寄存器(Host_Caps)选择3.3V/3.0V/1.8V供电(优先支持3.3V)。
if ((InstancePtr->Host_Caps & XSDPS_CAP_VOLT_3V3_MASK) != 0U) {
PowerLevel = XSDPS_PC_BUS_VSEL_3V3_MASK;
} else if ((InstancePtr->Host_Caps & XSDPS_CAP_VOLT_3V0_MASK) != 0U) {
PowerLevel = XSDPS_PC_BUS_VSEL_3V0_MASK;
} else if ((InstancePtr->Host_Caps & XSDPS_CAP_VOLT_1V8_MASK) != 0U) {
PowerLevel = XSDPS_PC_BUS_VSEL_1V8_MASK;
} else {
PowerLevel = 0U;
}
/* Select voltage based on capability and enable bus power. */
XSdPs_WriteReg8(InstancePtr->Config.BaseAddress,
XSDPS_POWER_CTRL_OFFSET,
PowerLevel | XSDPS_PC_BUS_PWR_MASK);
if (InstancePtr->HC_Version == XSDPS_HC_SPEC_V3) {
/* Enable ADMA2 in 64bit mode. */
XSdPs_WriteReg8(InstancePtr->Config.BaseAddress,
XSDPS_HOST_CTRL1_OFFSET,
XSDPS_HC_DMA_ADMA2_64_MASK);
} else {
/* Enable ADMA2 in 32bit mode. */
XSdPs_WriteReg8(InstancePtr->Config.BaseAddress,
XSDPS_HOST_CTRL1_OFFSET,
XSDPS_HC_DMA_ADMA2_32_MASK);
}
//开启所有正常中断(除卡检测)和错误中断的状态记录
/* Enable all interrupt status except card interrupt initially */
XSdPs_WriteReg16(InstancePtr->Config.BaseAddress,
XSDPS_NORM_INTR_STS_EN_OFFSET,
XSDPS_NORM_INTR_ALL_MASK & (~XSDPS_INTR_CARD_MASK));
XSdPs_WriteReg16(InstancePtr->Config.BaseAddress,
XSDPS_ERR_INTR_STS_EN_OFFSET,
XSDPS_ERROR_INTR_ALL_MASK);
//初始化阶段禁用中断信号触发,防止未就绪的中断服务程序(ISR)误执行。
/* Disable all interrupt signals by default. */
XSdPs_WriteReg16(InstancePtr->Config.BaseAddress,
XSDPS_NORM_INTR_SIG_EN_OFFSET, 0x0U);
XSdPs_WriteReg16(InstancePtr->Config.BaseAddress,
XSDPS_ERR_INTR_SIG_EN_OFFSET, 0x0U);
/*
* Transfer mode register - default value
* DMA enabled, block count enabled, data direction card to host(read)
*/
TransferMode = XSDPS_TM_DMA_EN_MASK | XSDPS_TM_BLK_CNT_EN_MASK |
XSDPS_TM_DAT_DIR_SEL_MASK;
//512字节是SD/eMMC协议的标准块大小,适用于大多数存储设备的初始化阶段。
块大小需与物理扇区大小一致,否则可能导致数据传输错误或CRC校验失败
/* Set block size to 512 by default */
XSdPs_WriteReg16(InstancePtr->Config.BaseAddress,
XSDPS_BLK_SIZE_OFFSET, XSDPS_BLK_SIZE_512_MASK);
Status = XST_SUCCESS;
RETURN_PATH:
return Status;
}
2 sd卡的初始化 XSdPs_MmcCardInitialize();
---->如果是sd卡时,检测sd卡是否插入。XSDPS_CAPS_EMB_SLOT
嵌入式卡槽,这里代表emcc。
if ((InstancePtr->HC_Version != XSDPS_HC_SPEC_V3) ||
((InstancePtr->Host_Caps & XSDPS_CAPS_SLOT_TYPE_MASK)
!= XSDPS_CAPS_EMB_SLOT)) {
if(InstancePtr->Config.CardDetect != 0U) {
/*
* Check the present state register to make sure
* card is inserted and detected by host controller
*/
PresentStateReg = XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
XSDPS_PRES_STATE_OFFSET);
if ((PresentStateReg & XSDPS_PSR_CARD_INSRT_MASK) == 0U) {
Status = XST_FAILURE;
goto RETURN_PATH;
}
}
}
命令编号 类型 命令名称 描述
CMD0 CMD GO_IDLE_STATE 将SD卡置于空闲状态。
CMD2 CMD ALL_SEND_CID 请求卡发送其卡识别号(CID)。
CMD3 CMD SEND_RELATIVE_ADDR 请求卡发布一个新的相对地址(RCA),这是卡的网络地址。
CMD5 CMD IO_SEND_OP_COND SDIO特有命令,用于发送操作条件。
CMD7 CMD SELECT/DESELECT_CARD 选择或取消选择RCA指定的卡。
CMD8 CMD SEND_IF_COND 发送接口条件,验证供电电压范围,并提供一种方法来验证主机支持的最高版本的SD物理层规范。
CMD9 CMD SEND_CSD 请求选定的卡发送其卡特定数据(CSD)。
CMD10 CMD SEND_CID 请求选定的卡发送其卡识别号(CID)。
CMD12 CMD STOP_TRANSMISSION 强制卡停止当前的数据读取或写入过程。
CMD13 CMD SEND_STATUS 查询选定卡的状态。
CMD16 CMD SET_BLOCKLEN 设置块长度,用于后续的块读取和写入操作。
CMD17 CMD READ_SINGLE_BLOCK 从卡中读取一个单独的数据块。
CMD18 CMD READ_MULTIPLE_BLOCK 连续地从卡中读取多个数据块。
CMD24 CMD WRITE_BLOCK 向卡写入一个单独的数据块。
CMD25 CMD WRITE_MULTIPLE_BLOCK 连续地向卡写入多个数据块。
CMD41 CMD SD_SEND_OP_COND 发送操作条件命令,用于初始化步骤(ACMD41之前的初始化)。
CMD55 CMD APP_CMD 标记接下来的命令为应用特定命令(ACMD)。
CMD58 CMD READ_OCR 读取OCR寄存器的内容。
CMD59 CMD CRC_ON_OFF 开启或关闭CRC校验。
应用特定命令(Application Specific Commands, ACMD)
命令编号 类型 命令名称 描述
ACMD41 ACMD SD_SEND_OP_COND 发送SD操作条件命令,用于初始化SD卡。
ACMD51 ACMD SEND_SCR 请求卡发送其配置寄存器(SCR)内容。
---->发送复位命令,将设备置于空闲状态。
/* CMD0 no response expected */
Status = XSdPs_CmdTransfer(InstancePtr, CMD0, 0U, 0U);
if (Status != XST_SUCCESS) {
Status = XST_FAILURE;
goto RETURN_PATH;
}
---->通过发送 CMD1 命令与 SD 卡通信,直到卡完成上电初始化并准备好进入操作状态.
XSDPS_ACMD41_HCS | XSDPS_CMD1_HIGH_VOL 通知sd卡主机支持HCS高容量和高电压。
cmd1是zynq上的特有命令 类似 sd标准协议中的 cmd 8 和cmd41.
RespOCR = 0U;
/* Send CMD1 while card is still busy with power up */
while ((RespOCR & XSDPS_RESPOCR_READY) == 0U) {
/* Host High Capacity support & High volage window */
Status = XSdPs_CmdTransfer(InstancePtr, CMD1,
XSDPS_ACMD41_HCS | XSDPS_CMD1_HIGH_VOL, 0U);
if (Status != XST_SUCCESS) {
Status = XST_FAILURE;
goto RETURN_PATH;
}
/* Response with card capacity */
RespOCR = XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
XSDPS_RESP0_OFFSET);
}
---->通过解析 OCR 数据中的 HCS 标志位,更新主机实例中的 HCS 标志,如果卡支持高容量模式,则将 InstancePtr->HCS 设置为 1。
/* Update HCS support flag based on card capacity response */
if ((RespOCR & XSDPS_ACMD41_HCS) != 0U) {
InstancePtr->HCS = 1U;
}
---->通过发送 CMD2 命令获取 SD 卡的 CID 数据,并将其存储到主机实例中。
/* CMD2 for Card ID */
Status = XSdPs_CmdTransfer(InstancePtr, CMD2, 0U, 0U);
if (Status != XST_SUCCESS) {
Status = XST_FAILURE;
goto RETURN_PATH;
}
InstancePtr->CardID[0] =
XSdPs_ReadReg16(InstancePtr->Config.BaseAddress,
XSDPS_RESP0_OFFSET);
InstancePtr->CardID[1] =
XSdPs_ReadReg16(InstancePtr->Config.BaseAddress,
XSDPS_RESP1_OFFSET);
InstancePtr->CardID[2] =
XSdPs_ReadReg16(InstancePtr->Config.BaseAddress,
XSDPS_RESP2_OFFSET);
InstancePtr->CardID[3] =
XSdPs_ReadReg16(InstancePtr->Config.BaseAddress,
XSDPS_RESP3_OFFSET);
---->通过发送 CMD3 和 CMD9 命令,分别为 SD 卡分配相对卡地址(RCA)并获取卡的特定数据(CSD),RCA 是主机用来唯一标识和寻址卡的一个 16 位值,CSD 包含卡的容量、块大小、读写速度等信息
/* Set relative card address */
InstancePtr->RelCardAddr = 0x12340000U;
Status = XSdPs_CmdTransfer(InstancePtr, CMD3, (InstancePtr->RelCardAddr), 0U);
if (Status != XST_SUCCESS) {
Status = XST_FAILURE;
goto RETURN_PATH;
}
Status = XSdPs_CmdTransfer(InstancePtr, CMD9, (InstancePtr->RelCardAddr), 0U);
if (Status != XST_SUCCESS) {
Status = XST_FAILURE;
goto RETURN_PATH;
}
---->从 SD 卡控制器的响应寄存器中读取卡的 CSD 数据,并将其存储到 CSD 数组中,(在发送 CMD9 后,卡会返回其 CSD 数据。这些数据被分成 4 个 32 位寄存器(RESP0 到 RESP3),并通过读取寄存器的方式提取)
/*
* Card specific data is read.
* Currently not used for any operation.
*/
CSD[0] = XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
XSDPS_RESP0_OFFSET);
CSD[1] = XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
XSDPS_RESP1_OFFSET);
CSD[2] = XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
XSDPS_RESP2_OFFSET);
CSD[3] = XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
XSDPS_RESP3_OFFSET);
//提取sd卡的版本号
InstancePtr->Card_Version = (CSD[3] & CSD_SPEC_VER_MASK) >>18U;
---->根据 CSD 数据中的字段计算 SD 卡的总容量,并将其转换为以 512 字节为单位的扇区数量。这是 SD 卡初始化流程中的重要一步,用于为主机提供卡的容量信息。
/* Calculating the memory capacity */
BlkLen = 1 << ((CSD[2] & READ_BLK_LEN_MASK) >> 8U);
Mult = 1 << (((CSD[1] & C_SIZE_MULT_MASK) >> 7U) + 2U);
DeviceSize = (CSD[1] & C_SIZE_LOWER_MASK) >> 22U;
DeviceSize |= (CSD[2] & C_SIZE_UPPER_MASK) << 10U;
DeviceSize = (DeviceSize + 1U) * Mult;
DeviceSize = DeviceSize * BlkLen;
InstancePtr->SectorCount = (DeviceSize/XSDPS_BLK_SIZE_512_MASK);
3 完成 SD/eMMC 卡的初始化流程中的几个关键步骤,包括更改时钟频率、选择卡、设置块大小以及读取 eMMC 的扩展 CSD(Extended Card Specific Data)。
Status=XSdPs_Change_ClkFreq(&ps7_EMMC,50000000);
Status=XSdPs_Select_Card(&ps7_EMMC);
Status=XSdPs_SetBlkSize(&ps7_EMMC,XSDPS_BLK_SIZE_512_MASK);
Status=XSdPs_Get_Mmc_ExtCsd(&ps7_EMMC,Emmc_ExtCsd);
- 通过调用
XSdPs_Change_ClkFreq
函数,将控制器的时钟频率调整到更高的值(50 MHz),以提高数据传输速率。 - 通过调用
XSdPs_Select_Card
函数,发送 CMD7 命令, 选择RCA指定的卡,将目标卡置于传输状态。 - 通过调用
XSdPs_SetBlkSize
函数,cmd16 设置传输块大小为 512 字节。 - 对于 eMMC 卡,通过调用
XSdPs_Get_Mmc_ExtCsd
函数,发送 CMD8 命令以读取扩展 CSD 数据。获取更多关于卡的详细信息。
4 XSdPs_Get_Mmc_ExtCsd()的详细流程
---->配置块的数量为1和块的大小为512 (CSD 只占用一个块),设置 ADMA2 描述符表。
BlkCnt = XSDPS_EXT_CSD_CMD_BLKCNT;
BlkSize = XSDPS_EXT_CSD_CMD_BLKSIZE;
BlkSize &= XSDPS_BLK_SIZE_MASK;
XSdPs_WriteReg16(InstancePtr->Config.BaseAddress,
XSDPS_BLK_SIZE_OFFSET, BlkSize);
XSdPs_SetupADMA2DescTbl(InstancePtr, BlkCnt, ReadBuff);
---->缓存一致性设置
if (InstancePtr->Config.IsCacheCoherent == 0) {
Xil_DCacheInvalidateRange((INTPTR)ReadBuff, 512U);
}
---->配置传输模式 传输方向为卡到主机,使能dma
TransferMode = XSDPS_TM_DAT_DIR_SEL_MASK | XSDPS_TM_DMA_EN_MASK;
---->通过发送 CMD8 命令(SEND_EXT_CSD)请求 eMMC 卡返回其扩展 CSD(Extended Card Specific Data)。
/* Send SEND_EXT_CSD command */
Status = XSdPs_CmdTransfer(InstancePtr, CMD8, Arg, 1U);
if (Status != XST_SUCCESS) {
Status = XST_FAILURE;
goto RETURN_PATH;
}
---->通过轮询方式监控 SD/eMMC 数据传输的状态,确保传输顺利完成或及时处理错误。
/*
* Check for transfer complete
* Polling for response for now
*/
do {
StatusReg = XSdPs_ReadReg16(InstancePtr->Config.BaseAddress,
XSDPS_NORM_INTR_STS_OFFSET);
if ((StatusReg & XSDPS_INTR_ERR_MASK) != 0U) {
/* Write to clear error bits */
XSdPs_WriteReg16(InstancePtr->Config.BaseAddress,
XSDPS_ERR_INTR_STS_OFFSET,
XSDPS_ERROR_INTR_ALL_MASK);
Status = XST_FAILURE;
goto RETURN_PATH;
}
} while ((StatusReg & XSDPS_INTR_TC_MASK) == 0U);
/* Write to clear bit */
XSdPs_WriteReg16(InstancePtr->Config.BaseAddress,
XSDPS_NORM_INTR_STS_OFFSET, XSDPS_INTR_TC_MASK);
总结 sd卡初始化时,发送sd命令顺序 cmd 0 1 2 3 9 7 16 8.
5 XSdPs_WritePolled
函数流程
检查SD卡是否已插入并初始化。
设置块大小(通常为512字节)。
配置DMA传输模式(支持64位地址或32位地址)。
根据块数量发送单块或多块写命令(CMD24 或 CMD25)。
轮询检查传输是否完成,并处理传输错误。
s32 XSdPs_WritePolled(XSdPs *InstancePtr, u32 Arg, u32 BlkCnt, const u8 *Buff)
{
s32 Status;
u32 PresentStateReg;
u32 StatusReg;
if ((InstancePtr->HC_Version != XSDPS_HC_SPEC_V3) ||
((InstancePtr->Host_Caps & XSDPS_CAPS_SLOT_TYPE_MASK)
!= XSDPS_CAPS_EMB_SLOT)) {
if(InstancePtr->Config.CardDetect != 0U) {
/* Check status to ensure card is initialized */
PresentStateReg = XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
XSDPS_PRES_STATE_OFFSET);
if ((PresentStateReg & XSDPS_PSR_CARD_INSRT_MASK) == 0x0U) {
Status = XST_FAILURE;
goto RETURN_PATH;
}
}
}
/* Set block size to 512 if not already set */
if( XSdPs_ReadReg(InstancePtr->Config.BaseAddress,
XSDPS_BLK_SIZE_OFFSET) != XSDPS_BLK_SIZE_512_MASK ) {
Status = XSdPs_SetBlkSize(InstancePtr,
XSDPS_BLK_SIZE_512_MASK);
if (Status != XST_SUCCESS) {
Status = XST_FAILURE;
goto RETURN_PATH;
}
}
if (InstancePtr->Dma64BitAddr >= ADDRESS_BEYOND_32BIT) {
XSdPs_SetupADMA2DescTbl64Bit(InstancePtr, BlkCnt);
} else {
XSdPs_SetupADMA2DescTbl(InstancePtr, BlkCnt, Buff);
if (InstancePtr->Config.IsCacheCoherent == 0) {
Xil_DCacheFlushRange((INTPTR)Buff,
BlkCnt * XSDPS_BLK_SIZE_512_MASK);
}
}
if (BlkCnt == 1U) {
TransferMode = XSDPS_TM_BLK_CNT_EN_MASK | XSDPS_TM_DMA_EN_MASK;
/* Send single block write command */
Status = XSdPs_CmdTransfer(InstancePtr, CMD24, Arg, BlkCnt);
if (Status != XST_SUCCESS) {
Status = XST_FAILURE;
goto RETURN_PATH;
}
} else {
TransferMode = XSDPS_TM_AUTO_CMD12_EN_MASK |
XSDPS_TM_BLK_CNT_EN_MASK |
XSDPS_TM_MUL_SIN_BLK_SEL_MASK | XSDPS_TM_DMA_EN_MASK;
/* Send multiple blocks write command */
Status = XSdPs_CmdTransfer(InstancePtr, CMD25, Arg, BlkCnt);
if (Status != XST_SUCCESS) {
Status = XST_FAILURE;
goto RETURN_PATH;
}
}
/*
* Check for transfer complete
* Polling for response for now
*/
do {
StatusReg = XSdPs_ReadReg16(InstancePtr->Config.BaseAddress,
XSDPS_NORM_INTR_STS_OFFSET);
if ((StatusReg & XSDPS_INTR_ERR_MASK) != 0U) {
/* Write to clear error bits */
XSdPs_WriteReg16(InstancePtr->Config.BaseAddress,
XSDPS_ERR_INTR_STS_OFFSET,
XSDPS_ERROR_INTR_ALL_MASK);
Status = XST_FAILURE;
goto RETURN_PATH;
}
} while((StatusReg & XSDPS_INTR_TC_MASK) == 0U);
/* Write to clear bit */
XSdPs_WriteReg16(InstancePtr->Config.BaseAddress,
XSDPS_NORM_INTR_STS_OFFSET, XSDPS_INTR_TC_MASK);
Status = XST_SUCCESS;
RETURN_PATH:
return Status;
}