Framebuffer_LCD驱动介绍
前言
学习Framebuffer过程中,最大的困惑不是fb层的各个数据结构的交互,而是stm32驱动LCD时的画点,画线,画圈这样的函数上哪去了。。。直到了解了LCD控制器的原理,总算知道了为什么没了。原来给LCD控制器承包了!
一、LCD控制器介绍
在说Framebuffer之前,得先提一下LCD控制器。如下图:
- LCD控制器的主要作用是承担GPIO与LCD显示器的数据交互,集成复杂硬件控制(像单片机驱动LCD需要复杂的寄存器设置等操作)
- 反映到内核中,则是分配一个空间作为显存(一般为对应显示屏的一帧数据的大小),将首尾地址给到LCD控制器对应的寄存器,这时候它就会自动地从这块内存中读取数据,控制LCD显示出来,呈现到用户面前。
二、Framebuffer介绍:
- 首先,上面说的LCD控制器完成了处理器与LCD之间的交互,只需要对一块内存空间写数据就能让lcd显示我们想要的东西
那么我们要怎么在应用编程中控制LCD呢?
这就是Framebuffer的作用了。
- Framebuffer就是将上面的那块空间封装上read、write、open、mmap等函数,虚拟出一个fb设备,当我们编写好 LCD驱动以后会生成一个名为 /dev/fbX(X=0~n)的设备,应用程序通过访问 /dev/fbx这个设备就可以访问 LCD。
Framebuffer子系统的层次结构如下:
在应用层中反映为/dev/fbx,保存着一帧图像,可以操作试试:
# dd if=/dev/zero of=/dev/fbx // 清屏
# cat /dev/fbx > text // 截屏(未编码)
# cat text > /dev/fbx // 显示text到屏幕上
从结构图中可以看到Framebuffer重要代码在fbmem.c和xxxfb.c;
- fbmem.c 中的代码是与硬件无关中间层部分。
- xxxfb.c 是真正的跟LCD控制器相关的驱动部分(driver),在s3c2440是用了s3c2440fb.c中的驱动,210系列是用了s5pv210fb.c的驱动,s5p6818使用的是nxp-fb.c(一般来说)。
- device一般在arch/arm中相关代码中。
相关结构体:
struct fb_info *fb_info 描述了一个framebuffer device相关一系列信息
struct fb_ops *fb_ops 描述了一个framebuffer device的操作函数集合,具体到对硬件的操作,比如清屏,画圆等
static const struct file_operations fb_fops 文件操作函数集合,当应用程序打开设备时,用户可以read,write,ioctl等
struct fb_var_screeninfo var 描述LCD硬件可变参数
struct fb_fix_screeninfo fix 描述LCD硬件不可变参数,如显存的首尾地址
1.fbmem.c
主要作用是
- 注册占用主设备号29,
- 获得info结构体,以调用info->fb_ops的lcd硬件操作函数。
- mmap将fb=>vma虚拟地址空间–进程空间。
首先看init函数中:
static int __init fbmem_init(void){
...
if (register_chrdev(FB_MAJOR,"fb",&fb_fops))
...
fb_class = class_create(THIS_MODULE, "graphics");
...
return 0;
}
可以看到init中用了字符设备驱动模型:register_chrdev( 29 , ”fb” , &fb_fops ),创建并占用主设备号29。以fb_fops作为操作函数,主要作用是获取info结构体,它才保存了具体的lcd驱动函数。
static const struct file_operations fb_fops = {
.read = fb_read,
.write = fb_write,
.unlocked_ioctl = fb_ioctl,
.mmap = fb_mmap,
.open = fb_open,
.release = fb_release,
};
很典型的一个字符设备驱动模型。应用层对设备文件打开读写调用对应函数。首先看看fb_open函数主要内容:
fb_open(struct inode *inode, struct file *file){
int fbidx = iminor(inode); // 获取次设备号
info = get_fb_info(fbidx);
// ----fb_info = registered_fb[idx];从队列中获取info
file->private_data = info;//将info保存到file中,方便read等获取info
if (info->fbops->fb_open) {// 调用fbops的open函数
res = info->fbops->fb_open(info,1);}
fb_mman主要内容:
fb_mmap(struct file *file, struct vm_area_struct * vma){
struct fb_info *info = file_fb_info(file);
// open已经将info存到file中,所以现在从file中获取info
...
if (fb->fb_mmap) { // 调用fbops的mmap
res = fb->fb_mmap(info, vma);}
/* 将获得的info结构体中的framebuffer空间赋给vma虚拟地址空间 */
...
}
2.2.选择nxp-fb.c介绍 info定义处
同样首先看到init函数:
static int __init nxp_fb_init(void){
return platform_driver_register(&fb_plat_driver);
}
这是一个典型的platform总线-设备-驱动模型,注册了一个platform驱动,驱动结构如下:
static struct platform_driver fb_plat_driver = {
.probe = nxp_fb_probe,
...
.driver = { .name = DEV_NAME_FB,},
};
platform总线特点是当有设备注册并且与驱动的“DEV_NAME_FB”相同时完成匹配,调用nxp_fb_probe函数,所以接下来看到这个函数:
static int nxp_fb_probe(struct platform_device *pdev)
{
struct nxp_fb_plat_data *plat = pdev->dev.platform_data;
/* 从设备结构体中获取硬件相关信息 */
...
/* 填充info结构体,操作函数fb_ops、以及各种硬件相关信息var-fix */
...
ret = register_framebuffer(info);
// 最重要的工作 -- 申请framebuffer
}
这里申请framebuffer的函数中执行了:
device_create(fb_class, fb_info->device,
MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
在类中创建一个设备dev(29,i),并且在/dev/下创建了一个fbx供应用层操作。
以上,就是对于framebuffer子系统的解释;
后面再做一篇LCD驱动开发