Framebuffer学习

news2024/11/23 19:52:19

目录

    • 1. Framebuffer概念
    • 2. LCD操作原理
    • 3. 源码分析
      • 3.1 打开设备
      • 3.2 获取LCD参数
      • 3.3 映射Framebuffer
      • 3.4 描点实现

基于韦东山IMX6ULL开发板学习

参考教程:

韦东山老师教程

1. Framebuffer概念

Framebuffer,可以译作“帧缓冲”,有时简称为fbdrv,是一种在Linux系统中广泛使用的图形设备接口。

Framebuffer是一个视频输出设备,它从包含完整帧数据的一个内存缓冲区中驱动视频显示设备。简而言之,它是Linux内核为显示设备提供的一个接口,将显存抽象化后,允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。这种机制屏蔽了图像硬件的底层差异,使得上层应用不必关心物理显存的具体位置和存放方式。

Framebuffer 通常由以下几个部分组成:

  • Framebuffer 设备文件:在 Linux 系统中,Framebuffer 设备通常以 /dev/fb0/dev/fb1 等形式存在。应用程序可以通过这些设备文件来访问和控制 framebuffer。
  • Framebuffer 内存:Framebuffer 内存是一块连续的物理内存区域,用于存储像素数据。每个像素在内存中占用一定的位数(例如 8 位、16 位、24 位或 32 位),具体取决于显示模式。
  • 显示控制器:负责将 framebuffer 中的数据传输到显示器上。显示控制器可以是硬件的一部分,也可以是软件模拟的。

2. LCD操作原理

在Linux系统中通过Framebuffer驱动程序来控制LCD。Frame是帧的意思,buffer是缓冲的意思,这意味着Framebuffer就是一块内存,里面保存着一帧图像。Framebuffer中保存着一帧图像的每一个像素颜色值,假设LCD的分辨率是1024x768,每一个像素的颜色用32位来表示,那么Framebuffer的大小就是:1024x768x32/8=3145728字节。

LCD操作原理:

  • 驱动程序设置好 LCD 控制器:
    • 根据LCD的参数设置LCD控制器的时序、信号极性;
    • 根据LCD分辨率、BPP分配Framebuffer。
  • APP使用ioctl获得LCD分辨率、BPP。
  • APP通过mmap映射Framebuffer,在Framebuffer中写入数据。

在这里插入图片描述

假设需要设置LCD中坐标(x,y)处像素的颜色,首要要找到这个像素对应的内存,然后根据它的BPP值设置颜色。假设fb_base是APP执行mmap后得到的Framebuffer地址:

在这里插入图片描述

可以用以下公式算出(x,y)坐标处像素对应的Framebuffer地址:

(x,y)像素起始地址=fb_base+(xres*bpp/8)*y+x*bpp/8

颜色是用RGB三原色(红、绿、蓝)来表示的,在不同的BPP格式中,用不同的位来分别表示R、G、B,一般有3中表示颜色的方式:

在这里插入图片描述

  • 对于32BPP,一般只设置其中的低24位,高8位表示透明度,一般的LCD都不支持。
  • 对于24BPP,硬件上为了方便处理,在Framebuffer中也是用32位来表示,效果跟32BPP是一样的。
  • 对于16BPP,常用的是RGB565;很少的场合会用到RGB555,这可以通过ioctl读取驱动程序中的RGB位偏移来确定使用哪一种格式。

3. 源码分析

在IM6ULL开发板上操作LCD像素点的源码:

#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>

static int fd_fb;
static struct fb_var_screeninfo var;	/* Current var */
static int screen_size;
static unsigned char *fb_base;
static unsigned int line_width;
static unsigned int pixel_width;

/**********************************************************************
 * 函数名称: lcd_put_pixel
 * 功能描述: 在LCD指定位置上输出指定颜色(描点)
 * 输入参数: x坐标,y坐标,颜色
 * 输出参数: 无
 * 返 回 值: 会
 * 修改日期        版本号     修改人	      修改内容
 * -----------------------------------------------
 * 2020/05/12	     V1.0	  zh(angenao)	      创建
 ***********************************************************************/ 
void lcd_put_pixel(int x, int y, unsigned int color)
{
	unsigned char *pen_8 = fb_base+y*line_width+x*pixel_width;
	unsigned short *pen_16;	
	unsigned int *pen_32;	

	unsigned int red, green, blue;	

	pen_16 = (unsigned short *)pen_8;
	pen_32 = (unsigned int *)pen_8;

	switch (var.bits_per_pixel)
	{
		case 8:
		{
			*pen_8 = color;
			break;
		}
		case 16:
		{
			/* 565 */
			red   = (color >> 16) & 0xff;
			green = (color >> 8) & 0xff;
			blue  = (color >> 0) & 0xff;
			color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
			*pen_16 = color;
			break;
		}
		case 32:
		{
			*pen_32 = color;
			break;
		}
		default:
		{
			printf("can't surport %dbpp\n", var.bits_per_pixel);
			break;
		}
	}
}

int main(int argc, char **argv)
{
	int i;
	
	fd_fb = open("/dev/fb0", O_RDWR);
	if (fd_fb < 0)
	{
		printf("can't open /dev/fb0\n");
		return -1;
	}
	if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))
	{
		printf("can't get var\n");
		return -1;
	}

	line_width  = var.xres * var.bits_per_pixel / 8;
	pixel_width = var.bits_per_pixel / 8;
	screen_size = var.xres * var.yres * var.bits_per_pixel / 8;
	fb_base = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
	if (fb_base == (unsigned char *)-1)
	{
		printf("can't mmap\n");
		return -1;
	}

	/* 清屏: 全部设为白色 */
	memset(fb_base, 0xff, screen_size);

	/* 随便设置出100个为红色 */
	for (i = 0; i < 100; i++)
		lcd_put_pixel(var.xres/2+i, var.yres/2, 0xFF0000);
	
	munmap(fb_base , screen_size);
	close(fd_fb);
	
	return 0;	
}

操作LCD的步骤大致分为:

  • 打开设备
  • 获取LCD参数
  • 映射Framebuffer
  • 描点

3.1 打开设备

在 Linux 系统中,Framebuffer 设备通常以 /dev/fb0/dev/fb1 等形式存在。因此打开/dev/fb0设备:

73 fd_fb = open("/dev/fb0", O_RDWR);
74 if (fd_fb < 0)
75 {
76 		printf("can't open /dev/fb0\n");
77 		return -1;
78 }

open函数

在这里插入图片描述

函数原型:

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

参数说明:

  • pathname:表示打开文件的路径
  • flags:表示打开文件的方式,常用的有以下6种方式:
    • O_RDWR表示可读可写方式打开;
    • O_RDONLY表示只读方式打开;
    • O_WRONLY表示只写方式打开;
    • O_APPEND表示如果这个文件中本来是有内容的,则新写入的内容会接续到原来内容的后面;
    • O_TRUNC表示如果这个文件中本来是有内容的,则原来的内容会被丢弃,截断;
    • O_CREAT表示当前打开文件不存在,我们创建它并打开它,通常与O_EXCL结合使用,当没有文件时创建文件,有这个文件时会报错提醒我们;
  • mode:表示创建文件的权限,只有在flags使用了O_CREAT时才有效,否则忽略。
  • 返回值:打开成功返回文件描述符,失败返回-1。

3.2 获取LCD参数

参考fb.h文件中,Framebuffer主要定义了2类参数:可变的参数fb_var_screeninfo、固定的参数fb_fix_screeninfo。编写应用程序时主要关心可变参数,可以用FBIOGET_VSCREENINFO来读出这些信息,同样也可以使用FBIOPUT_VSCREENINFO来设置或调整这些参数,一般很少会设置或调整:

在这里插入图片描述

对于固定的参数fb_fix_screeninfo,在应用编程中很少用到,可以使用ioctlFBIOGET_FSCREENINFO来读出这些信息:

在这里插入图片描述

获取fb_var_screeninfo信息:

12 static struct fb_var_screeninfo var; /* Current var */
……
79 if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))
80 {
81 		printf("can't get var\n");
82 		return -1;
83 }

将获取到的fb_var_screeninfo信息保存到var变量中:

在这里插入图片描述

ioctl函数

在这里插入图片描述

函数原型:

int ioctl(int fd, unsigned long request, ...);

参数说明:

  • fd:表示文件描述符;
  • request:表示与驱动程序交互的命令,用不同的命令控制驱动程序输出我们需要的数据;
  • …:表示可变参数arg,根据request命令,设备驱动程序返回输出的数据。
  • 返回值:打开成功返回文件描述符,失败返回-1。

ioctl的作用非常强大、灵活。不同的驱动程序内部会实现不同的ioctl,APP可以使用各种ioctl跟驱动程序交互:可以传数据给驱动程序,也可以从驱动程序中读出数据。

3.3 映射Framebuffer

要映射一块内存,需要知道它的地址──这由驱动程序来设置,需要知道它的大小──这由应用程序决定:

85 line_width = var.xres * var.bits_per_pixel / 8; /* 一行有多少个字节(一行宽度) */
86 pixel_width = var.bits_per_pixel / 8; /* 一个像素点占多少个字节(一个像素点宽度) */
87 screen_size = var.xres * var.yres * var.bits_per_pixel / 8; /* 整个屏幕占多少字节 */
88 fb_base = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
89 if (fb_base == (unsigned char *)-1)
90 {
91 		printf("can't mmap\n");
92 		return -1;
93 }

mmap函数

在这里插入图片描述

函数原型:

void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);

参数说明:

  • addr:表示指定映射的內存起始地址,通常设为NULL表示让系统自动选定地址,并在成功映射后返回该地址;
  • length:表示将文件中多大的内容映射到内存中;
  • prot:表示映射区域的保护方式,可以为以下4种方式的组合:
    • PROT_EXEC映射区域可被执行
    • PROT_READ映射区域可被读出
    • PROT_WRITE映射区域可被写入
    • PROT_NONE映射区域不能存取
  • flags:表示影响映射区域的不同特性,常用的有以下两种:
    • MAP_SHARED表示对映射区域写入的数据会复制回文件内,原来的文件会改变。
    • MAP_PRIVATE表示对映射区域的操作会产生一个映射文件的复制,对此区域的任何修改都不会写回原来的文件内容中。
  • offset:文件或设备中的偏移量,映射区域将从该偏移量开始。偏移量必须是页面大小的整数倍。
  • 返回值:若成功映射,将返回指向映射的区域的指针,失败将返回-1。

3.4 描点实现

在LCD上描绘指定像素后,就可以写字、画图,描点函数是基础:

28 void lcd_put_pixel(int x, int y, unsigned int color) /* 传入的color表示颜色,它的格式永远是0x00RRGGBB,即RGB888。当LCD是16bpp时,要把color变量中的R、G、B抽出来再合并成RGB565格式。 */
29 {
30 		unsigned char *pen_8 = fb_base+y*line_width+x*pixel_width; /* 计算(x,y)坐标上像素对应的Framebuffer地址。 */
31 		unsigned short *pen_16;
32 		unsigned int *pen_32;
33		
34 		unsigned int red, green, blue;
35		
36 		pen_16 = (unsigned short *)pen_8;
37 		pen_32 = (unsigned int *)pen_8;
38		
39 		switch (var.bits_per_pixel)
40 		{
41 			case 8:
42 			{
43 				*pen_8 = color; /* 对于8bpp,color就不再表示RBG三原色了,这涉及调色板的概念,color是调色板的值。 */
44 				break;
45 			}
46 			case 16:
47 			{
48 				/* 565 */
49 				red = (color >> 16) & 0xff; /* 先从color变量中把R、G、B抽出来。 */
50 				green = (color >> 8) & 0xff;
51 				blue = (color >> 0) & 0xff;
52 				color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3); /* 把red、green、blue这三种8位颜色值,根据RGB565的格式,只保留red中的高5位、green中的高6位、blue中的高5位,组合成一个新的16位颜色值。 */
53 				*pen_16 = color; /* 把新的16位颜色值写入Framebuffer。 */
54 				break;
55 			}
56 			case 32:
57 			{
58 				*pen_32 = color; /* 对于32bpp,颜色格式跟color参数一致,可以直接写入Framebuffer。 */
59 				break;
60 			}
61 			default:
62 			{
63 				printf("can't surport %dbpp\n",var.bits_per_pixel);
64 				break;
65 			}
66 		}
67 }

最终在main函数中调用描点函数画一条红色的线:

95 /* 清屏: 全部设为白色 */
96 memset(fbmem, 0xff, screen_size);
97
98 /* 随便设置出 100 个为红色 */
99 for (i = 0; i < 100; i++)
100 	lcd_put_pixel(var.xres/2+i, var.yres/2, 0xFF0000);

其中白色是0xFFFFFF:

在这里插入图片描述

红色是0xFF0000:

在这里插入图片描述

使用工具链交叉编译后生成可执行程序拷贝到IMX6ULL开发板后运行效果:

在这里插入图片描述

100个红色像素点生成的1条红线。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2186141.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

“衣依”服装销售平台:Spring Boot技术实践与创新

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常适…

【STM32开发笔记】移植AI框架TensorFlow到STM32单片机【下篇】

【STM32开发笔记】移植AI框架TensorFlow到STM32单片机【下篇】 一、上篇回顾二、项目准备2.1 准备模板项目2.2 支持计时功能2.3 配置UART4引脚2.4 支持printf重定向到UART42.5 支持printf输出浮点数2.6 支持printf不带\r的换行2.7 支持ccache编译缓存 三、TFLM集成3.1 添加tfli…

记录win11 蓝屏修复

1原因&#xff1a; win11 edge 的浏览器异常 打开新窗口的广告 打不开显示网络未连接下载驱动精灵 下载驱动要开会员 果断卸载 然后发现没有卸载干净 任务管理器 搜驱动 不小心干掉了win自带的文件win提示更新 更新重启就蓝屏随便点击一个新闻页面 解决办法&#xff1a; 在…

吴恩达深度学习笔记:卷积神经网络(Foundations of Convolutional Neural Networks)2.5-2.6

目录 第四门课 卷积神经网络&#xff08;Convolutional Neural Networks&#xff09;第二周 深度卷积网络&#xff1a;实例探究&#xff08;Deep convolutional models: case studies&#xff09;2.5 网络中的网络以及 11 卷积&#xff08;Network in Network and 11 convoluti…

利用Spring Boot开发“衣依”服装销售系统

1系统概述 1.1 研究背景 如今互联网高速发展&#xff0c;网络遍布全球&#xff0c;通过互联网发布的消息能快而方便的传播到世界每个角落&#xff0c;并且互联网上能传播的信息也很广&#xff0c;比如文字、图片、声音、视频等。从而&#xff0c;这种种好处使得互联网成了信息传…

如何从 PC 中检索已删除的文件?从 PC 恢复已删除的照片技巧

按 Shift Delete 以后后悔&#xff1f;想要恢复已删除的照片吗&#xff1f;好吧&#xff0c;如果是这样的话&#xff0c;那么您来对地方了。在本文中&#xff0c;我们将讨论如何从 PC 中检索已删除的文件。 自从摄影的概念被提出以来&#xff0c;人们就对它着迷。早期的照片保…

YOLO11改进|上采样篇|引入DySample轻量级动态上采样器

目录 一、DySample轻量级动态上采样器1.1DySample上采样模块介绍1.2DySample核心代码 五、添加DySample上采样器5.1STEP15.2STEP25.3STEP35.4STEP4 六、yaml文件与运行6.1yaml文件6.2运行成功截图 一、DySample轻量级动态上采样器 1.1DySample上采样模块介绍 DySample是一种基…

Koa2+Vue2的简书后台管理系统

文章目录 项目实战:前(vue)后(koa)端分离1、创建简书项目2、创建数据库2.1 创建数据库2.2 连接数据库3、模型对象3.1 设计用户模块的Schema3.2 实现用户增删改查3.2.1 增加用户3.2.2 修改用户3.2.3 删除用户3.2.4 查询用户4、封装业务逻辑层5、封装CRUD6、创建Vue项目7、配…

“衣依”服装销售平台:Spring Boot技术架构剖析

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常适…

TCP四次挥手过程详解

TCP四次挥手全过程 有几点需要澄清&#xff1a; 1.首先&#xff0c;tcp四次挥手只有主动和被动方之分&#xff0c;没有客户端和服务端的概念 2.其次&#xff0c;发送报文段是tcp协议栈的行为&#xff0c;用户态调用close会陷入到内核态 3.再者&#xff0c;图中的情况前提是双…

【CKA】十、统计node节点ready状态的数量

10、统计node节点ready状态的数量 1. 考题内容&#xff1a; 2. 答题思路&#xff1a; 1、检查有多个node状态ready 2、去除有Taint和NoSchedule的节点数量 3、将结果写入到指定文件中 3. 官网地址&#xff1a; https://kubernetes.io/zh-cn/docs/reference/node/node-statu…

LeetCode[中等] 45. 跳跃游戏 II

给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说&#xff0c;如果你在 nums[i] 处&#xff0c;你可以跳转到任意 nums[i j] 处: 0 < j < nums[i] i j < n 返回到达 nums[n - 1] 的最…

在Docker中运行微服务注册中心Eureka

1、Docker简介&#xff1a; 作为开发者&#xff0c;经常遇到一个头大的问题&#xff1a;“在我机器上能运行”。而将SpringCloud微服务运行在Docker容器中&#xff0c;避免了因环境差异带来的兼容性问题&#xff0c;能够有效的解决此类问题。 通过Docker&#xff0c;开发者可…

五子棋双人对战项目(4)——匹配模块(解读代码)

目录 一、约定前后端交互接口的参数 1、websocket连接路径 2、构造请求、响应对象 二、用户在线状态管理 三、房间管理 1、房间类&#xff1a; 2、房间管理器&#xff1a; 四、匹配器(Matcher) 1、玩家实力划分 2、加入匹配队列&#xff08;add&#xff09; 3、移除…

golang grpc初体验

grpc 是一个高性能、开源和通用的 RPC 框架&#xff0c;面向服务端和移动端&#xff0c;基于 HTTP/2 设计。目前支持c、java和go&#xff0c;分别是grpc、grpc-java、grpc-go&#xff0c;目前c版本支持c、c、node.js、ruby、python、objective-c、php和c#。grpc官网 grpc-go P…

Linux相关概念和重要知识点(11)(进程调度、Linux内核链表)

1.Linux调度算法 上篇文章我粗略讲过queue[140]的结构&#xff0c;根据哈希表&#xff0c;我们可以将40个不同优先级的进程借助哈希桶链入queue[140]中。调度器会根据queue的下标来进行调度。但这个具体的调度过程是怎样的呢&#xff1f;以及runqueue和queue[140]的关系是什么…

谷歌给到的185个使用生成式AI的案例

很多公司从利用AI回答问题&#xff0c;进而使用AI进行预测&#xff0c;向使用生成式AI Agent转变。AI Agent的独特之处在于它们可以采取行动以实现特定目标&#xff0c;比如引导购物者找到合适的鞋子&#xff0c;帮助员工寻找合适的健康福利&#xff0c;或在护理人员交接班期间…

python之输入输出

1、输入 Python在控制台输入内容&#xff0c;需要使用input函数。input函数会在控制台等待用户输入&#xff0c;直到用户按下了回车键才算完成输入。 注意&#xff1a;input函数接收的内容为字符串。 str1 input("请输入内容\n") print(str1) print(type(str1))1…

Python酷库之旅-第三方库Pandas(132)

目录 一、用法精讲 591、pandas.DataFrame.plot方法 591-1、语法 591-2、参数 591-3、功能 591-4、返回值 591-5、说明 591-6、用法 591-6-1、数据准备 591-6-2、代码示例 591-6-3、结果输出 592、pandas.DataFrame.plot.area方法 592-1、语法 592-2、参数 592-…

9.28学习笔记

1.ping 网址 2.ssh nscc/l20 3.crtl,打开vscode的setting 4.win 10修改ssh配置文件及其密钥权限为600 - 晴云孤魂 - 博客园 整体来看&#xff1a; 使用transformer作为其主干网络&#xff0c;代替了原先的UNet 在latent space进行训练&#xff0c;通过transformer处理潜…