当前位置: 首页 > news >正文

LINUX_LCD编程 TFT LCD

1:LCD屏幕

1.1:LCDTTL时序信号

1.2:LCD数据显示

        

一帧数据由多行组成,一行数据由多个像素点组成,每个像素由若干个数据位组成。对于单色显示器,每个像素为1位,称为1bpp,对于256色显示器,每个像素为8位,为8bpp。

        显示是从左上方开始,一行一行的将每个像素的颜色显示出来,当一行显示数据到最右边是,自动跳至下一列最左边开始执行,当数据至右下角时,调至左上方显示下一帧数据。

        该图结合下面时序图一起理解

LCD时序图

其中,VSYNC表示每一帧数据的开始,HSYNC表示每一行数据的开始,VCLK表示传输一个像素的数据。xrse个像素构成一行数据,yrse行数据构成一帧数据。xrse和yrse为分辨率,也就是一行有xrse个像素点,一列由yrse个像素点。

时序图讲解

        1)当VSYNC有效时,一帧数据开始传输。

        2)VSPW表示VSYNC信号脉冲宽度为(VSPW+1)个HSYNC信号周期,这几行数据无效。

        3)在经过HBPD+1个HSYNC个信号后,有效的数据行开始出现。

        注:在上图,我们可以看到VSYNC出现高电平之后,需要经过(VSPW+1)+(VBPD+1)个信号后,LINEVA+1的有效行才开始出现。(上图上黑框)

        4)LINEVA+1的有效行开始发送数据

        5)随后发送VFPD+1个无效行,(上图下黑框)

一行数据讲解。

        1)HSYNC信号有效后,一行数据开始传输

        2)首先前(HSPW+1)+(HBPD+1)个数据无效,第一个有效行像素开始出现。(上图左黑框)

        3)发送完一行数据之后,经过HFPD+1个无效数据点,(上图右黑框)

        4)下一个HSYN信号出现,开始传输下一行数据。

        

由图中我们可以知道,VSYNC他的频率表示了数据的帧率。一秒钟发送多少个VSYNC,则显示多少帧图片。

        VCLK= HCLK/[CLKVAL+1*2]

VSYNC是帧频率。

将VSYNC、HSYNC、VCLK等时间参数设置好之后,将帧内存Framebuffer地址告诉LCD控制器,他即可自动发起DMA传输从Framebuffer中得到数据,在上述信号的作用下出现在数据总线VD[23:0]上,用户只需要把现实的图像数据写入帧内存即可。

        注意:

        Framebuffer这个地址中,可以按照下图理解,假设每个像素位置占据2个字节,那么(0,0)所在的内存地址即为FB+0, (1,0)为FB+2,以此类推,下一列地址(0,1)内存地址为(FB+ xres*2).

像素存储

对于 24 16 BPP来说

        他们的Framebuffer存放的就是颜色

对于8bpp来说

        8BPP访问256调色板中对应的地址,将改地址的颜色 通过VD[23:0]发送出去。

1:16M(24BPP)像素

        即通过24条数据线VD[23:0]发起数据传输,RGB三色每个占据8条数据线。为方便BMA传输,每个像素使用4个字节32位来存储数据。

        设置不同的寄存器格式,数据排列方式不同

2:64K,16BPP

16BPP代表使用16条数据线传输RGB三色,分为两种接线方式 RGB为 5:6:5  和 5:6:5:1。

1个四字节表示两个BPP像素,

3:256色,8BPP

在8PP中,8条数据线要分给RGB,因此在这里,设置8条数据线的作用不是显示颜色了。而是我们定义一个256的调色板,这8条数据线的作用就是访问这256调色板中对应的地址,每个地址存放一种颜色。

8BBPP访问256调色板中对应的地址,将改地址的颜色 通过VD[23:0]发送出去。

调色板的起始地址 需要根据手册来确定。

2:寄存器

那么我门TTF只需要设置上2组寄存器即可。

2.1:LCDCON1

用于设置像素时钟,使能LCD信号输出等

 CLKVAL  与我们在时序中的 VCLK相关,负责1s有多少帧数据

BPPMOODE    像素格式

ENVID    使能位

2.2LCDCON2

用于设置垂直方向的时间参数

即我们图中的上下黑框,不同的LCD参数不同

2.3LCDCON3

设置水平方向的参数

即我们图中的左右黑框,不同的LCD参数不同

2.4 LCDCON4

对于TFT,这个寄存器只设置HSYNC信号的脉冲宽度【7:0】为HSPW

2.5 LCDCON5

设置控制信号的极性,并读取一些状态信息

2.6 帧内存地址 LCDSADDR1-LCDSADDR3

ADDR1寄存器格式

ADDR2寄存器格式

ADDR3寄存器格式

2.7 调色板

3:代码

为了方便调用LCD代码,以及以后添加新的LCD屏幕参数,和不同控制芯片对LCD的控制,采用面向对象编程方式。

首先,定义control.c和control.h文件,

          作用:将LCD_4_3所对应的参数,例如时序,bpp等,写入到S3C2440_LCD_Control.c中。

LCD_4_3

        作用,主要是配置LCD屏幕的参数。

S3C2440_LCD_Control.c

        配置LCD寄存器。

3.1:LCD.h函数编写

在LCD.h中,我们需要定义LCD屏幕他们需要的参数,并将其封装为结构体,在4.3存屏幕参数设置中,我们可以直接调用该结构体赋值。


#ifndef _LCD_H
#define _LCD_Henum {NORMAL = 0,INVERT = 1,
};/* NORMAL : 正常极性* INVERT : 反转极性*/
typedef struct pins_polarity {int de;    /* normal: 高电平时可以传输数据 */int pwren; /* normal: 高电平有效 */int vclk;  /* normal: 在下降沿获取数据 */int rgb;   /* normal: 高电平表示1 */int hsync; /* normal: 高脉冲 */int vsync; /* normal: 高脉冲 */
}pins_polarity, *p_pins_polarity;typedef struct time_sequence {/* 垂直方向 */int tvp; /* vysnc脉冲宽度 */int tvb; /* 上边黑框, Vertical Back porch */int tvf; /* 下边黑框, Vertical Front porch *//* 水平方向 */int thp; /* hsync脉冲宽度 */int thb; /* 左边黑框, Horizontal Back porch */int thf; /* 右边黑框, Horizontal Front porch */int vclk;
}time_sequence, *p_time_sequence;typedef struct lcd_params {char *name;/* 引脚极性 */pins_polarity pins_pol;/* 时序 */time_sequence time_seq;/* 分辨率, bpp */int xres;int yres;int bpp;/* framebuffer的地址 */unsigned int fb_base;
}lcd_params, *p_lcd_params;void get_lcd_params(unsigned int *fb_base, int *xres, int *yres, int *bpp);#endif /* _LCD_H */

3.2:LCD.c函数编写

        我们设置一个包含多种类型LCD屏幕参数结构体 的数组                                          p_lcd_params p_array_lcd[LCD_NUM];

        并设置一个选择函数,根据选择的LCD的名字,来给g_p_lcd_selected赋予不同的屏幕参数。

get_lcd_params函数的作用是,获取 所选择的LCD 屏幕的 Framebuffer xres yres bpp。

        LCD的初始化再函数lcd_init()中,再调用LCD时,一般只修改这里即可。就不需要涉及底层文件。

#include "lcd.h"
#include "lcd_controller.h"#define LCD_NUM 10static p_lcd_params p_array_lcd[LCD_NUM];
static p_lcd_params g_p_lcd_selected;int register_lcd(p_lcd_params plcd)
{int i;for (i = 0; i < LCD_NUM; i++){if (!p_array_lcd[i]){p_array_lcd[i] = plcd;return i;}}return -1;
}int select_lcd(char *name)
{int i;for (i = 0; i < LCD_NUM; i++){if (p_array_lcd[i] && !strcmp(p_array_lcd[i]->name, name)){g_p_lcd_selected = p_array_lcd[i];return i;}}return -1;
}void get_lcd_params(unsigned int *fb_base, int *xres, int *yres, int *bpp)
{*fb_base = g_p_lcd_selected->fb_base;*xres = g_p_lcd_selected->xres;*yres = g_p_lcd_selected->yres;*bpp = g_p_lcd_selected->bpp;
}void lcd_enable(void)
{lcd_controller_enable();
}void lcd_disable(void)
{lcd_controller_disable();
}int lcd_init(void)
{/* 注册LCD */lcd_4_3_add();/* 注册LCD控制器 */lcd_contoller_add();/* 选择某款LCD */select_lcd("lcd_4.3");/* 选择某款LCD控制器 */select_lcd_controller("s3c2440");/* 使用LCD的参数, 初始化LCD控制器 */lcd_controller_init(g_p_lcd_selected);
}

3.3 LCD_4_3.c设计

        这里我们所起的作用即将我们的4.3LCD的参数,写入到这个结构体中,为后续使用做铺垫

这里的参数现需要根据LCD手册来获取。


#include "lcd.h"#define LCD_FB_BASE 0x33c00000lcd_params lcd_4_3_params = {.name = "lcd_4.3",.pins_pol = {.de    = NORMAL,	/* normal: 高电平时可以传输数据 */.pwren = NORMAL,    /* normal: 高电平有效 */.vclk  = NORMAL,	/* normal: 在下降沿获取数据 */.rgb   = NORMAL,	/* normal: 高电平表示1 */.hsync = INVERT,    /* normal: 高脉冲 */.vsync = INVERT, 	/* normal: 高脉冲 */},.time_seq = {/* 垂直方向 */.tvp=	10, /* vysnc脉冲宽度 */.tvb=	2,  /* 上边黑框, Vertical Back porch */.tvf=	2,  /* 下边黑框, Vertical Front porch *//* 水平方向 */.thp=	41, /* hsync脉冲宽度 */.thb=	2,  /* 左边黑框, Horizontal Back porch */.thf=	2,  /* 右边黑框, Horizontal Front porch */.vclk=	9,  /* MHz */},.xres = 480,.yres = 272,.bpp  = 32,  /* 16, no 24bpp */.fb_base = LCD_FB_BASE,
};void lcd_4_3_add(void)
{register_lcd(&lcd_4_3_params);
}

注:

3.1 3.2 3.3这三节是相互关联的。

3.1 设计参数结构体, 3.3 将4.3屏幕的参数写入结构体  3.2将屏幕参数写入数组,为后续写入寄存器做准备。

3.4 LCD_Controller.h

这里,我们实现了一个结构体,该结构体中 包含除了名字之外,剩下的四个均为 函数指针

我们需要这四个函数指针 分别指向 控制器初始化函数 、 使能函数 、 失能函数 、 调色板函数。


#ifndef _LCD_CONTROLLER_H
#define _LCD_CONTROLLER_H#include "lcd.h"typedef struct lcd_controller
{char *name;void (*init)(p_lcd_params plcdparams);void (*enable)(void);void (*disable)(void);void (*init_palette)(void);
} lcd_controller, *p_lcd_controller;#endif /* _LCD_CONTROLLER_H */

3.5 LCD_Controller.c

      我们设置一个包含多种类型LCD屏幕控制器结构体的数组                                         static p_lcd_controller p_array_lcd_controller[LCD_CONTROLLER_NUM];

        并设置一个选择函数,根据选择的LCD的控制器的名字,来给g_p_lcd_controller_selected赋予不同的控制器函数。


#include "lcd_controller.h"#define LCD_CONTROLLER_NUM 10static p_lcd_controller p_array_lcd_controller[LCD_CONTROLLER_NUM];
static p_lcd_controller g_p_lcd_controller_selected;int register_lcd_controller(p_lcd_controller plcdcon)
{int i;for (i = 0; i < LCD_CONTROLLER_NUM; i++){if (!p_array_lcd_controller[i]){p_array_lcd_controller[i] = plcdcon;return i;}}return -1;
}int select_lcd_controller(char *name)
{int i;for (i = 0; i < LCD_CONTROLLER_NUM; i++){if (p_array_lcd_controller[i] && !strcmp(p_array_lcd_controller[i]->name, name)){g_p_lcd_controller_selected = p_array_lcd_controller[i];return i;}}return -1;
}/* 向上: 接收不同LCD的参数* 向下: 使用这些参数设置对应的LCD控制器*/int lcd_controller_init(p_lcd_params plcdparams)
{/* 调用所选择的LCD控制器的初始化函数 */if (g_p_lcd_controller_selected){g_p_lcd_controller_selected->init(plcdparams);g_p_lcd_controller_selected->init_palette();return 0;}return -1;
}void lcd_controller_enable(void)
{if (g_p_lcd_controller_selected){g_p_lcd_controller_selected->enable();}
}void lcd_controller_disable(void)
{if (g_p_lcd_controller_selected){g_p_lcd_controller_selected->disable();}
}void lcd_contoller_add(void)
{s3c2440_lcd_contoller_add();
}

3.6 S3C2440_Controller.c

将LCD参数写入到寄存器中


#include "lcd.h"
#include "lcd_controller.h"
#include "../s3c2440_soc.h"#define HCLK 100void jz2440_lcd_pin_init(void)
{/* 初始化引脚 : 背光引脚 */GPBCON &= ~0x3;GPBCON |= 0x01;/* LCD专用引脚 */GPCCON = 0xaaaaaaaa;GPDCON = 0xaaaaaaaa;/* PWREN */GPGCON |= (3 << 8);
}/* 根据传入的LCD参数设置LCD控制器 */
void s3c2440_lcd_controller_init(p_lcd_params plcdparams)
{int pixelplace;unsigned int addr;jz2440_lcd_pin_init();/* [17:8]: clkval, vclk = HCLK / [(CLKVAL+1) x 2]*                   9   = 100M /[(CLKVAL+1) x 2], clkval = 4.5 = 5*                 CLKVAL = 100/vclk/2-1* [6:5]: 0b11, tft lcd* [4:1]: bpp mode* [0]  : LCD video output and the logic enable/disable*/int clkval = (float)HCLK / plcdparams->time_seq.vclk / 2 - 1 + 0.5;// int clkval = 5;int bppmode = plcdparams->bpp == 8 ? 0xb : plcdparams->bpp == 16 ? 0xc: 0xd; /* 0xd: 24,32bpp */LCDCON1 = (clkval << 8) | (3 << 5) | (bppmode << 1);/* [31:24] : VBPD    = tvb - 1* [23:14] : LINEVAL = line - 1* [13:6]  : VFPD    = tvf - 1* [5:0]   : VSPW    = tvp - 1*/LCDCON2 = ((plcdparams->time_seq.tvb - 1) << 24) |((plcdparams->yres - 1) << 14) |((plcdparams->time_seq.tvf - 1) << 6) |((plcdparams->time_seq.tvp - 1) << 0);/* [25:19] : HBPD	 = thb - 1* [18:8]  : HOZVAL  = 列 - 1* [7:0]   : HFPD	 = thf - 1*/LCDCON3 = ((plcdparams->time_seq.thb - 1) << 19) |((plcdparams->xres - 1) << 8) |((plcdparams->time_seq.thf - 1) << 0);/** [7:0]   : HSPW	 = thp - 1*/LCDCON4 = ((plcdparams->time_seq.thp - 1) << 0);/* 用来设置引脚极性, 设置16bpp, 设置内存中象素存放的格式* [12] : BPP24BL* [11] : FRM565, 1-565* [10] : INVVCLK, 0 = The video data is fetched at VCLK falling edge* [9]  : HSYNC是否反转* [8]  : VSYNC是否反转* [7]  : INVVD, rgb是否反转* [6]  : INVVDEN* [5]  : INVPWREN* [4]  : INVLEND* [3]  : PWREN, LCD_PWREN output signal enable/disable* [2]  : ENLEND* [1]  : BSWP* [0]  : HWSWP*/pixelplace = plcdparams->bpp == 32 ? (0) : plcdparams->bpp == 16 ? (1): (1 << 1); /* 8bpp */LCDCON5 = (plcdparams->pins_pol.vclk << 10) |(plcdparams->pins_pol.rgb << 7) |(plcdparams->pins_pol.hsync << 9) |(plcdparams->pins_pol.vsync << 8) |(plcdparams->pins_pol.de << 6) |(plcdparams->pins_pol.pwren << 5) |(1 << 11) | pixelplace;/* framebuffer地址 *//** [29:21] : LCDBANK, A[30:22] of fb* [20:0]  : LCDBASEU, A[21:1] of fb*/addr = plcdparams->fb_base & ~(1 << 31);LCDSADDR1 = (addr >> 1);/** [20:0] : LCDBASEL, A[21:1] of end addr*/addr = plcdparams->fb_base + plcdparams->xres * plcdparams->yres * plcdparams->bpp / 8;addr >>= 1;addr &= 0x1fffff;LCDSADDR2 = addr; //
}void s3c2440_lcd_controller_enalbe(void)
{/* 背光引脚 : GPB0 */GPBDAT |= (1 << 0);/* pwren    : 给LCD提供AVDD  */LCDCON5 |= (1 << 3);/* LCDCON1'BIT 0 : 设置LCD控制器是否输出信号 */LCDCON1 |= (1 << 0);
}void s3c2440_lcd_controller_disable(void)
{/* 背光引脚 : GPB0 */GPBDAT &= ~(1 << 0);/* pwren	: 给LCD提供AVDD  */LCDCON5 &= ~(1 << 3);/* LCDCON1'BIT 0 : 设置LCD控制器是否输出信号 */LCDCON1 &= ~(1 << 0);
}// 设置调色板那之前先关闭LCD控制器
void s3c2440_lcd_controller_init_palette(void)
{int bit = LCDCON1 & (1 << 0);if (bit)LCDCON1 &= ~(1 << 0);// 4d000400-4d00041f 16色调色板volatile unsigned int *palette_base = (volatile unsigned int *)0x4d000400;int i;for (i = 0; i < 256; i++){// 存放565格式  低16位*palette_base++ = i;}if (bit)LCDCON1 |= (1 << 0);
}struct lcd_controller s3c2440_lcd_controller = {.name = "s3c2440",.init = s3c2440_lcd_controller_init,.enable = s3c2440_lcd_controller_enalbe,.disable = s3c2440_lcd_controller_disable,.init_palette = s3c2440_lcd_controller_init_palette,
};void s3c2440_lcd_contoller_add(void)
{register_lcd_controller(&s3c2440_lcd_controller);
}

再所有设置结束后,我们看LCD.C的init函数


int lcd_init(void)
{/* 注册LCD */lcd_4_3_add();/* 注册LCD控制器 */lcd_contoller_add();/* 选择某款LCD */select_lcd("lcd_4.3");/* 选择某款LCD控制器 */select_lcd_controller("s3c2440");/* 使用LCD的参数, 初始化LCD控制器 */lcd_controller_init(g_p_lcd_selected);
}

       1) 首先将LCD4_3添加到 参数 数组中 ,将 S3C2440控制器的的结构体添加至 控制器数组中

lcd_4_3.c函数中
void lcd_4_3_add(void){register_lcd(&lcd_4_3_params);}S3C2440_LCD_Controller.c函数中
struct lcd_controller s3c2440_lcd_controller = {.name = "s3c2440",.init = s3c2440_lcd_controller_init,.enable = s3c2440_lcd_controller_enalbe,.disable = s3c2440_lcd_controller_disable,.init_palette = s3c2440_lcd_controller_init_palette,
};void s3c2440_lcd_contoller_add(void)
{register_lcd_controller(&s3c2440_lcd_controller);
}

2) 选择LCD参数,和控制器参数

        位于LCD.C和LCD_Controller.c中。

3)将所选的 LCD参数 放入 控制器 初始化函数中, 使用该参数对控制器进行初始化。

该函数位于LCD_controller.c函数中,根据传入的参数对选择的控制器进行in

int lcd_controller_init(p_lcd_params plcdparams)
{/* 调用所选择的LCD控制器的初始化函数 */if (g_p_lcd_controller_selected){g_p_lcd_controller_selected->init(plcdparams);g_p_lcd_controller_selected->init_palette();return 0;}return -1;
}

4:测试

根据不同的BPP值,分别显示 红绿蓝。

void lcd_test(void)
{unsigned int fb_base;int xres, yres, bpp;int x, y;unsigned short *p;unsigned int *p2;unsigned char *p0;/* 初始化LCD */lcd_init();/* 使能LCD */lcd_enable();/* 获得LCD的参数: fb_base, xres, yres, bpp */get_lcd_params(&fb_base, &xres, &yres, &bpp);fb_get_lcd_params();font_init();/* 往framebuffer中写数据 */if (bpp == 16){/* 让LCD输出整屏的红色 *//* 565: 0xf800 */p = (unsigned short *)fb_base;for (x = 0; x < xres; x++)for (y = 0; y < yres; y++)*p++ = 0xf800;/* green */p = (unsigned short *)fb_base;for (x = 0; x < xres; x++)for (y = 0; y < yres; y++)*p++ = 0x7e0;/* blue */p = (unsigned short *)fb_base;for (x = 0; x < xres; x++)for (y = 0; y < yres; y++)*p++ = 0x1f;/* black */p = (unsigned short *)fb_base;for (x = 0; x < xres; x++)for (y = 0; y < yres; y++)*p++ = 0;}else if (bpp == 32){/* 让LCD输出整屏的红色 *//* 0xRRGGBB */p2 = (unsigned int *)fb_base;for (x = 0; x < xres; x++)for (y = 0; y < yres; y++)*p2++ = 0xff0000;/* green */p2 = (unsigned int *)fb_base;for (x = 0; x < xres; x++)for (y = 0; y < yres; y++)*p2++ = 0x00ff00;/* blue */p2 = (unsigned int *)fb_base;for (x = 0; x < xres; x++)for (y = 0; y < yres; y++)*p2++ = 0x0000ff;/* black */p2 = (unsigned int *)fb_base;for (x = 0; x < xres; x++)for (y = 0; y < yres; y++)*p2++ = 0;}else if (bpp == 8){/* 让LCD输出整屏的红色 *//* bpp = 8: 调色板中某个数值*/p0 = (unsigned char *)fb_base;for (x = 0; x < xres; x++)for (y = 0; y < yres; y++)*p0++ = 0xf12;/* green */p0 = (unsigned char *)fb_base;for (x = 0; x < xres; x++)for (y = 0; y < yres; y++)*p0++ = 0x49;/* blue */p0 = (unsigned char *)fb_base;for (x = 0; x < xres; x++)for (y = 0; y < yres; y++)*p0++ = 0x1f;/* black */p0 = (unsigned char *)fb_base;for (x = 0; x < xres; x++)for (y = 0; y < yres; y++)*p0++ = 0;}
}

相关文章:

  • 数据结构 [一] 基本概念
  • 【网络安全】fastjson原生链分析
  • Axure高保真LayUI框架 V2.6.8元件库
  • Python基础:文件简单操作
  • StringRedisTemplete使用
  • 网络安全中网络诈骗的攻防博弈
  • Pluto论文阅读笔记
  • RabbitMQ 开机启动配置教程
  • 各个布局的区别以及示例
  • Python 中 Django 中间件:原理、方法与实战应用
  • [论文阅读]PPT: Backdoor Attacks on Pre-trained Models via Poisoned Prompt Tuning
  • STP-生成树
  • 第四十二天打卡
  • MCP客户端Client开发流程
  • PyTorch——优化器(9)
  • Java设计模式深度解析:策略模式的核心原理与实战应用
  • 完成一个可交互的k8s管理平台的页面开发
  • MYSQL(二) ---MySQL 8.4 新特性与变量变更
  • Flutter快速上手,入门教程
  • 从OCR到Document Parsing,AI时代的非结构化数据处理发生了什么改变?
  • 菏泽疫情最新消息/重庆seo整站优化效果
  • 网站值不值得做seo/嘉兴关键词优化报价
  • 网页设计个人页面/福建seo顾问
  • 微信赌博链接网站建设/免费代理浏览网页
  • 网站导航条/站长工具四叶草
  • 珠海医疗网站建设公司/打开百度首页