LCD驱动的移植及其GUI仿真如何进行,LCD数模转换现实原理及其源代码

LED驱动

57人已加入

描述

  LCD字模显示程序如何设计?

  系统中显示部分的子程序与字模数据结构互相关联,这里将ASCII字符显示子程序和单独显示汉字字模的子程序列出来,根据这两个子程序也可以看出显示部分的显示程序实现原理。

lcd
lcd

  在这个子程序中,x指的是汉字行,只能是0~3共4行;y指的是半角字符列,只能是0~15共16列,因而可以在屏幕任何一个半角字符位置上显示一个ASCII半角字符。在处理汉字时是将汉字当作两个半角字符来处理的,显示时将一个汉字分左上、左下、右上、右下4个部分顺序显示出来,从下面的显示汉字字模子程序中可以看出这一点。

lcd
lcd

  从程序中可以看出,当j=0时写的是第工个汉字的左上和左下,然后在j的循环过程中依次显示的是第一个汉字的右上、右下,第二个汉字的左上、左下,第二个汉字的右上、右下。实际上LCD的起始页,表示起始显示的8行,也就是表示一个半角字符的上半部分。这就是显示部分汉字的显示过程。

  S3C2410 LCD 驱动程序移植及GUI程序编写:

  1. 为了不让大家觉枯燥,让朋友们更好的理解,我以一个实例来叙述 S3C2410 下一个驱动程序的编写(本文的初始化源码以华恒公司提供的 s3c2410fb.c 为基础)及简单的 GUI程序的编写。

  2. 拿到一块 LCD,首先要将 LCD的各个控制线与 S3C2410 的 LCD控制信号相接,当然,电源也一定要接入了,否则不亮可别找我。另外需要注意以下几点:

  1) 背光:对于大部分的彩色 LCD一定要接背光,我们才能看到屏上的内容;

  2) 控制信号:不同的 LCD 厂商对于控制信号有不同的叫法,S3C2410 芯片手册也给出了一个信号的多个名称(图一),这就要看你们硬件工程师的功底了,

lcd

  图一 S3C2410 手册上给出的控制信号的名称及解释

  这里我做一个简单的介绍:

  VFRAME:LCD 控制器和 LCD 驱动器之间的帧同步信号。该信号告诉 LCD屏的新的一帧开始了。LCD 控制器在一个完整帧显示完成后立即插入一个VFRAME 信号,开始新一帧的显示;

  VLINE:LCD控制器和 LCD驱动器之间的线同步脉冲信号,该信号用于 LCD驱动器将水平线(行)移位寄存器的内容传送给 LCD 屏显示。LCD 控制器在整个水平线(整行)数据移入 LCD驱动器后,插入一个 VLINE 信号;

  VCLK:LCD控制器和 LCD驱动器之间的像素时钟信号,由 LCD控制器送出的数据在 VCLK的上升沿处送出,在 VCLK的下降沿处被 LCD驱动器采样;

  VM:LCD驱动器的 AC 信号。VM 信号被 LCD驱动器用于改变行和列的电压极性,从而控制像素点的显示或熄灭。VM 信号可以与每个帧同步,也可以与可变数量的 VLINE 信号同步。

  3) 数据线:也就是我们说的 RGB 信号线,S3C2410 芯片手册上都有详细的说明,由于篇幅关系,在此不一一摘录,不过需要与硬件工程是配合的是他采用了哪种接线方法,24 位 16 位或其它。对于 16 位 TFT 屏又有两种方式,在写驱动前你要清楚是 5:6:5还是 5:5:5:I,这些与驱动的编写都有关系

  4) 要注意一下 LCD 的电源电压,对于手持设备来说一般都为 5V 或 3.3V,或同时支持 5V和 3.3V,如果 LCD的需要的电源电压是 5V,那就要注意了,S3C2410 的逻辑输出电压只有 3.3V,此时一定要让你们的硬件工程师帮忙把 S3C2410 的逻辑输出电压提高到 5V,否则你可能能将屏点亮,但显示的图像要等到太阳从西边出来的那一天才能正常,呵呵,我可吃过苦头的哦!

  5) 3.3V逻辑电压转变成 5V逻辑电压电路图

lcd

  6) 最后还有一个问题,有些 LCD 屏还需要一颗伴侣芯片,就是 S3C2410 手册中的那颗 LPC3600。这可能在 LCD 的手册中都有论述吧,我没有遇到过这样的屏,所以也不是很清楚。那么是不是所有的屏与 S3C2410相接都需要那个讨厌的家伙呢?这是好多人(包括我)在最开始都会有的疑问,不过现在的大部分 LCD 屏应该都不需要这个讨厌的家伙了,屏的控制信号直接与 S3C2410 的控制信号相接就可以了,至少我还没有遇到过。

  7) 还得提醒大家一下,S3C2410到 LCD屏的连线千万千万别超过 0.5 米,否则会给你带来麻烦,我也是吃过苦头的,LCD屏上面的部分显示任何信息都是正确的,而只有屏的底部会有时正确有时错误,折腾了好一阵,才知道是连线太长的缘故!

  3. 好了,在硬件工程师的帮助下,硬件接好了,那就该我们做软件的干活了,编写驱动吧

  1) 让我们首先看一下 RGB数据结构的定义

  在 s3c2410fb.c 中找到如下信息

  static struct s3c2410fb_rgb xxx_tft_rgb_16 = {

  red: {offset:11, length:5,},

  green: {offset:5, length:6,},

  blue: {offset:0, length:5,},

  transp: {offset:0, length:0,},

  };

  这是对 16 位色的 RGB 颜色进行定义,R:G:B:I = 5:6:5:0,即我们常说的565 显示方式。呵呵,为了让有些朋友更好的理解,我多罗嗦几句,我们随便写一个 16 位数据的颜色数据(为了分析的方便,我把它写成二进制)

  RGB = 10101101 10111001

  根据上面的结构定义我们来分析一下 RGB 各是多少(因为没有透明色,我们不去分析)

  a) blue: {offset: 0, length: 5} 偏移量为 0,长度为 5,我们从那个 RGB 中提取出来便是“11001”

  b) green:{offset: 5, length: 6} 偏移量为 5,长度为 6,我们从那个 RGB 中提取出来便是 101 101

  c) red: {offset: 11, length: 5 } 偏移量为 11,长度为 5,我们从那个 RGB 中提取出来便是 10101

  d) 我们得到了一个 RGB 值为 13:45:200,就是这个颜色

  e) 那么反过来,有了 RGB的值我们该如何,因为 RGB 的有效位数都不足一个字节(8 位),那我们只能忍痛割爱了,舍弃掉低位数据,代码如下

  r=(rDat&0xF8);

  g=(gDat&0xFC);

  b=(bDat&0xF8);

  hight=r|(g《《5);

  low=(g》》3)|(b《《3);

  color=(hight》》8)|low;

  记住,这段代码在 GUI 程序中是有用的

  2) 对于 8 位色(256 色)的数据结构定义

  static struct s3c2410fb_rgb rgb_8 = {

  red: {offset:0, length:4,},

  green: {offset:0, length:4,},

  blue: {offset:0, length:4,},

  transp: {offset:0, length:0,},

  };

  这是原程序中给出的定义,我感觉有些错误,我认为应该为 R:G:B = 3:3:2

  static struct s3c2410fb_rgb rgb_8 = {

  red: {offset:5, length:3,},

  green: {offset:2, length:3,},

  blue: {offset:0, length:2,},

  transp: {offset:0, length:0,},

  };

  因为没有亲自去调试,所以没有什么发言权,希望做过这方面的朋友给我一个答案。

  3) 对于 CSTN 屏,一般都能达到 12 位色(4096 色)的,S3C2410 这颗芯片也是支持的,但是在软件方面要做的工作比较大,因为从原有的代码,我们找不到任何 12位色显示的迹象,另外 Linux 本身好像也不支持 12 位色的,如果你要作的事情比较简单,那你就自己写代码吧。我在此给出 12位色的数据结构定义

  static struct s3c2410fb_rgb xxx_stn_rgb_12 = {

  red: {offset:8, length:4,},

  green: {offset:4, length:4,},

  blue: {offset:0, length:4,},

  transp: {offset:0, length:0,},

  };

  但是要完成 12 位色 CSTN 屏驱动程序的编写还有一些工作要做,稍后我会适当的向大家介绍。

  4) 接着看下面的代码,其中要修改的部分已经用绿色标出,下面分别进行介绍。

lcd

  a) 颜色位数

  bpp:16

  如果你的 LCD 屏是 TFT 的,那一般都可以达到 16 位色或 24 位色,这也要看硬件怎么连接了,根据情况进行设置即可;

  如 果你的 LCD屏是 CSTN的,按照常规 LCD手册的介绍,一般都可以支持到8 位色(256色),而实际的 CSTN屏的显示效果都可以达到 12 位色(4096色),那可有很大的区别的,如果你要选择便宜的屏又要丰富的颜色,那就费点劲,完成 12 位色的驱动。

  b) LCD屏的宽度和高度

  xres: 240

  yres: 320

  这个就不用多说了,你的屏的分辨率是多少就设置成多少呗。

  c) 寄存器的设置,这些也不困难。下面就让我们一起一口一口的将 S3C2410 的LCD寄存器统统吃掉! 首先介绍一下我这块屏,这是日立的一块 TFT 屏,大小为 640X240,可以支持到 16位色。 与驱动有关的一张表

lcd

  图二 LCD屏资料

  有了这些信息,让我们看一下 LCD寄存器的设置。

  LCD控制器1

lcd

  LINECNT --- 这是一个只读的数据,我们当然没有必要理它

  CLKVAL --- 这可是一个很有用的参数,其实没必要管它后面的计算,我们可以通过实际的测试来得出一个有效的值,对于320x240 的屏一般设置为 7 就可以了,而对于 640x480 的屏,该值可以小一点。对于后面的计算公式及注释(STN: CLKVAL 《= 2,TFT: CLKVAL 《= 0),我不知道该如何去理解,因为在实际的应用中我点了一块 640X240 的CSTN 屏,当我的 CLKVAL = 1 时才达到了一个最佳的效果,这似乎与说明书相违背,我也解释不清为什么?!

  PNRMODE --- 这个应该不用多做解释,大家一看都明白了,对于 TFT 屏,只能设置成 11,而对于 CSTN 屏,可能需要根据实际屏的信息去设置,我遇到的屏都设置成 10,即 8bit 单扫描模式。对于4bit单扫描、4bit 双扫描、8bit 单扫描的说明在 s3c2410 的手册中有详细的介绍,大家可以去参考一下。

  BPPMODE --- 这个参数更不用多说了吧,就是设置屏的颜色位数喽。

  这些参数的设置都很简单,我给出我这块屏的定义:

  lcdcon1: LCD1_BPP_16T | LCD1_PNR_TFT | LCD1_CLKVAL(1),

  同时,我也给出一块 CSTN 屏的寄存器参数信息

  lcdcon1: LCD1_BPP_12S | LCD1_PNR_8S | LCD1_CLKVAL(9),

  LCD控制器2

lcd

  对于 TFT 屏必须要填,至于什么意思怎么翻译,相信大家都比我的水平强,自己翻译吧。我只说明从 LCD中如何将这个值“扣”出来。

  很容易,看一下图二 LCD屏资料,对比一下得出如下信息:

  LCD2_VBPD:

  Vertical back porch 典型值为 7

  LCD2_VFPD:

  Vertical front porch 典型值为 4

  LCD2_VSPW:

  Vsync Valid width 典型值为 2

  关于 LINEVAL 在程序的后面将会提到,此处不必理会。

  经过分析,我们知道了如何设置 LCD2:

  lcdcon2: LCD2_VBPD(7) | LCD2_VFPD(4) | LCD2_VSPW(2),

  对于 STN(CSTN)屏,这个寄存器的设置最简单,将 VBPD、VFPD、VSPW 都设置成 Zero 就可以了。即

  lcdcon2: LCD2_VBPD(0) | LCD2_VFPD(0) | LCD2_VSPW(0),

  LCD控制器3

lcd

  对于 TFT 屏,很容易将 HBPD 和 HFPD 找出来,如下

  LCD3_HBPD:

  Horizontal back porch 典型值为 37

  LCD3_HFBD:

  Horizontal back porch 典型值为 32

  对于 HOZVAL 同样会在后面提到,此处暂时不管

  经过分析,我们知道了如何设置 LCD3:

  lcdcon3: LCD3_HBPD(37) | LCD3_HFPD(32) ,

  对于(STN)CSTN屏,我没有很好的理解 WDLY 和 LINEBLANK 的真正涵义,通过改变这两个参数的值,我也没有得到特别明显的差异,我一般设置为:

  lcdcon3: LCD3_WDLY_16 | 0x10 ,

  LCD控制器4

lcd

  对于 TFT 屏,需要设置 HSPW 的值,这个在 LCD 手册上也很容易得到

  LCD4_HSPW:

  Hsync Valid width 典型值为 5

  至于 MVAL,我不知道是什么意思,有什么作用,我从来不动它,只取它最初的那个值 13

  经过分析,我们知道了如何设置 LCD4:

  lcdcon4: LCD4_HSPW(5) | LCD4_MVAL(13) ,

  对于 STN(CSTN)屏,像 WDLY 一样,我通常不改变,因为改变了没有发现有什么作用,这是我驱动中的代码,好几块屏都一样的:

  lcdcon4: LCD4_WLH(0) | LCD4_MVAL(13) ,

  LCD控制器5

lcd

  这个寄存器的看起来比较复杂,但是无外乎这几类:

  只读信息:VSTATUS和 HSTATUS

  只读的东东,设置它也没用,不必理会。

  TFT 屏的颜色信息:BPP24BL、FRM565

  TFT 屏的颜色信息,这个我们在 LCD的硬件连接时已经提到了,根据具体的接线方式,设置信息。

  控制信号的极性

  TFT/STN 屏控制信号的极性:INVVCLK、INVVLINE、INVVFRAME、INVVD、INVPWREN、PWREN

  TFT 屏特有的控制信号的极性:INVVDEN、INVLEND、ENLEND

  这些信息主要是使S3C2410的信号输出极性与LCD屏的输入极性的问题,需要根据具体的硬件进行设置,较为常见的是vline/hsync 、VFRAME/VSYNC脉冲的极性。

  颜色信息的字节交换控制位:BSWP、HWSWP

  这两位用来控制字节交换和半字交换,主要用来大小头的问题,如果输出到屏上的汉字左右互换了,或者输出到屏上的图花屏了,可以更改这个选项。具体涵义在 S3C2410芯片手册上有详细的说明。

  我的这块 TFT 的信息设置如下:

  lcdcon5: LCD5_FRM565 | LCD5_HWSWP | LCD5_PWREN ,

  一块 CSTN屏的信息

  lcdcon5: LCD5_BSWP | LCD5_PWREN ,

  FrameBuffer 起始寄存器 1

lcd

  这个寄存器的设置没有必要去修改(TFT/STN),都使用默认的代码即可:

lcd

  FrameBuffer 起始寄存器 2 和 FrameBuffer 起始寄存器 3

  这两个寄存器的设置比较重要,在此我给出 12 位色 CSTN 屏和 16 位色TFT 的设置代码:

lcd

  前面提到的 LINEVAL 和 HOZVAL 以源码的形式给出,其中 CSTN 8 位色没有经过测试。

  RGB Loopup Table Register

  这三个寄存器的在驱动 256 色 CSTN 屏的时候需要使用,我在别的芯片上使用过,因为这颗芯片支持 12 位色,所以没有去调试,我给

  出两组可能的值:

  S3C44B0 上的

  rREDLUT = 0xFCA86420;

  rGREENLUT = 0xFCA86420;

  rBLUELUT = 0xFFFFFA50;

  Jupiter 上的

  rREDLUT = 0xFEC85310

  rGREENLUT = 0xFEC85310

  rBLUELUT = 0xFB40

  5) 好了,各个寄存器的设置完成了,最后在驱动 CSTN屏的时候需要提醒大家一句,CSTN的信号引脚中有一个叫VM/DISP的信号线,这个信号线的作用就是打开LCD的显示开关,让其进行显示,它 可以接到任何一个 GPIO 口上。S3C2410 中提供了一个 VM 信号,可以将 LCD的这个信号与 S3C2410 的 VM 信号相接即可,然后在驱动中一定要加上如下语句(蓝色选中部分):

lcd

  否则你的 LCD可能没有任何显示哦(对于 TFT 屏不需要这个语句)

  6) 关于 12 位色的 CSTN屏的驱动还需要做一些工作,我在这里简单介绍一下:

  a) 首先要完成一个 fbcon-cfb12.c和 fbcon-cfb12.h 的编写,这两个文件很简单,在armLinux 中不是提供了 fbcon-cfb16.c 和 fbcon-cfb12.h 吗?简单修改一下就可以了;

lcd

  b) 将 fbcon-cfb12.c 的编译加入 Config.in 中(不会的话去 google 搜一下,或者看一下我的另一篇文章《JFFS2 在 HHARM2410 上的实现》,里边有一些说明),并定义一个 FBCON_HAS_CFB12 参数(模仿 FBCON_HAS_CFB16 呗);

  c) 另外,需要在 s3c2410fb.c 中的相应部分加上对 12位色的支持即可。呵,说起来简单,但实际做起来可能会有一些问题,给大家一个窍门:在程序中找到#ifdef FBCON_HAS_CFB16 之类的代码,简单理解一下加上对 12 位色的支持;

lcd

  d) 我只给出函数 s3c2410fb_set_var中的改动,其他的应该都不是很困难,相信朋友们都能搞定。

  e) 不要跟我要源码哦,否则老板会不高兴哦 。

  4. 驱动写好了,重新 Make,下载就可以了。如果一切顺利,在 TFT 屏或 256 色的 CSTN屏上会有一个漂亮的小蜻蜓(应该是蜻蜓吧)出现。注意,并不是蜻蜓出现了就代表你的驱动 OK了,还要用 GUI 程序做进一步的测试,因为某一个或几个参数虽然不正确,但是仍然能够看到小蜻蜓的,但显示图形的时候就有问题了。另外,在驱动 CSTN到 12位色的时候,我们在屏上看不到小蜻蜓(我的 N块 CSTN屏上都没见到小蜻蜓),我想,可能是 armLinux 本身不支持 12 位色显示,或者我们某些地方没搞对的原因吧,但这不代表你的驱动有问题,用 GUI 程序写 FrameBuffer,看看能否的到正确的结果。

lcd

  5. GUI 程序的编写

  FrameBuffer 驱动写好了,那么怎么去使用,怎么在 LCD 上显示图像呢?这就是 GUI程序的任务了,其实要在 LCD 上显示图像,说白了就是把数据(包含颜色)写到FrameBuffer 中对应的位置就可以了。如果你使用如 Microwinow、MiniGui、Qt 之类的GUI,则没有必要关心 FrameBuffer与 LCD屏上的点如何进行映射了,但如果你在使用了 CSTN 屏,并且要显示效果好的照片,选择了 CSTN 的 12 位色(4096色 ),那你就要自己写 GUI 程序了,因为好像 armLinux(Linux)本身都不支持 12 位色的,听说 MiniGui支持 12 位色,但我在工作中的要求只是显示图形而已,没有去深入研究 MiniGui,所以自己写了。

  另外请朋友们见谅的是我不能给出全部的源代码,因为我毕竟受雇于人,有些东东是可以 GPL 的,而有些东东暂时是不可以 GPL 的。

lcd

  下面给出我的程序的部分代码,希望对朋友们有所帮助。

  1) 全局变量的定义:

  定义几个全局变量,用起来方便。

  2) 初始化图形显示引擎,将 fb0与 GUI 的 buffer做个映射

  用mmap函数使用户空间的一段地址关联到设备内存(FrameBuffer)上。无论何时,只要程序在分配的地址范围内进行读取或者写入,实际上就是对 设备的访问,使用 mmap 可以既快速又简单地访问显示卡的内存。对于象这样的性能要求比较严格的应用来说,直接访问能给我们提供很大不同。 不过我曾将帮一个网友调试了一个 S3C44B0 上的 GUI 程序,在他的 GUI 中 mmap 函数总会出错,因为没有拿到他的硬件和驱动源码,没有分析出其中的原因,所以只得用 write函数,直接向 fb0 写入数据,奇怪的是只写入一部分数据好像都不起任何作用,只得整屏数据写入才搞定了。这可就比较痛苦了,不过好在他只是写入的黑白数据,数据量还不是很 大,要是彩色的那可真的痛苦了 。

lcd

  另外,我还想多啰嗦两句,FrameBuffer的像素点与LCD屏上的像素点的对应关系 ,深入了解一下对程序的理解可能会更清楚一点。我们知道黑白(2 色)颜色用 0 和 1 就可以表示了,也就是 1 位数据就可以了,那 1 个字节就可以表示 8 位数据,假如这个字节是10101010,FrameBuffer 的偏移地址为 0,则在 LCD 屏上便会显示出 4 个黑点,黑点中间会有 4 个白点出现(假如 1 是黑色);对于 4 色则用 00、01、10、11 就可以表示出四种颜色,即用两位数据可以表示一位数据,那同样是 10101010,则对应于 LCD 屏上则显示的

  是颜色值为10,长度为4(8/2)的一条直线;同理,对于8位色(256色),则8位数据才能表示出一个点的颜色值,10101010在LCD屏上就只能显示为颜色值为10101010的点了。

  有了上面的基础我们就可以很好的理解这个语句了:

  screensize = vinfo.xres*vinfo.yres*vinfo.bits_per_pixel/8;

  即FrameBuffer 的大小=LCD屏的宽度 * LCD屏的高度 * 每像素的位数 / 每字节的位数

lcd

  例如,一个320*240的黑白平,FrameBuffer的大小为

  320 * 240 * 1 / 8 = 9600 (字节)

  而一个320 * 240的16位色LCD的 FrameBuffer的大小则为

  320 * 240 * 16 / 8 = 153600(字节)

  3) TFT 屏 16 位色的画点函数

  有了画点函数,你还愁什么?图形汉字都可以搞定了吧!

  4) CSTN屏 12位色的画点函数

  注意,为了更便于代码书写,我在这个函数中将 fbp 定义为 static char * fbp,而在TFT 屏 16 位色的画点函数中 fbp 的定义为 U16 * fbp,你可以根据需要进行修改。

  5) TFT 屏 16 位色下显示 24色位图函数

  Bmp文件的格式可以参考网上的一些资料,如果需要也可以直接找我要。

  6) CSTN屏 12位色下显示 24 色位图函数

  7) 呵呵,别忘了关闭设备哦

  void closegraph()

  {

  munmap(fbp,screensize);

  close(fb);

  }

  收获:

  1、LCD的电源电压,对于手持设备来说一般都为 5V 或 3.3V,或同时支持 5V和 3.3V,如果 LCD的需要的电源电压是 5V,那就要注意了,S3C2410 的逻辑输出电压只有 3.3V,此时一定要让你们的硬件工程师帮忙把 S3C2410 的逻辑输出电压提高到 5V,否则不能将屏点亮

  2、S3C2410到 LCD屏的连线千万千万别超过 0.5 米,否则出现问题。如LCD屏上面的部分显示任何信息都是正确的,而只有屏的底部会有时正确有时错误。

  3、8位色的数据结构定义:

  static struct s3c2410fb_rgb rgb_8 = {

  red: {offset:5, length:3,},

  green: {offset:2, length:3,},

  blue: {offset:0, length:2,},

  transp: {offset:0, length:0,},

  };

  R:G:B = 3:3:2

  4、CLKVAL的确定:

  CLKVAL --- 这可是一个很有用的参数,其实没必要管它后面的计算,我们可以通过实际的测试来得出一个有效的值,对于320x240 的屏一般设置为 7 就可以了,而对于 640x480 的屏,该值可以小一点。

  5、在设置LCDCON5时,通过 TFT/STN 屏控制信号的极性,使S3C2410的信号输出极性转换后与LCD屏的输入极性相一致

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分