SDIO(Secure Digital Input and Output)中文名称:安全数字输入输出,SDIO在SD标准上定义了一种外设接口。SDIO主要有两类应用——可移动和不可移动。可移动设备作为Palm和Windows Mobile的扩展设备,用来增加蓝牙、照相机、GPS和802.11b功能。不可移动设备遵循相同的电气标准,但不要求符合物理标准。某些手机内包含通过SDIO连接CPU的802.11芯片。此举将“珍贵”的I/ O管脚资源用于更重要的功能。
蓝牙、照相机、GPS和802.11b设备有专为它们定义的应用规范。这些应用规范与为PCI和USB设备定义的类规范很相像。它们允许任何宿主设备与任意外设“通话”,只要它们都支持应用规范。
SDIO和SD卡规范间的一个重要区别是增加了低速标准。SDIO卡只需要SPI和1位SD传输模式。低速卡的目标应用是以最小的硬件开支支持低速I/ O能力。低速卡支持类似调制解调器、条码扫描仪和GPS接受器等应用。对“组合”卡(存储器+ SDIO)而言,全速和4位操作对卡内存储器和SDIO部分都是强制要求的。
MM32F3270系列控制器支持SDIO接口,本文在接下来会对MM32F3270的SDIO进行介绍,并通过实验演示SDIO驱动SD卡。
01 、SDIO 简介
SD/MMC/SDIO 控制器是 AMBA AHB 从外设,用于控制外部 SD/MMC/SDIO 卡,并支持 DSP/MCU的读/写访问。它作为主机与连接的 SD/MMC/SDIO 卡进行通信。所述控制器是基于 AMBA HCLK 域的完全同步设计。
1.1、SDIO 功能框图
1) AHB 从模式接口:为 32 位 AHB 总线提供接口。
2) FIFO 控制:产生握手信号到 DMA 硬件接口,并控制对外部数据 FIFO(128x32)的读/写访问。
3) 总线接口单元:包括控制寄存器和命令缓冲单元。
4) 多重块控制:控制多块数据的读写。
5) 时钟控制:通用时钟基于寄存器中定义的分频值。
6) 命令通路:从总线接口单元或 irq 响应中加载新的命令,然后发送命令,并接收响应 crc7 检查和 8 个空时钟。
7) 数据通路:发送和接收数据,用 crc16 检查。
8) 缓冲接口:控制对数据 FIFO 的读写控制信号。
1.2、SDIO 功能描述
1) 完全兼容 SD 记忆卡规格 1.0
2) 完全兼容 SD 存储卡规格 1.1(高速)
3) 完全兼容 SD 记忆卡规格 2.0(SDHC)
4) 完全兼容 MMC 系统规格 2.0~4.2
5) 完全兼容 SDIO 存储卡规格 1.1.0
6) 标准的 MMC 模式接口支持
7) 可编程时钟速率
8) 自动命令/响应 CRC 生成/检查
9) 自动数据 CRC 生成/检查
10) 可编程超时检测
11) AMBA 2.0 32 位 AHB 接口
12) 用于 AHB 数据访问的总共外部 128*32 数据 FIFO
13) 32 位 DMA 硬件接口,用于更快的 DMA 访问
14) DMA 接口可以配置为启用/禁用
15) DMA 请求是可配置的
16) 组合中断输出
02 、SD 存储卡
2.1、Read-Write 属性
根据 Read-Write 属性不同可分为两种类型的 SD 存储卡:
1) 可读可写(RW)卡(FLASH, OTP, MTP)。这些卡通常作为空白媒介来售卖,用于海量终端用户的视频、音频或数字图像记录的存储。
2) 只读存储卡(ROM)。这些卡是用固定的数据内容生产出来的,它们通常用作软件、音频、视频等的传播媒介。
2.2、电源电压
根据工作电源电压不同可分为两种类型的 SD 存储卡:
1) 高电压 SD 存储卡,可以在 2.7-3.6V 的电压范围内工作;
2) 双电压 SD 存储卡,可以在 1.6-3.6V 的电压范围内工作。
2.3、卡容量
根据卡容量不同可分为两种类型的 SD 存储卡:
1) 标准容量的 SD 存储卡支持最大 2G 字节的容量。所有版本的物理规格都定义了标准容量 SD 存储卡;
2) 高容量 SD 存储卡支持超过 2G 字节的容量,此版本规范限制容量高达 32GB。高容量 SD 存储卡是物理层规范版本 2.00 中新定义的。
2.4、传输速度
定义了四种速度等级,表示 SD 卡的最低性能:
1) Class0:这类卡不指定性能;
2) Class2:速度不低于 2MB/s;
3) Class4:速度不低于 4MB/s;
4) Class6:速度不低于 6MB/s;
大容量 SD 存储卡应支持速度等级规范,性能大于或等于 2 级。
2.5、总线拓扑
SD 卡系统定义了两种通信协议:SD 和 SPI。主机系统可以选择任意一种。当收到 reset 命令的时候, SD 卡通过主机的信息来决定使用何种模式,并且之后的通讯都会使用相同模式。不推荐多卡槽用共同的总线信号。一个单独的 SD 总线应该连接一个单独的 SD 卡。SD 总线包含下面的信号:
1) CLK:时钟信号;
2) CMD:双向命令/响应信号;
3) DAT0-DAT3:双向数据信号;
4) Vdd, Vss1, Vss2:电源和地信号。
2.6、总线协议
SD 总线通信:
1) Command:命令是一次操作开始的令牌,从主机发送到一个卡片(编址命令)或者连接到主机的所有卡片(广播命令)。命令在 CMD 线上连续传输。
2) Response:响应是从已寻址的卡或从所有连接上的卡发送到主机的令牌,作为对先前接收到指令的应答。响应在 CMD 线上连续传输。
3) Data:数据可以通过 DATA 线双向传输。
卡片寻址通过使用会话地址来实现,会话地址会在初始化阶段分配给卡。SD 总线上的基本交互是命令/响应交互。这种总线交互直接在命令或者响应的结构里面传输他们的信息。此外,某些操作还有数据令牌。SD 卡发送或接收的数据在块(block)中完成。数据块以 CRC 位来保证传输成功。目前有单块和多块操作。
注:多块操作模式在快速写操作时更好一点。多块传输在 CMD 线上产生 stop 命令时结束。主机端可以配置数据传输是单线还是多线。
03、卡的初始化以及识别过程
初始化进程以命令 ACMD41 作为开始,通过设置工作条件和 OCR 来进行。HCS(HighCapacitySupport)位为 1 表示主机支持高容量 SD 卡。卡通过 OCR 的 busy 位来通知主机 ACMD41 的初始化完成了。busy 位为 0表示卡仍然在初始化;为 1 表示已经完成初始化。主机会重复发送 ACMD41,直到 busy 位被置 1。卡片旨在第一个 ACMD41 的命令时,检查工作条件和 OCR 里面的 HCS 位。当重复 ACMD41 的时候,除了 CMD0,主机不再发其他命令。接着主机会发送命令 ALL_SEND_CID(CMD2),来获得卡的 CID 号。未识别的卡(处于Ready 状态的)发送自己的 CID 作为响应。当卡发送 CID 后,进入卡识别(Identification)状态。之后主机发送 SEND_RELATIVE_ADDR(CMD3)命令要求卡发布新的相对地址(RCA),一旦收到 RCA,卡就会变为等待(Stand-by)状态。主机会重复识别进程,为系统中每个卡循环发送 CMD2 和 CMD3。对于 SDI/O 卡而言,总线被激活后 SDIO 卡主机先发送 IO_SEND_OP_COND(CMD5)命令,得到的响应是卡的工作条件寄存器的内容,之后再同上发送 CMD3 命令,执行后续操作。
04、MM32F3270 SDIO驱动SD卡
MM32的SDIO支持SD/MMC/SDIO卡,其中SDIO卡与SD存储卡是有区别的。SDIO卡实际上就是利用SDIO接口的一些模块,插入SD的插槽中,扩展设备的功能,如:SDIO wifi, SDIO CMOS相机等。SD存储卡就是平时常见的用于存储数据的卡。
本实验中使用的Micro SD卡属于SDSC(标准容量,最大2G)卡。介绍卡的种类是因为SD协议中的命令也支持这三种类型的卡,因此对MM32中的SDIO接口进行初始化后,上电后就要对接入的卡进行检测、分类,这个过程是通过向卡发送一系列不同的命令,根据卡不同的响应来进行分类。
本实验使用MM32F3270的SDIO对SD卡进行读写测试,首先填充一个块大小的存储器,通过写入操作把数据写入到 SD卡内,然后通过读取操作读取数据到另外的存储器,然后再对比存储器内容,判断读写操作是否正确。
实现的大概流程包括:初始化SDIO 外设以及GPIO,配置 SDIO 基本通信环境进入卡识别模式,通过命令处理后获取卡信息及状态。如果是SD卡正常则进行数据传输,接下来就可以进行读、写以及擦除操作,否则打印SD卡错误信息,不再进行后续操作。
硬件设计
实验使用MB-039开发板,主控芯片为MM32F3277G9P,如图是MB-039开发板的SDIO/TF卡接口部分,完整原理图可以通过官网下载。
各个信号引脚对应如下:
程序设计
根据 SD 卡识别过程和数据传输过程理解 SD 卡驱动函数代码。这部分代码内容也较多,在本文中只对部分核心函数介绍其功能,详细代码可到灵动官网下载参考。
SPIO配置初始化
void SDIO_ConfigInit(void) { SDIO_InitTypeDef SDIO_InitStruct; SDIO_PIN_GPIO_Config(); SDIO_Detect_Pin_Config(); RCC_AHBPeriphClockCmd(RCC_AHBENR_SDIO, ENABLE); SDIO_DeInit(); RCC_AHBPeriphClockCmd(RCC_AHBENR_SDIO, DISABLE); RCC_AHBPeriphClockCmd(RCC_AHBENR_SDIO, ENABLE); SDIO_ClockSet(0x2F); SDIO_StructInit(&SDIO_InitStruct); SDIO_InitStruct.SDIO_OPMSel = SDIO_MMC_CTRL_OPMSel; SDIO_InitStruct.SDIO_SelPTSM = SDIO_MMC_CTRL_SelSM; SDIO_InitStruct.SDIO_DATWT = SDIO_MMC_CTRL_DATWT; SDIO_Init(&SDIO_InitStruct); SDIO_CRCConfig(SDIO_MMC_CRCCTL_CMD_CRCEN | SDIO_MMC_CRCCTL_DAT_CRCEN, ENABLE); } void show_sdcard_info(void) { switch(SDCardInfo.CardType) { case SDIO_STD_CAPACITY_SD_CARD_V1_1: printf("Card Type:SDSC V1.1\r\n"); break; case SDIO_STD_CAPACITY_SD_CARD_V2_0: printf("Card Type:SDSC V2.0\r\n"); break; case SDIO_HIGH_CAPACITY_SD_CARD: printf("Card Type:SDHC V2.0\r\n"); break; case SDIO_MULTIMEDIA_CARD: printf("Card Type:MMC Card\r\n"); break; } printf("Card ManufacturerID:%d\r\n", SDCardInfo.SD_cid.ManufacturerID); //The manufacturer ID printf("Card RCA:%d\r\n", SDCardInfo.RCA); //Card relative address printf("Card Capacity:%d MB\r\n", (u32)(SDCardInfo.CardCapacity >> 20)); printf("Card BlockSize:%d\r\n\r\n", SDCardInfo.CardBlockSize); }
SD卡的初始化主要进行卡识别和卡状态获取,定义SD_Init()如下:
SD_Error SD_Init(void) { u32 clk; RCC_ClocksTypeDef bclk; u32 targetFreq; __IO SD_Error errorstatus = SD_OK; u8 clkdiv = 0; INTX_DISABLE(); errorstatus = SD_PowerON(); //SD Power On if (errorstatus != SD_OK) { return errorstatus; } errorstatus = SD_InitializeCards(); //Initialize SD Card if (errorstatus != SD_OK) { return errorstatus; } errorstatus = SD_GetCardInfo(&SDCardInfo); //Get card information if (errorstatus != SD_OK) { return errorstatus; } errorstatus = SD_SelectDeselect((u32)(SDCardInfo.RCA << 16)); //Select the SD card if (errorstatus != SD_OK) { return errorstatus; } errorstatus = SD_EnableWideBusOperation(1); //4 bit width, if it is an MMC card, you cannot use 4 bit mode if ((errorstatus != SD_OK)) { if( (SDIO_MULTIMEDIA_CARD == CardType)) { if (SDCardInfo.CardType == SDIO_STD_CAPACITY_SD_CARD_V1_1 || SDCardInfo.CardType == SDIO_STD_CAPACITY_SD_CARD_V2_0) { clkdiv = 0; //V1.1/V2.0 card with the maximum setting of 48/4=12Mhz } else { clkdiv = 1; //For other cards such as SDHC, the maximum setting is 48/2=24Mhz } if(clkdiv != 0) { targetFreq = 24000000; } else { targetFreq = 12000000; } RCC_GetClocksFreq(&bclk); clk = (bclk.HCLK_Frequency / 2 / 2 / targetFreq - 1); SDIO_ClockSet(clk); } else { __NOP(); } } else { if (SDCardInfo.CardType == SDIO_STD_CAPACITY_SD_CARD_V1_1 || SDCardInfo.CardType == SDIO_STD_CAPACITY_SD_CARD_V2_0) { clkdiv = 0; } else { clkdiv = 1; } if(clkdiv != 0) { targetFreq = 24000000; } else { targetFreq = 12000000; } RCC_GetClocksFreq(&bclk); clk = (bclk.HCLK_Frequency / 2 / 2 / targetFreq - 1); SDIO_ClockSet(clk); } INTX_ENABLE(); return errorstatus; }
1) SD_PowerON()函数用于查询卡的工作电压和时钟控制配置,并返回 SD_Error 类型错误,该函数是整个 SD 识别的关键函数。
2) SD_InitializeCards()函数初始化SD卡,并将其置入就绪状态;
3) SD_GetCardInfo()函数获取SD卡的信息;
4) SD_SelectDeselect()选择SD卡,发送CMD7命令,选择具有相对地址(RCA)的卡作为ADDR;
5) SD_EnableWideBusOperation()配置SDIO数据宽度;
6) SDIO_ClockSet()配置SDIO的时钟频率。
获取SD卡信息及数据的函数定义如下:
void read_sd_card_info(void) { u16 i = 5; SD_Error result; u32 sd_size; SDIO_ConfigInit(); printf("SDCARD TEST\r\n"); while(1) { result = SD_Init(); if(result == SD_OK) { break; } printf("SD Card Error!\r\n"); DELAY_Ms(1); } show_sdcard_info(); i = 5; while(i--) { if(SD_ReadDisk(&vbuf[0], 0, 1) == 0) { printf("UART Sending Data...\r\n"); printf("SECTOR 0 DATA:\r\n"); for(sd_size = 0; sd_size < 512; sd_size++) { printf("%02x ", vbuf[sd_size]); } printf("\r\nDATA ENDED\r\n"); printf("UART Send Data Over!\r\n"); } DELAY_Ms(50); } }
实验演示
在MB-039开发板的SDIO/TF卡槽插入SD卡,运行程序,串口调试助手显示如下:
如果SD卡可用,串口调试助手会打印SD卡的信息,包括卡类型、生产ID、RCA、容量和块大小,接着打印扇区0的数据,会连续打印5次该部分内容。
本次实验的例程可以通过MindMotion的官网下载MM32F3270 lib_Samples:
https://www.mindmotion.com.cn/products/mm32mcu/mm32f/mm32f_mainstream/mm32f3270/
工程路径如下:
~MM32F327x_Samples\ LibSamples\SDIO\SDIO_ReadSDCardInfo
可以看到详细的样例与功能操作。
来源:灵动MM32MCU
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。