学习记录:调用TFTLCD液晶屏

目录

前言

        一、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的应用函数进行说明。

学习记录:调用TFTLCD液晶屏_第1张图片


 

前言

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

        一、TFT LCD简介 

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

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

学习记录:调用TFTLCD液晶屏_第2张图片  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液晶屏_第3张图片 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 模块的使用流程

学习记录:调用TFTLCD液晶屏_第4张图片

         (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();         //开始写入GRAM

    for (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();     //开始写入GRAM

        for (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();     //开始写入GRAM

        for (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);        //5
        LCD_DrawPoint(x0 + b, y0 - a);        //0
        LCD_DrawPoint(x0 + b, y0 + a);        //4
        LCD_DrawPoint(x0 + a, y0 + b);        //6
        LCD_DrawPoint(x0 - a, y0 + b);        //1
        LCD_DrawPoint(x0 - b, y0 + a);
        LCD_DrawPoint(x0 - a, y0 - b);        //2
        LCD_DrawPoint(x0 - b, y0 - a);        //7
        a++;

        //使用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。

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

        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);	 	//串口初始化为115200
 	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,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);	
	} 
}
 



你可能感兴趣的:(STM32学习笔记,机器学习,stm32,驱动开发)