文件IO--------1.3.1Linux Frame Buffer

目录

前言:

1.基本操作

2.使用ioctl函数去获取设备信息

3.操作LCD屏幕的效率问题


前言:

        Linux Frame Buffer 其指的是 Linux 底层的帧缓冲设备,可以显示一帧一帧的图像(屏幕的显示)。因为linux中的任何东西都是以文件方式进行存储的,所以我们可以通过读写这个文件来对屏幕显示进行操作。


1.基本操作


        帧缓冲是Linux系统为显示设备提供的一个接口,把显示设备抽象成一个设备文件,它可以让上层的图像应用程序不需要关心具体的硬件的实现细节,上层的图像应用程序只需要操作对应的"文件",往文件中写入"数据",经过帧缓冲驱动,就可以在硬件设备(LCD)上面显示对应的图像  
    如何让LCD设备显示颜色呢?
    Linux中一切都是文件,只需要利用文件IO的接口去操作LCD设备就可以了
    操作文件的大概步骤:
        open    //打开文件获得文件描述符
        read/write     //对文件进行读写操作
        close        //关闭文件

    操作文件的时候需要知道文件名:
        lcd的文件名  "/dev/fb0"  是一个绝对路径(Frame Buffer)

    Lcd上图像的显示,是由一个一个的像素点组成的
        像素点:可以描述一个点的颜色
        将LCD上面每一个像素点"描绘"成不同的颜色,就可以显示一副图画

        颜色的组成:RGB三原色
        每一个基色使用一个byte(8bits)表示,给基色中的每一个bit赋值为不同的值,就可以表示不同的颜色
            问题:
                一个基色可以表示多少种颜色?   0~255 ------>256
                    8个bit的值不同,就可以表示不同的颜色 
                三个基色可以表示多少种颜色?   2^24 ------>1670万种
                    24个bit的值不同,就可以表示不同的颜色 

        LCD上面一个像素点的表示: ARGB   A:透明度(1byte),R:红色,G:绿色,B:蓝色
            在LCD上面一个像素点使用几个字节表示?   4byte
            使用4byte就可以表示一个像素点的颜色
    
    如果使用一种数据类型来表示一个像素点的颜色的话,可以使用什么数据类型?
        int  刚好4个字节
        int color; //color就是一个int类型的变量,占用4byte
        给color赋值为不同的值,color就表示不同的颜色

            红色:0x00FF0000
            绿色:0x0000FF00
            蓝色:0x000000FF
            黑色:0x0
            白色:0x00FFFFFF

                所以只需要把颜色数据写入到屏幕对应的文件中,就可以让屏幕显示对应的颜色
       而像素点在"文件"中的排列顺序是从上至下,从左至右的。
                理论上就可以通过文件去操作屏幕上面每一个像素点的颜色。

        假设开发板屏幕上有像素点:800*480个,则每一行有800个像素点,有480行

        于是我们就可以写一个简单的代码,让开发板显示对应的颜色
            //打开屏幕
            //写入颜色数据
            //关闭屏幕

        注意:
            有的开发板上面开机就会自动运行一个iot的程序
            关闭方法:
                killall iot 
                or 
                设置 /etc/profile 文件

            并且要注意文件光标的位置!!!!!

        例子如下:

#include
#include 
#include 
#include 
#include 

int main()
{
    //打开屏幕
    int fd = open("/dev/fb0", O_RDWR);
    if(-1 == fd)
    {
        perror("open lcd error");
        return -1;
    }
    printf("open success!\n");
    //写入颜色数据
    int color[480][800] = {0};
    int i,j;
    for(i=0;i<480;i++)
    {
        for(j=0;j<800;j++)
        {
            color[i][j] = 0x0000FF00;
        }
    }
    write(fd,color,800*480*4);
    sleep(1);
    lseek(fd,0,SEEK_SET);
    
    for(i=0;i<480;i++)
    {
        for(j=0;j<800;j++)
        {
            color[i][j] = 0x0000FFFF;
        }
    }
    write(fd,color,800*480*4);

    //关闭屏幕
    close(fd);
    return 0;
}

2.使用ioctl函数去获取设备信息


    ioctl函数是用来对文件/设备进行除了读写以外的其他控制操作,每一个设备的控制操作都是不一样的,这些设备具体有哪些操作是由设备的驱动程序决定的.
    NAME
        ioctl - control device
        控制设备(具体的操作由驱动程序决定)
        此处我们可以使用函数获取LCD屏幕的一些基本信息
    SYNOPSIS
        #include

        int ioctl(int fd, unsigned long request, ...);
        fd:文件描述符,表示你要操作哪一个设备
        request:命令号码,在驱动实现的时候,一般会把某些特定的操作取一个命令号,命令号的具体含义是由提供该命令号的驱动程序决定
        ...:可变参数,具体的操作由命令号决定
        返回值:
            成功返回0
            失败返回-1,同时errno被设置

    例子:
        如果程序需要知道帧缓冲设备的相关信息,可以使用ioctl来完成
        对于帧缓冲设备,最常用的有两条命令(/usr/include/linux/fb.h):


            #define FBIOGET_VSCREENINFO    0x4600
                返回和设备相关的可变信息
                如:帧缓冲设备的大小(宽度,高度),以及颜色显示等信息


            #define FBIOGET_FSCREENINFO    0x4602
                返回和设备相关的固定信息
                如:设备本身的一些信息,硬件加速度等信息

            而对应的结构体是:
            struct fb_var_screeninfo{}    
            和
            struct fb_fix_screeninfo{}

           使用方法:
            //定义一个结构体用来保存即将获取到的数据
            struct fb_var_screeninfo  vinfo;      
            //把获取到的信息保存到结构体中
            ioctl(fd,FBIOGET_VSCREENINFO,&vinfo);

        结构体内容如下

        struct fb_var_screeninfo 
        {
        __u32 xres;            /* 可视分辨率    */
        __u32 yres;
        __u32 xres_virtual;        /* virtual resolution        */
        __u32 yres_virtual;
        __u32 xoffset;            /* offset from virtual to visible */
        __u32 yoffset;            /* resolution            */

        __u32 bits_per_pixel;        /* 每一个像素点占用的bit数量*/
        __u32 grayscale;        /* 0 = color, 1 = grayscale,    */
                        /* >1 = FOURCC            */
        struct fb_bitfield red;        Red
        struct fb_bitfield green;    Green
        struct fb_bitfield blue;    Blue
        struct fb_bitfield transp;    /* transparency            */    
        }       
        struct fb_bitfield {
            __u32 offset;            /* beginning of bitfield    */
            __u32 length;            /* length of bitfield        */
            __u32 msb_right;        /* != 0 : Most significant bit is right */ 
        };

3.操作LCD屏幕的效率问题


    每一次操作屏幕的图像,都需要使用write函数
    但是write函数会直接去访问硬件资源,占用总线,效率是非常低的。于是我们可以使用内存映射,把屏幕文件映射到内存中(操作内存就相当于操作屏幕)

    映射:把文件和一段内存一一对应(建立一个联系),操作文件的时候就不需要使用write函数,只需要使用指针操作对应的内存就可以了(C语言中指针可以直接操作内存),内存内容的改变会由具体的映射驱动同步到文件中去

    在LCD中,如果把屏幕映射到内存中,就不需要使用write函数了,只需要去操作映射之后的内存即可


    NAME
        mmap, munmap - map or unmap files or devices into memory
    SYNOPSIS
        #include
        mmap是把指定的文件映射到进程地址空间(堆和栈中间)
        void *mmap(void *addr, size_t length, int prot, int flags,
                    int fd, off_t offset);

注释:
            addr:表示映射地址,你要把文件映射到内存的哪一个位置
                一般为NULL,表示让OS自行选择一个合适的地址
            length:表示你要映射的长度(字节),你的文件多大
            prot:应用程序对映射之后的内存的操作权限
                    PROT_READ   可读
                    PROT_WRITE  可写
                        PROT_READ  |   PROT_WRITE     
            flags:映射标记
                MAP_SHARED:共享映射,内存的改变会同时同步到文件
                MAP_PRIVATE:私有映射,内存数据的改变对其他程序是不可见的
            fd:你要映射的文件的文件描述符
            offset:偏移量,表示你要从文件的哪一个位置开始映射,一般为0,表示从文件的开头开始映射
        返回值:
            成功返回映射之后的内存的首地址(相当于文件的开头)
            失败返回MAP_FAILED,errno被设置

        munmap是解除内存的映射关系
        int munmap(void *addr, size_t length);
            addr:你要接映射的首地址,是mmap的返回值
            length:你要接映射的长度

    流程:
        打开文件 
        映射文件到内存
        ==============
        操作内存就是操作文件
        ==============      
        解除映射
        关闭文件  

int *plcd = (int *)mmap(NULL, 800*480*4, PROT_READ | PROT_WRITE, MAP_SHARED,fd,0);
    if(plcd == MAP_FAILED)
    {
        perror("mmap failed");
        close(fd);
        return -1;
    }

    假设映射成功后,映射后的像素点在内存中的对应关系是从左至右,从上至下
    也就是说,plcd指向的位置对应屏幕上面第0行的第0个点
    plcd+1对应屏幕上面第0行的第1个点(指针做偏移,是偏移单位个指向类型的长度)

    如果想要通过plcd把屏幕的第0行的第0个点设置为红色,应该如何操作?
        把0x00ff0000这个数据放到第0行的第0个点对应的内存地址
        第0行的第0个点对应的内存地址就是plcd
        *plcd = 0x00FF0000

    如果想要通过plcd把屏幕的第0行的第1个点设置为红色,应该如何操作?
        把0x00ff0000这个数据放到第0行的第1个点对应的内存地址
        第0行的第1个点对应的内存地址就是(plcd+1)
        *(plcd+1) = 0x00FF0000    

    如果想要通过plcd把屏幕的第0行的第x个点设置为红色,应该如何操作?
        把0x00ff0000这个数据放到第0行的第x个点对应的内存地址
        第0行的第x个点对应的内存地址就是(plcd+x)
        *(plcd+x) = 0x00FF0000   

    如果想要通过plcd把屏幕的第y行的第x个点设置为红色,应该如何操作?
        把0x00ff0000这个数据放到第y行的第x个点对应的内存地址
        第y行的第x个点对应的内存地址就是(plcd+800*y+x)
        *(plcd+800*y+x) = 0x00FF0000   

    我们可以通过plcd去操作屏幕上面的每一个像素点
    注意:因为内存是连续的,plcd向后只能偏移800*480*4个字节有权限使用

你可能感兴趣的:(网络,linux)