STM32控制TFTLCD显示

您所在的位置:网站首页 单片机tft显存开辟 STM32控制TFTLCD显示

STM32控制TFTLCD显示

2024-07-03 16:15| 来源: 网络整理| 查看: 265

一、用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 IDu8  dir;//横屏还是竖屏控制:0,竖屏;1,横屏。 u16wramcmd;//开始写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;     //全部输出0LCD_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();//开始写入GRAMLCD_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 READLCD_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位gCRH=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)10)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



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3