入局:应用程序是如何 *** 控LCD显示器的?
我们知道应用程序的调用接口,无非 open/read/write ...然后通过驱动程序最终作用到硬件设备上。以字符设备为例,对于驱动的开发者,实现了应用程序调用的驱动层中与之相匹配的 drv_open/drv_read/drv_write 函数,为应用层序提供了 *** 作实际硬件设备的通道。那么,对于LCD驱动程序又是如何?先来了解下两个非常重要的概念。
LCD控制器的功能是控制驱动信号,进而驱动LCD。用户只需要通过读写一系列的寄存器,完成配置和显示驱动。在驱动LCD设计的过程中首要的是配置LCD控制器,而在配置LCD控制器中最重要的一步则是帧缓冲区(Frame Buffer)的指定。用户所要显示的内容皆是从缓冲区中读出,从而显示到屏幕上的。帧缓冲区的大小由屏幕的分辨率和显示色彩数决定。驱动帧缓冲的实现是整个驱动开发过程的重点。
帧缓冲区是出现在Linux 2.2.xx及以后版本内核当中的一种驱动程序接口,这种接口将显示设备抽象为帧缓冲区设备区。帧缓冲区为图像硬件设备提供了一种抽象化处理,它代表了一些视频硬件设备,允许应用软件通过定义明确的界面来访问图像硬件设备。这样软件无须了解任何涉及硬件底层驱动的东西(如硬件寄存器)。它允许上层应用程序在图形模式下直接对显示缓冲区进行读写和I/O控制等 *** 作。通过专门的设备节点可对该设备进行访问,如/dev/fb*。用户可以将它看成是显示内存的一个映像,将其映射到进程地址空间之后,就可以进行读写 *** 作,而读写 *** 作可以反映到LCD。
帧缓冲(Frame Buffer)是Linux为显示设备提供的一个接口,把显存抽象后的一种设备,允许上层应用程序在图形模式下直接对显示缓冲区进行读写 *** 作。用户不必关心物理显存的位置、换页机制等等具体细节,这些都是由Frame Buffer设备驱动来完成的。帧缓冲设备属于字符设备。
Linux系统Frame Buffer本质上只是提供了对图形设备的硬件抽象,在开发者看来,Frame Buffer是一块显示缓存,向显示缓存中写入特定格式的数据就意味着向屏幕输出内容。
由于有了frambuffer的抽象,使得应用程序通过定义好的接口就可以访问硬件。所以应用程序不需要考虑底层的(寄存器级)的 *** 作。应用程序对设备文件的访问一般在/dev目录,如 /dev/fb*。
内核中的frambuffer在: drivers/video/fbmem.c (fb: frame buffer)
(1) 创建字符设备"fb", FB_MAJOR=29,主设备号为29。
(2)创建类,但并没有创建设备节点,因为需要注册了LCD驱动后,才会有设备节点;
2.1 fb_open函数如下:
(1) registered_fb[fbidx] 这个数组也是fb_info结构体,其中fbidx等于次设备号id,显然这个数组就是保存我们各个lcd驱动的信息
2.2 fb_read函数如下:
从.open和.read函数中可以发现,都依赖于fb_info帧缓冲信息结构体,它从registered_fb[fbidx]数组中得到,这个数组保存我们各个lcd驱动的信息。由此可见,fbmem.c提供的都是些抽象出来的东西,最终都得依赖registered_fb这个数组。
这个register_framebuffer()除了注册fb_info,还创建了设备节点。
以s3c2410fb.c为例,分析驱动的实现。
既然是总线设备驱动模型,那我们关心的是它的probe函数。
看到这里驱动的写法也大致清晰:
附:
LCD的显示过程与时序:
1.显示从屏幕左上角第一行的第一个点开始,一个点一个点地在LCD上显示,点与点之间的时间间隔为VCLK(像素时钟信号);当显示到屏幕的最右边就结束这一行(Line),这一行的显示对应时序图上的HSYNC(水平同步信号)
2. 接下来显示指针又回到屏幕的左边从第二行开始显示,显示指针针在从第一行的右边回到第二行的左边是需要一定的时间的,我们称之为行切换。
3. 以此类推,显示指针就这样一行一行的显示至矩形的右下角才把一幅图像(帧:frame)显示完成,这一帧的显示时间在时序图上表示为VSYNC(垂直同步信号)。
参考:
https://sites.google.com/a/hongdy.org/www/linux/kernel/lcddriver
1.打开/dev/fbXfp = open ("/dev/fb0",O_RDWR)
2.获取可变参数,固定参数
ioctl(fp,FBIOGET_VSCREENINFO,&vinfo) 可变参数
ioctl(fp,FBIOGET_FSCREENINFO,&finfo) 固定参数
3.内存映射(mmap)
screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8
/*这就是把fp所指的文件中从开始到 screensize 大小的内容给映射出来,得到一个指向这块空间的指针*/
fbp =(unsigned char *) mmap (0, screensize,
PROT_READ | PROT_WRITE,
MAP_SHARED, fp,0)
4.使用映射后的地址对屏进行 *** 作。
使用上面得到的 fbp 指针来 *** 作显示缓冲区了。
把lcd看成是一块内存,使用mmap函数把它的缓冲区映射到进程空间中,然后通过映射后的地址直接 *** 作驱动中的显示缓冲区,往这块缓冲写数据,lcd就会按数值转换成相应颜色显示在LCD屏上。
本移植主要参考友善之臂移植手册完成,做个笔记以备不时之需Linux-2.6.32 内核LCD驱动移植
使用环境:fedora9
交叉编译工具链:arm-linux-gcc-4.4.3
内核源码来源:https://www.kernel.org/pub/linux/kernel/v2.6/
内核存放目录:/opt/mymini2440/linux-2.6.32
一、LCD背光驱动移植
在、opt/mymini2440/linux-2.6.32/drivers/video/目录下添加背光驱动程序mini2440_backlight.c,内容如下:
//以下头文件可能并不是每一个都必须的,但多余的并不会影响驱动程序的内容
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/miscdevice.h>
#include <linux/gpio.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <mach/regs-clock.h>
#include <plat/regs-timer.h>
#include <mach/regs-gpio.h>
#include <linux/cdev.h>
#undef DEBUG
//#define DEBUG
#ifdef DEBUG
#define DPRINTK(x...) {printk(__FUNCTION__"(%d): ",__LINE__)printk(##x)}
#else
#define DPRINTK(x...) (void)(0)
#endif
//定义背光驱动的名称为backligh,将会出现在/dev/backlight
#define DEVICE_NAME "backlight"
//定义背光变量bl_state,以记录背光的开关状态
static unsigned int bl_state
//设置背光开关的函数,主要是翻转背光变量bl_state
static inline void set_bl(int state)
{
bl_state = !!state//翻转bl_state 变量
s3c2410_gpio_setpin(S3C2410_GPG(4), bl_state)//把结果写入背光所用的寄存器GPG4
}
//获取背光状态
static inline unsigned int get_bl(void)
{
return bl_state
}
//从应用程序读取参数,并传递到内核中
static ssize_t dev_write(struct file *file, const char *buffer, size_t count, loff_t * ppos)
{
unsigned char ch
int ret
if (count == 0) {
return count
}
//使用copy_from_user 函数从用户层/应用层读取参数
ret = copy_from_user(&ch, buffer, sizeof ch) ? -EFAULT : 0
if (ret) {
return ret
}
ch &= 0x01//判断奇数还是偶数
set_bl(ch)//设置背光状态
return count
}
//把内核参数传递给用户层/应用层的读函数
static ssize_t dev_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
{
int ret
unsigned char str[] = {'0', '1' }
if (count == 0) {
return 0
}
//使用copy_to_user 函数把内核参数传递到用户层/应用层
ret = copy_to_user(buffer, str + get_bl(), sizeof(unsigned char) ) ? -EFAULT : 0
if (ret) {
return ret
}
return sizeof(unsigned char)
}
//设备 *** 作集
static struct file_operations dev_fops = {
owner: THIS_MODULE,
read:dev_read,
write: dev_write,
}
static struct miscdevice misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &dev_fops,
}
//设备初始化,内核启动时就有效
static int __init dev_init(void)
{
int ret
ret = misc_register(&misc)
printk (DEVICE_NAME"\tinitialized\n")
//初始化背光所用的端口GPG4 为输出
s3c2410_gpio_cfgpin(S3C2410_GPG(4), S3C2410_GPIO_OUTPUT)
//启动内核时打开背光
set_bl(1)
return ret
}
static void __exit dev_exit(void)
{
misc_deregister(&misc)
}
module_init(dev_init)//注册背光驱动模块
module_exit(dev_exit)//卸载背光驱动模块
MODULE_LICENSE("GPL")
MODULE_AUTHOR("FriendlyARM Inc.")
在/opt/mymini2440/linux-2.6.32/drivers/video/目录项的菜单文件Kconfig中添加LCD背光驱动配置菜单如下:
config FB_S3C2410_DEBUG
bool "S3C2410 lcd debug messages"
depends on FB_S3C2410
help
Turn on debugging messages. Note that you can set/unset at run time
through sysfs
config BACKLIGHT_MINI2440
tristate "Backlight support for mini2440 from FriendlyARM"
depends on MACH_MINI2440 &&FB_S3C2410
help
backlight driver for MINI2440 from FriendlyARM
config FB_SM501
tristate "Silicon Motion SM501 framebuffer support"
在/opt/mymini2440/linux-2.6.32/drivers/video/Makefile中添加背光驱动目标文件
# the test framebuffer is last
obj-$(CONFIG_FB_VIRTUAL) += vfb.o
#video output switch sysfs driver
obj-$(CONFIG_VIDEO_OUTPUT_CONTROL) += output.o
obj-$(CONFIG_BACKLIGHT_MINI2440) += mini2440_backlight.o
配置内核:
Device Drivers --->Graphics support ---> <*>Support for frame buffer devices
---><*> Backlight support for mini2440 from FriendlyARM
背光驱动移植完毕!
二、LCD驱动移植
在内核中添加各种LCD 类型的支持(我是X35的屏,也是我只需关注的部分,不过还是都添加了,X35有红色标出)
删除mach-mini2440.c原有代码(本人115行-158行)
162
163 //LCD2VGA(分辨率为1024x768)模块的配置和参数设置
164 #elif defined(CONFIG_FB_S3C2410_VGA1024768)
165 #define LCD_WIDTH 1024
166 #define LCD_HEIGHT 768
167 #define LCD_PIXCLOCK 80000
168 #define LCD_RIGHT_MARGIN 15
169 #define LCD_LEFT_MARGIN 199
170 #define LCD_HSYNC_LEN 15
171 #define LCD_UPPER_MARGIN 1
172 #define LCD_LOWER_MARGIN 1
173 #define LCD_VSYNC_LEN 1
174 #define LCD_CON5 (S3C2410_LCDCON5_FRM565 | S3C2410_LCDCON5_HWSWP)
175
176 #elif defined(CONFIG_FB_S3C2410_X240320)
177 #define LCD_WIDTH 240
178 #define LCD_HEIGHT 320
179 #define LCD_PIXCLOCK 170000
180 #define LCD_RIGHT_MARGIN 25
181 #define LCD_LEFT_MARGIN 0
182 #define LCD_HSYNC_LEN 4
183 #define LCD_UPPER_MARGIN 0
184 #define LCD_LOWER_MARGIN 4
185 #define LCD_VSYNC_LEN 9
186 #define LCD_CON5 (S3C2410_LCDCON5_FRM565 | S3C2410_LCDCON5_INVVDEN | S3C2410_LCDCON5_INVVFRAME | S3C2410_LCDCON5_INVVLINE | S3C2410_LCDCON5_INVVCLK | S3C2410_LCDCON5_HWSWP )
187 #endif
188
189 #if defined (LCD_WIDTH)
190
191 static struct s3c2410fb_display mini2440_lcd_cfg __initdata = {
192 #if !defined (LCD_CON5)
193 .lcdcon5 = S3C2410_LCDCON5_FRM565 |
194 S3C2410_LCDCON5_INVVLINE |
195 S3C2410_LCDCON5_INVVFRAME |
196 S3C2410_LCDCON5_PWREN |
197 S3C2410_LCDCON5_HWSWP,
198 #else
199 .lcdcon5 = LCD_CON5,
200 #endif
201 .type = S3C2410_LCDCON1_TFT,
202 .width = LCD_WIDTH,
203 .height = LCD_HEIGHT,
204 .pixclock = LCD_PIXCLOCK,
205 .xres = LCD_WIDTH,
206 .yres = LCD_HEIGHT,
207 .bpp= 16,
208 .left_margin= LCD_LEFT_MARGIN + 1,
209 .right_margin = LCD_RIGHT_MARGIN + 1,
210 .hsync_len = LCD_HSYNC_LEN + 1,
211 .upper_margin = LCD_UPPER_MARGIN + 1,
212 .lower_margin = LCD_LOWER_MARGIN + 1,
213 .vsync_len = LCD_VSYNC_LEN + 1,
214 }
215
216 static struct s3c2410fb_mach_info mini2440_fb_info __initdata = {
217 .displays = &mini2440_lcd_cfg,
218 .num_displays = 1,
219 .default_display = 0,
220 .gpccon = 0xaa955699,
221 .gpccon_mask= 0xffc003cc,
222 .gpcup = 0x0000ffff,
223 .gpcup_mask = 0xffffffff,
224 .gpdcon = 0xaa95aaa1,
225 .gpdcon_mask= 0xffc0fff0,
226 .gpdup = 0x0000faff,
227 .gpdup_mask = 0xffffffff,
228 .lpcsel = 0xf82,
229 }
230
231 #endif
232
然后打开drivers/video/Kconfig,在大概1935 行加入以下配置信息:
1923 config FB_S3C2410_DEBUG
1924 bool "S3C2410 lcd debug messages"
1925 depends on FB_S3C2410
1926 help
1927 Turn on debugging messages. Note that you can set/unset at run time
1928 through sysfs
1929
1930 choice
1931 prompt "LCD select"
1932 depends on FB_S3C2410
1933 help
1934 S3C24x0 LCD size select
1935
1936 config FB_S3C2410_T240320
1937 boolean "3.5 inch 240X320 Toppoly LCD"
1938 depends on FB_S3C2410
1939 help
1940 3.5 inch 240X320 Toppoly LCD
1941
1942 config FB_S3C2410_N240320
1943 boolean "3.5 inch 240X320 NEC LCD"
1944 depends on FB_S3C2410
1945 help
1946 3.5 inch 240x320 NEC LCD
1947
1948 config FB_S3C2410_TFT640480
1949 boolean "8 inch 640X480 L80 LCD"
1950 depends on FB_S3C2410
1951 help
1952 8 inch 640X480 LCD
1953
1954 config FB_S3C2410_TFT800480
1955 boolean "7 inch 800x480 TFT LCD"
1956 depends on FB_S3C2410
1957 help
1958 7 inch 800x480 TFT LCD
1959
1960 config FB_S3C2410_VGA1024768
1961 boolean "VGA 1024x768"
1962 depends on FB_S3C2410
1963 help
1964 VGA 1024x768
1965
1966 config FB_S3C2410_X240320
1967 boolean "3.5 inch 240X320 LCD(ACX502BMU)"
1968 depends on FB_S3C2410
1969 help
1970 3.5 inch 240X320 LCD(ACX502BMU)
1971
1972 endchoice
1973
1974 config BACKLIGHT_MINI2440
配置内核
Device Drivers ---> Graphics support ---> <*>Support for frame buffer devices ---> LCD select (3.5 inch 240X320 LCD(ACX502BMU)) ---> (X) 3.5 inch 240X320 LCD(ACX502BMU)
LCD驱动移植完成!!!
三、开机logo和开机信息显示
Device Drivers ---> Graphics support ---> <*>Support for frame buffer devices --->[*] Bootup logo ---> [*] Standard 16-color Linux logo (本人的24位死活不能显示,先改成16位吧)
在文件系统rootfs/etc/inittab下作如下修改(为了在LCD上显示打印信息):
1 ::sysinit:/etc/init.d/rcS
2 tty1::askfirst:-/bin/sh//添加
3 s3c2410_serial0::askfirst:-/bin/sh
4 ::ctrlaltdel:/sbin/reboot
5 ::shutdown:/bin/umount -a -r
6
四,编译测试
#make zImage
#cd arch/arm/boot/
#mkimage -n 'mini2440_linux' -A arm -O linux -T kernel -C none -a 0x31000000 -e 0x31000040 -d zImage uImage
#chmod a+x uImage
#cp uImage /tftp/boot
设置U-BOOT参数如下:
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)