1. 程式人生 > >STM32驅動ILI9341控制器控制TFTLCD顯示

STM32驅動ILI9341控制器控制TFTLCD顯示

point 簡化 draw 光標 索引 hit 數字 nbsp 狀態

STM32驅動ILI9341控制器控制TFTLCD顯示

一、用STM32控制TFTLCD顯示的編程方法,在編程驅動TFTLCD液晶顯示器之前,我們先熟悉以下概念:

1、色彩深度,這是一個與TFTLCD顯存對應的概念;所謂色彩深度就是每個像素點需要多少位的RGB

數據表示該點的顏色信息。註意,不同的TFTLCD顯示器的RGB的對應關系不一樣,這個可以在LCD

控制芯片手冊中找到答案。

例: 某LCD顯示支持8、16、24位RGB,這些位數是指該像素點顏色由8、16、24位RGB構成,但是

R\G\B三種顏色各占的位數可以查看數據手冊。

2、TFTLCD的操作分為兩種:

A、對控制寄存器的讀寫操作(即程序員將要操作LCD顯存寄存器的地址設置成可讀或者可寫)。

B、對顯存寄存器的讀寫操作(即讀寫LCD顯存寄存器)。

3、TFTLCD有一個索引寄存器,對控制寄存器操作前,需要對索引寄存器進行定入操作,用以指明

寄存器讀寫是針對那個寄存器的,具體操作步驟如下:

RS為低電平狀態下,寫入兩個字節的數據,第一個字節為零,第二個字節為寄存器索引值。

RS為高電平狀態下,讀取兩個字節數據,第一個字節為高八位,第二個字節為低八位。

二、實驗平臺STM32F103RCT6與ILI9341 TFTLCD驅動模塊

硬件采用 16 位的並方式與外部連接,之所以不采用 8 位的方式,是因為彩屏的數據量比較大,

尤其在顯示圖片的時候,如果用 8 位數據線,就會比 16 位方式慢一倍以上,我們當然希望速

度越快越好,所以我們選擇 16 位的 80 並口。有如下一些信號線:
CS:TFTLCD 片選信號。
WR:向 TFTLCD 寫入數據。
RD:從 TFTLCD 讀取數據。
D[15:0]:16 位雙向數據線。
RST:硬復位 TFTLCD。
RS:命令/數據標誌(0,讀寫命令;1,讀寫數據)。

在 16 位模式下,ILI9341 采用 RGB565 格式存儲顏色數據,接下來看一下ILI9341 的幾個重要命令

1、 0XD3,用於讀取 LCD 控制器的 ID。

2、0X36,這是存儲訪問控制指令,可以控制 ILI9341 存儲器的讀寫方向,簡單的說,就是在連續寫

GRAM 的時候,可以控制 GRAM 指針的增長方向,從而控制顯示方式。

3、0X2A,這是列地址設置指令,在從左到右,從上到下的掃描方式(默認)下面,該指令用於設置

橫坐標(x 坐標)。

4、0X2B,是頁地址設置指令,在從左到右,從上到下的掃描方式(默認)下面,該指令用於設置縱

坐標(y 坐標)。

5、0X2C,該指令是寫 GRAM 指令,在發送該指令之後,我們便可以往 LCD的 GRAM 裏面寫入顏色

數據了,該指令支持連續寫,在收到指令 0X2C 之後,數據有效位寬變為 16 位,我們可以連續寫入

LCD GRAM 值,而 GRAM 的地址將根據 MY/MX/MV 設置的掃描方向進行自增。

6、 0X2E, 該指令是讀 GRAM 指令,用於讀取 ILI9341 的顯存(GRAM)。

三、軟件編程

lcd.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重要參數

該結構體用於保存一些 LCD 重要參數信息,比如 LCD 的長寬、LCD ID(驅動 IC 型號)、
LCD 橫豎屏狀態等,這個結構體雖然占用了 14 個字節的內存,但是卻可以讓我們的驅動函數
支持不同尺寸的 LCD,同時可以實現 LCD 橫豎屏切換等重要功能,所以還是利大於弊的。有
了以上了解,下面我們開始介紹 ILI93xx.c 裏面的一些重要函數。
第一個是 LCD_WR_DATA 函數,該函數在 lcd.h 裏面,通過宏定義的方式申明。該函數通
過 80 並口向 LCD 模塊寫入一個 16 位的數據,使用頻率是最高的,這裏我們采用了宏定義的方
式,以提高速度。其代碼如下

//寫數據函數
#define LCD_WR_DATA(data){\
LCD_RS_SET;\
LCD_CS_CLR;\
DATAOUT(data);\
LCD_WR_CLR;\
LCD_WR_SET;\
LCD_CS_SET;\
}

//寫數據函數
//可以替代LCD_WR_DATAX宏,拿時間換空間.
//data:寄存器值
void LCD_WR_DATAX(u16 data)
{
LCD_RS_SET;
LCD_CS_CLR;
DATAOUT(data);
LCD_WR_CLR;
LCD_WR_SET;
LCD_CS_SET;
}

第三個是 LCD_WR_REG 函數,該函數是通過 8080 並口向 LCD 模塊寫入寄存器命令,因
為該函數使用頻率不是很高,我們不采用宏定義來做(宏定義占用 FLASH 較多),通過 LCD_RS
來標記是寫入命令(LCD_RS=0)還是數據(LCD_RS=1)。該函數代碼如下:
//寫寄存器函數

//data:寄存器值
void LCD_WR_REG(u16 data)
{
LCD_RS_CLR;//寫地址
LCD_CS_CLR;
DATAOUT(data);
LCD_WR_CLR;
LCD_WR_SET;
LCD_CS_SET;
}


既然有寫寄存器命令函數,那就有讀寄存器數據函數。接下來介紹 LCD_RD_DATA 函數,
該函數用來讀取 LCD 控制器的寄存器數據(非 GRAM 數據),該函數代碼如下:

//讀LCD數據
//返回值:讀到的值
u16 LCD_RD_DATA(void)
{
u16 t;
GPIOB->CRL=0X88888888; //PB0-7 上拉輸入
GPIOB->CRH=0X88888888; //PB8-15 上拉輸入
GPIOB->ODR=0X0000; //全部輸出0


LCD_RS_SET;
LCD_CS_CLR;
//讀取數據(讀寄存器時,並不需要讀2次)
LCD_RD_CLR;
if(lcddev.id==0X8989)delay_us(2);//FOR 8989,延時2us
t=DATAIN;
LCD_RD_SET;
LCD_CS_SET;


GPIOB->CRL=0X33333333; //PB0-7 上拉輸出
GPIOB->CRH=0X33333333; //PB8-15 上拉輸出
GPIOB->ODR=0XFFFF; //全部輸出高
return t;
}

以上 4 個函數,用於實現 LCD 基本的讀寫操作,接下來,我們介紹 2 個 LCD 寄存器操作
的函數,LCD_WriteReg 和 LCD_ReadReg,這兩個函數代碼如下:
//寫寄存器

//寫寄存器
//LCD_Reg:寄存器編號
//LCD_RegValue:要寫入的值
void LCD_WriteReg(u16 LCD_Reg,u16 LCD_RegValue)
{
LCD_WR_REG(LCD_Reg);
LCD_WR_DATA(LCD_RegValue);
}


//讀寄存器
//LCD_Reg:寄存器編號
//返回值:讀到的值
u16 LCD_ReadReg(u16 LCD_Reg)
{
LCD_WR_REG(LCD_Reg); //寫入要讀的寄存器號
return LCD_RD_DATA();
}

這兩個函數函數十分簡單,LCD_WriteReg 用於向 LCD 指定寄存器寫入指定數據,而
LCD_ReadReg 則用於讀取指定寄存器的數據,這兩個函數,都只帶一個參數/返回值,所以,
在有多個參數操作(讀取/寫入)的時候,就不適合用這兩個函數了,得另外實現。
第七個要介紹的函數是坐標設置函數,該函數代碼如下:

//設置光標位置
//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==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);
}
}

該函數實現將 LCD 的當前操作點設置到指定坐標(x,y)。因為不同 LCD 的設置方式不一定
完全一樣,所以代碼裏面有好幾個判斷,對不同的驅動 IC 進行不同的設置。
接下來我們介紹第八個函數:畫點函數。該函數實現代碼如下:

//畫點
//x,y:坐標
//POINT_COLOR:此點的顏色
void LCD_DrawPoint(u16 x,u16 y)
{
LCD_SetCursor(x,y);//設置光標位置
LCD_WriteRAM_Prepare();//開始寫入GRAM
LCD_WR_DATA(POINT_COLOR);
}
該函數實現比較簡單,就是先設置坐標,然後往坐標寫顏色。其中 POINT_COLOR 是我們
定義的一個全局變量,用於存放畫筆顏色,順帶介紹一下另外一個全局變量: BACK_COLOR,
該變量代表 LCD 的背景色。LCD_DrawPoint 函數雖然簡單,但是至關重要,其他幾乎所有上
層函數,都是通過調用這個函數實現的。
有了畫點,當然還需要有讀點的函數,第九個介紹的函數就是讀點函數,用於讀取 LCD
的 GRAM, 這裏說明一下,為什麽 OLED 模塊沒做讀 GRAM 的函數,而這裏做了。因為 OLED
模塊是單色的,所需要全部 GRAM 也就 1K 個字節,而 TFTLCD 模塊為彩色的,點數也比 OLED
模塊多很多,以 16 位色計算, 一款 320×240 的液晶,需要 320×240×2 個字節來存儲顏色值,
也就是也需要 150K 字節,這對任何一款單片機來說,都不是一個小數目了。而且我們在圖形
疊加的時候,可以先讀回原來的值,然後寫入新的值,在完成疊加後,我們又恢復原來的值。
這樣在做一些簡單菜單的時候,是很有用的。這裏我們讀取 TFTLCD 模塊數據的函數為
LCD_ReadPoint,該函數直接返回讀到的 GRAM 值。該函數使用之前要先設置讀取的 GRAM
地址,通過 LCD_SetCursor 函數來實現。LCD_ReadPoint 的代碼如下:

//讀取個某點的顏色值
//x,y:坐標
//返回值:此點的顏色
u16 LCD_ReadPoint(u16 x,u16 y)
{
u16 r,g,b;
if(x>=lcddev.width||y>=lcddev.height)return 0;//超過了範圍,直接返回
LCD_SetCursor(x,y);
if(lcddev.id==0X9341||lcddev.id==0X6804||lcddev.id==0X5310)LCD_WR_REG(0X2E);//9341/6804/5310發送 讀GRAM指令
else if(lcddev.id==0X5510)LCD_WR_REG(0X2E00);//5510 發送讀GRAM指令
else LCD_WR_REG(R34); //其他IC發送讀GRAM指令
GPIOB->CRL=0X88888888; //PB0-7 上拉輸入
GPIOB->CRH=0X88888888; //PB8-15 上拉輸入
GPIOB->ODR=0XFFFF; //全部輸出高


LCD_RS_SET;
LCD_CS_CLR;
//讀取數據(讀GRAM時,第一次為假讀)
LCD_RD_CLR;
delay_us(1);//延時1us
LCD_RD_SET;
//dummy READ
LCD_RD_CLR;
delay_us(1);//延時1us
r=DATAIN; //實際坐標顏色
LCD_RD_SET;
if(lcddev.id==0X9341||lcddev.id==0X5310||lcddev.id==0X5510)//9341/NT35310/NT35510要分2次讀出
{
LCD_RD_CLR;
b=DATAIN;//讀取藍色值
LCD_RD_SET;
g=r&0XFF;//對於9341,第一次讀取的是RG的值,R在前,G在後,各占8位
g<<=8;
}else if(lcddev.id==0X6804)
{
LCD_RD_CLR;
LCD_RD_SET;
r=DATAIN;//6804第二次讀取的才是真實值
}
LCD_CS_SET;
GPIOB->CRL=0X33333333; //PB0-7 上拉輸出
GPIOB->CRH=0X33333333; //PB8-15 上拉輸出
GPIOB->ODR=0XFFFF; //全部輸出高
if(lcddev.id==0X9325||lcddev.id==0X4535||lcddev.id==0X4531||lcddev.id==0X8989||lcddev.id==0XB505)

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_ReadPoint 函數中,因為我們的代碼不止支持一種 LCD 驅動器,所以,我們根據
不同的 LCD 驅動器((lcddev.id)型號,執行不同的操作,以實現對各個驅動器兼容,提高函數
的通用性。

第十個要介紹的是字符顯示函數 LCD_ShowChar,該函數同前面 OLED 模塊的字符顯示函
數差不多,但是這裏的字符顯示函數多了一個功能,就是可以以疊加方式顯示,或者以非疊加
方式顯示。疊加方式顯示多用於在顯示的圖片上再顯示字符。非疊加方式一般用於普通的顯示。
該函數實現代碼如下:
//在指定位置顯示一個字符
//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-‘ ‘;//得到偏移後的值
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(x>=lcddev.width)return; //超區域了
if((y-y0)==size)
{
y=y0;
x++;
if(x>=lcddev.width)return; //超區域了
break;
}
}
}
}


在 LCD_ShowChar 函數裏面,我們采用快速畫點函數 LCD_Fast_DrawPoint 來畫點顯示字
符,該函數同 LCD_DrawPoint 一樣,只是帶了顏色參數,且減少了函數調用的時間,詳見本例
程源碼。

最後,我們再介紹一下 TFTLCD 模塊的初始化函數 LCD_Init,該函數先初始化 STM32 與
TFTLCD 連接的 IO 口,並配置 FSMC 控制器,然後讀取 LCD 控制器的型號,根據控制 IC 的
型號執行不同的初始化代碼,其簡化代碼如下:

void LCD_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO, ENABLE); //使能PORTB,C時鐘和AFIO時鐘
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable , ENABLE);//開啟SWD,失能JTAG

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10|GPIO_Pin_9|GPIO_Pin_8|GPIO_Pin_7|GPIO_Pin_6; ///PORTC6~10復用推挽輸出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure); //GPIOC


GPIO_SetBits(GPIOC,GPIO_Pin_10|GPIO_Pin_9|GPIO_Pin_8|GPIO_Pin_7|GPIO_Pin_6);


GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All; // PORTB推挽輸出
GPIO_Init(GPIOB, &GPIO_InitStructure); //GPIOB

GPIO_SetBits(GPIOB,GPIO_Pin_All);


delay_ms(50); // delay 50 ms
LCD_WriteReg(0x0000,0x0001);
delay_ms(50); // delay 50 ms
lcddev.id = LCD_ReadReg(0x0000);
if(lcddev.id<0XFF||lcddev.id==0XFFFF||lcddev.id==0X9300)//讀到ID不正確,新增lcddev.id==0X9300判斷,因為9341在未被復位的情況下會被讀成9300
{
//嘗試9341 ID的讀取
LCD_WR_REG(0XD3);
LCD_RD_DATA(); //dummy read
LCD_RD_DATA(); //讀到0X00
lcddev.id=LCD_RD_DATA(); //讀取93
lcddev.id<<=8;
lcddev.id|=LCD_RD_DATA(); //讀取41
if(lcddev.id!=0X9341) //非9341,嘗試是不是6804
{
LCD_WR_REG(0XBF);
LCD_RD_DATA(); //dummy read
LCD_RD_DATA(); //讀回0X01
LCD_RD_DATA(); //讀回0XD0
lcddev.id=LCD_RD_DATA();//這裏讀回0X68
lcddev.id<<=8;
lcddev.id|=LCD_RD_DATA();//這裏讀回0X04
if(lcddev.id!=0X6804) //也不是6804,嘗試看看是不是NT35310
{
LCD_WR_REG(0XD4);
LCD_RD_DATA(); //dummy read
LCD_RD_DATA(); //讀回0X01
lcddev.id=LCD_RD_DATA(); //讀回0X53
lcddev.id<<=8;
lcddev.id|=LCD_RD_DATA(); //這裏讀回0X10
if(lcddev.id!=0X5310) //也不是NT35310,嘗試看看是不是NT35510
{
LCD_WR_REG(0XDA00);
LCD_RD_DATA(); //讀回0X00
LCD_WR_REG(0XDB00);
lcddev.id=LCD_RD_DATA();//讀回0X80
lcddev.id<<=8;
LCD_WR_REG(0XDC00);
lcddev.id|=LCD_RD_DATA();//讀回0X00
if(lcddev.id==0x8000)lcddev.id=0x5510;//NT35510讀回的ID是8000H,為方便區分,我們強制設置為5510
}
}
}
}
printf(" LCD ID:%x\r\n",lcddev.id); //打印LCD ID
if(lcddev.id==0X9341) //9341初始化
{
LCD_WR_REG(0xCF);
LCD_WR_DATAX(0x00);
LCD_WR_DATAX(0xC1);
LCD_WR_DATAX(0X30);
LCD_WR_REG(0xED);
LCD_WR_DATAX(0x64);
LCD_WR_DATAX(0x03);
LCD_WR_DATAX(0X12);
LCD_WR_DATAX(0X81);
LCD_WR_REG(0xE8);
LCD_WR_DATAX(0x85);
LCD_WR_DATAX(0x10);
LCD_WR_DATAX(0x7A);
LCD_WR_REG(0xCB);
LCD_WR_DATAX(0x39);
LCD_WR_DATAX(0x2C);
LCD_WR_DATAX(0x00);
LCD_WR_DATAX(0x34);
LCD_WR_DATAX(0x02);
LCD_WR_REG(0xF7);
LCD_WR_DATAX(0x20);
LCD_WR_REG(0xEA);
LCD_WR_DATAX(0x00);
LCD_WR_DATAX(0x00);
LCD_WR_REG(0xC0); //Power control
LCD_WR_DATAX(0x1B); //VRH[5:0]
LCD_WR_REG(0xC1); //Power control
LCD_WR_DATAX(0x01); //SAP[2:0];BT[3:0]
LCD_WR_REG(0xC5); //VCM control
LCD_WR_DATAX(0x30); //3F
LCD_WR_DATAX(0x30); //3C
LCD_WR_REG(0xC7); //VCM control2
LCD_WR_DATAX(0XB7);
LCD_WR_REG(0x36); // Memory Access Control
LCD_WR_DATAX(0x48);
LCD_WR_REG(0x3A);
LCD_WR_DATAX(0x55);
LCD_WR_REG(0xB1);
LCD_WR_DATAX(0x00);
LCD_WR_DATAX(0x1A);
LCD_WR_REG(0xB6); // Display Function Control
LCD_WR_DATAX(0x0A);
LCD_WR_DATAX(0xA2);
LCD_WR_REG(0xF2); // 3Gamma Function Disable
LCD_WR_DATAX(0x00);
LCD_WR_REG(0x26); //Gamma curve selected
LCD_WR_DATAX(0x01);
LCD_WR_REG(0xE0); //Set Gamma
LCD_WR_DATAX(0x0F);
LCD_WR_DATAX(0x2A);
LCD_WR_DATAX(0x28);
LCD_WR_DATAX(0x08);
LCD_WR_DATAX(0x0E);
LCD_WR_DATAX(0x08);
LCD_WR_DATAX(0x54);
LCD_WR_DATAX(0XA9);
LCD_WR_DATAX(0x43);
LCD_WR_DATAX(0x0A);
LCD_WR_DATAX(0x0F);
LCD_WR_DATAX(0x00);
LCD_WR_DATAX(0x00);
LCD_WR_DATAX(0x00);
LCD_WR_DATAX(0x00);
LCD_WR_REG(0XE1); //Set Gamma
LCD_WR_DATAX(0x00);
LCD_WR_DATAX(0x15);
LCD_WR_DATAX(0x17);
LCD_WR_DATAX(0x07);
LCD_WR_DATAX(0x11);
LCD_WR_DATAX(0x06);
LCD_WR_DATAX(0x2B);
LCD_WR_DATAX(0x56);
LCD_WR_DATAX(0x3C);
LCD_WR_DATAX(0x05);
LCD_WR_DATAX(0x10);
LCD_WR_DATAX(0x0F);
LCD_WR_DATAX(0x3F);
LCD_WR_DATAX(0x3F);
LCD_WR_DATAX(0x0F);
LCD_WR_REG(0x2B);
LCD_WR_DATAX(0x00);
LCD_WR_DATAX(0x00);
LCD_WR_DATAX(0x01);
LCD_WR_DATAX(0x3f);
LCD_WR_REG(0x2A);
LCD_WR_DATAX(0x00);
LCD_WR_DATAX(0x00);
LCD_WR_DATAX(0x00);
LCD_WR_DATAX(0xef);
LCD_WR_REG(0x11); //Exit Sleep
delay_ms(120);
LCD_WR_REG(0x29); //display on
}

LCD_Display_Dir(0); //默認為豎屏
LCD_LED=1; //點亮背光
LCD_Clear(WHITE);
}

//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重要參數
//LCD的畫筆顏色和背景色
extern u16 POINT_COLOR;//默認紅色
extern u16 BACK_COLOR; //背景顏色.默認為白色


////////////////////////////////////////////////////////////////////
//-----------------LCD端口定義----------------
#define LCD_LED PCout(10) //LCD背光 PC10


/*
#define LCD_CS_SET GPIOC->BSRR=1<<9 //片選端口 PC9
#define LCD_RS_SET GPIOC->BSRR=1<<8 //數據/命令 PC8
#define LCD_WR_SET GPIOC->BSRR=1<<7 //寫數據 PC7
#define LCD_RD_SET GPIOC->BSRR=1<<6 //讀數據 PC6

#define LCD_CS_CLR GPIOC->BRR=1<<9 //片選端口 PC9
#define LCD_RS_CLR GPIOC->BRR=1<<8 //數據/命令 PC8
#define LCD_WR_CLR GPIOC->BRR=1<<7 //寫數據 PC7
#define LCD_RD_CLR GPIOC->BRR=1<<6 //讀數據 PC6
*/


#define LCD_CS_SET GPIO_SetBits(GPIOC,GPIO_Pin_9) //片選端口 PC9
#define LCD_RS_SET GPIO_SetBits(GPIOC,GPIO_Pin_8) //數據/命令 PC8
#define LCD_WR_SET GPIO_SetBits(GPIOC,GPIO_Pin_7) //寫數據 PC7
#define LCD_RD_SET GPIO_SetBits(GPIOC,GPIO_Pin_6) //讀數據 PC6

#define LCD_CS_CLR GPIO_ResetBits(GPIOC,GPIO_Pin_9) //片選端口 PC9
#define LCD_RS_CLR GPIO_ResetBits(GPIOC,GPIO_Pin_8) //數據/命令 PC8
#define LCD_WR_CLR GPIO_ResetBits(GPIOC,GPIO_Pin_7) //寫數據 PC7
#define LCD_RD_CLR GPIO_ResetBits(GPIOC,GPIO_Pin_6) //讀數據 PC6






//PB0~15,作為數據線
//#define DATAOUT(x) GPIOB->ODR=x; //數據輸出
//#define DATAIN GPIOB->IDR; //數據輸入


#define DATAOUT(x) GPIO_Write(GPIOB,x);
#define DATAIN GPIO_ReadInputData(GPIOB)

//////////////////////////////////////////////////////////////////////
//掃描方向定義
#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 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 LGRAY 0XC618 //淺灰色(PANNEL),窗體背景色


#define LGRAYBLUE 0XA651 //淺灰藍色(中間層顏色)
#define LBBLUE 0X2B12 //淺棕藍色(選擇條目的反色)

void LCD_Init(void); //初始化
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_Scan_Dir(u8 dir); //設置屏掃描方向
void LCD_Display_Dir(u8 dir); //設置屏幕顯示方向
void LCD_Set_Window(u16 sx,u16 sy,u16 width,u16 height); //設置窗口


//寫數據函數
#define LCD_WR_DATA(data){\
LCD_RS_SET;\
LCD_CS_CLR;\
DATAOUT(data);\
LCD_WR_CLR;\
LCD_WR_SET;\
LCD_CS_SET;\
}

//9320/9325 LCD寄存器
#define R0 0x00
#define R1 0x01
#define R2 0x02
#define R3 0x03
#define R4 0x04
#define R5 0x05
#define R6 0x06
#define R7 0x07
#define R8 0x08
#define R9 0x09
#define R10 0x0A
#define R12 0x0C
#define R13 0x0D
#define R14 0x0E
#define R15 0x0F
#define R16 0x10
#define R17 0x11
#define R18 0x12
#define R19 0x13
#define R20 0x14
#define R21 0x15
#define R22 0x16
#define R23 0x17
#define R24 0x18
#define R25 0x19
#define R26 0x1A
#define R27 0x1B
#define R28 0x1C
#define R29 0x1D
#define R30 0x1E
#define R31 0x1F
#define R32 0x20
#define R33 0x21
#define R34 0x22
#define R36 0x24
#define R37 0x25
#define R40 0x28
#define R41 0x29
#define R43 0x2B
#define R45 0x2D
#define R48 0x30
#define R49 0x31
#define R50 0x32
#define R51 0x33
#define R52 0x34
#define R53 0x35
#define R54 0x36
#define R55 0x37
#define R56 0x38
#define R57 0x39
#define R59 0x3B
#define R60 0x3C
#define R61 0x3D
#define R62 0x3E
#define R63 0x3F
#define R64 0x40
#define R65 0x41
#define R66 0x42
#define R67 0x43
#define R68 0x44
#define R69 0x45
#define R70 0x46
#define R71 0x47
#define R72 0x48
#define R73 0x49
#define R74 0x4A
#define R75 0x4B
#define R76 0x4C
#define R77 0x4D
#define R78 0x4E
#define R79 0x4F
#define R80 0x50
#define R81 0x51
#define R82 0x52
#define R83 0x53
#define R96 0x60
#define R97 0x61
#define R106 0x6A
#define R118 0x76
#define R128 0x80
#define R129 0x81
#define R130 0x82
#define R131 0x83
#define R132 0x84
#define R133 0x85
#define R134 0x86
#define R135 0x87
#define R136 0x88
#define R137 0x89
#define R139 0x8B
#define R140 0x8C
#define R141 0x8D
#define R143 0x8F
#define R144 0x90
#define R145 0x91
#define R146 0x92
#define R147 0x93
#define R148 0x94
#define R149 0x95
#define R150 0x96
#define R151 0x97
#define R152 0x98
#define R153 0x99
#define R154 0x9A
#define R157 0x9D
#define R192 0xC0
#define R193 0xC1
#define R229 0xE5
#endif

STM32驅動ILI9341控制器控制TFTLCD顯示