在介绍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是在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屏幕IDTFT_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); //3FLCD_Write_DATA(0x30); //3CLCD_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 SleepHAL_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){//设置窗口,大小为8x16LCD_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); elseLCD_Write_DATA(usColor_Background); ucTemp >>= 1; }} }//判断字体 - 24号字体if(font == ASCII_font_24){//设置窗口,大小为12x24LCD_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); elseLCD_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); elseLCD_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>= 1; }}break; //已找到并显示了汉字,退出循环}} }//判断字体 - 24号字体if(font == CHN_font_24){//统计汉字数量CHN_Num = sizeof(FONT_CHN24) / sizeof(FONT_CHN24_t);//for循环查找汉字位置for(usIndex=0;usIndex >= 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_VLCD_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
上一篇:企业级nginx使用