项目知识点

发布时间:2024年01月24日

Linux Frame Buffer(Linux 底层的帧缓冲设备)
????????可以显示一帧一帧的图像(屏幕的显示)

一. 基本操作

? ? ?帧缓冲是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)
????????在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 文件

练习:

????????使用基本的文件IO操作函数,在屏幕上面显示简单的颜色
? ? ? ? ? ? ? ? 先显示红色
? ? ? ? ? ? ? ? 1s后(sleep(1))
? ? ? ? ? ? ? ? 显示绿色
? ? ? ? ? ? ? ? 1s后?
? ? ? ? ? ? ? ? 显示蓝色

? ? ? ? 注意光标的位置!!!

代码实现:write函数会直接去访问硬件资源,占用总线,效率是非常低的

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

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};
    for (int i = 0; i < 480; i++) {
        for (int j = 0; j < 800; j++) {
            color[i][j] = 0x0000FF00;
        }
    }
    int ret = write(fd, color, 800 * 480 * 4);
    if (-1 == ret) {
        perror("write_1 lcd fail");
        close(fd); // 关闭屏幕
    }

    sleep(1);

    // 注意:移光标!!! 
    lseek(fd, 0, SEEK_SET);
    
    for (int i = 0; i < 480; i++) {
        for(int j = 0; j < 800; j++) {
            color[i][j] = 0x0000FFFF;
        }
    }

    ret = write(fd, color, 800 * 480 * 4);
    if (-1 == ret) {
        perror("write_2 lcd fail");
    }

    // 关闭屏幕
    close(fd);

    return 0;
}

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

ioctl函数是用来对文件/设备进行除了读写以外的其他控制操作,每一个设备的控制操作都是不一样的,这些设备具体有哪些操作是由设备的驱动程序决定的


? ? NAME
? ? ? ? ioctl - control device
? ? ? ? ????????控制设备(具体的操作由驱动程序决定)
? ? ? ? ????????此处我们可以使用函数去获取LCD屏幕的一些基本信息
? ? SYNOPSIS
? ? ? ?
#include <sys/ioctl.h>

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

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

? ? ? ? ????????vim?/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? */?
};

代码实现:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/fb.h> // 注意!!!
/*
包含
    struct fb_var_screeninfo{}、struct fb_fix_screeninfo{}、
    #define FBIOGET_VSCREENINFO	0x4600、
    #define FBIOGET_FSCREENINFO	0x4602
的头文件
*/

int main() {
    // 打开屏幕
    int fd = open("/dev/fb0", O_RDWR);
    if (-1 == fd) {
        perror("open lcd error");
        return -1;
    }
    printf("open success!\n");
    
    /* 获取屏幕的信息 */
    // 定义一个结构体用来保存即将获取到的数据
    struct fb_var_screeninfo vinfo;    
    //把获取到的信息保存到结构体中
    int r = ioctl(fd, FBIOGET_VSCREENINFO, &vinfo);
    if (-1 == r) {
        perror("ioctl failed");
        close(fd); // 关闭屏幕
        return -1;
    }

    /* 解析结构体,打印获取到的屏幕的信息 */
    // 分辨率
    printf("resolution:%d*%d\n", vinfo.xres, vinfo.yres); // 800*480
    // 每一个像素点占用的bit数量(位)
    printf("bits_per_pixel:%d\n", vinfo.bits_per_pixel); // 32
    // 每一种颜色的信息
    printf("transp.offset:%d\n", vinfo.transp.offset); // 24
    printf("transp.length:%d\n", vinfo.transp.length); // 8

    printf("red.offset:%d\n", vinfo.red.offset); // 16
    printf("red.length:%d\n", vinfo.red.length); // 8

    printf("green.offset:%d\n", vinfo.green.offset); // 8
    printf("green.length:%d\n", vinfo.green.length); // 8

    printf("blue.offset:%d\n", vinfo.blue.offset); // 0
    printf("blue.length:%d\n", vinfo.blue.length); // 8

    // 关闭屏幕
    close(fd);

    return 0;
}

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

每一次操作屏幕的图像,都需要使用write函数
但是write函数会直接去访问硬件资源,占用总线,效率是非常低的

?========>

内存映射,把屏幕文件映射到内存中(操作内存就相当于操作屏幕)

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

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

NAME
    mmap, munmap - map or unmap files or devices into memory
SYNOPSIS
    #include <sys/mman.h>

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:你要接映射的长度

代码实现:把1.txt映射到内存

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>

int main(int argc,char *argv[]) {
    // 打开文件
    int fd = open("1.txt", O_RDWR | O_CREAT, 0777);
    if (-1 == fd) {
        perror("open 1.txt");
        return -1;
    }

    // 把文件映射到内存
    char *plcd = (char*)mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (plcd == MAP_FAILED) {
        perror("mmap failed");
        close(fd);
        return -1;
    }
    /*
        注意!!!:1.txt内容不能为空,并且执行*(plcd + i) = x; 或者 plcd[i] = x;时,
            只有当*(plcd + i) 或者plcd[i]在文件中有值时才能修改(空格也可以),否则无效
    */
    *plcd = 'A';
    plcd[1] = 'B';
    *(plcd+20) = 'H';

    // 解除映射
    munmap(plcd, 1024);

    // 关闭文件
    close(fd);

    return 0;
}

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

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

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

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

代码实现:使用内存映射,把屏幕设置为红色

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>

int main() {
    // 打开屏幕
    int fd = open("/dev/fb0", O_RDWR);
    if (-1 == fd) {
        perror("open lcd error");
        return -1;
    }
    printf("open success!\n");
    
    // 把屏幕映射到内存
    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把屏幕设置为红色
    for (int y = 0; y < 480; y++) {
        for (int x = 0; x < 800; x++) {
            *(plcd + 800 * y + x) = 0x00FF0000;
        }
    }

    // 解除映射
    munmap(plcd, 1024);

    // 关闭屏幕
    close(fd);

    return 0;
}

练习:

? ? ? ? 使用内存映射的形式,在屏幕上面显示颜色?
? ? ? ? 要求:
? ? ? ? ? ? 设计一个函数,在屏幕上指定的位置(x,y)的像素点显示指定的颜色
? ? ? ? ? ? ????????void lcd_draw_point(......);

? ? ? ? ? ? 设计一个函数,在屏幕上指定的位置(x,y)显示一个指定大小,指定颜色的矩形
? ? ? ? ? ? ????????void lcd_draw_rect(....);

? ? ? ? ? ? 设计一个函数,在屏幕上指定的位置(x,y)显示一个指定大小,指定颜色的圆
? ? ? ? ? ? ????????void lcd_draw_cir(....);

? ? ? ? ? ? 设计一个函数,可以把屏幕刷成指定的颜色
? ? ? ? ? ? ????????void lcd_clear(....);

代码实现:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>

// 设计一个函数,在屏幕上指定的位置(x,y)的像素点显示指定的颜色
void lcd_draw_point(int *plcd, int x, int y, int color) {
    *(plcd + 800 * y + x) = color;
}

// 设计一个函数,在屏幕上指定的位置(x,y)显示一个指定大小,指定颜色的矩形 w:长  h:宽
void lcd_draw_rect(int *plcd, int x, int y, int w, int h, int color) {
    for (int i = x, j = y; i >= 0 && i < 800 && i - x <= w && j > 0 && j < 480; i++) {
        *(plcd + 800 * j + i) = color;
    }
    for (int i = x, j = y + h; i >= 0 && i < 800 && i - x <= w && j > 0 && j < 480; i++) {
        *(plcd + 800 * j + i) = color;
    }
    for (int i = x, j = y; i >= 0 && i < 800 && j - y <= h && j > 0 && j < 480; j++) {
        *(plcd + 800 * j + i) = color;
    }
    for (int i = x + w, j = y; i >= 0 && i < 800 && j - y <= h && j > 0 && j < 480; j++) {
        *(plcd + 800 * j + i) = color;
    }
}

// 判断点是否在圆内
int is_within(int x, int y, int i, int j, int r) {
    int a = (x - i) * (x - i) + (y - j) * (y - j);
    int b = r * r;
    if (a <= b) {
        return 1; 
    }
    return 0;
}

// 设计一个函数,在屏幕上指定的位置(x,y)显示一个指定大小,指定颜色的圆
void lcd_draw_cir(int *plcd, int x, int y, int r, int color) {
    for (int i = 0; i < 800; i++) {
        for (int j = 0; j < 480; j++) {
            if (is_within(x, y, i, j, r)) {
                *(plcd + 800 * j + i) = color;
            }
        }
    }
}

// 设计一个函数,可以把屏幕刷成指定的颜色
void lcd_clear(int *plcd, int color) {
    for (int i = 0; i < 480; i++) {
        for (int y = 0; y < 800; y++) {
            *(plcd + 800 * i + y) = color;
        }
    }
}

int main(int argc, char *argv[]) {
    // 打开屏幕
    int fd = open("/dev/fb0", O_RDWR);
    if (-1 == fd) {
        perror("open fail");
        return -1;
    }

    // 映射
    int *plcd =(int*)mmap(NULL, 800 * 480 * 4, PROT_READ | PROT_WRITE, MAP_SHARED,
            fd, 0);
    if (plcd == MAP_FAILED) {
        perror("mmap fail");
        close(fd); // 关闭屏幕
        return -1;
    }

    // 设计一个函数,可以把屏幕刷成指定的颜色
    lcd_clear(plcd, 0x00ffffff);

    // 设计一个函数,在屏幕上指定的位置(x,y)的像素点显示指定的颜色
    lcd_draw_point(plcd, 500, 400, 0x00ff0000);

    // 设计一个函数,在屏幕上指定的位置(x,y)显示一个指定大小,指定颜色的矩形
    lcd_draw_rect(plcd, 100, 100, 200, 500, 0x00ff0000);

    // 设计一个函数,在屏幕上指定的位置(x,y)显示一个指定大小,指定颜色的圆
    lcd_draw_cir(plcd, 840, 410, 250, 0x00ff0000);

    // 解映射
    munmap(plcd, 800 * 480 *4);

    // 关闭屏幕
    close(fd);

    return 0;
}

文章来源:https://blog.csdn.net/qq_68915288/article/details/135819446
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。