Linux应用开发基础知识——Framebuffer 应用编程(四)

news2024/11/26 17:30:45

前言:

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

目录

一、LCD 操作原理

二、涉及的 API 函数

 1.open 函数

2.ioctl 函数

 3.mmap 函数

三、Framebuffer 程序分析 

1.Framebuffer源码如下:

2. 打开设备:

3.获取 LCD 参数

4.映射 Framebuffer

5.描点函数

6.随便画几个点 

四、上机实验测试


一、LCD 操作原理

1.驱动程序设置好 LCD 控制器:

         根据 LCD 的参数设置 LCD 控制器的时序、信号极性;

         根据 LCD 分辨率、BPP 分配 Framebuffer。

2.APP 使用 ioctl 获得 LCD 分辨率、BPP

3.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

         对于 32BPP,一般只设置其中的低 24 位,高 8 位表示透明度,一般的 LCD 都不支持。

         对于 24BPP,硬件上为了方便处理,在 Framebuffer 中也是用 32 位来表 示,效果跟 32BPP 是一样的。

        对于 16BPP,常用的是 RGB565;很少的场合会用到 RGB555,这可以通过 ioctl 读取驱动程序中的 RGB 位偏移来确定使用哪一种格式。

二、涉及的 API 函数

目的是:打开 LCD 设备节点,获取分辨率等参数,映射 Framebuffer,最后实现描点函数。

 1.open 函数

在 Ubuntu 中执行“man 2 open”,可以看到 open 函数的说明:

头文件:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

函数原型:

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

函数说明:

1.pathname 表示打开文件的路径;

2.Flags 表示打开文件的方式,常用的有以下 6 种,

        ◼ O_RDWR 表示可读可写方式打开;

        ◼ O_RDONLY 表示只读方式打开;

        ◼ O_WRONLY 表示只写方式打开;

        ◼ O_APPEND 表示如果这个文件中本来是有内容的,则新写入的内容会接续到原来内容的后面;

        ◼ O_TRUNC 表示如果这个文件中本来是有内容的,则原来的内容会被丢弃,截断;         ◼ O_CREAT 表示当前打开文件不存在,我们创建它并打开它,通常与 O_EXCL 结合使用,当没有文件时创建文件,有这个文件时会报错提醒我们;

3.Mode 表示创建文件的权限,只有在 flags 中使用了 O_CREAT 时才有效, 否则忽略。

4.返回值:打开成功返回文件描述符,失败将返回-1。

2.ioctl 函数

在 Ubuntu 中执行“man ioctl”,可以看到 ioctl 函数的说明:

头文件: 

#include <sys/ioctl.h>

函数原型:

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

1. fd 表示文件描述符;

2. request 表示与驱动程序交互的命令,用不同的命令控制驱动程序输出我们 需要的数据; 3. … 表示可变参数 arg,根据 request 命令,设备驱动程序返回输出的数据。

4.返回值:打开成功返回文件描述符,失败将返回-1。

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

 3.mmap 函数

在 Ubuntu 中执行“man mmap”,可以看到 mmap 函数的说明:

头文件:

#include <sys/mman.h>

函数原型:

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

1.addr 表示指定映射的內存起始地址,通常设为 NULL 表示让系统自动选定 地址,并在成功映射后返回该地址;

2.length 表示将文件中多大的内容映射到内存中;

3.prot 表示映射区域的保护方式,可以为以下 4 种方式的组合

        ◼ PROT_EXEC 映射区域可被执行

        ◼ PROT_READ 映射区域可被读出

        ◼ PROT_WRITE 映射区域可被写入

        ◼ PROT_NONE 映射区域不能存取 Flags 表示影响映射区域的不同特性,常用的有以下两种

        ◼ MAP_SHARED 表示对映射区域写入的数据会复制回文件内,原来的文件会改变。         ◼ MAP_PRIVATE 表示对映射区域的操作会产生一个映射文件的复制,对此区域的任何修改都不会写回原来的文件内容中。

5.返回值:若成功映射,将返回指向映射的区域的指针,失败将返回-1。

三、Framebuffer 程序分析 

1.Framebuffer源码如下:


/**********************************************************************
 * 函数名称: lcd_put_pixel
 * 功能描述: 在LCD指定位置上输出指定颜色(描点)
 * 输入参数: x坐标,y坐标,颜色
 * 输出参数: 无
 * 返 回 值: 会
 ***********************************************************************/
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;
}

2. 打开设备:

首先打开设备节点:

 fd_fb = open("/dev/fb0", O_RDWR);

 if (fd_fb < 0)
 {
     printf("can't open /dev/fb0\n");
     return -1;
 }

3.获取 LCD 参数

LCD 驱动程序给 APP 提供 2 类参数:

        可变的参数 fb_var_screeninfo

        固 定的参数 fb_fix_screeninfo

编写应用程序时主要关心可变参数,它的结构 体定义如下(#include <Linux/fd.h>):

可以使用以下代码获取 fb_var_screeninfo:

static struct fb_var_screeninfo var;    /* Current var */

......

if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))
{
     printf("can't get var\n");
     return -1;
}

        注意到 ioctl 里用的参数是:FBIOGET_VSCREENINFO,它表示 get var screen info,获得屏幕的可变信息;当然也可以使用 FBIOPUT_VSCREENINFO 来调整这 些参数,但是很少用到。

对于固定的参数 fb_fix_screeninfo,在应用编程中很少用到。它的结构 体定义如下:

 可以使用 ioctl FBIOGET_FSCREENINFO 来读出这些信息,但是很少用到。

4.映射 Framebuffer

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

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;
}

screen_size 是整个 Framebuffer 的大小;PROT_READ | PROT_WRITE 表示该区域可读、可写;MAP_SHARED 表示该区域是共享的,APP 写 入数据时,会直达驱动程序。

5.描点函数

能够在 LCD 上描绘指定像素后,就可以写字、画图,描点函数是基础。代码如下:


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;
                }
        }
}

1.void lcd_put_pixel(int x, int y, unsigned int color)

传入的 color 表示颜色,它的格式永远是 0x00RRGGBB,即 RGB888。 当 LCD 是16bpp 时,要把 color 变量中的 R、G、B 抽出来再合并成 RGB565 格式。

2.unsigned char *pen_8 = fb_base+y*line_width+x*pixel_width;

计算(x,y)坐标上像素对应的 Framebuffer 地址。

3.*pen_8 = color;

对于 8bpp,color 就不再表示 RBG 三原色了,这涉及调色板的概念,color 是调色板的值。 第 49~51 行,先从 color 变量中把 R、G、B 抽出来。

4.  red = (color >> 16) & 0xff;

     green = (color >> 8) & 0xff;

     blue = (color >> 0) & 0xff;

把 red、green、blue 这三种 8 位颜色值,根据 RGB565 的格式, 只保留 red 中的高 5 位、green 中的高 6 位、blue 中的高 5 位,组合成一个新 的 16 位颜色值。

5.color = ((red >> 3) > 2) > 3);

把新的 16 位颜色值写入 Framebuffer。

6.*pen_32 = color;

对于 32bpp,颜色格式跟 color 参数一致,可以直接写入 Framebuffer。

6.随便画几个点 

本程序的 main 函数,在最后只是简单地画了几个点:

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

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

四、上机实验测试

在 Ubuntu 中编译程序,先设置交叉编译工具链,再执行以下命令:

book@100ask:~/source/07_framebuffer$ arm-buildroot-linux-gnueabihf-gcc -o show_pixel show_pixel.c
book@100ask:~/source/07_framebuffer$ cp show_pixel ~/nfs_rootfs/

在开发板上挂载网络文件系统

 ​​​​​

 运行程序

[root@100ask:~]#  mount -t nfs -o nolock,vers=3 192.168.5.11:/home/book/nfs_rootfs /mnt
[root@100ask:~]# /mnt/show_pixel

 运行效果:

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

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

相关文章

2023年9月少儿编程 中国电子学会图形化编程等级考试Scratch编程二级真题解析(选择题)

2023年9月scratch编程等级考试二级真题 选择题(共25题,每题2分,共50分) 1、点击绿旗,运行程序后,舞台上的图形是 A、画笔粗细为4的三角形 B、画笔粗细为5的六边形 C、画笔粗细为4的六角形 D、画笔粗细为5的三角形 答案:D 考点分析:考查积木综合使用,重点考查画笔…

100 寻找重复数

寻找重复数 题解1 二分法题解2 快慢指针(同环形链表2(ab)(ab)kL) 给定一个包含 n 1 个整数的数组 nums &#xff0c;其数字都在 [1, n] 范围内&#xff08;包括 1 和 n&#xff09;&#xff0c;可知至少存在一个重复的整数。 假设 nums 只有 一个重复的整数 &#xff0c;返…

LDR6023AQ-PDHUB最简外围成本低搂到底就是干

USB-C PD协议里&#xff0c;SRC和SNK双方之间通过CC通信来协商请求确定充电功率及数据传输速率。当个设备需要充电时&#xff0c;它会发送消息去给适配器请求充电&#xff0c;此时充电器会回应设备的请求&#xff0c;并告知其可提供的档位功率&#xff0c;设备端会根据适配器端…

@JSONField或@JsonProperty注解使用

一、需求 使用JSONField或JsonProperty注解&#xff0c;来解决bean与json字段不一致问题&#xff0c;或者字段定义不符合前端所需要的标准&#xff0c;最近在项目中发现实体类属性中&#xff0c;同时使用了JSONField和JsonProperty注解&#xff0c;用于重新声明属性key。有时候…

数字滤波器分析---频率响应

数字滤波器分析---频率响应 幅值、相位、冲激和阶跃响应、相位和群延迟、零极点分析。 分析滤波器的频域和时域响应。可视化复平面中的滤波器极点和零点。 频率响应 数字域 freqz 使用基于 FFT 的算法来计算数字滤波器的 Z 变换频率响应。具体来说&#xff0c;语句 [h,w]…

Android jetpack compose 组件学习

官网地址&#xff1a;https://developer.android.com/jetpack/compose/tutorial?hlzh-cn 一、写一个 hello world 在New Project的时候选择Photo and Tablet里的Empty Compose Activity&#xff0c;如下所示&#xff1a; 打开这个project之后&#xff0c;可以看到MainActivit…

重新定义客户服务 UniPro Mailhandler 彻底改变团队处理请求模式

现代企业管理越精细越专业&#xff0c;涉及到的跨团队、跨组织之间的高效协作程度越高。如果没有趁手的协作工具加持&#xff0c;协作过程中必然会出现一些问题&#xff1a; 信息查找低效&#xff0c;例如找人、找部门&#xff0c;不清楚任务分配该找谁&#xff1b; 信息传达…

leetcode刷题 - SQL - 中等

1. 176. 第二高的薪水 筛选出第二大 查询并返回 Employee 表中第二高的薪水 。如果不存在第二高的薪水&#xff0c;查询应该返回 null(Pandas 则返回 None) 。查询结果如下例所示。 666中等的第一题就上强度 强行解法 select max(salary) as SecondHighestSalary from Emp…

模型剪枝算法——L1正则化BN层的γ因子

ICCV在2017年刊登了一篇经典论文《 Learning Efficient Convolutional Networks through Network Slimming》。在神经网络的卷积操作之后会得到多个特征图&#xff0c;通过策略突出重要的特征达到对网络瘦身的目的。在该论文中使用的剪枝策略就是稀疏化BN层中的缩放因子 。 Bat…

NSS [鹏城杯 2022]压缩包

NSS [鹏城杯 2022]压缩包 考点&#xff1a;条件竞争/逻辑漏洞&#xff08;解压失败不删除已经解压文件&#xff09; 参考&#xff1a;回忆phpcms头像上传漏洞以及后续影响 | 离别歌 (leavesongs.com) 源码有点小多 <?php highlight_file(__FILE__);function removedir($…

【Python】 Python 使用 Pillow 处理图像:几何变换

Python 使用 Pillow 处理图像&#xff1a;几何变换 pillow库操作切片、旋转、滤镜、输出文字、调色板等功能一应俱全。 1. 几何变换 Image 包含调整图像大小 resize() 和旋转 rotate() 的方法。前者采用元组给出新的大小&#xff0c;后者采用逆时针方向的角度。 调整大小并…

视频批量剪辑:AI智剪入门,轻松掌握智能剪辑技巧

在数字媒体时代&#xff0c;视频剪辑已经成为一项必备的技能。无论是为了工作需要&#xff0c;还是为了在社交媒体上分享生活&#xff0c;掌握视频剪辑技巧都能为我们的生活和工作带来很多便利。然而&#xff0c;对于初学者来说&#xff0c;视频剪辑可能是一项艰巨的任务。现在…

1210. 连号区间数(枚举)

题目&#xff1a; 1210. 连号区间数 - AcWing题库 思路&#xff1a;枚举 枚举一般是先暴力再优化。 注意&#xff1a;对于区间的枚举&#xff0c;一般是定一移一。固定任何一端移动另外一端均可以。但是此题为便于在枚举移动端的过程中确定最大最小&#xff0c;选择定左移右…

银行测试丨信贷长链路业务测试数据快速构造方法,了解一下

一、引言 随着银行数字化转型的不断深入&#xff0c;对信贷领域的测试工作提出了更高的标准和要求&#xff0c;如何在高效完成测试任务的同时确保测试质量就成了一个亟待解决的难题。有实践研究表明&#xff0c;在具体测试过程中往往大量的时间精力都是耗费在测试数据准备工作…

原神私服搭建服务器配置该如何选择

原神是一款开放世界的冒险游戏&#xff0c;自从这款游戏上线以来&#xff0c;就受到越来越多的玩家喜欢&#xff0c;因为这款游戏的设定比较少见&#xff0c;剧情也非常精彩&#xff0c;有一些玩家为了更好的游戏体验想要搭建原神的私服&#xff0c;满足玩家的需求&#xff0c;…

【云栖2023】王峰:开源大数据平台3.0技术解读

本文根据2023云栖大会演讲实录整理而成&#xff0c;演讲信息如下&#xff1a; 演讲人&#xff1a;王峰 | 阿里云研究员&#xff0c;阿里云计算平台事业部开源大数据平台负责人 演讲主题&#xff1a;开源大数据平台3.0技术解读 实时化与Serverless是开源大数据3.0时代的必然选…

提升采购订单管理效率的五个最佳实践

企业每年都要订购成千上万的商品和服务。公司运营和发展所需的一切都来自庞大的供应商网络&#xff0c;而沟通这些需求的主要方式是通过采购订单。 由于所有订单都会流经系统&#xff0c;而且每个月都会发生数千元不等的供应支出&#xff0c;因此掌握采购订单流程成为重中之重…

2023年云计算发展趋势浅析

​​​​​​​ 云计算的概念 云计算是一种通过互联网提供计算资源和服务的模式。它允许用户通过网络访问和使用共享的计算资源&#xff0c;而无需拥有或管理这些资源的物理设备。云计算的核心理念是将计算能力、存储资源和应用程序提供给用户&#xff0c;以便随时随地根据需要…

Ubuntu18.04 安装docker教程

Ubuntu18.04 安装docker教程 1、前言 Docker Engine-Community 支持以下的 Ubuntu 版本&#xff1a; Xenial 16.04 (LTS)Bionic 18.04 (LTS)Cosmic 18.10Disco 19.04 Docker Engine-Community 支持以下CPU架构&#xff1a; x86_64&#xff08;或 amd64&#xff09;armhfarm…

Win10共享打印机,别人连接不上出现无法连接到打印机错误码0x0000011b

环境&#xff1a; Win10 专业版 惠普L1119 问题描述&#xff1a; Win10共享打印机&#xff0c;别人连接不上出现无法连接到打印机错误码0x0000011b 解决方案&#xff1a; 1.打开我这台电脑的注册表找到 HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Print在右侧…