上一节我们介绍了OLED模块及其显示,但是该模块只能显示单色/双色,不能显示彩色,这一节我们将介绍ALIENTEK
TFT LCD模块,该模块采用TFTLCD面板,可以显示16位色的真彩图片。本节将利用TFTLCD来显示字符和数字,并显示各种颜色。本节分为如下几个部分:
3.10.1
TFTLCD简介
3.10.2 硬件设计
3.10.3 软件设计
3.10.4 下载与测试
3.10.1 TFTLCD简介
TFT-LCD即薄膜晶体管液晶显示器。其英文全称为:Thin Film
Transistor-Liquid Crystal Display。TFT-LCD与无源TN-LCD、STN-LCD的简单矩阵不同,它在液晶显示屏的每一个象素上都设置有一个薄膜晶体管(TFT),可有效地克服非选通时的串扰,使显示液晶屏的静态特性与扫描线数无关,因此大大提高了图像质量。TFT-LCD也被叫做真彩液晶显示器。
上一节介绍了OLED模块,这一节,我们给大家介绍ALIENTEK
TFTLCD模块,该模块有如下特点:
1,2.4’/2.8’两种大小的屏幕可选。
2,320×240的分辨率。
3,16位真彩显示。
4,自带触摸屏,可以用来作为控制输入。
5,通用的接口,除了ALIENTEK MiniSTM32开发板,该液晶模块还可以使用在优异特、STMSKY、红牛等开发板上。
本节,我们以2.8寸的ALIENTEK
TFTLCD模块为例介绍,该模块采用的是显尚光电的DST2001PH
TFTLCD,DST2001PH的控制器为ILI9320,采用26万色的TFTLCD屏,分辨率为320×240,采用16位的80并口。
该模块的外观图如下:
图3.10.1.1
ALIENTEK 2.8寸TFTLCD外观图
模块原理图如下:
图3.10.1.2 ALIENTEK 2.8寸TFTLCD模块原理图
TFTLCD模块采用2*17的2.54公排针与外部连接,接口图如下:
图3.10.1.3 ALIENTEK 2.8寸TFTLCD模块接口图
该接口同目前主流的几款STM32开发板的接口完全兼容,所以模块除了用在ALIENTEK
MiniSTM32开发板上,也可以用在其他开发板上,当然你也可以使用其他接口一样的LCD模块放到我们的ALIENTEK
MiniSTM32开发板上使用。ALIENTEK
TFTLCD模块采用80并口口方与外部链接,采用16位数据线(低了速度太慢,用彩色就没什么效果了)。该模块的80并口有如下一些信号线:
CS:TFTLCD片选信号。
WR:向TFTLCD写入数据。
RD:从TFTLCD读取数据。
D[15:0]:16位双向数据线。
RST:硬复位TFTLCD。
RS:命令/数据标志(0,读写命令;1,读写数据)。
80并口在上一节我们已经有详细的介绍了,这里我们就不在介绍,需要说明的是,TFTLCD模块的RST信号线和OLED模块一样,也是直接接到STM32的复位脚上,并不由软件控制,这样可以省下来一个IO口。另外我们还需要一个背光控制线来控制TFTLCD的背光。所以,我们总共需要的IO口数目为21个。
模块的控制器为ILI9320,该控制器自带显存,其显存总大小为172820(240*320*18/8),即18位模式(26万色)下的显存量。模块的16位数据线与显寸的对应关系为565方式,如下图所示:
图3.10.1.4 16位数据与显存对应关系图
最低5位代表蓝色,中间6位为绿色,最高5位为红色。数值越大,表示该颜色越深。
接下来,我们介绍一下ILI9320的几个重要命令,因为ILI9320的命令很多,我们这里不可能一一介绍,有兴趣的大家可以找到ILI9320的datasheet看看。里面对这些命令有详细的介绍。这里我们要介绍的命令列表如下:
表3.10.1.1
ILI9320常用命令表
R0,这个命令,有两个功能,如果对它写,则最低位为OSC,用于开启或关闭振荡器。而如果对它读操作,则返回的是控制器的型号。这个命令最大的功能就是通过读它可以得到控制器的型号,而我们代码在知道了控制器的型号之后,可以针对不同型号的控制器,进行不同的初始化。因为93xx系列的初始化,其实都比较类似,我们完全可以用一个代码兼容好几个控制器。
R3,入口模式命令。我们重点关注的是I/D0、I/D1、AM这3个位,因为这3个位控制了屏幕的显示方向。
AM:控制GRAM更新方向。当AM=0的时候,地址以行方向更新。当AM=1的时候,地址以列方向更新。
I/D[1:0]:当更新了一个数据之后,根据这两个位的设置来控制地址计数器自动增加/减少1,
其关系如下图:
图3.10.1.5 GRAM显示方向设置图
通过这几个位的设置,我们就可以控制屏幕的显示方向了。
R7,显示控制命令。该命令CL位用来控制是8位彩色,还是26万色。为0时26万色,为1时八位色。D1、D0、BASEE这三个位用来控制显示开关与否的。当全部设置为1的时候开启显示,全0是关闭。我们一般通过该命令的设置来开启或关闭显示器,以降低功耗。
R32,R33,设置GRAM的行地址和列地址。R32用于设置列地址(X坐标,0~239),R33用于设置行地址(Y坐标,0~319)。当我们要在某个指定点写入一个颜色的时候,先通过这两个命令设置到改点,然后写入颜色值就可以了。
R34,写数据到GRAM命令,当写入了这个命令之后,地址计数器才会自动的增加和减少。该命令是我们要介绍的这一组命令里面唯一的单个操作的命令,只需要写入该值就可以了,其他的都是要先写入命令编号,然后写入操作数。
R80~R83,行列GRAM地址位置设置。这几个命令用于设定你显示区域的大小,我们整个屏的大小为240*320,但是有时候我们只需要在其中的一部分区域写入数据,如果用先写坐标,后写数据这样的方式来实现,则速度大打折扣。此时我们就可以通过这几个命令,在其中开辟一个区域,然后不停的丢数据,地址计数器就会根据R3的设置自动增加/减少,这样就不需要频繁的写地址了,大大提高了刷新的速度。
命令部分,我们就为大家介绍到这里,我们接下来看看要如何才能驱动ALIENTEK
TFTLCD模块,这里TFTLCD模块的初始化和我们前面介绍的OLED模块的初始化框图是一样的,只是初始化代码部分不同。接下来我们也是将该模块用来来显示字符和数字。通过以上介绍,我们可以得出TFTLCD显示需要的相关设置步骤如下:
1)设置STM32与TFTLCD模块相连接的IO。
这一步,先将我们与TFTLCD模块相连的IO口设置为输出,具体使用哪些IO口,这里需要根据连接电路以及TFTLCD模块的设置来确定。
2)初始化TFTLCD模块。
其实这里就是上和上面OLED模块的初始化过程差不多。通过向TFTLCD写入一系列的设置,来启动TFTLCD的显示。为后续显示字符和数字做准备。
3)通过函数将字符和数字显示到TFTLCD模块上。
这里就是通过我们设计的程序,将要显示的字符送到TFTLCD模块就可以了,这些函数将在软件设计部分向大家介绍。
通过以上三步,我们就可以使用ALIENTEK TFTLCD模块来显示字符和数字了, 并且可以显示各种颜色的背景。
3.10.2 硬件设计
TFTLCD模块的电路在上一部分已有详细说明了,这里我们介绍TFTLCD模块与ALIETEK
MiniSTM32开发板的连接,MiniSTM32开发板底板的LCD接口和ALIENTEK
TFTLCD模块直接可以对插,连接如下图:
图3.10.2.1 TFTLCD与开发板连接示意图
图中绿色线圈出来的部分就是连接TFTLCD模块的接口,这里在硬件上,TFTLCD模块与MiniSTM32开发板的IO口对应关系如下:
LCD_LED对应PC10;
LCD_CS对应PC9;
LCD
_RS对应PC8;
LCD
_WR对应PC7;
LCD
_RD对应PC6;
LCD
_D[17:1]对应PB[15:0];
这些线的连接,MiniSTM32的内部已经连接好了,我们只需要将TFTLCD模块插上去就好了。实物连接如下图所示:
图3.10.2.2 TFTLCD与开发板连接实物图
3.10.3 软件设计
软件设计我们依旧在之前的工程上面增加,首先在HARDWARE文件夹下新建一个LCD的文件夹。然后打开USER文件夹下的工程,新建一个ILI93xx.c的文件和lcd.h的头文件,保存在LCD文件夹下,并将LCD文件夹加入头文件包含路径。
在ILI93xx.c里面要输入的代码比较多,我们这里就不完全列出来了,只针对几个重要的函数进行讲解。完整版的代码见光盘->ALIENTEK
MiniSTM32开发板例程->实验10的ILI93xx.c文件。
第一个是LCD_WR_DATA函数,该函数通过80并口向LCD模块写入一个16位的数据,使用频率是最高的,这里我们采用了宏定义的方式,以提高速度。其代码如下:
#define LCD_WR_DATA(data){\
LCD_RS=1;\
LCD_CS=0;\
DATAOUT(data);\
LCD_WR=0;\
LCD_WR=1;\
LCD_CS=1;\
}
第二个是LCD_WR_REG函数,该函数也是通过80并口向LCD模块写入8位的寄存器命令,因为该函数使用频率不是很高,我们不采用宏定义来做(宏定义占用FLASH较多),通过LCD_RS来标记是写入命令(LCD_RS=0)还是数据(LCD_RS=1)。该函数代码如下:
void LCD_WR_REG(u8 data)
{
LCD_RS=0;//写地址
LCD_CS=0;
DATAOUT(data);
LCD_WR=0;
LCD_WR=1;
LCD_CS=1;
}
既然有写命令/数据的函数,那就有读命令/数据的函数。接下来介绍LCD_ReadReg函数,该函数用来读取某个寄存器的值。在读某个寄存器的值之前,先要写入该寄存器的编号(命令号),然后设置D[15:0]为输入,在读取该改寄存器的值,读完数据之后,我们再设置IO口为输出。其代码如下:
u16 LCD_ReadReg(u8 LCD_Reg)
{
u16
t;
LCD_WR_REG(LCD_Reg); //写入要读的寄存器号
GPIOB->CRL=0X88888888;
//PB0-7 上拉输入
GPIOB->CRH=0X88888888;
//PB8-15 上拉输入
GPIOB->ODR=0XFFFF; //全部输出高
LCD_RS=1;
LCD_CS=0;
//读取数据(读寄存器时,并不需要读2次)
LCD_RD=0;
LCD_RD=1;
t=DATAIN;
LCD_CS=1;
GPIOB->CRL=0X33333333;
//PB0-7 上拉输出
GPIOB->CRH=0X33333333;
//PB8-15 上拉输出
GPIOB->ODR=0XFFFF; //全部输出高
return
t;
}
第四个要介绍的是读取GRAM的函数,这里说明一下,为什么OLED模块没做读GRAM的函数,而这里做了。因为OLED模块是单色的,所需要全部GRAM也就1K个字节,而TFTLCD模块为彩色的,点数也比OLED模块多很多,以16位色计算,也需要150K字节,这对任何一款ARM来说,都不是一个小数目了。而且我们在图形叠加的时候,可以先读回原来的值,然后写入新的值,在完成叠加后,我们又恢复原来的值。这样在做一些简单菜单的时候,是很有用的。这里我们读取TFTLCD模块数据的函数为LCD_ReadRAM,该函数直接返回读到的GRAM值。该函数使用之前要先设置读取的GRAM地址,通过LCD_SetCursor函数来实现。LCD_ReadRAM的代码如下:
u16 LCD_ReadRAM(void)
{
u16
t;
LCD_WR_REG(R34); //选择GRAM地址
GPIOB->CRL=0X88888888;
//PB0-7 上拉输入
GPIOB->CRH=0X88888888;
//PB8-15 上拉输入
GPIOB->ODR=0XFFFF; //全部输出高
LCD_RS=1;
LCD_CS=0;
//读取数据(读GRAM时,需要读2次)
LCD_RD=0;
LCD_RD=1;
//dummy
READ
LCD_RD=0;
LCD_RD=1;
t=DATAIN;
LCD_CS=1;
GPIOB->CRL=0X33333333;
//PB0-7 上拉输出
GPIOB->CRH=0X33333333;
//PB8-15 上拉输出
GPIOB->ODR=0XFFFF; //全部输出高
return
t;
}
第五个要介绍的函数为LCD_SetCursor函数,该函数用来设置坐标的,其代码如下:
void LCD_SetCursor(u8 Xpos, u16 Ypos)
{
LCD_WriteReg(R32, Xpos);
LCD_WriteReg(R33, Ypos);
}
(Xpos,Ypos)为要写入或读取的像素点坐标,这里的设置其实就是利用了R32,R33这两个命令,在前面寄存器介绍的时候已经介绍过了。
第六个要介绍的是画点函数LCD_DrawPoint,该函数带2个参数(x,y)就代表TFTLCD上的坐标,x的范围为0~239,代表横坐标。Y的范围为0~319,代表纵坐标,写入点的颜色是根据全局变量POINT_COLOR来决定的。该函数代码如下:
void LCD_DrawPoint(u8 x,u16 y)
{
LCD_SetCursor(x,y);//设置光标位置
LCD_WR_REG(R34);//开始写入GRAM
LCD_WR_DATA(POINT_COLOR);
}
这里顺带介绍一下2个全局变量POINT_COLOR和BACK_COLOR,这两个16位的全局变量,第一个POINT_COLOR代表要写入的点的颜色,而BACK_COLOR则代表背景色。
第七个要介绍的函数是字符显示函数LCD_ShowChar,该函数同前面OLED模块的自负显示函数差不多,但是这里的字符显示函数多了1个功能,就是可以以叠加方式显示,或者以非叠加方式显示。叠加方式显示多用于在显示的图片上再显示字符。非叠加方式一般用于普通的显示。
这里要注意的是TFTLCD模块的ASCII字符集取模方式与OLED模块的ASCII字符集取模方式不一样,这里的字符写入函数,采取的开辟窗口的形式来做的,并不是采用画点函数来做的。开辟窗口我们用的是前面介绍的R80~R83四个命令来做的,具体请参考前面的命令介绍部分。这里,我们的ASCII字符集取模还是采用PCtoLCD2002完美版来做,只是取模方式设置成如下方式:
图3.10.3.1 TFTLCD显示字符取模方式设置
LCD_ShowChar的代码如下:
void LCD_ShowChar(u8 x,u16 y,u8
num,u8
size,u8
mode)
{
#define MAX_CHAR_POSX 232
#define MAX_CHAR_POSY 304
u8 temp;
u8 pos,t;
if(x>MAX_CHAR_POSX||y>MAX_CHAR_POSY)return;
//设置窗口
LCD_WriteReg(R80,x); //水平方向GRAM起始地址
LCD_WriteReg(R81,x+(size/2-1));//水平方向GRAM结束地址
LCD_WriteReg(R82,y); //垂直方向GRAM起始地址
LCD_WriteReg(R83,y+size-1); //垂直方向GRAM结束地址
LCD_SetCursor(x,y); //设置光标位置
LCD_WriteRAM_Prepare(); //开始写入GRAM
num=num-'
';//得到偏移后的值
if(!mode)
//非叠加方式
{
for(pos=0;pos<size;pos++)
{
if(size==12)temp=asc2_1206[num][pos];//调用1206字体
else
temp=asc2_1608[num][pos]; //调用1608字体
for(t=0;t<size/2;t++)
{
if(temp&0x01)
{
LCD_WR_DATA(POINT_COLOR);
}else
LCD_WR_DATA(BACK_COLOR);
temp>>=1;
}
}
}else//叠加方式
{
for(pos=0;pos<size;pos++)
{
if(size==12)temp=asc2_1206[num][pos];//调用1206字体
else
temp=asc2_1608[num][pos]; //调用1608字体
for(t=0;t<size/2;t++)
{
if(temp&0x01)LCD_DrawPoint(x+t,y+pos);//画一个点
temp>>=1;
}
}
}
//恢复窗体大小
LCD_WriteReg(R80, 0x0000); //水平方向GRAM起始地址
LCD_WriteReg(R81, 0x00EF); //水平方向GRAM结束地址
LCD_WriteReg(R82, 0x0000); //垂直方向GRAM起始地址
LCD_WriteReg(R83, 0x013F); //垂直方向GRAM结束地址
}
最后,我们再介绍一下TFTLCD模块的初始化函数LCD_Init,该函数先初始化STM32与TFTLCD连接的IO口,然后读取控制芯片的型号,根据控制IC的型号执行不同的初始化代码,其简化代码如下:
void LCD_Init(void)
{
u16
DeviceCode;
RCC->APB2ENR|=1<<3;//先使能外设PORTB时钟
RCC->APB2ENR|=1<<4;//先使能外设PORTC时钟
RCC->APB2ENR|=1<<0; //开启辅助时钟
AFIO->MAPR=0X04000000;
//关闭JTAG
//PORTC6~10复用推挽输出
GPIOC->CRH&=0XFFFFF000;
GPIOC->CRH|=0X00000333;
GPIOC->CRL&=0X00FFFFFF;
GPIOC->CRL|=0X33000000;
GPIOC->ODR|=0X07C0;
//PORTB推挽输出
GPIOB->CRH=0X33333333;
GPIOB->CRL=0X33333333;
GPIOB->ODR=0XFFFF;
Delay(5);
// delay 50 ms
LCD_WriteReg(0x0000,0x0001);
Delay(5);
// delay 50 ms
DeviceCode
= LCD_ReadReg(0x0000);
//printf("ID:%d\n",DeviceCode);
if(DeviceCode==0x9325||DeviceCode==0x9328)//ILI9325
{
……//9325/9328初始化代码
}
else
if(DeviceCode==0x9320||DeviceCode==0x9300)
{
……//9320/9300初始化代码
}
else
if(DeviceCode==0x1505)
{
……//1505初始化代码
}
else
if(DeviceCode==0x8989)
{
……//8989初始化代码
}
Delay(5000);
LCD_Clear(WHITE);
}
保存ILI93xx.c,并将该代码加入到HARDWARE组下。在介绍完了ILI93xx.c的内容之后,然后我们在lcd.h里面输入如下内容:
#ifndef __LCD_H
#define __LCD_H
#include "sys.h"
#include "stdlib.h"
//TFTLCD部分外要调用的函数
extern u16 POINT_COLOR;//默认红色
extern u16 BACK_COLOR; //背景颜色.默认为白色
//-----------------LCD端口定义----------------
#define LCD_LED
PCout(10) //LCD背光 PC10
#define LCD_CS PCout(9) //片选端口 PC9
#define LCD_RS PCout(8) //数据/命令 PC8
#define LCD_WR PCout(7) //写数据 PC7
#define LCD_RD PCout(6) //读数据 PC6
//PB0~15,作为数据线
#define DATAOUT(x) GPIOB->ODR=x; //数据输出
#define DATAIN GPIOB->IDR; //数据输入
//画笔颜色
#define WHITE 0xFFFF
#define BLACK 0x0000
#define BLUE 0x001F
#define RED 0xF800
#define MAGENTA 0xF81F
#define GREEN 0x07E0
#define CYAN 0x7FFF
#define YELLOW 0xFFE0
#define BROWN 0XBC40 //棕色
#define BRRED 0XFC07 //棕红色
#define GRAY 0X8430 //灰色
#define LGRAY 0XC618 //浅灰色
extern u16 BACK_COLOR, POINT_COLOR ;
void LCD_Init(void);
void LCD_Clear(u16 Color);
void LCD_SetCursor(u8 Xpos, u16 Ypos);
void LCD_DrawPoint(u8 x,u16
y);//画点
void Draw_Circle(u8 x0,u16 y0,u8
r);
void LCD_DrawLine(u8 x1, u16 y1, u8 x2, u16 y2);
void LCD_DrawRectangle(u8 x1, u16 y1, u8 x2, u16 y2);
void LCD_Fill(u8 xsta,u16 ysta,u8
xend,u16
yend,u16
color);
void LCD_ShowChar(u8 x,u16 y,u8
num,u8
size,u8
mode);//显示一个字符
void LCD_ShowNum(u8 x,u8 y,u32
num,u8
len,u8
size); //显示一个数字
void LCD_ShowString(u8 x,u16 y,const
u8 *p); //显示一个字符串,16字体
void LCD_WriteReg(u8 LCD_Reg, u16 LCD_RegValue);
u16 LCD_ReadReg(u8 LCD_Reg);
void LCD_WriteRAM_Prepare(void);
void LCD_WriteRAM(u16 RGB_Code);
u16 LCD_ReadRAM(void);
//9320/9325 LCD寄存器
#define R0 0x00
#define R1 0x01
#define R2 0x02
…… //寄存器定义区
#define R192 0xC0
#define R193 0xC1
#define R229 0xE5
#endif
此部分代码我们也没有完全贴出来,寄存器定义区省略了大部分,这里很简单,我们就不多说了。接下来,我们在test.c里面修改main函数如下:
int main(void)
{
u8
x=0;
Stm32_Clock_Init(9);//系统时钟设置
delay_init(72); //延时初始化
uart_init(72,9600);
//串口1初始化
LED_Init();
KEY_Init();
LCD_Init();
POINT_COLOR=RED;
while(1)
{
switch(x)
{
case
0 CD_Clear(WHITE);break;
case
1 CD_Clear(BLACK);break;
case
2 CD_Clear(BLUE);break;
case
3 CD_Clear(RED);break;
case
4 CD_Clear(MAGENTA);break;
case
5 CD_Clear(GREEN);break;
case
6 CD_Clear(CYAN);break;
case
7 CD_Clear(YELLOW);break;
case
8 CD_Clear(BRRED);break;
case
9 CD_Clear(GRAY);break;
case
10:LCD_Clear(LGRAY);break;
case
11:LCD_Clear(BROWN);break;
}
POINT_COLOR=RED;
LCD_ShowString(30,50,"Mini
STM32 ^_^");
LCD_ShowString(30,70,"2.4’/2.8’ TFT LCD TEST");
LCD_ShowString(30,90,"ATOM@ALIENTEK");
LCD_ShowString(30,110,"2010/5/13");
x++;
if(x==12)x=0;
delay_ms(1000);
}
}
该部分代码将显示一些固定的字符,然后不停的切换背景颜色,每1s切换一次。而LED0也会不停的闪烁,指示程序已经在运行了。这里我们因为是在前面OLED模块驱动代码的基础上修改的,而两个模块的代码都有各自的font.h,这两个font.h并不能通用,所以我们要先删除oled.c。然后把ILI93xx.c加入到HARDWARE组下,在test.c里面加入lcd.h头文件。编译此工程,在编译完之后,我们开始下一步工作。
3.10.4 下载与测试
将代码下载到MiniSTM32后,可以看到DS0不停的闪烁,提示程序已经在运行了。同时可以看到TFTLCD模块显示如下内容:
图3.10.4.1 TFTLCD显示效果图
我们可以看到屏幕的背景是不停切换的。 |