Framebuffer应用编程

本文详细介绍了Linux中的Framebuffer概念,如何确定像素地址,颜色表示方式,以及如何通过ioctl和mmap函数与LCD驱动程序交互。重点展示了如何获取屏幕参数并进行像素级别的操作,适用于开发人员理解Linux图形接口编程。
摘要由CSDN通过智能技术生成

一、Framebuffer介绍

在Linux系统中通过Framebuffer(帧缓冲)驱动程序来控制LCD。
Framebuffer就是一块内存,里面保存着一帧图像。Framebuffer中保存着一帧图像的每一个像素颜色值。LCD控制器周而复始地从Framebuffer中逐一取出每个像素的颜色值发送到LCD,这样LCD便能显示出图像。

假设LCD的分辨率是1024x768,每一个像素的颜色用32位来表示,那么Framebuffer的大小就是:1024x768x32/8=3145728字节。

二、确定LCD某坐标像素对应的Frambuffer地址

若LCD中存在某一点的坐标是(x,y),LCD最开始坐标是(0,0)。
(x,y)与(0,0)在Y方向距离y行,在X方向距离x个像素点。那么可以得出该点的偏移地址为(xres*bpp/8)*y + x *bpp/8(xres表示在纵方向的像素点数即分辨率)
xres * bpp/8即表示每一行所占用多少字节的内存

由偏移地址即可得出某点坐标处像素的起始地址:
(x,y)像素起始地址=fb_base+(xres*bpp/8)y + xbpp/8

bpp: bits per pixel 每个像素用多少位来表示它的颜色

fb_base是程序执行mmap后得到的Framebuffer地址

三、像素颜色的表示

颜色是由三原色构成,以下是三种表示方式
在这里插入图片描述
对于32BPP,一般只设置其中的低24位,高8位表示透明度,一般的LCD都不支持。
对于24BPP,硬件上为了方便处理,在Framebuffer中也是用32位来表示,效果跟32BPP是一样的。
对于16BPP,常用的是RGB565;很少的场合会用到RGB555,这可以通过ioctl读取驱动程序中的RGB位偏移来确定使用哪一种格式。

若想从以RGB888表示的颜色值得到以RGB565表示的值,只需要保留RGB888中相应高位即可。
比如RGB888的低八位表示蓝色,需要保留这八位的高5位即可得出RGB565中的蓝色数值。

四、API函数

1.ioctl函数

int ioctl(int fd, unsigned long request, ...);
// request表示与驱动程序交互的命令
//用不同的命令控制驱动程序输出我们需要的数据;

// … 表示可变参数arg,根据request命令,设备驱动程序返回输出的数据。

不同的驱动程序内部会实现不同的ioctl,APP可以使用各种ioctl跟驱动程序交互:可以传数据给驱动程序,也可以从驱动程序中读出数据。

2.mmap函数

void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);

// 关闭内存映射
munmap(void *start, size_t lenght)
  • addr表示指定映射的內存起始地址,通常设为 NULL表示让系统自动选定地址,并在成功映射后返回该地址;
  • length表示将文件中多大的内容映射到内存中;
  • prot 表示映射区域的保护方式,可以为以下4种方式的组合
    a. PROT_EXEC 映射区域可被执行
    b. PROT_READ 映射区域可被读出
    c. PROT_WRITE 映射区域可被写入
    d. PROT_NONE 映射区域不能存取
  • Flags 表示影响映射区域的不同特性,常用的有以下两种
    a. MAP_SHARED 表示对映射区域写入的数据会复制回文件内,原来的文件会改变。
    b. MAP_PRIVATE 表示对映射区域的操作会产生一个映射文件的复制,对此区域的任何修改都不会写回原来的文件内容中。
  • 参数offset:文件映射的偏移量,通常设置为0,代表从文件最前方开始对应,offset必须是分页大小的整数倍。
  • 返回值:若成功映射,将返回指向映射的区域的指针,失败将返回-1。

五、代码实例

需要用ioctl函数获取LCD参数,在ioctl函数中用FBIOGET_VSCREENINFO获得屏幕的可变信息。

LCD驱动程序给APP提供2类参数:可变的参数fb_var_screeninfo、固定的参数fb_fix_screeninfo。编写应用程序时主要关心可变参数,它的结构体定义如下(#include <linux/fb.h>):

1 struct fb_var_screeninfo {
 2     __u32 xres;             // LCD的水平像素大小
 3     __u32 yres;             // LCD的垂直像素大小
 4     __u32 xres_virtual;     // LCD的虚拟水平像素大小
 5     __u32 yres_virtual;     // LCD的虚拟垂直像素大小
 6     __u32 xoffset;          // 水平像素偏移量
 7     __u32 yoffset;          // 垂直像素偏移量
 8 
 9     __u32 bits_per_pixel;            // 像素深度bpp
10     __u32 grayscale;        /* != 0 Graylevels instead of colors */
11 
12     struct fb_bitfield red;        /* bitfield in fb mem if true color, */
13     struct fb_bitfield green;    /* else only length is significant */
14     struct fb_bitfield blue;
15     struct fb_bitfield transp;    /* transparency            */    
16 
17     __u32 nonstd;            /* != 0 Non standard pixel format */
18 
19     __u32 activate;            /* see FB_ACTIVATE_*        */
20 
21     __u32 height;           // LCD的物理高度 mm
22     __u32 width;            // LCD的物理宽度 mm
23 
24     __u32 accel_flags;        /* (OBSOLETE) see fb_info.flags */
25 
27     __u32 pixclock;            // 像素时钟
28 
29     /* 下面是六个时序参数 */
30     __u32 left_margin;        /* time from sync to picture    */
31     __u32 right_margin;        /* time from picture to sync    */  
32     __u32 upper_margin;        /* time from sync to picture    */ 
33     __u32 lower_margin;
34     __u32 hsync_len;        /* length of horizontal sync    */
35     __u32 vsync_len;        /* length of vertical sync    */
36     
37     __u32 sync;            /* see FB_SYNC_*        */
38     __u32 vmode;            /* see FB_VMODE_*        */
39     __u32 rotate;            /* angle we rotate counter clockwise */
40     __u32 reserved[5];        /* Reserved for future compatibility */
41 };

具体代码如下:

#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <linux/fb.h>

struct fb_var_screeninfo var;
unsigned char *fb_base;
unsigned int line_width;
unsigned int pixel_width;
unsigned int screen_size;

// 描点函数
// 传入的color的格式是RGB888格式
void lcd_put_pixel(int x, int y, unsigned int color)
{
    unsigned char *pen_8 = fb_base + y*line_width+x*pixel_width;
    unsigned short *pen_16;
    unsigned int *pen_32;

    pen_16 = (unsigned short*)pen_8;
    pen_32 = (unsigned int*)pen_8;

    unsigned int red,green,blue;

    switch (var.bits_per_pixel)
   {
  
        case 16:
        {	
        	// 从color变量中把R、G、B抽出来
            red = (color>>16) & 0xff;
            green = (color>>8) & 0xff;
            blue = (color>>0) & 0xff;
            
            // 根据RGB565的格式,只保留red中的高5位、green中的高6位、blue中的高5位,组合成一个新的16位颜色值
            color = ((red>>3)<<11)|((green>>2)<<5)|(blue>>3);
            *pen_16 = color;
            break;
        }
        case 32:
        {
            *pen_32 = color;
            break;
        }
        default:
        {
            printf("can not support bits_per_pixel:%d\n",var.bits_per_pixel);
            break;
        }
   }
    
}

int main(int argc, char * * argv)
{   
    // 打开设备文件
    fd_fb = open("/dev/fb0",O_RDWR);

    if(fd_fb==-1){
        printf("open fb0 failed\n");
    }

    // 获取屏幕的可变信息
    if(ioctl(fd_fb,FBIOGET_VSCREENINFO,&var)==-1){
        printf("can not get var\n");
    }

    line_width = var.xres*var.bits_per_pixel/8;
    pixel_width = var.bits_per_pixel/8;
    screen_size = var.xres*var.yres*var.bits_per_pixel/8;

    // 映射framebuffer
    fb_base = (unsigned char*)mmap(NULL,screen_size,PROT_READ|PROT_WRITE,MAP_SHARED,fd_fb,0);
    if(fb_base==(unsigned char*)-1){
        printf("fb_base error\n");
    }

    // 清屏 设置为白色
    memset(fb_base,0xff,screen_size);
    int i;
    for(i=0;i<100;i++){
        lcd_put_pixel(var.xres/2+i, var.yres/2, 0xFF0000);
    }

    munmap(fb_base,screen_size);
    close(fd_fb);
    
    return 0;
}

运行平台:100ask-imx6ull开发板
交叉编译工具链:arm-linux-gnueabihf-gcc

在虚拟机已经开启NFS服务前提下,将编译好的文件复制到NFS目录下:

cp pixel ~/nfs_rootfs/

在ARM开发板上,若VMware使用桥接网络,使用下面命令挂载NFS

mount -t nfs -o nolock,vers=3  "ubuntu IP":/home/heavysea/nfs_rootfs  /mnt

在这里插入图片描述

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值