stm32—LCD外設詳解


stm32—LCD外設詳解(5510)

圖像處理,不會用LCD怎么行。本實驗基於正點原子戰艦開發板重新編寫,正點原子的代碼寫的很好,但奈何本新手看了表示一臉懵逼,因此重新編寫,將代碼簡單化,去除操作系統以及兼容性等干擾項。

寫命令函數
//regval:命令
void LCD_WR_REG(u16 regval){   
	LCD->LCD_REG=regval;//寫入命令
}
寫數據函數
//data:要寫入的值
void LCD_WR_DATA(u16 data){	 
	LCD->LCD_RAM=data;		 
}
讀LCD數據函數

這個需要采用volatile防止編譯器優化

u16 LCD_RD_DATA(void){
	vu16 ram;//防止被優化,否則會出現讀出的值為剛寫入的值,這是編譯器優化的結果,也可以用volatile
	ram=LCD->LCD_RAM;	
	return ram;	 
}			
寫寄存器函數
void LCD_WriteReg(u16 LCD_Reg,u16 LCD_RegValue){	
	LCD->LCD_REG = LCD_Reg;		//寫入命令	 
	LCD->LCD_RAM = LCD_RegValue;//寫入數據	    		 
}
讀寄存器函數
u16 LCD_ReadReg(u16 LCD_Reg){										   
	LCD_WR_REG(LCD_Reg);		//寫入命令
	delay_us(5);		  
	return LCD_RD_DATA();		//返回讀到的值
}
寫GRAM准備函數

可以看到5510的寫顯存指令為2C00h

//開始寫GRAM命令
void LCD_WriteRAM_Prepare(void){
 	LCD->LCD_REG=2C00h;	 
}
延時函數

提供不是那么精確的延時

void opt_delay(u8 i){
	while(i--);
}
編寫設置光標函數

x坐標由列地址設置寄存器控制,通過參考手冊可以看到列地址設置寄存器都是低八位有效,而地址為16位數據需要將地址的高八位放入2A00h寄存器的低八位,地址的低八位放入2A01h寄存器的低八位。

y坐標由頁地址設置寄存器控制,指令為2B00h,原理參考x坐標設置。

//設置光標坐標
void LCD_SetCursor(u16 Xpos, u16 Ypos){	 
	LCD_WR_REG(lcddev.setxcmd);LCD_WR_DATA(Xpos>>8); 		 //0X2A00h寫入x的高字節數據
	LCD_WR_REG(lcddev.setxcmd+1);LCD_WR_DATA(Xpos&0XFF);	 //0X2A01h寫入x的低字節數據		 
	LCD_WR_REG(lcddev.setycmd);LCD_WR_DATA(Ypos>>8);  		 //0X2B00h寫入y的高字節數據
	LCD_WR_REG(lcddev.setycmd+1);LCD_WR_DATA(Ypos&0XFF);	 //0X2B01h寫入y的低字節數據	
}
開窗函數

XS為x坐標的起始坐標,XE為X坐標的結束坐標

根據手冊可以發現,輸入0X2A00h指令后輸入XS高八位,輸入0X2A01h指令后輸入XS低八位,輸入0X2A02h指令后輸入XE高八位,輸入0X2A03h指令后輸入XE低八位。

Y軸同理,由此可以設置出X軸的起始坐標與結束坐標,Y軸的起始坐標與結束坐標,這樣就可以畫一個框出來。

void open_windows(u16 x,u16 y,u16 width,u16 heigth){
	LCD_WR_REG(0X2A00);LCD_WR_DATA((x&0xFF00)>>8);
	LCD_WR_REG(0X2A01);LCD_WR_DATA((x&0x00FF));
	LCD_WR_REG(0X2A02);LCD_WR_DATA(((x+width)&0xFF00)>>8);
	LCD_WR_REG(0X2A03);LCD_WR_DATA(((x+width)&0x00FF));
	
	LCD_WR_REG(0X2B00);LCD_WR_DATA((y&0xFF00)>>8);
	LCD_WR_REG(0X2B01);LCD_WR_DATA((y&0x00FF));
	LCD_WR_REG(0X2B02);LCD_WR_DATA(((y+heigth)&0xFF00)>>8);
	LCD_WR_REG(0X2B03);LCD_WR_DATA(((y+heigth)&0x00FF));	
}
畫矩形函數

在開窗后,總得驗證一下吧,先來一個畫矩形函數:0x2C00為寫顯存指令,我這里是以坐標(1.1)為原點,開了一個長寬為100個像素的正方形窗口,開窗后,使用0x2C00向窗口顯存輸入數據為0x00,也就是黑色。

void draw_rectangle(void){
	u16 i;
	open_windows(1,1,100,100);
	LCD_WR_REG((0X2C00);
	for(i=0;i<100*100;i++){
		LCD_WR_DATA(0x00);
	}

顯示結果:

存儲器數據訪問控制

想要讓顯示屏亮起來,還需要規定像素點如何去刷新,也就是是先打印行還是先打印列。這個由命令0x3600指令控制。在輸入指令后還需要輸入一個8字節的數據,來規定掃描方向。

DO:上下翻轉顯示圖像。

D1:左右翻轉顯示圖像。

MH:水平方向控制。

RGB:“0”=RGB顏色序列,“1”=BGR顏色序列。

ML:垂直方向控制。

MV:行列變換,交換X、Y軸

MX:列控制。

MY:行控制。

LCD初始化函數

LCD初始化函數剛開始就是一段廠商提供的初始化代碼,下面是代碼的一部分,這個不需要管,稍作修改復制粘貼進來即可:

void LCD_Init(void){
    /*此處應該有廠方提供的函數,已省略*/
    LCD_Display();
	LCD_LED=1;				//點亮背光
	LCD_Clear(RED);
} 	

先編寫LCD_Display()函數,這個應該很清晰了,開窗,這個開窗是針對整個顯示屏:

	LCD_WR_REG(0X3600);//存儲器數據訪問控制指令,主要控制方向
	LCD_WR_DATA(0x0000);
	LCD_WR_REG(0x2A00);LCD_WR_DATA(0); 	//2A00h XS[15:8]
	LCD_WR_REG(0x2A01);LCD_WR_DATA(0);//2A01h  XS[7:0] 
	LCD_WR_REG(0x2A02);LCD_WR_DATA((u16)(480-1)>>8);		//2A02h	XE[15:8]           
	LCD_WR_REG(0x2A03);LCD_WR_DATA((u16)(480-1)&0XFF);	 //2A03h XE[7:0]
	LCD_WR_REG(0x2B00);LCD_WR_DATA(0); 	//2B00h	YS[15:8]
	LCD_WR_REG(0x2B01);LCD_WR_DATA(0);//2B01h	YS[7:0] 
	LCD_WR_REG(0x2B02);LCD_WR_DATA((u16)(800-1)>>8); 	//2B02h YE[15:8]
	LCD_WR_REG(0x2B03);LCD_WR_DATA((u16)(800-1)&0XFF);	

設置好顯示格式開窗以后,將背光線拉高,設置好第一個點的位置后,開始打點:

void LCD_Clear(u16 color){
	u32 index=0;      
	u32 totalpoint=480*800;//得到總點數	
	LCD_SetCursor(0x00,0x00);	//設置光標開始位置與(0,0) ,這個函數在上面已經寫過了
	LCD->LCD_REG=0x2C00;     		//開始寫入GRAM命令	 	  
	for(index=0;index<totalpoint;index++){
		LCD->LCD_RAM=color;	
	}
}

LCD相關函數設置完以后,設置好GPIO初始化函數:

void GPIO_LCD_Init(void){
	GPIO_InitTypeDef GPIO_InitStructure;
	
			           RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOE|RCC_APB2Periph_GPIOG,ENABLE);//使能PORTB,D,E,G以及AFIO復用功能時鍾

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;				 //PB0 推挽輸出 背光
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽輸出
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
 	//PORTD復用推挽輸出  
	GPIO_InitStructure.GPIO_Pin =     GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_14|GPIO_Pin_15;				 //	//PORTD復用推挽輸出  
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; 		 //復用推挽輸出   
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOD, &GPIO_InitStructure); 
  	 
	//PORTE復用推挽輸出  
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;				 //	//PORTD復用推挽輸出  
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; 		 //復用推挽輸出   
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOE, &GPIO_InitStructure);    	    	 											 
   	//	//PORTG12復用推挽輸出 A0	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_12;	 //	//PORTD復用推挽輸出  
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; 		 //復用推挽輸出   
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOG, &GPIO_InitStructure); 
}

以及FSMC初始化函數:

void FSMC_LCD_Init(void){
	FSMC_NORSRAMInitTypeDef  FSMC_NORSRAMInitStructure;
  FSMC_NORSRAMTimingInitTypeDef  readWriteTiming; 
	FSMC_NORSRAMTimingInitTypeDef  writeTiming;
	
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC,ENABLE);	//使能FSMC時鍾
	
	readWriteTiming.FSMC_AddressSetupTime = 0x01;	 //地址建立時間(ADDSET)為2個HCLK 1/36M=27ns
  readWriteTiming.FSMC_AddressHoldTime = 0x00;	 //地址保持時間(ADDHLD)模式A未用到	
  readWriteTiming.FSMC_DataSetupTime = 0x0f;		 // 數據保存時間為16個HCLK,因為液晶驅動IC的讀數據的時候,速度不能太快,尤其對1289這個IC。
  readWriteTiming.FSMC_BusTurnAroundDuration = 0x00;
  readWriteTiming.FSMC_CLKDivision = 0x00;
  readWriteTiming.FSMC_DataLatency = 0x00;
  readWriteTiming.FSMC_AccessMode = FSMC_AccessMode_A;	 //模式A 
    

	writeTiming.FSMC_AddressSetupTime = 0x00;	 //地址建立時間(ADDSET)為1個HCLK  
  writeTiming.FSMC_AddressHoldTime = 0x00;	 //地址保持時間(A		
  writeTiming.FSMC_DataSetupTime = 0x03;		 ////數據保存時間為4個HCLK	
  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;// FSMC_MemoryType_SRAM;  //SRAM   
  FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;//存儲器數據寬度為16bit   
  FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode =FSMC_BurstAccessMode_Disable;// 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配置

  FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM4, ENABLE);  // 使能BANK1 
}

至此LCD就可以開始工作了,還可以使用矩形函數 draw_rectangle( )在LCD上畫一個矩陣:

這就是最基礎的LCD顯示了


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM