历史上的今天

今天是:2024年11月02日(星期六)

2021年11月02日 | stm32专题三十九:SDIO

发布者:TranquilWhisper 来源: eefocus关键字:stm32  SDIO  接口设备 手机看文章 扫描二维码
随时随地手机看文章

SDIO协议简介


SDIO简介

SDIO接口设备

目前 SD 协议提供的 SD 卡规范版本最新是 4.01 版本,但 STM32F10x 系列控制器只支持 SD 卡规范版本 2.0,即只支持标准容量 SD 和高容量SDHC 标准卡,不支持超大容量 SDXC 标准卡,所以可以支持的最高卡容量是 32GB。


如下图所示(左边64GB为超大容量 SDXC,右边32GB为大容量 SDHC):

SD卡物理结构


一张SD卡包括有存储单元、存储单元接口、电源检测、卡及接口控制器和接口驱动器5个部分。


SD 卡总共有 8 个寄存器,用于设定或表示 SD 卡信息。这些寄存器只能通过对应的命令访问,对 SD 卡进行控制操作并不是像操作控制器 GPIO 相关寄存器那样一次读写一个寄存器的,它是通过命令来控制。 SDIO 定义了 64 个命令,每个命令都有特殊意义,可以实现某一特定功能, SD 卡接收到命令后,根据命令要求对 SD 卡内部寄存器进行修改,程序控制中只需要发送组合命令就可以实现 SD 卡的控制以及读写操作。


SD卡寄存器描述(很复杂,只需要关注常用的几个就可以):

SD卡总线结构:

引脚说明和操作时序:

SDIO和SPI的区别:


SDIO明显时钟频率更快,而且SDIO拥有4根数据线,同等频率下通信速度能提高4倍。


总线协议


SD 总线通信是基于命令和数据传输的。通讯由一个起始位(0),由一个停止位(1)终止。 SD 通信一般是主机发送一个命令,从设备在接收到命令后作出响应,如有需要会有数据传输参与。

stm32 多数据块写入


SD 数据传输支持单块和多块读写,它们分别对应不同的操作命令,多块写入还需要使用命令来停止整个写入操作。数据写入前需要检测 SD 卡忙状态,因为 SD 卡在接收到数据后编程到存储区过程需要一定操作时间, SD卡忙状态通过把 D0 线拉低表示。


SD卡操作时序:


先发送第1字节,然后发送第2字节,以此类推...


在发送每字节时,D0 ~ D3先发送高 4 位,再发送低 4 位。

宽位数据包格式(专用于SSR寄存器):

SDIO命令及响应


命令


SD 命令由主机发出,以广播命令和寻址命令为例,广播命令是针对与 SD 主机总线连接的所有从设备发送的,寻址命令是指定某个地址设备进行命令传输。

命令格式

命令参数意义:

命令类型:

常规命令(64条)和特定应用命令(厂商自定义的命令)

响应:


响应由 SD 卡向主机发出,部分命令要求 SD 卡作出响应,这些响应多用于反馈SD卡的状态。 SDIO 总共有7个响应类型(代号: R1~R7),其中 SD 卡没有 R4、 R5 类型响应。特定的命令对应有特定的响应类型,比如当主机发送 CMD3 命令时,可以得到响应 R6。与命令一样,SD卡的响应也是通过 CMD 线连续传输的。根据响应内容大小可以分为短响应和长响应。短响应是 48bit 长度,只有 R2 类型是长响应,其长度为 136bit。


命令描述:

如上所示,CMD0命令,类型为无响应广播命令,复位所有卡到空闲状态;


CMD2命令,位带响应广播命令,通知所有卡通过CMD线返回CID值,响应类型为R2;

可以看到,发送命令CMD2,返回R2类型,实际上就是返回CID或者CSD寄存器的值。

当发送CMD2命令后,会返回CID值(128位),这个128位标识符太长,可以通过CMD3命令,通知所有卡发布新RCA(16位)来区分系统上有几张卡(总线上的卡通信后会知道总线上有多少张卡,然后协调并自动分配每张卡的RCA标识符),返回类型为R6,如下所示:

R6类型会返回RCA寄存器值,并返回部分卡状态寄存器值(32位寄存器);

R1类型会返回卡状态寄存器的全部位。

CMD8命令,发送SD卡的接口条件,如果SD卡认为这是正常的电压,则返回R7类型;

数据读取操作(每次读取前,要先设置块的长度):

可以看到,SD卡的数据宽度为32位。注意,这里有区分SDSC(标准容量SD卡)和SDHC(大容量SD卡):


SDSC:地址以字节位单位,如 0 ~ 511地址位第1数据块,512 ~ 1023地址为第2数据块;


SDHC:地址以块为单位,1地址就是第1数据块,2地址就是第2数据块;


块写入操作类似:

块擦除操作(需要使用3个命令):


块擦除需要指定数据起始地址、结束地址、然后开始进行擦除操作;

当有多个SD卡存在时,SD卡如何得知是对哪一张SD卡进行操作?

实际上,要通过CMD7命令,选中一张SD卡。


SD卡操作模式


SD卡操作模式

模式状态如下:

卡识别模式状态机:

数据传输模式状态机:

stm32 SDIO接口


SDIO功能框图:

SDIO适配器

控制单元:


命令路径:


下面是 stm32 中文参考手册中,相关的寄存器描述:


SDIO参数寄存器:

SDIO命令寄存器:

SDIO响应1...4寄存器(根据短响应or长响应不同来区分)

命令路径状态机:

数据路径:

数据路径状态机:

数据 FIFO

对应的寄存器描述:


SDIO状态寄存器 SDIO_STA


SDIO数据控制寄存器(设置数据块大小):

stm32 SDIO结构体


stm32标准库对 SDIO 外设建立了 3 个结构体,分别为 SDIO 初始化结构体 SDIO_InitTypeDef ,SDIO 命令初始化结构体 SDIO_CmdInitTypeDef 和 SDIO 数据初始化结构体 SDIO_DataInitTypeDef 。

1 SDIO 初始化结构体 SDIO_InitTypeDef

2 SDIO 命令初始化结构体 SDIO_CmdInitTypeDef


3 SDIO 数据初始化结构体 SDIO_DataInitTypeDef

SDIO 驱动分析


1 SD卡 原理图(使用到stm32的6个引脚)

标准库中,实际上给出了 SDIO 的示例,如下所示:

其中包含了 main.c       stm32f10x_ic.h 等文件

在 Utilities 文件夹下,提供了评估板的驱动文件(提供了SD卡驱动函数):

以及评估板的BSP支持文件(外设GPIO初始化):

移植过程:


说白了,其实就是把 stm32_eval_sdio_sd.c 文件复制过来,但是,stm32_eval_sdio_sd.c 中会初始化一些外设,如 SDIO_GPIO 初始化,这些外设的初始化和评估板有关,在板级支持包 stm32f10e_eval.c 中实现,因此要把这些初始化程序也复制进来。


如下图所示(移植好的工程和原始工程对比):


移植过程,就是不断的编译,一开始肯定会各种报错(缺少初始化函数,位于stm32f10e_eval.c中),我们去stm32f10e_eval.c这个文件中找到对应的函数,移植添加进来。


注意,SDIO使用了中断,因此我们也需要将中断服务函数移植进来。

这就是移植的过程,测试代码一样在 example 中有提供,同样的可以移植。


驱动分析


下面是函数的执行过程:


if ((Status = SD_Init()) != SD_OK)

{

    LED_RED;

    printf("SD卡初始化失败,请确保SD卡已正确接入开发板,或换一张SD卡测试!n");

}

else

{

    printf("SD卡初始化成功!n");

}

重点就是 SD_Init() 这个SD卡初始化函数:


/**

 * 函数名:SD_Init

 * 描述  :初始化SD卡,使卡处于就绪状态(准备传输数据)

 * 输入  :无

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

 *         成功时则为 SD_OK

 * 调用  :外部调用

 */

SD_Error SD_Init(void)

{

    /*重置SD_Error状态*/

    SD_Error errorstatus = SD_OK;

 

    NVIC_Configuration();

 

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

    }

 

    /* 配置SDIO外设

   * 上电识别,卡初始化都完成后,进入数据传输模式,提高读写速度

   */

 

    /* 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;

 

    /* Bypass模式使能的话,SDIO_CK不经过SDIO_ClockDiv分频 */

    SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable;

 

    /* 若开启此功能,在总线空闲时关闭sd_clk时钟 */

    SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable;

 

    /* 暂时配置成1bit模式 */

    SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_1b;

 

    /* 硬件流,若开启,在FIFO不能进行发送和接收数据时,数据传输暂停 */

    SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable;

 

    SDIO_Init(&SDIO_InitStructure);

 

    if (errorstatus == SD_OK)

    {

        /* 用来读取csd/cid寄存器 */

        errorstatus = SD_GetCardInfo(&SDCardInfo);

    }

 

    if (errorstatus == SD_OK)

    {

        /* 通过cmd7  ,rca选择要操作的卡 */

        errorstatus = SD_SelectDeselect((uint32_t)(SDCardInfo.RCA << 16));

    }

 

    if (errorstatus == SD_OK)

    {

        /* 最后为了提高读写,开启4bits模式 */

        errorstatus = SD_EnableWideBusOperation(SDIO_BusWide_4b);

    }

 

    return (errorstatus);

}

1 基本的 NVIC配置(SDIO中断源,中断分组、中断优先级,使能NVIC);


2 SDIO引脚初始化(注意,这里还要使能SDIO AHB总线时钟 + DMA);


/*

 * 函数名:GPIO_Configuration

 * 描述  :初始化SDIO用到的引脚,开启时钟。

 * 输入  :无

 * 输出  :无

 * 调用  :内部调用

 */

static void GPIO_Configuration(void)

{

    GPIO_InitTypeDef GPIO_InitStructure;

 

    /*!< GPIOC and GPIOD Periph clock enable */

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD, ENABLE);

 

    /*!< Configure PC.08, PC.09, PC.10, PC.11, PC.12 pin: D0, D1, D2, D3, CLK pin */

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12;

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

    GPIO_Init(GPIOC, &GPIO_InitStructure);

 

    /*!< Configure PD.02 CMD line */

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;

    GPIO_Init(GPIOD, &GPIO_InitStructure);

 

    /*!< Enable the SDIO AHB Clock */

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_SDIO, ENABLE);

 

    /*!< Enable the DMA2 Clock */

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);

}

3 复位 SDIO 寄存器,这个实际上应该放在最前面比较好,但这里因为没有配置寄存器,在这里执行也可以;


/*对SDIO的所有寄存器进行复位*/

SDIO_DeInit();

4 上电识别SD卡;


/* 上电并进行卡识别流程,确认卡的操作电压 */

errorstatus = SD_PowerON();

这里,主要就是 SD_PowerON() 这个函数,实现了SD卡整个的识别过程;


a 初始化时钟;


/******************************************************************************/

/* 上电初始化 

   * 配置SDIO的外设

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

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

   */

/* HCLK = 72MHz, SDIOCLK = 72MHz, SDIO_CK = HCLK/(178 + 2) = 400 KHz */

SDIO_InitStructure.SDIO_ClockDiv = SDIO_INIT_CLK_DIV;

SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising;

/* 不使用bypass模式,直接用HCLK进行分频得到SDIO_CK */

SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable;

/* 空闲时不关闭时钟电源 */

SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable;

/* 初始化的时候暂时先把数据线配置成1根 */

SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_1b;

/* 失能硬件流控制 */

SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable;

SDIO_Init(&SDIO_InitStructure);

/* 开启SDIO外设的电源 */

SDIO_SetPowerState(SDIO_PowerState_ON);

/* 使能 SDIO 时钟 */

SDIO_ClockCmd(ENABLE);

b 发送CMD0命令,使SD卡进入空闲状态;


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

  * CMD0: GO_IDLE_STATE(复位所以SD卡进入空闲状态) 

  * 没有响应  

  */

SDIO_CmdInitStructure.SDIO_Argument = 0x0;

SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_GO_IDLE_STATE;

 

/* 没有响应 */

SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_No;

/* 关闭等待中断 */

SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;

/* 则CPSM在开始发送命令之前等待数据传输结束 */

SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;

SDIO_SendCommand(&SDIO_CmdInitStructure);

 

/* 检测是否正确接收到cmd0 */

errorstatus = CmdError();

 

/* 命令发送出错,返回 */

if (errorstatus != SD_OK)

{

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

    return (errorstatus);

}

 

/**

 * 作用  :检查发送命令超时的函数

 * 描述  :对CMD0命令的检查。

 * 输出  :SD错误类型

 */

static SD_Error CmdError(void)

{

    SD_Error errorstatus = SD_OK;

    uint32_t timeout;

    timeout = SDIO_CMD0TIMEOUT; /*!< 10000 */

 

    /*检查命令是否已发送*/

    while ((timeout > 0) && (SDIO_GetFlagStatus(SDIO_FLAG_CMDSENT) == RESET))

    {

        timeout--;

    }

    if (timeout == 0)

    {

        errorstatus = SD_CMD_RSP_TIMEOUT;   // 返回超时

        return (errorstatus);

    }

    /*!< Clear all the static flags */

    SDIO_ClearFlag(SDIO_STATIC_FLAGS); //清除静态标志位

 

    return (errorstatus);       // 返回SD_OK

}

c 发送 CMD8 命令,判断SD卡是否为2.0协议版本;


d 循环发送 SDIO 支持的电压范围,发送一定次数。如果一直没有应答,说明供电电压不支持,如果没有超出循环次数,说明 SD 卡初始化正常,根据响应值判断SD卡表标准容量还是高容量类型;


5 初始化所有卡(实际上stm32只支持一个SDIO设备);


发送 CMD2 命令,获取CID号并存储;然后发送 CMD3 命令,获取 RCA 地址;


if (SDIO_SECURE_DIGITAL_IO_CARD != CardType)

{

    /* Send CMD2 ALL_SEND_CID 

* 响应:R2,对应CID寄存器

*/

    SDIO_CmdInitStructure.SDIO_Argument = 0x0;

[1] [2]
关键字:stm32  SDIO  接口设备 引用地址:stm32专题三十九:SDIO

上一篇:stm32专题三十八:在SRAM中调试代码
下一篇:stm32实用篇2: 裸机系统实现多任务轮转

推荐阅读

意法半导体的 STM32L412和STM32L422微控制器(MCU)以功能专一和封装紧凑为特色,为注重成本预算的消费类、工业和医疗应用带来超低功耗技术和优异的处理性能。 新微控制器为设计人员提供了更大的发挥空间,突破各种设计资源的制约,不论是内核性能和能耗限制,还是空间尺寸和物料清单成本限制。通过整合采用意法半导体的FlexPowerControl(FPC)技术经济...
(文章来源:IT168) 近年来,随着服务机器人产业技术的不断进步,产品的不断迭代,以及家具生活方式的逐渐智能化,我国扫地机器人销量以惊人的速度增长。捷孚凯零售监测的数据显示,近五年我国扫地机器人零售量复合增速已经超过50%。截至2017年12月,我国扫地机器人零售量市场规模已达332.5万台,零售额约44亿元。然而, 2019年上半年,随着人口红利...
11 月 2 日讯,近日,蔚来创办人李斌在 EP CLUB(蔚来汽车知名品牌最大端客户俱乐部队)上表示,蔚来目前正在研发 150 度电池包,NEDC 续航里程将超过 900 公里。 据悉,蔚来目前共有 70kWh、84kWh 以及 100kWh 三个电池包可供消费者选择,而 100kWh 的电池包将于 2020 年第四季度开始提供。根据猜测,150 度电池包将于前三者的尺寸一致...
到了冬天,傍晚的能见度和光线条件不佳,驾驶员需要更加谨慎。然而,对于欧宝汽车的用户来说,这不是问题。(图片来源:Stellantis)据外媒报道,自适应、无眩光Intelli-Lux LED®技术适用于这家德国汽车制造商的所有车型,从小型畅销车欧宝Corsa,到引人注目的欧宝Mokka和旗舰车型欧宝Insignia。此外,全新欧宝Astra和新版欧宝Grandland,还可选配最新一...

史海拾趣

问答坊 | AI 解惑

Keil C51 UV2调试命令(中文版) .pdf

传个Keil C51 UV2调试命令(中文版)资料,希望大家喜欢。…

查看全部问答∨

最小系统6

功能: 将LCD填充为黑色 ******************************************************************************/ void lcd_fill(void) { uchar i,j; uchar byte[1] = {0xFF}; for(i=0;i<=127;i++) { for(j=0;j<=7;j++) { l ...…

查看全部问答∨

调试经验之---硬件篇

调试经验之---硬件篇 好多的新手在最初的硬件调试过程中,由于出现硬件故障,导致实验失败。所以所有软件实验也将无法进行下去。本人将自己从事硬件调试的一些心得写给大家,希望大家能够缩短硬件调试时间,提高实验的成功率。 第一步:在焊接 ...…

查看全部问答∨

FPGA综合仿真错误

Error: Design requires 1087 I/O resources -- too many to fit in 314 available in the selected device or any device in the device family Error: Cannot find device that meets Compiler settings specifications…

查看全部问答∨

北京旋极 高薪急招 高级税控软件工程师

北京旋极 高薪急招 高级税控软件工程师 职责: 负责税控项目系统软件的技术研发。 要求: 1、有完整税控项目研发的经验。 2、精通税控系统的各项技术。 3、诚信健康 联系:hr@watertek.com …

查看全部问答∨

选型问题求助

最近正在和客户谈灌装设备,主传动和灌装都要求用伺服电机,由于设备有无杯不灌装的功能,所以每个灌装头都要配备一个伺服,这样加上主传动,一共要用到9个伺服,客户要求PLC用西门子的,请问如果选型的话用300的好还是400的好?…

查看全部问答∨

STM8S用TIME1来触发ADC1转换

我把STM8 固件库中的TIME1触发 ADC2 的列程,翻译成 TIME1触发ADC1,可失败了!请大家帮我找找原因! void main(void) {     /*  Init GPIO for ADC2 */     GPIO_Init(GPIOB, GPIO_PIN_3, GPIO_MODE_IN_FL_ ...…

查看全部问答∨

【我给xilinx资源中心做贡献】Xilinx Spartan 3E Starter Kit平台控制1602液晶程序

Xilinx Spartan 3E Starter Kit平台控制1602液晶程序Xilinx Spartan 3E Starter Kit平台上LCD1602与板载Intel Flash Memory公用四根数据线,读写命令和数据时分别写高四位和低四位 源码: library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.S ...…

查看全部问答∨

2011年全国电子设计竞赛培训资料

本帖最后由 paulhyde 于 2014-9-15 03:35 编辑 希望可以帮助到今年参加电赛的同学们    …

查看全部问答∨

求一篇相关外文

毕业设计的需要,向大神们要一片与430 1122相关的英文论文(能带翻译的就带翻译) 最好是论文,因为够 简短,翻译起来没有那么吃力. 谢谢…

查看全部问答∨
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved