韦东山嵌入式入门笔记之——应用开发基础篇(四)

注:开发板以IMX6ULL PRO开发板为例!!!

第五章  Framebuffer应用编程

Framebuffer是LCD设备的一种驱动程序

5.1    LCD驱动原理
1、Linux系统通过Framebuffer程序来控制LCD,frame是帧,buffer是缓存,所以Framebuffer就是储存一帧图像数据的内存。Framebuffer保存着一帧图像中的每一个像素颜色值。
假设LCD的分辨率为1024 × 768,每一个像素的颜色用32位来表示,那么Framebuffer的大小就是:(1024 × 768 × 32/8)字节。

 2、显示图像的过程:
应用程序来将数据存放进Framebuffer中,而显示这些数据的工作是由LCD控制器完成的,驱动程序设置好LCD控制器后,就会周而复始地从Framebuffer中逐一取出每个像素的颜色值发送给LCD然后逐一显示出来,这样就完成了一帧图像的显示,周而复始地读取像素数据就能一直显示图像。因为读取的速度非常快,所以尽管像素点很多,但是读取一遍的时间很快,人眼并不会发现。

3、当像素想要修改某个像素的颜色值时,就必须要知道LCD的分辨率、bpp、首地址
(1)LCD的分辨率xres × yres表示在屏幕上共有xres × yres个像素点,每行有xres个像素点,总共yres行。这些像素点和Framebuffer中的数据一一对应。
(2)bpp(bits_per_pixel)就是每个像素要用多少位来表示颜色值
(3)已知分辨率、bpp,那么像素点(x,y)前面共有(y × xres + x)个像素点,因为每个像素为bpp位,一个字节占8位,所以像素点(x,y)的在Framebuffer中的偏移地址为:
offset   =(y × xres + x)× bpp/8
(4)知道了偏移地址后,还需要知道Framebuffer的首地址fb_base(第一个像素点的内存地址),fb_base + offset 就能得出像素点(x,y)的内存地址并修改颜色值。

4、颜色值的存储方式
如图所示:

                                                                     

(1)每种颜色都可以用三原色:红绿蓝组合得到,其中比较特别的是白色(0xffffff)和黑色(0)。所以在每个像素中以不用方式存储了每个颜色所需的三原色,这取决于存储的位数bpp
(2)对于32bpp,常用的是RGB888,一般只设置低24位,高8位表示透明度(一般LCD都不支持)
         对于24bpp,为了硬件上方便处理,实际效果和32bpp一样,只是没有高8位
         对于16bpp,常用的是RGB565,即红色5位绿色6位蓝色5位的存储方式,很少场合会用到RGB555,而这可以通过系统调用 ioctl 读取驱动程序中的RGB位偏移来确定使用哪种格式
(3)从RGB888转换成RGB565:

5、涉及的API函数

(1)打开设备节点:open

(2)获取分辨率等参数:ioctl

static struct fb_var_screeninfo var;    /* 存放LCD可变参数的结构体 */

if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))  

  printf("can't get var\n");
  return -1;
}

注意ioctl用到的参数是:FBIOGET_VSCREENINFO,FB即Framebuffer,V即var可变的,VSCREENINFO即可变的屏幕信息,所以该参数表示get var screen info ,获得屏幕的可变信息。

(3)映射Framebuffer:mmap
         因为Framebuffer是驱动程序分配的,如果要使用就必须把Framebuffer映射到用户空间里
         要映射一块内存,就要知道它的地址(由驱动程序设置)和大小(由应用程序设定),代码如下:

line_width  = var.xres * var.bits_per_pixel / 8;              //每一行偏移的字节长度,等于xres*pixel_width

pixel_width = var.bits_per_pixel / 8;                             // 每个像素点偏移的字节长度,等于bpp/8

screen_size = var.xres * var.yres * var.bits_per_pixel / 8;    //屏幕上像素点占用的总字节数

fb_base = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);  //映射Framebuffer,只要访问fb_base就可以访问到Framebuffer

if (fb_base == (unsigned char *)-1)
{
  printf("can't mmap\n");
  return -1;
}

(4)实现描点函数:
描点函数是基础,实现描点就可以实现写字、画图
附上代码:

void lcd_put_pixel(int x, int y, unsigned int color)                            //color是32位RGB888格式
{
    unsigned char *pen_8 = fb_base+y*line_width+x*pixel_width;   //pen_8指针指向像素点(x,y)的起始地址,可以使用pen_8指针来修改目标像素点的颜色值
    unsigned short *pen_16; 
    unsigned int *pen_32;   
    unsigned int red, green, blue;  

    pen_16 = (unsigned short *)pen_8;
    pen_32 = (unsigned int *)pen_8;        //可以使用pen_16、pen_32指针来修改目标像素点的颜色值

    switch (var.bits_per_pixel)                  //判断分辨率,然后改变像素点的颜色值
    {
        case 8:
       {
            *pen_8 = color;
           break;
        }

        case 16:                                       //把16位转换成32位,从RGB888转换成RGB565
        {
        
   /* 565 */
            red  = (color >> 16) & 0xff;       
            green = (color >> 8) & 0xff;       
            blue  = (color >> 0) & 0xff;       
//取出三原色各8位的颜色信息
            color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);     //保留红色的高5位,绿色的高6位,蓝色的高5位,然后得到RGB565的color变量
            *pen_16 = color;           // 将得到的color的值赋给pen_16指针指向的内存值,这样就成功改变了像素点的颜色值
            break;
        }

        case 32:
        {
            *pen_32 = color; 
            break;
        }

        default:
        {
            printf("can't surport %dbpp\n", var.bits_per_pixel);
            break;
        }
    }
}

附:

下面是全部的代码:

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



static int fd_fb;
static struct fb_var_screeninfo var;    /* Current var */
static int screen_size;
static unsigned char *fb_base;
static unsigned int line_width;       
static unsigned int pixel_width;     

/**********************************************************************
 * 函数名称: lcd_put_pixel
 * 功能描述: 在LCD指定位置上输出指定颜色(描点)
 * 输入参数: x坐标,y坐标,颜色
 * 输出参数: 无
 * 返 回 值: 会
 ***********************************************************************/ 

void lcd_put_pixel(int x, int y, unsigned int color)                            //color是32位RGB888格式
{
    unsigned char *pen_8 = fb_base+y*line_width+x*pixel_width;   //pen_8指针指向像素点(x,y)的起始地址,可以使用pen_8指针来修改目标像素点的颜色值
    unsigned short *pen_16; 
    unsigned int *pen_32;   

    unsigned int red, green, blue;  

    pen_16 = (unsigned short *)pen_8;
    pen_32 = (unsigned int *)pen_8;        //可以使用pen_16、pen_32指针来修改目标像素点的颜色值

    switch (var.bits_per_pixel)                  //判断分辨率,然后改变像素点的颜色值
    {
        case 8:
        {
            *pen_8 = color;
            break;
        }

        case 16:                                       //把16位转换成32位,从RGB888转换成RGB565
        {
            /* 565 */
            red  = (color >> 16) & 0xff;       
            green = (color >> 8) & 0xff;       
            blue  = (color >> 0) & 0xff;        //取出三原色各8位的颜色信息
            color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);     //保留红色的高5位,绿色的高6位,蓝色的高5位,然后得到RGB565的color变量
            *pen_16 = color;           // 将得到的color的值赋给pen_16指针指向的内存值,这样就成功改变了像素点的颜色值
            break;
        }

        case 32:
        {
            *pen_32 = color;                    //思考32位为什么 不用像16位一样改变格式?
            break;
        }

        default:
        {
            printf("can't surport %dbpp\n", var.bits_per_pixel);
            break;
        }
    }
}



int main(int argc, char **argv)
{
    int i;    
    fd_fb = open("/dev/fb0", O_RDWR);
    if (fd_fb < 0)
    {
        printf("can't open /dev/fb0\n");
        return -1;
    }
    if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))          //获取LCD的分辨率、bpp等信息并放在var中
    {
        printf("can't get var\n");
        return -1;
    }

    line_width  = var.xres * var.bits_per_pixel / 8;              //每一行偏移的字节长度,等于xres*pixel_width
    pixel_width = var.bits_per_pixel / 8;                             // 每个像素点偏移的字节长度,等于bpp/8
    screen_size = var.xres * var.yres * var.bits_per_pixel / 8;    //屏幕上像素点占用的总字节数
    fb_base = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);  //映射Framebuufer,只要访问fb_base就可以访问到Framebuffer
    if (fb_base == (unsigned char *)-1)
    {
        printf("can't mmap\n");
        return -1;
    }

    /* 清屏: 全部设为白色 */
    memset(fb_base, 0xff, screen_size);

    /* 随便设置出100个为红色 */
    for (i = 0; i < 100; i++)
        lcd_put_pixel(var.xres/2+i, var.yres/2, 0xFF0000);    

    /* 释放映射的Framebuffer内存段 */
    munmap(fb_base , screen_size);

    close(fd_fb);
    return 0;   
}

猜你喜欢

转载自blog.csdn.net/San_a_fish_of_dream/article/details/113486805