在介绍SDIO接口之前先了解一下MMC、SD卡、SD标准等背景知识。
MMC(Multi Media Card):即多媒体卡,它是一种非易失性存储器件,体积小巧,容量大,耗电量低,传输速度快,主要应用于消费类电子产品中;
SD(Secure Digital Memory Card):即安全数码卡,它是在MMC卡的基础上发展而来,并增加两个主要特色:SD卡增加了数据的安全访问,可以设定对存储数据的访问权限,防止数据被他人复制;SD相比2.11版的MMC卡传输速度更快;SD卡向前兼容MMC卡,所有支持SD卡的设备也支持MMC卡;
SD 标准,即针对SD卡的标准化指定的对应标准,使制造商能够提供高性能产品,以增强每天听音乐、录制视频、拍照、保存数据和使用手机的数百万人的体验。作为行业标准,SD 标准被用于便携式存储行业的多个细分市场,包括手机、数码相机、MP3 播放器、个人电脑、平板电脑、打印机、汽车导航系统、电子书和许多其他消费电子设备。
SDIO
SDIO是在SD标准上定义的一种外设接口,和SD可规范的唯一区别是增加了低速标准。
SDIO卡只需要使用SPI或1位SD传输模式支持低速模式,低速卡的应用目标是以最小的硬件开销支持低速IO能力,低速卡支持类似调制解调器、条码扫描仪和GPS接收器等应用。
目前常见的SDIO外围有:SD卡、多媒体卡(MMC卡),TF卡等等,另外随着SDIO硬件设备的扩充SDIO总线的外围能够支持更多的SDIO设备比如bluetooth,wifi,GPS,camera sensor等,它们的识别过程跟SD卡类似,主要差别是在SD协议的基础上做了些扩展。
SDIO总线和USB总线类似,SDIO总线也有两端,其中一端是主机(HOST)端,另一端是设备端(DEVICE),采用HOST- DEVICE这样的设计是为了简化DEVICE的设计,所有的通信都是由HOST端发出命令开始的。在DEVICE端只要能解析HOST的命令,就可以同HOST进行通信了,SDIO的HOST可以连接多个DEVICE。
图中SDIO控制器有两组SDIO总线,可以挂两张SDIO card。
SDIO总线pin脚:
1,CLK信号:HOST给DEVICE的时钟信号线
2,CMD信号:用于HOST发送命令和DEVICE回复命令
3,DAT0-DAT3:用于传送的数据线(DAT1复用作中断和数据传输)另外还有
4,VDD信号:电源信号
5,VSS1,VSS2:电源地信号共9个引脚。
SDIO有三种传输模式
SPI传输模式
1Bit传输模式
4Bit传输模式SDIO协议规定,在SDIO的1bit模式下,数据线DATA0用来传输数据,DATA1用作中断。在SDIO的4bit模式下,数据线DATA0~3用于传输数据,其中DATA1复用作中断线;
以SD卡和TF卡为例,查看它们的pin脚定义
SD存储卡( Secure Digital Memory Card),TF卡( Micro SD Card,原名Trans-flash Card),他们数据传输都同是通过SDIO总线,我们可以称它为SD card,虽然都用的是SDIO总线,SD card在HOST识别的过程中使用的是SD规范,SDIO card与之不同的是SDIO card识别使用的是SDIO规范。SDIO规范是从SD规范进行了扩展而来。SD卡比TF卡多了一个VSS引脚,他们都支持SD 1bit,4bit模式,和SPI模式,模式不同pin脚的功能也不一样。
SDIO命令
SDIO总线上的设置和控制都是通过命令来实现,SDIO总线上都是HOST端发起请求,然后DEVICE端回应请求,其中请求和应答中会包含数据信息:
- Command: 用于开始传输的命令,是由HOST端发往DEVICE端的,其中命令是通过CMD信号线传送的。
- Response: DEVICE返回的应答。也是通过CMD线传送的;
- Data: 数据是双向传送的。可以设置为1线模式,也可以设置为4线模式。数据是通过DAT0-DAT3信号线传输的。
SDIO的命令分为:应用相关命令(ACMD)和通用命令(CMD)两部分。发送ACMD时,需先发送CMD55。
SDIO所有的命令和响应都是在SDIO_CMD引脚上面传输的,命令长度固定为48位,SDIO命令格式如下表所示:
SDIO的响应
一般SD卡在接收到命令行,都会有一个应答(CMD0例外),这个应答我们也称之为响应。STM32的SDIO接口,支持2种响应类型:短响应(48位)和长响应(136位)
SDIO短响应(48位)格式如下表所示:
SDIO长响应(136位)格式如下表所示:
SD卡总共有6类响应(R1、R1b、R2、R3、R6、R7),我们这里以R1为例简单介绍一下。R1(普通响应命令)响应属于短响应,其长度为48位,如下表所示:
STM32的SDIO
具体查看数据手册
本文不赘述。
关键代码
/* Includes ------------------------------------------------------------------*/ #include "MyApplication.h" #include "font_ASCII.h" #include "font_CHN.h" #include "Pic1.h" #include "Pic2.h" /* Private define-------------------------------------------------------------*/ /* Private variables----------------------------------------------------------*/ static void LCD_Init(void); //LCD屏幕初始化 static void LCD_FillColor(uint16_t,uint16_t,uint16_t,uint16_t,LCD_Color_t); //LCD屏幕填充颜色 static void LCD_ShowChar(uint16_t, uint16_t, const char, uint16_t, uint16_t,ASCII_font_t); //在LCD屏幕上显示一个英文字符 static void LCD_ShowString(uint16_t, uint16_t, const char *, uint16_t, uint16_t,ASCII_font_t); //在LCD屏幕上显示英文字符串 static void LCD_ShowCHN(uint16_t, uint16_t, const char *, uint16_t, uint16_t,CHN_font_t); //在LCD屏幕上显示一个中文字符 static void LCD_ShowCHNandENGstring(uint16_t, uint16_t, const char *, uint16_t, uint16_t,CHN_font_t,ASCII_font_t); //在LCD屏幕上显示中英文字符串 static void LCD_ShowPicture(uint16_t, uint16_t, uint16_t, uint16_t,uint8_t); //在LCD屏幕上显示图片 /* Public variables-----------------------------------------------------------*/ TFT_LCD_t TFT_LCD = { 0, LCD_Init, LCD_FillColor, LCD_ShowChar, LCD_ShowString, LCD_ShowCHN, LCD_ShowCHNandENGstring, LCD_ShowPicture }; /* Private function prototypes------------------------------------------------*/ static uint32_t LCD_ReadID(void); //LCD读取ID static void LCD_Disp_Direction(void); //LCD显示方向 static void LCD_SetWindows(uint16_t,uint16_t,uint16_t,uint16_t); //设置LCD显示窗口 /* * @name LCD_ReadID * @brief LCD读取ID * @param None * @retval LCD_ID -> 返回LCD屏幕ID */ static uint32_t LCD_ReadID(void) { uint32_t LCD_ID = 0; uint32_t buf[4]; LCD_Write_CMD(0xD3); buf[0] = LCD_Read_DATA(); // 第一个读取数据无效 buf[1] = LCD_Read_DATA()&0x00FF; // 只有低8位数据有效 buf[2] = LCD_Read_DATA()&0x00FF; // 只有低8位数据有效 buf[3] = LCD_Read_DATA()&0x00FF; // 只有低8位数据有效 LCD_ID = (buf[1] << 16) + (buf[2] << 8) + buf[3]; return LCD_ID; } /* * @name LCD_Init * @brief LCD屏幕初始化 * @param None * @retval None */ static void LCD_Init(void) { //读取LCD屏幕ID TFT_LCD.ID = LCD_ReadID(); printf("The ID of TFT LCD is 0x%.6X\r\n",TFT_LCD.ID); //2.8inch ILI9341初始化 LCD_Write_CMD(0xCF); LCD_Write_DATA(0x00); LCD_Write_DATA(0xC9); //C1 LCD_Write_DATA(0x30); LCD_Write_CMD(0xED); LCD_Write_DATA(0x64); LCD_Write_DATA(0x03); LCD_Write_DATA(0X12); LCD_Write_DATA(0X81); LCD_Write_CMD(0xE8); LCD_Write_DATA(0x85); LCD_Write_DATA(0x10); LCD_Write_DATA(0x7A); LCD_Write_CMD(0xCB); LCD_Write_DATA(0x39); LCD_Write_DATA(0x2C); LCD_Write_DATA(0x00); LCD_Write_DATA(0x34); LCD_Write_DATA(0x02); LCD_Write_CMD(0xF7); LCD_Write_DATA(0x20); LCD_Write_CMD(0xEA); LCD_Write_DATA(0x00); LCD_Write_DATA(0x00); LCD_Write_CMD(0xC0); //Power control LCD_Write_DATA(0x1B); //VRH[5:0] LCD_Write_CMD(0xC1); //Power control LCD_Write_DATA(0x00); //SAP[2:0];BT[3:0] 01 LCD_Write_CMD(0xC5); //VCM control LCD_Write_DATA(0x30); //3F LCD_Write_DATA(0x30); //3C LCD_Write_CMD(0xC7); //VCM control2 LCD_Write_DATA(0XB7); //LCD_Write_CMD(0x36); // Memory Access Control //LCD_Write_DATA(0x08); LCD_Disp_Direction(); //设置LCD显示方向 LCD_Write_CMD(0x3A); LCD_Write_DATA(0x55); LCD_Write_CMD(0xB1); LCD_Write_DATA(0x00); LCD_Write_DATA(0x1A); LCD_Write_CMD(0xB6); // Display Function Control LCD_Write_DATA(0x0A); LCD_Write_DATA(0xA2); LCD_Write_CMD(0xF2); // 3Gamma Function Disable LCD_Write_DATA(0x00); LCD_Write_CMD(0x26); //Gamma curve selected LCD_Write_DATA(0x01); LCD_Write_CMD(0xE0); //Set Gamma LCD_Write_DATA(0x0F); LCD_Write_DATA(0x2A); LCD_Write_DATA(0x28); LCD_Write_DATA(0x08); LCD_Write_DATA(0x0E); LCD_Write_DATA(0x08); LCD_Write_DATA(0x54); LCD_Write_DATA(0XA9); LCD_Write_DATA(0x43); LCD_Write_DATA(0x0A); LCD_Write_DATA(0x0F); LCD_Write_DATA(0x00); LCD_Write_DATA(0x00); LCD_Write_DATA(0x00); LCD_Write_DATA(0x00); LCD_Write_CMD(0XE1); //Set Gamma LCD_Write_DATA(0x00); LCD_Write_DATA(0x15); LCD_Write_DATA(0x17); LCD_Write_DATA(0x07); LCD_Write_DATA(0x11); LCD_Write_DATA(0x06); LCD_Write_DATA(0x2B); LCD_Write_DATA(0x56); LCD_Write_DATA(0x3C); LCD_Write_DATA(0x05); LCD_Write_DATA(0x10); LCD_Write_DATA(0x0F); LCD_Write_DATA(0x3F); LCD_Write_DATA(0x3F); LCD_Write_DATA(0x0F); LCD_Write_CMD(0x2B); LCD_Write_DATA(0x00); LCD_Write_DATA(0x00); LCD_Write_DATA(0x01); LCD_Write_DATA(0x3f); LCD_Write_CMD(0x2A); LCD_Write_DATA(0x00); LCD_Write_DATA(0x00); LCD_Write_DATA(0x00); LCD_Write_DATA(0xef); LCD_Write_CMD(0x11); //Exit Sleep HAL_Delay(120); LCD_Write_CMD(0x29); //display on TFT_LCD_BL_ON; //打开背光 TFT_LCD.FillColor(0,0,LCD_WIDTH,LCD_HEIGTH,Color_GRAY); //屏幕填充灰色 } /* * @name LCD_Disp_Directio * @brief LCD显示方向 * @param None * @retval None */ static void LCD_Disp_Direction() { switch(LCD_DIRECTION) { case 1: LCD_Write_CMD(0x36); LCD_Write_DATA(1<<3); break; case 2: LCD_Write_CMD(0x36); LCD_Write_DATA((1<<3)|(1<<5)|(1<<6)); break; case 3: LCD_Write_CMD(0x36); LCD_Write_DATA((1<<3)|(1<<7)|(1<<4)|(1<<6)); break; case 4: LCD_Write_CMD(0x36); LCD_Write_DATA((1<<3)|(1<<7)|(1<<5)|(1<<4)); break; default:LCD_Write_CMD(0x36); LCD_Write_DATA(1<<3); break; } } /* * @name LCD_SetWindows * @brief 设置LCD显示窗口 * @param xStar ->窗口的起点X坐标 yStar ->窗口的起点Y坐标 xWidth ->窗口的宽度 yHeight->窗口的高度 * @retval None */ static void LCD_SetWindows(uint16_t xStar, uint16_t yStar,uint16_t xWidth,uint16_t yHeight) { LCD_Write_CMD(LCD_CMD_SETxOrgin); LCD_Write_DATA(xStar>>8); LCD_Write_DATA(0x00FF&xStar); LCD_Write_DATA((xStar+xWidth-1)>>8); LCD_Write_DATA((xStar+xWidth-1)&0xFF); LCD_Write_CMD(LCD_CMD_SETyOrgin); LCD_Write_DATA(yStar>>8); LCD_Write_DATA(0x00FF&yStar); LCD_Write_DATA((yStar+yHeight-1)>>8); LCD_Write_DATA((yStar+yHeight-1)&0xFF); LCD_Write_CMD(LCD_CMD_WRgram); //开始写入GRAM } /* * @name LCD_FillColor * @brief LCD屏幕填充颜色 * @param xStar ->窗口的起点X坐标 yStar ->窗口的起点Y坐标 xWidth ->窗口的宽度 yHeight->窗口的高度 FillColor -> 填充色 * @retval None */ static void LCD_FillColor(uint16_t xStar, uint16_t yStar,uint16_t xWidth,uint16_t yHeight,LCD_Color_t FillColor) { uint16_t i,j; //uint16_t k; //设置窗口 LCD_SetWindows(xStar,yStar,xWidth,yHeight); //填充颜色 for(i=xStar;i<(xStar+xWidth);i++) { for(j=0;j<(yStar+yHeight);j++) { LCD_Write_DATA(FillColor); //动态观看屏幕显示过程 //for(k=0;k<100;k++); } } } /* * @name LCD_ShowChar * @brief 在LCD屏幕上显示一个英文字符 * @param usX: 起始X坐标 * usY :起始Y坐标 * cChar :要显示的英文字符 * usColor_Background :选择英文字符的背景色 * usColor_Foreground :选择英文字符的前景色 * font:字体选择 * 参数:ASCII_font_16 :16号字体 * ASCII_font_24 :24号字体 * @retval None */ static void LCD_ShowChar(uint16_t usX, uint16_t usY, const char cChar, uint16_t usColor_Background, uint16_t usColor_Foreground,ASCII_font_t font) { uint8_t ucTemp, ucIndex, ucPage, ucColumn; //检查输入参数是否合法 assert_param(IS_ASCII_font(font)); //ASCII字符集数组索引,需要减去偏移量(' ' -> 空格键的码值) ucIndex = cChar - ' '; //判断字体 - 16号字体 if(font == ASCII_font_16) { //设置窗口,大小为8x16 LCD_SetWindows(usX,usY,8,16); //逐行写入数据,共16行,每行8个像素点 for(ucPage=0;ucPage<16;ucPage++) { //从ASCII字符集数组获取像素数据 //像素点数据为1时,写入字符颜色,为0时,写入背景颜色 ucTemp = ucAscii_1608[ucIndex][ucPage]; for(ucColumn=0;ucColumn<8;ucColumn++) { if((ucTemp & BIT0) == BIT0) LCD_Write_DATA(usColor_Foreground); else LCD_Write_DATA(usColor_Background); ucTemp >>= 1; } } } //判断字体 - 24号字体 if(font == ASCII_font_24) { //设置窗口,大小为12x24 LCD_SetWindows(usX,usY,12,24); //逐行写入数据,共24行,每行12个像素点(占2个字节) for(ucPage=0;ucPage<48;ucPage+=2) { //从ASCII字符集数组获取像素数据,前8个像素点 //像素点数据为1时,写入字符颜色,为0时,写入背景颜色 ucTemp = ucAscii_2412[ucIndex][ucPage]; for(ucColumn=0;ucColumn<8;ucColumn++) { if((ucTemp & BIT0) == BIT0) LCD_Write_DATA(usColor_Foreground); else LCD_Write_DATA(usColor_Background); ucTemp >>= 1; } //从ASCII字符集数组获取像素数据,后4个像素点 //像素点数据为1时,写入字符颜色,为0时,写入背景颜色 ucTemp=ucAscii_2412[ucIndex][ucPage+1]; for(ucColumn=0;ucColumn<4;ucColumn++) { if((ucTemp & BIT0) == BIT0) LCD_Write_DATA(usColor_Foreground); else LCD_Write_DATA(usColor_Background); ucTemp >>= 1; } } } } /* * @name LCD_ShowString * @brief 在LCD屏幕上显示英文字符串 * @param usX: 起始X坐标 * usY :起始Y坐标 * pStr:要显示的英文字符串的首地址 * usColor_Background :选择英文字符的背景色 * usColor_Foreground :选择英文字符的前景色 * font:字体选择 * 参数:ASCII_font_16 :16号字体 * ASCII_font_24 :24号字体 * @retval None */ static void LCD_ShowString(uint16_t usX, uint16_t usY, const char * pStr, uint16_t usColor_Background, uint16_t usColor_Foreground,ASCII_font_t font) { while (* pStr != '\0') { //自动换行 if ((usX + font/2) > LCD_WIDTH) { usX = 0; usY += font; } //自动换页 if ((usY + font) > LCD_HEIGTH) { usX = 0; usY = 0; } //显示字符 TFT_LCD.LCD_ShowChar(usX, usY, * pStr, usColor_Background, usColor_Foreground,font); //更新位置 pStr ++; usX += font/2; } } /* * @name LCD_ShowCHN * @brief 在LCD屏幕上显示1个中文字符 * @param usX: 起始X坐标 * usY :起始Y坐标 * pStr:要显示的英文字符串的首地址 * usColor_Background :选择英文字符的背景色 * usColor_Foreground :选择英文字符的前景色 * font:字体选择 * 参数:CHN_font_16 :16号字体 * CHN_font_24 :24号字体 * @retval None */ static void LCD_ShowCHN(uint16_t usX, uint16_t usY, const char * pStr, uint16_t usColor_Background, uint16_t usColor_Foreground,CHN_font_t font) { uint8_t ucTemp, ucPage, ucColumn; uint16_t usIndex; //字库中的汉字索引 uint16_t CHN_Num; //字库中的汉字数量 //检查输入参数是否合法 assert_param(IS_ASCII_font(font)); //判断字体 - 16号字体 if(font == CHN_font_16) { //统计汉字数量 CHN_Num = sizeof(FONT_CHN16) / sizeof(FONT_CHN16_t); //for循环查找汉字位置 for(usIndex=0;usIndex<CHN_Num;usIndex++) { if((FONT_CHN16[usIndex].Index[0] == *pStr) && (FONT_CHN16[usIndex].Index[1] == *(pStr+1))) { //设置窗口,大小为16x16 LCD_SetWindows(usX,usY,16,16); //逐行写入数据,共16行,每行16个像素点 for(ucPage=0;ucPage<32;ucPage++) { //从ASCII字符集数组获取像素数据 //像素点数据为1时,写入字符颜色,为0时,写入背景颜色 ucTemp = FONT_CHN16[usIndex].CHN_code[ucPage]; for(ucColumn=0;ucColumn<8;ucColumn++) { if((ucTemp & BIT0) == BIT0) LCD_Write_DATA(usColor_Foreground); else LCD_Write_DATA(usColor_Background); ucTemp >>= 1; } } break; //已找到并显示了汉字,退出循环 } } } //判断字体 - 24号字体 if(font == CHN_font_24) { //统计汉字数量 CHN_Num = sizeof(FONT_CHN24) / sizeof(FONT_CHN24_t); //for循环查找汉字位置 for(usIndex=0;usIndex<CHN_Num;usIndex++) { if((FONT_CHN24[usIndex].Index[0] == *pStr) && (FONT_CHN24[usIndex].Index[1] == *(pStr+1))) { //设置窗口,大小为24x24 LCD_SetWindows(usX,usY,24,24); //逐行写入数据,共24行,每行24个像素点 for(ucPage=0;ucPage<72;ucPage++) { //从ASCII字符集数组获取像素数据 //像素点数据为1时,写入字符颜色,为0时,写入背景颜色 ucTemp = FONT_CHN24[usIndex].CHN_code[ucPage]; for(ucColumn=0;ucColumn<8;ucColumn++) { if((ucTemp & BIT0) == BIT0) LCD_Write_DATA(usColor_Foreground); else LCD_Write_DATA(usColor_Background); ucTemp >>= 1; } } break; //已找到并显示了汉字,退出循环 } } } } /* * @name LCD_ShowCHNandENGstring * @brief 在LCD屏幕上显示中英文字符串 * @param usX: 起始X坐标 * usY :起始Y坐标 * pStr:要显示的中英文字符串的首地址 * usColor_Background :选择字符的背景色 * usColor_Foreground :选择字符的前景色 * font_CHN: 中文字体选择 * 参数:CHN_font_16 :16号字体 * CHN_font_24 :24号字体 * font_ASCII:ASCII码字体选择 * 参数:ASCII_font_16 :16号字体 * ASCII_font_24 :24号字体 * @retval None */ static void LCD_ShowCHNandENGstring(uint16_t usX, uint16_t usY, const char * pStr, uint16_t usColor_Background, uint16_t usColor_Foreground,CHN_font_t font_CHN,ASCII_font_t font_ASCII) { while (* pStr != '\0') { //中文字符 if((* pStr) > 127) { //自动换行 if ((usX + font_CHN) > LCD_WIDTH) { usX = 0; usY += font_CHN; } //自动换页 if ((usY + font_CHN) > LCD_HEIGTH) { usX = 0; usY = 0; } //显示中文字符 TFT_LCD.LCD_ShowCHN(usX, usY, pStr, usColor_Background, usColor_Foreground,font_CHN); //更新位置 pStr += 2; usX += font_CHN; } //英文字符 else { if((* pStr == '\r') | (* pStr == '\n')) { //前面的字符为中文 if((* (pStr-1)) > 127) { //换行 usX = 0; usY += font_CHN; } //前面的字符为英文 else { //换行 usX = 0; usY += font_ASCII; } } else { //自动换行 if ((usX + font_ASCII/2) > LCD_WIDTH) { usX = 0; usY += font_ASCII; } //自动换页 if ((usY + font_ASCII) > LCD_HEIGTH) { usX = 0; usY = 0; } //显示字符 TFT_LCD.LCD_ShowChar(usX, usY, * pStr, usColor_Background, usColor_Foreground,font_ASCII); //更新位置 usX += font_ASCII/2; } //指向下一个字符 pStr ++; } } } /* * @name LCD_ShowPicture * @brief 在LCD屏幕上显示图片 * @param usX :起始X坐标 * usY :起始Y坐标 * Pic_H :图片水平分辨率 * Pic_V :图片垂直分辨率 * Pic_num:图片序号 * @retval None */ static void LCD_ShowPicture(uint16_t usX, uint16_t usY,uint16_t Pic_H, uint16_t Pic_V,uint8_t Pic_num) { uint32_t ulIndex; const uint8_t* pic = NULL; //设置窗口,大小为Pic_HxPic_V LCD_SetWindows(usX,usY,Pic_H,Pic_V); //获取图像数据首地址 switch(Pic_num) { case 1: pic = gImage_Pic1; break; case 2: pic = gImage_Pic2; break; default:pic = gImage_Pic1; } //逐行写入图片数据 for(ulIndex=0;ulIndex<Pic_H*Pic_V*2;ulIndex+=2) { LCD_Write_DATA((pic[ulIndex]<<8) | pic[ulIndex+1]); } } /******************************************************** End Of File ********************************************************/