历史上的今天

今天是: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: 裸机系统实现多任务轮转

推荐阅读

芯科技消息(文/雷明正),中国大陆福建晋华集成电路公司和台湾联华电子公司2日遭美方起诉共谋窃取美国半导体厂美光的商业机密。联电一早公告声明,表示美国政府起诉内容实际上与先前美光对公司提起民事诉讼所主张的内容相同。对于美国检察官办公室未事先通知联电,并未给予讨论事件始末机会表示遗憾。联电特别强调,针对此类指控,都将严正以对。晋华和...
载人无人机第一股就要来了。今日,亿航无人机正式向美国SEC(美国证券交易委员会)递交招股书,预备在美国纳斯达克上市,拟融资1亿美元。这家国内的明星无人机公司,终于赶在大疆之前走向IPO了。两年半净亏损2亿元,空中交通市场却“意外”爆发亿航在招股书中表示,“我们是一家自动飞行器(AAV)技术平台公司,通过自主开发的自动飞行器和相关商业解决方...
奥来德发布公告称,公司拟使用500万元超募资金在日本投资设立全资子公司“吉林OLED日本研究所株式会社”(公司名称最终以日本法务局核准为准),由该公司投资建设研发平台,实施“OLED印刷型发光材料研发项目”。公告显示,该项目主要是涉及OLED印刷型发光材料研发项目及产业化重点应用方向研究,逐步实现重点产品研发及关键技术的创新。项目开展共涉及成...
Cadence 助力新一代耳戴式设备、可穿戴设备和始终在线设备,延长电池寿命并改善用户体验新的 Tensilica HiFi 1 DSP 以超低能耗的更紧凑尺寸,提供更高的语音和音乐处理性能,以及最佳的神经网络能力中国上海,2021年10月29日—楷登电子(美国 Cadence 公司)今日宣布推出 Cadence® Tensilica® HiFi 1 DSP,该产品可为小型电池供电设备提供...

史海拾趣

问答坊 | AI 解惑

有趣的香蕉手机 太搞笑了

有趣的香蕉手机外壳: 以后如果你看到有人把手机放在耳朵边自言自语,而不是放在嘴里,那你也不要大惊小怪,因为那个人是在打手机。这款有趣的香蕉手机外壳有别于其它手机外壳,适合把一些长条形的手机放在里面,如果你是一个爱搞笑的人,那这就是 ...…

查看全部问答∨

希望各位能帮帮小弟我啊

现在学校要求做毕业论文&nbsp; 题目是模拟电子万能调试板&nbsp; 题目很大 但内容并不多&nbsp; 就是用个板子可以多次利用做电路 但是我真的不知道如何下手啊&nbsp; 尤其是论文更是头疼 谁能帮帮我给个方向也好啊 谢谢了啊 [ 本帖最后由 nish ...…

查看全部问答∨

请教:如何用51控制舵机360度连续旋转

大家好,我第一次到这学习,呵呵,目前小弟在学习控制舵机,基础的准备都做好了,但是实在查找不到关于360度连续旋转舵机的相关资料,具体如何用51控制舵机360度连续旋转小弟一直很困惑,请各位大虾给予指导,非常感谢!…

查看全部问答∨

超低价专业PCB打样 双面板200元/款(含飞针测试)

超低价专业PCB打样  双面板200元/款(含飞针测试) 一、单面板  工艺:喷锡/镀金  规格:10CMX10CM 单价 150元(10PCS)   交期:3-4天    加急1天 二、双面板  工艺:喷锡/镀金&n ...…

查看全部问答∨

各位大虾帮帮忙。怎么在Tornado下生成.syms文件啊?跟帖也给分。

.syms是内存映象文件。文档中只说可以使用 -mapped和-readnow用它。但是不知道怎么生成这个文件。 是在编译时在Makefile中加命令生成吗?还是通过代码或者一些选项?…

查看全部问答∨

无线模块zigbee

无线模块zigbee怎么用单片机C程序处理…

查看全部问答∨

需求游戏3D眼镜投影仪3D眼镜影院3D眼镜方案

本帖最后由 jameswangsynnex 于 2015-3-3 20:00 编辑 需求游戏3D眼镜投影仪3D眼镜影院3D眼镜方案,各种3D眼镜的解决方案,有意者请QQ联系:231628044 …

查看全部问答∨

求MSP430G2系列的硬件spi和I2C的模块化程序

哪位大哥大姐如果有的话请发我下&nbsp;&nbsp; 谢谢了&nbsp;…

查看全部问答∨

LED新政策鑫久盛照明科技新款LED吸顶灯

导读:新款LED吸顶灯 品牌:鑫久盛 芯片:台湾进口芯片灯体尺寸:灯盘直径:300mmLED功率:18W LED颜色:白/暖白 输入电压:AC110V~AC240V 频率范围:50/60Hz 功率因数:>0.9 显色指数:Ra>90 控制方式:恒流驱动模块  环境温度:-45℃ ...…

查看全部问答∨

分享购买TI产品的好方法

在项目设计中,因为我们都是直接负责采购器件,所以获得了一些宝贵的经验,现在和大家分享一些。 德州仪器的产品质量非常好,但是也存在一个缺点,那就是价格与其它厂家相比,就是贵一些,当你面对不同的需求时,就需要采取不同的策略。 1、需求 ...…

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

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

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

换一换 更多 相关热搜器件
随便看看

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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