编写使用多buffer的应用程序
文章目录
一、 编写一个支持单buffer、多buffer的APP
循环显示整屏幕的红、绿、蓝、黑、白。
二、 编译程序
2.1 设置工具链
-
对于IMX6ULL
export ARCH=arm export CROSS_COMPILE=arm-linux-gnueabihf- export PATH=$PATH:/work/imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin
2.2 编译
#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.h>
#include <time.h>
static int fd_fb;
static struct fb_fix_screeninfo fix; /* Current fix */
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;
/*******************************************************************************
* @FunctionName: lcd_put_pixel
* @Author: Hilbert
* @DateTime: 2023年2月23日T12:38:51+0800
* @Purpose: 在LCD指定位置上输出指定颜色(描点)
* @param: x坐标,y坐标,颜色
*******************************************************************************/
void lcd_put_pixel(void *fb_base, int x, int y, unsigned int color)
{
unsigned char *pen_8 = fb_base+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)
{
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("can't surport %dbpp\n", var.bits_per_pixel);
break;
}
}
}
void lcd_draw_screen(void *fb_base, unsigned int color)
{
int x, y;
for (x = 0; x < var.xres; x++)
for (y = 0; y < var.yres; y++)
lcd_put_pixel(fb_base, x, y, color);
}
/* ./multi_framebuffer_test single
* ./multi_framebuffer_test double
*/
int main(int argc, char **argv)
{
int i;
int ret;
int nBuffers;
int nNextBuffer = 1;
char *pNextBuffer;
unsigned int colors[] = {0x00FF0000, 0x0000FF00, 0x000000FF, 0, 0x00FFFFFF}; /* 0x00RRGGBB */
struct timespec time;
time.tv_sec = 0;
time.tv_nsec = 100000000;
if (argc != 2)
{
printf("Usage : %s <single|double>\n", argv[0]);
return -1;
}
fd_fb = open("/dev/fb0", O_RDWR);
if (fd_fb < 0)
{
printf("can't open /dev/fb0\n");
return -1;
}
if (ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix))
{
printf("can't get fix\n");
return -1;
}
if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))
{
printf("can't 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;
nBuffers = fix.smem_len / screen_size;
printf("nBuffers = %d\n", nBuffers);
fb_base = (unsigned char *)mmap(NULL , fix.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
if (fb_base == (unsigned char *)-1)
{
printf("can't mmap\n");
return -1;
}
if ((argv[1][0] == 's') || (nBuffers == 1))
{
while (1)
{
/* use single buffer */
for (i = 0; i < sizeof(colors)/sizeof(colors[0]); i++)
{
lcd_draw_screen(fb_base, colors[i]);
nanosleep(&time, NULL);
}
}
}
else
{
/* use double buffer */
/* a. enable use multi buffers */
var.yres_virtual = nBuffers * var.yres;
ioctl(fd_fb, FBIOPUT_VSCREENINFO, &var);
while (1)
{
for (i = 0; i < sizeof(colors)/sizeof(colors[0]); i++)
{
/* get buffer */
pNextBuffer = fb_base + nNextBuffer * screen_size;
/* set buffer */
lcd_draw_screen(pNextBuffer, colors[i]);
/* switch buffer */
var.yoffset = nNextBuffer * var.yres;
ioctl(fd_fb, FBIOPAN_DISPLAY, &var);
ret = 0;
ioctl(fd_fb, FBIO_WAITFORVSYNC, &ret);
nNextBuffer = !nNextBuffer;
nanosleep(&time, NULL);
}
}
}
munmap(fb_base , screen_size);
close(fd_fb);
return 0;
}
设置好工具链后,把use_multi_framebuffer
上传到Ubuntu,在该目录下执行make
即可
三、上机测试
3.1 恢复内核使用自带的LCD驱动
- 恢复驱动程序:修改
drivers/video/fbdev/Makefile
,恢复内核自带的mxsfb.c,如下:
obj-$(CONFIG_FB_MXS) += mxsfb.o
#obj-$(CONFIG_FB_MXS) += lcd_drv.o
-
编译设备树
- 把设备村文件复制到内核arch/arm/boo/dts目录
-
重新编译内核、设备树
make zImage make dtbs
-
替换内核、设备树
- 把编译出来的
arch/arm/boot/zImage
、arch/arm/boot/dts/imx6ull-14x14.dtb
- 放到开发板的/boot目录
- 把编译出来的
3.2 禁止开发板自带的GUI程序
在开发板上执行以下命令:
[original@ubuntu:~]# mv /etc/init.d/S99myirhmi2 /etc/
[original@ubuntu:~]# reboot
3.3 把测试程序放到板子上、执行
以下命令在开发板中执行。
-
挂载NFS
-
vmware使用NAT(假设windowsIP为192.168.1.100)
[original@ubuntu:~]# mount -t nfs -o nolock,vers=3,port=2049,mountport=9999 192.168.1.100:/work/nfs_rootfs /mnt
-
vmware使用桥接,或者不使用vmware而是直接使用服务器:假设Ubuntu IP为192.168.19.25
[original@ubuntu:~]# mount -t nfs -o nolock,vers=3 192.168.96.25:/work/nfs_rootfs /mnt
-
-
复制、执行程序
[original@ubuntu:~]# cp /mnt/multi_framebuffer_test /bin [original@ubuntu:~]# multi_framebuffer_test single 或 multi_framebuffer_test double
四、 LCD自动黑屏
为了省电,LCD在10分钟左右会自动黑屏。
如果你正在运行multi_framebuffer_test程序,可能会有如下提示(以IMX6ULL为例):
[ 961.147548] mxsfb 21c8000.lcdif: can't wait for VSYNC when fb is blank
这表示:当屏幕为blank(黑屏)时,无法等待VSYNC。
我们可以禁止LCD自动黑屏,执行以下命令即可:
#close lcd sleep
echo -e "\033[9;0]" > /dev/tty1
echo -e "\033[?25l" > /dev/tty1
致谢
以上笔记源自
韦东山
老师的视频课程,感谢韦老师,韦老师是嵌入式培训界一股清流,为嵌入式linux开发点起的星星之火,也愿韦老师桃李满园。聚是一团火,散是满天星!
在这样一个速食的时代,坚持做自己,慢下来,潜心琢磨,心怀敬畏,领悟知识,才能向下扎到根,向上捅破天,背着世界往前行!
仅此向嵌入行业里的每一个认真做技术的从业者致敬!