FrameBuffer驱动开发
LCD控制器
硬件部分…
FrameBuffer简介
帧缓冲(framebuffer)是Linux 系统为显示设备提供的一个接口,它将显示缓冲区抽象,屏蔽图像硬件的底层差异,允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。用户不必关心物理显示缓冲区的具体位置及存放方式,这些都由帧缓冲设备驱动本身来完成。
framebuffer机制模仿显卡的功能,将显卡硬件结构抽象为一系列的数据结构,可以通过framebuffer的读写直接对显存进行操作。用户可以将framebuffer看成是显存的一个映像,将其映射到进程空间后,就可以直接进行读写操作,写操作会直接反映在屏幕上。
framebuffer是个字符设备,主设备号为29,对应于/dev/fb%d 设备文件。
通常,使用如下方式(前面的数字表示次设备号) 0 = /dev/fb0 第一个fb 设备 1 = /dev/fb1 第二个fb 设备。fb也是一种普通的内存设备,可以读写其内容。例如,屏幕抓屏:cp /dev/fb0 myfilefb 虽然可以像内存设备(/dev/mem)一样,对其read,write,seek 以及mmap。但区别在于fb使用的不是整个内存区,而是显存部分。
FrameBuffer相关结构体
fb_ops[*]
fb_ops结构体是对底层硬件操作的函数指针,该结构体中定义了对硬件的操作有:
注: fb_ops结构与file_operations 结构不同,fb_ops是底层操作的抽象,而file_operations是提供给上层系统调用的接口,可以直接调用.
//include\linux\fb.h
struct fb_ops {
/* open/release and usage marking */
struct module *owner;
/* 打开和释放 */
int (*fb_open)(struct fb_info *info, int user);
int (*fb_release)(struct fb_info *info, int user);
/* For framebuffers with strange non linear layouts or that do not
* work with normal memory mapped access
*/
/*对于非线性布局的/常规内存映射无法工作的帧缓冲设备需要这两个函数*/
ssize_t (*fb_read)(struct fb_info *info, char __user *buf,size_t count, loff_t *ppos);
ssize_t (*fb_write)(struct fb_info *info, const char __user *buf,size_t count, loff_t *ppos);
/* checks var and eventually tweaks it to something supported,
* DO NOT MODIFY PAR */
/* 检查可变参数并进行设置 */
int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);
/* set the video mode according to info->var */
/* 根据设置的值进行更新,使之有效 */
int (*fb_set_par)(struct fb_info *info);
/* set color register */
/* 设置颜色寄存器 */
int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,unsigned blue, unsigned transp, struct fb_info *info);
/* set color registers in batch */
/* 批量设置颜色寄存器,设置颜色表 */
int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);
/* blank display */
/* 显示空白 */
int (*fb_blank)(int blank, struct fb_info *info);
/* pan display */
/* pan显示 */
int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);
/* Draws a rectangle */
/* 填充矩形 */
void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);
/* Copy data from area to another */
/* 数据复制 */
void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);
/* Draws a image to the display */
/* 图形填充 */
void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);
/* Draws cursor */
/* 绘制光标 */
int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);
/* wait for blit idle, optional */
/* 等待blit空闲 */
int (*fb_sync)(struct fb_info *info);
/* perform fb specific ioctl (optional) */
/* fb 特定的 ioctl */
int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,
unsigned long arg);
/* Handle 32bit compat ioctl (optional) */
/* 处理32兼容的ioctl操作 */
int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,
unsigned long arg);
/* perform fb specific mmap */
/* fb 特定的 mmap */
int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);
/* get capability given var */
/* 通过fb_info获得framebuffer的能力 */
void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,struct fb_var_screeninfo *var);
/* teardown any resources to do with this framebuffer */
void (*fb_destroy)(struct fb_info *info);
/* called at KDB enter and leave time to prepare the console */
int (*fb_debug_enter)(struct fb_info *info);
int (*fb_debug_leave)(struct fb_info *info);
};
fb_var_screeninfo[*]
struct fb_info的成员(可变参数),其记录用户可修改的显示控制器的参数,包括分辨率和每个像素点的比特数,其成员需要在驱动程序中初始化和设置
xoffset、yoffset可视画面相对于虚拟画面的x、y轴偏移量,可以实现双缓冲的功能。将yres_virtual设置成yres的两倍。
//include\uapi\linux\fb.h
struct fb_var_screeninfo {
/********可见解析度(实际屏幕)********/
__u32 xres;/*定义屏幕一行有多少个像素点 */
__u32 yres;/*定义屏幕一列由多少个像素点 */
/********虚拟解析度(虚拟屏幕)********/
__u32 xres_virtual;/*虚拟屏幕一行有多少个像素点 */
__u32 yres_virtual;/*虚拟屏幕一列由多少个像素点*/
__u32 xoffset; /*虚拟到可见(实际)之间的行方向偏移 */
__u32 yoffset; /*虚拟到可见(实际)之间的列方向偏移*/
__u32 bits_per_pixel; /*每像素位数(多少BPP),单位为字节 */
__u32 grayscale; /*非0时指灰度*/
/********fb缓存的RGB位域**********/
struct fb_bitfield red; /* fb缓存的红色位域*/
struct fb_bitfield green;/* fb缓存的绿色位域*/
struct fb_bitfield blue; /* fb缓存的蓝色位域*/
struct fb_bitfield transp;/*透明度 =0 */
__u32 nonstd; /*非标准像素格式时应该为非0值 (标志像素格式时 nonstd=0) */
__u32 activate; /*查看宏FB_ACTIVATE_NOW */
__u32 height; /* 高度*/
__u32 width; /* 宽度 */
__u32 accel_flags; /*查看fb_info.flags */
/************这参数必须通过查看LCD数据手册得到**************/
/* Timing: All values in pixclocks, except pixclock (of course) */
__u32 pixclock; /*像素时钟(皮秒),pixclock=1/Dclk=... */
/* 行切换,从同步到绘图之间的延迟即HFPD(有效数据之后无效的像素的个数) ,对应于LCD数据手册的Hsyn的front-porch*/
__u32 left_margin; /* time from sync to picture */
/*行切换,从绘图到同步之间的延迟即HBPD(Hsyn脉冲下降沿之后的无效像素的个数) ,对应于LCD数据手册的Hsyn的back-porch*/
__u32 right_margin; /* time from picture to sync */
/*帧切换,从同步到绘图之间的延迟即VFPD(有效数据之后还要经历的无效行数(之后是下一帧数据)) ,对应于LCD数据手册的Vsyn的front-porch*/
__u32 upper_margin; /* time from sync to picture */
/*帧切换,从绘图到同步之间的延迟即VBPD(Vsyn脉冲下降沿之后还要经历的无效行数) ,对应于LCD数据手册的Vsyn的back-porch */
__u32 lower_margin;
/*水平同步的长度即HSPW(Hsyn信号的脉冲宽度),对应于LCD数据手册的Hsyn的pulse Width */
__u32 hsync_len;
/*垂直同步的长度即VSPW(Vsyn信号的脉冲宽度),对应于LCD数据手册的Vsyn的pulse Width */
__u32 vsync_len;
__u32 sync; /* 查看宏FB_SYNC_*/
__u32 vmode; /* 查看宏FB_VMODE_ */
__u32 rotate; /*顺时钟旋转的角度 */
__u32 reserved[5]; /* Reserved for future compatibility */
};
fb_fix_screeninfo[*]
struct fb_info的成员(固定参数),其记录用户不能修改的显示控制器的参数,如屏幕缓冲区物理地址,长度,当对帧缓冲设备进行映射操作时,就是从此结构中取得缓冲区物理地址,其成员需要在驱动程序中初始化和设置
//include\uapi\linux\fb.h
struct fb_fix_screeninfo {
char id[16]; /*字符串形式的标识符 */
unsigned long smem_start; /* fb缓冲内存的开始地址(物理地址),它一般是作为dma_alloc_writecombine的参数,该函数会将物理地址存放在该变量中*/
__u32 smem_len; /* fb缓冲的长度,等于max_xres *max_yres*max_bpp/8 */
__u32 type; /* 查看宏 FB_TYPE_ FB_TYPE_PACKED_PIXELS=0 */
__u32 type_aux; /* Interleave for interleaved Planes*/ /* 交错[隔行],=0 */
__u32 visual; /* 查看宏FB_VISUAL_,用于记录屏幕使用的色彩模式,一般是FB_VISUAL_TRUECOLOR(真彩色) */
__u16 xpanstep; /* 如果没有硬件 panning,=0 */
__u16 ypanstep; /* 如果没有硬件 panning,=0 */
__u16 ywrapstep;/* 如果没有硬件 panning,=0 */
__u32 line_length;/* 一行的字节数 */
unsigned long mmio_start; /* 内存映射的I/O的开始位置(物理地址)*/
__u32 mmio_len;/* 内存映射的I/O的长度 */
__u32 accel; /* Indicate to driver which */ /*特定的芯片 = FB_ACCEL_NONE */
/* specific chip/card we have */
__u16 reserved[3];/* Reserved for future compatibility 保留为将来扩充*/
};
Linux 中支持的帧缓冲存储类型由宏 FB TYPE *指定,主要包括以下几个:
FB TYPE PACKED PIXELS :紧缩像素格式 (最常见的一种格式) 。 像素被连续地存
放在一个平面中 。FB TYPE PLANES :非间隔平面格式 。 像素中的各分量被分离地存放在不同的平面
中,平面的个数等于像素的有效位数。 例如,可以先依次存储所有像素的第 0 位,接
着存储第 1 位,以此类推。FB TYPE INTERLEAVED PLANES :间隔平面格式。 像素中的各分量被分离地存放
在不同平面中 , 平面的个数等于像素的有效位数,只是每个平面在内存中并不连续。
例如,可以先按照非间隔平面格式存储 8 像素,接着储存后面 8 像素 , 以此类推。FB TYPE TEXT :文本模式 。 有的显示设备支持直接文本显示,这时只需要在帧缓
冲中存储需要显示文本的索引,而不是构成这些文本的像素信息 。
Linux 中支持的可视模式类型由宏 FB VISUAL *指定,主要包括以下几个:
- FB VISUAL MONOOI 、 FB VISUAL MONOlO :单色模式.每像素由二进制的 0 或
1 表示是黑色或者白色 。 - FB VISUAL PSEUDOCOLOR 、 FB VISUAL STATIC PSEUDOCOLOR :伪彩色模式,
每像素数据记录的是颜色的索引,通过索引可以在颜色表中查找到相应的颜色。 - FB VISUAL TRUECOLOR :真影色模式,每像素由红、绿 、 蓝 3 个颜色分量构成。
每个颜色分量都是一个索引,对应的索引表是只读的,并依赖于具体的显示设备。 - FB VISUAL DIRECTCOLOR :直接影色模式 , 每像素同样由红、绿、蓝 3 个颜色分
量构成。 每个颜色分量也是一个索引,不过索引表是可编程的 。
fb_cmap[*]
描述设备无关的颜色映射信息。可以通过FBIOGETCMAP
和 FBIOPUTCMAP
对应的ioctl操作设定或获取颜色映射信息.
//include\uapi\linux\fb.h
struct fb_cmap {
__u32 start; /* 第一个颜色在颜色表中的索引 */
__u32 len; /* 颜色个数 */
__u16 *red; /* 红色 */
__u16 *green; /* 绿色 */
__u16 *blue; /* 蓝色 */
__u16 *transp; /* 透明度,可以为空 */
};
fb_bitfield
fb缓存的RGB位域,该结构描述每一个像素显示缓冲区的组织方式。
假如为RGB565模式,R占5位=bit[11:15] G占6位=bit[10:5] B占5位=bit[4:0] */
struct fb_bitfield {
/*位域偏移 red=11 ,green=5 blue=0 */
__u32 offset; /* beginning of bitfield */
/*位域长度 red=5 green=6 blue=5*/
__u32 length; /* length of bitfield */
/*msb_right!=0=>MSB在右边 */
__u32 msb_right; /* != 0 : Most significant bit is right */
};
fb_videomode
定义lcd屏幕信息
//include\linux\fb.h
struct fb_videomode {
const char *name;/* 液晶屏名字 optional */
u32 refresh; /* 刷新频率(内核中很多例子都赋值为60)optional */
u32 xres; /* 每行的像素个数 */
u32 yres; /* 屏幕的行数 */
u32 pixclock; /* 每个像素时钟周期的长度,单位是皮秒(10的负12次方分之1秒) */
u32 left_margin;/* 在每行或每列的像素数据开始输出时要插入的像素时钟周期数 */
u32 right_margin;/* 在每行或每列的像素结束到LCD 行时钟输出脉冲之间的像素时钟数 */
u32 upper_margin;/* 在垂直同步周期之后帧开头时的无效行数 */
u32 lower_margin;/* 本帧数据输出结束到下一帧垂直同步周期开始之前的无效行数 */
u32 hsync_len; /* 行同步脉宽 单位:像素时钟周期 */
u32 vsync_len; /* 垂直同步脉宽 单位:显示一行的时间th*/
u32 sync; /* 各同步信号的极性定义,如hsync、vsync、DEN的极性等 */
u32 vmode; /* 显示模式,逐行还是隔行扫描 */
u32 flag; /* 一般为0 */
};
fb_info[*]
定义当显卡的当前状态。fb_info结构仅在内核中可见,在这个结构中有一个fb_ops指针,指向驱动设备工作所需的函数集
//include\linux\fb.h
struct fb_info {
atomic_t count;
int node;
int flags;
struct mutex lock; /* Lock for open/release/ioctl funcs */
struct mutex mm_lock;/* Lock for fb_mmap and smem_* fields */
struct fb_var_screeninfo var;/* Current var 可变参数*/
struct fb_fix_screeninfo fix;/* Current fix 固定参数*/
struct fb_monspecs monspecs; /* Current Monitor specs 当前显示器标准*/
struct work_struct queue; /* Framebuffer event queue 帧缓冲事件队列*/
struct fb_pixmap pixmap; /* Image hardware mapper */
struct fb_pixmap sprite; /* Cursor hardware mapper */
struct fb_cmap cmap; /* Current cmap 当前的调色板*/
struct list_head modelist; /* mode list 模式列表*/
struct fb_videomode *mode; /* current mode 当前的显示模式*/
#ifdef CONFIG_FB_BACKLIGHT
/* assigned backlight device */
/* set before framebuffer registration,
remove after unregister */
struct backlight_device *bl_dev; /* 对应的背光设备 */
/* Backlight level curve */
struct mutex bl_curve_mutex;
u8 bl_curve[FB_BACKLIGHT_LEVELS]; /* 背光调整 */
#endif
#ifdef CONFIG_FB_DEFERRED_IO
struct delayed_work deferred_work;
struct fb_deferred_io *fbdefio;
#endif
struct fb_ops *fbops; /* 帧缓冲操作函数集 */
struct device *device; /* This is the parent 父设备*/
struct device *dev; /* This is this fb device fb设备*/
int class_flag; /* private sysfs flags 私有的sysfs标志*/
#ifdef CONFIG_FB_TILEBLITTING
struct fb_tile_ops *tileops; /* Tile Blitting 图块blitting*/
#endif
union {
char __iomem *screen_base; /* Virtual address 虚拟基地址*/
char *screen_buffer;
};
unsigned long screen_size;/* Amount of ioremapped VRAM or 0 LCD IO映射的虚拟内存大小*/
void *pseudo_palette; /* Fake palette of 16 colors 伪16色 颜色表*/
#define FBINFO_STATE_RUNNING 0
#define FBINFO_STATE_SUSPENDED 1
u32 state; /* Hardware state i.e suspend LCD 挂起或复位的状态*/
void *fbcon_par; /* fbcon use-only private area */
/* From here on everything is device dependent */
void *par;
/* we need the PCI or similar aperture base/size not
smem_start/size as smem_start may just be an object
allocated inside the aperture so may not actually overlap */
struct apertures_struct {
unsigned int count;
struct aperture {
resource_size_t base;
resource_size_t size;
} ranges[0];
} *apertures;
bool skip_vt_switch; /* no VT switch on suspend/resume required */
};
FrameBuffer核心层
操作帧缓冲设备的字符设备接口在已经有默认实现,其中write 、read、mmap
和ioctl等方法在fbmem.c(drivers\video\fbdev\core\fbmem.c
或drivers\video\fbmem.c
) 中实现。
以下接口的相应实现在drivers\video\fbdev\core
目录下(早前linux版本在drivers\video\
目录下)
内核接口 | 说明 |
---|---|
register_framebuffer | 注册底层帧缓冲设备 |
unregister_framebuffer | 注销底层帧缓冲设备 |
framebuffer_alloc | 为fb_info结构体分配内存 |
framebuffer_release | 释放为fb_info结构体分配的内存 |
fb_alloc_cmap | 分配色彩映射 |
fb_dealloc_cmap | 释放色彩映射 |
dma_alloc_coherent | 分配和映射一致性DMA缓存区 |
dma_free_coherent | 释放一致性DMA缓存 |
FrameBuffer驱动
1.分配一个fb_info结构体
2.设置fb_info
2.1 设置固定的参数fb_info-> fix
2.2 设置可变的参数fb_info->var
2.3 设置操作函数fb_info-> fbops
2.4 设置fb_info其它的成员
3.设置硬件相关的操作
3.1 配置LCD引脚
3.2 根据LCD手册设置LCD控制器
3.3 分配显存(dma_alloc_coherent),把地址告诉LCD控制器和fb_info
开启LCD,并注册fb_info: register_framebuffer()
4.1 直接在init函数中开启LCD(后面讲到电源管理,再来优化)
控制LCDCON5允许PWREN信号,
然后控制LCDCON1输出PWREN信号,
输出GPB0高电平来开背光,
4.2 注册fb_info
5.卸载内核中的fb_info
5.1 控制LCDCON1关闭PWREN信号,关背光,iounmap注销地址
5.2 释放DMA缓存地址dma_free_coherent()
5.3 释放注册的fb_info
内核中提供的模板和简单的例子
drivers\video\fbdev\skeletonfb.c
drivers\video\fbdev\simplefb.c
FrameBuffer应用编程
对于用户程序而言,它和其他的设备并没有什么区别,用户可以把fb看成是一块内存,既可以向内存中写数据,也可以读数据。fb的显示缓冲区位于内核空间,应用程序可以把此空间映射到自己的用户空间,在进行操作。
在应用程序中,操作/dev/fbn的一般步骤如下:
(1)打开/dev/fbn设备文件。
(2)用ioctl()操作取得当前显示屏幕的参数,如屏幕分辨率、每个像素点的比特数。根据屏幕参数可计算屏幕缓冲区的大小。
(3)用mmap()函数,将屏幕缓冲区映射到用户空间。
(4)映射后就可以直接读/写屏幕缓冲区,进行绘图和图片显示了。
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <unistd.h>
#define FBDEVICE "/dev/fb0"
void draw_back(unsigned int *pfb, unsigned int width, unsigned int height, unsigned int color);
void draw_line(unsigned int *pfb, unsigned int width, unsigned int height);
int main(void)
{
int fd = -1;
int ret = -1;
unsigned int *pfb = NULL;
struct fb_fix_screeninfo finfo;
struct fb_var_screeninfo vinfo;
fd = open(FBDEVICE, O_RDWR);
if (fd < 0)
{
perror("open");
return -1;
}
printf("open %s success \n", FBDEVICE);
ret = ioctl(fd, FBIOGET_FSCREENINFO, &finfo);
if (ret < 0)
{
perror("ioctl");
return -1;
}
ret = ioctl(fd, FBIOGET_VSCREENINFO, &vinfo);
if (ret < 0)
{
perror("ioctl");
return -1;
}
pfb = (unsigned int *)mmap(NULL, finfo.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (NULL == pfb)
{
perror("mmap");
return -1;
}
printf("pfb :0x%x \n", *pfb);
draw_back(pfb, vinfo.xres_virtual, vinfo.yres_virtual, 0xffff0000);
draw_line(pfb, vinfo.xres_virtual, vinfo.yres_virtual);
close(fd);
return 0;
}
void draw_back(unsigned int *pfb, unsigned int width, unsigned int height, unsigned int color)
{
unsigned int x, y;
for (y = 0; y < height; y++)
{
for (x = 0; x < width; x++)
{
*(pfb + y * width + x) = color;
}
}
}
void draw_line(unsigned int *pfb, unsigned int width, unsigned int height)
{
unsigned int x, y;
for (x = 50; x < width - 50; x++)
{
*(pfb + 50 * width + x) = 0xffffff00;
}
for (y = 50; y < height -50; y++)
{
*(pfb + y * width + 50) = 0xffffff00;
}
}
FrameBuffer一些操作命令
framebuffer的配置工具fbset
fbset --help
ramebuffer的设备文件是 /dev/fb0、/dev/fb1 等等。
清空屏幕命令:
#dd if="/dev/zero" of="/dev/fb0"
如果显示模式是 1024x768-8 位色,
清空屏幕命令:
dd if="/dev/zero" of="/dev/fb0" bs="1024" count="768"
保存屏幕信息命令:
dd if=/dev/fb0 of=fbfile
或:cp /dev/fb0 fbfile
恢复屏幕信息命令:
dd if=fbfile of=/dev/fb0
或:cat fbfile > /dev/fb0
FrameBuffer双缓冲
Linux中的Framebuffer模型中,提供了一些ioctl功能,给定一些参数,然后系统可以实现对应的功能,其中有个参数就是FBIOPAN_DISPLAY。具体也就是类似如下调用:
ret = ioctl(fd, FBIOGET_VSCREENINFO, &vinfo);
vinfo.yoffset = vinfo.yres;//vinfo.yoffset = 0;
ioctl (framebuffer_handler, FBIOPAN_DISPLAY, &vinfo);
http://www.voidcn.com/article/p-dodryini-bku.html
https://blog.csdn.net/wocao1226/article/details/8177362?locationNum=10&fps=1
https://blog.csdn.net/devehe/article/details/5725228