发新帖我要提问
123
返回列表
打印
[应用相关]

零死角玩转stm32-高级篇之SDIO

[复制链接]
楼主: 雨果喝水
手机看帖
扫描二维码
随时随地手机跟帖
41
雨果喝水|  楼主 | 2021-6-30 17:05 | 只看该作者 |只看大图 回帖奖励 |倒序浏览
6.调用SD_EnableWideBusOperation(SDIO_BusWide_4b)开启4bit数据线模式

如果SD_Init()函数能够执行完整个流程,并且返回值是SD_OK的话则说明初始化成功,就可以开始进行擦除、读写的操作了。

下面进入SD_PowerON()函数,分析完这个函数大家就能了解SDIO如何接收、发送命令了。

使用特权

评论回复
42
雨果喝水|  楼主 | 2021-6-30 17:06 | 只看该作者
/*

* 函数名:SD_PowerON

* 描述  :确保SD卡的工作电压和配置控制时钟

* 输入  :无

* 输出  :-SD_Error SD卡错误代码

*         成功时则为 SD_OK

* 调用  :在 SD_Init() 调用

*/

SD_Error SD_PowerON(void)

{

SD_Error errorstatus = SD_OK;

uint32_t response = 0, count = 0, validvoltage = 0;

uint32_t SDType = SD_STD_CAPACITY;

/*!< Power ON Sequence -----------------------------------------------------*/

/*!< Configure the SDIO peripheral */

/*!< SDIOCLK = HCLK, SDIO_CK = HCLK/(2 + SDIO_INIT_CLK_DIV) */

/*!< on STM32F2xx devices, SDIOCLK is fixed to 48MHz */

/*!< SDIO_CK for initialization should not exceed 400 KHz */

/*初始化时的时钟不能大于400KHz*/

SDIO_InitStructure.SDIO_ClockDiv = SDIO_INIT_CLK_DIV; /* HCLK = 72MHz, SDIOCLK = 72MHz, SDIO_CK = HCLK/(178 + 2) = 400 KHz */

SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising;

SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable;  //不使用bypass模式,直接用HCLK进行分频得到SDIO_CK

SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable; // 空闲时不关闭时钟电源

SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_1b;                    //1位数据线

SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable;//硬件流

SDIO_Init(&SDIO_InitStructure);

/*!< Set Power State to ON */

SDIO_SetPowerState(SDIO_PowerState_ON);

/*!< Enable SDIO Clock */

SDIO_ClockCmd(ENABLE);

/*下面发送一系列命令,开始卡识别流程*/

/*!< CMD0: GO_IDLE_STATE ---------------------------------------------------*/

/*!< No CMD response required */

SDIO_CmdInitStructure.SDIO_Argument = 0x0;

SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_GO_IDLE_STATE; //cmd0

SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_No;  //无响应

SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;

SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;  //则CPSM在开始发送命令之前等待数据传输结束。

SDIO_SendCommand(&SDIO_CmdInitStructure);         //写命令进命令寄存器

errorstatus = CmdError();//检测是否正确接收到cmd0

if (errorstatus != SD_OK) //命令发送出错,返回

{

/*!< CMD Response TimeOut (wait for CMDSENT flag) */

return(errorstatus);

}

/*!< CMD8: SEND_IF_COND ----------------------------------------------------*/

/*!< Send CMD8 to verify SD card interface operating condition */

/*!< Argument: - [31:12]: Reserved (shall be set to '0')

- [11:8]: Supply Voltage (VHS) 0x1 (Range: 2.7-3.6 V)

- [7:0]: Check Pattern (recommended 0xAA) */

/*!< CMD Response: R7 */

SDIO_CmdInitStructure.SDIO_Argument = SD_CHECK_PATTERN;   //接收到命令sd会返回这个参数

SDIO_CmdInitStructure.SDIO_CmdIndex = SDIO_SEND_IF_COND;  //cmd8

SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;     //r7

SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;            //关闭等待中断

SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;

SDIO_SendCommand(&SDIO_CmdInitStructure);

/*检查是否接收到命令*/

errorstatus = CmdResp7Error();

if (errorstatus == SD_OK)     //有响应则card遵循sd协议2.0版本

{

CardType = SDIO_STD_CAPACITY_SD_CARD_V2_0; /*!< SD Card 2.0 ,先把它定义会sdsc类型的卡*/

SDType = SD_HIGH_CAPACITY;  //这个变量用作acmd41的参数,用来询问是sdsc卡还是sdhc卡

}

else  //无响应,说明是1.x的或mmc的卡

{

/*!< CMD55 */

SDIO_CmdInitStructure.SDIO_Argument = 0x00;

SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD;

SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;

SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;

SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;

SDIO_SendCommand(&SDIO_CmdInitStructure);

errorstatus = CmdResp1Error(SD_CMD_APP_CMD);

}

/*!< CMD55 */     //为什么在else里和else外面都要发送CMD55?

//发送cmd55,用于检测是sd卡还是mmc卡,或是不支持的卡

SDIO_CmdInitStructure.SDIO_Argument = 0x00;

SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD;

SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short; //r1

SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;

SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;

SDIO_SendCommand(&SDIO_CmdInitStructure);

errorstatus = CmdResp1Error(SD_CMD_APP_CMD);  //是否响应,没响应的是mmc或不支持的卡

/*!< If errorstatus is Command TimeOut, it is a MMC card */

/*!< If errorstatus is SD_OK it is a SD card: SD card 2.0 (voltage range mismatch)

or SD card 1.x */

if (errorstatus == SD_OK) //响应了cmd55,是sd卡,可能为1.x,可能为2.0

{

/*下面开始循环地发送sdio支持的电压范围,循环一定次数*/

/*!< SD CARD */

/*!< Send ACMD41 SD_APP_OP_COND with Argument 0x80100000 */

while ((!validvoltage) && (count < SD_MAX_VOLT_TRIAL))

{

//因为下面要用到ACMD41,是ACMD命令,在发送ACMD命令前都要先向卡发送CMD55

/*!< SEND CMD55 APP_CMD with RCA as 0 */

SDIO_CmdInitStructure.SDIO_Argument = 0x00;

SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD;   //CMD55

SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;

SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;

SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;

SDIO_SendCommand(&SDIO_CmdInitStructure);

errorstatus = CmdResp1Error(SD_CMD_APP_CMD); //检测响应

if (errorstatus != SD_OK)

{

return(errorstatus);//没响应CMD55,返回

}

//acmd41,命令参数由支持的电压范围及HCS位组成,HCS位置一来区分卡是SDSc还是sdhc

SDIO_CmdInitStructure.SDIO_Argument = SD_VOLTAGE_WINDOW_SD | SDType;    //参数为主机可供电压范围及hcs位

SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_APP_OP_COND;

SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;  //r3

SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;

SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;

SDIO_SendCommand(&SDIO_CmdInitStructure);

errorstatus = CmdResp3Error();    //检测是否正确接收到数据

if (errorstatus != SD_OK)

{

return(errorstatus);  //没正确接收到acmd41,出错,返回

}

/*若卡需求电压在SDIO的供电电压范围内,会自动上电并标志pwr_up位*/

response = SDIO_GetResponse(SDIO_RESP1);   //读取卡寄存器,卡状态

validvoltage = (((response >> 31) == 1) ? 1 : 0); //读取卡的ocr寄存器的pwr_up位,看是否已工作在正常电压

count++;            //计算循环次数

}

if (count >= SD_MAX_VOLT_TRIAL) //循环检测超过一定次数还没上电

{

errorstatus = SD_INVALID_VOLTRANGE;      //SDIO不支持card的供电电压

return(errorstatus);

}

/*检查卡返回信息中的HCS位*/

if (response &= SD_HIGH_CAPACITY)  //判断ocr中的ccs位 ,如果是sdsc卡则不执行下面的语句

{

CardType = SDIO_HIGH_CAPACITY_SD_CARD;  //把卡类型从初始化的sdsc型改为sdhc型

}

}/*!< else MMC Card */

return(errorstatus);

}

使用特权

评论回复
43
雨果喝水|  楼主 | 2021-6-30 17:07 | 只看该作者
这个函数的流程就是卡的上电、识别操作,如下图:

使用特权

评论回复
44
雨果喝水|  楼主 | 2021-6-30 17:10 | 只看该作者

使用特权

评论回复
45
雨果喝水|  楼主 | 2021-6-30 17:11 | 只看该作者
卡的上电,识别流程:

截图来自《Simplified_Physical_Layer_Spec.pdf》 page27

代码中所有的判断语句都是根据这个图的各个识别走向展开的,最终把卡分为1.0版的SD存储卡,2.0版的SDSC卡和2.0版的SDHC卡。

使用特权

评论回复
46
雨果喝水|  楼主 | 2021-6-30 17:12 | 只看该作者
在这个代码流程中有两点要注意一下:

1.初始化的时钟。SDIO_CK的时钟分为两个阶段,在初始化阶段SDIO_CK的频率要小于400KHz,初始化完成后可把SDIO_CK调整成高速模式,高速模式时超过24M要开启bypass模式,对于SD存储卡即使开启bypass,最高频率不能超过25MHz。

使用特权

评论回复
47
雨果喝水|  楼主 | 2021-6-30 17:13 | 只看该作者
2.CMD8命令。

使用特权

评论回复
48
雨果喝水|  楼主 | 2021-6-30 17:14 | 只看该作者
CMD8命令中的VHS是用来确认主机SDIO是否支持卡的工作电压的。Check pattern部分可以是任何数值,若SDIO支持卡的工作电压,卡会把接收到的check pattern数值原样返回给主机。

使用特权

评论回复
49
雨果喝水|  楼主 | 2021-6-30 17:14 | 只看该作者

使用特权

评论回复
50
雨果喝水|  楼主 | 2021-6-30 17:15 | 只看该作者
3.ACMD41命令。

这个命令也是用来进一步检查SDIO是否支持卡的工作电压的,协议要它在调用它之前必须先调用CMD8,另外还可以通过它命令参数中的HCS位来区分卡是SDHC卡还是SDSC卡。

使用特权

评论回复
51
雨果喝水|  楼主 | 2021-6-30 17:15 | 只看该作者
确认工作电压时循环地发送ACMD41,发送后检查在SD卡上的OCR寄存器中的pwr_up位,若pwr_up位置为1,表明SDIO支持卡的工作电压,卡开始正常工作。

使用特权

评论回复
52
雨果喝水|  楼主 | 2021-6-30 17:16 | 只看该作者
同时把ACMD41中的命令参数HCS位置1,卡正常工作的时候检测OCR寄存器中的CCS位,若CCS位为1则说明该卡为SDHC卡,为零则为SDSC卡。

使用特权

评论回复
53
雨果喝水|  楼主 | 2021-6-30 17:16 | 只看该作者
因为ACMD41命令属于ACMD命令,在发送ACMD命令前都要先发送CMD55.

使用特权

评论回复
54
雨果喝水|  楼主 | 2021-6-30 17:17 | 只看该作者

使用特权

评论回复
55
雨果喝水|  楼主 | 2021-6-30 17:18 | 只看该作者
ACMD41命令的响应(R3),返回的是OCR寄存器的值

使用特权

评论回复
56
雨果喝水|  楼主 | 2021-6-30 17:20 | 只看该作者

6220960dc36ef87de6.png (143.03 KB )

6220960dc36ef87de6.png

使用特权

评论回复
57
雨果喝水|  楼主 | 2021-6-30 17:21 | 只看该作者
SD卡上电确认成功后,进入SD_InitializeCards()函数:

使用特权

评论回复
58
雨果喝水|  楼主 | 2021-6-30 17:21 | 只看该作者
/*

* 函数名:SD_InitializeCards

* 描述  :初始化所有的卡或者单个卡进入就绪状态

* 输入  :无

* 输出  :-SD_Error SD卡错误代码

*         成功时则为 SD_OK

* 调用  :在 SD_Init() 调用,在调用power_on()上电卡识别完毕后,调用此函数进**初始化

*/

SD_Error SD_InitializeCards(void)

{

SD_Error errorstatus = SD_OK;

uint16_t rca = 0x01;

if (SDIO_GetPowerState() == SDIO_PowerState_OFF)

{

errorstatus = SD_REQUEST_NOT_APPLICABLE;

return(errorstatus);

}

if (SDIO_SECURE_DIGITAL_IO_CARD != CardType)//判断卡的类型

{

/*!< Send CMD2 ALL_SEND_CID */

SDIO_CmdInitStructure.SDIO_Argument = 0x0;

SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_ALL_SEND_CID;

SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Long;

SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;

SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;

SDIO_SendCommand(&SDIO_CmdInitStructure);

errorstatus = CmdResp2Error();

if (SD_OK != errorstatus)

{

return(errorstatus);

}

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);

}

/*下面开始SD卡初始化流程*/

if ((SDIO_STD_CAPACITY_SD_CARD_V1_1 == CardType) ||  (SDIO_STD_CAPACITY_SD_CARD_V2_0 == CardType) ||  (SDIO_SECURE_DIGITAL_IO_COMBO_CARD == CardType)

||  (SDIO_HIGH_CAPACITY_SD_CARD == CardType))  //使用的是2.0的卡

{

/*!< Send CMD3 SET_REL_ADDR with argument 0 */

/*!< SD Card publishes its RCA. */

SDIO_CmdInitStructure.SDIO_Argument = 0x00;

SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_REL_ADDR;  //cmd3

SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short; //r6

SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;

SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;

SDIO_SendCommand(&SDIO_CmdInitStructure);

errorstatus = CmdResp6Error(SD_CMD_SET_REL_ADDR, &rca); //把接收到的卡相对地址存起来。

if (SD_OK != errorstatus)

{

return(errorstatus);

}

}

if (SDIO_SECURE_DIGITAL_IO_CARD != CardType)

{

RCA = rca;

/*!< Send CMD9 SEND_CSD with argument as card's RCA */

SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)(rca << 16);

SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SEND_CSD;

SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Long;

SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;

SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;

SDIO_SendCommand(&SDIO_CmdInitStructure);

errorstatus = CmdResp2Error();

if (SD_OK != errorstatus)

{

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);

}

errorstatus = SD_OK; /*!< All cards get intialized */

return(errorstatus);

}

使用特权

评论回复
59
雨果喝水|  楼主 | 2021-6-30 17:22 | 只看该作者
这个函数向卡发送了CMD2和CMD3命令

1.CMD2

使用特权

评论回复
60
雨果喝水|  楼主 | 2021-6-30 17:22 | 只看该作者
CMD2命令是要求卡返回它的CID寄存器的内容。

命令的响应格式(R2)。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则