Chinaunix首页 | 论坛 | 博客
  • 博客访问: 38723
  • 博文数量: 27
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 10
  • 用 户 组: 普通用户
  • 注册时间: 2022-05-02 01:54
文章分类

全部博文(27)

文章存档

2022年(27)

我的朋友

分类: 嵌入式

2022-05-03 15:27:18

原文地址:STM32中SD的初始化代码 作者:我本痴情

STM32中SD的初始化代码:
SD_Error SD_Init(void)
{
        /*重置SD_Error状态*/
        SD_Error errorstatus = SD_OK;

        /* SDIO 外设底层引脚初始化 */
        GPIO_Configuration();

        /*对SDIO的所有寄存器进行复位*/
        SDIO_DeInit();

        /*上电并进行卡识别流程,确认卡的操作电压*/
        errorstatus = SD_PowerON();

        /*如果上电,识别不成功,返回“响应超时”错误 */
        if (errorstatus != SD_OK)
        {
                /*!< CMD Response TimeOut (wait for CMDSENT flag) */
                return(errorstatus);
        }

        /*卡识别成功,进行卡初始化    */
        errorstatus = SD_InitializeCards();

        if (errorstatus != SD_OK)          //失败返回
        {
                /*!< CMD Response TimeOut (wait for CMDSENT flag) */
                return(errorstatus);
        }

        /*!< Configure the SDIO peripheral
        上电识别,卡初始化都完成后,进入数据传输模式,提高读写速度
        速度若超过24M要进入bypass模式
        !< on STM32F2xx devices, SDIOCLK is fixed to 48MHz
        !< SDIOCLK = HCLK, SDIO_CK = HCLK/(2 + SDIO_TRANSFER_CLK_DIV) */
        SDIO_InitStructure.SDIO_ClockDiv = SDIO_TRANSFER_CLK_DIV;
        SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising;                //上升沿采集数据
        SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable;//时钟频率若超过24M,要开启此模式
        SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable;           //若开启此功能,在总线空闲时关闭sd_clk时钟
        SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_1b;                                                //1位模式
        SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable; //硬件流,若开启,在FIFO不能进行发送和接收数据时,数据传输暂停
        SDIO_Init(&SDIO_InitStructure);

        if (errorstatus == SD_OK)
        {
                /*----------------- Read CSD/CID MSD registers ------------------*/
                errorstatus = SD_GetCardInfo(&SDCardInfo);       //用来读取csd/cid寄存器
        }

        if (errorstatus == SD_OK)
        {
                /*----------------- Select Card --------------------------------*/
                errorstatus = SD_SelectDeselect((uint32_t) (SDCardInfo.RCA << 16));          //通过cmd7,rca选择要操作的卡
        }

        if (errorstatus == SD_OK)
        {
                errorstatus = SD_EnableWideBusOperation(SDIO_BusWide_4b);        //开启4bits模式
        }

        return(errorstatus);
}

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 */
        /* 上电顺序 -------------------------------------------------------------------------*/
        /* 为卡识别模式配置 SDIO 外设 */
        /* SDIOCLK = HCLK, SDIO_CK = HCLK/(2 + SDIO_INIT_CLK_DIV) = 400KHz */
        /* 卡识别模式时的 SDIO_CK 不能超过 400KHz */
        /*初始化时的时钟不能大于400KHz*/
        SDIO_InitStructure.SDIO_ClockDiv = SDIO_INIT_CLK_DIV;/*设置时钟分频=178*/        /* 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);//初始化SDIO

        /*!< Set Power State to ON */          /*打开SDIO的电源*/
        SDIO_SetPowerState(SDIO_PowerState_ON);

        /*!< Enable SDIO Clock */        /*开SDIO的时钟*/
        SDIO_ClockCmd(ENABLE);

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

        /************************发送CMD0***************************************        */
        /************************发送CMD0***************************************        */
        /************************发送CMD0***************************************        */
        /*!< CMD0: GO_IDLE_STATE变为空闲状态---------------------------------------------------*/
        /*!< No CMD response required */
        /* 发送 CMD0 复位卡,该命令不需要响应 */
        SDIO_CmdInitStructure.SDIO_Argument = 0x0;//命令参数,是命令的一部分,和命令一起发送
        SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_GO_IDLE_STATE; //cmd0命令索引,变为空闲状态
        SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_No;//无响应        SDIO响应类型
        SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;           //SDIO不等待中断
        SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;//则CPSM在开始发送命令之前等待数据传输结束。
        SDIO_SendCommand(&SDIO_CmdInitStructure);                          //写命令进SDIO命令寄存器

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

        /* 没发送CMD0,返回命令超时错误 */
        if (errorstatus != SD_OK)        //命令发送出错,返回
        {
                /*!< CMD Response TimeOut (wait for CMDSENT flag) */
                return(errorstatus);
        }

        /************************发送CMD8***************************************        */
        /************************发送CMD8***************************************        */
        /************************发送CMD8***************************************        */
        /*!< CMD8: SEND_IF_COND ----------------------------------------------------*/
        /*!< Send CMD8 to verify SD card interface operating condition */
        /*!< Argument: - : Reserved (shall be set to '0')
                   - : Supply Voltage (VHS) 0x1 (Range: 2.7-3.6 V)
                   - : Check Pattern (recommended 0xAA) */
        /*!< CMD Response: R7 */
        /* CMD8: SEND_IF_COND -------------------------------------------------------------*/
        /* 发送 CMD8 核实 SD 卡接口操作条件 */
        /* 参数: - : 保留('0')
                - : 供电电压(VHS) 0x1 (范围: 2.7-3.6 V)
                - : 检查类型 (推荐 0xAA) */
        /* 响应类型: 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();

        /* 如果已收到响应 R7,则符合SD卡2.0标准,并且可以设置HCS(HCS=1,表示主机支持SDHC) */
        if (errorstatus == SD_OK)                  //有响应则card遵循sd协议2.0版本
        {
                CardType = SDIO_STD_CAPACITY_SD_CARD_V2_0; /*!< SD Card 2.0 ,先把它定义会sdsc类型的卡*/ /* 标准容量SD卡2.0 */
                SDType = SD_HIGH_CAPACITY;        //这个变量用作acmd41的参数,用来询问是sdsc卡还是sdhc卡
        }
        else        //无响应,说明是1.x的或mmc的卡
        {
                /*!< CMD55 也就是PDF图中的ACMD41 0x41=d55*/
                /* CMD55: APP_CMD */
                /* 发送CMD55判断是不是SD卡 */
                /* 参数: - : RCA = 0x0000
                         - : 保留('0') */
                /* 响应类型: R1 */
                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: APP_CMD */
        /* 发送CMD55判断是不是SD卡 */
        /* 参数: - : RCA = 0x0000
                 - : 保留('0') */
        /* 响应类型: R1 */
        /*!< 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 */
        /* 如果 errorstatus 是命令响应超时,则是 MMC 卡 */
        /* 如果 errorstatus 是 SD_OK ,则是 SD 卡: SD卡1.x(不符合SD卡2.0标准)或SD 卡 2.0(不支持设置的电压范围(2.7~3.6V)) */
        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))
                {
                        /* CMD55: APP_CMD */
                        /* 必须在发送 ACMD41 之前发送 CMD55 通知卡,下面一条命令是应用特定命令 */
                        /* 参数: - : RCA = 0x0000
                                 - : 保留('0') */
                        /* 响应类型: R1 */
                        //因为下面要用到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
                        /* ACMD41: SD_SEND_OP_COND */
                        /* 发送 ACMD41 要求卡发送操作条件寄存器(OCR) */
                        /* 参数: - : 保留('0')
                                - : 主机容量支持(HCS) SDType = 0x40000000
                                - : VDD电压窗口 SD_VOLTAGE_WINDOW_SD = 0x00100000(3.2~3.3V) */
                        /* 响应类型: R3 */
                        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_RESP1寄存器中读取响应R3(OCR),并解析 */
                        /*若卡需求电压在SDIO的供电电压范围内,会自动上电并标志pwr_up位*/
                        response = SDIO_GetResponse(SDIO_RESP1);       //读取卡寄存器,卡状态
                        /* 判断busy位(OCR)是否为1(busy=1,表示ACMD41的初始化完成) */
                        validvoltage = (((response >> 31) == 1) ? 1 : 0);        //读取卡的ocr寄存器的pwr_up位,看是否已工作在正常电压
                        count++;                          //计算循环次数
                }

                /* 如果尝试了 SD_MAX_VOLT_TRIAL 次,初始化还没有完成,则VDD电压窗口(3.2~3.3V)是无效的 */
                if (count >= SD_MAX_VOLT_TRIAL)        //循环检测超过一定次数还没上电
                {
                        errorstatus = SD_INVALID_VOLTRANGE;           //SDIO不支持card的供电电压
                        return(errorstatus);
                }

                /*检查卡返回信息中的HCS位*/
                /* 判断CCS位(OCR)是否为1(CCS=1,表示卡是高容量SD卡) */
                if (response &= SD_HIGH_CAPACITY)//判断ocr中的ccs位 ,如果是sdsc卡则不执行下面的语句
                {
                        CardType = SDIO_HIGH_CAPACITY_SD_CARD;//把卡类型从初始化的sdsc型改为sdhc型
                }

        }/*!< else MMC Card *//* 否则是 MMC 卡 */

        return(errorstatus);
}

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 = SDIO_GetResponse(SDIO_RESP1);
                CID_Tab = SDIO_GetResponse(SDIO_RESP2);
                CID_Tab = SDIO_GetResponse(SDIO_RESP3);
                CID_Tab = 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 = SDIO_GetResponse(SDIO_RESP1);
                CSD_Tab = SDIO_GetResponse(SDIO_RESP2);
                CSD_Tab = SDIO_GetResponse(SDIO_RESP3);
                CSD_Tab = SDIO_GetResponse(SDIO_RESP4);
        }

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

        return(errorstatus);
}
阅读(1613) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~