linux framebuffer设备驱动,图像获取

在linux系统中,没有lcd驱动这种叫法,只有framebuffer设备驱动,也就是显存驱动,所以你是在内核驱动代码当中找不到直接lcd命名的文件夹。

这种驱动是用于实现提供应用程序的窗口图像的显示接口,如QT窗口程序的显示等。

那下面实现这个显示接口,需要初始化下面的结构体,与linux的系统编程比较相似:

struct fb_info 的一个对象表示一个framebuffer设备

struct fb_info结构体成员比较多, 要完成功能,下面的成员是必须初始化的:

struct fb_info fbi {
    ...
    struct fb_var_screeninfo var;   /* Current var */ 
    struct fb_fix_screeninfo fix;   /* Current fix */ 
    ...
    struct fb_ops *fbops;
    char __iomem *screen_base;  /* Virtual address */
    unsigned long screen_size;  /* Amount of ioremapped VRAM or 0 */ 
    void *pseudo_palette;       /* Fake palette of 16 colors    
    ....
};

struct fb_var_screeninfo {
    __u32 xres;         /* visible resolution       */
    __u32 yres;
    __u32 xres_virtual;     /* virtual resolution       */
    __u32 yres_virtual;
    __u32 bits_per_pixel;       /* guess what           */

    struct fb_bitfield red;     /* bitfield in fb mem if true color, */
    struct fb_bitfield green;   /* else only length is significant */
    struct fb_bitfield blue;
    struct fb_bitfield transp;  /* transparency 
    ....
}

struct fb_fix_screeninfo {
    char id[16];            /* identification string eg "TT Builtin" */
    unsigned long smem_start;   /* Start of frame buffer mem */
                    /* (physical address) */
    __u32 smem_len;         /* Length of frame buffer mem */
    __u32 type;         /* see FB_TYPE_*        */
    __u32 visual;           /* see FB_VISUAL_*      */ 
    __u32 line_length;      /* length of a line in bytes    */ bit
    ....
}

==================

对应要用到的初始化函数:

extern struct fb_info *framebuffer_alloc(size_t size, struct device *dev); //因fb_info结构体比较大,需用这函数动态分配空间. size是除了fb_info对象的空间外,额外还分配多少空间
extern void framebuffer_release(struct fb_info *info); // 回收fb_info对象

extern int register_framebuffer(struct fb_info *fb_info); //注册fb设备
extern int unregister_framebuffer(struct fb_info *fb_info); //反注册fb设备


//显存其实就是在内存里分配出的一个缓冲区,而且这缓冲区还需要禁用数据缓存,为了让显存数据可以实时更新,所以还需要用下面两函数来分配和回收显存的缓冲区

void *dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle,
           gfp_t flag)  //申请出来的缓冲区禁用data cache, write buffer
    size指申请多大, dma_handle用来存放物理地址, GFP_KERNEL
    函数返回值为申请的缓冲区的虚拟地址

void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr,
            dma_addr_t dma_handle)
    释放空间. cpu_addr为虚拟地址, dma_handle为物理地址

///////////////
在fb_info里的struct fb_ops成员可以为空间,因为基本的操作如fb_open/release/read/write/ioctl等都已在fbmem.c里有实现
    同时fb_fillrect函数功能有在cfbfillrect.c里实现,函数名为cfb_fillrect
    fb_copyarea   在cfbcopyarea.c
    fb_imageblit  在cfbimgblt.c
    fb_cursor     在softcursor.c

==============

实现一个最简单的fb设备驱动:

my_fb.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fb.h>
#include <linux/dma-mapping.h>

#define X 1024  //分辨根据自己要截的图大小或要显示的屏幕大小设置
#define Y 768

u32 p_addr; //用于存放显存的物理地址
u8 *v_addr; //用于存放显存的虚拟地址, 在驱动代码,我们也只能操作缓存的虚拟地址

struct fb_ops fops = { //这里不实现操作函数,使用fbmem.c里实现的功能函数
               //如果这里实现了功能函数,则会调用这里实现的函数
};

struct fb_info *fbi;
static int __init test_init(void)
{
    v_addr = dma_alloc_coherent(NULL, X*Y*4, &p_addr, GFP_KERNEL); //分配显存的缓冲区

    //动态分配fb_info的对象空间, 而且额外分配100个字节(这100字节空间的地址==&fbi[1])
    fbi = framebuffer_alloc(100, NULL);

    fbi->var.xres = X; //分辨率
    fbi->var.yres = Y;
    fbi->var.xres_virtual = X;
    fbi->var.yres_virtual = Y;
    fbi->var.bits_per_pixel = 32; // 32位每个像素
    fbi->var.red.offset = 16; //像素数据里的红
    fbi->var.red.length = 8;
    fbi->var.green.offset = 8; //像素数据里的绿
    fbi->var.green.length = 8;
    fbi->var.blue.offset = 0; //像素数据里的蓝
    fbi->var.blue.length = 8;

    strcpy(fbi->fix.id, "myfb"); 
    fbi->fix.smem_start = p_addr; //显存的物理地址
    fbi->fix.smem_len = X*Y*4; //显存缓冲区大小
    fbi->fix.type = FB_TYPE_PACKED_PIXELS; //像素数据是打包的方式
    fbi->fix.visual = FB_VISUAL_TRUECOLOR; //真彩色
    fbi->fix.line_length = X*4; //每一行多少个字节

    fbi->fbops = &fops; 
    fbi->screen_base = v_addr; // 显示缓冲区的虚拟地址
    fbi->screen_size = X*Y*4;


    return register_framebuffer(fbi); //注册fb设备
}

static void __exit test_exit(void)
{
    unregister_framebuffer(fbi);
    framebuffer_release(fbi);

    dma_free_coherent(NULL, X*Y*4, v_addr, p_addr);
}

module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");

==================

保存显存内容成.pnm格式图片

save2pnm.c

#include 
#include 
#include 
#include 
#include 
#include 

int main(int argc, char *argv[])
{
    int fd, x, y, i, j;
    unsigned char *p;
    FILE *fl;


    if (argc < 3)
    {
        printf("usage: ./a.out fb_device bmp_file \n");
        return 1;
    }

    fd = open(argv[1], O_RDWR);
    if (fd < 0)
    {
                 printf("============\n");
        perror("open fb");
        return 1;
    }
    
    struct fb_var_screeninfo vinfo;
    ioctl(fd, FBIOGET_VSCREENINFO, &vinfo);
    y = vinfo.yres;
    // x = vinfo.xres;
    
    struct fb_fix_screeninfo finfo;
    ioctl(fd, FBIOGET_FSCREENINFO, &finfo);
    x = finfo.line_length/(vinfo.bits_per_pixel/8);

    p = mmap(NULL, finfo.line_length * y, PROT_READ, MAP_SHARED, fd, 0);
    if (MAP_FAILED == p)
    {
        perror("mmap");
        return -1;
    }

    printf("x = %d, y = %d\n", x, y);
    ////////////////////////

    fl = fopen(argv[2], "w+");
    if (NULL == fl)
        printf("fopen failed\n");

    fprintf(fl, "P6\n%d %d\n255\n", x, y);
    
    for (i = 0; i < y ; i++)
    {
        for (j = 0; j < x; j++)
        {
            fwrite(p+(i*x+j)*4+2, 1, 1, fl); // R
            fwrite(p+(i*x+j)*4+1, 1, 1, fl); // G
            fwrite(p+(i*x+j)*4,   1, 1, fl); // B
        }
    }
    fclose(fl);
    return 0;
}

arm-linux-gcc filename.c  -o save

==================

使用说明:

加载模块后,在”/dev/”目录下会多一个fb8设备文件。
可以设置QT的环境变量让QT程序从指定的fb设备刷出来.

即: export QT_QPA_PLATFORM=linuxfb:fb=/dev/fb8  或更改配置文件  改为: /etc/profile   export QT_QPA_PLATFORM=linuxfb:fb=/dev/fb8

 ( 原先配置文件里面为export QT_QPA_PLATFORM=linuxfb:fb=/dev/fb0)

原先使用的为/dev/fb0  为当加载图片时,图片会通过对应的hmdi接口显示到屏幕,这里的更改,是要让对应的界面图像保存到/dev/fb8中,然后再显示

让qt程序执行起来后,再从/dev/fb8读出显存数据,加上图像文件头后,就可以看到QT程序在显存里的状况了


我的具体操作步骤:

1 先实现上面的驱动的编写,以及加载

2 修改QT的板载上的配置文件为 /etc/profile   export QT_QPA_PLATFORM=linuxfb:fb=/dev/fb8

3 运行QT的可执行文件 ./qrmtest

4 运行./save /dev/fb8  222.pnm

就可以查看对应的图片222.pnm  。我们也可以在自己的spi屏上一直刷,就能完成对应的显示功能

==================end================

遇到的问题:

1)  NFS文件,设置的读写权限,写入时出现permisson denied的问题解决:

nfs服务器端 /etc/exports文件中已指定(rw),可读可写,在客户端也能正常挂载,可在向挂载目录里写入内容提示:permission denied。后来才搞清楚,nfs在服务器端导出的目录,也有一定权限要求,当把服务端导出目录,修改权限(chmod 777 /导出目录)后,再重启nfs服务,客户端就能读能写了。

vim /etc/exports

/work  192.168.11.11 (rw,no_root_squash,all_squash,sync)

systemctl restart nfs

systemctl restart rpcbind

2) 在centos 中打开图像查看器的命令行为eog  :  eog 222.pnm就可以显示图片了

你可能感兴趣的:(linux驱动)