TFTLCD是薄膜晶体管液晶显示器。TFTLCD具有亮度好,对比度高,层次感强,颜色鲜艳等优点,是目前最主流的LCD显示器 ,广泛用于电视,手机,电脑,平板等各种的电子产品。
LCD原理图
(资料图片)
LCD_CS为芯片选择输入引脚(“低”启用)。
RS用于在并行接口中选择“数据或命令”,当RS为1时,数据被选中;当RS为0时,命令被选中。
WR作为写信号,上升沿写入数据。
RD作为读取信号,上升沿读取数据。
RST为硬复位LCD信号。
D0-D15为16位双向数据线。
BL为背光灯控制信号。
MISO/MOSI/T_PEN/T_CS/CLK为触摸屏接口信号,本节暂不做介绍。
引脚分配为:
LCD_CS:PG12、RS:PF12、WR:PD5、RD:PD4、RESET:PG15、BL:PB15、D0:PD14、D1:PD15、D2:PD0、D3:PD1、D4:PE7、D5:PE8、D6:PE9、D7:PE10、D8:PE11、D9:PE12、D10:PE13、D11:PE14、D12:PE15、D13:PD8、D14:PD9、D15:PD10。
由于开漏模式下电压达不到LCD的要求,所以所有引脚配置为推挽模式,数据输出时切换为输出模式,接收数据时切换为输入模式。
构造时序时经常要对片选信号CS、数据/命令选择RS、写信号WR、读信号RD、背光灯控制BL进行操作,为了更方便编写程序,可以对这几个信号进行宏定义。
#define LCD_CS_H() do{GPIOG- >BSRRL = 0x1< <12;}while(0) //片选失效#define LCD_CS_L() do{GPIOG- >BSRRH = 0x1< <12;}while(0) //片选有效#define RS_H() do{GPIOF- >BSRRL = 0x1< <12;}while(0) //选择为参数状态#define RS_L() do{GPIOF- >BSRRH = 0x1< <12;}while(0) //选择为命令状态#define WR_H() do{GPIOD- >BSRRL = 0x1< <5;}while(0) //写失效#define WR_L() do{GPIOD- >BSRRH = 0x1< <5;}while(0) //写有效#define RD_H() do{GPIOD- >BSRRL = 0x1< <4;}while(0) //读失效#define RD_L() do{GPIOD- >BSRRH = 0x1< <4;}while(0) //读有效#define BL_H() do{GPIOB- >BSRRL = 0x1< <15;}while(0) //关背光灯#define BL_L() do{GPIOB- >BSRRH = 0x1< <15;}while(0) //开背光灯
接着编写ILI9341GPIO口的初始化函数
staticvoid ILI9341_GpioInit(){ //1. 开时钟PB/D/E/F/G RCC- >AHB1ENR |= 1< <1 | 0XF< <3; //2. 设置模式(输出) // BL:PB15 GPIOB- >MODER &=~ (0x3< <30); GPIOB- >MODER |= (0x1< <30); // D2:PD0、D3:PD1、RD:PD4、WR:PD5、D13:PD8、D14:PD9、D15:PD10、D0:PD14、D1:PD15 GPIOD- >MODER &=~ (0xf03f0f0f< <0); GPIOD- >MODER |= (0x50150505< <0); // D4:PE7、D5:PE8、D6:PE9、D7:PE10、D8:PE11、D9:PE12、D10:PE13、D11:PE14、D12:PE15 GPIOE- >MODER &=~ (0xffffc000< <0); GPIOE- >MODER |= (0x55554000< <0); // RS:PF12 GPIOF- >MODER &=~ (0x3< <24); GPIOF- >MODER |= (0x1< <24); // LCD_CS:PG12、RESET:PG15 GPIOG- >MODER &=~ (0xc3000000< <0); GPIOG- >MODER |= (0x41000000< <0); //3. 输出类型:推挽输出 // BL:PB15 GPIOB- >OTYPER &=~ (0x1< <15); // D2:PD0、D3:PD1、RD:PD4、WR:PD5、D13:PD8、D14:PD9、D15:PD10、D0:PD14、D1:PD15 GPIOD- >OTYPER &=~ (0xc733< <0); // D4:PE7、D5:PE8、D6:PE9、D7:PE10、D8:PE11、D9:PE12、D10:PE13、D11:PE14、D12:PE15 GPIOE- >OTYPER &=~ (0xff80< <0); // RS:PF12 GPIOF- >OTYPER &=~ (0x1< <24); // LCD_CS:PG12、RESET:PG15 GPIOG- >OTYPER &=~ (0x9000< <0); //4. 速度(100Mhz) // BL:PB15 GPIOB- >OSPEEDR |= (0x3< <30); // D2:PD0、D3:PD1、RD:PD4、WR:PD5、D13:PD8、D14:PD9、D15:PD10、D0:PD14、D1:PD15 GPIOD- >OSPEEDR |= (0xf03f0f0f< <0); // D4:PE7、D5:PE8、D6:PE9、D7:PE10、D8:PE11、D9:PE12、D10:PE13、D11:PE14、D12:PE15 GPIOE- >OSPEEDR |= (0xffffc000< <0); // RS:PF12 GPIOF- >OSPEEDR |= (0x3< <24); // LCD_CS:PG12、RESET:PG15 GPIOG- >OSPEEDR |= (0xc3000000< <0); //5. 上下拉(上拉) // BL:PB15 GPIOB- >PUPDR &=~ (0x3< <30); // D2:PD0、D3:PD1、RD:PD4、WR:PD5、D13:PD8、D14:PD9、D15:PD10、D0:PD14、D1:PD15 GPIOD- >PUPDR &=~ (0xf03f0f0f< <0); // D4:PE7、D5:PE8、D6:PE9、D7:PE10、D8:PE11、D9:PE12、D10:PE13、D11:PE14、D12:PE15 GPIOE- >PUPDR &=~ (0xffffc000< <0); // RS:PF12 GPIOF- >PUPDR &=~ (0x3< <24); // LCD_CS:PG12、RESET:PG15 GPIOG- >PUPDR &=~ (0xc3000000< <0); //6. 引脚初始电平 LCD_CS_H(); //片选失效 RS_H(); //选择为参数状态 WR_H(); //写失效 RD_H(); //读失效 BL_H(); //关背光灯}
引脚初始化中,由于LCD模块涉及的引脚相对比较多,最好在配置时先写好每个寄存器的注释,方便进行配置,也方便后面进行排错。为方便数据的读写,添加数据引脚的输入输出模式切换函数。
//模式切换为输入void ILI9341_MODE_IN(){ // D2:PD0、D3:PD1、D13:PD8、D14:PD9、D15:PD10、D0:PD14、D1:PD15 GPIOD- >MODER &=~ (0xf03f000f< <0); // D4:PE7、D5:PE8、D6:PE9、D7:PE10、D8:PE11、D9:PE12、D10:PE13、D11:PE14、D12:PE15 GPIOE- >MODER &=~ (0xffffc000< <0);}//模式切换为输出void ILI9341_MODE_OUT(){ // D2:PD0、D3:PD1、D13:PD8、D14:PD9、D15:PD10、D0:PD14、D1:PD15 GPIOD- >MODER &=~ (0xf03f000f< <0); GPIOD- >MODER |= (0x50150005< <0); // D4:PE7、D5:PE8、D6:PE9、D7:PE10、D8:PE11、D9:PE12、D10:PE13、D11:PE14、D12:PE15 GPIOE- >MODER &=~ (0xffffc000< <0); GPIOE- >MODER |= (0x55554000< <0);}
写数据到ILI9341时要通过判断16位数据来确定输出数据线的高低电平。
void ILI9341_Write(u16 dat){ //D0-D1:PD14/PD15 GPIOD- >ODR &= 0x3fff; GPIOD- >ODR |= ((dat)&0x0003)< <14; //D2-D3:PD0/PD1 GPIOD- >ODR &= 0xfffc; GPIOD- >ODR |= ((dat >>2)&0x0003)< <0; //D4-D12:PE7-PE15 GPIOE- >ODR &= 0X007F; GPIOE- >ODR |= ((dat >>4)&0x01ff)< <7; //D13-D15:PD8-PD10 GPIOD- >ODR &= ~(0X7< <8); GPIOD- >ODR |= (dat >>13)< <8;}
读数据则通过判断数据线输入电平来确定输入的16位数据。
u16 ILI9341_Read(){ u16 temp = 0; //D0-D1:PD14/PD15 temp |= (GPIOD- >IDR & 0X3)< <0; //D2-D3:PD0/PD1 temp |= (GPIOD- >IDR & 0X3)< <2; //D4-D12:PE7-PE15 temp |= (GPIOE- >IDR > >7)< <4; //D13-D15:PD8-PD10 temp |= ((GPIOD- >IDR > >8)&0X7)< <13; return temp;}
由于使用的数据线是16条,所以采用8080时序的16位总线操作进行写命令、读状态、写参数、读参数。由表可以看出片选信号CS低电平时为使能。
根据表格8080时序写出写命令、读状态、写参数、读参数的函数。
//写命令void ILI9341_WriteCmd(u16 cmd){ LCD_CS_L(); //片选有效 RS_L(); //选择为命令状态 ILI9341_Write(cmd); WR_L(); //写失效 WR_H(); //写有效 LCD_CS_L(); //片选失效 }//读状态u16 ILI9341_ReadStatus(){ u16 temp = 0; //模式切换为读 ILI9341_MODE_IN(); LCD_CS_L(); //片选有效 RS_H(); //选择为参数状态 //rd:PD4 RD_H(); //读失效 RD_L(); //读有效 temp = ILI9341_Read(); LCD_CS_L(); //片选失效 //模式切换为读 ILI9341_MODE_OUT(); return temp;}//写参数void ILI9341_WriteParam(u16 param){ LCD_CS_L(); //片选有效 RS_H(); //选择为参数状态 ILI9341_Write(param); //WR:PD5 WR_L(); //写失效 WR_H(); //写有效 LCD_CS_L(); //片选失效 }//读参数u16 ILI9341_ReadParam(){ u16 temp = 0; //模式切换为读 ILI9341_MODE_IN(); LCD_CS_L(); //片选有效 RS_H(); //选择为参数状态 //rd:PD4 RD_H(); //读失效 RD_L(); //读有效 temp = ILI9341_Read(); LCD_CS_L(); //片选失效 //模式切换为读 ILI9341_MODE_OUT(); return temp;}
最后,完成ILI9341初始化函数。先初始化GPIO引脚,软件复位,再添加屏幕厂家提供的初始化序列。
void ILI9341_Init(){ u32 i = 0; //引脚初始化 ILI9341_GpioInit(); ILI9341_WriteCmd(0x01); //初始化9341 Delay_ms(120); // Delay 120 ms//****Start Initial Sequence(以下代码厂家提供) **** ILI9341_WriteCmd(0xCF); //电源设置 ILI9341_WriteParam(0x00); //默认值 ILI9341_WriteParam(0x81); //默认值 ILI9341_WriteParam(0X30); //默认值 ILI9341_WriteCmd(0xED); //上电序列控制 ILI9341_WriteParam(0x64); ILI9341_WriteParam(0x03); ILI9341_WriteParam(0X12); ILI9341_WriteParam(0X81); ILI9341_WriteCmd(0xE8); //驱动时序控制 ILI9341_WriteParam(0x85); ILI9341_WriteParam(0x01); ILI9341_WriteParam(0x79); ILI9341_WriteCmd(0xCB); //电源控制A ILI9341_WriteParam(0x39); ILI9341_WriteParam(0x2C); ILI9341_WriteParam(0x00); ILI9341_WriteParam(0x34); ILI9341_WriteParam(0x02); ILI9341_WriteCmd(0xF7); //Pump ratio control ILI9341_WriteParam(0x20); ILI9341_WriteCmd(0xEA); //Driver timing control B ILI9341_WriteParam(0x00); ILI9341_WriteParam(0x00); ILI9341_WriteCmd(0xC0); //Power control ILI9341_WriteParam(0x1D); //VRH[5:0] ILI9341_WriteCmd(0xC1); //Power control ILI9341_WriteParam(0x11); //SAP[2:0];BT[3:0] ILI9341_WriteCmd(0xC5); //VCM control ILI9341_WriteParam(0x33); ILI9341_WriteParam(0x34); ILI9341_WriteCmd(0xC7); //VCM control2 ILI9341_WriteParam(0Xbe);// ILI9341_MemoryAccessCtrl(SCAN_MODE,1); //扫描模式,默认为L2R_U2D,颜色顺序:RGB ILI9341_WriteCmd(0X36); ILI9341_WriteParam(0); ILI9341_WriteCmd(0xB1); ILI9341_WriteParam(0x00); ILI9341_WriteParam(0x1B); ILI9341_WriteCmd(0xB6); // Display Function Control ILI9341_WriteParam(0x0A); ILI9341_WriteParam(0xA2); ILI9341_WriteCmd(0xF2); // 3Gamma Function Disable ILI9341_WriteParam(0x00); ILI9341_WriteCmd(0x26); //Gamma curve selected ILI9341_WriteParam(0x01); ILI9341_WriteCmd(0xE0); //Set Gamma ILI9341_WriteParam(0x0F); ILI9341_WriteParam(0x23); ILI9341_WriteParam(0x1F); ILI9341_WriteParam(0x09); ILI9341_WriteParam(0x0f); ILI9341_WriteParam(0x08); ILI9341_WriteParam(0x4B); ILI9341_WriteParam(0Xf2); ILI9341_WriteParam(0x38); ILI9341_WriteParam(0x09); ILI9341_WriteParam(0x13); ILI9341_WriteParam(0x03); ILI9341_WriteParam(0x12); ILI9341_WriteParam(0x07); ILI9341_WriteParam(0x04); ILI9341_WriteCmd(0XE1); //Set Gamma ILI9341_WriteParam(0x00); ILI9341_WriteParam(0x1d); ILI9341_WriteParam(0x20); ILI9341_WriteParam(0x02); ILI9341_WriteParam(0x11); ILI9341_WriteParam(0x07); ILI9341_WriteParam(0x34); ILI9341_WriteParam(0x81); ILI9341_WriteParam(0x46); ILI9341_WriteParam(0x06); ILI9341_WriteParam(0x0e); ILI9341_WriteParam(0x0c); ILI9341_WriteParam(0x32); ILI9341_WriteParam(0x38); ILI9341_WriteParam(0x0F); //数据格式16bit设置 ILI9341_WriteCmd(0x3a); ILI9341_WriteParam(0x55); //RGB接口格式16bits/pixel,MCU接口格式16bits/pixel// ILI9341_ExitSleepMode(); //退出睡眠模式 ILI9341_WriteCmd(0x11); Delay_ms(120); ILI9341_WriteCmd(0X2C); //2CH for(i=0;i< 240*320;i++) { ILI9341_WriteParam(0xffff); //显示绿屏 } ILI9341_WriteCmd(0X29); GPIOB- >BSRRL = 1< <15; //开背光 }
最后在主文件调用ILI9341的初始化函数,进行测试。
#include "stm32f4xx.h"#include "core_cm4.h"#include "delay.h"#include "ili9341.h"int main(){ ILI9341_Init(); while(1);}
编译程序并烧入开发板,LCD亮并显示绿色,ILI9341初始化成功。
ILI9341驱动LCD涉及的引脚比较多,所以在配置时一定要细心,一旦出错也会很难排查,所以尽量一次写成。
X 关闭