小马厂网站建设,开平建设局网站,oyster wordpress,易企秀h5制作免费目录
前言
LCD操作原理
涉及的 API 函数
open函数
ioctl 函数
mmap 函数
Framebuffer程序分析
源码
1.打开设备
2.获取LCD参数
3.映射Framebuffer
4.描点函数
5.随便画几个点
上机实验 前言
本文介绍LCD的操作原理和涉及到的API函数#xff0c;分析Framebuffer…目录
前言
LCD操作原理
涉及的 API 函数
open函数
ioctl 函数
mmap 函数
Framebuffer程序分析
源码
1.打开设备
2.获取LCD参数
3.映射Framebuffer
4.描点函数
5.随便画几个点
上机实验 前言
本文介绍LCD的操作原理和涉及到的API函数分析Framebuffer部分代码 LCD操作原理
在 Linux 系统中通过 Framebuffer 驱动程序来控制 LCD。Frame 是帧的意思buffer 是缓冲的意思这意味着 Framebuffer 就是一块内存里面保存着一帧图像。Framebuffer 中保存着一帧图像的每一个像素的颜色值。Framebuffer的大小为屏幕分辨率x每一个像素所占的字节数。
LCD操作流程如下 驱动程序设置好 LCD 控制器我并没有学习linux驱动本文只介绍要用到的函数和分析 Framebuffer代码注重应用编程实在看不懂原理的会用就行应用为王道 APP 使用 ioctl 获得 LCD 分辨率、BPP每一个像素所占的位数 APP 通过 mmap 映射 Framebuffer在 Framebuffer 中写入数据
LCD操作原理图 问怎么知道内存中的哪个地址对应哪个元素呢 假设需要设置 LCD 中坐标(x,y)处像素的颜色首要要找到这个像素对应的内存然后根据它的 BPP 值设置颜色。假设 fb_base 是 APP 执行 mmap 后得到的 Framebuffer 地址如下图 假设一个元素的左上角表示这个元素的地址 (x,y)坐标的元素前面有y行在这一行的前面又有x个元素我们不需要知道它是第几个元素这样子会很混乱我们只需要计算在它之前的所有元素占多大内存那么计算出来的内存空间再加上 fb_base 就是(x,y)坐标元素的地址。 可以用以下公式算出(x,y)坐标处像素对应的 Framebuffer 地址(xy)像素起始地址fb_base(xres*bpp/8)*y x*bpp/8 最后一个要解决的问题就是像素的颜色怎么表示 它是用 RGB 三原色(红、绿、 蓝)来表示的在不同的 BPP 格式中用不同的位来分别表示 R、G、B。 对于 32BPP一般只设置其中的低 24 位高 8 位表示透明度一般的 LCD 都不支持。 对于 16BPP常用的是 RGB565很少的场合会用到 RGB555这可以通过 ioctl 读取驱动程序中的 RGB 位偏移来确定使用哪一种格式。 涉及的 API 函数
open函数
#include sys/types.h
#include sys/stat.h
#include fcntl.hint open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
open函数之前用的很多了这里就不过多介绍了可以看我之前的博客Linux文件编程。
ioctl 函数
#include sys/ioctl.hint ioctl(int fd, unsigned long request, ...);
函数说明 fd 表示文件描述符 request 表示与驱动程序交互的命令用不同的命令控制驱动程序输出我们需要的数据 … 表示可变参数 arg根据 request 命令设置驱动程序返回输出的数据 返回值打开成功返回文件描述符失败将返回-1 ioctl 的作用非常强大、灵活。不同的驱动程序内部会实现不同的 ioctlAPP 可以使用各种 ioctl 跟驱动程序交互可以传数据给驱动程序也可以从驱动程序中读出数据。
mmap 函数
#include sys/mman.hvoid *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
mmap 函数的功能是将文件或其他对象映射到进程的内存地址空间。这使得程序可以通过内存操作直接访问文件内容而无需使用传统的文件读写函数。其主要功能包括 文件映射将文件内容映射到内存使得文件可以像数组一样进行读取和修改 共享内存在多个进程之间共享数据通过映射同一文件或匿名映射区域实现进程间通信 内存映射设备映射设备文件到内存以便进行高效的数据访问和操作
函数说明 addr 表示指定映射的內存起始地址通常设为 NULL 表示让系统自动选定地址并在成功映射 后返回该地址 length 表示将文件中多大的内容映射到内存中 prot 表示映射区域的保护方式可以为以下 4 种方式的组合 PROT_EXEC 映射区域可被执行 PROT_READ 映射区域可被读出 PROT_WRITE 映射区域可被写入 PROT_NONE 映射区域不能存取 Flags 表示影响映射区域的不同特性常用的有以下两种 MAP_SHARED 表示对映射区域写入的数据会复制回文件内原来的文件会改变 MAP_PRIVATE 表示对映射区域的操作会产生一个映射文件的复制对此区域的任何修改 都不会写回原来的文件内容中返回值若成功映射将返回指向映射的区域的指针失败将返回-1 Framebuffer程序分析
源码
#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.hstatic 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_basey*line_widthx*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(cant 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(cant open /dev/fb0\n);return -1;}if (ioctl(fd_fb, FBIOGET_VSCREENINFO, var)){printf(cant 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(cant mmap\n);return -1;}/* 清屏: 全部设为白色 */memset(fb_base, 0xff, screen_size);/* 随便设置出100个为红色 */for (i 0; i 100; i)lcd_put_pixel(var.xres/2i, var.yres/2, 0xFF0000);munmap(fb_base , screen_size);close(fd_fb);return 0;
}
1.打开设备
首先打开设备节点
fd_fb open(/dev/fb0, O_RDWR);
if (fd_fb 0)
{printf(cant open /dev/fb0\n);return -1;
}2.获取LCD参数 LCD 驱动程序给 APP 提供 2 类参数可变的参数 fb_var_screeninfo、固定的参数 fb_fix_screeninfo。编写应用程序时主要关心可变参数它的结构体定义如下(#include linux/fb.h) 可以使用以下代码获取 fb_var_screeninfo
static struct fb_var_screeninfo var; /* Current var */if (ioctl(fd_fb, FBIOGET_VSCREENINFO, var))
{printf(cant get var\n);return -1;
}注意到 ioctl 里用的参数是FBIOGET_VSCREENINFO它表示 get var screeninfo获得屏幕的可变信息当然也可以使用 FBIOPUT_VSCREENINFO 来调整这些参数但是很少用到。
对于固定的参数 fb_fix_screeninfo在应用编程中很少用到。它的结构体定义如下 可以使用 ioctl FBIOGET_FSCREENINFO 来读出这些信息但是很少用到。F表示fix确定的V表示var-variable 可变的
3.映射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(cant mmap\n);return -1;
} 第3行中screen_size 是整个 Framebuffer 的大小PROT_READ | PROT_WRITE 表示该区域可读、可写MAP_SHARED 表示该区域是共享的APP 写入数据时会直达驱动程序这个参数的更深刻理解可以参考后面驱动基础中讲到的 mmap 知识。我的理解是将驱动程序所控制的硬件的真实地址映射mmap到文件里linux一切皆文件然后往地址写数据对硬件进行操作
4.描点函数 能够在 LCD 上描绘指定像素后就可以写字、画图描点函数是基础。代码如下
1 void lcd_put_pixel(int x, int y, unsigned int color)
2 {
3 unsigned char *pen_8 fb_basey*line_widthx*pixel_width;
4 unsigned short *pen_16;
5 unsigned int *pen_32;
6
7 unsigned int red, green, blue;
8
9 pen_16 (unsigned short *)pen_8;
10 pen_32 (unsigned int *)pen_8;
11
12 switch (var.bits_per_pixel)
13 {
14 case 8:
15 {
16 *pen_8 color;
17 break;
18 }
19 case 16:
20 {
21 /* 565 */
22 red (color 16) 0xff;
23 green (color 8) 0xff;
24 blue (color 0) 0xff;
25 color ((red 3) 11) | ((green 2) 5) | (blue 3);
26 *pen_16 color;
27 break;
28 }
29 case 32:
30 {
31 *pen_32 color;
32 break;
33 }
34 default:
35 {
36 printf(cant surport %dbpp\n, var.bits_per_pixel);
37 break;
38 }
39 }
40 } 第 1 行中传入的 color 表示颜色它的格式永远是 0x00RRGGBB即 RGB888。当 LCD 是 16bpp 时要把 color 变量中的 R、G、B 抽出来再合并成 RGB565 格式。 第 3 行计算(x,y)坐标上像素对应的 Framebuffer 地址。 第 16 行对于 8bppcolor 就不再表示 RBG 三原色了这涉及调色板的概念color 是调色板的值。 第 2224 行先从 color 变量中把 R、G、B 抽出来。 第 25 行把 red、green、blue 这三种 8 位颜色值根据 RGB565 的格式只保留 red 中的高 5 位、green 中的高 6 位、blue 中的高 5 位组合成一个新的 16 位颜色值。 第 26 行把新的 16 位颜色值写入 Framebuffer。 第 31 行对于 32bpp颜色格式跟 color 参数一致可以直接写入Framebuffer。
5.随便画几个点
本程序的 main 函数在最后只是简单地画了几个点
/* 清屏: 全部设为白色 */
memset(fb_base, 0xff, screen_size);/* 随便设置出100个为红色 */
for (i 0; i 100; i)lcd_put_pixel(var.xres/2i, var.yres/2, 0xFF0000); 上机实验
板子用的是韦东山老师的imx6ull板由于我的屏幕被烧坏了这里就演示不了了。 在 Ubuntu 中编译程序先设置交叉编译工具链再执行以下命令arm-buildroot-linux-gnueabihf-gcc -o show_pixel show_pixel.c 交叉编译后在板子上就能运行应用程序了正常会在屏幕中心显示一条红色的线。