天天看點

STM32F4——TFT-LCD原理及FSMC

TFT-LCD

一、簡介:

    TFT-LCD即薄膜半導體液晶顯示器,依據其尺寸、分辨率和驅動晶片的不同有很多分類,下邊會依據2.8寸320X240分辨率以ILI9341晶片驅動的TFT-LCD做相關介紹。

二、接口:

    子產品采用16位并方式與外部連接配接,其相關接口圖及信号線功能如下:

STM32F4——TFT-LCD原理及FSMC

    CS:TFTLCD片選信号。WR:向TFTLCD寫資料。RD:從TFTLCD讀取資料。D[15:0]:16位資料線。RS:指令/資料标示(0,讀寫指令;1,讀寫資料)。

三、驅動時序:

STM32F4——TFT-LCD原理及FSMC

    對于寫時序:CS拉低做片選,RS表示是要寫資料還是要寫指令,在WR信号線的上升沿擷取資料線D[0:15]上的資料,在寫時序上RD信号線總是處于高電平。對于讀時序同理。

四、驅動流程:

    對于LCD的驅動流程可由下圖表示:

STM32F4——TFT-LCD原理及FSMC

    首先通過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
STM32F4——TFT-LCD原理及FSMC

       對于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

    相關位:EXTMOD:控制是否允許讀寫使用不同的時序。WREN:寫使能。MWID[1:0]:設定資料總線寬。MTYP[1:0]:配置存儲器類型。MBKEN:存儲塊使能。

    2、FSMC_BTRx:SRAM/NOR閃存片選時序寄存器。

STM32F4——TFT-LCD原理及FSMC

    相關位:ACCMOD[1:0]:設定通路模式。DATAST[7:0]:資料保持時間。ADDSET[3:0]:位址建立時間設定。

    3、FSMC_BWTRx:SRAM/NOR閃存寫時序控制器。

STM32F4——TFT-LCD原理及FSMC

    相位位同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的初步認識,後邊還會繼續學習和認識有關其操作和應用。



繼續閱讀