lcd字模显示程序如何设计?
系统中显示部分的子程序与字模数据结构互相关联,这里将ascii字符显示子程序和单独显示汉字字模的子程序列出来,根据这两个子程序也可以看出显示部分的显示程序实现原理。
在这个子程序中,x指的是汉字行,只能是0~3共4行;y指的是半角字符列,只能是0~15共16列,因而可以在屏幕任何一个半角字符位置上显示一个ascii半角字符。在处理汉字时是将汉字当作两个半角字符来处理的,显示时将一个汉字分左上、左下、右上、右下4个部分顺序显示出来,从下面的显示汉字字模子程序中可以看出这一点。
从程序中可以看出,当j=0时写的是第工个汉字的左上和左下,然后在j的循环过程中依次显示的是第一个汉字的右上、右下,第二个汉字的左上、左下,第二个汉字的右上、右下。实际上lcd的起始页,表示起始显示的8行,也就是表示一个半角字符的上半部分。这就是显示部分汉字的显示过程。
s3c2410 lcd 驱动程序移植及gui程序编写:
1. 为了不让大家觉枯燥,让朋友们更好的理解,我以一个实例来叙述 s3c2410 下一个驱动程序的编写(本文的初始化源码以华恒公司提供的 s3c2410fb.c 为基础)及简单的 gui程序的编写。
2. 拿到一块 lcd,首先要将 lcd的各个控制线与 s3c2410 的 lcd控制信号相接,当然,电源也一定要接入了,否则不亮可别找我。另外需要注意以下几点:
1) 背光:对于大部分的彩色 lcd一定要接背光,我们才能看到屏上的内容;
2) 控制信号:不同的 lcd 厂商对于控制信号有不同的叫法,s3c2410 芯片手册也给出了一个信号的多个名称(图一),这就要看你们硬件工程师的功底了,
图一 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逻辑电压电路图
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) 接着看下面的代码,其中要修改的部分已经用绿色标出,下面分别进行介绍。
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控制器1
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
对于 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
对于 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
对于 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
这个寄存器的看起来比较复杂,但是无外乎这几类:
只读信息: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
这个寄存器的设置没有必要去修改(tft/stn),都使用默认的代码即可:
framebuffer 起始寄存器 2 和 framebuffer 起始寄存器 3
这两个寄存器的设置比较重要,在此我给出 12 位色 cstn 屏和 16 位色tft 的设置代码:
前面提到的 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可能没有任何显示哦(对于 tft 屏不需要这个语句)
6) 关于 12 位色的 cstn屏的驱动还需要做一些工作,我在这里简单介绍一下:
a) 首先要完成一个 fbcon-cfb12.c和 fbcon-cfb12.h 的编写,这两个文件很简单,在armlinux 中不是提供了 fbcon-cfb16.c 和 fbcon-cfb12.h 吗?简单修改一下就可以了;
b) 将 fbcon-cfb12.c 的编译加入 config.in 中(不会的话去 google 搜一下,或者看一下我的另一篇文章《jffs2 在 hharm2410 上的实现》,里边有一些说明),并定义一个 fbcon_has_cfb12 参数(模仿 fbcon_has_cfb16 呗);
c) 另外,需要在 s3c2410fb.c 中的相应部分加上对 12位色的支持即可。呵,说起来简单,但实际做起来可能会有一些问题,给大家一个窍门:在程序中找到#ifdef fbcon_has_cfb16 之类的代码,简单理解一下加上对 12 位色的支持;
d) 我只给出函数 s3c2410fb_set_var中的改动,其他的应该都不是很困难,相信朋友们都能搞定。
e) 不要跟我要源码哦,否则老板会不高兴哦 。
4. 驱动写好了,重新 make,下载就可以了。如果一切顺利,在 tft 屏或 256 色的 cstn屏上会有一个漂亮的小蜻蜓(应该是蜻蜓吧)出现。注意,并不是蜻蜓出现了就代表你的驱动 ok了,还要用 gui 程序做进一步的测试,因为某一个或几个参数虽然不正确,但是仍然能够看到小蜻蜓的,但显示图形的时候就有问题了。另外,在驱动 cstn到 12位色的时候,我们在屏上看不到小蜻蜓(我的 n块 cstn屏上都没见到小蜻蜓),我想,可能是 armlinux 本身不支持 12 位色显示,或者我们某些地方没搞对的原因吧,但这不代表你的驱动有问题,用 gui 程序写 framebuffer,看看能否的到正确的结果。
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 的。
下面给出我的程序的部分代码,希望对朋友们有所帮助。
1) 全局变量的定义:
定义几个全局变量,用起来方便。
2) 初始化图形显示引擎,将 fb0与 gui 的 buffer做个映射
用mmap函数使用户空间的一段地址关联到设备内存(framebuffer)上。无论何时,只要程序在分配的地址范围内进行读取或者写入,实际上就是对 设备的访问,使用 mmap 可以既快速又简单地访问显示卡的内存。对于象这样的性能要求比较严格的应用来说,直接访问能给我们提供很大不同。 不过我曾将帮一个网友调试了一个 s3c44b0 上的 gui 程序,在他的 gui 中 mmap 函数总会出错,因为没有拿到他的硬件和驱动源码,没有分析出其中的原因,所以只得用 write函数,直接向 fb0 写入数据,奇怪的是只写入一部分数据好像都不起任何作用,只得整屏数据写入才搞定了。这可就比较痛苦了,不过好在他只是写入的黑白数据,数据量还不是很 大,要是彩色的那可真的痛苦了 。
另外,我还想多啰嗦两句,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屏的高度 * 每像素的位数 / 每字节的位数
例如,一个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屏的输入极性相一致
中国投资内存项目总预算达180亿,欲终结三星市场垄断
数控机床网络控制系统设计概述
无人机所涉及的应用领域 只有你想不到的
LQR横向控制算法的求解
华为正式宣布P10和P10 Plus:前置指纹解锁
LCD驱动的移植及其GUI仿真如何进行,LCD数模转换现实原理及其源代码
董明珠物联网时代的野望
Allegro推出ACS70310电流检测解决方案中精度最高的电流传感器
半导体芯片供应商吉莱电子拟A股IPO
备货破千万,安卓新机皇三星S8为何如此有信心?
DDS信号生成模块的Verilog实现
腾讯宣布战略大调整,全面拥抱产业互联网
浅谈SSR的输入控制方法和电路驱动
社区沟通对于区块链有什么意义
抗辐射精密SAR ADC的单事件效应性能
5.5KW电机应该配置多大的交流接触器?
估算热插拔 MOSFET 的瞬态温升——第 1 部分
搜狗推出全仿真智能合成主持人——“AI合成主播”
吊白块残留检测仪的主要作用
变压器安装事项