本节例程基于 野火电子的STM32F407的SD卡读写例程进行讲解。上一节中讲解了SD卡上电过程,这节将会讲解一下SD卡的初始化过程,包括 获取SD卡的CID、CSD和SD卡RCA地址。
例程讲解-SD_InitializeCards & SD_GetCardInfo
CMD2:ALL_SEND_CID
CMD2的作用是通知所有卡通过CMD线返回CID值,CID值包括SD卡的识别号、制造商ID、OEMID、产品名称、版本号、序列号等信息,相当于是SD卡的身份证。
命令发送程序
CMD2命令无需带参数,所以SDIO控制器可以直接发送CMD2命令
SDIO_CmdInitStructure.SDIO_Argument = 0x0; // 不带参数
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_ALL_SEND_CID; // 命令所以为CMD2
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Long; // 长响应
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No; // 不等待
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable; // 使能CPSM状态机
SDIO_SendCommand(&SDIO_CmdInitStructure);
命令响应程序
CMD2的响应是R2,其响应类型为长响应。其中bit[127:1]是其响应的数据。
首先需要先判断SD卡的响应是否正常,如果响应正常,下一步就需要把128位的CID读出来。
static SD_Error CmdResp2Error(void)
{
SD_Error errorstatus = SD_OK;
uint32_t status;
status = SDIO->STA;
while (!(status & (SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CTIMEOUT | SDIO_FLAG_CMDREND)))
{
status = SDIO->STA;
}
if (status & SDIO_FLAG_CTIMEOUT)
{
errorstatus = SD_CMD_RSP_TIMEOUT;
SDIO_ClearFlag(SDIO_FLAG_CTIMEOUT);
return(errorstatus);
}
else if (status & SDIO_FLAG_CCRCFAIL)
{
errorstatus = SD_CMD_CRC_FAIL;
SDIO_ClearFlag(SDIO_FLAG_CCRCFAIL);
return(errorstatus);
}
/*!< Clear all the static flags */
SDIO_ClearFlag(SDIO_STATIC_FLAGS);
return(errorstatus);
}
// 读取128位CID
CID_Tab[0] = SDIO_GetResponse(SDIO_RESP1);
CID_Tab[1] = SDIO_GetResponse(SDIO_RESP2);
CID_Tab[2] = SDIO_GetResponse(SDIO_RESP3);
CID_Tab[3] = SDIO_GetResponse(SDIO_RESP4);
根据STM32F4X的数据手册可知,如果是长响应,高位的数据保存在SDIO_RESP1寄存器,低位保存在SDIO_RESP4寄存器
CID数据解析
根据波形图可知SD卡的CID响应数据为0x9f54495344333247614af80704017158,CID的数据解析表如下
下面就来对SD卡读取到CID数据进行解析
Name | Field | Width | CID-slice | Value |
---|---|---|---|---|
Manufacturer ID | MID | 8 | [127:120] | 0x9F |
OEM/Application ID | OID | 16 | [119:104] | 0x5449 |
Product name | PNM | 40 | [103:64] | 0x5344333247 "SD32G" |
Product revision | PRV | 8 | [63:56] | 0x61 |
Product serial number | PSN | 32 | [55:24] | 0x4af80704 |
reserved | X | 4 | [23:20] | 0 |
Manufacturing date | MDT | 12 | [19:8] | 0x171 |
CRC7 checksum | CRC | 7 | [7:1] | 0x2C |
not used, always 1 | x | 1 | [0:0] | 0x1 |
CMD3:SEND_RELATIVE_ADDR
CMD3的作用是请求SD卡发布一个RCA,RCA的作用相当于是一个地址,这个RCA后续在读写SD卡中用于对SD卡进行寻址。
命令发送程序
SDIO_CmdInitStructure.SDIO_Argument = 0x00; // 不带参数
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_REL_ADDR; // 命令索引 CMD3
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short; // 短响应
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No; // 不等待
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable; // 使能 CPSM状态机
SDIO_SendCommand(&SDIO_CmdInitStructure);
命令响应程序
CMD3的响应是R6,其中SD卡的RCA在响应数据的bit[31:16]
在程序中会先判断SD卡的响应是否正常,如果响应正常就通过SDIO_RESP1寄存器读取R6响应的数据,然后保存高16位的RCA地址。
static SD_Error CmdResp6Error(uint8_t cmd, uint16_t *prca)
{
SD_Error errorstatus = SD_OK;
uint32_t status;
uint32_t response_r1;
status = SDIO->STA;
while (!(status & (SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CTIMEOUT | SDIO_FLAG_CMDREND)))
{
status = SDIO->STA;
}
if (status & SDIO_FLAG_CTIMEOUT)
{
errorstatus = SD_CMD_RSP_TIMEOUT;
SDIO_ClearFlag(SDIO_FLAG_CTIMEOUT);
return(errorstatus);
}
else if (status & SDIO_FLAG_CCRCFAIL)
{
errorstatus = SD_CMD_CRC_FAIL;
SDIO_ClearFlag(SDIO_FLAG_CCRCFAIL);
return(errorstatus);
}
/*!< Check response received is of desired command */
if (SDIO_GetCommandResponse() != cmd)
{
errorstatus = SD_ILLEGAL_CMD;
return(errorstatus);
}
/*!< Clear all the static flags */
SDIO_ClearFlag(SDIO_STATIC_FLAGS);
/*!< We have received response, retrieve it. */
response_r1 = SDIO_GetResponse(SDIO_RESP1);
if (SD_ALLZERO == (response_r1 & (SD_R6_GENERAL_UNKNOWN_ERROR | SD_R6_ILLEGAL_CMD | SD_R6_COM_CRC_FAILED)))
{
*prca = (uint16_t) (response_r1 >> 16);
return(errorstatus);
}
if (response_r1 & SD_R6_GENERAL_UNKNOWN_ERROR)
{
return(SD_GENERAL_UNKNOWN_ERROR);
}
if (response_r1 & SD_R6_ILLEGAL_CMD)
{
return(SD_ILLEGAL_CMD);
}
if (response_r1 & SD_R6_COM_CRC_FAILED)
{
return(SD_COM_CRC_FAILED);
}
return(errorstatus);
}
CMD9:SEND_CSD
CMD9的作用是获取SD卡的CSD数据,CSD数据主要包含SD卡一些基本状态信息,包括SD的块大小、SD卡传输速率、SD卡的擦除扇区大小等信息。CMD9命令需要带参数,其参数是SD卡的RCA地址。
命令发送程序
SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)(rca << 16); // 参数RCA地址
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SEND_CSD; // 命令索引 CMD9
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Long; // 长响应
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No; // 不等待
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable; // 使能CPSM状态机
SDIO_SendCommand(&SDIO_CmdInitStructure);
命令响应程序
CMD9的响应是R2,长响应,CSD的返回数据是128位。
static SD_Error CmdResp2Error(void)
{
SD_Error errorstatus = SD_OK;
uint32_t status;
status = SDIO->STA;
while (!(status & (SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CTIMEOUT | SDIO_FLAG_CMDREND)))
{
status = SDIO->STA;
}
if (status & SDIO_FLAG_CTIMEOUT)
{
errorstatus = SD_CMD_RSP_TIMEOUT;
SDIO_ClearFlag(SDIO_FLAG_CTIMEOUT);
return(errorstatus);
}
else if (status & SDIO_FLAG_CCRCFAIL)
{
errorstatus = SD_CMD_CRC_FAIL;
SDIO_ClearFlag(SDIO_FLAG_CCRCFAIL);
return(errorstatus);
}
/*!< Clear all the static flags */
SDIO_ClearFlag(SDIO_STATIC_FLAGS);
return(errorstatus);
}
CSD_Tab[0] = SDIO_GetResponse(SDIO_RESP1);
CSD_Tab[1] = SDIO_GetResponse(SDIO_RESP2);
CSD_Tab[2] = SDIO_GetResponse(SDIO_RESP3);
CSD_Tab[3] = SDIO_GetResponse(SDIO_RESP4);
CSD的数据读取跟CID的数据读取是一样,不同的是两个数据的解析方法不一样
CSD数据解析
从波形图可知CSD的数据为0x400e00325b590000e68f7f800a400018,下面就对读取到的CSD数据进行解析。
Name | Field | Width | CID-slice | Value |
---|---|---|---|---|
CSD structure | CSD_STRUCTURE | 2 | [127:126] | 0x1 |
reserved | X | 6 | [125:120] | 0 |
data read access-time-1 | TAAC | 8 | [119:112] | 0xE "SD32G" |
data read access-time-2 in CLK cycles (NSAC*100) | NSAC | 8 | [111:104] | 0x0 |
max. data transfer rate | TRAN_SPEED | 8 | [103:96] | 0x32 |
card command classes | CCC | 12 | [95:84] | 0x5b5 |
max. read data block length | READ_BL_LEN | 4 | [83:80] | 0x9 |
partial blocks for read allowed | READ_BL_PARTIAL | 1 | [79:79] | 0x0 |
write block misalignment | WRITE_BLK_MISALIGN | 1 | [78:78] | 0x0 |
read block misalignment | READ_BLK_MISALIGN | 1 | [77:77] | 0x0 |
DSR implemented | DSR_IMP | 1 | [76:76] | 0x0 |
reserved | X | 6 | [75:70] | 0x0 |
device size | C_SIZE | 22 | [69:48] | 0xE68F |
reserved | X | 1 | [47:47] | 0x0 |
erase single block enable | ERASE_BLK_EN | 1 | [46:46] | 0x1 |
erase sector size | SECTOR_SIZE | 7 | [45:39] | 0x7F |
write protect group size | WP_GRP_SIZE | 7 | [39:32] | 0x0 |
write protect group enable | WP_GRP_ENABLE | 1 | [31:31] | 0x0 |
reserved | X | 2 | [30:29] | 0x0 |
write speed factor | R2W_FACTOR | 2 | [28:26] | 0x2 |
max. write data block length | WRITE_BL_LEN | 4 | [25:22] | 0x9 |
partial blocks for write allowed | WRITE_BL_PARTIAL | 1 | [21:21] | 0x0 |
reserved | X | 5 | [20:16] | 0x0 |
File format group | FILE_FORMAT_GRP | 1 | [15:15] | 0x0 |
copy flag | COPY | 1 | [14:14] | 0x0 |
permanent write protection | PERM_WRITE_PROTECT | 1 | [13:13] | 0x0 |
temporary write protection | TMP_WRITE_PROTECT | 1 | [12:12] | 0x0 |
File format | FILE_FORMAT | 2 | [11:10] | 0x0 |
reserved | X | 2 | [9:8] | 0x0 |
CRC | CRC | 7 | [7:1] | 0xC |
not used, always'1' | X | 1 | [0:0] | 0x1 |
CSD结构版本
CSD_STRUCTURE区域代表的是CSD寄存器结构使用的解析版本号,在SD卡的发展中使用了不同的CSD版本,在上面的例程中CSD_STRUCTURE的值是0x01,则代表使用的CSD V2.0的版本
READ_BL_LEN
最大的数据块读取长度,数值为9则代表是512字节
SD卡容量
根据上面的CSD数据我们可以计算出SD卡的容量大小,其计算公式如下memory capacity = (C_SIZE+1) * 512KByte
memory capacity = (0xE68F + 1) * 512 = 30,220,288(Byte) = 29512(M) = 28G文章来源:https://www.toymoban.com/news/detail-745692.html
获取SD卡信息
MCU读取到SD卡的CSD和CID数据后,就可以根据上面的表格进行数据的解析文章来源地址https://www.toymoban.com/news/detail-745692.html
SD_Error SD_GetCardInfo(SD_CardInfo *cardinfo)
{
SD_Error errorstatus = SD_OK;
uint8_t tmp = 0;
cardinfo->CardType = (uint8_t)CardType;
cardinfo->RCA = (uint16_t)RCA;
/*!< Byte 0 */
tmp = (uint8_t)((CSD_Tab[0] & 0xFF000000) >> 24);
cardinfo->SD_csd.CSDStruct = (tmp & 0xC0) >> 6;
cardinfo->SD_csd.SysSpecVersion = (tmp & 0x3C) >> 2;
cardinfo->SD_csd.Reserved1 = tmp & 0x03;
/*!< Byte 1 */
tmp = (uint8_t)((CSD_Tab[0] & 0x00FF0000) >> 16);
cardinfo->SD_csd.TAAC = tmp;
/*!< Byte 2 */
tmp = (uint8_t)((CSD_Tab[0] & 0x0000FF00) >> 8);
cardinfo->SD_csd.NSAC = tmp;
/*!< Byte 3 */
tmp = (uint8_t)(CSD_Tab[0] & 0x000000FF);
cardinfo->SD_csd.MaxBusClkFrec = tmp;
/*!< Byte 4 */
tmp = (uint8_t)((CSD_Tab[1] & 0xFF000000) >> 24);
cardinfo->SD_csd.CardComdClasses = tmp << 4;
/*!< Byte 5 */
tmp = (uint8_t)((CSD_Tab[1] & 0x00FF0000) >> 16);
cardinfo->SD_csd.CardComdClasses |= (tmp & 0xF0) >> 4;
cardinfo->SD_csd.RdBlockLen = tmp & 0x0F;
/*!< Byte 6 */
tmp = (uint8_t)((CSD_Tab[1] & 0x0000FF00) >> 8);
cardinfo->SD_csd.PartBlockRead = (tmp & 0x80) >> 7;
cardinfo->SD_csd.WrBlockMisalign = (tmp & 0x40) >> 6;
cardinfo->SD_csd.RdBlockMisalign = (tmp & 0x20) >> 5;
cardinfo->SD_csd.DSRImpl = (tmp & 0x10) >> 4;
cardinfo->SD_csd.Reserved2 = 0; /*!< Reserved */
if ((CardType == SDIO_STD_CAPACITY_SD_CARD_V1_1) || (CardType == SDIO_STD_CAPACITY_SD_CARD_V2_0))
{
cardinfo->SD_csd.DeviceSize = (tmp & 0x03) << 10;
/*!< Byte 7 */
tmp = (uint8_t)(CSD_Tab[1] & 0x000000FF);
cardinfo->SD_csd.DeviceSize |= (tmp) << 2;
/*!< Byte 8 */
tmp = (uint8_t)((CSD_Tab[2] & 0xFF000000) >> 24);
cardinfo->SD_csd.DeviceSize |= (tmp & 0xC0) >> 6;
cardinfo->SD_csd.MaxRdCurrentVDDMin = (tmp & 0x38) >> 3;
cardinfo->SD_csd.MaxRdCurrentVDDMax = (tmp & 0x07);
/*!< Byte 9 */
tmp = (uint8_t)((CSD_Tab[2] & 0x00FF0000) >> 16);
cardinfo->SD_csd.MaxWrCurrentVDDMin = (tmp & 0xE0) >> 5;
cardinfo->SD_csd.MaxWrCurrentVDDMax = (tmp & 0x1C) >> 2;
cardinfo->SD_csd.DeviceSizeMul = (tmp & 0x03) << 1;
/*!< Byte 10 */
tmp = (uint8_t)((CSD_Tab[2] & 0x0000FF00) >> 8);
cardinfo->SD_csd.DeviceSizeMul |= (tmp & 0x80) >> 7;
cardinfo->CardCapacity = (cardinfo->SD_csd.DeviceSize + 1) ;
cardinfo->CardCapacity *= (1 << (cardinfo->SD_csd.DeviceSizeMul + 2));
cardinfo->CardBlockSize = 1 << (cardinfo->SD_csd.RdBlockLen);
cardinfo->CardCapacity *= cardinfo->CardBlockSize;
}
else if (CardType == SDIO_HIGH_CAPACITY_SD_CARD)
{
/*!< Byte 7 */
tmp = (uint8_t)(CSD_Tab[1] & 0x000000FF);
cardinfo->SD_csd.DeviceSize = (tmp & 0x3F) << 16;
/*!< Byte 8 */
tmp = (uint8_t)((CSD_Tab[2] & 0xFF000000) >> 24);
cardinfo->SD_csd.DeviceSize |= (tmp << 8);
/*!< Byte 9 */
tmp = (uint8_t)((CSD_Tab[2] & 0x00FF0000) >> 16);
cardinfo->SD_csd.DeviceSize |= (tmp);
/*!< Byte 10 */
tmp = (uint8_t)((CSD_Tab[2] & 0x0000FF00) >> 8);
cardinfo->CardCapacity = ((uint64_t)cardinfo->SD_csd.DeviceSize + 1) * 512 * 1024;
cardinfo->CardBlockSize = 512;
}
cardinfo->SD_csd.EraseGrSize = (tmp & 0x40) >> 6;
cardinfo->SD_csd.EraseGrMul = (tmp & 0x3F) << 1;
/*!< Byte 11 */
tmp = (uint8_t)(CSD_Tab[2] & 0x000000FF);
cardinfo->SD_csd.EraseGrMul |= (tmp & 0x80) >> 7;
cardinfo->SD_csd.WrProtectGrSize = (tmp & 0x7F);
/*!< Byte 12 */
tmp = (uint8_t)((CSD_Tab[3] & 0xFF000000) >> 24);
cardinfo->SD_csd.WrProtectGrEnable = (tmp & 0x80) >> 7;
cardinfo->SD_csd.ManDeflECC = (tmp & 0x60) >> 5;
cardinfo->SD_csd.WrSpeedFact = (tmp & 0x1C) >> 2;
cardinfo->SD_csd.MaxWrBlockLen = (tmp & 0x03) << 2;
/*!< Byte 13 */
tmp = (uint8_t)((CSD_Tab[3] & 0x00FF0000) >> 16);
cardinfo->SD_csd.MaxWrBlockLen |= (tmp & 0xC0) >> 6;
cardinfo->SD_csd.WriteBlockPaPartial = (tmp & 0x20) >> 5;
cardinfo->SD_csd.Reserved3 = 0;
cardinfo->SD_csd.ContentProtectAppli = (tmp & 0x01);
/*!< Byte 14 */
tmp = (uint8_t)((CSD_Tab[3] & 0x0000FF00) >> 8);
cardinfo->SD_csd.FileFormatGrouop = (tmp & 0x80) >> 7;
cardinfo->SD_csd.CopyFlag = (tmp & 0x40) >> 6;
cardinfo->SD_csd.PermWrProtect = (tmp & 0x20) >> 5;
cardinfo->SD_csd.TempWrProtect = (tmp & 0x10) >> 4;
cardinfo->SD_csd.FileFormat = (tmp & 0x0C) >> 2;
cardinfo->SD_csd.ECC = (tmp & 0x03);
/*!< Byte 15 */
tmp = (uint8_t)(CSD_Tab[3] & 0x000000FF);
cardinfo->SD_csd.CSD_CRC = (tmp & 0xFE) >> 1;
cardinfo->SD_csd.Reserved4 = 1;
/*!< Byte 0 */
tmp = (uint8_t)((CID_Tab[0] & 0xFF000000) >> 24);
cardinfo->SD_cid.ManufacturerID = tmp;
/*!< Byte 1 */
tmp = (uint8_t)((CID_Tab[0] & 0x00FF0000) >> 16);
cardinfo->SD_cid.OEM_AppliID = tmp << 8;
/*!< Byte 2 */
tmp = (uint8_t)((CID_Tab[0] & 0x000000FF00) >> 8);
cardinfo->SD_cid.OEM_AppliID |= tmp;
/*!< Byte 3 */
tmp = (uint8_t)(CID_Tab[0] & 0x000000FF);
cardinfo->SD_cid.ProdName1 = tmp << 24;
/*!< Byte 4 */
tmp = (uint8_t)((CID_Tab[1] & 0xFF000000) >> 24);
cardinfo->SD_cid.ProdName1 |= tmp << 16;
/*!< Byte 5 */
tmp = (uint8_t)((CID_Tab[1] & 0x00FF0000) >> 16);
cardinfo->SD_cid.ProdName1 |= tmp << 8;
/*!< Byte 6 */
tmp = (uint8_t)((CID_Tab[1] & 0x0000FF00) >> 8);
cardinfo->SD_cid.ProdName1 |= tmp;
/*!< Byte 7 */
tmp = (uint8_t)(CID_Tab[1] & 0x000000FF);
cardinfo->SD_cid.ProdName2 = tmp;
/*!< Byte 8 */
tmp = (uint8_t)((CID_Tab[2] & 0xFF000000) >> 24);
cardinfo->SD_cid.ProdRev = tmp;
/*!< Byte 9 */
tmp = (uint8_t)((CID_Tab[2] & 0x00FF0000) >> 16);
cardinfo->SD_cid.ProdSN = tmp << 24;
/*!< Byte 10 */
tmp = (uint8_t)((CID_Tab[2] & 0x0000FF00) >> 8);
cardinfo->SD_cid.ProdSN |= tmp << 16;
/*!< Byte 11 */
tmp = (uint8_t)(CID_Tab[2] & 0x000000FF);
cardinfo->SD_cid.ProdSN |= tmp << 8;
/*!< Byte 12 */
tmp = (uint8_t)((CID_Tab[3] & 0xFF000000) >> 24);
cardinfo->SD_cid.ProdSN |= tmp;
/*!< Byte 13 */
tmp = (uint8_t)((CID_Tab[3] & 0x00FF0000) >> 16);
cardinfo->SD_cid.Reserved1 |= (tmp & 0xF0) >> 4;
cardinfo->SD_cid.ManufactDate = (tmp & 0x0F) << 8;
/*!< Byte 14 */
tmp = (uint8_t)((CID_Tab[3] & 0x0000FF00) >> 8);
cardinfo->SD_cid.ManufactDate |= tmp;
/*!< Byte 15 */
tmp = (uint8_t)(CID_Tab[3] & 0x000000FF);
cardinfo->SD_cid.CID_CRC = (tmp & 0xFE) >> 1;
cardinfo->SD_cid.Reserved2 = 1;
return(errorstatus);
}
SD卡初始化流程
- 发送CMD2获取CID数据
- 发送CMD3获取SD卡的RCA地址
- 发送CMD9获取SD卡的CSD数据
- 根据CID和CSD表格解析数据
到了这里,关于STM32F4X SDIO(七) 例程讲解-SD_InitializeCards & SD_GetCardInfo的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!