Linux FrameBuffer驱动开发

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wyy626562203/article/details/82564315

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[*]

描述设备无关的颜色映射信息。可以通过FBIOGETCMAPFBIOPUTCMAP 对应的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.cdrivers\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

  1. 开启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

猜你喜欢

转载自blog.csdn.net/wyy626562203/article/details/82564315