20. 屏幕显示¶
20.1. Framebuffer介绍¶
Framebuffer是用一个视频输出设备从包含完整的帧数据的一个内存缓冲区中来驱动一个视频显示设备。 也就是说Framebuffer是一块内存保存着一帧的图像,向这块内存写入数据就相当于向屏幕中写入数据, 如果使用32位的数据来表示一个像素点(使用BBP表示),假设屏幕的显示频分辨率为1920x1080, 那么Framebuffer所需要的内存为1920x1080x32÷4=8,294,400字节约等于7.9M。
简单来说Framebuffer把屏幕上的每个点映射成一段线性内存空间, 程序可以简单的改变这段内存的值来改变屏幕上某一点的颜色。
20.2. 使能lcd相关设备树插件¶
野火imx6ull提供了很多的设备树插件,用于控制板子上外设的开启,默认状态下lcd相关插件是开启的, 可查看修改 /boot/uEnv.txt 文件内容以确认是否开启了imx-fire-lcd.dtbo设备树插件,如下所示
1 | dtoverlay=/usr/lib/linux-image-4.19.35-imx6/overlays/imx-fire-lcd.dtbo
|
20.2.1. 简单测试¶
前面提到可以理解Framebuffer把屏幕上的每个点映射成一段线性内存空间, 程序可以简单的改变这段内存的值来改变屏幕上某一点的颜色。 可以使用以下命令不断地往 /dev/fb0 写入随机数据,使得屏幕花屏。
1 | cat /dev/urandom > /dev/fb0
|
20.2.2. fbset工具使用¶
1 2 3 | #安装fbset工具
sudo apt update
sudo apt install fbset
|
安装完fbset工具之后可以使用 fbset命令查看显示屏相关参数
1 2 3 4 5 6 7 8 9 | root@npi:/home/zhan# fbset
mode "800x480-59"
# D: 27.000 MHz, H: 31.070 kHz, V: 59.069 Hz
geometry 800 480 800 480 16
timings 37037 46 22 23 22 1 1
sync 0x40000000
rgba 5/11,6/5,5/0,0/0
endmode
|
fbset工具很强大不仅可以插件显示设备相关参数,同时也能够在用户空间修改lcd参数。
20.3. Framebuffer相关数据结构与ioctl函数¶
20.3.1. 获取LCD参数¶
LCD驱动程序给APP提供2类参数:可变的参数 fb_var_screeninfo、固定的参数 fb_fix_screeninfo。 编写应用程序时主要关心可变参数,其结构体定义如下:
20.3.1.1. fb_var_screeninfo结构体¶
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 | struct fb_var_screeninfo {
__u32 xres; /* visible resolution */
__u32 yres;
__u32 xres_virtual; /* virtual resolution */
__u32 yres_virtual;
__u32 xoffset; /* offset from virtual to visible */
__u32 yoffset; /* resolution */
__u32 bits_per_pixel; /* guess what */
__u32 grayscale; /* 0 = color, 1 = grayscale, */
/* >1 = FOURCC */
struct fb_bitfield red; /* bitfield in fb mem if true color, */
struct fb_bitfield green; /* else only length is significant */
struct fb_bitfield blue;
struct fb_bitfield transp; /* transparency */
__u32 nonstd; /* != 0 Non standard pixel format */
__u32 activate; /* see FB_ACTIVATE_* */
__u32 height; /* height of picture in mm */
__u32 width; /* width of picture in mm */
__u32 accel_flags; /* (OBSOLETE) see fb_info.flags */
/* Timing: All values in pixclocks, except pixclock (of course) */
__u32 pixclock; /* pixel clock in ps (pico seconds) */
__u32 left_margin; /* time from sync to picture */
__u32 right_margin; /* time from picture to sync */
__u32 upper_margin; /* time from sync to picture */
__u32 lower_margin;
__u32 hsync_len; /* length of horizontal sync */
__u32 vsync_len; /* length of vertical sync */
__u32 sync; /* see FB_SYNC_* */
__u32 vmode; /* see FB_VMODE_* */
__u32 rotate; /* angle we rotate counter clockwise */
__u32 colorspace; /* colorspace for FOURCC-based modes */
__u32 reserved[4]; /* Reserved for future compatibility */
};
|
仅仅编写应用程序,我们需要关注的成员只有以下几个
xres:用于表示lcd的x坐标长度。
yres:用于表示lcd的y坐标长度。
bits_per_pixel:用于表示屏幕的bbp
20.3.1.2. fb_fix_screeninfo¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | struct fb_fix_screeninfo {
char id[16]; /* identification string eg "TT Builtin" */
unsigned long smem_start; /* Start of frame buffer mem */
/* (physical address) */
__u32 smem_len; /* Length of frame buffer mem */
__u32 type; /* see FB_TYPE_* */
__u32 type_aux; /* Interleave for interleaved Planes */
__u32 visual; /* see FB_VISUAL_* */
__u16 xpanstep; /* zero if no hardware panning */
__u16 ypanstep; /* zero if no hardware panning */
__u16 ywrapstep; /* zero if no hardware ywrap */
__u32 line_length; /* length of a line in bytes */
unsigned long mmio_start; /* Start of Memory Mapped I/O */
/* (physical address) */
__u32 mmio_len; /* Length of Memory Mapped I/O */
__u32 accel; /* Indicate to driver which */
/* specific chip/card we have */
__u16 capabilities; /* see FB_CAP_* */
__u16 reserved[2]; /* Reserved for future compatibility */
};
|
对于固定的参数 fb_fix_screeninfo,在应用编程中很少用到,主要包含比如图形硬件上实际的帧缓存空间的大小、 能否硬件加速等信息。其中 smem_start 表示的是Framebuffer的物理地址。
20.3.2. ioctl函数¶
需要使用ioctl函数来获取Framebuffer的结构体信息,其函数原型如下
1 | int ioctl(int fd, unsigned long request, ...);
|
request常用的参数如下
FBIOGET_VSCREENINFO |
获取可变参数fb_var_screeninfo结构体 |
FBIOGET_FSCREENINFO |
获取固定参数fb_fix_screeninfo结构体 |
FBIOPUT_VSCREENINFO |
设置可变参数fb_var_screeninfo结构体 |
20.4. LCD显示控制¶
根据以上信息可编写相关LCD相关程序,如下所示
代码较长复制粘贴有可能会排序不正常,可从这里下载 framebuffer.c
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 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | #include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <linux/fb.h>
#include <linux/input.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
//RGB565 颜色
#define RED 0xF800 //红色
#define GREED 0x07E0 //绿色
#define BLUE 0x001f //蓝色
#define WTITE 0xFFFF //白色
#define BLACK 0x0000 //黑色
static struct fb_var_screeninfo var; /* LCD可变参数 */
static unsigned char *fb_base; /* Framebuffer映射基地址 */
static int screen_size; /* 整个Framebuffer大小*/
void lcd_show_pixel_rgb565(int x,int y, unsigned short color);
void lcd_fill_rgb565(unsigned short color);
int get_sta();
int main(int argc, char * argv[])
{
int i;
int fd_fb; /* 文件句柄 */
fd_fb = open("/dev/fb0", O_RDWR); //打开LCD文件
if(fd_fb < 0)
{
perror("/dev/fb0");
exit(-1);
}
//获取屏幕可变参数相关信息
if (ioctl(fd_fb,FBIOGET_VSCREENINFO,&var))
{
printf("can't get fb_var_screeninfo \n");
goto err1;
}
printf("X:%d Y:%d bbp:%d\n",var.xres,var.yres,var.bits_per_pixel);
screen_size = var.xres *var.yres *var.bits_per_pixel /8; //整个Framebuffer大小
//建立内存映射 方便控制
fb_base = (unsigned char*)mmap(NULL,screen_size,PROT_READ|PROT_WRITE,MAP_SHARED, fd_fb,0);
if(fb_base == NULL)
{
printf("can't mmap Framebuffer\n");
goto err1;
}
//屏幕闪烁
lcd_fill_rgb565(WTITE);
lcd_fill_rgb565(RED);
sleep(1);
lcd_fill_rgb565(GREED);
sleep(1);
lcd_fill_rgb565(BLUE);
sleep(1);
lcd_fill_rgb565(WTITE);
get_sta();
//显示线条
for(i = 0;i<100;i++)
{
lcd_show_pixel_rgb565(var.xres/2,var.yres/2+i,RED);
}
//解除内存映射关系
munmap(fb_base,screen_size);
close(fd_fb);
return 0;
err1:
close(fd_fb);
return -1;
}
int get_sta()
{
int fd_ts;
unsigned int x,y;
struct input_event buf;
fd_ts=open("/dev/input/event2",O_RDWR);
if(fd_ts < 0)
{
printf("open ts error\r\n");
return -1;
}
while(1)
{
read(fd_ts, &buf, sizeof buf);
if(buf.type == EV_ABS|| buf.type==EV_KEY)
{
if(buf.code == ABS_X)
{
x = buf.value;
}
if(buf.code == ABS_Y)
{
y = buf.value;
}
}
if(buf.code == ABS_X)
{
x = buf.value;
}
if(buf.code == ABS_Y)
{
y = buf.value
}
lcd_show_pixel_rgb565(x,y,BLUE);
lcd_show_pixel_rgb565(x-1,y-1,BLUE);
lcd_show_pixel_rgb565(x-1,y+1,BLUE);
lcd_show_pixel_rgb565(x+1,y+1,BLUE);
lcd_show_pixel_rgb565(x+1,y-1,BLUE);
lcd_show_pixel_rgb565(x,y-1,BLUE);
lcd_show_pixel_rgb565(x,y+1,BLUE);
lcd_show_pixel_rgb565(x+1,y,BLUE);
lcd_show_pixel_rgb565(x-1,y,BLUE);
}
return 0;
}
//画点函数
void lcd_show_pixel_rgb565(int x,int y, unsigned short color)
{
//确定坐标点所在的内存
void *pen = fb_base + ((var.xres * y + x) * (var.bits_per_pixel/8));
if( (x > var.xres) || (y > var.yres) )
{
return ;
}
else
{
*((unsigned short*)pen) = color;
}
}
//屏幕填充函数
void lcd_fill_rgb565(unsigned short color)
{
int i;
unsigned short *base = (unsigned short*)fb_base;
for(i = 0 ;i < (screen_size/2); i++)
{
*(base+i) = color;
}
}
|
20.5. 编译运行¶
将代码放到板子上(可以wget,NFS,TFTP等方法),然后在板子上运行如下代码
1 2 3 | sudo gcc framebuffer.c -o LCD_show
sudo chmod 777 LCD_show
./LCD_show
|
程序现象也是相对简单,红色、绿色、蓝色、白色各显示一遍之后,如果是触摸屏可在上进行绘制。

如果要修改LCD的引脚可参考 修改lcd相关设备树 小节
20.6. fire-config修改液晶参数¶
在终端执行sudo fire-config命令,选择“Device”项,如下图:

选择“LCD”项

使用空格选中“C2 General LCD”项。

选中回车之后即可根据显示屏说明文档配置修改LCD相关配置信息。
