• 嵌入式Linux应用开发-Framebuffer 应用编程


    第五章 Framebuffer 应用编程

    5.1 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 + xbpp/8
    最后一个要解决的问题就是像素的颜色怎么表示?它是用 RGB 三原色(红、绿、蓝)来表示的,在不同的BPP 格式中,用不同的位来分别表示 R、G、B,如下图所示:
    在这里插入图片描述
    对于 32BPP,一般只设置其中的低 24 位,高 8 位表示透明度,一般的 LCD 都不支持。
    对于 24BPP,硬件上为了方便处理,在 Framebuffer 中也是用 32 位来表示,效果跟 32BPP 是一样的。
    对于 16BPP,常用的是 RGB565;很少的场合会用到 RGB555,这可以通过 ioctl 读取驱动程序中的 RGB位偏移来确定使用哪一种格式。

    5.2 涉及的 API 函数

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

    5.2.1 open 函数

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

    在这里插入图片描述
    头文件:

    #include  
    #include  
    #include  
    
    • 1
    • 2
    • 3

    函数原型:

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

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

    5.2.2 ioctl 函数

    在 Ubuntu 中执行“man ioctl”,可以看到 ioctl 函数的说明:
    在这里插入图片描述
    头文件:

    #include  
    
    • 1

    函数原型:

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

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

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

    5.2.3 mmap 函数

    在 Ubuntu 中执行“man mmap”,可以看到 mmap 函数的说明:
    在这里插入图片描述
    想更深刻地理解 mmap 的内部机制,可以看《嵌入式 Linux 驱动开发基础知识》中关于 mmap 的介绍。作
    为 APP 开发,只需要知道它的用法就可以了。
    头文件:

    #include  
    
    • 1

    函数原型:

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

    函数说明:
    ① addr 表示指定映射的內存起始地址,通常设为 NULL 表示让系统自动选定地址,并在成功映射后返回该
    地址;
    ② length 表示将文件中多大的内容映射到内存中;
    ③ prot 表示映射区域的保护方式,可以为以下 4 种方式的组合
    a. PROT_EXEC 映射区域可被执行
    b. PROT_READ 映射区域可被读出
    c. PROT_WRITE 映射区域可被写入
    d. PROT_NONE 映射区域不能存取
    ④ Flags 表示影响映射区域的不同特性,常用的有以下两种
    a. MAP_SHARED 表示对映射区域写入的数据会复制回文件内,原来的文件会改变。
    b. MAP_PRIVATE 表示对映射区域的操作会产生一个映射文件的复制,对此区域的任何修改都不会写回
    原来的文件内容中。
    ⑤ 返回值:若成功映射,将返回指向映射的区域的指针,失败将返回-1。

    5.3 Framebuffer 程序分析

    使用 GIT 下载所有源码后,本节源码位于如下目录:
    01_all_series_quickstart\
    04_嵌入式 Linux 应用开发基础知识\source\07_framebuffer\show_pixel.c

    5.3.1 打开设备

    首先打开设备节点:

    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 } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    5.3.2 获取 LCD 参数

    LCD 驱动程序给 APP 提供 2 类参数:可变的参数 fb_var_screeninfo、固定的参数 fb_fix_screeninfo。
    编写应用程序时主要关心可变参数,它的结构体定义如下(#include ):
    在这里插入图片描述

    可以使用以下代码获取 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; 
     } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

    对于固定的参数 fb_fix_screeninfo,在应用编程中很少用到。它的结构体定义如下:
    在这里插入图片描述
    可以使用 ioctl FBIOGET_FSCREENINFO 来读出这些信息,但是很少用到。

    5.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 } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    第 88 行中,screen_size 是整个 Framebuffer 的大小;PROT_READ | PROT_WRITE 表示该区域可读、可写;MAP_SHARED 表示该区域是共享的,APP 写入数据时,会直达驱动程序,这个参数的更深刻理解可以参考后面驱动基础中讲到的 mmap 知识。

    5.3.4 描点函数

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

    28 void lcd_put_pixel(int x, int y, unsigned int color) 
    29 { 
    30 unsigned char *pen_8 = fb_base+y*line_width+x*pixel_width; 
    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; 
    44 		break; 
    45 	} 
    46 	case 16: 
    47 	{ 
    48 		/* 565 */ 
    49 		red = (color >> 16) & 0xff; 
    50 		green = (color >> 8) & 0xff; 
    51 		blue = (color >> 0) & 0xff; 
    52 		color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3); 
    53 		*pen_16 = color; 
    54 		break; 
    55 	} 
    56 	case 32: 
    57 	{ 
    58 		*pen_32 = color; 
    59 		break; 
    60 	} 
    61 	default: 
    62 	{ 
    63 		printf("can't surport %dbpp\n", var.bits_per_pixel); 
    64 		break; 
    65 	} 
    66 } 
    67 }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    第 28 行中传入的 color 表示颜色,它的格式永远是 0x00RRGGBB,即 RGB888。当 LCD 是 16bpp 时,要把 color 变量中的 R、G、B 抽出来再合并成 RGB565 格式。
    第 30 行计算(x,y)坐标上像素对应的 Framebuffer 地址。
    第 43 行,对于 8bpp,color 就不再表示 RBG 三原色了,这涉及调色板的概念,color 是调色板的值。
    第 49~51 行,先从 color 变量中把 R、G、B 抽出来。
    第 52 行,把 red、green、blue 这三种 8 位颜色值,根据 RGB565 的格式,只保留 red 中的高 5 位、green 中的高 6 位、blue 中的高 5 位,组合成一个新的 16 位颜色值。
    第 53 行,把新的 16 位颜色值写入 Framebuffer。
    第 58 行,对于 32bpp,颜色格式跟 color 参数一致,可以直接写入 Framebuffer。

    5.3.5 随便画几个点

    本程序的 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); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    5.4 上机实验

    在 Ubuntu 中编译程序,先设置交叉编译工具链,再执行以下命令:
    arm-linux-gnueabihf-gcc -o show_pixel show_pixel.c

    然后在开发板上执行 show_pixel 程序。

    注意:板子的出厂程序中一般都有 GUI,所以可能需要把 GUI 程序禁止掉。具体方法请看本文档,以后会补充禁止 GUI 的方法。你可以先不禁止 GUI,直接执行 show_pixel 看看 LCD 有无现象。

  • 相关阅读:
    365天深度学习 | 第7周:咖啡豆识别
    pycharm交互式编程 python console
    海外版知乎Quora,如何使用Quora进行营销?
    Unity3D 引擎学习2022资料整理(二)
    ASP.NET MVC--数据验证
    Python中的贝叶斯推断和马尔科夫链蒙特卡洛抽样调查
    前端面试题JS篇(3)
    【附源码】计算机毕业设计JAVA学生信息管理系统2021
    PyCharm 安装库时显示连接超时
    【逆向基础】九、dnSpy使用技巧随记
  • 原文地址:https://blog.csdn.net/kingpower2018/article/details/133181719