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;
上一篇:stm32专题三十八:在SRAM中调试代码
下一篇:stm32实用篇2: 裸机系统实现多任务轮转
推荐阅读
史海拾趣
随着产品技术的不断提升,Alan Industries Inc.开始积极拓展市场。公司制定了详细的市场营销策略,通过线上线下的多渠道推广,成功将产品打入国内外市场。同时,公司还注重品牌建设,通过优质的产品和服务赢得了消费者的信任和口碑。随着品牌知名度的提升,公司的市场份额不断扩大,成为电子行业中的佼佼者。
在激烈的市场竞争中, Friedrich Lütze GmbH 深知品牌建设的重要性。公司不断加大品牌宣传力度,通过参加国内外知名展会、举办技术研讨会等方式,提升品牌知名度和影响力。同时,公司注重客户服务体验,建立了完善的售后服务体系,为客户提供及时、专业的技术支持和解决方案。这些举措有效提升了公司在市场上的竞争力和客户满意度。
随着电子行业的快速发展,e2v不断拓展其产品线,以满足市场的多样化需求。除了传统的图像传感器产品外,公司还开发了示波器数据转换器、高性能图像处理器等,广泛应用于科学、医学、工业、汽车和电信等领域。这些产品的推出,不仅拓展了e2v的市场空间,也为其带来了可观的经济效益。
随着国内市场的饱和,巨盛电子(Chesen)开始寻求国际化发展。公司制定了一系列国际化战略,包括拓展海外市场、建立海外生产基地、与国际品牌合作等。通过这些努力,巨盛电子成功进入了欧美、东南亚等市场,实现了品牌的全球化布局。同时,公司还积极与国际品牌合作,共同开发新产品,提高了品牌知名度和市场份额。
除了天然气业务外,E.ON还致力于电力市场的拓展。通过与其他电力公司的合作和并购,E.ON逐渐扩大了在欧洲电力市场的份额。特别是在2003年收购德国鲁尔天然气公司后,E.ON间接持有了俄天然气公司的股份,进一步巩固了其在欧洲能源市场的地位。
宏发公司的发展离不开自主研发和技术创新。1998年,宏发成立了精合公司,开启了自主研发自动化设备设计及制造的征程。此后,公司不断加大在技术研发上的投入,建立了博士后科研工作站和院士专家工作站,形成了从产品研发、模具制造、零件制造到自动化成品装配及在线检测的一体化全产业链。通过技术创新,宏发不仅提升了产品性能和质量,还成功打破了国外技术垄断,实现了高端继电器的自主研发和生产。
调试经验之---硬件篇 好多的新手在最初的硬件调试过程中,由于出现硬件故障,导致实验失败。所以所有软件实验也将无法进行下去。本人将自己从事硬件调试的一些心得写给大家,希望大家能够缩短硬件调试时间,提高实验的成功率。 第一步:在焊接 ...… 查看全部问答∨ |
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 … 查看全部问答∨ |
|
我把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 ...… 查看全部问答∨ |