目录
TFT-LCD简介
TFT-LCD 即薄膜晶体管液晶显示器。其英文全称为:Thin Film Transistor-Liquid Crystal Display。TFT-LCD与无源 TN-LCD、STN-LCD 的简单矩阵不同,它在液晶显示屏的每一个象素上都设置有一个薄膜晶体管(TFT),可有效地克服非选通时的串扰,使显示液晶屏的静态特性与扫描线数无关,因此大大提高了图像质量。TFT-LCD 也被叫做真彩液晶显示器
以2.8 寸的 ALIENTEK TFTLCD 模块为例介绍,该模块支持 65K 色显示,显示分辨率为 320×240,接口为 16 位的 80 并口,自带触摸屏。
TFT-LCD模块接口定义
TFTLCD 模块采用 2*17 的 2.54 公排针与外部连接,接口定义如图 16.1.3 所示:
图 16.1.3 ALIENTEK 2.8 寸 TFTLCD 模块接口图
ALIENTEK TFTLCD 模块采用 16 位的并方式与外部连接,之所以不采用 8 位的方式,是因为彩屏的数据量比较大,尤其在显示图片的时候,如果用 8 位数据线,就会比 16 位方式慢一倍以上
该模块的 80 并口有如下一些信号线:
CS:TFTLCD 片选信号。
WR:向 TFTLCD 写入数据。
RD:从 TFTLCD 读取数据。
D[15:0]:16 位双向数据线。
RST:硬复位 TFTLCD。
RS:命令/数据标志(0,读写命令;1,读写数据)。
ILI9341 控制器
ALIENTEK 提供 2.8/3.5/4.3/7 寸等不同尺寸的 TFTLCD 模块,其驱动芯片有很多种类型,
这里我们仅以 ILI9341 控制器为例进行介绍
ILI9341 液晶控制器自带显存,其显存总大小为 172800(240*320*18/8),即 18 位模式(26
万色)下的显存量。在 16 位模式下,ILI9341 采用 RGB565 格式存储颜色数据,此时 ILI9341
的 18 位数据线与 MCU 的 16 位数据线以及 LCD GRAM 的对应关系如图 16.1.4 所示:
图 16.1.4 16 位数据与显存对应关系图
MCU 的 16 位数据,最低 5 位代表蓝色,中间 6 位为绿色,最高 5 位为红色。数值越
大,表示该颜色越深另外,特别注意 ILI9341 所有的指令都是 8 位的(高 8 位无效),且参数
除了读写 GRAM 的时候是 16 位,其他操作参数,都是 8 位的,这个和 ILI9320 等驱动器不一
样,必须加以注意。
ILI9341 的几个重要命令
0XD3,
这个是读 ID4 指令,用于读取 LCD 控制器的 ID
0XD3 指令后面跟了 4 个参数,最后 2 个参数,读出来是 0X93 和 0X41,
刚好是我们控制器 ILI9341 的数字部分,从而,通过该指令,即可判别所用的 LCD 驱动器是什
么型号,这样,我们的代码,就可以根据控制器的型号去执行对应驱动 IC 的初始化代码,从而
兼容不同驱动 IC 的屏
0X36,
这是存储访问控制指令,可以控制 ILI9341 存储器的读写方向,简单的说,就是在连续写 GRAM 的时候,可以控制 GRAM 指针的增长方向,从而控制显示方式
MY、MX、MV这三个位,通过这三个位的设置,我们可以控制整个 ILI9341 的全部扫描方向
0X2A
这是列地址设置指令,在从左到右,从上到下的扫描方式(默认)下面,该指令用于设置横坐标(x 坐标)在默认扫描方式时该指令带有 4 个参数,实际上是 2 个坐标值:
SC 和 EC,即列地址的起始值和结束值,SC 必须小于等于 EC,且 0≤SC/EC≤239。一般在设
置 x 坐标的时候,我们只需要带 2 个参数即可,也就是设置 SC 即可,因为如果 EC 没有变化,
我们只需要设置一次即可(在初始化 ILI9341 的时候设置),从而提高速度。
0X2B 是页地址设置指令,在从左到右,从上到下的扫描方式(默认)下面,该指令用于设置纵坐标(y 坐标)
在默认扫描方式时,该指令用于设置 y 坐标,该指令带有 4 个参数,实际上是 2 个坐标值:
SP 和 EP,即页地址的起始值和结束值,SP 必须小于等于 EP,且 0≤SP/EP≤319。一般在设置
y 坐标的时候,我们只需要带 2 个参数即可,也就是设置 SP 即可,因为如果 EP 没有变化,我
们只需要设置一次即可(在初始化 ILI9341 的时候设置),从而提高速度。
0X2C 该指令是写 GRAM 指令,在发送该指令之后,我们便可以往 LCD的 GRAM 里面写入颜色数据了,该指令支持连续写
在收到指令 0X2C 之后,数据有效位宽变为 16 位,我们可以连续写入 LCD
GRAM 值,而 GRAM 的地址将根据 MY/MX/MV 设置的扫描方向进行自增。例如:假设设置
的是从左到右,从上到下的扫描方式,那么设置好起始坐标(通过 SC,SP 设置)后,每写入
一个颜色值,GRAM 地址将会自动自增 1(SC++),如果碰到 EC,则回到 SC,同时 SP++,一
直到坐标:EC,EP 结束,其间无需再次设置的坐标,从而大大提高写入速度。
0X2E,该指令是读 GRAM 指令,用于读取 ILI9341 的显存(GRAM),
输出规律为:每个颜色分量占 8 个位,一次输出 2 个颜色分量。比如:第一次输出是
R1G1,随后的规律为:B1R2→G2B2→R3G3→B3R4→G4B4→R5G5... 以此类推。如果我们只
需要读取一个点的颜色值,那么只需要接收到参数 3 即可
一般 TFTLCD 模块的使用流程如图 16.1.4:
任何 LCD,使用流程都可以简单的用以上流程图表示。其中硬复位和初始化序列,只需要
执行一次即可。而画点流程就是:设置坐标→写 GRAM 指令→写入颜色数据,然后在 LCD 上
面,我们就可以看到对应的点显示我们写入的颜色了。读点流程为:设置坐标→读 GRAM 指令
→读取颜色数据,这样就可以获取到对应点的颜色数据了。
设置步骤
1) )设置 STM32 与 与 TFTLCD 模块相连接的 IO 。
这一步,先将我们与 TFTLCD 模块相连的 IO 口进行初始化,以便驱动 LCD。这里需要根
据连接电路以及 TFTLCD 模块的设置来确定。
2) )化 初始化 TFTLCD 模块。
3) )到 通过函数将字符和数字显示到 TFTLCD 模块上。
设置坐标→写 GRAM 指令→写 GRAM 来实现,这个步骤,只是一个点的处理,我们要显示字符/数字,就必须要多次使用这个步骤,从而达到显示字符/数字的目标,所以需要设计一个函数来实现数字/字符的显示,之后调用该函数,就可以实现数字/字符的显示了。
软件设计
1. 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;
//LCD 参数
extern _lcd_dev lcddev; //管理 LCD 重要参数
2.1写数据函数
//写数据函数
#define LCD_WR_DATA(data){\
LCD_RS_SET;\
LCD_CS_CLR;\
DATAOUT(data);\
LCD_WR_CLR;\
LCD_WR_SET;\
LCD_CS_SET;\
}
2.2 LCD_WR_DATAX 函数
//写数据函数
//可以替代 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;
}
3. LCD_WR_REG 函数
//写寄存器函数
//data:寄存器值
void LCD_WR_REG(u16 data)
{
LCD_RS_CLR;//写地址
LCD_CS_CLR;
DATAOUT(data);
LCD_WR_CLR;
LCD_WR_SET;
LCD_CS_SET;
}
4.读数据函数
//读 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;
LCD_RD_CLR; //读取数据(读寄存器时,并不需要读 2 次)
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,
5.读写寄存器中的值
//写寄存器
//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();
}
6.坐标设置函数
//设置光标位置
//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);
}
7.画点函数
//画点
//x,y:坐标
//POINT_COLOR:此点的颜色
void LCD_DrawPoint(u16 x,u16 y)
{
LCD_SetCursor(x,y); //设置光标位置
LCD_WriteRAM_Prepare(); //开始写入 GRAM
LCD_WR_DATA(POINT_COLOR);
}
8. LCD_ShowChar
/在指定位置显示一个字符
//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;
}
}
}
}
9.主函数
int main(void)
{
u8 x=0;
u8 lcd_id[12]; //存放 LCD ID 字符串
delay_init(); //延时函数初始化
uart_init(9600); //串口初始化为 9600
LED_Init(); //初始化与 LED 连接的硬件接口
LCD_Init();
POINT_COLOR=RED; sprintf((char*)lcd_id,"LCD ID:%04X",lcddev.id);
//将 LCD ID 打印到 lcd_id 数组。
while(1)
{
switch(x)
{
case 0:LCD_Clear(WHITE);break;
case 1:LCD_Clear(BLACK);break;
case 2:LCD_Clear(BLUE);break;
case 3:LCD_Clear(RED);break;
case 4:LCD_Clear(MAGENTA);break;
case 5:LCD_Clear(GREEN);break;
case 6:LCD_Clear(CYAN);break;
case 7:LCD_Clear(YELLOW);break;
case 8:LCD_Clear(BRRED);break;
case 9:LCD_Clear(GRAY);break;
case 10:LCD_Clear(LGRAY);break;
case 11:LCD_Clear(BROWN);break;
}
POINT_COLOR=RED;
LCD_ShowString(30,40,200,24,24,"Mini STM32 ^_^");
LCD_ShowString(30,70,200,16,16,"TFTLCD TEST") ;
LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
LCD_ShowString(30,110,200,16,16,lcd_id); //显示 LCD ID
LCD_ShowString(30,130,200,12,12,"2014/3/7");
x++;
if(x==12)x=0;
LED0=!LED0;
delay_ms(1000);
}
}
该部分代码将显示一些固定的字符,字体大小包括 24*12、16*8 和 12*6 等三种,同时显示
LCD 驱动 IC 的型号,然后不停的切换背景颜色,每 1s 切换一次。而 LED0 也会不停的闪烁,
指示程序已经在运行了
以上来源于官方资料