LCD触控屏——FrameBuffer

LCD触控屏——FrameBuffer

一、基本概念

1、什么是framebuffer

​ 所谓frame buffer就是用于存储图像数据的一块内存区域。如果我们想要在Linux下实现图形界面,就得往这个区域内写入自己想要显示的内容数据,驱动程序会自动的,每隔一段时间读取这个区域内的数据,并且将这些数据显示在屏幕上。

2、颜色的表示

​ 众所周知,三原色组成了这个世界上各种各样的颜色。三原色即红绿蓝,在计算机中又叫做RGB,通过RGB模型将颜色描述为三个分量的组合,分别为红色、绿色、蓝色分量的亮度值。根据每个分量的位数,产生了不同的颜色格式:RGB888、RGB565、RGB555。

RGB888:

  • RGB888 格式使用 24 位来表示一个像素的颜色信息,其中 8 位用于红色通道、8 位用于绿色通道、8 位用于蓝色通道。这意味着每个颜色通道可以有 256(2^8)种不同的亮度级别,共同组合形成一个广泛的颜色范围。

RGB565:

  • RGB565 格式使用 16 位来表示一个像素的颜色信息,其中 5 位用于红色通道、6 位用于绿色通道、5 位用于蓝色通道。这种格式相比 RGB888 来说更加节省存储空间,但同时牺牲了一定的颜色精度。

RGB555:

  • RGB555 格式也使用 16 位来表示一个像素的颜色信息,其中 5 位用于红色通道、5 位用于绿色通道、5 位用于蓝色通道。与 RGB565 相比,RGB555 在绿色通道上减少了一位,因此在绿色调性上可能会略显不足,但整体颜色表现仍比较均衡。

3、如何计算屏幕上某一像素在framebuffer中对应的地址

(x,y)像素起始地址=fb_base+(xres*bpp/8)*y + x*bpp/8
fb_base: framebuffer的首地址
xres: 屏幕x轴的最大宽度
bbp: 每一个像素点的由几位数据表示(RGB888是32位 RGB565和RGB555 是16位)

二、关于frambuffer的编程流程

在Linux下编写framebuffer程序主要是调用各种系统接口函数。

​ 1、定义结构体存储屏幕相关信息

typedef enum {
	rgb888 = 0,
	rgb565,
	rgb555,
} ecolor_type_t;
   
//屏幕信息结构体
typedef struct {
 	int x_res;               // x轴的像素大小
 	int y_res;               // y轴的像素大小
 	int bbp;                 // 每个像素数据的大小(单位:bit)
 	int size;                // frambuffer的大小
 	void *base_addr;         // framebuffer 的基地址
 	int fd;                  // framebuffer的文件描述符
 	ecolor_type_t colortype; // 颜色类型
} screen_info_t;
screen_info_t g_screen_info;

2、使用open函数打开framebuffer字符设备文件

int fd = open(dev_name, O_RDWR);

3、获取FrameBuffer设备的固定信息

使用 ioctl() 系统调用和 FBIOGET_FSCREENINFO 操作来获取FrameBuffer中的设备固定信息(例如屏幕尺寸、颜色深度等)。这些信息都存储在 fb_fix_screeninfo 结构体当中

struct fb_fix_screeninfo finfo;  
if (ioctl(fb, FBIOGET_FSCREENINFO, &finfo) == -1) {  
    perror("Error reading fixed information");  
    close(fb);  
    exit(1);  
}

4、获取FrameBuffer设备的可变信息

使用 ioctl()FBIOGET_VSCREENINFO 来获取设备的可变信息(如像素格式、虚拟屏幕尺寸等)。这些信息存储在fb_var_screeninfo 结构体中。

struct fb_var_screeninfo vinfo;  
if (ioctl(fb, FBIOGET_VSCREENINFO, &vinfo) == -1) {  
    perror("Error reading variable information");  
    close(fb);  
    exit(1);  
}

*5、修改Frame Buffer设置

如果需要,可以修改 vinfo 中的设置(例如分辨率、颜色模式等等),然后使用 FBIOPUT_VSCREENINFO 将其写回设备。

// 修改vinfo中的一些设置...  
if (ioctl(fb, FBIOPUT_VSCREENINFO, &vinfo) == -1) {  
    perror("Error writing new variable information");  
    close(fb);  
    exit(1);  
}

6、映射FrameBuffer到内存

使用 mmap() 将FrameBuffer映射到进程的地址空间,以便可以直接访问屏幕内存。

unsigned char *fbp = mmap(0, finfo.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fb, 0);  
if ((int)fbp == -1) {  
    perror("Error: failed to map framebuffer device to memory");  
    close(fb);  
    exit(1);  
}

7、在FrameBuffer上绘图

在FrameBuffer上绘图具体可以按照以下流程进行:

  • 确定绘图区域:首先,你需要确定要在屏幕上绘制的区域。这通常基于Framebuffer的分辨率和你要绘制的图形的尺寸。
  • 计算像素位置:对于要在屏幕上绘制的每个像素,你需要根据屏幕的分辨率和像素格式计算出该像素在Framebuffer内存中的位置。这通常涉及到将像素的(x, y)坐标转换为内存中的偏移量。
  • 写入像素数据:一旦你知道了像素在Framebuffer内存中的位置,你就可以向该位置写入像素数据了。像素数据通常是一个或多个字节,具体取决于屏幕的颜色深度和像素格式。
  • 处理颜色:如果你的屏幕支持多种颜色,你需要将你想要的颜色转换为屏幕能够理解的格式,并将其写入相应的像素位置。这通常涉及到颜色空间的转换和颜色深度的处理。
  • 重复步骤:对于要绘制的每个像素,重复上述步骤。这可能需要遍历整个绘图区域,并为每个像素写入数据。
  • 优化性能:如果绘图操作非常频繁或需要处理大量数据,你可能需要考虑优化你的代码以提高性能。这可能包括减少不必要的内存访问、使用更快的算法或利用硬件加速功能。
  • 测试:在完成绘图操作后,不要忘记测试你的代码以确保它按预期工作。你可以查看屏幕上的输出,以确保绘制的图形正确无误。

8、清理和关闭

完成绘图后,取消映射FrameBuffer内存,并关闭设备文件。

munmap(fbp, finfo.smem_len);  
close(fb);

以下是一个在Linux下显示一个紫色方块的例程:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <sys/ioctl.h>

int main() {
    int fb = 0;
    struct fb_var_screeninfo vinfo;
    struct fb_fix_screeninfo finfo;
    long int screensize = 0;
    char *fbp = 0;
    unsigned int *fbpp;

    // 打开FrameBuffer设备
    fb = open("/dev/fb0", O_RDWR);
    if (fb == -1) {
        perror("Error opening framebuffer device");
        exit(EXIT_FAILURE);
    }

    // 获取屏幕信息
    if (ioctl(fb, FBIOGET_VSCREENINFO, &vinfo) == -1) {
        perror("Error reading variable information");
        exit(EXIT_FAILURE);
    }

    if (ioctl(fb, FBIOGET_FSCREENINFO, &finfo) == -1) {
        perror("Error reading fixed information");
        exit(EXIT_FAILURE);
    }

    // 计算屏幕尺寸
    screensize = vinfo.yres_virtual * finfo.line_length;

    // 映射FrameBuffer到用户空间
    fbp = (char*)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fb, 0);
    if ((int)fbp == -1) {
        perror("Error: failed to map framebuffer device to memory");
        exit(EXIT_FAILURE);
    }

    // 转换为无符号整型指针,方便操作32位颜色值
    fbpp = (unsigned int*)fbp;

    // 绘制一个红色的正方形
    // 假设正方形的左上角在(100, 100),边长为100像素
    int x, y;
    unsigned int red = 0xFFFF0000; // 32位ARGB颜色,红色

    for (y = 100; y < 200; y++) {
        for (x = 100; x < 200; x++) {
            // 计算像素位置
            long int location = (y * finfo.line_length) + (x * (vinfo.bits_per_pixel / 8));
            // 直接通过指针访问并设置颜色(注意这里我们使用了fbpp来简化操作)
            fbpp[(y * vinfo.xres + x)] = red;
            // 注意:上面的计算方式假设了每行像素是紧密排列的,这在大多数情况下是正确的,
            // 但如果屏幕有padding或者使用了特殊的行对齐方式,则可能需要使用finfo.line_length来计算。
            // 这里为了简化,我们直接使用了vinfo.xres,这在大多数情况下也是可行的,特别是当vinfo.xres_virtual等于vinfo.xres时。
        }
    }

    // 清理和退出(在实际应用中,你可能希望保持FrameBuffer的映射直到程序结束)
    // munmap(fbp, screensize);
    // close(fb);

    // 注意:由于我们直接在屏幕上绘制了内容,因此程序结束后这些内容仍然会留在屏幕上,
    // 除非有其他程序(如另一个FrameBuffer程序或图形界面)覆盖了它们。

    return 0;
}

// 注意:上面的代码中的像素位置计算部分做了简化处理。
// 在实际情况下,你应该总是使用finfo.line_length来计算每行的字节数,
// 因为屏幕的行可能包含额外的填充字节(padding),以确保每行从某个特定的内存地址开始。
// 但是,对于许多现代屏幕和驱动,这些填充字节可能不存在,或者每行的像素确实是紧密排列的。

效果图:
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值