硬件软件系统方案应用范例智能车大赛技术支持学习园地关于我们恩智浦官网

GCC编译含有libdrm的c语言程序

云实验室案例-GCC编译

本文档涉及案例包括:

•        在云实验室开发板上直接编译包含DRM库的测试程序

为编译在开发板上运行的程序,根据编译机器的不同可简单分为两种:

1)使用ARM交叉编译工具链,在X86 PC机平台交叉编译

2)在开发板本机直接编译

其中,第一种方式常采用mesonyocto的方式进行编译,但编译相对比较复杂;但第二种方式编译快速便捷,尤其当客户的程序相对简单的时候。但当程序中涉及到了一些库的使用,比如DRMG2D等,又往往会找不到相应的参考资料快速实现编译。

本文的目的就是带领用户,快速在云实验室的开发板上,完成含有DRM库的测试程序基于gcc的编译任务。

相关知识:

GCC基本介绍

GCCGNU Compiler CollectionGNU编译器套件)是由GNU开发的编程语言编译器。

GCC 编译器对程序的编译下图所示,分为 4 个阶段:预处理(预编译 )、编译和优化、汇编和链接。GCC 的编译器可以将这 4 个步骤合并成一个。

预处理(-E):在这个阶段主要做了三件事: 展开头文件 、宏替换 、去掉注释行

  这个阶段需要 GCC 调用预处理器来完成,最终得到的还是源文件,文本格式

编译(-S):这个阶段需要 GCC 调用编译器对文件进行编译,最终得到一个汇编文件

汇编-c:这个阶段需要 GCC 调用汇编器对文件进行汇编,最终得到一个二进制文件

链接:这个阶段需要 GCC 调用链接器对程序需要调用的库进行链接,最终得到一个可执行的二进制文件

GCC基本用法

GCC最基本的命令是∶

gcc [options] [filenames]

其中options就是编译器所需要的参数,filenames给出相关的文件名称。常见选项包括:

gcc 编译选项 [options]选项的意义
-E预处理指定的源文件,不进行编译
-S编译指定的源文件,但是不进行汇编
-c编译、汇编指定的源文件,但是不进行链接
-o [file1] [file2] / [file2] -o [file1]将文件 file2 编译成文件 file1
-I directory (大写的 i)指定 include 包含文件的搜索目录
-l在程序编译的时候,指定使用的库
-Onn 的取值范围:0~3。编译器的优化选项的 4 个级别,-O0 表示没有优化,-O1 为缺省值,-O3 优化级别最高

对于gcc生成的文件,其不同后缀所代表的意义为:


说明gcc参数
.c源文件
.i预处理后的c文件-E
.s编译后的得到的汇编语言的源文件-S
.o汇编后得到的二进制文件-c

执行步骤:

1        预定板子

2        案例执行

2.1       DRM测试程序的编译

首先用户先在测试开发板上创建一个测试程序

vi drm_test.c

并将如下显示测试代码复制到文件中并保存:

// SPDX-License-Identifier: GPL-2.0+
 /* * 
 * Copyright 2024 NXP
 */
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
 
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
 
#include <drm/drm.h>
#include <drm/drm_mode.h>            
#include <xf86drm.h>
#include <xf86drmMode.h>
 
#define WIDTH  1280
#define HEIGHT 800
 
int main()
{
 int ret;
 int drm_fd;
 drm_fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC | O_NONBLOCK);
 
 ret = drmSetMaster(drm_fd);
 
 drmModeRes *res;
 drmModeConnector *conn;
 
 res = drmModeGetResources(drm_fd);
 conn = drmModeGetConnector(drm_fd, res->connectors[0]);
 
 drmModeEncoder *encoder;
 encoder = drmModeGetEncoder(drm_fd, conn->encoders[0]);
 
 int g_crtc_id;
 
 for (int j = 0; j < res->count_crtcs; j++) {
     if (encoder->possible_crtcs & (1 << j)) {
        g_crtc_id = res->crtcs[j];
     }
 }
 
 struct drm_mode_create_dumb creq;
 struct drm_mode_map_dumb mreq;
 
 memset(&creq, 0, sizeof(creq));
 creq.width = WIDTH;
 creq.height = HEIGHT;
 creq.bpp = 32; //ARGB
 
 ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq);
 
 __u32 buffer_id;
 
 ret = drmModeAddFB(drm_fd, WIDTH, HEIGHT, creq.bpp, creq.bpp,
            creq.pitch, creq.handle, &buffer_id);
 
 memset(&mreq, 0, sizeof(mreq));
 mreq.handle = creq.handle;
 ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq);
 
 __u32 *fb_base = mmap(0, creq.size, PROT_READ | PROT_WRITE, MAP_SHARED,
                      drm_fd, mreq.offset);
 
 __u32 color[1] = {0xFF0000};
 
 for (int j = 0; j < (creq.size / 4); j++)
     fb_base[j] = color[0];
 
 drmModeModeInfo mode;
 memcpy(&mode, &conn->modes[0], sizeof(mode));
 
 __u32 *conn_id = &(res->connectors[0]);
 
 ret = drmModeSetCrtc(drm_fd, g_crtc_id, buffer_id, 
                    0, 0, conn_id, 1, &mode);
 
 while(1);
 
 return 0;
}

请注意一定要申请带有DY1212-4856 LVDS显示屏的板子,因为不同的显示屏幕分辨率不同,所对应的测试程序会有所不同。

在代码中,我们使用DRM应用层的API函数,绘制了一张格式为ARGB的全红图像,分辨率为1280x720。首先尝试使用gcc命令对其直接进行编译,命令为:

gcc drm_test.c -o drm_test

格式为:

gcc [file_name] -o [output_file_name]

file_name: 要编译的c文件名

output_file_name:要输出的可执行文件名

可以看到,在编译的过程中不能够找到drm.h文件。

寻找头文件的位置,可以使用find命令,常见的find命令格式为:

find [path] -name [file_name] 

如在根文件目录( / )下寻找drm.h文件,可以通过如下命令:

find / -name drm.h 

能够看到如下log

里列出了i.MX系列的DRM相关头文件被放置的路径。因此考虑在gcc的命令中加入

-I 参数,加入include路径指定,如下所示:

gcc drm_test.c -I /usr/include/drm -o drm_test

随后看到如下log

以上log表明gcc找到了DRM的头文件,但存在大量没有定义的函数,可以猜测对应函数名可以在头文件中找到,但是函数的具体实现,也就是所谓的库并没有被链接进来,gcc无法找到对应的库文件。

gcc使用-l(小写的l)参数为编译添加库文件,小写的l后面紧跟的就是库的名称,该名称不包含前缀 lib 和后缀.so/.a。比如我们希望添加名为libg2d.so的库,就会使用参数-lg2d

gcc在链接时按照以下顺序查询库文件:

1.       -l 参数指定的目录。

2.       环境变量 LIBRARY_PATH 指定的目录。

3.       /usr/lib /lib(在 Unix-like 系统中)。

4.       系统默认的库目录。

如果库文件不在上述目录中,你需要使用 -L 参数指定库文件的路径。

例如,假设你有一个名为 libmylib.so 的库文件位于 /opt/mylib/lib 目录下,链接时应该这样写:

gcc -o myprogram myprogram.c -L/opt/mylib/lib -lmylib

命令中 -lmylib 告诉 GCC 链接名为 mylib 的库,它会查找名为 libmylib.so 的文件。

这里进入/usr/lib路径,搜索drm相关的库

在默认的路径下有名为libdrm.so的库,因此可以直接输入命令

gcc drm_test.c -I /usr/include/drm -ldrm -o test

即可完成编译:

如果将drm相关的库换到另一个文件夹下,如

此时就需要加入-L参数,指定drm库所在的位置也同样可以完成编译任务。

gcc drm_test.c -I /usr/include/drm -L/home/root/display_test -ldrm -o test

2.2       DRM测试程序的运行

   下一步直接运行该测试程序,首先关掉weston,使其释放显示的控制权限:

   可以看到weston UI界面被关掉,屏幕只显示一个console界面,右上角有一道横线:

然后直接运行该测试程序,可以发现屏幕显示为纯色的图片: