weiqi7777

STM32驱动TFT屏

0
阅读(4992)

 

         现在开始找工作了,要将以前做过学习的东西要回顾一下,还记着以前用STM32加一个TFT屏,实现了2048游戏。通过按键控制游戏运行,界面通过TFT显示。

         要通过TFT显示,就要先实现TFT驱动。TFT本身的驱动是比较麻烦的,要控制各种信号,但是有了TFT驱动芯片,那驱动TFT就相当简单了,只要你会驱动1602,你就会驱动TFT屏了。

         我使用的STM32开发板用的是ILI9320的驱动芯片。接口原理图如下:

clip_image002

         信号说明

1、  LCDCS   片选信号,低电平有效

2、  LCDRS   命令/数据标志,0命令,1数据

3、  LCDWR:写数据信号

4、  LCDRD 读数据信号

5、  RESET 复位信号

6、  DB[15:0] 16位真彩色数据

7、  SP2CLK SPI协议clk信号

8、  SP2MOSI SPI协议mosi信号

9、  SP2MISO SPI协议miso信号

10TCS         SPI协议片选信号

上面这些信号,是不是和LCD1602是相似了。只是多了一个SPI接口,表示可以通过SPI和驱动芯片数据交互的。

下面是时序图:

clip_image004

  1、对于写入

                   WR的上升沿,数据/命令被写入

         2、对于读取

                   RD的上升沿,数据被读取     

         这时序图是不是也比较熟悉了,和LCD1602也差不多。只是读写信号分开,并且是在上升沿触发。

         有了这些基础了,写程序,那就简单了。先实现最底层的三个函数,一个写命令,一个写数据,一个读数据。

1、  写数据,写数据是比较常用的,所以使用宏定义,加快写的速度

//写16位数据函数
#define LCD_WR_DATA(data){\
LCD_RS_SET;\
LCD_CS_CLR;\
DATAOUT(data);\
LCD_WR_CLR;\
LCD_WR_SET;\
LCD_CS_SET;\
}

2、  写命令,这个就直接用函数是函数实现就可以了

//写命令 
void LCD_WR_REG(u16 data)
{
    LCD_RS_CLR;
    LCD_CS_CLR;
    DATAOUT(data);
    LCD_WR_CLR; 
    LCD_WR_SET;
    LCD_CS_SET;  
}

3、  读数据,首先写命令,然后将数据线设置为输入,在读取数据,读取完毕后,在将数据线设置为输出

//读寄存器
u16 LCD_ReadReg(u8 LCD_Reg)
{                                    
    u16 t;
    LCD_WR_REG(LCD_Reg);  //写入要读的寄存器号 
    GPIOE->CRL=0X88888888; //PE0-7  上拉输入
    GPIOE->CRH=0X88888888; //PE8-15 上拉输入
    GPIOE->ODR=0XFFFF;    //全部输出高
    LCD_RS_SET;
    LCD_CS_CLR;
    //读取数据
    LCD_RD_CLR;
    delay_us(5);//FOR 8989,延时5us                  
    LCD_RD_SET;
    t=DATAIN; 
    LCD_CS_SET;
    GPIOE->CRL=0X33333333; //PE0-7  上拉输出
    GPIOE->CRH=0X33333333; //PE8-15 上拉输出
    GPIOE->ODR=0XFFFF;     //全部输出高
    return t; 
}  

以上就是3个底层函数了。其中的SET,CLR都是使用的宏。有了这三个代码,下面就可以封装一个函数,向寄存器写数据。从寄存器读数据,上面已经是实现了。

  //写寄存器写数据
void LCD_WriteReg(u8 LCD_Reg, u16 LCD_RegValue)
{  
        LCD_WR_REG(LCD_Reg); 
        LCD_WR_DATA(LCD_RegValue);           
}  

以上程序,都没有加延时,那是因为STM32是运行比较慢的,运行起来完全符合芯片规定的时序,所以就不用加延时,但是如果用高速的设备的话,就要考虑芯片的时序了。以下是芯片的时序:

clip_image006

有了向寄存器写数据和从寄存器读取数据,然后就可以封装其他的函数了。比如显示图片,显示数字,画线,画矩形框等。无非就是调用这两个函数,再看数据手册,要往哪些寄存器写数据,往哪些寄存器读数据即可。

我用的TFT是用的16位真彩色,R(5)G(6)B(5)。我们使用的pc机用的是24位真彩色,所以如果我们想要把PC上的某一种颜色给显示到TFT上的话,就要将24位真彩色给转换成16位真彩色。

转换的原则也很简单,在24位真彩色中,RGB都是8位。将RB3位舍弃,G低两位舍弃,然后在按照RGB的顺序组合即可。

以下代码可以实现这个功能:

/*********************************************
RGB颜色混合
入口参数:R(红色分量)0-255,G(绿色分量)0-255,B(蓝色分量)0-255
出口参数: 按R5-G6-B5格式混合后的16位颜色码。
说明:将电脑上常见的R8-G8-B8格式转换成16位单片机常用的R5-G6-B5格式。
**********************************************/
u16 RGB(u8 R,u8 G,u8 B)
{
    return((u16)(R&0XF8)<<8|(u16)(G&0XFC)<<3|(u16)(B&0XF8)>>3);
}   

    因为后面实现2048,会在TFT上显示图片,所以,要实现显示图片的函数。以下就是显示图片的函数:

/**********************************************************
显示图片(图标)
入口参数:(x,y)是开始点的坐标,length是图片长度,high是图片高度。//pic 图片数组的指针
出口参数: 无
说明:用指定位置上显示事先定义的图片。
要显示的图片事先定义在bmp  pic[]数组中,
如果想修改图片大小、内容,请修改bmp pic[]数组,
建议用Image2Lcd软件将你要显示的图象自动转换为数组数据。
************************************************************/
void GUI_DisPicture(uchar x, uint y, uchar length, uint high ,const uchar *pic)
{
    u16 temp=0,tmp=0,num=0;
    u32 address=0;
    LCD_setwindow(x,y,x+length-1,y+high-1);
    num=length*high*2;
    do
    { 
       temp=pic[tmp]|( pic[tmp+1]<<8);
       LCD_WR_DATA(temp);//逐点显示
       tmp+=2;
    }while(tmp<num);
} 

    参数有5个:

    x:显示图片的左上角的x坐标

    y:显示图片的左上角的y坐标

    length: 图片的长度(x方向)

    high   图片的高度(y方向)

    pic  图片数据的一维数组  

    首先是使用LCD_setwindow函数,因为是要在一个矩形区域里面显示图片,也就意味着是在这个区域内写数据,如果每次都写一行数据,然后写下一行坐标,再写数据的话,效率就比较低,ILI9320提供了4个寄存器,只要往这4个寄存器里面写入矩形区域的坐标的话,就可以一直写数据,数据会依次的填充矩形区域,就实现在矩形区域显示图片了。

    然后就是不断从图片的数组中把数据取出来,因为数据是以8位存储的,所以要读取两个数据再拼接下,再讲数据写入到TFT中。直到图片的数据写完。

    所以TFT驱动,核心的还是底层的三个函数,只要底层的三个函数实现了,实现其他的函数也就很简单了。