modetest 是由 libdrm 提供的测试程序,可以查询显示设备的特性,进行基本的显示测试,以及设置显示的模式。
我们可以借助该工具来学习 Linux DRM 应用编程,另外为了深入分析 Rockchip DRM driver,有必要先了解一下这个工具的使用方法和内部实现。
1、开发板:ATK-DLRK3568 开发板
2、环境:ubuntu20.0 正点原子网盘提供的版本
本人没自己编译过,编译过程只供参考,正点的板子自带了
编写一个libdrm的测试程序较为复杂,这里我们使用libdrm官方的测试工具来进行测试,我们可以在这里下载源码并进行交叉编译出测试工具,以供在开发板上使用: libdrm .
新版的libdrm使用meson+ninja的构建方式,而不是老版的autotools,没有基础的同学构建新版libdrm会比较痛苦。 建议直接使用我们给大家编译好的测试程序,测试程序位于配套例程 linux_driver/framework_drm/modetest。
如果要自己编译libdrm,可以参考下面命令:
git clone https://gitlab.freedesktop.org/mesa/drm
sudo apt -y install python3-pip cmake git ninja-build
python3 -m pip install meson /*安装之后,重启板卡*/
meson . build && ninja -C build
编译之后在build/tests/modetest/下会有modetest程序, 对libdrm测试程序感兴趣的同学,可以下载libdrm源码解压,在其目录/drm/tests/modetest/下,查看modetest.c文件,此为测试程序源码。
modetest在bin目录下,板子上电后打开终端进入bin目录
cd /bin
modetest -h
modetest -M rockchip
参数说明:
-M
:用于指定访问 rockchip DRM driver关键内容:
modetest -M rockchip | cut -f1 | grep -E ^[0-9A-Z]\|id
正点原子的RK3568提供了一路的MIPI DSI 接口,所以测试MIPI
modetest -M rockchip -s 163@115:720x1280
直接上代码,目录根据yolov5的那个程序修改的,替换mian.cc文件后需要添加drm库和头文件。
// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*-------------------------------------------
Includes
-------------------------------------------*/
#include
#include
#include
#include
#include
#include
#include
#include "drm_func.h"
#include "rga_func.h"
#include "rknn_api.h"
#include "yolo.h"
#include "RgaUtils.h"
#include "im2d.h"
#include "opencv2/core/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include
#include "rga.h"
#include
#include
#define RED 0XFF0000
#define GREEN 0X00FF00
#define BLUE 0X0000FF
#define BLACK 0X000000
#define WHITE 0XFFFFFF
#define BLACK_BLUE 0X123456
struct drm_device {
uint32_t width; //显示器的宽的像素点数量
uint32_t height; //显示器的高的像素点数量
uint32_t pitch; //每行占据的字节数
uint32_t handle; //drm_mode_create_dumb的返回句柄
uint32_t size; //显示器占据的总字节数
uint32_t *vaddr; //mmap的首地址
uint32_t fb_id; //创建的framebuffer的id号
struct drm_mode_create_dumb create ; //创建的dumb
struct drm_mode_map_dumb map; //内存映射结构体
};
drmModeConnector *conn; //connetor相关的结构体
drmModeRes *res; //资源
uint32_t conn_id; //connetor的id号
uint32_t crtc_id; //crtc的id号
int fd; //文件描述符
#define RED 0XFF0000
#define GREEN 0X00FF00
#define BLUE 0X0000FF
uint32_t color_table[6] = {RED,GREEN,BLUE,BLACK,WHITE,BLACK_BLUE};
uint32_t plane_id[3]; //图层id数组
struct drm_device buf;
static int drm_create_fb(struct drm_device *bo)
{
/* create a dumb-buffer, the pixel format is XRGB888 */
bo->create.width = bo->width;
bo->create.height = bo->height;
bo->create.bpp = 32;
/* handle, pitch, size will be returned */
drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &bo->create);
/* bind the dumb-buffer to an FB object */
bo->pitch = bo->create.pitch;
bo->size = bo->create.size;
bo->handle = bo->create.handle;
drmModeAddFB(fd, bo->width, bo->height, 24, 32, bo->pitch,
bo->handle, &bo->fb_id);
//每行占用字节数,共占用字节数,MAP_DUMB的句柄
printf("pitch = %d ,size = %d, handle = %d \n",bo->pitch,bo->size,bo->handle);
/* map the dumb-buffer to userspace */
bo->map.handle = bo->create.handle;
drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &bo->map);
bo->vaddr = (uint32_t*)mmap(0, bo->create.size, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, bo->map.offset);
/* initialize the dumb-buffer with white-color */
memset(bo->vaddr, 0xff,bo->size);
return 0;
}
static void drm_destroy_fb(struct drm_device *bo)
{
struct drm_mode_destroy_dumb destroy = {};
drmModeRmFB(fd, bo->fb_id);
munmap(bo->vaddr, bo->size);
destroy.handle = bo->handle;
drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy);
}
int drm_init()
{
//打开drm设备,设备会随设备树的更改而改变,多个设备时,请留一下每个屏幕设备对应的drm设备
fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);
if(fd < 0){
printf("wrong\n");
return 0;
}
//获取drm的信息
res = drmModeGetResources(fd);
crtc_id = res->crtcs[1];
conn_id = res->connectors[1];
//打印CRTCS,以及conneter的id
printf("crtc = %d , conneter = %d\n",crtc_id,conn_id);
conn = drmModeGetConnector(fd, conn_id);
buf.width = conn->modes[0].hdisplay;
buf.height = conn->modes[0].vdisplay;
//打印屏幕分辨率
printf("width = %d , height = %d\n",buf.width,buf.height);
//创建framebuffer层
drm_create_fb(&buf);
//设置CRTCS
drmModeSetCrtc(fd, crtc_id, buf.fb_id,
0, 0, &conn_id, 1, &conn->modes[0]);
return 0;
}
void drm_exit()
{
drm_destroy_fb(&buf);
drmModeFreeConnector(conn);
drmModeFreeResources(res);
close(fd);
}
int main(int argc, char **argv)
{
int i,j;
drm_init();
sleep(2);
printf("display colour\n");
//显示三色
for(j=0;j<3;j++){
for(i =j*buf.width*buf.height/3;i< (j+1)*buf.width*buf.height/3;i++)
buf.vaddr[i] = color_table[j];
}
getchar();
printf("getchar\n");
drm_exit();
exit(0);
}
运行结果
程序需要注意一点。
//获取drm的信息
res = drmModeGetResources(fd);
crtc_id = res->crtcs[1];
conn_id = res->connectors[1];
接下来,实现采集视频显示。