TFTLCD

TDTLCD即薄膜晶體管液晶顯示器,在液晶顯示屏每個像素上都設置有一個薄膜晶體管(TFT),圖像質量高

一般TFTLCD模塊位3.3V供電,不支持5V電壓MCU,如果使用5V MCU需在信號線串接120R電阻使用

LCD使用16位80並口驅動,與OLED並口驅動類似

電容觸摸模塊使用SPI串口驅動

採用廠商提供配置文件或參考數據手冊即可完成LCD和觸摸屏的驅動配置

LCD驅動流程

  1. 硬復位
  2. 發送初始化序列(按照廠家提供設置)
  3. 設置座標
  4. 寫點或讀點
  5. 寫點步驟:
  1. 寫GRAM指令
  2. 寫入顏色數據
  3. LCD顯示點
  1. 讀點步驟
  1. 讀GRAM指令
  2. 讀出顏色數據
  3. 輸出數據給單片機處理

RGB565格式

LCD模塊對外採用16位80並口,顏色深度為16為,格式為RGB565

對應關係如下:5位R、6位G、5位B——RGB565

數據線

D15

D14

D13

D12

D11

D10

D9

D8

D7

D6

D5

D4

D3

D2

D1

D0

LCD GRAM

R[4]

R[3]

R[2]

R[1]

R[0]

G[5]

G[4]

G[3]

G[2]

G[1]

G[0]

B[4]

B[3]

B[2]

B[1]

B[0]

重點代碼

font.h

//這裏省略
//可利用字符點陣畫圖軟件或已有字庫導出對應字符的點陣集
//應#include到lcd.c文件中

lcd.h

#ifndef __LCD_H
#define __LCD_H		
#include "sys.h"	 
#include "stdlib.h"

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

//LCD參數
extern _lcd_dev lcddev;//管理LCD重要參數
extern u16  POINT_COLOR;//畫筆顏色,默認紅色    
extern u16  BACK_COLOR;//背景顏色.默認為白色
/*-----------------LCD重要參數集定義----------------*/
/*-----------------LCD端口定義----------------*/
#define	LCD_LED PBout(15)  		//LCD背光 PB15
/*-----------------LCD端口定義----------------*/
/*-----------------LCD地址定義----------------*/
//LCD地址結構體
typedef struct
{
	vu16 LCD_REG;
	vu16 LCD_RAM;
} LCD_TypeDef;
//使用NOR/SRAM的 Bank1.sector4,地址位HADDR[27,26]=11 A6作為數據命令區分線 
//注意設置時STM32內部會右移一位對其! 111 1110=0X7E			    
#define LCD_BASE        ((u32)(0x6C000000|0x0000007E))
//Bank1Sector4基地址為0x6C000000,A10的偏移地址為0x000007EF
#define LCD             ((LCD_TypeDef *) LCD_BASE)
//將上述地址強制轉換為LCD地址結構體指針,即得到LCD->LCD_REG的地址,實現對RS的控制
/*-----------------LCD地址定義----------------*/

//掃描方向定義
#define L2R_U2D  0 //從左到右,從上到下
#define L2R_D2U  1 //從左到右,從下到上
#define R2L_U2D  2 //從右到左,從上到下
#define R2L_D2U  3 //從右到左,從下到上
#define U2D_L2R  4 //從上到下,從左到右
#define U2D_R2L  5 //從上到下,從右到左
#define D2U_L2R  6 //從下到上,從左到右
#define D2U_R2L  7 //從下到上,從右到左
#define DFT_SCAN_DIR L2R_U2D//默認的掃描方向

//畫筆顏色
#define WHITE         	 0xFFFF
#define BLACK         	 0x0000	  
#define BLUE         	 0x001F  
#define BRED             0XF81F
#define GRED 			 0XFFE0
#define GBLUE			 0X07FF
#define RED           	 0xF800
#define MAGENTA       	 0xF81F
#define GREEN         	 0x07E0
#define CYAN          	 0x7FFF
#define YELLOW        	 0xFFE0
#define BROWN 			 0XBC40 //棕色
#define BRRED 			 0XFC07 //棕紅色
#define GRAY  			 0X8430 //灰色
//GUI顏色
#define DARKBLUE      	 0X01CF	//深藍色
#define LIGHTBLUE      	 0X7D7C	//淺藍色  
#define GRAYBLUE       	 0X5458 //灰藍色
//以上三色為PANEL的顏色 
#define LIGHTGREEN     	 0X841F //淺綠色
//#define LIGHTGRAY        0XEF5B //淺灰色(PANNEL)
#define LGRAY 			 0XC618 //淺灰色(PANNEL),窗體背景色
#define LGRAYBLUE        0XA651 //淺灰藍色(中間層顏色)
#define LBBLUE           0X2B12 //淺棕藍色(選擇條目的反色)
	    															  
void LCD_Init(void);													   	//初始化LCD
void LCD_DisplayOn(void);													//開顯示
void LCD_DisplayOff(void);													//關顯示
void LCD_Clear(u16 Color);	 												//清屏
void LCD_SetCursor(u16 Xpos, u16 Ypos);										//設置光標
void LCD_DrawPoint(u16 x,u16 y);											//畫點
void LCD_Fast_DrawPoint(u16 x,u16 y,u16 color);								//快速畫點
u16  LCD_ReadPoint(u16 x,u16 y); 											//讀點 
void LCD_Draw_Circle(u16 x0,u16 y0,u8 r);						 			//畫圓
void LCD_DrawLine(u16 x1, u16 y1, u16 x2, u16 y2);							//畫線
void LCD_DrawRectangle(u16 x1, u16 y1, u16 x2, u16 y2);		   				//畫矩形
void LCD_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u16 color);		   				//填充單色
void LCD_Color_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u16 *color);				//填充指定顏色
void LCD_ShowChar(u16 x,u16 y,u8 num,u8 size,u8 mode);						//顯示一個字符
void LCD_ShowNum(u16 x,u16 y,u32 num,u8 len,u8 size);  						//顯示一個數字
void LCD_ShowxNum(u16 x,u16 y,u32 num,u8 len,u8 size,u8 mode);				//顯示 數字
void LCD_ShowString(u16 x,u16 y,u16 width,u16 height,u8 size,u8 *p);		//顯示一個字符串,可用12/16字體

void LCD_WriteReg(u16 LCD_Reg, u16 LCD_RegValue);
u16 LCD_ReadReg(u16 LCD_Reg);
void LCD_WriteRAM_Prepare(void);
void LCD_WriteRAM(u16 RGB_Code);

void LCD_SSD_BackLightSet(u8 pwm);							//SSD1963 背光控制
void LCD_Scan_Dir(u8 dir);									//設置屏掃描方向
void LCD_Display_Dir(u8 dir);								//設置屏幕顯示方向
void LCD_Set_Window(u16 sx,u16 sy,u16 width,u16 height);	//設置窗口					   						   																			 
//LCD分辨率設置參數
#define SSD_HOR_RESOLUTION		800		//LCD水平分辨率
#define SSD_VER_RESOLUTION		480		//LCD垂直分辨率
//LCD驅動設置參數
#define SSD_HOR_PULSE_WIDTH		1		//水平脈寬
#define SSD_HOR_BACK_PORCH		46		//水平前廊
#define SSD_HOR_FRONT_PORCH		210		//水平後廊
#define SSD_VER_PULSE_WIDTH		1		//垂直脈寬
#define SSD_VER_BACK_PORCH		23		//垂直前廊
#define SSD_VER_FRONT_PORCH		22		//垂直前廊
//如下幾個參數,自動計算
#define SSD_HT	(SSD_HOR_RESOLUTION+SSD_HOR_BACK_PORCH+SSD_HOR_FRONT_PORCH)
#define SSD_HPS	(SSD_HOR_BACK_PORCH)
#define SSD_VT 	(SSD_VER_RESOLUTION+SSD_VER_BACK_PORCH+SSD_VER_FRONT_PORCH)
#define SSD_VPS (SSD_VER_BACK_PORCH)

#endif

lcd.c

這裏僅寫了顯示函數(讀寫數據函數),LCD控制函數參考正點原子示例文檔

u16 POINT_COLOR=0x0000;//畫筆顏色
u16 BACK_COLOR=0xFFFF;//背景色 
//默認為豎屏
_lcd_dev lcddev;

//寫寄存器函數
//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
}
//從ILI93xx讀出的數據為GBR格式,而我們寫入的時候為RGB格式。
//通過該函數轉換
//c:GBR格式的顏色值
//返回值:RGB格式的顏色值
u16 LCD_BGR2RGB(u16 c)
{
	u16  r,g,b,rgb;   
	b=(c>>0)&0x1f;
	g=(c>>5)&0x3f;
	r=(c>>11)&0x1f;	 
	rgb=(b<<11)+(g<<5)+(r<<0);		 
	return(rgb);
}
//當mdk -O1時間優化時需要設置
//延時i
void opt_delay(u8 i)
{
	while(i--);
}
//讀取個某點的顏色值
//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
}

//LCD開啓顯示
void LCD_DisplayOn(void)
{					   
	if(lcddev.id==0X9341||lcddev.id==0X6804||lcddev.id==0X5310||lcddev.id==0X1963)
        LCD_WR_REG(0X29);//開啓顯示
	else if(lcddev.id==0X5510)
        LCD_WR_REG(0X2900);//開啓顯示
	else
        LCD_WriteReg(0X07,0x0173);//開啓顯示
}
//LCD關閉顯示
void LCD_DisplayOff(void)
{	   
	if(lcddev.id==0X9341||lcddev.id==0X6804||lcddev.id==0X5310||lcddev.id==0X1963)
        LCD_WR_REG(0X28);//關閉顯示
	else if(lcddev.id==0X5510)
        LCD_WR_REG(0X2800);//關閉顯示
	else
        LCD_WriteReg(0X07,0x0);//關閉顯示 
}

//畫點
//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; 
}

//清屏函數
//color:要清屏的填充色
void LCD_Clear(u16 color)
{
	u32 index=0;      
	u32 totalpoint=lcddev.width;
	totalpoint*=lcddev.height;//得到總點數
	if((lcddev.id==0X6804)&&(lcddev.dir==1))//6804橫屏的時候特殊處理  
	{						    
 		lcddev.dir=0;	 
 		lcddev.setxcmd=0X2A;
		lcddev.setycmd=0X2B;  	 			
		LCD_SetCursor(0x00,0x0000);//設置光標位置為初始位置
 		lcddev.dir=1;
  		lcddev.setxcmd=0X2B;
		lcddev.setycmd=0X2A;  	 
 	}
    else
        LCD_SetCursor(0x00,0x0000);//設置光標位置
    
	LCD_WriteRAM_Prepare();//開始寫入GRAM	 	  
	for(index=0;index<totalpoint;index++)
	{
		LCD->LCD_RAM=color;	
	}
}

//在指定位置顯示一個字符
//x,y:起始座標
//num:要顯示的字符:" "--->"~"
//size:字體大小 12/16/24
//mode:疊加方式(1)還是非疊加方式(0)
void LCD_ShowChar(u16 x,u16 y,u8 num,u8 size,u8 mode)
{  							  
    u8 temp,t1,t;
	u16 y0=y;
	u8 csize=(size/8+((size%8)?1:0))*(size/2);//得到字體一個字符對應點陣集所佔的字節數	
 	num=num-' ';//得到偏移後的值(ASCII字庫是從空格開始取模,所以-' '就是對應字符的字庫)
    
	for(t=0;t<csize;t++)
	{   
		if(size==12)
            temp=asc2_1206[num][t];//調用1206字體
		else if(size==16)
            temp=asc2_1608[num][t];//調用1608字體
		else if(size==24)
            temp=asc2_2412[num][t];//調用2412字體
		else
            return;//沒有的字庫
        
		for(t1=0;t1<8;t1++)
		{			    
			if(temp&0x80)LCD_Fast_DrawPoint(x,y,POINT_COLOR);
			else if(mode==0)LCD_Fast_DrawPoint(x,y,BACK_COLOR);
			temp<<=1;
			y++;
			if(y>=lcddev.height)
                return;//超區域了
			if((y-y0)==size)
			{
				y=y0;
				x++;
				if(x>=lcddev.width)
                    return;//超區域了
				break;
			}
		}  	 
	}  	    	   	 	  
}   

//顯示數字,高位為0,還是顯示
//x,y:起點座標
//num:數值(0~999999999);	 
//len:長度(即要顯示的位數)
//size:字體大小
//mode:
//[7]:0,不填充;1,填充0.
//[6:1]:保留
//[0]:0,非疊加顯示;1,疊加顯示.
void LCD_ShowxNum(u16 x,u16 y,u32 num,u8 len,u8 size,u8 mode)
{  
	u8 t,temp;
	u8 enshow=0;						   
	for(t=0;t<len;t++)
	{
		temp=(num/LCD_Pow(10,len-t-1))%10;
        
		if(enshow==0&&t<(len-1))
		{
			if(temp==0)
			{
				if(mode&0X80)
                    LCD_ShowChar(x+(size/2)*t,y,'0',size,mode&0X01);  
				else
                    LCD_ShowChar(x+(size/2)*t,y,' ',size,mode&0X01);  
 				continue;
			}
            else
                enshow=1;
		}
	 	LCD_ShowChar(x+(size/2)*t,y,temp+'0',size,mode&0X01); 
	}
}

//顯示字符串
//x,y:起點座標
//width,height:區域大小  
//size:字體大小
//*p:字符串起始地址		  
void LCD_ShowString(u16 x,u16 y,u16 width,u16 height,u8 size,u8 *p)
{         
	u8 x0=x;
	width+=x;
	height+=y;
    while((*p<='~')&&(*p>=' '))//判斷是不是非法字符!
    {       
        if(x>=width)
        {
            x=x0;
            y+=size;
        }
        
        if(y>=height)
            break;//退出
        
        LCD_ShowChar(x,y,*p,size,0);
        x+=size/2;
        p++;
    }
}

lcd.c

這裏用偽代碼説明LCD初始化函數內容,詳細內容見例程

//LCD初始化函數
void LCD_init(void)
{
    vu32 i=0;

	GPIO_InitTypeDef GPIO_InitStructure;
	FSMC_NORSRAMInitTypeDef FSMC_NORSRAMInitStructure;
	FSMC_NORSRAMTimingInitTypeDef readWriteTiming; 
	FSMC_NORSRAMTimingInitTypeDef writeTiming;
    
    /*初始化GPIO和FSMC*/

    //使能PD,PE,PF,PG和FSMC時鐘
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB|\
                           RCC_AHB1Periph_GPIOD|\
                           RCC_AHB1Periph_GPIOE|\
                           RCC_AHB1Periph_GPIOF|\
                           RCC_AHB1Periph_GPIOG, ENABLE); 
	RCC_AHB3PeriphClockCmd(RCC_AHB3Periph_FSMC,ENABLE);

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;//PB15 推輓輸出,控制背光
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通輸出
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推輓輸出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//100MHz
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//內部上拉
    //應用設置
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	GPIO_InitStructure.GPIO_Pin = (3<<0)|(3<<4)|(7<<8)|(3<<14);//PD0,1,4,5,8,9,10,14,15複用輸出
    //應用設置
	GPIO_Init(GPIOD, &GPIO_InitStructure);  
	GPIO_InitStructure.GPIO_Pin = (0X1FF<<7);//PE7~15複用輸出
    //應用設置
	GPIO_Init(GPIOE, &GPIO_InitStructure); 
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;//PF12,FSMC_A6
    //應用設置
	GPIO_Init(GPIOF, &GPIO_InitStructure);  
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;//PF12,FSMC_A6
    //應用設置
	GPIO_Init(GPIOG, &GPIO_InitStructure); 

    /*設置引腳複用*/
	GPIO_PinAFConfig(GPIOD,GPIO_PinSource0,GPIO_AF_FSMC);//PD0,AF12
	GPIO_PinAFConfig(GPIOD,GPIO_PinSource1,GPIO_AF_FSMC);//PD1,AF12
	GPIO_PinAFConfig(GPIOD,GPIO_PinSource4,GPIO_AF_FSMC);
	GPIO_PinAFConfig(GPIOD,GPIO_PinSource5,GPIO_AF_FSMC); 
	GPIO_PinAFConfig(GPIOD,GPIO_PinSource8,GPIO_AF_FSMC); 
	GPIO_PinAFConfig(GPIOD,GPIO_PinSource9,GPIO_AF_FSMC);
	GPIO_PinAFConfig(GPIOD,GPIO_PinSource10,GPIO_AF_FSMC);
	GPIO_PinAFConfig(GPIOD,GPIO_PinSource14,GPIO_AF_FSMC);
	GPIO_PinAFConfig(GPIOD,GPIO_PinSource15,GPIO_AF_FSMC);//PD15,AF12

	GPIO_PinAFConfig(GPIOE,GPIO_PinSource7,GPIO_AF_FSMC);//PE7,AF12
	GPIO_PinAFConfig(GPIOE,GPIO_PinSource8,GPIO_AF_FSMC);
	GPIO_PinAFConfig(GPIOE,GPIO_PinSource9,GPIO_AF_FSMC);
	GPIO_PinAFConfig(GPIOE,GPIO_PinSource10,GPIO_AF_FSMC);
	GPIO_PinAFConfig(GPIOE,GPIO_PinSource11,GPIO_AF_FSMC);
	GPIO_PinAFConfig(GPIOE,GPIO_PinSource12,GPIO_AF_FSMC);
	GPIO_PinAFConfig(GPIOE,GPIO_PinSource13,GPIO_AF_FSMC);
	GPIO_PinAFConfig(GPIOE,GPIO_PinSource14,GPIO_AF_FSMC);
	GPIO_PinAFConfig(GPIOE,GPIO_PinSource15,GPIO_AF_FSMC);//PE15,AF12

	GPIO_PinAFConfig(GPIOF,GPIO_PinSource12,GPIO_AF_FSMC);//PF12,AF12
	GPIO_PinAFConfig(GPIOG,GPIO_PinSource12,GPIO_AF_FSMC);

    /*讀寫時序初始化*/
	readWriteTiming.FSMC_AddressSetupTime=0XF;//地址建立時間(ADDSET)為16個HCLK 1/168M=6ns*16=96ns
	readWriteTiming.FSMC_AddressHoldTime=0x00;//地址保持時間(ADDHLD)模式A未用到	
	readWriteTiming.FSMC_DataSetupTime=60;//數據保存時間為60個HCLK=6*60=360ns
	readWriteTiming.FSMC_BusTurnAroundDuration=0x00;
	readWriteTiming.FSMC_CLKDivision=0x00;
	readWriteTiming.FSMC_DataLatency=0x00;
	readWriteTiming.FSMC_AccessMode=FSMC_AccessMode_A;//模式A
    /*寫時序初始化*/
	writeTiming.FSMC_AddressSetupTime =9;//地址建立時間(ADDSET)為9個HCLK =54ns 
	writeTiming.FSMC_AddressHoldTime = 0x00;//地址保持時間(A		
	writeTiming.FSMC_DataSetupTime = 8;//數據保存時間為6ns*9個HCLK=54ns
	writeTiming.FSMC_BusTurnAroundDuration = 0x00;
	writeTiming.FSMC_CLKDivision = 0x00;
	writeTiming.FSMC_DataLatency = 0x00;
	writeTiming.FSMC_AccessMode = FSMC_AccessMode_A;//模式A 

	FSMC_NORSRAMInitStructure.FSMC_Bank=FSMC_Bank1_NORSRAM4;//NE4對應BTCR[6],[7]
	FSMC_NORSRAMInitStructure.FSMC_DataAddressMux=FSMC_DataAddressMux_Disable;//不使用數據地址複用
	FSMC_NORSRAMInitStructure.FSMC_MemoryType=FSMC_MemoryType_SRAM;//外部SRAM操作
	FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth=FSMC_MemoryDataWidth_16b;//存儲器數據寬度為16位
	FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode=FSMC_BurstAccessMode_Disable;//不使用突發訪問模式
	FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity=FSMC_WaitSignalPolarity_Low;
	FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait=FSMC_AsynchronousWait_Disable;
	FSMC_NORSRAMInitStructure.FSMC_WrapMode=FSMC_WrapMode_Disable;
	FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive=FSMC_WaitSignalActive_BeforeWaitState;
	FSMC_NORSRAMInitStructure.FSMC_WriteOperation=FSMC_WriteOperation_Enable;//存儲器寫使能
	FSMC_NORSRAMInitStructure.FSMC_WaitSignal=FSMC_WaitSignal_Disable;
	FSMC_NORSRAMInitStructure.FSMC_ExtendedMode=FSMC_ExtendedMode_Enable;//讀寫使用不同時序
	FSMC_NORSRAMInitStructure.FSMC_WriteBurst=FSMC_WriteBurst_Disable;
	FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct=&readWriteTiming;//讀寫時序生效
	FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct=&writeTiming;//寫時序生效
	//應用設置
	FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure);

	FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM4, ENABLE);//使能BANK1 
	
    /*讀取LCD ID並進行核驗*/
 	delay_ms(50);
 	LCD_WriteReg(0x0000,0x0001);
	delay_ms(50);
  	lcddev.id=LCD_ReadReg(0x0000);   

    printf("LCD ID:%x\r\n",lcddev,id);//在串口1打印LCD ID
    
根據不同ID執行不同的LCD初始化代碼;//全都是if...else if... else if...else...
    
    LCD_Display_Dir(0);//默認為豎屏
	LCD_LED=1;//點亮背光
	LCD_Clear(WHITE);//清屏,準備顯示
}

特殊情況:使用GPIO連接LCD

在不使用FSMC的情況下,可以使用GPIO模擬FSMC讀取/寫入GRAM

直接操作GPIO會佔用CPU且速度較慢,但可作為替代方式

FSMC

FSMC(Flexible Static Memory Controller)即“靈活的靜態存儲控制器”,能夠與同步/異步存儲器和16位PC存儲器卡連接,STM32的FSMC接口支持SRAM、NANDFLASH、NORFLASH、PSRAM等存儲器,對f407來説不支持SDRAM,由HCLK直接提供時鐘,能夠設置FSMC中斷

FSMC驅動外部SRAM時,一般通過地址線(A0~A25)、數據線(D0~D15)、寫信號(WE/WR)、讀信號(OD/RD)、片選信號(CS)、UB/LB信號(僅支持字節控制的SRAM可使用)進行控制

FSMC控制TFTLCD

將LCD的GRAM看作外部SRAM即可使用FSMC控制LCD

TFTLCD通過RS信號決定傳輸的數據是數據還是命令,即可把LCD映射為一個具有2個地址的SRAM,向其中存入數據就等效為向LCD發送指令或發送數據

FSMC外設接口

stm32f4的FSMC支持8/16/32位數據寬度,自動將外部存儲器劃分為固定大小為256M字節的四個存儲塊

如下所示

地址

存儲塊

支持的存儲器類型

6000 0000H

塊1

NORFLASH/PSRAM

6FFF FFFFH

4*64MB

NORFLASH/PSRAM

7000 0000H

塊2

NANDFLASH

7FFF FFFFH

4*64MB

NANDFLASH

8000 0000H

塊3

NANDFLASH

8FFF FFFFH

4*64MB

NANDFLASH

9000 0000H

塊4

PC卡

9FFF FFFFH

4*64MB

PC卡

存儲塊1

用於驅動NORFLASH/PSRAM/SRAM

Bank1被劃分為4個區,每個區64MB,都有獨立的寄存器進行配置,由28根地址線(HADDR[27:0])尋址,HADDR是內部AHB地址總線的外延,其中HADDR[25:0]來自外部存儲器地址FSMC_A[25:0]HADDR[26:27]對四個區進行尋址

當外接16位存儲器,HADDR[25:1]->FAMC_A[24:0](右移1位對齊 即 除以2)

當外接8位存儲器,HADDR[25:0]->FAMC_A[25:0]

不管外接8位還是16位寬設備,FSMC_A[0]永遠接在外部設備地址A[0]

支持多種異步突發訪問模式,詳見芯片手冊

存儲塊2、3

用於驅動NANDFLASH

存儲塊4

用於驅動PC卡

詳見數據手冊

寄存器庫函數封裝

在標準庫中,ST將FSMC_BCRx FSMC_BTRx FSMC_BWTRx三個單獨寄存器進行組合封裝

  1. FSMC_BCRx和FSMC_BTRx組合成BTCR[8]寄存器數組

BTCR[0]

FSMC_BCR1

對應關係

BTCR[1]

FSMC_BTR1

BTCR[2]

FSMC_BCR2

BTCR[3]

FSMC_BTR2

BTCR[4]

FSMC_BCR3

BTCR[5]

FSMC_BTR3

BTCR[6]

FSMC_BCR4

BTCR[7]

FSMC_BTR4

  1. 將FSMC_BWTRx組合位BWTR[7]數組

BWTR[0]對應FSMC_BWTR1

BWTR[2]對應FSMC_BWTR2

BWTR[4]對應FSMC_BWTR3

BWTR[6]對應FSMC_BWTR4

BWTR[1]、BWTR[3]、BWTR[5]保留,未用到

外部SRAM操作

IS62WV51216ALL參數簡介

IS62WV51216是ISSI(Intergrated Silicon Solution,Inc)公司生產的16位寬512K(512*16=1MB)容量的CMOS靜態內存(SRAM)芯片

特點:

  1. 高速(45ns/55ns訪問速度)
  2. 低功耗(操作電流36mW,待機12uW)
  3. 兼容TTL電平
  4. 全靜態操作(SRAM特點,不需要刷新和時鐘電路)
  5. 支持三態輸出
  6. 字節控制(支持高/低字節控制)

引腳:

  1. A0~A18共19根地址線(2^19=512K)
  2. IO0~IO15共16根數據線
  3. CS1、CS2:片選信號,其中CS1低電平有效,CS2高電平有效
  4. OE:輸出使能信號(讀使能信號)
  5. WE:輸入使能信號(寫使能信號)
  6. UB、LB:高字節/低字節控制信號,低電平有效

讀寫時序參考芯片數據手冊

示例項目使用55ns的IS62WV51216,讀週期時間tRC=寫週期時間tWC=55ns;讀取尋址時間tAA=55ns(MAX);寫入尋址時間tAA=0ns;讀信號OE建立時間tOE=25ns(MAX);寫信號WE建立時間tPWE=45ns

硬件連接

連接方式

數據線

地址線

讀使能

寫使能

片選

高字節控制

低字節控制

SRAM

IO0~15

A0~18

OE

WE

CS

UB

LB

FSMC

D0~15

A0~18

OE

WE

CS

UB

LB

軟件配置

sram.c

#include "sram.h"

//使用NOR/SRAM的Bank1.sector3,地址位HADDR[27,26]=10
//對IS61LV25616/IS62WV25616,地址線範圍為A0~A17
//對IS61LV51216/IS62WV51216,地址線範圍為A0~A18
#define Bank1_SRAM3_ADDR ((u32)(0x68000000))
  						   
//初始化FSMC與外部SRAM
void FSMC_SRAM_Init(void)
{	
	GPIO_InitTypeDef GPIO_InitStructure;
	FSMC_NORSRAMInitTypeDef FSMC_NORSRAMInitStructure;
  	FSMC_NORSRAMTimingInitTypeDef readWriteTiming; 

    //使能PB、PD、PE、PF、PG與FSMC時鐘
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB|\
                 		   RCC_AHB1Periph_GPIOD|\
                           RCC_AHB1Periph_GPIOE|\
                           RCC_AHB1Periph_GPIOF|\
                           RCC_AHB1Periph_GPIOG, ENABLE);  
  	RCC_AHB3PeriphClockCmd(RCC_AHB3Periph_FSMC,ENABLE);

    //PB15推輓輸出,控制背光
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;//PB15 
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通輸出模式
  	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推輓輸出
  	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//100MHz
  	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//內部上拉
    //應用設置
 	GPIO_Init(GPIOB, &GPIO_InitStructure);
	GPIO_InitStructure.GPIO_Pin = (3<<0)|(3<<4)|(0XFF<<8);//PD0,1,4,5,8~15 AF OUT
    //應用設置
  	GPIO_Init(GPIOD, &GPIO_InitStructure); 
  	GPIO_InitStructure.GPIO_Pin = (3<<0)|(0X1FF<<7);//PE0,1,7~15,AF OUT
    //應用設置
  	GPIO_Init(GPIOE, &GPIO_InitStructure);
 	GPIO_InitStructure.GPIO_Pin = (0X3F<<0)|(0XF<<12);//PF0~5,12~15
    //應用設置
  	GPIO_Init(GPIOF, &GPIO_InitStructure);  
	GPIO_InitStructure.GPIO_Pin =(0X3F<<0)| GPIO_Pin_10;//PG0~5,10
	//應用設置
  	GPIO_Init(GPIOG, &GPIO_InitStructure);
 
    /*讀寫時序設置*/
    readWriteTiming.FSMC_AddressSetupTime=0x00;//地址建立時間(ADDSET)為1個HCLK 1/36M=27ns
  	readWriteTiming.FSMC_AddressHoldTime=0x00;//地址保持時間(ADDHLD)模式A未用到
  	readWriteTiming.FSMC_DataSetupTime=0x08;//數據保持時間(DATAST)為9個HCLK 6*9=54ns	 	 
  	readWriteTiming.FSMC_BusTurnAroundDuration=0x00;
  	readWriteTiming.FSMC_CLKDivision=0x00;
  	readWriteTiming.FSMC_DataLatency=0x00;
  	readWriteTiming.FSMC_AccessMode=FSMC_AccessMode_A;//模式A 
    
    /*配置引腳複用*/
  	GPIO_PinAFConfig(GPIOD,GPIO_PinSource0,GPIO_AF_FSMC);//PD0,AF12
  	GPIO_PinAFConfig(GPIOD,GPIO_PinSource1,GPIO_AF_FSMC);//PD1,AF12
  	GPIO_PinAFConfig(GPIOD,GPIO_PinSource4,GPIO_AF_FSMC);
  	GPIO_PinAFConfig(GPIOD,GPIO_PinSource5,GPIO_AF_FSMC); 
  	GPIO_PinAFConfig(GPIOD,GPIO_PinSource8,GPIO_AF_FSMC); 
  	GPIO_PinAFConfig(GPIOD,GPIO_PinSource9,GPIO_AF_FSMC);
  	GPIO_PinAFConfig(GPIOD,GPIO_PinSource10,GPIO_AF_FSMC);
  	GPIO_PinAFConfig(GPIOD,GPIO_PinSource11,GPIO_AF_FSMC);
	GPIO_PinAFConfig(GPIOD,GPIO_PinSource12,GPIO_AF_FSMC);
  	GPIO_PinAFConfig(GPIOD,GPIO_PinSource13,GPIO_AF_FSMC);
  	GPIO_PinAFConfig(GPIOD,GPIO_PinSource14,GPIO_AF_FSMC);
  	GPIO_PinAFConfig(GPIOD,GPIO_PinSource15,GPIO_AF_FSMC);//PD15,AF12
 
  	GPIO_PinAFConfig(GPIOE,GPIO_PinSource0,GPIO_AF_FSMC);
  	GPIO_PinAFConfig(GPIOE,GPIO_PinSource1,GPIO_AF_FSMC);
	GPIO_PinAFConfig(GPIOE,GPIO_PinSource7,GPIO_AF_FSMC);//PE7,AF12
  	GPIO_PinAFConfig(GPIOE,GPIO_PinSource8,GPIO_AF_FSMC);
  	GPIO_PinAFConfig(GPIOE,GPIO_PinSource9,GPIO_AF_FSMC);
  	GPIO_PinAFConfig(GPIOE,GPIO_PinSource10,GPIO_AF_FSMC);
  	GPIO_PinAFConfig(GPIOE,GPIO_PinSource11,GPIO_AF_FSMC);
  	GPIO_PinAFConfig(GPIOE,GPIO_PinSource12,GPIO_AF_FSMC);
  	GPIO_PinAFConfig(GPIOE,GPIO_PinSource13,GPIO_AF_FSMC);
  	GPIO_PinAFConfig(GPIOE,GPIO_PinSource14,GPIO_AF_FSMC);
  	GPIO_PinAFConfig(GPIOE,GPIO_PinSource15,GPIO_AF_FSMC);//PE15,AF12
 
  	GPIO_PinAFConfig(GPIOF,GPIO_PinSource0,GPIO_AF_FSMC);//PF0,AF12
  	GPIO_PinAFConfig(GPIOF,GPIO_PinSource1,GPIO_AF_FSMC);//PF1,AF12
  	GPIO_PinAFConfig(GPIOF,GPIO_PinSource2,GPIO_AF_FSMC);//PF2,AF12
  	GPIO_PinAFConfig(GPIOF,GPIO_PinSource3,GPIO_AF_FSMC);//PF3,AF12
  	GPIO_PinAFConfig(GPIOF,GPIO_PinSource4,GPIO_AF_FSMC);//PF4,AF12
  	GPIO_PinAFConfig(GPIOF,GPIO_PinSource5,GPIO_AF_FSMC);//PF5,AF12
  	GPIO_PinAFConfig(GPIOF,GPIO_PinSource12,GPIO_AF_FSMC);//PF12,AF12
  	GPIO_PinAFConfig(GPIOF,GPIO_PinSource13,GPIO_AF_FSMC);//PF13,AF12
  	GPIO_PinAFConfig(GPIOF,GPIO_PinSource14,GPIO_AF_FSMC);//PF14,AF12
  	GPIO_PinAFConfig(GPIOF,GPIO_PinSource15,GPIO_AF_FSMC);//PF15,AF12
	
  	GPIO_PinAFConfig(GPIOG,GPIO_PinSource0,GPIO_AF_FSMC);
  	GPIO_PinAFConfig(GPIOG,GPIO_PinSource1,GPIO_AF_FSMC);
  	GPIO_PinAFConfig(GPIOG,GPIO_PinSource2,GPIO_AF_FSMC);
  	GPIO_PinAFConfig(GPIOG,GPIO_PinSource3,GPIO_AF_FSMC);
  	GPIO_PinAFConfig(GPIOG,GPIO_PinSource4,GPIO_AF_FSMC);
  	GPIO_PinAFConfig(GPIOG,GPIO_PinSource5,GPIO_AF_FSMC);
  	GPIO_PinAFConfig(GPIOG,GPIO_PinSource10,GPIO_AF_FSMC);

  	FSMC_NORSRAMInitStructure.FSMC_Bank=FSMC_Bank1_NORSRAM3;//NE3對應BTCR[4],[5]
  	FSMC_NORSRAMInitStructure.FSMC_DataAddressMux=FSMC_DataAddressMux_Disable;//關閉數據地址複用
  	FSMC_NORSRAMInitStructure.FSMC_MemoryType=FSMC_MemoryType_SRAM;//操作外部SRAM   
  	FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth=FSMC_MemoryDataWidth_16b;//存儲器數據寬度16位  
  	FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode=FSMC_BurstAccessMode_Disable;//關閉突發訪問模式
  	FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity=FSMC_WaitSignalPolarity_Low;//等待信號電平為低電平
	FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait=FSMC_AsynchronousWait_Disable;//關閉異步等待傳輸
  	FSMC_NORSRAMInitStructure.FSMC_WrapMode=FSMC_WrapMode_Disable;//關閉抓取模式
  	FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive=FSMC_WaitSignalActive_BeforeWaitState;  
  	FSMC_NORSRAMInitStructure.FSMC_WriteOperation=FSMC_WriteOperation_Enable;//存儲器寫使能 
  	FSMC_NORSRAMInitStructure.FSMC_WaitSignal=FSMC_WaitSignal_Disable;//關閉信號等待
  	FSMC_NORSRAMInitStructure.FSMC_ExtendedMode=FSMC_ExtendedMode_Disable;//不使用擴展模式,讀寫使用相同時序
  	FSMC_NORSRAMInitStructure.FSMC_WriteBurst=FSMC_WriteBurst_Disable;//關閉寫突發
  	FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct=&readWriteTiming;
  	FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct=&readWriteTiming;//讀寫使用同步時序,設置生效

  	FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure);//初始化FSMC配置

 	FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM3, ENABLE);//使能BANK1區域3
}
	  														  
//在指定地址(WriteAddr+Bank1_SRAM3_ADDR)開始,連續寫入n個字節.
//pBuffer:字節指針
//WriteAddr:要寫入地址
//n:寫入字節數
void FSMC_SRAM_WriteBuffer(u8* pBuffer,u32 WriteAddr,u32 n)
{
	for(;n!=0;n--)  
	{
        //Bank1_SRAM3_ADDR為((u32)(0x68000000))
		*(vu8*)(Bank1_SRAM3_ADDR+WriteAddr)=*pBuffer;
		WriteAddr++;
		pBuffer++;
	}   
}

//在指定地址((WriteAddr+Bank1_SRAM3_ADDR))開始,連續讀出n個字節.
//pBuffer:字節指針
//ReadAddr:要讀出的起始地址
//n:讀取字節數
//將寫入函數位置調換即可
void FSMC_SRAM_ReadBuffer(u8* pBuffer,u32 ReadAddr,u32 n)
{
	for(;n!=0;n--)  
	{
        //Bank1_SRAM3_ADDR為((u32)(0x68000000))
		*pBuffer++=*(vu8*)(Bank1_SRAM3_ADDR+ReadAddr);    
		ReadAddr++;
	}  
}

sram.h

#ifndef __SRAM_H
	#define __SRAM_H															    
	#include "sys.h"

	void FSMC_SRAM_Init(void);//初始化FSMC
	void FSMC_SRAM_WriteBuffer(u8* pBuffer,u32 WriteAddr,u32 NumHalfwordToWrite);//向外部SRAM寫字節
	void FSMC_SRAM_ReadBuffer(u8* pBuffer,u32 ReadAddr,u32 NumHalfwordToRead);//從外部SRAM讀字節

	void fsmc_sram_test_write(u32 addr,u8 data);//寫SRAM
	u8 fsmc_sram_test_read(u32 addr);//讀SRAM
#endif