网站使用微软雅黑,开网页速度慢,临沂做商城网站的公司,临沂龙文网站建设1.LCD 操作原理
在 Linux 系统中通过 Framebuffer 驱动程序来控制 LCD。Frame 是帧的意思#xff0c;buffer 是缓冲的意思#xff0c;这意味着 Framebuffer 就是一块内存#xff0c;里面保存着一帧图像。Framebuffer 中保存着一帧图像的每一个像素颜色值#xff0c;假设 L…1.LCD 操作原理
在 Linux 系统中通过 Framebuffer 驱动程序来控制 LCD。Frame 是帧的意思buffer 是缓冲的意思这意味着 Framebuffer 就是一块内存里面保存着一帧图像。Framebuffer 中保存着一帧图像的每一个像素颜色值假设 LCD 的分辨率是 1024x768每一个像素的颜色用 32 位来表示那么 Framebuffer 的大小就是1024x768x32/83145728 字节。LCD的操作原理 驱动程序设置好 LCD 控制器 根据 LCD 的参数设置 LCD 控制器的时序、信号极性 根据 LCD 分辨率、 BPP 分配 Framebuffer 。 APP 使用 ioctl 获得 LCD 分辨率、 BPP APP 通过 mmap 映射 Framebuffer 在 Framebuffer 中写入数据 bpp每个像素用多少位来表示它的颜色 假设需要设置 LCD 中坐标 (x,y) 处像素的颜色首要要找到这个像素对应的内存然后根据它的 BPP 值设置颜色。假设 fb_base 是 APP 执行 mmap 后得到的 Framebuffer 地址如图 可以用以下公式算出 (x,y) 坐标处像素对应的 Framebuffer 地址 (xy)像素起始地址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 位偏移来确定使用哪一种格式。 2.涉及的 API 函数 open 打开 LCD 设备节点 ioctl 获取LCD黑色版分辨率等参数 mmap 映射 Framebuffer 最后实现描点函数。 2.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); 函数说明 pathname 表示打开文件的路径 Flags 表示打开文件的方式常用的有以下 6 种 O_RDWR 表示可读可写方式打开 ; O_RDONLY 表示只读方式打开 ; O_WRONLY 表示只写方式打开 ; O_APPEND 表示如果这个文件中本来是有内容的则新写入的内容会接续到原来内容的后面; O_TRUNC 表示如果这个文件中本来是有内容的则原来的内容会被丢弃截断 O_CREAT 表示当前打开文件不存在我们创建它并打开它通常与 O_EXCL 结合使用当没有文件时创建文件有这个文件时会报错提醒我们 Mode 表示创建文件的权限只有在 flags 中使用了 O_CREAT 时才有效否则忽略。 返回值打开成功返回文件描述符失败将返回 -1 。 2.2 ioctl 函数
在 Ubuntu 中执行“ man ioctl ”可以看到 ioctl 函数的说明。 头文件 #include sys/ioctl.h 函数原型 int ioctl(int fd, unsigned long request, ...); 函数说明 fd 表示文件描述符 request 表示与驱动程序交互的命令用不同的命令控制驱动程序输出我们需要的数据 … 表示可变参数 arg 根据 request 命令设备驱动程序返回输出的数据。 返回值打开成功返回文件描述符失败将返回 -1 。 ioctl 的作用非常强大、灵活。不同的驱动程序内部会实现不同的 ioctl APP 可以使用各种 ioctl 跟驱动程序交互可以传数据给驱动程序也可以从驱动程序中读出数据。 2.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); 函数说明 addr 表示指定映射的內存起始地址通常设为 NULL 表示让系统自动选定地址并在成功映射后返回该地址 length 表示将文件中多大的内容映射到内存中 prot 表示映射区域的保护方式可以为以下 4 种方式的组合 PROT_EXEC 映射区域可被执行 PROT_READ 映射区域可被读出 PROT_WRITE 映射区域可被写入 PROT_NONE 映射区域不能存取 Flags 表示影响映射区域的不同特性常用的有以下两种 MAP_SHARED 表示对映射区域写入的数据会复制回文件内原来的文件会改变。 MAP_PRIVATE 表示对映射区域的操作会产生一个映射文件的复制对此区域的任何修改都不会写回原来的文件内容中。 返回值若成功映射将返回指向映射的区域的指针失败将返回 -1 。 3.Framebuffer 程序分析
3.1 打开设备节点 fd_fb open(/dev/fb0, O_RDWR);//打开设备节点
if (fd_fb 0)
{printf(cant open /dev/fb0\n);return -1;
} 3.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 screen info获得屏幕的可变信息当然也可以使用 FBIOPUT_VSCREENINFO 来调整这些参数但是很少用到。 对于固定的参数 fb_fix_screeninfo在应用编程中很少用到。它的结构体定义如下 可以使用 ioctl FBIOGET_FSCREENINFO 来读出这些信息但是很少用到。 3.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;
} screen_size 是整个 Framebuffer 的大小 PROT_READ | PROT_WRITE 表示该区域可读、可写 MAP_SHARED 表示该区域是共享的 APP 写入数据时会直达驱动程序这个参数的更深刻理解可以参考后面驱动基础中讲到的 mmap 知识。 3.4 描点函数 能够在 LCD 上描绘指定像素后就可以写字、画图描点函数是基础。代码如下 //传入的 color 表示颜色它的格式永远是 0x00RRGGBB即 RGB888。当 LCD 是 16bpp 时要把 color 变量中的 R、G、B 抽出来再合并成 RGB565 格式
void lcd_put_pixel(int x, int y, unsigned int color)
{unsigned char *pen_8 fb_basey*line_widthx*pixel_width;//计算要显示的坐标fb_base(原地址)y*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;//对于 8bppcolor 就不再表示 RBG 三原色了这涉及调色板的概念color 是调色板的值。break;}case 16:{/* 565 */red (color 16) 0xff; //32位red数据是16~24 24~32没用green (color 8) 0xff; //32位green数据是8~16blue (color 0) 0xff; //32位blue数据是0~8//总共16位:红保留高5位放在11~16绿保留高6位放在5~11蓝保留高5位放在0~5color ((red 3) 11) | ((green 2) 5) | (blue 3);*pen_16 color;//将计算的位置坐标显示该颜色break;}case 32:{*pen_32 color;//对于 32bpp颜色格式跟 color 参数一致可以直接写入Framebuffer。break;}default:{printf(cant surport %dbpp\n, var.bits_per_pixel);break;}}
} 3.5 随便画几个点的完整程序 #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;//传入的 color 表示颜色它的格式永远是 0x00RRGGBB即 RGB888。当 LCD 是 16bpp 时要把 color 变量中的 R、G、B 抽出来再合并成 RGB565 格式
void lcd_put_pixel(int x, int y, unsigned int color)
{unsigned char *pen_8 fb_basey*line_widthx*pixel_width;//计算要显示的坐标fb_base(原地址)y*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;//对于 8bppcolor 就不再表示 RBG 三原色了这涉及调色板的概念color 是调色板的值。break;}case 16:{/* 565 */red (color 16) 0xff; //32位red数据是16~24 24~32没用green (color 8) 0xff; //32位green数据是8~16blue (color 0) 0xff; //32位blue数据是0~8//总共16位:红保留高5位放在11~16绿保留高6位放在5~11蓝保留高5位放在0~5color ((red 3) 11) | ((green 2) 5) | (blue 3);*pen_16 color;//将计算的位置坐标显示该颜色break;}case 32:{*pen_32 color;//对于 32bpp颜色格式跟 color 参数一致可以直接写入Framebuffer。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;
}