STM32F4——TFT-LCD原理及FSMC

TFT-LCD

一、简介:

    TFT-LCD即薄膜晶体管液晶显示器,依据其尺寸、分辨率和驱动芯片的不同有很多分类,下边会依据2.8寸320X240分辨率以ILI9341芯片驱动的TFT-LCD做相关介绍。

二、接口:

    模块采用16位并方式与外部连接,其相关接口图及信号线功能如下:

STM32F4——TFT-LCD原理及FSMC_第1张图片

    CS:TFTLCD片选信号。WR:向TFTLCD写数据。RD:从TFTLCD读取数据。D[15:0]:16位数据线。RS:命令/数据标示(0,读写命令;1,读写数据)。

三、驱动时序:

STM32F4——TFT-LCD原理及FSMC_第2张图片

    对于写时序:CS拉低做片选,RS表示是要写数据还是要写命令,在WR信号线的上升沿获取数据线D[0:15]上的数据,在写时序上RD信号线总是处于高电平。对于读时序同理。

四、驱动流程:

    对于LCD的驱动流程可由下图表示:

STM32F4——TFT-LCD原理及FSMC_第3张图片

    首先通过LCD_RST引脚做复位,再进行初始化序列,由于本人现阶段水平有限,先不去研究相关初始化序列,所以就直接运用LCD商家给出的初始化序列代码。无论是读写指令,都需要设置好坐标,再做出读写GRAM的相关指令。在随后涉及到颜色数据的相关处理,下边就针对颜色数据做一下相关说明。

    对于颜色的设定也有多种格式,在这里只是针对RGB565颜色格式做说明。RGB565这样看(Red)[5位](Green)[6位](Blue)[5位],组成16位颜色深度。

五、指令:

    对于ILI9341的指令有很多,下边对ILI9341驱动芯片的个别指令做相关说明:1、0XD3:该指令用于读取芯片ID。由此可以依据不同的LCD做相关初始化,做到更好的兼容。2、OX36:存储器访问控制指令,可以在读写数据过程中控制GRAM指针的增长方向,简单说明就是控制像素的扫描方式。3、0X2A:用于设置列地址,也就是在从左到右从上到下的扫描方式下,设置x坐标。即用于设置x坐标的范围。4、0X2B:与OX2A类似,该指令用于设置y坐标。需要说明的是在OX2A和OX2B的控制下就可以在屏幕上开窗显示了。5、0X2C:该指令用于向GRAM中写入颜色数据。6、0X2E:读取GRAM中的颜色数据。


FSMC

一、简介:

    FSMC即灵活静态存储控制器,用于静态存储器的扩展。对于TFT-LCD显示中将LCD当做一个SRAM来进行操作,下边会有所介绍。

二、框图结构:

STM32F4——TFT-LCD原理及FSMC_第4张图片

STM32F4——TFT-LCD原理及FSMC_第5张图片

       对于FSMC的引脚已经在框图中有所标注,对于下边的存储块的图是FSMC对其存储器的管理方式,每个存储块大小为256M,由于LCD是要当做SRAM来处理的,那么就要有存储块1的扩展来使得LCD工作。存储块1由28根地址线(HADDR[27:0])寻址,由于每个存储块分为四个区,HADDR[27:26]用于做相关区域选择。注意:由于在这里LCD作为一个16位宽的存储器,而内部的存储是以字节为单位的,则需要HADDR[25:1]对应到FSMC[24:0],即相当于内部的存储地址除以2(右移1位),从而与外部地址对应上。

三、相关寄存器:

    1、FSMC_BCRx:SRAM/NOR闪存片选控制寄存器。

STM32F4——TFT-LCD原理及FSMC_第6张图片

    相关位:EXTMOD:控制是否允许读写使用不同的时序。WREN:写使能。MWID[1:0]:设置数据总线宽。MTYP[1:0]:配置存储器类型。MBKEN:存储块使能。

    2、FSMC_BTRx:SRAM/NOR闪存片选时序寄存器。

    相关位:ACCMOD[1:0]:设置访问模式。DATAST[7:0]:数据保持时间。ADDSET[3:0]:地址建立时间设置。

    3、FSMC_BWTRx:SRAM/NOR闪存写时序控制器。

    相位位同FSMC_BTRx对应位相同。


TFT-LCD显示部分代码参考   

一、LCD地址结构体分析

//LCD地址结构体
typedef struct
{
	u16 LCD_REG;
	u16 LCD_RAM;
} LCD_TypeDef;
//使用NOR/SRAM的 Bank1.sector4,地址位HADDR[27,26]=11 A6作为数据命令区分线 
//注意设置时STM32内部会右移一位对其! 111 1110=0X7E			    
#define LCD_BASE        ((u32)(0x6C000000 | 0x0000007E))
#define LCD             ((LCD_TypeDef *) LCD_BASE)

    由于存储块1的第4区的起始地址为0X6C000000,又由于A6作为数据和命令的区分线,根据代码可得:LCD是将LCD_BASE地址经过强制类型转换,装换为结构体类型,由此知:LCD_REG表示的为写命令地址,LCD_RAM表示为写数据地址。

二、LCD重要参数结构体

//LCD重要参数集
typedef struct  
{										    
	u16 width;			//LCD 宽度
	u16 height;			//LCD 高度
	u16 id;				//LCD ID
	u8  dir;			//横屏还是竖屏控制:0,竖屏;1,横屏。	
	u16 wramcmd;		        //开始写gram指令
	u16 setxcmd;		        //设置x坐标指令
	u16 setycmd;		        //设置y坐标指令 
}_lcd_dev; 

三、读写寄存器或数据函数(底层接口函数)

//写寄存器函数
//regval:寄存器值
void LCD_WR_REG(vu16 regval)
{   
	regval=regval;		//使用-O2优化的时候,必须插入的延时
	LCD->LCD_REG=regval;    //写入要写的寄存器序号	 
}
//写LCD数据
//data:要写入的值
void LCD_WR_DATA(vu16 data)
{	 
	data=data;			//使用-O2优化的时候,必须插入的延时
	LCD->LCD_RAM=data;		 
}
//读LCD数据
//返回值:读到的值
u16 LCD_RD_DATA(void)
{
	vu16 ram;			//防止被优化
	ram=LCD->LCD_RAM;	
	return ram;	 
}					   
//写寄存器
//LCD_Reg:寄存器地址
//LCD_RegValue:要写入的数据
void LCD_WriteReg(u16 LCD_Reg,u16 LCD_RegValue)
{	
	LCD->LCD_REG = LCD_Reg;		//写入要写的寄存器序号	 
	LCD->LCD_RAM = LCD_RegValue;//写入数据	    		 
}	   
//读寄存器
//LCD_Reg:寄存器地址
//返回值:读到的数据
u16 LCD_ReadReg(u16 LCD_Reg)
{										   
	LCD_WR_REG(LCD_Reg);		//写入要读的寄存器序号
	delay_us(5);		  
	return LCD_RD_DATA();		//返回读到的值
}   
//开始写GRAM
void LCD_WriteRAM_Prepare(void)
{
 	LCD->LCD_REG=lcddev.wramcmd;	  
}	 
//LCD写GRAM
//RGB_Code:颜色值
void LCD_WriteRAM(u16 RGB_Code)
{							    
	LCD->LCD_RAM = RGB_Code;//写十六位GRAM
}

四、设置坐标(根据不同的芯片选择不同的设置方式)

//设置光标位置
//Xpos:横坐标
//Ypos:纵坐标
void LCD_SetCursor(u16 Xpos, u16 Ypos)
{	 
 	if(lcddev.id==0X9341||lcddev.id==0X5310)
	{		    
		LCD_WR_REG(lcddev.setxcmd); 
		LCD_WR_DATA(Xpos>>8);LCD_WR_DATA(Xpos&0XFF); 			 
		LCD_WR_REG(lcddev.setycmd); 
		LCD_WR_DATA(Ypos>>8);LCD_WR_DATA(Ypos&0XFF); 		
	}else if(lcddev.id==0X6804)
	{
		if(lcddev.dir==1)Xpos=lcddev.width-1-Xpos;//横屏时处理
		LCD_WR_REG(lcddev.setxcmd); 
		LCD_WR_DATA(Xpos>>8);LCD_WR_DATA(Xpos&0XFF); 
		LCD_WR_REG(lcddev.setycmd); 
		LCD_WR_DATA(Ypos>>8);LCD_WR_DATA(Ypos&0XFF); 
	}else if(lcddev.id==0X1963)
	{  			 		
		if(lcddev.dir==0)//x坐标需要变换
		{
			Xpos=lcddev.width-1-Xpos;
			LCD_WR_REG(lcddev.setxcmd); 
			LCD_WR_DATA(0);LCD_WR_DATA(0); 		
			LCD_WR_DATA(Xpos>>8);LCD_WR_DATA(Xpos&0XFF);		 	 
		}else
		{
			LCD_WR_REG(lcddev.setxcmd); 
			LCD_WR_DATA(Xpos>>8);LCD_WR_DATA(Xpos&0XFF); 		
			LCD_WR_DATA((lcddev.width-1)>>8);LCD_WR_DATA((lcddev.width-1)&0XFF);		 	 			
		}	
		LCD_WR_REG(lcddev.setycmd); 
		LCD_WR_DATA(Ypos>>8);LCD_WR_DATA(Ypos&0XFF); 		
		LCD_WR_DATA((lcddev.height-1)>>8);LCD_WR_DATA((lcddev.height-1)&0XFF); 			 		
		
	}else if(lcddev.id==0X5510)
	{
		LCD_WR_REG(lcddev.setxcmd);LCD_WR_DATA(Xpos>>8); 		
		LCD_WR_REG(lcddev.setxcmd+1);LCD_WR_DATA(Xpos&0XFF);			 
		LCD_WR_REG(lcddev.setycmd);LCD_WR_DATA(Ypos>>8);  		
		LCD_WR_REG(lcddev.setycmd+1);LCD_WR_DATA(Ypos&0XFF);			
	}else
	{
		if(lcddev.dir==1)Xpos=lcddev.width-1-Xpos;//横屏其实就是调转x,y坐标
		LCD_WriteReg(lcddev.setxcmd, Xpos);
		LCD_WriteReg(lcddev.setycmd, Ypos);
	}	 
}

五、画点函数

//画点
//x,y:坐标
//POINT_COLOR:此点的颜色
void LCD_DrawPoint(u16 x,u16 y)
{
	LCD_SetCursor(x,y);		//设置光标位置 
	LCD_WriteRAM_Prepare();	//开始写入GRAM
	LCD->LCD_RAM=POINT_COLOR; 
}
//快速画点
//x,y:坐标
//color:颜色
void LCD_Fast_DrawPoint(u16 x,u16 y,u16 color)
{	   
	if(lcddev.id==0X9341||lcddev.id==0X5310)
	{
		LCD_WR_REG(lcddev.setxcmd); 
		LCD_WR_DATA(x>>8);LCD_WR_DATA(x&0XFF);  			 
		LCD_WR_REG(lcddev.setycmd); 
		LCD_WR_DATA(y>>8);LCD_WR_DATA(y&0XFF); 		 	 
	}else if(lcddev.id==0X5510)
	{
		LCD_WR_REG(lcddev.setxcmd);LCD_WR_DATA(x>>8);  
		LCD_WR_REG(lcddev.setxcmd+1);LCD_WR_DATA(x&0XFF);	  
		LCD_WR_REG(lcddev.setycmd);LCD_WR_DATA(y>>8);  
		LCD_WR_REG(lcddev.setycmd+1);LCD_WR_DATA(y&0XFF); 
	}else if(lcddev.id==0X1963)
	{
		if(lcddev.dir==0)x=lcddev.width-1-x;
		LCD_WR_REG(lcddev.setxcmd); 
		LCD_WR_DATA(x>>8);LCD_WR_DATA(x&0XFF); 		
		LCD_WR_DATA(x>>8);LCD_WR_DATA(x&0XFF); 		
		LCD_WR_REG(lcddev.setycmd); 
		LCD_WR_DATA(y>>8);LCD_WR_DATA(y&0XFF); 		
		LCD_WR_DATA(y>>8);LCD_WR_DATA(y&0XFF); 		
	}else if(lcddev.id==0X6804)
	{		    
		if(lcddev.dir==1)x=lcddev.width-1-x;//横屏时处理
		LCD_WR_REG(lcddev.setxcmd); 
		LCD_WR_DATA(x>>8);LCD_WR_DATA(x&0XFF);			 
		LCD_WR_REG(lcddev.setycmd); 
		LCD_WR_DATA(y>>8);LCD_WR_DATA(y&0XFF); 		
	}else
	{
 		if(lcddev.dir==1)x=lcddev.width-1-x;//横屏其实就是调转x,y坐标
		LCD_WriteReg(lcddev.setxcmd,x);
		LCD_WriteReg(lcddev.setycmd,y);
	}			 
	LCD->LCD_REG=lcddev.wramcmd; 
	LCD->LCD_RAM=color; 
}

    两个函数一个是调用函数,一个是直接快速设置。功能相同。

六、读点函数

//读取个某点的颜色值	 
//x,y:坐标
//返回值:此点的颜色
u16 LCD_ReadPoint(u16 x,u16 y)
{
 	u16 r=0,g=0,b=0;
	if(x>=lcddev.width||y>=lcddev.height)return 0;	//超过了范围,直接返回		   
	LCD_SetCursor(x,y);	    
	if(lcddev.id==0X9341||lcddev.id==0X6804||lcddev.id==0X5310||lcddev.id==0X1963)LCD_WR_REG(0X2E);//9341/6804/3510/1963 发送读GRAM指令
	else if(lcddev.id==0X5510)LCD_WR_REG(0X2E00);	//5510 发送读GRAM指令
	else LCD_WR_REG(0X22);      		 			//其他IC发送读GRAM指令
	if(lcddev.id==0X9320)opt_delay(2);				//FOR 9320,延时2us	    
 	r=LCD_RD_DATA();								//dummy Read	   
	if(lcddev.id==0X1963)return r;					//1963直接读就可以 
	opt_delay(2);	  
 	r=LCD_RD_DATA();  		  						//实际坐标颜色
 	if(lcddev.id==0X9341||lcddev.id==0X5310||lcddev.id==0X5510)		//9341/NT35310/NT35510要分2次读出
 	{
		opt_delay(2);	  
		b=LCD_RD_DATA(); 
		g=r&0XFF;		//对于9341/5310/5510,第一次读取的是RG的值,R在前,G在后,各占8位
		g<<=8;
	} 
	if(lcddev.id==0X9325||lcddev.id==0X4535||lcddev.id==0X4531||lcddev.id==0XB505||lcddev.id==0XC505)return r;	//这几种IC直接返回颜色值
	else if(lcddev.id==0X9341||lcddev.id==0X5310||lcddev.id==0X5510)return (((r>>11)<<11)|((g>>10)<<5)|(b>>11));//ILI9341/NT35310/NT35510需要公式转换一下
	else return LCD_BGR2RGB(r);						//其他IC
}

    这是TFT-LCD显示基本的一些函数,这是有关TFT-LCD的初步认识,后边还会继续学习和认识有关其操作和应用。



你可能感兴趣的:(STM32单片机)