Linux LCD Frambuffer 基础介绍和使用

简介: Linux LCD Frambuffer 基础介绍和使用

1. 什么是Framebuffer?


Framebuffer 字面意思就是帧缓存的意思,即显存,里面保存着一帧图像。事实上,对于嵌入式系统而言。没有真正意义上的显存,Framebuffer 是通过内存模拟出来的。


LCD FrameBuffer 里的若干字节表示(具体根据驱动适配),LCD 屏幕上的一个像素点。


  • RGB888:32bpp,占4字节,分别是A8、R8、G8、B8,一般只用其中低24位,高8位表示透明度。


  • RGB565:16bpp,占2字节,分别是R5、G6、B5,比较常用的一种颜色


  • RGB555:很少用。


假设LCD屏幕分辨率是800x600,每个像素占4字节,那么framebuffer 大小就是:


800 x 600 x 4 = 960000 字节


framebuffer 显示原理如下:


image.png


假设需要设置 LCD 中坐标(x,y)处像素的颜色,首要要找到这个像素对应的内存,然后根据它的 BPP 值设置颜色。假设 fb_base 是 APP 执行 mmap 后得到的 Framebuffer 地址,如下图所示:


image.png


(x,y)像素起始地址=fb_base+(xres*bpp/8)y + xbpp/8


2. 为什么要有Frambuffer?


思考一个问题,为什么要用Framebuffer?


image.png


如图为LCD 驱动框架图:


从软件层面分析:framebuffer 起着承上启下的作用,向上,为应用层提供通用系统调用(open(),ioctl(),mmap());向下,联接LCD控制器,之前对硬件进行操作。


从硬件层面分析:用户只需要将数据写到framebuffer,硬件会自动刷新到屏幕上。


3. 常用接口和数据结构


3.1 常用接口


  1. 打开设备:open()系统调用。


通过 man 2 查看如下


image.png


函数说明:


image.png


  1. ioctl 系统调用:


image.png


函数原型:


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


函数说明:


  • fd :表示文件描述符;


  • request:表示与驱动程序交互的命令,用不同的命令控制驱动程序输出我们需要的数据;


  • … :表示可变参数arg,根据request命令,设备驱动程序返回输出的数据。


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


3)mmap 系统调用:



函数说明:



3.2 相关数据结构


fb_var_screeninfo:包含xres, yres, bits_per_pixel等信息,在后续会经常用到。


image.png


4. 如何在LCD 上描点?


4.1 LCD 显示原理


当我们需要显示一个字母‘A’时,是通过判断点阵的每一个位数值状态,来填充颜色,达到显示字符效果。其中‘1’表示一种颜色,‘0’表示填充另一种颜色。


如下图[1]8*16的点阵,只要有这个点阵,我们就可以在LCD上面描点,达到显示字符的效果。


image.png


4.2 Framebuffer 操作说明


framebuffer 操作如下流程:


  • 打开设备(open)


  • 获取屏幕参数信息(ioctl)


  • 分配显存(mmap)


  • 描点/写数据


  • 释放资源(unmmap)


  • 关闭设备(close)


int fd_fb;
struct fb_var_screeninfo var; /* Current var */
int screen_size;
unsigned char *fbmem;
unsigned int line_width;
unsigned int pixel_width;
int main(int argc, char *argv[])
{
    /*Step1: 打开设备 */
    fd_fb = open("/dev/fb0", O_RDWR);
 if (fd_fb < 0)
 {
  printf("can't open /dev/fb0\n");
  return -1;
 }
    /*Step2:获取设备参数信息 
    * xres:x 方向总像素
    * yres:y 方向总像素
    * bits_per_pixel:每个像素占多少位
    */
 if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))
 {
  printf("can't get var\n");
  return -1;
 }
    /* Step3: 计算线宽,分配显存 */
    /* line_width 每行占的字节 */
 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;
 fbmem = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
 if (fbmem == (unsigned char *)-1)
 {
  printf("can't mmap\n");
  return -1;
 }
 /* Step4: 清屏: 全部设为黑色 */
 memset(fbmem, 0, screen_size);
   /* Step5: 描点 */
 lcd_put_ascii(var.xres/2, var.yres/2, 'A'); /*在屏幕中间显示8*16的字母A*/
    /* Step6:释放资源*/
 munmap(fbmem , screen_size);
    /* Step7:关闭设备 */
 close(fd_fb);
 return 0;     
}


4.3 描点实现


描点的关键是计算点(x,y)位置对应的地址,然后直接指向fbmem即可向frambuffer 写入数据。


/**********************************************************************
 * 函数名称:lcd_put_pixel
 * 功能描述: 在LCD指定位置上输出指定颜色(描点)
 * 输入参数:x坐标,y坐标,颜色
 * 输出参数: 无
 * 返 回 值: 会
 ***********************************************************************/ 
void lcd_put_pixel(int x, int y, unsigned int color)
{
  /* 
  * 最主要的就是fbmem 
  * 计算(x,y)位置的偏移,然后指向fbmem,这块直接映射到framebuffer 内存里
  */
  unsigned char *pen_8 = fbmem+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)
  {
       /* 8bpp*/
    case 8:
    {
      *pen_8 = color;
      break;
    }
        /*16 bpp */
    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;
    }
  }
}


4.4 向 LCD 写入 英文


写入英文的前提:


  • 描点函数已实现;


  • 具备该英文字符的点阵数据;


/**********************************************************************
 * 函数名称:lcd_put_ascii
 * 功能描述: 在LCD指定位置上显示一个8*16的字符
 * 输入参数:x坐标,y坐标,ascii码
 * 输出参数: 无
 * 返 回 值: 无
 ***********************************************************************/ 
void lcd_put_ascii(int x, int y, unsigned char c)
{
    /* fontdata_8x16 8x16 英文点阵数据 */
    unsigned char *dots = (unsigned char *)&fontdata_8x16[c*16];
    int i, b;
    unsigned char byte;
   /* 8 x 16 的点阵, 16行 8列*/
   for (i = 0; i < 16; i++)
   {
     byte = dots[i];
     for (b = 7; b >= 0; b--)
     {
       if (byte & (1<<b))
       {
         /* show */
         lcd_put_pixel(x+7-b, y+i, 0xffffff); /* 白 */
       }
       else
       {
         /* hide */
         lcd_put_pixel(x+7-b, y+i, 0); /* 黑 */
        }
        }
 }
}


参考资料


[1]


如下图: 《嵌入式linux应用开发完全手册.韦东山》

相关文章
|
编解码 Linux
Linux MIPI DSI驱动调试笔记-LCD时序参数配置(三)
Linux MIPI DSI驱动调试笔记-LCD时序参数配置(三)
1050 0
|
编解码 Linux
Linux MIPI DSI驱动调试笔记-设备树DCS格式序列之配置LCD初始化代码(二)
Linux MIPI DSI驱动调试笔记-设备树DCS格式序列之配置LCD初始化代码(二)
1419 0
|
5月前
|
Linux
Linux交叉编译+粤嵌LCD实现三色图
Linux交叉编译+粤嵌LCD实现三色图
|
6月前
|
Ubuntu Linux Windows
Linux应用开发基础知识——LCD上的矢量字体Freetype(六)
Linux应用开发基础知识——LCD上的矢量字体Freetype(六)
326 0
Linux应用开发基础知识——LCD上的矢量字体Freetype(六)
|
开发框架 Ubuntu Linux
嵌入式Linux系列第12篇:LCD显示及QT
嵌入式Linux系列第12篇:LCD显示及QT
|
编解码 监控 Linux
嵌入式Linux MIPI接口LCD调试-关于DRM显示与应用调试的干货浓缩
嵌入式Linux MIPI接口LCD调试-关于DRM显示与应用调试的干货浓缩
1160 0
|
编解码 Linux 数据格式
Linux MIPI DSI LCD设备驱动开发调试细节学习笔记(一)
Linux MIPI DSI LCD设备驱动开发调试细节学习笔记(一)
1628 0
|
Linux API
Linux驱动分析之LCD驱动架构
在Linux设备中,LCD显示采用了帧缓冲(framebuffer)技术,所以LCD驱动也叫Framebuffer驱动,所以LCD驱动框架就是围绕帧缓冲展开工作。帧缓冲(framebuffer)是Linux系统为显示设备提供的一个接口,它将显示缓冲区抽象出来,屏蔽图像硬件的底层差异,允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。对于帧缓冲设备而言,只要在显示缓冲区中与显示点对应的区域写入颜色值,对应的颜色会自动在屏幕上显示。帧缓冲为标准字符设备, 主设备号为29,对应于/dev/fbn。
|
编解码 Linux 芯片
linux LCD 驱动框架分析
linux LCD 驱动框架分析
200 0