STM32实战总结:HAL之SDIO

在介绍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

具体查看数据手册

本文不赘述。  

注意:

STM32的SDIO没有SPI兼容的通信模式;

MMC支持1/4/8位操作模式;

SD卡只支持1/4位操作模式;

复位后默认情况下SDIO_D0用于数据传输。初始化后主机可以改变数据总线的宽度。

如果SD或SD I/O卡接到了总线上,可通过主机配置数据传输使用SDIO_D0或SDIO_D[3:0]。所有的数据线都工作在推挽模式。

MX初始化

首先选择模式

分别为SD卡1位模式、4位模式;MMC卡1/4/8位模式。

常用的是SD 4 bits,即使用4根数据线来传输数据。

参考原理图如下:

更多参数设置:

在对参数进行解释前,先做几点相关说明:

SDIO模块使用了两个时钟,一个是用于AHB一侧的,一个是用于SD卡一侧的。

这两个时钟在时钟树中有所体现:

图中上方框里的是SD卡侧时钟,下方框是AHB接口时钟。

这里要注意的是,SDIO并非APB上的外设,而是直接跟AHB连接的接口。

接下来说明参数设置

从上到下:

✔在时钟上升沿还是下降沿进行位捕获,SDIO_CK是卡的时钟,每个时钟周期在命令和数据线上传输1位命令或数据;

✔是否启用时钟旁路,SDIO_CK输出可以使用时钟分频或时钟旁路模式,我们一般使用时钟分频,所以时钟旁路模式不使能;

直接旁路使用,则72M时钟用于SDIO_CK过快。对于SD卡来说,一般不超过25MHz

✔当总线空闲时是否使能时钟输出,这里主要是低功耗时考虑,即空闲时间是否关闭时钟;

✔硬件流控;

✔时钟分频因子,这里设置的是SDIO时钟引脚输出的时钟,不是时钟源

在下方有针对时钟分频因子的说明,分频因子在0—255之间,最终得到的时钟等于SD卡适配器侧时钟源除以(分频因子+2)

综上所述,参数一般默认即可。

如果时钟过快,也可根据实际情况进行适当分频。

关键代码

相关库函数:

stm32f1xx_hal_sd.h

/* 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
********************************************************/

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值