[应用相关] 零死角玩转stm32-高级篇之SDIO

[复制链接]
4320|59
 楼主| 雨果喝水 发表于 2021-6-30 17:05 | 显示全部楼层
6.调用SD_EnableWideBusOperation(SDIO_BusWide_4b)开启4bit数据线模式

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

下面进入SD_PowerON()函数,分析完这个函数大家就能了解SDIO如何接收、发送命令了。
 楼主| 雨果喝水 发表于 2021-6-30 17:06 | 显示全部楼层
  1. /*

  2. * 函数名:SD_PowerON

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

  4. * 输入  :无

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

  6. *         成功时则为 SD_OK

  7. * 调用  :在 SD_Init() 调用

  8. */

  9. SD_Error SD_PowerON(void)

  10. {

  11. SD_Error errorstatus = SD_OK;

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

  13. uint32_t SDType = SD_STD_CAPACITY;

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

  15. /*!< Configure the SDIO peripheral */

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

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

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

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

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

  21. SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising;

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

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

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

  25. SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable;//硬件流

  26. SDIO_Init(&SDIO_InitStructure);

  27. /*!< Set Power State to ON */

  28. SDIO_SetPowerState(SDIO_PowerState_ON);

  29. /*!< Enable SDIO Clock */

  30. SDIO_ClockCmd(ENABLE);

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

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

  33. /*!< No CMD response required */

  34. SDIO_CmdInitStructure.SDIO_Argument = 0x0;

  35. SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_GO_IDLE_STATE; //cmd0

  36. SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_No;  //无响应

  37. SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;

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

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

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

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

  42. {

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

  44. return(errorstatus);

  45. }

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

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

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

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

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

  51. /*!< CMD Response: R7 */

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

  53. SDIO_CmdInitStructure.SDIO_CmdIndex = SDIO_SEND_IF_COND;  //cmd8

  54. SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;     //r7

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

  56. SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;

  57. SDIO_SendCommand(&SDIO_CmdInitStructure);

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

  59. errorstatus = CmdResp7Error();

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

  61. {

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

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

  64. }

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

  66. {

  67. /*!< CMD55 */

  68. SDIO_CmdInitStructure.SDIO_Argument = 0x00;

  69. SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD;

  70. SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;

  71. SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;

  72. SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;

  73. SDIO_SendCommand(&SDIO_CmdInitStructure);

  74. errorstatus = CmdResp1Error(SD_CMD_APP_CMD);

  75. }

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

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

  78. SDIO_CmdInitStructure.SDIO_Argument = 0x00;

  79. SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD;

  80. SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short; //r1

  81. SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;

  82. SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;

  83. SDIO_SendCommand(&SDIO_CmdInitStructure);

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

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

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

  87. or SD card 1.x */

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

  89. {

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

  91. /*!< SD CARD */

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

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

  94. {

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

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

  97. SDIO_CmdInitStructure.SDIO_Argument = 0x00;

  98. SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD;   //CMD55

  99. SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;

  100. SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;

  101. SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;

  102. SDIO_SendCommand(&SDIO_CmdInitStructure);

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

  104. if (errorstatus != SD_OK)

  105. {

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

  107. }

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

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

  110. SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_APP_OP_COND;

  111. SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;  //r3

  112. SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;

  113. SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;

  114. SDIO_SendCommand(&SDIO_CmdInitStructure);

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

  116. if (errorstatus != SD_OK)

  117. {

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

  119. }

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

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

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

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

  124. }

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

  126. {

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

  128. return(errorstatus);

  129. }

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

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

  132. {

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

  134. }

  135. }/*!< else MMC Card */

  136. return(errorstatus);

  137. }
 楼主| 雨果喝水 发表于 2021-6-30 17:07 | 显示全部楼层
这个函数的流程就是卡的上电、识别操作,如下图:
 楼主| 雨果喝水 发表于 2021-6-30 17:10 | 显示全部楼层
 楼主| 雨果喝水 发表于 2021-6-30 17:11 | 显示全部楼层
卡的上电,识别流程:

截图来自《Simplified_Physical_Layer_Spec.pdf》 page27

代码中所有的判断语句都是根据这个图的各个识别走向展开的,最终把卡分为1.0版的SD存储卡,2.0版的SDSC卡和2.0版的SDHC卡。
 楼主| 雨果喝水 发表于 2021-6-30 17:12 | 显示全部楼层
在这个代码流程中有两点要注意一下:

1.初始化的时钟。SDIO_CK的时钟分为两个阶段,在初始化阶段SDIO_CK的频率要小于400KHz,初始化完成后可把SDIO_CK调整成高速模式,高速模式时超过24M要开启bypass模式,对于SD存储卡即使开启bypass,最高频率不能超过25MHz。
 楼主| 雨果喝水 发表于 2021-6-30 17:13 | 显示全部楼层
2.CMD8命令。
6931460dc35b6bad0d.png
 楼主| 雨果喝水 发表于 2021-6-30 17:14 | 显示全部楼层
CMD8命令中的VHS是用来确认主机SDIO是否支持卡的工作电压的。Check pattern部分可以是任何数值,若SDIO支持卡的工作电压,卡会把接收到的check pattern数值原样返回给主机。
 楼主| 雨果喝水 发表于 2021-6-30 17:14 | 显示全部楼层
 楼主| 雨果喝水 发表于 2021-6-30 17:15 | 显示全部楼层
3.ACMD41命令。

这个命令也是用来进一步检查SDIO是否支持卡的工作电压的,协议要它在调用它之前必须先调用CMD8,另外还可以通过它命令参数中的HCS位来区分卡是SDHC卡还是SDSC卡。
 楼主| 雨果喝水 发表于 2021-6-30 17:15 | 显示全部楼层
确认工作电压时循环地发送ACMD41,发送后检查在SD卡上的OCR寄存器中的pwr_up位,若pwr_up位置为1,表明SDIO支持卡的工作电压,卡开始正常工作。
 楼主| 雨果喝水 发表于 2021-6-30 17:16 | 显示全部楼层
同时把ACMD41中的命令参数HCS位置1,卡正常工作的时候检测OCR寄存器中的CCS位,若CCS位为1则说明该卡为SDHC卡,为零则为SDSC卡。
 楼主| 雨果喝水 发表于 2021-6-30 17:16 | 显示全部楼层
因为ACMD41命令属于ACMD命令,在发送ACMD命令前都要先发送CMD55.
 楼主| 雨果喝水 发表于 2021-6-30 17:17 | 显示全部楼层
 楼主| 雨果喝水 发表于 2021-6-30 17:18 | 显示全部楼层
ACMD41命令的响应(R3),返回的是OCR寄存器的值
 楼主| 雨果喝水 发表于 2021-6-30 17:20 | 显示全部楼层
 楼主| 雨果喝水 发表于 2021-6-30 17:21 | 显示全部楼层
SD卡上电确认成功后,进入SD_InitializeCards()函数:
 楼主| 雨果喝水 发表于 2021-6-30 17:21 | 显示全部楼层
  1. /*

  2. * 函数名:SD_InitializeCards

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

  4. * 输入  :无

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

  6. *         成功时则为 SD_OK

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

  8. */

  9. SD_Error SD_InitializeCards(void)

  10. {

  11. SD_Error errorstatus = SD_OK;

  12. uint16_t rca = 0x01;

  13. if (SDIO_GetPowerState() == SDIO_PowerState_OFF)

  14. {

  15. errorstatus = SD_REQUEST_NOT_APPLICABLE;

  16. return(errorstatus);

  17. }

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

  19. {

  20. /*!< Send CMD2 ALL_SEND_CID */

  21. SDIO_CmdInitStructure.SDIO_Argument = 0x0;

  22. SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_ALL_SEND_CID;

  23. SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Long;

  24. SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;

  25. SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;

  26. SDIO_SendCommand(&SDIO_CmdInitStructure);

  27. errorstatus = CmdResp2Error();

  28. if (SD_OK != errorstatus)

  29. {

  30. return(errorstatus);

  31. }

  32. CID_Tab[0] = SDIO_GetResponse(SDIO_RESP1);

  33. CID_Tab[1] = SDIO_GetResponse(SDIO_RESP2);

  34. CID_Tab[2] = SDIO_GetResponse(SDIO_RESP3);

  35. CID_Tab[3] = SDIO_GetResponse(SDIO_RESP4);

  36. }

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

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

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

  40. {

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

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

  43. SDIO_CmdInitStructure.SDIO_Argument = 0x00;

  44. SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_REL_ADDR;  //cmd3

  45. SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short; //r6

  46. SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;

  47. SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;

  48. SDIO_SendCommand(&SDIO_CmdInitStructure);

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

  50. if (SD_OK != errorstatus)

  51. {

  52. return(errorstatus);

  53. }

  54. }

  55. if (SDIO_SECURE_DIGITAL_IO_CARD != CardType)

  56. {

  57. RCA = rca;

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

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

  60. SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SEND_CSD;

  61. SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Long;

  62. SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;

  63. SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;

  64. SDIO_SendCommand(&SDIO_CmdInitStructure);

  65. errorstatus = CmdResp2Error();

  66. if (SD_OK != errorstatus)

  67. {

  68. return(errorstatus);

  69. }

  70. CSD_Tab[0] = SDIO_GetResponse(SDIO_RESP1);

  71. CSD_Tab[1] = SDIO_GetResponse(SDIO_RESP2);

  72. CSD_Tab[2] = SDIO_GetResponse(SDIO_RESP3);

  73. CSD_Tab[3] = SDIO_GetResponse(SDIO_RESP4);

  74. }

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

  76. return(errorstatus);

  77. }
 楼主| 雨果喝水 发表于 2021-6-30 17:22 | 显示全部楼层
这个函数向卡发送了CMD2和CMD3命令

1.CMD2
 楼主| 雨果喝水 发表于 2021-6-30 17:22 | 显示全部楼层
CMD2命令是要求卡返回它的CID寄存器的内容。

命令的响应格式(R2)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 在线客服 返回列表 返回顶部