STM32实战总结:HAL之SDIO

news2024/10/6 4:02:54

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/52458.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

D. Social Network(并查集修改连通块数量)

Problem - D - Codeforces 威廉来到了一个专门讨论加密货币的会议。要想了解加密货币世界的最新消息&#xff0c;建立联系、认识新朋友、利用朋友的关系是必不可少的。 会议有N个参与者&#xff0c;他们最初都不熟悉对方。威廉可以把之前不熟悉的任何两个人a和b介绍给对方。 …

spi驱动数码管

spi是串行全双工同步通信&#xff0c;支持多从机模式&#xff0c;没有应答机制&#xff0c;可靠性方面存在劣势&#xff1b; 采用边沿采样&#xff0c;根据时钟极性和时钟相位&#xff0c;有四种数据传输方式&#xff08;由时钟变化(极性&#xff0c;相位)决定&#xff1b; 因…

Java Tomcat内存马——Listener内存马

目录 &#xff08;一&#xff09;前置知识 0x01 什么是Listener 0x02 Listener的简单案例 0x03 Listener流程分析 &#xff08;二&#xff09;注入分析 (三&#xff09;实现内存马 得到完整的内存马 (四&#xff09;漏洞复现 其他的payload: 总结 &#xff08;一&#…

Observability:从零开始创建 Java 微服务并监控它 (二)

这篇文章是继上一篇文章 “Observability&#xff1a;从零开始创建 Java 微服务并监控它 &#xff08;一&#xff09;” 的续篇。在上一篇文章中&#xff0c;我们讲述了如何创建一个 Java web 应用&#xff0c;并使用 Filebeat 来收集应用所生成的日志。在今天的文章中&#xf…

机器学习3判断机器算法的性能

文章目录一、判断机器算法的性能1基本使用1.目的2.使用pycharm函数封装3.sklearn中的train test split&#xff1a;4.完美调用&#xff1a;二、判断机器算法的性能2分类的准确度&#xff08;accuracy&#xff09;准确度初步计算&#xff1a;完善KNNpy程序如下&#xff1a;一、判…

[附源码]Python计算机毕业设计Django高校社团管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

GHost系统备份与还原

前期准备工作&#xff1a;U盘&#xff08;>8G&#xff09;,最好大一点&#xff0c;如果你U盘要放GHO或者ISO文件的话&#xff0c;可能就不够用了。 我这里使用的老白菜工具&#xff0c;然后制作一个启动U盘。附教程连接&#xff1a;http://laobaicai.bsllcmgs.cn/upqdzz.htm…

无线通信系统简述(学习笔记)

文章目录Evolution of Mobile Radio CommunicationsFirst GenerationSecond GenerationThird GenerationFourth GenerationFifth GenerationOther Wireless Communication SystemsWireless Local Area Networks (WLAN&#xff09;Satellite communication networkWireless Sens…

揭秘如今市场上最火爆的三大商业模式,点进来看看有没有什么收获

大家好&#xff0c;我是爱生活爱分享&#xff0c;无限输出干活内容的阿璋&#xff0c;今天和大家分享一下现在最实用最流行的三大商业模式&#xff0c;现在市面上都有成熟的案例&#xff0c;每个模式都有不同的效果&#xff0c;大家可以看一看&#xff0c;学一学&#xff0c;借…

高通导航器软件开发包使用指南(17)

高通导航器软件开发包使用指南&#xff08;17&#xff09;11 附加的功能11.1 螺旋桨障碍检测11.1.1 螺旋桨起转期间11.1.2 飞行中11.2 低电压警告和迫降11.3 GPS 模式下的 Geotether11.4 禁飞区功能11.5 不允许螺旋桨旋转的传感器检查11.6 仿真模式11.6.1 简介11.6.2 用法12 状…

一种用于入侵检测的自适应集成机器学习模型

一种用于入侵检测的自适应集成机器学习模型学习目标学习内容一&#xff0e;Decision Tree Based Intrusion Detection System for NSL-KDD Dataset二、一种用于入侵检测的自适应集成机器学习模型D. Multi-Tree算法E. 深度神经网络(Deep Neural Networks)算法/多层感知机&#x…

pikachu平台SQL注入

pikachu平台SQL注入 日常心累、速通pikachu注入相关 目录pikachu平台SQL注入使用到的名词解释1. 数字型注入 --使用bp处理数据包2. 字符型注入 --hackbar处理3. 搜索型注入4. xx型注入5. insert/update注入6. delete注入7. http头注入8. 布尔盲注9. 时间盲注10. 宽字节注入使用…

MYSQL 事务、事务隔离级别和MVCC,幻读

快照读和当前读 快照读&#xff1a;快照读就是读取的是快照数据&#xff0c;不加锁的普通 SELECT 都属于快照读。如下面语句&#xff1a; select * from table where ..... 当前读:当前读就是读的是最新数据&#xff0c;而不是历史的数据&#xff0c;加锁的 SELECT&#xff0c…

八、Nacos服务注册和配置中心

SpringCloud Alibaba Nacos服务注册和配置中心 Nacos简介 为什么叫Nacos 前四个字母分别为Naming和Configuration的前两个字母&#xff0c;最后的s为Service 是什么 一个更易于构建云原生应用的动态服务发现&#xff0c;配置管理和服务管理中心 Nacos&#xff1a;Dynamic…

微信

引言&#xff1a;微信必不可少&#xff0c;但用着用着 C 盘内存飙升&#xff0c;不知如何解决&#xff1f;这篇博文来帮你 文章目录一、安装微信二、微信文件默认保存位置的更改三、WeChat Files 有什么&#xff1f;一、安装微信 下载之前先在 D 盘&#xff08;除了 C 盘就行&…

Android Profiler入门与实践

1、内存分析 点击MEMORY&#xff0c;可以看到正在运行进程的各种内存使用情况 Tips&#xff1a;点击右上角的垃圾桶图标会触发强制gc 1.1、查看Java堆内存和Java实例 执行以下代码&#xff1a; 说明&#xff1a;list为MainActivity的全局变量&#xff0c;所以list的只有在M…

数据结构和算法——基于Java——4.1栈(数组实现栈、链表实现栈)

理论补充 先进后出 FILO&#xff08;First-Input-Last-Out&#xff09;的有序列表&#xff0c;限制线性表中元素的插入和删除只能在线性表的同一端进行 栈顶&#xff1a;变化的一端栈底:固定的一端 代码实现 2.1 数组模拟栈 package com.b0.stack;import java.util.Scanner…

[附源码]Python计算机毕业设计SSM考研信息共享博客系统(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

为ubuntu配置WiFi

1.前提条件 ubuntu不论什么版本&#xff0c;如果想要正常使用wifi&#xff0c;必须在电脑的bios中把secure boot设定为disable 2.快捷指令&#xff08;WiFi相关的常用命令行指令&#xff09; 2.1查看电脑连接的蓝牙和网卡设备 rfkill list all0: hci0: Bluetooth 这个是我电…

79. 单词搜索

79. 单词搜索 给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 单词必须按照字母顺序&#xff0c;通过相邻的单元格内的字母构成&#xff0c;其中“相邻”单元格是那些水…