UI技术:User interface
framebuffer:帧缓冲,帧缓存技术,Linux内核专门为图形化显示提供的一套应用程序接口
mmap
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
功能
建立内存映射,将文件/设备内容映射到进程虚拟地址空间,实现高效数据读写。
参数
-addr :期望映射到的用户空间首地址,填 NULL 让操作系统自动分配合适地址
-length :要映射的空间大小(字节为单位,需合理规划,避免越界)
-prot :内存保护权限(可组合,如 `PROT_READ ,PROT_WRITE)
-flags :映射类型,常用 MAP_SHARED (修改映射区会回写文件/设备,共享变更)
-fd :待映射的文件/设备的文件描述符(需提前 open 打开)
-offset :映射起始偏移(通常填 0 ,从文件/设备起始位置开始;需是页大小整数倍)
返回值
- 成功:返回实际映射到的用户空间首地址( void* 类型,可强转为需求指针)
- 失败:返回 MAP_FAILED (本质是 (void *)-1 ,需用 perror 等排查错误)
munmap
int munmap(void *addr, size_t length);
解除映射
功能
- 释放 mmap 建立的内存映射关系,回收映射的虚拟地址空间,避免内存泄漏。
- 调用后,原映射区域的指针( addr 起始的 length 空间)不可再访问,否则会触发段错误。
参数
addr mmap :返回的映射首地址(必须精准匹配,否则可能导致未定义行为)
length :映射的空间大小(需与 mmap 时传入的 length 一致,确保完整解除)
返回值
- 成功:返回 0
- 失败:返回 -1 ,可通过 errno 查具体错误(如非法地址、权限问题等)
头文件
#ifndef __FRAMEBUFF_H__
#define __FRAMEBUFF_H__
#define RGB_FMT_888 32
#define RGB_FMT_565 16
#pragma pack(1)
// bmp文件相关信息
typedef struct tagBITMAPFILEHEADER
{
short bfType; // 文件类型标志
int bfSize; // 文件大小,单位为字节
short bfReserved1; // 保留字节
short bfReserved2; // 保留字节
int bfOffBits; // 数据偏移量,即实际图像数据开始的位置
} Bmp_file_head_t;
// bmp图像信息
typedef struct tagBITMAPINFOHEADER
{
int biSize; // BITMAPINFOHEADER的大小,单位为字节
int biWidth; // 位图的宽度,单位为像素
int biHeight; // 位图的高度,单位为像素
short biPlanes; // 目标设备的位平面数,必须为1
short biBitCount; // 每像素位数(颜色深度)
int biCompression; // 图像压缩类型
int biSizeImage; // 图像大小,单位为字节
int biXPelsPerMeter; // 水平分辨率,单位为像素/米
int biYPelsPerMeter; // 垂直分辨率,单位为像素/米
int biClrUsed; // 实际使用颜色数
int biClrImportant; // 重要颜色数
} Bmp_info_t;
#pragma pack()
extern int init_fb(char *devname);
extern int uninit_fb();
extern void draw_point(int x, int y, unsigned int col);
extern int DrawXLine(int x, int y, int len, unsigned int col);
extern int DrawYLine(int x, int y, int len, unsigned int col);
extern int DrawRect(int x, int y, int high, int wide, unsigned int col);
extern int DrawCircle(int x, int y, int r, unsigned int col);
extern void draw_bmp(int x, int y, char *bmpname);
extern void draw_word(int x, int y, unsigned char *pword, int w, int h,
unsigned int col);
#endif
配置framebuffer
#include "framebuff.h"
#include <fcntl.h>
#include <linux/fb.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
void *pmem = NULL;
int fb;
struct fb_var_screeninfo vinfo;
int init_fb(char *devname)
{
// 1. 打开显示设备(/dev/fb0)
fb = open(devname, O_RDWR);
if (-1 == fb)
{
perror("open fb error");
return -1;
}
// 2. 获取显示设备相关参数(分辨率,像素格式)
int ret = ioctl(fb, FBIOGET_VSCREENINFO, &vinfo);
if (ret < 0)
{
perror("ioctl error");
return -1;
}
printf("xres = %d, yres = %d\n", vinfo.xres, vinfo.yres);
printf("xres_virtual = %d, yres_virtual = %d\n", vinfo.xres_virtual,
vinfo.yres_virtual);
printf("bits_per_pixel = %d\n", vinfo.bits_per_pixel);
// 3. 建立显存空间和用户空间的内存映射
size_t len =
vinfo.xres_virtual * vinfo.yres_virtual * vinfo.bits_per_pixel / 8;
pmem = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fb, 0);
if (pmem == MAP_FAILED)
{
perror("mmap error");
return -1;
}
return 0;
}
int uninit_fb()
{
// 5. 解除映射关系
// 6. 关闭显示设备
size_t len =
vinfo.xres_virtual * vinfo.yres_virtual * vinfo.bits_per_pixel / 8;
munmap(pmem, len);
close(fb);
}
绘制点
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;
}
}
绘制横线
int DrawXLine(int x, int y, int len, unsigned int col)
{
int i = 0;
for (i = 0; i < len; i++)
{
draw_point(x + i, y, col);
}
return 0;
}
绘制竖线
int DrawYLine(int x, int y, int len, unsigned int col)
{
int i = 0;
for (i = 0; i < len; i++)
{
draw_point(x, y + i, col);
}
return 0;
}
绘制矩形框
int DrawRect(int x, int y, int high, int wide, unsigned int col)
{
DrawXLine(x, y, wide, col);
DrawYLine(x, y, high, col);
DrawYLine(x + wide, y, high, col);
DrawXLine(x, y + high, wide, col);
return 0;
}
绘制圆
int DrawCircle(int x, int y, int r, unsigned int col)
{
int i, a, b;
for (i = 0; i < 360; ++i)
{
a = x + r * cos(i / 180.0 * M_PI);
b = y + r * sin(i / 180.0 * M_PI);
draw_point(a, b, col);
}
}
绘制图片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.txt.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);
}
绘制文字(需要取模软件)
void draw_word(int x, int y, unsigned char *pword, int w, int h,
unsigned int col)
{
for (int j = 0; j < h; j++)
{
for (int i = 0; i < w; i++)
{
unsigned char tmp = pword[i + j * w];
for (int k = 0; k < 8; k++)
{
if (tmp & 0x80)
{
draw_point(x + i * 8 + k, y + j, col);
}
else
{
}
tmp = tmp << 1;
}
}
}
}
主函数调用
#include <stdio.h>
#include "framebuff.h"
/*
1. 打开显示设备(/dev/fb0)
2. 获取显示设备相关参数(分辨率,像素格式)
3. 建立显存空间和用户空间的内存映射
4. 向映射的用户空间写入RGB颜色值
5. 解除映射关系
6. 关闭显示设备
*/
unsigned char p[4 * 33] = {
/*-- 文字: 辉 --*/
/*-- 仿宋24; 此字体下对应的点阵为:宽x高=32x33 --*/
/*-- 文字: 辉 --*/
/*-- 仿宋24; 此字体下对应的点阵为:宽x高=32x33 --*/
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xC0,
0x00, 0x00, 0x00, 0xE0, 0x00, 0x18, 0x00, 0xE1, 0x80, 0x3C,
0x00, 0xCD, 0xFF, 0xFC, 0x00, 0xCD, 0xFF, 0xF8, 0x1C, 0xCF,
0x98, 0x70, 0x0E, 0xDF, 0x9C, 0x60, 0x0E, 0xDB, 0x9C, 0x00,
0x07, 0xF8, 0x19, 0xE0, 0x06, 0xF1, 0xFF, 0xF0, 0x00, 0xC1,
0xFC, 0x00, 0x00, 0xFF, 0x7C, 0x00, 0x3F, 0xFF, 0x7E, 0x00,
0x3F, 0xF0, 0xEE, 0x00, 0x03, 0xF0, 0xEF, 0xE0, 0x03, 0xF1,
0xFF, 0xF0, 0x03, 0xF1, 0xFE, 0x00, 0x03, 0x70, 0x8E, 0x00,
0x07, 0x70, 0x0E, 0x00, 0x07, 0x76, 0x1F, 0xFC, 0x07, 0x7F,
0xFF, 0xFC, 0x06, 0x79, 0xCE, 0x00, 0x0E, 0x78, 0x0E, 0x00,
0x0C, 0x70, 0x0E, 0x00, 0x1C, 0x20, 0x0E, 0x00, 0x18, 0x00,
0x0E, 0x00, 0x30, 0x00, 0x0E, 0x00, 0x20, 0x00, 0x0E, 0x00,
0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00
};
int main(void)
{
int ret = init_fb("/dev/fb0");
if (ret < 0)
{
return -1;
}
// 4. 向映射的用户空间写入RGB颜色值
draw_point(400, 300, 0x00FF0000);
// DrawXLine(200, 200, 400, 0x00ff0000);
// DrawYLine(200, 200, 400, 0x00ff0000);
// DrawRect(0, 0, 400, 400, 0x00FF0000);
// DrawCircle(200, 200, 200, 0x00FF0000);
draw_bmp(0, 0, "./1.txt.bmp");
draw_word(400, 300, p, 4, 33, 0x00FFff00);
uninit_fb();
return 0;
}
补充:
perror函数可打印系统给出的错误信息