文章目录
- 前言
- 一、framebuffer简介
- 二、framebuffer接口
- 1.framebuffer设备描述信息
- 2.framebuffer访问接口
- 3.查询/设置可更改信息
- 三、使用步骤
前言
前段时间由于笔记本没有保管好,LCD显示屏压碎了。于是,将笔记本电脑拆开查看LCD型号。在淘宝上下单买了块新的,给老伙计重新装上了。又给笔记本重新装了系统,这个过程甚是艰辛。本来是想装WIN10 X64系统的,制作好U盘启动后程序卡在了win10的启动界面。本来以为可能是内存不够,在网上下单又购买了4G内存条给笔记本换上。装好内存条后,笔记本的运行速度确实有所提升。但是,安装win10 仍然是同样的情况。看来是CPU的性能不够,打算升级CPU。在网上查阅了很多关于升级CPU的帖子,最终结论是主板南桥芯片H55限制了CPU只能选择一代CPU。一代CPU都是双核四线程的,对性能提升应该不是很大。基于此,重新安装回win 7 x64旗舰版。装软件的时候发现,win7 跟 XP一样很多软件都不适用了。而且,还有大量的补丁。最终,选择了给它装上ubuntu 22.04。于是有了本文的硬件基础,本文就是在这台x86_64 ubuntu22.04上做的实验。
一、framebuffer简介
framebuffer是对LCD显示屏的硬件抽象,通过对framebuffer的操作可以直接显示到LCD上。framebuffer是一个字符设备,它向上为用户层提供统一的访问接口,向下屏蔽底层各类硬件显示设备的差异,提高framebuffer设备应用程序的兼容性和可移植性。
framebuffer是一个标准的字符设备,主设备号为29。linux系统最大支持32个framebuffer设备,次设备号一般从0开始,依次为0到31。与其它设备一样,设备节点位于/dev目录下面,一般命名为fbx。例如,/dev/fb0。
二、framebuffer接口
对于framebuffer应用程序开发,不需关心底层的具体实现,只需知道具体访问接口、数据结构以及显示设备的一些必要信息,即可开发应用程序。
1.framebuffer设备描述信息
framebuffer设备信息包括两部分,分别是不可更改和可更改信息。设备信息描述数据结构位于“include/uaip/linux/fb.h”中声明。
不可更改信息描述的是显示设备的固有属性、物理内存地址、物理内存大小等等;对于应用层来说,该部分信息可以用来查询显示设备固有属性信息,对于具体应用过程关联性不大。
struct fb_fix_screeninfo{
char id[16]; /* identification string eg “TT Builtin” /
unsigned long smem_start; / Start of frame buffer mem /
/ (physical address) /
__u32 smem_len; / Length of frame buffer mem /
__u32 type; / see FB_TYPE_* /
__u32 type_aux; / Interleave for interleaved Planes /
__u32 visual; / see FB_VISUAL_* /
__u16 xpanstep; / zero if no hardware panning /
__u16 ypanstep; / zero if no hardware panning /
__u16 ywrapstep; / zero if no hardware ywrap /
__u32 line_length; / length of a line in bytes /
unsigned long mmio_start; / Start of Memory Mapped I/O /
/ (physical address) /
__u32 mmio_len; / Length of Memory Mapped I/O /
__u32 accel; / Indicate to driver which /
/ specific chip/card we have /
__u16 capabilities; / see FB_CAP_* /
__u16 reserved[2]; / Reserved for future compatibility */
};
第二部分可更改的信息,涉及显示设备物理分辨率、虚拟分辨率、像素位宽,这部分信息在应用编程需要关注的,以计算显示位置、颜色、映射内存等等。该部分也设计显示设备的固有信息,但应用程序可以通过接口修改。
struct fb_var_screeninfo{
__u32 xres; /* 可视分辨率(物理分辨率) */
__u32 yres;
__u32 xres_virtual; /* 虚拟分辨率 */
__u32 yres_virtual;
__u32 xoffset; /* 虚拟分辨率相对可视分配率偏移值 */
__u32 yoffset;
__u32 bits_per_pixel; /* 像素位宽,每个像素用多少未表示 */
__u32 grayscale; /* 灰度等级,0表示彩色,1表示灰度(黑白屏)*/
/* 缓存RGB位域 */
struct fb_bitfield red; /* bitfield in fb mem if true color, */
struct fb_bitfield green; /* else only length is significant */
struct fb_bitfield blue;
struct fb_bitfield transp; /* transparency */
__u32 nonstd; /* != 0 Non standard pixel format */
__u32 activate; /* see FB_ACTIVATE_* */
__u32 height; /* height of picture in mm */
__u32 width; /* width of picture in mm */
__u32 accel_flags; /* (OBSOLETE) see fb_info.flags */
/* 实际显示屏(LCD)参数,根据LCD手册设置,用户层一般不用更改 */
__u32 pixclock; /* 像素时钟 */
__u32 left_margin; /* 显示设备水平方向前肩,单位:时钟 */
__u32 right_margin; /* 显示设备水平方向后肩,单位:时钟 */
__u32 upper_margin; /* 显示设备垂直方式前肩,单位:行 */
__u32 lower_margin; /* 显示设备垂直方向后肩,单位:行 */
__u32 hsync_len; /* 显示设备水平方向有效区域,单位:时钟 */
__u32 vsync_len; /* 显示设备垂直方向有效区域,单位:行 */
__u32 sync; /* see FB_SYNC_* */
__u32 vmode; /* see FB_VMODE_* */
__u32 rotate; /* angle we rotate counter clockwise */
__u32 colorspace; /* colorspace for FOURCC-based modes */
__u32 reserved[4]; /* Reserved for future compatibility */
}
framebuffer应用程序开发,主要关注的的成员参数是物理分辨率、像素位宽。物理分辨率和像素位宽决定了映射内存的大小,如一个LCD分辨率为1024768、像素位宽为32位;则需要的内存空间大小为:1024768*32/8=3145728字节。
2.framebuffer访问接口
framebuffer设备是一个标准的linux字符设备,可以通过标准虚拟文件接口open/read/write/ioctl/close访问。设备访问分为两部分数据,分别是控制数据流和显示数据流传。对控制数据流通过ioctl结合指定命令实现;对于显示流数据,由于数据量比较大,结合mmap函数,将framebuffer物理内存映射到用户态,直接访问物理内存。
ioctl(fd, cmd, param); /* 文件描述符, 命令字, 参数信息 */
framebuffer设备ioctl命令位于“include/uapi/linux/fb.h”定义。
/* include/uaip/linux/fb.h */
/* ioctls
0x46 is 'F' */
#define FBIOGET_VSCREENINFO 0x4600 /* 查询显示设备可更改信息 */
#define FBIOPUT_VSCREENINFO 0x4601 /* 设置显示设备可更改信息 */
#define FBIOGET_FSCREENINFO 0x4602 /* 查询显示设备不可更改信息 */
#define FBIOGETCMAP 0x4604
#define FBIOPUTCMAP 0x4605
#define FBIOPAN_DISPLAY 0x4606
......
3.查询/设置可更改信息
函数原型:
int ioctl(int fd, int cmd, struct fb_var_screeninfo *var);
cmd,命令字:FBIOGET_VSCREENINFO
var,设备可更改信息数据结构
返回,成功返回0,失败返回-1,错误码存于errno中
获取显示设备可更改信息伪代码:
int fb = 0;
struct fb_var_screeninfo var;
fb = open(“/dev/fb0”, O_RDWR);
if (fb < 0)
{
printf(“open fb device failed:%s\n”,strerror(errno));
return -1;
}
if (ioctl(fb, FBIOGET_VSCREENINFO, &var))
{
printf(“read fb device param failed:%s\n”,strerror(errno));
return -1;
}
查询不可更改信息
函数原型:
int ioctl(int fd, int cmd, struct fb_fix_screeninfo *var);
cmd,命令字:FBIOGET_FSCREENINFO
var,设备不可更改信息数据结构
返回,成功返回0,失败返回-1,错误码存于errno中
获取显示设备不可更改信息伪代码:
int fb = 0;
struct fb_fix_screeninfo fix;
fb = open(“/dev/fb0”, O_RDWR);
if (fb < 0)
{
printf(“open fb device failed:%s\n”,strerror(errno));
return -1;
}
if (ioctl(fb, FBIOGET_FSCREENINFO, &fix))
{
printf(“read fb device param failed:%s\n”,strerror(errno));
return -1;
}
三、使用步骤
应用程序访问一个framebuffer设备的的总体流程如下图。
实例:
获取framebuffer设备信息
“红—绿—蓝” 1秒周期循环刷新输出
##include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/fb.h>
struct _fb_info
{
int fd; /* framebuffer 文件描述符 /
uint8_t pbuf; / 映射内存 /
struct fb_fix_screeninfo fix;
struct fb_var_screeninfo var;/ framebuffer设备信息/
};
struct _fb_info fb_app = {0};
/* 画点函数 */
static void draw_pixel(struct _fb_info *fb, int x, int y, uint32_t color)
{
uint8_t *poffset_buf = NULL;
poffset_buf = fb->pbuf + (x*fb->var.bits_per_pixel/8)
+ (y*fb->var.xres*fb->var.bits_per_pixel/8); /* 计算内存偏移地址 */
*(uint32_t*)poffset_buf = color; /* ARGB32格式 */
}
/* 全屏画点函数 */
static void fill_pixel(struct _fb_info *fb, uint32_t color)
{
int i, j;
for (i=0; i<fb->var.xres; i ++)
{
for (j=0; j<fb->var.yres; j ++)
{
draw_pixel(fb, i, j, color);
}
}
}
int main(int argc, char *argv[])
{
int ret = 0;
int mem_size = 0;
if (argc < 2)
{
printf("parameter invalid\n");
return -1;
}
fb_app.fd = open(argv[1], O_RDWR);
if (fb_app.fd < 0)
{
printf("open device [%s] failed:%s\n", argv[1], strerror(errno));
return -1;
}
printf("framebuffer device:%s\n", argv[1]);
/* 读取不可更改信息 */
ret = ioctl(fb_app.fd, FBIOGET_FSCREENINFO, &fb_app.fix);
if (ret < 0)
{
printf("read fb device fscreeninfo failed:%s\n", strerror(errno));
close(fb_app.fd);
return -1;
}
printf("device id:%s\n", fb_app.fix.id);
printf("smem_start:0x%x, smem_len:%u\n", fb_app.fix.smem_start, fb_app.fix.smem_len);
/* 读取可更改信息 */
ret = ioctl(fb_app.fd, FBIOGET_VSCREENINFO, &fb_app.var);
if (ret < 0)
{
printf("read fb device vscreeninfo failed:%s\n", strerror(errno));
close(fb_app.fd);
return -1;
}
printf("visible resolution:%d*%d\n", fb_app.var.xres, fb_app.var.yres);
printf("virtual resolution:%d*%d\n", fb_app.var.xres_virtual, fb_app.var.yres_virtual);
printf("pixel bits wide:%d\n", fb_app.var.bits_per_pixel);
printf("grayscale:%d\n", fb_app.var.grayscale);
mem_size = fb_app.var.xres * fb_app.var.yres * fb_app.var.bits_per_pixel / 8; /* 计算内存 */
fb_app.pbuf = (uint8_t *)mmap(NULL, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, fb_app.fd, 0);
if (fb_app.pbuf == NULL)
{
printf("fb device mmap failed:%s\n", strerror(errno));
close(fb_app.fd);
return -1;
}
memset(fb_app.pbuf, 0, mem_size); /* 清屏操作(黑屏)*/
for (;;)
{
fill_pixel(&fb_app, 0xffff0000);/* 红 */
sleep(1);
fill_pixel(&fb_app, 0xff00ff00);/* 绿 */
sleep(1);
fill_pixel(&fb_app, 0xff0000ff);/* 蓝 */
sleep(1);
}
munmap(fb_app.pbuf, mem_size);
close(fb_app.fd);
return 0;
}