【强烈推荐】基于STM32的TFT-LCD各种显示实现(内容详尽含代码)

2023-05-16

        前言:TFT-LCD模块作为人们日常生活中常见屏幕类型之一,使用的受众面非常广阔。例如:显示各个传感器数值显示精美界面多级化菜单系统等等都不离不开他的身影。可以说学会TFT-LCD模块是嵌入式开发必须掌握的驱动开发技能之一,同时,也是嵌入式开发调试配置的重要手段与技巧!(文章结尾会有代码开源

        实验硬件:STM32F103C8T6;2.4寸TFT-LCD(240×320)

        硬件实物图:

         效果图:

引脚连接:

VCC --> 3.3V

GND --> GND

CS --> PB11

Reset --> PB12

DC --> PB10

SDI --> PB15

SCK --> PB13

LED -->  PB9控制LCD背光,可以同PWM调节改变LCD亮暗

一、TFT-LCD模块简介

        TFT-LCDThin Film Transistor)液晶显示屏是薄膜晶体管型液晶显示屏,也就是“真彩”(TFT)。TFT液晶为每个像素都设有一个半导体开关,每个像素都可以通过点脉冲直接控制,因而每个节点都相对独立,并可以连续控制,不仅提高了显示屏的反应速度,同时可以精确控制显示色阶,所以TFT液晶的色彩更真

        TFT液晶显示屏的特点亮度好对比度高层次感强颜色鲜艳,但也存在着比较耗电成本过高的不足。TFT液晶技术加快了手机彩屏的发展。新一代的彩屏手机中很多都支持65536色显示,有的甚至支持16万色显示,这时TFT的高对比度,色彩丰富的优势就非常重要了。

        市面上的TFT-LCD有需要的芯片驱动类型(不同的驱动芯片,其显存大小与其驱动时的传输LCD初始化数据不一样。其显示功能的API函数可以互通),市面上常见的芯片驱动有:ILI9341/ ILI9325/ RM68042/ RM68021/ ILI9320/ ILI9328/ LGDP4531/ LGDP4535/ SPFD5408/ SSD1289/ 1505/ B505/ C505/ NT35310/ NT35510 等。

        笔者所用的TFTLCD驱动芯片为常见的ILI9341,这里就以ILI9341给大家为例讲诉(需要其他驱动芯片资料的可以评论留言,笔者基本上市面上常见的都有其代码与资料)。

        ILI9341液晶控制器自带显存,其显存总大小为 172800(240*320*18/8),即 18 位模式(26万色)下的显存量。在 16 位模式下,ILI9341 采用 RGB565 格式存储颜色数据(在更高级的32位RGB储存颜色中还有RGBA888,Linux开发板中较为常见),此时 ILI9341的 18 位数据线与 MCU 的 16 位数据线以及 LCD GRAM 的对应关系如图 所示:

        这样 MCU 的 16 位数据,最低 5 位代表蓝色中间 6 位为绿色最高 5 位为红色。数值越大,表示该颜色越深。另外,特别注意 ILI9341 所有的指令都是 8 位的(高 8 位无效),且参数除了读写 GRAM 的时候是 16 位,其他操作参数,都是 8 位的,这个和 ILI9320 等驱动器不一样,必须加以注意。

        注意:不同的TFT-LCD模块的引脚可能不同,这里原因为该LCD的硬件通讯方式的不同。较为常见的TFTLCD通讯方式有:串行通讯SPI,LVDS、EDP、MIPI等。(较多的为标红

        笔者这块TFT-LCD模块采用了SPI的通讯方式,故此接下来就以SPI下的TFT-LCD驱动为讲解。

一般 TFTLCD 模块的使用流程如图:

二、SPI简介

        SPI 协议是由摩托罗拉公司提出的通讯协议 (Serial Peripheral Interface),即串行外围设备接口,是一种高速全双工的通信总线。它被广泛地使用在 ADC设备LCD 等设备与 MCU 间,要求通讯速率较高的场合。

2.1 SPI通讯系统

        SPI 通讯使用3条总线片选线,3 条总线分别为 SCK、MOSI、MISO,片选线为 SS/CS ,它们的作用介绍如下:

(1) SS/CS( Slave Select)

        从设备选择信号线,常称为片选信号线,也称为 NSSCS,以下用 NSS表示。当有多个 SPI 从设备与 SPI 主机相连时,设备的其它信号线 SCK、MOSI 及 MISO同时并联到相同的 SPI 总线上,即无论有多少个从设备,都共同只使用这 3 条总线;而每个从设备都有独立的这一条 NSS 信号线,本信号线独占主机的一个引脚,即有多少个从设备,就有多少条片选信号线。I2C 协议中通过设备地址来寻址、选中总线上的某个设备并与其进行通讯;

        而SPI 协议中没有设备地址,它使用 NSS 信号线来寻址,当主机要选择从设备时,把该从设备的 NSS 信号线设置为低电平,该从设备即被选中,即片选有效,接着主机开始与被选中的从设备进行 SPI 通讯。所以 SPI 通讯以 NSS 线置低电平为开始信号,以NSS 线被拉高作为结束信号。(在LCD中,片选线有很多名称,CS,SS,NSS都是指片选

(2) SCK (Serial Clock):

        时钟信号线,用于通讯数据同步。它由通讯主机产生,决定了通讯的速率,不同的设备支持的最高时钟频率不一样,如 STM32 的 SPI 时钟频率最大为 fpclk/2,两个设备之间通讯时,通讯速率受限于低速设备

(3) MOSI (Master Output,Slave Input):

        主设备输出/从设备输入引脚。主机的数据从这条信号线输出,从机由这条信号线读入主机发送的数据,即这条线上数据的方向为主机到从机。 (IIC相比,这个就是信号线,由主机向从机发送数据,即SDA

(4) MISO(Master Input,,Slave Output):

        主设备输入/从设备输出引脚。主机从这条信线读入数据,从机的数据由这条信号线输出到主机,即在这条线上数据的方向为从机到主机。 (从机向主机发送数据,使用触摸屏时需要这根线。如果单纯使用LCD来显示,这根线可以不接)。

多设备的SPI通讯接线:

2.2 SPI时序图

SPI 有四种工作模式,通过串行时钟极性(CPOL)和相位(CPHA)的搭配来得到四种工作模式:
        ①、CPOL=0,串行时钟空闲状态为低电平。
        ②、CPOL=1,串行时钟空闲状态为高电平,此时可以通过配置时钟相位(CPHA)来选择具
体的传输协议。
        ③、CPHA=0,串行时钟的第一个跳变沿(上升沿或下降沿)采集数据。
        ④、CPHA=1,串行时钟的第二个跳变沿(上升沿或下降沿)采集数据。

三、CubexMX配置

1、RCC配置外部高速晶振(精度更高)——HSE;

2、SYS配置:Debug设置成Serial Wire否则可能导致芯片自锁);

 3、GPIO配置:将PB9,PB10,PB11,PB12都设置为GPIO_OUTPUT,速度为:Hight;

 4、SPI配置:配置使用SPI2作为TFT-LCD通讯方式

5、时钟树配置:

6、工程配置

四、代码实现与实验效果

4.1 TFT-LCD基础的初始化

        以下代码读者朋友可以参考各自TFT-LCD的datasheet文本,不同类型的TFT-LCD屏幕的初始化写入的数据可能不同,但是主要还是对那几个功能寄存器写入对应的数值。

lcd.h

/****************************************************************************
* 名    称:void  SPIv_WriteData(u8 Data)
* 功    能:STM32_模拟SPI写一个字节数据底层函数
* 入口参数:Data
* 出口参数:无
* 说    明:STM32_模拟SPI读写一个字节数据底层函数
****************************************************************************/
//void  SPIv_WriteData(u8 Data)
//{
//	unsigned char i=0;
//	for(i=8;i>0;i--)
//	{
//	  if(Data&0x80)	
//		{
//			LCD_SDA_SET; //????
//		}
//    else 
//		{
//			LCD_SDA_CLR;
//		}	   
//      LCD_SCL_CLR;       
//      LCD_SCL_SET;
//      Data<<=1; 
//	}
//}

void  SPIv_WriteData(u8 Data)
{
	SPI_WriteByte(&Data, 1);
}


/****************************************************************************
* 名    称:Lcd_WriteIndex(u8 Index)
* 功    能:向液晶屏写一个8位指令
* 入口参数:Index   寄存器地址
* 出口参数:无
* 说    明:调用前需先选中控制器,内部函数
****************************************************************************/
void Lcd_WriteIndex(u8 Index)
{
   LCD_CS_CLR;
   LCD_RS_CLR;

   SPIv_WriteData(Index);

   LCD_CS_SET;
}

/****************************************************************************
* 名    称:Lcd_WriteData(u8 Data)
* 功    能:向液晶屏写一个8位数据
* 入口参数:dat     寄存器数据
* 出口参数:无
* 说    明:向控制器指定地址写入数据,内部函数
****************************************************************************/
void Lcd_WriteData(u8 Data)
{
   LCD_CS_CLR;
   LCD_RS_SET;

   SPIv_WriteData(Data);

   LCD_CS_SET;
}

/****************************************************************************
* 名    称:void LCD_WriteReg(u8 Index,u16 Data)
* 功    能:写寄存器数据
* 入口参数:Index,Data
* 出口参数:无
* 说    明:本函数为组合函数,向Index地址的寄存器写入Data值
****************************************************************************/
void LCD_WriteReg(u8 Index,u16 Data)
{
	Lcd_WriteIndex(Index);
  	Lcd_WriteData_16Bit(Data);
}

/****************************************************************************
* 名    称:void Lcd_WriteData_16Bit(u16 Data)
* 功    能:向液晶屏写一个16位数据
* 入口参数:Data
* 出口参数:无
* 说    明:向控制器指定地址写入一个16位数据
****************************************************************************/
void Lcd_WriteData_16Bit(u16 Data)
{	
	LCD_CS_CLR;
  LCD_RS_SET;
	Lcd_WriteData(Data>>8);
	Lcd_WriteData(Data);
	LCD_CS_SET;
}

/****************************************************************************
* 名    称:void Lcd_Reset(void)
* 功    能:液晶硬复位函数
* 入口参数:无
* 出口参数:无
* 说    明:液晶初始化前需执行一次复位操作
****************************************************************************/
void Lcd_Reset(void)
{
	LCD_RST_CLR;
	HAL_Delay(100);
	LCD_RST_SET;
	HAL_Delay(50);
}

void Lcd_Init(void)
{	
	
	Lcd_Reset(); //Reset before LCD Init.

	//2.2inch TM2.2-G2.2 Init 20171020 
	Lcd_WriteIndex(0x11);  
	Lcd_WriteData(0x00); 

	Lcd_WriteIndex(0xCF);  
	Lcd_WriteData(0X00); 
	Lcd_WriteData(0XC1); 
	Lcd_WriteData(0X30);

	Lcd_WriteIndex(0xED);  
	Lcd_WriteData(0X64); 
	Lcd_WriteData(0X03); 
	Lcd_WriteData(0X12);
	Lcd_WriteData(0X81);

	Lcd_WriteIndex(0xE8);  
	Lcd_WriteData(0X85); 
	Lcd_WriteData(0X11); 
	Lcd_WriteData(0X78);

	Lcd_WriteIndex(0xF6);  
	Lcd_WriteData(0X01); 
	Lcd_WriteData(0X30); 
	Lcd_WriteData(0X00);

	Lcd_WriteIndex(0xCB);  
	Lcd_WriteData(0X39); 
	Lcd_WriteData(0X2C); 
	Lcd_WriteData(0X00);
	Lcd_WriteData(0X34);
	Lcd_WriteData(0X05);

	Lcd_WriteIndex(0xF7);  
	Lcd_WriteData(0X20); 

	Lcd_WriteIndex(0xEA);  
	Lcd_WriteData(0X00); 
	Lcd_WriteData(0X00); 

	Lcd_WriteIndex(0xC0);  
	Lcd_WriteData(0X20); 

	Lcd_WriteIndex(0xC1);  
	Lcd_WriteData(0X11); 

	Lcd_WriteIndex(0xC5);  
	Lcd_WriteData(0X31); 
	Lcd_WriteData(0X3C); 

	Lcd_WriteIndex(0xC7);  
	Lcd_WriteData(0XA9); 

	Lcd_WriteIndex(0x3A);  
	Lcd_WriteData(0X55); 
	
  Lcd_WriteIndex(0x36);  
	#if USE_HORIZONTAL
		 Lcd_WriteData(0xE8);//横屏参数
	#else
		 Lcd_WriteData(0x48);//竖屏参数 
	#endif

	Lcd_WriteIndex(0xB1);  
	Lcd_WriteData(0X00); 
	Lcd_WriteData(0X18); 

	Lcd_WriteIndex(0xB4);  
	Lcd_WriteData(0X00); 
	Lcd_WriteData(0X00); 

	Lcd_WriteIndex(0xF2);  
	Lcd_WriteData(0X00); 

	Lcd_WriteIndex(0x26);  
	Lcd_WriteData(0X01); 

	Lcd_WriteIndex(0xE0);  
	Lcd_WriteData(0X0F); 
	Lcd_WriteData(0X17); 
	Lcd_WriteData(0X14); 
	Lcd_WriteData(0X09); 
	Lcd_WriteData(0X0C); 
	Lcd_WriteData(0X06); 
	Lcd_WriteData(0X43); 
	Lcd_WriteData(0X75); 
	Lcd_WriteData(0X36); 
	Lcd_WriteData(0X08); 
	Lcd_WriteData(0X13); 
	Lcd_WriteData(0X05); 
	Lcd_WriteData(0X10); 
	Lcd_WriteData(0X0B); 
	Lcd_WriteData(0X08); 


	Lcd_WriteIndex(0xE1);  
	Lcd_WriteData(0X00); 
	Lcd_WriteData(0X1F); 
	Lcd_WriteData(0X23); 
	Lcd_WriteData(0X03); 
	Lcd_WriteData(0X0E); 
	Lcd_WriteData(0X04); 
	Lcd_WriteData(0X39); 
	Lcd_WriteData(0X25); 
	Lcd_WriteData(0X4D); 
	Lcd_WriteData(0X06); 
	Lcd_WriteData(0X0D); 
	Lcd_WriteData(0X0B); 
	Lcd_WriteData(0X33); 
	Lcd_WriteData(0X37); 
	Lcd_WriteData(0X0F); 

	Lcd_WriteIndex(0x29);  	
}

        以上代码为TFT-LCD的SPI软件通讯模式下常见的代码,其中不同驱动芯片下的LCD_Init函数会不同,基本一致的函数有:void  SPIv_WriteData(u8 Data),void Lcd_WriteIndex(u8 Index),void Lcd_WriteData(u8 Data),void LCD_WriteReg(u8 Index,u16 Data),void Lcd_WriteData_16Bit(u16 Data),void Lcd_Reset(void)。

引脚定义:

//define LCD PIN
#define	LCD_CS_SET  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_11,GPIO_PIN_SET)
#define	LCD_CS_CLR  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_11,GPIO_PIN_RESET)

#define	LCD_RS_SET  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_10,GPIO_PIN_SET)
#define	LCD_RS_CLR  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_10,GPIO_PIN_RESET)

#define	LCD_SDA_SET  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_15,GPIO_PIN_SET)
#define	LCD_SDA_CLR  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_15,GPIO_PIN_RESET)

#define	LCD_SCL_SET  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_13,GPIO_PIN_SET)
#define	LCD_SCL_CLR  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_13,GPIO_PIN_RESET)

#define	LCD_RST_SET  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_SET)
#define	LCD_RST_CLR  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_RESET)

#define	LCD_LED_SET  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET)
#define	LCD_LED_CLR  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_RESET)

4.2 SPI函数重写

        因为笔者采用了HAL库去使用SPI通讯,所以需要去重写SPI函数

spi.h:

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* USER CODE BEGIN Includes */
uint8_t SPI_WriteByte(uint8_t *TxData,uint16_t size);
/* USER CODE END Includes */

spi.c:

/* USER CODE BEGIN 1 */
uint8_t SPI_WriteByte(uint8_t *TxData,uint16_t size)
{
	return HAL_SPI_Transmit(&hspi2,TxData,size,1000);
}
/* USER CODE END 1 */

4.3 TFT-LCD显示的基础函数

        TFT-LCD的显示需要依赖几个基础功能函数,这几个函数也时通用的。

/*************************************************
函数名:LCD_Set_XY
功能:设置lcd显示起始点
入口参数:xy坐标
返回值:无
*************************************************/
void Lcd_SetXY(u16 Xpos, u16 Ypos)
{	
	Lcd_WriteIndex(0x2A);
	Lcd_WriteData_16Bit(Xpos);
	Lcd_WriteIndex(0x2B);
	Lcd_WriteData_16Bit(Ypos);
	Lcd_WriteIndex(0x2c);	
} 
/*************************************************
函数名:LCD_Set_Region
功能:设置lcd显示区域,在此区域写点数据自动换行
入口参数:xy起点和终点
返回值:无
*************************************************/
//设置显示窗口
void Lcd_SetRegion(u16 xStar, u16 yStar,u16 xEnd,u16 yEnd)
{
	Lcd_WriteIndex(0x2A);
	Lcd_WriteData_16Bit(xStar);
	Lcd_WriteData_16Bit(xEnd);
	Lcd_WriteIndex(0x2B);
	Lcd_WriteData_16Bit(yStar);
	Lcd_WriteData_16Bit(yEnd);
	Lcd_WriteIndex(0x2c);
}

	
/*************************************************
函数名:LCD_DrawPoint
功能:画一个点
入口参数:xy坐标和颜色数据
返回值:无
*************************************************/
void Gui_DrawPoint(u16 x,u16 y,u16 Data)
{
	Lcd_SetXY(x,y);
	Lcd_WriteData_16Bit(Data);

}    

/*************************************************
函数名:Lcd_Clear
功能:全屏清屏函数
入口参数:填充颜色COLOR
返回值:无
*************************************************/
void Lcd_Clear(u16 Color)               
{	
   unsigned int i;
   Lcd_SetRegion(0,0,X_MAX_PIXEL-1,Y_MAX_PIXEL-1);
	 LCD_CS_CLR;
   LCD_RS_SET;	
   for(i=0;i<X_MAX_PIXEL*Y_MAX_PIXEL;i++)
   {	
//	  	Lcd_WriteData_16Bit(Color);

		SPIv_WriteData(Color>>8);
		SPIv_WriteData(Color);

   }   
		LCD_CS_SET;
}

总的lcd.h函数:

#ifndef __LCD_H
#define __LCD_H

#include "main.h"

#define u8  unsigned char 
#define u16 unsigned int 

/用户配置区///	 
//支持横竖屏快速定义切换
#define USE_HORIZONTAL  		0	//定义是否使用横屏 		0,不使用.1,使用.

//-----------------------------SPI 总线配置--------------------------------------//
#define USE_HARDWARE_SPI     0  //1:Enable Hardware SPI;0:USE Soft SPI

//-------------------------屏幕物理像素设置--------------------------------------//
#define LCD_X_SIZE	        240
#define LCD_Y_SIZE	        320

#if USE_HORIZONTAL//如果定义了横屏 
#define X_MAX_PIXEL	        LCD_Y_SIZE
#define Y_MAX_PIXEL	        LCD_X_SIZE
#else
#define X_MAX_PIXEL	        LCD_X_SIZE
#define Y_MAX_PIXEL	        LCD_Y_SIZE
#endif
//
	 

#define RED  	0xf800
#define GREEN	0x07e0
#define BLUE 	0x001f
#define WHITE	0xffff
#define BLACK	0x0000
#define YELLOW  0xFFE0
#define GRAY0   0xEF7D   	//灰色0 3165 00110 001011 00101
#define GRAY1   0x8410      	//灰色1      00000 000000 00000
#define GRAY2   0x4208      	//灰色2  1111111111011111

//define LCD PIN
#define	LCD_CS_SET  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_11,GPIO_PIN_SET)
#define	LCD_CS_CLR  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_11,GPIO_PIN_RESET)

#define	LCD_RS_SET  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_10,GPIO_PIN_SET)
#define	LCD_RS_CLR  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_10,GPIO_PIN_RESET)

#define	LCD_SDA_SET  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_15,GPIO_PIN_SET)
#define	LCD_SDA_CLR  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_15,GPIO_PIN_RESET)

#define	LCD_SCL_SET  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_13,GPIO_PIN_SET)
#define	LCD_SCL_CLR  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_13,GPIO_PIN_RESET)

#define	LCD_RST_SET  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_SET)
#define	LCD_RST_CLR  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_RESET)

#define	LCD_LED_SET  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET)
#define	LCD_LED_CLR  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_RESET)


void Lcd_Reset(void);
void Lcd_WriteIndex(u8 Index);
void Lcd_WriteData(u8 Data);
void Lcd_WriteReg(u8 Index,u8 Data);
u16 Lcd_ReadReg(u8 LCD_Reg);
void Lcd_Reset(void);
void Lcd_Init(void);
void Lcd_Clear(u16 Color);
void Lcd_SetXY(u16 x,u16 y);
void Gui_DrawPoint(u16 x,u16 y,u16 Data);
unsigned int Lcd_ReadPoint(u16 x,u16 y);
void Lcd_SetRegion(u16 xStar, u16 yStar,u16 xEnd,u16 yEnd);
void Lcd_WriteData_16Bit(u16 Data);


#endif

4.4 TFT-LCD显示的API函数

LCDAPI.h:

#ifndef __LCDAPI_H
#define __LCDAPI_H
#include "main.h"

#define u8 unsigned char
#define u16 unsigned int

void Gui_Circle(u16 X,u16 Y,u16 R,u16 fc); 
void Gui_DrawLine(u16 x0, u16 y0,u16 x1, u16 y1,u16 Color);  
void Gui_box(u16 x, u16 y, u16 w, u16 h,u16 bc);
void Gui_box2(u16 x,u16 y,u16 w,u16 h, u8 mode);
void DisplayButtonDown(u16 x1,u16 y1,u16 x2,u16 y2);
void DisplayButtonUp(u16 x1,u16 y1,u16 x2,u16 y2);
void Gui_DrawFont_GBK16(u16 x, u16 y, u16 fc, u16 bc, u8 *s);
void Gui_DrawFont_GBK24(u16 x, u16 y, u16 fc, u16 bc, u8 *s);
void Gui_DrawFont_Num32(u16 x, u16 y, u16 fc, u16 bc, u16 num);

void LCD_DrawPoint(u16 x,u16 y);
unsigned long oled_pow(u8 m,u8 n);
void LCD_Showdecimal(u8 x,u8 y,float num,u8 z_len,u8 f_len,u8 size2);
void LCD_ShowChar(u16 x,u16 y,u8 num,u8 mode);
void Address_set(unsigned int x1,unsigned int y1,unsigned int x2,unsigned int y2);
void showhanzi(unsigned int x,unsigned int y,unsigned char index);
void showimage(const unsigned char *p);

void LCD_ShowNum(u16 x,u16 y,unsigned long num,u8 len);
void picture();

#endif

4.4.1 TFT-LCD显示字符和汉字的API函数

        笔者这里写了一个共用函数即可显示字符也可以显示汉字

        LCD的汉字与字符显示与OLED类似,也是需要去取字模的,但是TFT-LCD需要设定字体颜色

        不同的显示汉字字符API函数的取模方式不一样!!!(如果取模和程序画点顺序不一致,会导致显示字符为乱码)

        笔者这里的取字模方式:

 LCDAPI.c:

//display 16 ziti
void Gui_DrawFont_GBK16(u16 x, u16 y, u16 fc, u16 bc, u8 *s)
{
	unsigned char i,j;
	unsigned short k,x0;
	x0=x;

	while(*s) 
	{	
		if((*s) < 128) 
		{
			k=*s;
			if (k==13) 
			{
				x=x0;
				y+=16;
			}
			else 
			{
				if (k>32) k-=32; else k=0;
	
			    for(i=0;i<16;i++)
				for(j=0;j<8;j++) 
					{
				    	if(asc16[k*16+i]&(0x80>>j))	Gui_DrawPoint(x+j,y+i,fc);
						else 
						{
							if (fc!=bc) Gui_DrawPoint(x+j,y+i,bc);
						}
					}
				x+=8;
			}
			s++;
		}
			
		else 
		{
		

			for (k=0;k<hz16_num;k++) 
			{
			  if ((hz16[k].Index[0]==*(s))&&(hz16[k].Index[1]==*(s+1)))
			  { 
				    for(i=0;i<16;i++)
				    {
						for(j=0;j<8;j++) 
							{
						    	if(hz16[k].Msk[i*2]&(0x80>>j))	Gui_DrawPoint(x+j,y+i,fc);
								else {
									if (fc!=bc) Gui_DrawPoint(x+j,y+i,bc);
								}
							}
						for(j=0;j<8;j++) 
							{
						    	if(hz16[k].Msk[i*2+1]&(0x80>>j))	Gui_DrawPoint(x+j+8,y+i,fc);
								else 
								{
									if (fc!=bc) Gui_DrawPoint(x+j+8,y+i,bc);
								}
							}
				    }
				}
			  }
			s+=2;x+=16;
		} 
		
	}
}
//display 24 ziti
void Gui_DrawFont_GBK24(u16 x, u16 y, u16 fc, u16 bc, u8 *s)
{
	unsigned char i,j;
	unsigned short k;

	while(*s) 
	{
		if( *s < 0x80 ) 
		{
			k=*s;
			if (k>32) k-=32; else k=0;

		    for(i=0;i<16;i++)
			for(j=0;j<8;j++) 
				{
			    	if(asc16[k*16+i]&(0x80>>j))	
					Gui_DrawPoint(x+j,y+i,fc);
					else 
					{
						if (fc!=bc) Gui_DrawPoint(x+j,y+i,bc);
					}
				}
			s++;x+=8;
		}
		else 
		{

			for (k=0;k<hz24_num;k++) 
			{
			  if ((hz24[k].Index[0]==*(s))&&(hz24[k].Index[1]==*(s+1)))
			  { 
				    for(i=0;i<24;i++)
				    {
						for(j=0;j<8;j++) 
							{
						    	if(hz24[k].Msk[i*3]&(0x80>>j))
								Gui_DrawPoint(x+j,y+i,fc);
								else 
								{
									if (fc!=bc) Gui_DrawPoint(x+j,y+i,bc);
								}
							}
						for(j=0;j<8;j++) 
							{
						    	if(hz24[k].Msk[i*3+1]&(0x80>>j))	Gui_DrawPoint(x+j+8,y+i,fc);
								else {
									if (fc!=bc) Gui_DrawPoint(x+j+8,y+i,bc);
								}
							}
						for(j=0;j<8;j++) 
							{
						    	if(hz24[k].Msk[i*3+2]&(0x80>>j))	
								Gui_DrawPoint(x+j+16,y+i,fc);
								else 
								{
									if (fc!=bc) Gui_DrawPoint(x+j+16,y+i,bc);
								}
							}
				    }
			  }
			}
			s+=2;x+=24;
		}
	}
}

main.h函数:

//Font display 24 and 16
Gui_DrawFont_GBK24(0,0,GREEN,WHITE,"»ì·Ö¾ÞÊÞÁúijij");
Gui_DrawFont_GBK16(0,30,BLUE,WHITE,"»ì·Ö¾ÞÊÞÁúijij");
		
//string display
Gui_DrawFont_GBK16(0,50,BLACK,WHITE,"black sneak");

显示效果:

4.4.2 TFT-LCD显示数字和含小数的API函数

        动态的数字显示和含有小数的数字显示也是日常嵌入式开发中经常会遇到的,学会使用该API函数也是至关重要的。

常规数字显示函数:

void LCD_ShowNum(u16 x,u16 y,unsigned long num,u8 len)
{         	
	u8 t,temp;
	u8 enshow=0;
	num=(u16)num;
	for(t=0;t<len;t++)
	{
		temp=(num/oled_pow(10,len-t-1))%10;
		if(enshow==0&&t<(len-1))
		{
			if(temp==0)
			{
				LCD_ShowChar(x+8*t,y,' ',1);
				continue;
			}else enshow=1; 
		 	 
		}
	 	LCD_ShowChar(x+8*t,y,temp+48,1); 
	}
} 

含有小数的数字显示函数:

void LCD_Showdecimal(u8 x,u8 y,float num,u8 z_len,u8 f_len,u8 size2)
{         	
	u8 t,temp;
	u8 enshow;
	int z_temp,f_temp;      
	z_temp=(int)num;
	//????
	for(t=0;t<z_len;t++)
	{
		temp=(z_temp/oled_pow(10,z_len-t-1))%10;
		if(enshow==0 && t<(z_len-1))
		{
			if(temp==0)
			{
				LCD_ShowChar(x+(size2/2)*t,y,' ',size2);
				continue;
			}
			else
			enshow=1;
		}
		LCD_ShowChar(x+(size2/2)*t,y,temp+'0',size2); 
	}
	//小数点
	LCD_ShowChar(x+(size2/2)*(z_len),y,'.',size2); 
	
	f_temp=(int)((num-z_temp)*(oled_pow(10,f_len)));
    //小数部分
	for(t=0;t<f_len;t++)
	{
		temp=(f_temp/oled_pow(10,f_len-t-1))%10;
		LCD_ShowChar(x+(size2/2)*(t+z_len)+5,y,temp+'0',size2); 
	}
}

        以上API函数都是需要依赖部分基础显示函数,这里篇幅有限给大家省略了。大家可以在文章末尾下载源码,填补基础显示函数。

显示效果:

4.4.3 TFT-LCD特殊字符或数码管API显示函数

        一般特殊的字符或是数码管等也时通过取模软件进行特殊取模以呈现的效果,这里给大家介绍一个类似数码管数字的显示API函数,可以以后作为时钟显示使用。

数码管数字显示函数:

void Gui_DrawFont_Num32(u16 x, u16 y, u16 fc, u16 bc, u16 num)
{
	unsigned char i,j,k,c;
	//lcd_text_any(x+94+i*42,y+34,32,32,0x7E8,0x0,sz32,knum[i]);
//	w=w/8;

    for(i=0;i<32;i++)
	{
		for(j=0;j<4;j++) 
		{
			c=*(sz32+num*32*4+i*4+j);
			for (k=0;k<8;k++)	
			{
	
		    	if(c&(0x80>>k))	Gui_DrawPoint(x+j*8+k,y+i,fc);
				else {
					if (fc!=bc) Gui_DrawPoint(x+j*8+k,y+i,bc);
				}
			}
		}
	}
}

显示效果:

4.4.4 TFT-LCD图片显示API函数

        TFT-LCD图片显示需要使用到Img2Lcd软件,需要该软件的读者朋友,可以评论留言邮箱。

        Img2Lcd软件使用方法很简单,对于RGB565的真彩选择:16位真彩,其余选择如下图:

特别注意:由于MCU的显存通常有限,特别时C8T6等最小系统板的存储更是有限。如果取模图片之后,编译出现如下:Error: L6406E: No space in execution regions with .ANY selector matching xxx,那是你MCU炸内存了。

解决方法:

        ①换开发板:换一个大容量的开发板,比如ZET6等;

        ②从SD卡读图片:部分拓展实验,比如LCD显示动画,都是采用的这方法;

        ③改写MCU的malloc:这种方法属于大佬使用的,十分考验对MCU存储概念的理解;

图片显示API函数:

//显示图片
void showimage(const unsigned char *p) 
{//??128*128 ??
  int i; 
	unsigned char picH,picL;
//	Lcd_Clear(WHITE); //?? ->1 
	
			Address_set(100,100,219,219);
		    for(i=0;i<120*120;i++)
			 {	
			 	picL=*(p+i*2);	//??????
				picH=*(p+i*2+1);				
				Lcd_WriteData_16Bit(picH<<8|picL); 				
			 }	
}

        该函数的内参,也是需要读者朋友去根据自己取模的图片大小去设置的,很简单,相信有点C基础的都可以去使用。

显示效果:

4.4.4 main函数

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_SPI2_Init();
  /* USER CODE BEGIN 2 */
	Lcd_Init();
	LCD_LED_SET;//ͨ¹ýIO¿ØÖƱ³¹âÁ
	LCD_RST_SET;
	Lcd_Clear(WHITE);
//	LCD_RST_SET;
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		
		//Font display 24 and 16
		Gui_DrawFont_GBK24(0,0,GREEN,WHITE,"混分巨兽龙某某");
		Gui_DrawFont_GBK16(0,30,BLUE,WHITE,"混分巨兽龙某某");
		
		//string display
		Gui_DrawFont_GBK16(0,50,BLACK,WHITE,"black sneak");
		
		//Number display
		LCD_ShowNum(0,180,z,4);
		LCD_Showdecimal(0,200,t,2,2,16);
		t += 0.01;
		z += 1;
		
		//special thing display
		for(n = 0;n <10 ;n++)
		{
			Gui_DrawFont_Num32(0,100,RED,WHITE,num[n]);
			if(n == 10)
			{
				n = 0;
			}
		}
		
		//PNG display
		picture();
  }
  /* USER CODE END 3 */

五、实验效果

TFT-LCD各种显示

六、代码开源

        读者这里提供是2.4寸ILI9341驱动芯片的LCD显示代码,如果需要其他尺寸大小和芯片驱动的LCD代码可以评论区留言邮箱点个关注笔者免费提供

链接:https://pan.baidu.com/s/1tNzOOUUK7ioekmVSy5TqYA 提取码:yi93

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【强烈推荐】基于STM32的TFT-LCD各种显示实现(内容详尽含代码) 的相关文章

  • 常见移动机器人多角度对比分析

    混沌无形 混沌系统是世界本质 xff0c 无形之中存在规律 机器人智能化发展从线性过渡到混沌 xff0c 本号将分享机器人全栈技术 xff08 感知 规划 控制 xff1b 软件 机械 硬件等 xff09 43篇原创内容 公众号 文末提供原
  • 麦克纳姆轮运动特性分析

    混沌无形 混沌系统是世界本质 xff0c 无形之中存在规律 机器人智能化发展从线性过渡到混沌 xff0c 本号将分享机器人全栈技术 xff08 感知 规划 控制 xff1b 软件 机械 硬件等 xff09 43篇原创内容 公众号 文末提供原
  • 全向轮运动特性分析

    文末提供原文PDF免费下载 期刊论文版式 混沌无形 混沌系统是世界本质 xff0c 无形之中存在规律 机器人智能化发展从线性过渡到混沌 xff0c 本号将分享机器人全栈技术 xff08 感知 规划 控制 xff1b 软件 机械 硬件等 xf
  • matlab稳态和时变卡尔曼滤波器Kalman filter的设计和仿真植物动力学模型案例研究

    最近我们被客户要求撰写关于卡尔曼滤波器的研究报告 xff0c 包括一些图形和统计输出 本案例研究说明了卡尔曼滤波器的设计和仿真 考虑稳态和时变卡尔曼滤波器 植物动力学 考虑一个在输入u n 上有加性高斯噪声w n 此外 xff0c 让 yv
  • 正点原子-操作系统

    正点原子另一个学习网站 http www openedv com docs book videos zdyzshipin 4free zdyz freertos book html
  • 基于stm32的减速直流电机PID算法控制

    本例程采用了HAL库进行项目开发 xff08 主要使用软件CubexMX和keil5 xff09 xff0c 文章末尾会有代码开源 xff0c 欢迎各位对文章进行指正和探讨 基于PID的减速电机控制 一 硬件模块与原理图 1 硬件组成 硬件
  • 记录一次在VM上搭建k8s集群

    安装私有镜像仓库Harbor xff1a https blog csdn net a595077052 article details 119893070 配置k8s的dashboard xff1a https blog csdn net
  • ubuntu 上 docker 安装以及配置VNCserver

    1 install Docker 安装过程 根据docker官网安装过程 根据docker官网 lt a data cke saved href 61 34 https docs docker com install linux docke
  • 【论文阅读笔记】VNect: Real-time 3D human Pose Estimation with a Single RGB

    论文总结 xff1a 文中方法比较简单 xff0c 通过全卷积CNN预测一个heatmap及location map location map实际上就是X Y Z三个方向的坐标图 xff0c 找到关键点的过程为heatmap 中寻找照最大值
  • C++ 曲线平滑

    最近需要将数据通过图表展示出来 xff0c 以便观察其变化趋势 直接展示出来的效果不是很好 xff0c 故在网上查找了很久曲线平滑的方法 xff0c 最终找到了了一套比较适合的方法 xff0c 思路比较简单 xff0c 对于现在的项目来说效
  • (5)传感器驱动程序

    文章目录 前言 1 支持的协议 1 1 I2C 1 2 SPI 1 3 Serial UART 1 4 带有UAVCAN的CAN总线 2 前端 后端分离 3 如何以及何时运行驱动程序代码 4 无人机代码和前端示例 5 UART Serial
  • (11)EKF - (1) 导航综述和调参

    文章目录 前言 1 综述 2 理论 3 调参参数 4 解析日志数据 前言
  • (11)EKF - (1.3) EKF1调参参数

    系列文章目录 11 EKF 1 导航综述和调参 文章目录 系列文章目录 前言 3 1 AHRS EKF USE 3 2 EKF ABIAS PNOISE
  • (11)EKF - (2.7) EKF2调参参数

    系列文章目录 11 EKF 2 EKF2估算系统 文章目录 系列文章目录 前言 7 1 EK2 ENABLE
  • (4)(4.2.4) 连接电调和电机

    文章目录 前言 1 电机顺序图 1 1 四轴旋翼 1 2 三轴旋翼 1 3 翼机 1 4 六轴旋翼 1 5 Y6 1 6 八轴旋翼 1 7 四轴八翼 1 8 六轴十二翼 1 9 自定义框架 1 10 认识顺时针和逆时针的螺旋桨 2 测试电机
  • 【FreeRTOS】FreeRTOS删除任务vTaskDelete()

    使用说明 xff1a 任务中 小时 xff08 任务句柄 t xTask xff09 INCLUDE vTaskDelete必须定义为1 xff0c 才能使用此函数 有关更多信息 xff0c 请参见RTOS配置文档 从RTOS内核管理中删除
  • PyQt5中QSS设置全局StyleSheet的学习

    这阵子在学习PyQt5 xff0c 在学习过程中查阅很多资料和网站内容 现在希望一边学习 xff0c 一边作笔记 xff0c 并分享在互联网上 xff0c 能够给别人带来方便的同时自己也提升得更快 这篇博文主要总结PyQt5中QSS的使用方
  • 【强烈推荐】基于stm32的OLED各种显示实现(含动态图)

    前言 xff1a OLED模块作为人们日常生活中常见屏幕类型之一 xff0c 使用的受众面非常广阔 例如 xff1a 显示各个传感器数值 xff0c 显示精美界面 xff0c 多级化菜单系统等等都不离不开他的身影 可以说学会OLED模块是嵌
  • 医疗保健领域的 7 个拯救生命的 AI 用例。从早期疾病检测到增强医疗决策再到更好的患者治疗效果——这就是人工智能技术如何改变医疗保健行业。

    目录 1 肺部疾病的 AI 辅助胸部 X 线分析 新冠肺炎 肺癌 2 黑色素瘤的皮肤科扫描 3 人工智能和机器学习的 CT 和 MRI 扫描分析 4 人工智能辅助乳腺癌检测 5 数字病理学的人工智能 6 使用自然语言处理实现医疗保健管理任务
  • 定时器和计数器

    1 定时器介绍 1 1 什么是定时器 1 SoC的一种内部外设 2 定时器就是CPU的 闹钟 1 2 什么是计数器 1 定时器就是用计数的原始实现的 2 计数器可以计算外部脉冲个数 1 3 定时器如何工作 1 第1步 xff1a 先设置好定

随机推荐

  • 无人机地面站VS版

    界面如图所示 xff0c 源代码如下 源代码
  • 无人机地面站QT版

    下载链接 源代码
  • 利用递归函数调用方式,将所输入的5个字符,以相反顺序打印出来

    span class hljs keyword void span func span class hljs keyword int span n span class hljs keyword void span main span cl
  • 低通滤波(一阶)

    1 一阶滤波算法的原理 一阶滤波 xff0c 又叫一阶惯性滤波 xff0c 或一阶低通滤波 是使用软件编程实现普通硬件RC低通滤波器的功能 一阶低通滤波的算法公式为 xff1a Y n 61 1 Y n 1 43 X n 式中 xff1a
  • I2C初始化

    一 管脚初始化 由于STM32的硬件初始化比较复杂 xff0c 这里我们采用软件初始化 xff0c 选用端口PB6 PB7 以匿名的I2C初始化程序为例 span class hljs comment I2C GPIO定义 span spa
  • 四轴思路(持续更新)

    遥控器 xff1a 发送数据 MPU6050 xff1a 加速度做一维卡尔曼滤波 xff0c 角速度做一介低通滤波 然后四元数姿态解算 接着串级PID控制平衡 xff0c 外环角度环 xff0c 内环角速度环 高度控制 xff1a 高度和速
  • QT 上位机(网络通信)

    Client类 span class hljs comment span class hljs xmlDocTag span span class hljs xmlDocTag span span class hljs xmlDocTag
  • win10系统如何通过ssh远程登录另一台电脑的deepin linux系统

    1 首先 xff0c 更新一下软件源 xff0c 打开 终端窗口 xff0c 输入 sudo apt get update 2 然后 xff0c 在终端中 xff0c 输入 sudo apt get install openssh serv
  • 基于STM32与ESP8266的太空人WiFi天气时钟(代码开源)

    前言 xff1a 本文为手把手教学ESP8266著名开源项目 太空人WiFi天气时钟 xff0c 不同的是本次项目采用的是STM32 作为MCU 两者开发过程中有因为各自芯片的特点 xff08 时钟频率 xff0c 内存大小等 xff09
  • make menuconfig错误的解决办法

    如果使用make menuconfig的方式配置内核 xff0c 又碰巧系统没有安装ncurses库 xff08 ubuntu系统 默认就没有安装此库 xff09 xff0c 就会出现错误 xff0c 错误信息大体上如下 xff1a Una
  • FileZilla以root用户登录Linux

    一 首先创建root用户的密码 span class hljs built in sudo span passwd root 然后输入要设置的密码 xff0c 然后再输入一次 xff0c 成功 xff01 二 修改配置文件 filezila
  • px4编译

    如果下载速度特别慢 xff0c 可以使用手机的4G网络 位置确定 mkdir p src cd src 开始下载指定版本的px4 xff0c 在这里是v1 8 2版本 git clone b v1 8 2 https github com
  • px4源码----位置估算(position_estimator_inav_params.h)

    pragma once include lt parameters param h gt struct position estimator inav params float w z baro 权重 z轴 气压计位置 0 5 float
  • ubuntu如何把调整cpu策略

    一 安装cpu频率管理软件 sudo apt get install cpufrequtils 二 查看cpu当前状态 cpufreq info 其中available cpufreq governors xff1a performance
  • 树莓派系统介绍

    树莓派是一个微型计算机 xff0c 和普通的电脑没有什么区别 xff0c 只是体积更小 xff0c 只有卡片大小 xff0c 存储能力和计算能力会差一点 xff0c 主要用于学习 xff0c 实验所用 是电脑就要安装操作系统 xff0c 树
  • 小觅摄像头ROS编译错误

    GitHub slightech MYNT EYE ORB SLAM2 Sample Forked from ORB SLAM2 https github com raulmur ORB SLAM2 Forked from ORB SLAM
  • 华为路由器交换机常用命令(随时补充更新)

    一 视图切换 lt huawei gt 用户视图 huawei 系统视图 xff0c 在用户视图状态下输入sys进入 xff0c 在系统视图下输入quit或者return返回用户视图 huawei g0 0 1 端口视图 xff0c 从系统
  • 01路径规划问题的相关理论

    目录 1 旅行商问题 2 有能力约束的车辆路径问题 3 车辆路径主要要素特征 4 约束条件分析 5 带时间窗的车辆路径问题 6 车辆路径问题求解算法 7 小节 1 旅行商问题 旅行商问题 xff08 Traveling Saleman Pr
  • 时序数据库-3-[IoTDB]的安装与使用

    IoTDB官方文档手册 Apache IoTDB xff08 物联网数据库 xff09 是一体化收集 存储 管理与分析物联网时序数据的软件系统 Apache IoTDB 采用轻量式架构 xff0c 具有高性能和丰富的功能 xff0c 并与A
  • 【强烈推荐】基于STM32的TFT-LCD各种显示实现(内容详尽含代码)

    前言 xff1a TFT LCD模块作为人们日常生活中常见屏幕类型之一 xff0c 使用的受众面非常广阔 例如 xff1a 显示各个传感器数值 xff0c 显示精美界面 xff0c 多级化菜单系统等等都不离不开他的身影 可以说学会TFT L