目录
前言:
1.基本操作
2.使用ioctl函数去获取设备信息
3.操作LCD屏幕的效率问题
Linux Frame Buffer 其指的是 Linux 底层的帧缓冲设备,可以显示一帧一帧的图像(屏幕的显示)。因为linux中的任何东西都是以文件方式进行存储的,所以我们可以通过读写这个文件来对屏幕显示进行操作。
帧缓冲是Linux系统为显示设备提供的一个接口,把显示设备抽象成一个设备文件,它可以让上层的图像应用程序不需要关心具体的硬件的实现细节,上层的图像应用程序只需要操作对应的"文件",往文件中写入"数据",经过帧缓冲驱动,就可以在硬件设备(LCD)上面显示对应的图像
如何让LCD设备显示颜色呢?
Linux中一切都是文件,只需要利用文件IO的接口去操作LCD设备就可以了
操作文件的大概步骤:
open //打开文件获得文件描述符
read/write //对文件进行读写操作
close //关闭文件
操作文件的时候需要知道文件名:
lcd的文件名 "/dev/fb0" 是一个绝对路径(Frame Buffer)
Lcd上图像的显示,是由一个一个的像素点组成的
像素点:可以描述一个点的颜色
将LCD上面每一个像素点"描绘"成不同的颜色,就可以显示一副图画
颜色的组成:RGB三原色
每一个基色使用一个byte(8bits)表示,给基色中的每一个bit赋值为不同的值,就可以表示不同的颜色
问题:
一个基色可以表示多少种颜色? 0~255 ------>256
8个bit的值不同,就可以表示不同的颜色
三个基色可以表示多少种颜色? 2^24 ------>1670万种
24个bit的值不同,就可以表示不同的颜色
LCD上面一个像素点的表示: ARGB A:透明度(1byte),R:红色,G:绿色,B:蓝色
在LCD上面一个像素点使用几个字节表示? 4byte
使用4byte就可以表示一个像素点的颜色
如果使用一种数据类型来表示一个像素点的颜色的话,可以使用什么数据类型?
int 刚好4个字节
int color; //color就是一个int类型的变量,占用4byte
给color赋值为不同的值,color就表示不同的颜色
红色:0x00FF0000
绿色:0x0000FF00
蓝色:0x000000FF
黑色:0x0
白色:0x00FFFFFF
所以只需要把颜色数据写入到屏幕对应的文件中,就可以让屏幕显示对应的颜色
而像素点在"文件"中的排列顺序是从上至下,从左至右的。
理论上就可以通过文件去操作屏幕上面每一个像素点的颜色。
假设开发板屏幕上有像素点:800*480个,则每一行有800个像素点,有480行
于是我们就可以写一个简单的代码,让开发板显示对应的颜色
//打开屏幕
//写入颜色数据
//关闭屏幕
注意:
有的开发板上面开机就会自动运行一个iot的程序
关闭方法:
killall iot
or
设置 /etc/profile 文件
并且要注意文件光标的位置!!!!!
例子如下:
#include
#include
#include
#include
#include
int main()
{
//打开屏幕
int fd = open("/dev/fb0", O_RDWR);
if(-1 == fd)
{
perror("open lcd error");
return -1;
}
printf("open success!\n");
//写入颜色数据
int color[480][800] = {0};
int i,j;
for(i=0;i<480;i++)
{
for(j=0;j<800;j++)
{
color[i][j] = 0x0000FF00;
}
}
write(fd,color,800*480*4);
sleep(1);
lseek(fd,0,SEEK_SET);
for(i=0;i<480;i++)
{
for(j=0;j<800;j++)
{
color[i][j] = 0x0000FFFF;
}
}
write(fd,color,800*480*4);
//关闭屏幕
close(fd);
return 0;
}
ioctl函数是用来对文件/设备进行除了读写以外的其他控制操作,每一个设备的控制操作都是不一样的,这些设备具体有哪些操作是由设备的驱动程序决定的.
NAME
ioctl - control device
控制设备(具体的操作由驱动程序决定)
此处我们可以使用函数获取LCD屏幕的一些基本信息
SYNOPSIS
#include
int ioctl(int fd, unsigned long request, ...);
fd:文件描述符,表示你要操作哪一个设备
request:命令号码,在驱动实现的时候,一般会把某些特定的操作取一个命令号,命令号的具体含义是由提供该命令号的驱动程序决定
...:可变参数,具体的操作由命令号决定
返回值:
成功返回0
失败返回-1,同时errno被设置
例子:
如果程序需要知道帧缓冲设备的相关信息,可以使用ioctl来完成
对于帧缓冲设备,最常用的有两条命令(/usr/include/linux/fb.h):
#define FBIOGET_VSCREENINFO 0x4600
返回和设备相关的可变信息
如:帧缓冲设备的大小(宽度,高度),以及颜色显示等信息
#define FBIOGET_FSCREENINFO 0x4602
返回和设备相关的固定信息
如:设备本身的一些信息,硬件加速度等信息
而对应的结构体是:
struct fb_var_screeninfo{}
和
struct fb_fix_screeninfo{}
使用方法:
//定义一个结构体用来保存即将获取到的数据
struct fb_var_screeninfo vinfo;
//把获取到的信息保存到结构体中
ioctl(fd,FBIOGET_VSCREENINFO,&vinfo);
结构体内容如下
struct fb_var_screeninfo
{
__u32 xres; /* 可视分辨率 */
__u32 yres;
__u32 xres_virtual; /* virtual resolution */
__u32 yres_virtual;
__u32 xoffset; /* offset from virtual to visible */
__u32 yoffset; /* resolution */
__u32 bits_per_pixel; /* 每一个像素点占用的bit数量*/
__u32 grayscale; /* 0 = color, 1 = grayscale, */
/* >1 = FOURCC */
struct fb_bitfield red; Red
struct fb_bitfield green; Green
struct fb_bitfield blue; Blue
struct fb_bitfield transp; /* transparency */
}
struct fb_bitfield {
__u32 offset; /* beginning of bitfield */
__u32 length; /* length of bitfield */
__u32 msb_right; /* != 0 : Most significant bit is right */
};
每一次操作屏幕的图像,都需要使用write函数
但是write函数会直接去访问硬件资源,占用总线,效率是非常低的。于是我们可以使用内存映射,把屏幕文件映射到内存中(操作内存就相当于操作屏幕)
映射:把文件和一段内存一一对应(建立一个联系),操作文件的时候就不需要使用write函数,只需要使用指针操作对应的内存就可以了(C语言中指针可以直接操作内存),内存内容的改变会由具体的映射驱动同步到文件中去
在LCD中,如果把屏幕映射到内存中,就不需要使用write函数了,只需要去操作映射之后的内存即可
NAME
mmap, munmap - map or unmap files or devices into memory
SYNOPSIS
#include
mmap是把指定的文件映射到进程地址空间(堆和栈中间)
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
注释:
addr:表示映射地址,你要把文件映射到内存的哪一个位置
一般为NULL,表示让OS自行选择一个合适的地址
length:表示你要映射的长度(字节),你的文件多大
prot:应用程序对映射之后的内存的操作权限
PROT_READ 可读
PROT_WRITE 可写
PROT_READ | PROT_WRITE
flags:映射标记
MAP_SHARED:共享映射,内存的改变会同时同步到文件
MAP_PRIVATE:私有映射,内存数据的改变对其他程序是不可见的
fd:你要映射的文件的文件描述符
offset:偏移量,表示你要从文件的哪一个位置开始映射,一般为0,表示从文件的开头开始映射
返回值:
成功返回映射之后的内存的首地址(相当于文件的开头)
失败返回MAP_FAILED,errno被设置
munmap是解除内存的映射关系
int munmap(void *addr, size_t length);
addr:你要接映射的首地址,是mmap的返回值
length:你要接映射的长度
流程:
打开文件
映射文件到内存
==============
操作内存就是操作文件
==============
解除映射
关闭文件
int *plcd = (int *)mmap(NULL, 800*480*4, PROT_READ | PROT_WRITE, MAP_SHARED,fd,0);
if(plcd == MAP_FAILED)
{
perror("mmap failed");
close(fd);
return -1;
}
假设映射成功后,映射后的像素点在内存中的对应关系是从左至右,从上至下
也就是说,plcd指向的位置对应屏幕上面第0行的第0个点
plcd+1对应屏幕上面第0行的第1个点(指针做偏移,是偏移单位个指向类型的长度)
如果想要通过plcd把屏幕的第0行的第0个点设置为红色,应该如何操作?
把0x00ff0000这个数据放到第0行的第0个点对应的内存地址
第0行的第0个点对应的内存地址就是plcd
*plcd = 0x00FF0000
如果想要通过plcd把屏幕的第0行的第1个点设置为红色,应该如何操作?
把0x00ff0000这个数据放到第0行的第1个点对应的内存地址
第0行的第1个点对应的内存地址就是(plcd+1)
*(plcd+1) = 0x00FF0000
如果想要通过plcd把屏幕的第0行的第x个点设置为红色,应该如何操作?
把0x00ff0000这个数据放到第0行的第x个点对应的内存地址
第0行的第x个点对应的内存地址就是(plcd+x)
*(plcd+x) = 0x00FF0000
如果想要通过plcd把屏幕的第y行的第x个点设置为红色,应该如何操作?
把0x00ff0000这个数据放到第y行的第x个点对应的内存地址
第y行的第x个点对应的内存地址就是(plcd+800*y+x)
*(plcd+800*y+x) = 0x00FF0000
我们可以通过plcd去操作屏幕上面的每一个像素点
注意:因为内存是连续的,plcd向后只能偏移800*480*4个字节有权限使用