目录

前言

一、TFT LCD简介

二、液晶屏的信号线与8080 接口

三、TFTLCD 模块的使用流程

四、软件设计

4.1、常用的LCD函数

4.1.1、LCD地址结构体

4.1.2、清屏函数:void LCD_Clear(u16 color)

4.1.3、填充函数:void LCD_Fill(u16 sx, u16 sy, u16 ex, u16 ey, u16 color)

4.1.4、填充指定的颜色函数:void LCD_Color_Fill(u16 sx, u16 sy, u16 ex, u16 ey, u16 *color)

4.1.5、画线函数:void LCD_DrawLine(u16 x1, u16 y1, u16 x2, u16 y2)

4.1.6、画矩形函数:void LCD_DrawRectangle(u16 x1, u16 y1, u16 x2, u16 y2)

4.1.7、画圆函数:void LCD_Draw_Circle(u16 x0, u16 y0, u8 r)

4.1.8、显示字符函数:void LCD_ShowChar(u16 x, u16 y, u8 num, u8 size, u8 mode)

4.1.9、写数字函数:void LCD_ShowNum(u16 x, u16 y, u32 num, u8 len, u8 size)

4.1.10、写数字函数:void LCD_ShowxNum(u16 x, u16 y, u32 num, u8 len, u8 size, u8 mode)

4.1.11、显示字符串的函数:void LCD_ShowString(u16 x, u16 y, u16 width, u16 height, u8 size, u8 *p)

4.2、主函数的调用


本篇文章,着重是对lcd.c的应用函数进行说明。


前言

显示器属于计算机的 I/O 设备,即输入输出设备。它是一种将特定电子信息输出到屏幕上再反射到人眼的显示工具。常见的有 CRT 显示器、液晶显示器、LED 点阵显示器及 OLED 显示器。

一、TFT LCD简介

液晶显示器,简称 LCD(Liquid Crystal Display),相对于上一代 CRT 显示器 (阴极射线管显示器), LCD 显示器具有功耗低、体积小、承载的信息量大及不伤眼的优点,因而它成为了现在的主流电子显示设备,其中包括电视、电脑显示器、手机屏幕及各种嵌入式设备的显示器。

本液晶屏内部包含有一个液晶控制芯片 ILI9341,它的内部结构非常复杂。

ILI9341 控制器内部框图

该芯片最主核心部分是位于中间的 GRAM(Graphics RAM),它就是显存。GRAM 中每个存储单元都对应着液晶面板的一个像素点。它右侧的各种模块共同作用把 GRAM 存储单元的数据转化成液晶面板的控制信号,使像素点呈现特定的颜色,而像素点组合起来则成为一幅完整的图像。

框图的左上角为 ILI9341 的主要控制信号线和配置引脚,根据其不同状态设置可以使芯片工作在不同的模式,如每个像素点的位数是 6、16 还是 18 位;可配置使用 SPI 接口、8080 接口还是 RGB 接口与 MCU 进行通讯。MCU 通过 SPI、8080 接口或 RGB 接口与 ILI9341 进行通讯,从而访问它的控制寄存器 (CR)、地址计数器 (AC)、及 GRAM。

在 GRAM 的左侧还有一个 LED 控制器 (LED Controller)。LCD 为非发光性的显示装置,它需要借助背光源才能达到显示功能,LED 控制器就是用来控制液晶屏中的 LED 背光源。

二、液晶屏的信号线与8080 接口

ILI9341 控制器根据自身的 IM[3:0] 信号线电平决定它与 MCU 的通讯方式,它本身支持 SPI 及 8080 通讯方式,本液晶屏的 ILI9341 控制器在出厂前就已经按固定配置好 (内部已连接硬件电路),它被配置为通过 8080 接口通讯,使用 16 根数据线的 RGB565 格式。

TFTLCD 模块接口
管脚序号 液晶屏管脚 STM32管脚 管脚功能
1 FSMC_NE4 PG12 LCD片选信号(低电平有效)
2 FSMC_A10 PG0 命令/数据控制信号,0:命令,1:数据
3 FSMC_NWE PD5 写使能信号(低电平有效)
4 FSMC_NOE PD4 读使能信号(低电平有效)
5 RESET NRST 复位信号(低电平有效)
6 FSMC_D0 PD14 双向数据总线D0
7 FSMC_D1 PD15 双向数据总线D1
8 FSMC_D2 PD0 双向数据总线D2
9 FSMC_D3 PD1 双向数据总线D3
10 FSMC_D4 PE7 双向数据总线D4
11 FSMC_D5 PE8 双向数据总线D5
12 FSMC_D6 PE9 双向数据总线D6
13 FSMC_D7 PE10 双向数据总线D7
14 FSMC_D8 PE11 双向数据总线D8
15 FSMC_D9 PE12 双向数据总线D9
16 FSMC_D10 PE13 双向数据总线D10
17 FSMC_D11 PE14 双向数据总线D11
18 FSMC_D12 PE15 双向数据总线D12
19 FSMC_D13 PD8 双向数据总线D13
20 FSMC_D14 PD9 双向数据总线D14
21 FSMC_D15 PD10 双向数据总线D15
22 SD_CS PG6 SD卡片选线
23 LCD_BL PB0 LCD背光控制(1:点亮,0:关闭)
24 3.3V 3.3V 电源供电
25 3.3V 3.3V 电源供电
26 GND GND 电源地
27 GND GND 电源地
28 5V 5V 电源供电
29 T_ M ISO PB2 触摸屏SPI信号
30 T_ MOSI PF 9 触摸屏SPI信号
31 T_PEN PF10 触摸屏中断信号
32 NC NC
33 T_CS PF11 触摸屏SPI片选信号
34 T_SCK PB1 触摸屏SPI时钟信号

 ILI9341液晶控制器自带显存,其显存总大小为 172800(240*320*18/8),即 18 位模式(26 万色)下的显存量。在 16 位模式下,ILI9341 采用 RGB565 格式存储颜色数据,此时 ILI9341 的 18 位数据线与 MCU 的 16 位数据线以及 LCD GRAM 的对应关系,如图所示:

ILI9341 在 16 位模式下面,数据线有用的是:D17~D13 和 D11~D1,D0 和 D12 没有用到,实际上在LCD 模块里面,ILI9341 的 D0 和 D12 没有引出来,ILI9341 的 D17~D13 和 D11~D1 对应 MCU 的 D15~D0。

MCU 的 16 位数据,最低 5 位代表蓝色,中间 6 位为绿色,最高 5 位为红色。数值越大,表示该颜色越深。特别注意 ILI9341 所有的指令都是 8 位的(高 8 位无效),且参 除了读写 GRAM 的时候是 16 位,其他操作参数,都是 8 位的。要调用哪个颜色就令它对应的数据地址等于1就可以了。

三、TFTLCD 模块的使用流程

(1)设置 STM32F1 与 TFTLCD 模块相连接的 IO 。这一步,先将我们与 TFTLCD 模块相连的 IO 口进行初始化,以便驱动 LCD。这里用到的是 FSMC,FSMC 在 《STM32学习笔记》专栏,已经详细介绍了。

        (2)初始化 TFTLCD 模块。将 TFTLCD的 RST 同STM32F1的RESET连接在一起了,只要按下开发板的 RESET 键, 就会对 LCD 进行硬复位。

        初始化序列,就是向 LCD 控制器写入一系列的设置值(比如伽马校准)这些初始化序列一般 LCD 供应商会提供给客户。在初始化之后,LCD 才可以正常使用。

        (3)通过函数将字符和数字显示到 TFTLCD 模块上。即:设置坐标→写 GRAM 指令→写 GRAM来实现, 但是这个步骤,只是一个点的处理,要显示字符/数字,就必须要多次使用这个步骤,从而达到显示字符/数字的目的,需要设计一个函数来实现数字/字符的显示,之后调用该函数, 就可以实现数字/字符的显示了。

四、软件设计

4.1、常用的LCD函数

4.1.1、LCD地址结构体

结构体如下:

//写寄存器
//LCD_Reg:寄存器地址
//LCD_RegValue:要写入的数据
void LCD_WriteReg(u16 LCD_Reg,u16 LCD_RegValue)
{LCD->LCD_REG = LCD_Reg;     //写入要写的寄存器序号LCD->LCD_RAM = LCD_RegValue;//写入数据
}

写完REG寄存器地址后,RAM地址自增+1。

4.1.2、清屏函数:void LCD_Clear(u16 color)

函数代码如下:

void LCD_Clear(u16 color)
{u32 index = 0;u32 totalpoint = lcddev.width;totalpoint *= lcddev.height;    //得到总点数LCD_SetCursor(0x00, 0x0000);    //设置光标位置LCD_WriteRAM_Prepare();         //开始写入GRAMfor (index = 0; index < totalpoint; index++){LCD->LCD_RAM=color;}
}

4.1.3、填充函数:void LCD_Fill(u16 sx, u16 sy, u16 ex, u16 ey, u16 color)

函数代码如下:

void LCD_Fill(u16 sx, u16 sy, u16 ex, u16 ey, u16 color)
{u16 i, j;u16 xlen = 0;xlen = ex - sx + 1;for (i = sy; i <= ey; i++){LCD_SetCursor(sx, i);       //设置光标位置LCD_WriteRAM_Prepare();     //开始写入GRAMfor (j = 0; j < xlen; j++){LCD->LCD_RAM=color;     //设置光标位置}}
}

4.1.4、填充指定的颜色函数:void LCD_Color_Fill(u16 sx, u16 sy, u16 ex, u16 ey, u16 *color)

函数代码如下:

void LCD_Color_Fill(u16 sx, u16 sy, u16 ex, u16 ey, u16 *color)
{u16 height, width;u16 i, j;width = ex - sx + 1;            //得到填充的宽度height = ey - sy + 1;           //高度for (i = 0; i < height; i++){LCD_SetCursor(sx, sy + i);  //设置光标位置LCD_WriteRAM_Prepare();     //开始写入GRAMfor (j = 0; j < width; j++){LCD->LCD_RAM=color[i * width + j];  //写入数据}}
}

4.1.5、画线函数:void LCD_DrawLine(u16 x1, u16 y1, u16 x2, u16 y2)

函数代码如下:

void LCD_DrawLine(u16 x1, u16 y1, u16 x2, u16 y2)
{u16 t;int xerr = 0, yerr = 0, delta_x, delta_y, distance;int incx, incy, uRow, uCol;delta_x = x2 - x1;              //计算坐标增量delta_y = y2 - y1;uRow = x1;uCol = y1;if (delta_x > 0)incx = 1;       //设置单步方向else if (delta_x == 0)incx = 0; //垂直线else{incx = -1;delta_x = -delta_x;}if (delta_y > 0)incy = 1;else if (delta_y == 0)incy = 0; //水平线else{incy = -1;delta_y = -delta_y;}if ( delta_x > delta_y)distance = delta_x; //选取基本增量坐标轴else distance = delta_y;for (t = 0; t <= distance + 1; t++ )    //画线输出{LCD_DrawPoint(uRow, uCol); //画点xerr += delta_x ;yerr += delta_y ;if (xerr > distance){xerr -= distance;uRow += incx;}if (yerr > distance){yerr -= distance;uCol += incy;}}
}

4.1.6、画矩形函数:void LCD_DrawRectangle(u16 x1, u16 y1, u16 x2, u16 y2)

函数代码如下:

void LCD_DrawRectangle(u16 x1, u16 y1, u16 x2, u16 y2)
{LCD_DrawLine(x1, y1, x2, y1);LCD_DrawLine(x1, y1, x1, y2);LCD_DrawLine(x1, y2, x2, y2);LCD_DrawLine(x2, y1, x2, y2);
}

4.1.7、画圆函数:void LCD_Draw_Circle(u16 x0, u16 y0, u8 r)

函数代码如下:

void LCD_Draw_Circle(u16 x0, u16 y0, u8 r)
{int a, b;int di;a = 0;b = r;di = 3 - (r << 1);       //判断下个点位置的标志while (a <= b){LCD_DrawPoint(x0 + a, y0 - b);        //5LCD_DrawPoint(x0 + b, y0 - a);        //0LCD_DrawPoint(x0 + b, y0 + a);        //4LCD_DrawPoint(x0 + a, y0 + b);        //6LCD_DrawPoint(x0 - a, y0 + b);        //1LCD_DrawPoint(x0 - b, y0 + a);LCD_DrawPoint(x0 - a, y0 - b);        //2LCD_DrawPoint(x0 - b, y0 - a);        //7a++;//使用Bresenham算法画圆if (di < 0)di += 4 * a + 6;else{di += 10 + 4 * (a - b);b--;}}
}

4.1.8、显示字符函数:void LCD_ShowChar(u16 x, u16 y, u8 num, u8 size, u8 mode)

函数代码如下:

前两个参数(x,y)代表的是字符显示的起始坐标,num代表字符在字库中的位置,size代表了字节的大小,mode代表叠加方式。 

u8 csize 表示一个字节所占的字节数,横向的点是纵向点的1/2。

        接下来就是点开<font.h>这里面是取模的字库,接下来的程序会在这里面调用。

        for循环8位,一位一位的提取。先提取最高位(temp&0x80),然后通过画点LCD_Fast_DrawPoint(x, y, POINT_COLOR),画到显示屏中。

        如果mode==0非叠加方式,我们可以在上面加入底色LCD_Fast_DrawPoint(x, y, BACK_COLOR);

        画完点之后,纵坐标加一,连续取模。

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 - ' ';    //得到偏移后的值(ASCII字库是从空格开始取模,所以-' '就是对应字符的字库)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 (y >= lcddev.height)return;      //超区域了if ((y - y0) == size){y = y0;x++;if (x >= lcddev.width)return;   //超区域了break;}}}
}

4.1.9、写数字函数:void LCD_ShowNum(u16 x, u16 y, u32 num, u8 len, u8 size)

函数代码如下:

  该函数的优势是,不需要通过建模软件就可以直接写数字,通过num。

        但是如果num前面有多余的0不会显示。 

void LCD_ShowNum(u16 x, u16 y, u32 num, u8 len, u8 size)
{u8 t, temp;u8 enshow = 0;for (t = 0; t < len; t++){temp = (num / LCD_Pow(10, len - t - 1)) % 10;if (enshow == 0 && t < (len - 1)){if (temp == 0){LCD_ShowChar(x + (size / 2)*t, y, ' ', size, 0);continue;}else enshow = 1;}LCD_ShowChar(x + (size / 2)*t, y, temp + '0', size, 0);}
}

4.1.10、写数字函数:void LCD_ShowxNum(u16 x, u16 y, u32 num, u8 len, u8 size, u8 mode)

函数代码如下:

        该函数num前面有0,依旧显示0。

void LCD_ShowxNum(u16 x, u16 y, u32 num, u8 len, u8 size, u8 mode)
{u8 t, temp;u8 enshow = 0;for (t = 0; t < len; t++){temp = (num / LCD_Pow(10, len - t - 1)) % 10;if (enshow == 0 && t < (len - 1)){if (temp == 0){if (mode & 0X80)LCD_ShowChar(x + (size / 2)*t, y, '0', size, mode & 0X01);else LCD_ShowChar(x + (size / 2)*t, y, ' ', size, mode & 0X01);continue;}else enshow = 1;}LCD_ShowChar(x + (size / 2)*t, y, temp + '0', size, mode & 0X01);}
}

4.1.11、显示字符串的函数:void LCD_ShowString(u16 x, u16 y, u16 width, u16 height, u8 size, u8 *p)

函数代码如下:

void LCD_ShowString(u16 x, u16 y, u16 width, u16 height, u8 size, u8 *p)
{u8 x0 = x;width += x;height += y;while ((*p <= '~') && (*p >= ' '))   //判断是不是非法字符!{if (x >= width){x = x0;y += size;}if (y >= height)break; //退出LCD_ShowChar(x, y, *p, size, 0);x += size / 2;p++;}
}

4.2、主函数的调用

主函数main,如下所示:

#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "lcd.h"
#include "usart.h"int main(void){  u8 x=0;u8 lcd_id[12];         //存放LCD ID字符串delay_init();           //延时函数初始化    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);    //设置NVIC中断分组2:2位抢占优先级,2位响应优先级uart_init(115200);     //串口初始化为115200LED_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,210,24,24,"Follow me ^_^"); LCD_ShowString(30,70,200,16,16,"wlcome my space");LCD_ShowString(30,90,200,12,12,"bitter tea seeds");LCD_ShowString(30,110,200,16,16,lcd_id);      //显示LCD ID                           LCD_ShowString(30,130,200,12,12,"2022/7/17");                             x++;if(x==12)x=0;LED0=!LED0;                      delay_ms(1000);    }
}

学习记录:调用TFTLCD液晶屏相关推荐

  1. STM32——FSMC(灵活的静态存储器控制器)驱动TFTLCD液晶屏

    TFTLCD液晶屏介绍 TFTLCD即薄膜晶体管液晶显示器.它与无源TN-LCD.STN-LCD的简单矩阵不同,它在液晶显示屏的每一个象素上都设置有一个薄膜晶体管(TFT),可有效地克服非选通时的串扰 ...

  2. TFT-LCD液晶屏光学薄膜如何增加液晶屏亮度?

    TFT-LCD液晶屏光增亮膜是TFT-LCD背光模组的主要组成部分又叫棱镜片(Prism Sheet),其成本占总成本的比例最高.背光模块,也占总成本的2-8%.它的光学薄膜可以将散射光聚焦在前面的光 ...

  3. LCD显示屏和LED显示屏的区别,LCD液晶屏与LED显示屏什么区别

    LCD显示屏和LED显示屏的区别,LCD液晶屏与LED显示屏什么区别.下面由深圳市兴宇合电子技术人员为大家分析下,希望能有所帮助. 一.LED显示屏是什么东西呢 LED是发光二极管Light Emit ...

  4. 什么是液晶屏的极性变换(一)

    TFT-LCD 液晶屏是薄膜晶体管类型液晶屏,也叫"真彩色"(TFT).TFT液晶为每个像素配备了一个半导体开关,每个像素都可以通过点脉冲直接控制,因此每个节点相对独立,可以连续控 ...

  5. C++中调用usb摄像头并保存图片【学习记录第1篇】

    [学习记录第1篇]C++中通过OPENCV调用usb摄像头并保存图片 准备 外置USB摄像头代码 外置USB摄像头运行结果 外置USB超声波探头代码 外置USB超声波探头运行结果 第一篇博客的感想 准 ...

  6. IDL学习记录和Java调用IDL方法

    IDL学习记录和Java调用IDL方法 2018年02月06日 08:32:02 回首1949 阅读数:385更多 个人分类: 随想 版权声明:乐呵乐呵得了 https://blog.csdn.net ...

  7. 51单片机学习笔记-12LCD1602液晶屏

    12 LCD1602液晶屏 [toc] 注:笔记主要参考B站江科大自化协教学视频"51单片机入门教程-2020版 程序全程纯手打 从零开始入门". 注:工程及代码文件放在了本人的G ...

  8. 【C51单片机学习笔记----DS18B20温度传感器LCD1602液晶屏直流电机调速与呼吸灯AD模数转换红外外部中断】

    文章目录 一.DS18B20温度传感器 1.DS18B20温度传感器连接原理图 2.DS18B20温度传感器单总线通信时序 3.DS18B20温度传感器代码模块 二.LCD1602液晶屏 1.LCD1 ...

  9. OLED液晶屏操作记录

    今天在老谭的帮助下,操作了一下OLED,以前对于液晶显示不是很重视,特别是对于这种OLED的显示,更是没怎么好好的熟悉过,今天被老谭逼着,好好的练习了一下对于OLED的操作. 先来一些OLED的介绍. ...

最新文章

  1. oracle获取分组后的统计信息,并只要前五条
  2. c++ 标准库中 cin.ignore()
  3. ROS小白——knict相机标定(2)
  4. MySQL数据库安装Version5.7.25
  5. Ubuntu目录结构简单介绍笔记
  6. aes加密 js_吊打SRC的加密传输实现SQLi挖掘
  7. BGP 最佳路径选择之 -- Local Preference
  8. 华为 该软件被检测为风险软件_美团被华为标记为“病毒软件”,用户表示懵了,华为官方出面解释...
  9. 转行算法,90%的人还没开始就错在了这1步
  10. linux下svn常用命令集锦
  11. 转(matlab绘制不同形状)
  12. 查看磁盘阵列 使用率(简单)
  13. securecrt安装mysql_centos安装mysql(for 小白)
  14. 三、段地址与偏移地址
  15. java关闭自动更新_怎么关闭java自动更新
  16. tif数据84坐标经纬度转Unity3D坐标
  17. 下载文件夹压缩为zip格式时打开报不可预料的压缩文件末端
  18. win10怎么把两个盘合成一个,win10怎么把分区合并
  19. java银行项目对于金额的面试题,华为java面试视频直播
  20. Unity制作圆环进度条加载场景资源

热门文章

  1. 从单体开始的架构演进
  2. 家用路由器技术深入剖解
  3. Latex之添加删除线
  4. 为什么博客里的图片都显示不出来?
  5. 【转】C++多继承的细节
  6. Element-UI中打开本地文件
  7. python快速入门【三】-----For 循环、While 循环
  8. 实现登录注册页面详细(Servlet+jsp+java)
  9. 【电子电路】PT100 四线制高精度采样原理图和两线制和三线制接线办法
  10. 中金公司:券商IT广阔天际 明月不出群星闪耀