framebuffer
文件io:
b 文件io
c 文件io
d 目录io
- 标准io,文件io
l 具体看连接箱
s 文件io
p 文件io
UI技术:usr interface
framebuffer:帧缓冲,帧缓存技术
Linux内核专门为图形化显示提供的应用程序接口
常见像素格式:
RGB888:红、绿、蓝各占 8 位,共 24 位(3 字节),通常扩展为 32 位(4 字节)以便对齐内存访问。
RGB565:红 5 位、绿 6 位、蓝 5 位,共 16 位(2 字节)。
颜色值范围:每个颜色分量为 0–255(8 位无符号整数),全满为白色,全零为黑色。显卡负责将显存中的数字信号转换为模拟信号驱动显示屏。显存中数据为线性排列,需映射到二维屏幕坐标。坐标 (x, y) 对应的显存偏移量为:offset = y * width + x
。
显存位于内核空间,用户程序无法直接访问,需通过 内存映射(mmap) 将其映射到用户空间。
内存映射后,用户可通过指针访问映射区域,写入像素数据即等效于写入显存。
指针类型应为 unsigned int*
(4 字节),以匹配 RGB888 扩展后的像素大小。指针每加 1,偏移 4 字节,对应一个像素点。
示例:在 800×600 分辨率下点亮中心点 (400, 300) 为红色,应向 p_memory + 300 * 800 + 400
写入 0x00FF0000
。
实现步骤总结:
- 打开显示设备:操作
/dev/fb0
文件,代表帧缓冲设备。 - 获取设备参数:读取分辨率、像素格式等信息,以计算显存大小。
- 建立内存映射:使用
mmap
将显存映射到用户空间,获取可操作的内存首地址。 - 写入像素数据:通过映射后的指针写入 RGB 颜色值,实现图形绘制。
- 解除映射关系
- 关闭显示设备
#include <linux/fb.h>
struct fb_var_screeninfo vinfo;
int ret = ioctl(fb, FBIOGET_VSCREENINFO, &vinfo);
if (ret < 0) {printf("ioctl error\n");return -1;
}
使用 ioctl()
与用户层与内核交互,获取屏幕信息。
请求命令为 FBIOGET_VSCREENINFO
,对应宏定义在 linux/fb.h
头文件中。
定义结构体 struct fb_var_screeninfo vinfo
存储参数。
建立内存映射
- 使用
mmap()
将显存映射到用户空间。 - 映射大小计算公式:
xres * yres * (bits_per_pixel / 8)
- 参数设置:
- addr:
NULL
(由系统自动分配) - length: 计算得到的显存大小,映射的空间大小
- prot:
PROT_READ | PROT_WRITE
- flags:
MAP_SHARED(共享)
- fd:
fb
(设备文件描述符) - offset:
0
(从起始地址映射)偏移量
- addr:
munmap(p_memory, vinfo.xres * vinfo.yres * (vinfo.bits_per_pixel / 8));
close(fb);
解除映射,关闭设备。
错误处理机制
单纯使用 printf 打印错误信息缺乏具体原因,不利于调试。
引入 perror() 函数:可同时输出自定义错误信息和系统提供的错误原因(如 "No such file or directory")。
系统在函数调用失败时会设置全局变量 errno,其值对应特定错误类型。
可通过 strerror(errno) 将 errno 数值转换为可读的错误字符串。
推荐使用 perror() 替代 printf 输出错误信息,因其自动附加换行且包含系统错误说明。
该函数是在某坐标点上一个点。
void draw_point(int x, int y, unsigned int col)
{if (x >= vinfo.xres || y >= vinfo.yres){return ;}if (vinfo.bits_per_pixel == RGB_FMT_888){unsigned int *p = pmem;*(p+vinfo.xres_virtual*y+x) = col;}else if (vinfo.bits_per_pixel == RGB_FMT_565){unsigned short *p = pmem;*(p+vinfo.xres_virtual*y+x) = col;}
}
以下代码是画横线,竖线和矩形,原理是让一个点循环成一条线,一条线循环为一个面。
void draw_heng(int x, int y ,unsigned int col)
{if(x >= vinfo.xres || y >= vinfo.yres){return ;}if(vinfo.bits_per_pixel == RGB_FMT_888){unsigned int *p = pmem;for(x = 0; x < vinfo.xres_virtual; ++x){*(p+vinfo.xres_virtual*y+x) = col;}}else if(vinfo.bits_per_pixel == RGB_FMT_565){unsigned short *p = pmem;for(x = 0; x < vinfo.xres_virtual; ++x){*(p+vinfo.xres_virtual*y+x) = col;}}
}
void draw_shu(int x, int y, unsigned int col) { if (x < 0 || x >= vinfo.xres || y < 0 || y >= vinfo.yres) {return;}if (vinfo.bits_per_pixel == RGB_FMT_888) {unsigned int *p = (unsigned int *)pmem; for (; y < vinfo.yres_virtual; y++) { *(p + y * vinfo.xres_virtual + x) = col; }} else if (vinfo.bits_per_pixel == RGB_FMT_565) {unsigned short *p = (unsigned short *)pmem;for (; y < vinfo.yres_virtual; y++) { *(p + y * vinfo.xres_virtual + x) = col; }}
}
void draw_juxing(int x, int y ,unsigned int col)
{if(x >= vinfo.xres || y >= vinfo.yres){return ;}if(vinfo.bits_per_pixel == RGB_FMT_888){for(; y < vinfo.yres_virtual; ++y){draw_heng(0, y ,0x00FF0000);}}
}
图像显示技术实现摘要
一、图像格式基础
- 常见网络图片格式为JPG或PNG,属于压缩格式,体积小但可能存在图像损失(有损压缩)。
- BMP(位图)为未压缩的无损图像格式,数据完整,便于直接读取像素值。
- 由于压缩图像需解压才能获取RGB值,因此在嵌入式设备贴图时通常使用BMP格式。
二、BMP文件结构
- BMP文件前14字节为文件头信息,后40字节为图像信息头,共54字节头部数据。
- 第55字节起为实际像素颜色值(RGB),但存储顺序为BGR(蓝绿红),而非RGB。
- 显示前需跳过前54字节头部信息,读取后续像素数据。
三、图像处理流程
图像准备:
- 从网络下载图片并保存为JPG格式。
- 使用系统“画图”工具打开,调整分辨率至目标显示屏大小(如800×600)。
- 另存为BMP格式(不可仅修改后缀名,必须通过工具转换以确保数据格式正确)。
文件读取与头部解析:
- 使用
open()
函数以只读方式打开BMP文件。 - 读取前54字节头部信息,获取图像分辨率(biWidth, biHeight)和位深度(如24位)。
- 关闭文件后重新打开,用于读取像素数据。
- 使用
像素数据读取:
- 使用
lseek()
将文件指针偏移54字节,跳过头部。 - 计算所需内存空间:
width × height × 3
(24位 = 3字节/像素)。 - 在堆上分配内存,使用
read()
一次性读取所有BGR数据。
- 使用
颜色值转换与绘制:
定义指针遍历BGR数据,依次读取B、G、R三个字节。
根据目标显示格式进行颜色拼接:
RGB888格式:
- R左移16位,G左移8位,B不移。
- 按位或运算合成32位颜色值:
color = (R << 16) | (G << 8) | B
RGB565格式:
- 存在颜色损失,需压缩为2字节。
- R保留高5位(右移3位),G保留高6位(右移2位),B保留高5位(右移3位)。
- 分别左移至对应位置后按位或合成:
color = ((R >> 3) << 11) | ((G >> 2) << 5) | (B >> 3)
图像绘制逻辑:
- 使用双层循环遍历图像行列(i为列,j为行)。
- 绘制坐标为
(x + i, y + j)
,其中(x,y)为起始位置。 - 调用
draw_point()
在对应位置绘制颜色。
图像倒置问题处理:
- BMP图像数据按“从下到上”方式存储,即第一行数据对应图像最底行。
- 解决方案:将行索引倒序处理,即
j = height - 1 - current_row
。 - 实现方式:循环中j从
height - 1
递减至0,确保像素正确映射。
四、验证与调试
- 编译运行后发现
fopen
失败,原因为文件路径中使用了错误的文件名(如3.bmp
而非1.bmp
)。 - 修正文件名后成功加载并显示图像。
- 初次显示图像倒置,通过调整行绘制顺序解决,最终实现正常显示。
int get_bmp_head_info(const char *bmpname, Bmp_file_head_t *pheadinfo, Bmp_info_t *pbmpinfo)
{FILE *fp = fopen(bmpname, "r");if (NULL == fp){perror("fopen error");return -1;}fread(pheadinfo, sizeof(Bmp_file_head_t), 1, fp);fread(pbmpinfo, sizeof(Bmp_info_t), 1, fp);fclose(fp);return 0;
}
void draw_bmp(int x, int y, char *bmpname)
{Bmp_file_head_t headinfo;Bmp_info_t bmpinfo;get_bmp_head_info("./1.bmp", &headinfo, &bmpinfo);int fd = open(bmpname, O_RDONLY);if (-1 == fd){perror("open bmp error");return ;}lseek(fd, 54, SEEK_SET);unsigned char *buff = malloc(bmpinfo.biHeight*bmpinfo.biWidth*bmpinfo.biBitCount/8);read(fd, buff, bmpinfo.biHeight*bmpinfo.biWidth*bmpinfo.biBitCount/8);close(fd);unsigned char *p = buff;unsigned char r, g, b;for (int j = bmpinfo.biHeight-1; j >= 0; j--){for (int i = 0; i < bmpinfo.biWidth; i++){b = *p;++p;g = *p;++p;r = *p;++p;if (vinfo.bits_per_pixel == RGB_FMT_888){unsigned int col = (r << 16) | (g << 8) | (b << 0);draw_point(i+x, j+y, col);}else if (vinfo.bits_per_pixel == RGB_FMT_565){unsigned short col = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);draw_point(i+x, j+y, col);}}}free(buff);
}