c设备io

设备io

显卡显示Framebuffer

Framebuffer从字面意思理解帧缓冲,是Linux内核将显存进行抽象后的一块缓存(内存),目的是为了给用户态进程提供一套直接写屏的接口。不用关心物理显存的位置、换页机制等具体细节。

可以将它想象成一个二维平面,由一个个像素组成,它的分辨率宽度和高度的乘积就是整个Framebuffer的像素点,例如:分辨率为800*600的Framebuffer,就表示这块显存有600行,每行有800个像素点。

Framebuffer通常用作设备的LCD屏幕控制器或者其他设备的显示驱动,Framebuffer是一个字符设备。

该设备对应的驱动节点/dev/fb0,主设备号为29。

Framebuffer就是用户可以操作的显示内存的映像,操作时需要将其映射到一个内存进程地址之后,可以进行读写。

从驱动角度来看,fb是一个典型的字符设备文件,对应的物理接口:/sys/class/graphics

Framebuffer设备结构体

每个系统中帧缓冲设备都是一个对应的结构体和宏定义描述,对应的头文件:/usr/include/linux/fb.h

可以使用命令查看以下头文件
cat /usr/include/linux/fb.h

关于屏幕显示的结构体,主要内容如下:

c 复制代码
//后面如果需要对屏幕信息进行读写控制,需要这个指令,获取到屏幕信息
#define FBIOGET_VSCREENINFO	0x4600
//获取屏幕信息对应的结构体
struct fb_var_screeninfo {
	__u32 xres;//屏幕的宽度			
	__u32 yres;//屏幕的高度
	__u32 xres_virtual;//虚拟设备的宽度		
	__u32 yres_virtual;//虚拟设备的高度
	__u32 xoffset;//横向偏移的位数,字节			
	__u32 yoffset;//纵向偏移的位数,字节				

	__u32 bits_per_pixel;//像素显示的位数		
	__u32 grayscale;		
	struct fb_bitfield red;//红色描述结构体		
	struct fb_bitfield green;//绿色描述结构体
	struct fb_bitfield blue;//蓝色描述结构体
	struct fb_bitfield transp;//透明度描述结构体		
}

//颜色描述的结构体
struct fb_bitfield {
	__u32 offset;//颜色位数显示起点	
	__u32 length;//位域宽度			
	__u32 msb_right;		
};

ioctl函数:

c 复制代码
函数功能:通过设备文件描述符和一个控制命令对设备进行控制
函数原型:
int ioctl(int fd,unsigned long request,...);
函数参数:
int fd:是一个用于表示设备的文件描述符
unsigned long request:是一个表示控制命令的整数值,用来指定对设备的具体控制操作
...:可变参数,用于传递与控制命令相关的参数。

我们通常将Framerbuffer的操作步骤归纳如下:

  • 1.打开设备文件 open /dev/fb0
  • 2.获取设备信息,ioctl函数,通过FBIOGET_VSCREENINFO
  • 3.映射显示内存,mmap映射lcd屏幕
  • 4.操作屏幕(准备颜色)
  • 5.关闭设备,关闭映射。

映射显示内存

c 复制代码
#include <sys/mman.h>
//映射内存给文件或者设备
void *mmap(void *addr,size_t length,int prot,int flags,int fd,off_t offset);
函数参数:
void *addr:映射内存的首地址,一般设置为NULL,由系统分配地址
size_t length:映射内存的大小,有屏幕的宽度和高度和像素位数决定 字节
int prot:映射权限 PROT_READ|PORT_WRITE 设置读写权限
int flags:映射的共享权限 MAP_SHARED共享 MAP_PRIVATE 私有
int fd:设备的文件描述符 open函数的返回值,告诉函数,是为哪个设备映射内存
off_t offset:文件偏移量,一般设置为0
函数返回值:
成功:返回映射内存的首地址
失败:返回MAP_FAILED 本质(void *)-1

结束映射内存

c 复制代码
int munmap(void *addr,size_t length);
函数参数:
void *addr:映射内存的首地址
size_t length:取消映射内存的大小
返回值:
成功:返回0
失败:返回-1

操作屏幕颜色

c 复制代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/fb.h>
#include <sys/ioctl.h>
#include <sys/mman.h>

//驱动节点
#define LCD "/dev/fb0"

//mmap函数返回值,返回成功映射内存的首地址
typedef unsigned int * lcd;//映射内存地址类型别名lcd

int lcdfd;//设备的文件描述符
int size;//保存映射空间的大小

lcd open_lcd()
{
	//打开设备
	lcdfd=open(LCD,O_RDWR);
	if(lcdfd==-1)
	{
		perror("打开失败!");
		return NULL;
	}

	//通过ioctl获取设备属性
	struct  fb_var_screeninfo info;
	int ret=ioctl(lcdfd,FBIOGET_VSCREENINFO,&info);
	if(ret==-1)
	{
		perror("设备信息加载失败!");
		return NULL;
	}

	//每个像素bits_per_pixel占32位,也就是每个像素点32/8=4个字节
	//整个屏幕一共有:宽度*高度个像素点
	size=info.xres_virtual*info.yres_virtual*(info.bits_per_pixel/8);

	//映射内存
	lcd FB=(lcd)mmap(NULL,size,PROT_READ|PROT_WRITE,MAP_SHARED,lcdfd,0);
	if(FB==MAP_FAILED)
	{
		perror("映射内存失败!");
		return NULL;
	}
	return FB;
}

int close_lcd(lcd FB)
{
	//关闭映射
	int ret=munmap(FB,size);
	if(ret==-1)
	{
		perror("结束映射内存失败!");
		return -1;
	}

	//关闭文件
	ret=close(lcdfd);
	if(ret==-1)
	{
		perror("文件关闭失败!");
		return -1;
	}
	return 0;
}

int main()
{
	//打开设备
	lcd FB=open_lcd();

	//准备颜色
	unsigned int red=0x00FF0000;
	unsigned int green=0x0000FF00;
	unsigned int blue=0x000000FF;

	for (int i = 0; i < 1176*885; ++i)
	{
		FB[i]=green;
	}

	//关闭设备
	close_lcd(FB);

	//测试时:将Linux界面切换到字符界面:ctrl+alt+F2
	//将字符界面切成图形界面:ctrl+alt+F7
	return 0;
}

演示效果,我们需要将Linux切换到字符界面,切换方式:

1.切换到字符界面:ctrl+alt+F2

2.切换到图形界面:ctrl+alt+F7

vm安装模拟器

1.在Linux用户根目录下,创建lcd目录【mkdir】,准备拷贝vslcd

mkdir lcd;

2.将共享文件夹中的vslcd目录拷贝到lcd目录中(注意先将压缩包解压,然后拷贝解压后的文件夹)

cp vslcd lcd;//复制

3.进入到vslcd目录中,获取里面的设备驱动文件,进行编译安装

进行权限设置
sudo su

4.进入mmap_drv(内存映射目录),提供lcd相关的模拟方式,通过make进行编译

make

5.编译完成之后,安装驱动文件insmod mmp_drv.ko

6.进入到event_drv目录中(该目录为输入子系统目录,提供触摸点击相关的模拟方式,通过make编译)

7.编译完成之后,安装驱动文件insmod event_drv.ko

8.如果上述操作没有问题,运行虚拟的lcd显示器。

c 复制代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/fb.h>
#include <sys/ioctl.h>
#include <sys/mman.h>

//驱动节点
#define LCD "/dev/ubuntu_lcd"

#define SIZE 800*480*4

typedef unsigned int * lcd;//映射内存的首地址

int lcdfd;//设备的文件描述符

//打开设备,并返回映射内存的首地址
lcd open_lcd()
{
	//打开显示设备
	lcdfd=open(LCD,O_RDWR);
	if(lcdfd==-1)
	{
		perror("驱动打开失败!");
		return NULL;
	}

	//映射内存
	lcd FB=(lcd)mmap(NULL,SIZE,PROT_READ|PROT_WRITE,MAP_SHARED,lcdfd,0);
	if(FB==MAP_FAILED)
	{
		perror("映射内存失败!");
		return NULL;
	}
	return FB;
}

//关闭设备
int close_lcd(lcd FB)
{
	//关闭映射
	int ret=munmap(FB,SIZE);
	if(ret==-1)
	{
		perror("结束映射内存失败!");
		return -1;
	}

	ret=close(lcdfd);
	if(ret==-1)
	{
		perror("文件关闭失败!");
		return -1;
	}
	return 0;
}

void heng(lcd FB)
{
	//准备颜色
	unsigned int red=0x00FF0000;
	unsigned int black=0x00000000;
	unsigned int yellow=0x00FFFF00;

	//内存映射颜色写入
	int i=0;
	for (; i < 800*160; ++i)
	{
		FB[i]=black;
	}
	for (; i < 800*320; ++i)
	{
		FB[i]=red;
	}
	for (; i < 800*480; ++i)
	{
		FB[i]=yellow;
	}

}

int main()
{
	lcd FB=open_lcd();

	heng(FB);

	close_lcd(FB);
	return 0;
}
相关推荐
磁悬浮青蛙呱呱呱4 分钟前
C语言自编以e为底的对数函数ln,性能接近标准库函数
c语言·初等函数·fmadd指令加速
Bubluu6 分钟前
vue解决跨域问题
前端·javascript·vue.js
xujiaqi_china22 分钟前
C语言第11节:指针(1)
c语言
retun_true26 分钟前
JavaScript缓存之Service Worker && workbox
前端·javascript·vue.js
鲤鱼_59934 分钟前
记录————封装vue3+element-plus(el-upload)上传图片组件
前端·javascript·vue.js
萧鼎1 小时前
【Python】强大的正则表达式工具:re模块详解与应用
开发语言·python·正则表达式
yuchangchenTT1 小时前
就是这个样的粗爆,手搓一个计算器:JSON格式化计算器
前端·json·365快速计算器·在线计算器
乐闻x1 小时前
Vue Router 详细使用步骤:如何在 Vue 项目中配置 Vue Router
前端·javascript·vue.js
聚宝盆_1 小时前
【css flex 多行均分有间隙布局】
前端·css
弗拉唐1 小时前
SSM中maven
java·前端·maven