嵌入式Linux驱动开发(LCD屏幕专题)(四)

2023-11-12

单Buffer的缺点与改进方法

1. 单Buffer的缺点

  • 如果APP速度很慢,可以看到它在LCD上缓慢绘制图案

  • 即使APP速度很高,LCD控制器不断从Framebuffer中读取数据来显示,而APP不断把数据写入Framebuffer

    • 假设APP想把LCD显示为整屏幕的蓝色、红色

    • 很大几率出现这种情况:

      • LCD控制器读取Framebuffer数据,读到一半时,在LCD上显示了半屏幕的蓝色
      • 这是APP非常高效地把整个Framebuffer的数据都改为了红色
      • LCD控制器继续读取数据,于是LCD上就会显示半屏幕蓝色、半屏幕红色
      • 人眼就会感觉到屏幕闪烁、撕裂
        在这里插入图片描述

2. 使用多Buffer来改进

上述两个缺点的根源是一致的:Framebuffer中的数据还没准备好整帧数据,就被LCD控制器使用了。
使用双buffer甚至多buffer可以解决这个问题:

  • 假设有2个Framebuffer:FB0、FB1
  • LCD控制器正在读取FB0
  • APP写FB1
  • 写好FB1后,让LCD控制器切换到FB1
  • APP写FB0
  • 写好FB0后,让LCD控制器切换到FB0

3. 内核驱动程序、APP互相配合使用多buffer

流程如下:

在这里插入图片描述

  • 驱动:分配多个buffer

    fb_info->fix.smem_len = SZ_32M;
    fbi->screen_base = dma_alloc_writecombine(fbi->device,
    				fbi->fix.smem_len,
    				(dma_addr_t *)&fbi->fix.smem_start,
    				GFP_DMA | GFP_KERNEL);
    
  • 驱动:保存buffer信息

    fb_info->fix.smem_len  // 含有总buffer大小 
    fb_info->var           // 含有单个buffer信息
    
  • APP:读取buffer信息

    ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix);
    ioctl(fd_fb, FBIOGET_VSCREENINFO, &var);
    
    // 计算是否支持多buffer,有多少个buffer
    screen_size = var.xres * var.yres * var.bits_per_pixel / 8;
    nBuffers = fix.smem_len / screen_size;
    
  • APP:使能多buffer

    var.yres_virtual = nBuffers * var.yres;
    ioctl(fd_fb, FBIOPUT_VSCREENINFO, &var);
    
  • APP:写buffer

    fb_base = (unsigned char *)mmap(NULL , fix.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
    
    /* get buffer */
    pNextBuffer =  fb_base + nNextBuffer * screen_size;
    
    /* set buffer */
    lcd_draw_screen(pNextBuffer, colors[i]);
    
  • APP:开始切换buffer

    /* switch buffer */
    var.yoffset = nNextBuffer * var.yres;
    ioctl(fd_fb, FBIOPAN_DISPLAY, &var);
    
  • 驱动:切换buffer

    // fbmem.c
    fb_ioctl
        do_fb_ioctl
        	fb_pan_display(info, &var);
    			err = info->fbops->fb_pan_display(var, info) // 调用硬件相关的函数            
    

    示例:
    在这里插入图片描述

  • APP:等待切换完成(在驱动程序中已经等待切换完成了,所以这个调用并无必要)

    ret = 0;
    ioctl(fd_fb, FBIO_WAITFORVSYNC, &ret);
    

多buff代码

#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>
#include <time.h>

static int fd_fb;
static struct fb_fix_screeninfo fix;	/* Current fix */
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;


void lcd_put_pixel(void *fb_base, 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;
		}
	}
}

void lcd_draw_screen(void *fb_base, unsigned int color)
{
	int x, y;
	for (x = 0; x < var.xres; x++)
		for (y = 0; y < var.yres; y++)
			lcd_put_pixel(fb_base, x, y, color);
}


/* ./multi_framebuffer_test single
 * ./multi_framebuffer_test double
 */
int main(int argc, char **argv)
{
	int i;
	int ret;
	int nBuffers;
	int nNextBuffer = 1;
	char *pNextBuffer;
	unsigned int colors[] = {0x00FF0000, 0x0000FF00, 0x000000FF, 0, 0x00FFFFFF};  /* 0x00RRGGBB */
	struct timespec time;

	time.tv_sec  = 0;
	time.tv_nsec = 100000000;

	if (argc != 2)
	{
		printf("Usage : %s <single|double>\n", argv[0]);
		return -1;
	}
	
	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_FSCREENINFO, &fix))
	{
		printf("can't get fix\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;

	nBuffers = fix.smem_len / screen_size;
	printf("nBuffers = %d\n", nBuffers);
	
	fb_base = (unsigned char *)mmap(NULL , fix.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
	if (fb_base == (unsigned char *)-1)
	{
		printf("can't mmap\n");
		return -1;
	}

	if ((argv[1][0] == 's') || (nBuffers == 1))
	{
		while (1)
		{
			/* use single buffer */
			for (i = 0; i < sizeof(colors)/sizeof(colors[0]); i++)
			{
				lcd_draw_screen(fb_base, colors[i]);
				nanosleep(&time, NULL);
			}
		}
	}
	else
	{
		/* use double buffer */
		/* a. enable use multi buffers */
		var.yres_virtual = nBuffers * var.yres;
		ioctl(fd_fb, FBIOPUT_VSCREENINFO, &var);

		while (1)
		{
			for (i = 0; i < sizeof(colors)/sizeof(colors[0]); i++)
			{
				/* get buffer */
				pNextBuffer =  fb_base + nNextBuffer * screen_size;

				/* set buffer */
				lcd_draw_screen(pNextBuffer, colors[i]);

				/* switch buffer */
				var.yoffset = nNextBuffer * var.yres;
				ioctl(fd_fb, FBIOPAN_DISPLAY, &var);

				ret = 0;
				ioctl(fd_fb, FBIO_WAITFORVSYNC, &ret);
				
				nNextBuffer = !nNextBuffer;
				nanosleep(&time, NULL);
			}
		}
		
	}
	
	munmap(fb_base , screen_size);
	close(fd_fb);
	
	return 0;	
}



本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

嵌入式Linux驱动开发(LCD屏幕专题)(四) 的相关文章

  • 如何使用 Cloud Init 挂载未格式化的 EBS 卷

    Context 我正在使用https wiki jenkins io display JENKINS Amazon EC2 Plugin https wiki jenkins io display JENKINS Amazon EC2 Pl
  • 在centos中安装sqlite3 dev和其他包

    我正在尝试使用 cpanel 在 centos 机器上安装 sqlite dev 和其他库 以便能够编译应用程序 我对 debian 比 centos 更熟悉 我知道我需要的库是 libsqlite3 dev libkrb5 dev lib
  • Linux 上的静态 Qt5 构建:部署时如何处理字体?

    我使用这些配置选项创建了 Qt 5 2 0 库的静态版本 Ubuntu 12 04 开源 确认许可 force pkg config 发布 静止的 前缀 home juzzlin qt5 无icu opengl桌面 无油嘴滑舌 辅助功能 n
  • 使用 MAX_ORDER / 包含 mmzone.h

    根据https www kernel org doc Documentation networking packet mmap txt https www kernel org doc Documentation networking pa
  • .net-core:ILDASM / ILASM 的等效项

    net core 是否有相当于 ILDASM ILASM 的功能 具体来说 我正在寻找在 Linux 上运行的东西 因此为什么是 net core ildasm 和 ilasm 工具都是使用此存储库中的 CoreCLR 构建的 https
  • 如何使用waf构建共享库?

    我想使用构建一个共享库waf http code google com p waf 因为它看起来比 GNU 自动工具更容易 更简洁 到目前为止 我实际上有几个与我开始编写的 wscript 有关的问题 VERSION 0 0 1 APPNA
  • 内核的panic()函数是否完全冻结所有其他进程?

    我想确认内核的panic 功能和其他类似kernel halt and machine halt 一旦触发 保证机器完全冻结 那么 所有的内核和用户进程都被冻结了吗 是panic 可以被调度程序中断吗 中断处理程序仍然可以执行吗 用例 如果
  • 与 pthread 的进程间互斥

    我想使用一个互斥体 它将用于同步对两个不同进程共享的内存中驻留的某些变量的访问 我怎样才能做到这一点 执行该操作的代码示例将非常感激 以下示例演示了 Pthread 进程间互斥体的创建 使用和销毁 将示例推广到多个进程作为读者的练习 inc
  • 如何在 Linux 中使用 C 语言使用共享内存

    我的一个项目有点问题 我一直在试图找到一个有据可查的使用共享内存的例子fork 但没有成功 基本上情况是 当用户启动程序时 我需要在共享内存中存储两个值 当前路径这是一个char and a 文件名这也是char 根据命令参数 启动一个新进
  • Intel 上的 gcc 中的 _mm_pause 用法

    我参考过这个网页 https software intel com en us articles benefitting power and performance sleep loops https software intel com
  • 使用 python 脚本更改 shell 中的工作目录

    我想实现一个用户态命令 它将采用其参数之一 路径 并将目录更改为该目录 程序完成后 我希望 shell 位于该目录中 所以我想实施cd命令 但需要外部程序 可以在 python 脚本中完成还是我必须编写 bash 包装器 Example t
  • 在生产服务器上使用 Subversion 使文件生效的最佳方法是什么?

    目前我已经设置了 subversion 这样当我在 Eclipse PDT 中进行更改时 我可以提交更改 它们将保存在 home administrator 中项目文件 该文件具有 subversion 推荐的 branches tags
  • linux下如何从文本文件中获取值

    我有一些文本格式的文件 xxx conf 我在这个文件中有一些文本 disablelog 1 当我使用 grep r disablelog oscam conf 输出是 disablelog 1 但我只需要值1 请问你有什么想法吗 一种方法
  • 如何使用Android获取Linux内核的版本?

    如何在 Android 应用程序中获取 Linux 内核的版本 不是 100 确定 但我认为调用 uname r 需要 root 访问权限 无论如何 有一种不太肮脏的方法可以做到这一点 那就是 System getProperty os v
  • Apache 访问 Linux 中的 NTFS 链接文件夹

    在 Debian jessie 中使用 Apache2 PHP 当我想在 Apache 的文档文件夹 var www 中创建一个新的小节时 我只需创建一个指向我的 php 文件所在的外部文件夹的链接 然后只需更改该文件夹的所有者和权限文件夹
  • 这种文件锁定方法可以接受吗?

    我们有 10 个 Linux 机器 每周必须运行 100 个不同的任务 这些计算机主要在我们晚上在家时执行这些任务 我的一位同事正在开发一个项目 通过使用 Python 自动启动任务来优化运行时间 他的程序将读取任务列表 抓取一个打开的任务
  • cdc_acm:无法设置 dtr/rts - 无法与 USB cdc 设备通信

    我试图使用 pic24fj128gb206 枚举 usb cdc 设备 设备似乎已正确枚举 但是当我将设备连接到 Linux PC 时 我从内核收到以下警告消息 cdc acm 1 8 1 6 7 1 0 failed to set dtr
  • 无法显示 Laravel 欢迎页面

    我的服务器位于 DigitalOcean 云上 我正在使用 Ubuntu 和 Apache Web 服务器 我的家用计算机运行的是 Windows 7 我使用 putty 作为终端 遵循所有指示https laracasts com ser
  • 为 OpenWrt 编写和编译程序

    我有一个在 OpenWRT 下运行的具有 MIPS 架构的嵌入式设备 系统类型 MediaTek MT7628AN ver 1 eco 2机器 WRTnode2P 处理器 0CPU型号 MIPS 24KEc V5 5 我想通过我的电脑 ub
  • 跨 CPU 内核的 rdtsc 精度

    我从一个线程发送网络数据包 并在运行于不同 CPU 核心上的第二个线程上接收回复 我的流程测量每个数据包发送和接收之间的时间 类似于 ping 我使用 rdtsc 来获得高分辨率 低开销的计时 这是我的实现所需要的 所有测量结果看起来都很可

随机推荐

  • 零基础入门金融风控 Task2 数据分析

    文章目录 1 导入数据分析及可视化过程需要的库 2 读取文件 3 总体了解 4 查看数据集中特征缺失值 唯一值等 5 查看特征的数值类型有哪些 对象类型有哪些 6 变量分布可视化 6 时间格式数据处理及查看 7 掌握透视图可以让我们更好的了
  • 【Linux工具】-yum/gdb

    yum gdb 一 yum 1 简介 2 软件下载 3 软件删除 4 yum源与扩展yum源 5 常见选项 二 gdb 1 简介 2 gdb相关指令 一 yum 1 简介 在Linux下 下载软件通常的方法是下载源代码 然后进行编译得到可执
  • 【单片机毕业设计】【mcuclub-yq-001】基于单片机的翻蛋器 孵化器的设计

    最近设计了一个项目基于单片机的翻蛋器 孵化器系统 与大家分享一下 一 基本介绍 项目名 翻蛋器 孵化器 项目编号 mcuclub yq 001 单片机类型 STC89C52 STM32F103C8T6 具体功能 1 通过DS18B20测量温
  • 学习Java第三天——手机类的创建与使用

    需求 定义一个手机类 然后定义一个手机测试类 在手机测试类中通过对象完成成员变量和成员方法的使用 学习时间 2022 6 21凌晨 程序 成员变量 品牌 价格 成员方法 打电话 发短信 手机类 package com pipi test1
  • 第06篇 开闭原则

    一 定义 开闭原则 Open Closed Principle OCP 一个软件实体应当对扩展开放 对修改关闭 即软件实体应尽量在不修改原有代码的情况下进行扩展 开闭原则是面向对象的可复用设计的第一块基石 它是最重要的面向对象设计原则 二
  • 【gitHubDailyShare】开源的 C++ 入门学习资源,C++ 匠心之作

    分享 GitHub 上一份开源的 C 入门学习资源 Cpp 0 1 Resource 主要包含以下内容 第 1 阶段 C 匠心之作 从 0 到 1 入门 第 2 阶段实战 通讯录管理 第 3 阶段 C 核心编程 第 4 阶段实战 基于多态的
  • 解决ERROR 2003 (HY000): Can‘t connect to MySQL server on ‘localhost:3306‘ (10061)

    ERROR 2003 HY000 Can t connect to MySQL server on localhost 3306 10061 1 安装成功之后输入MYSQL报出ERROR 2003 HY000 Can t connect t
  • 【OpenCV图像处理入门学习教程六】基于Python的网络爬虫与OpenCV扩展库中的人脸识别算法比较

    OpenCV图像处理入门学习教程系列 上一篇第五篇 基于背景差分法的视频目标运动侦测 一 网络爬虫简介 Python3 网络爬虫 大家应该不陌生了 接下来援引一些Jack Cui在专栏 Python3网络爬虫入门 中的内容来帮助初学者理解
  • QT 如何修改工程(项目)名?

    前因 我们有时候一开始起的项目名到后面并不合乎心意时 而且项目里面的大多数类都是重复的 此时我们只想修改一下工程名即可 步骤如下 在这里假设我原来的工程名字是test 想要修改成名字为demo 第一步 打开工程文件夹 除了test pro以
  • 【JVM】JVM垃圾回收机制GC

    文章目录 JVM垃圾回收机制 一 堆内存区域划分 1 1内存分配策略 1 2永久代 Permanent Generation 1 3元空间 MetaSpace 二 标记算法 2 1引用计数算法 2 2可达性分析算法 2 3引用 强引用 Ha
  • Matlab中读取excel表格数据

    一 Matlab中读取excel表格数据步骤讲解 第二步 第三步 第四步 第五步 第六步 第七步 输入之后按回车键 就会出现相应的波形 效果图
  • 链接、装载与库——编译与链接

    从第二章开始不再按照目录的顺序总结 而是将大块知识点总结在一起 第二章 编译和链接 集成开发环境 IDE 一般都将编译和链接的过程一步完成 此过程成为构建 Bulid 但其掩盖了系统软件运行机制 gcc hello c a out 一个可执
  • win10离线安装ros-melodic-desktop_full

    在线安装最容易出现安装包下载不了导致的安装失败问题 本篇文章续上篇在线安装 安装在线包 下载ros melodic desktop full 下载地址 ros melodic离线包下载地址 开始菜单中 右键 x64 Native Tools
  • Codeforces Round 875 (Div. 1) A. Copil Copac Draws Trees

    题意 Copil Copac 给定了一个由 n 1 条边组成的列表 该列表描述了一棵由 n 个顶点组成的树 他决定用下面的算法来绘制它 步骤 0 绘制第一个顶点 顶点1 转到步骤1 步骤 1 对于输入中的每一条边 依次 如果该边连接一个已经
  • 【完全开源】小安派-Cam-U 摄像头核心板

    一 概述 小安派 Cam U AiPi Cam U 是安信可开源团队专门为Ai M61 32S设计的一款开发板 支持WiFi6 BLE5 3 所搭载的Ai M61 32S 模组具有丰富的外设接口 具体包括 DVP MJPEG Dispaly
  • [4G&5G专题-120]:培训-跟小朋友聊通信

    用小孩子的语言与小朋友聊通信
  • 【算法 -- LeetCode】(025) K 个一组翻转链表

    1 题目 给你链表的头节点 head 每 k 个节点一组进行翻转 请你返回修改后的链表 k 是一个正整数 它的值小于或等于链表的长度 如果节点总数不是 k 的整数倍 那么请将最后剩余的节点保持原有顺序 你不能只是单纯的改变节点内部的值 而是
  • 什么是钩子函数

    钩子函数 钩子函数 钩子函数是在一个事件触发的时候 在系统级捕获到了他 然后做一些操作 一段用以处理系统消息的程序 钩子 就是在某个阶段给你一个做某些处理的机会 钩子函数 1 是个函数 在系统消息触发时被系统调用 2 不是用户自己触发的 钩
  • Highcharts中更新series的5种方法

    用Highcharts画图时 经常需要更新所画的图表 最常见的就是改变数据以更新图表 在Highcarts中 数据对应的参数是series 这儿就以图1的柱状图为例 列举如何更新series的5种方法 以供参考 源代码可从这儿访问 图1 1
  • 嵌入式Linux驱动开发(LCD屏幕专题)(四)

    单Buffer的缺点与改进方法 1 单Buffer的缺点 如果APP速度很慢 可以看到它在LCD上缓慢绘制图案 即使APP速度很高 LCD控制器不断从Framebuffer中读取数据来显示 而APP不断把数据写入Framebuffer 假设