RK3568笔记九: DRM显示摄像头

news2024/9/28 3:22:08

若该文为原创文章,转载请注明原文出处。

一、介绍

学习DRM的目的是想做类似NVR显示多路实时流,通过勇哥(Marc)的指导,大概流程是通过Zlmedia拉流,RK3568的MPP解码,DRM显示,可以使用HDMI或DIS屏幕,所以为了后面的实现,先测试摄像头采集DRM显示。

二、移植过程

测试过程在笔记八里,如有不懂的,请看笔迹八。

移植过程参考GitHub - MontaukLaw/cvv_test

里面有DRM显示两个屏的例子。

程序是以正点原子里的rknn_yolov5_demo例程修改。主要修改几个地方。

大概流程:

1、初始化屏幕  drm_dis_init();

2、opencv打开摄像头cv::VideoCapture cap(0);

3、循环读数据并显示

cap.read(orig_img);

draw_lcd_screen_rgb_960((uint8_t *)img.data, 720 * 1280 * 3);

代码:

main.cc

// 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <dlfcn.h>
#include <vector>
#include <string>

#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 <opencv2/opencv.hpp>
#include "rga.h"
#include <xf86drm.h>
#include <xf86drmMode.h>
#include "dev.h"
#include "bo.h"


static sp_dev *mDev;
#define OUTPUT_DEVICE_LCD 1

static drmModeConnectorPtr lcdConnectorPtr = nullptr;
static struct sp_crtc *lcdCRPtr;
static drmModeEncoderPtr lcdEncoderPtr = nullptr;
static uint32_t lcdCrtcId = 0;
static drmModeModeInfoPtr lcdModInfoPtr;


static void get_connector(uint8_t outpuDevice)
{
    int i, j = 0;
    int ret = 0;

    printf("mDev->num_connectors = %d\n", mDev->num_connectors);
    for (j = 0; j < mDev->num_connectors; j++)
    {
        // name 是分辨率信息
        printf("connector name:%d\n", j);
        printf("connector_type:%d\n", j);
        printf("connector_type_id:%d\n", j);
        printf("connector status:%d\n", j);
        // 对应不同的输出设备, 指定不同的connector跟encoder
        if (outpuDevice == OUTPUT_DEVICE_LCD)
        {
            if (mDev->connectors[j]->connector_type == DRM_MODE_CONNECTOR_DSI &&
                mDev->connectors[j]->connection == DRM_MODE_CONNECTED)
            {
                lcdConnectorPtr = mDev->connectors[j];
            }
        }
       
    }
}

static void get_encoder(uint8_t outpuDevice)
{
    int i;
    for (i = 0; i < mDev->num_encoders; i++)
    {
        if (outpuDevice == OUTPUT_DEVICE_LCD)
        {
            if (mDev->encoders[i]->encoder_type == DRM_MODE_ENCODER_DSI)
            {
                lcdEncoderPtr = mDev->encoders[i];
                lcdCrtcId = lcdEncoderPtr->crtc_id;
            }
        }
       
    }
}

static void get_crtc()
{
    int j;

    printf("lcd crtc id:%d\n", lcdCrtcId);

    for (j = 0; j < mDev->num_crtcs; j++)
    {

        printf("encoderPtr->crtc_id:%d\n", mDev->crtcs[j].crtc->crtc_id);
        printf("mode_valid:%d\n", mDev->crtcs[j].crtc->mode_valid);
        printf("mode_name:%s\n", mDev->crtcs[j].crtc->mode.name);
        if (mDev->crtcs[j].crtc->crtc_id == lcdCrtcId && mDev->crtcs[j].crtc->mode_valid)
        {
            lcdCRPtr = &mDev->crtcs[j];
        }
       
    }
}


static int init_screens()
{
    int ret = 0;
    
    // 获取lcd connector
    get_connector(OUTPUT_DEVICE_LCD);

    if (!lcdConnectorPtr)
    {
        printf("failed to get hdmi connector or encoder.\n");
        return -1;
    }

    printf("lcd connector id:%d\n", lcdConnectorPtr->connector_id);

    // 获取lcd encoder
    get_encoder(OUTPUT_DEVICE_LCD);


    if (!lcdEncoderPtr)
    {
        printf("failed to get encoder.\n");
        return -2;
    }

    printf("lcd encoder id:%d\n", lcdEncoderPtr->encoder_id);

    // 获取一下显示分辨率之类
    lcdModInfoPtr = &lcdConnectorPtr->modes[0];

    // 把connector的encoder id赋值为encoder的id
    lcdConnectorPtr->encoder_id = lcdEncoderPtr->encoder_id;

    // 获取lcd crtc
    get_crtc();
    if (!lcdCRPtr)
    {
        printf("failed to get crtc.\n");
        return -3;
    }

    if (lcdCRPtr->scanout)
    {
        printf("crtc already in use\n");
        return -4;
    }

    printf("lcd crtc id:%d\n", lcdCRPtr->crtc->crtc_id);

    // allset
    // 获取bo, 只需要输入分辨率即可.
    lcdCRPtr->scanout = create_sp_bo(mDev, lcdModInfoPtr->hdisplay, lcdModInfoPtr->vdisplay, 24, 32, DRM_FORMAT_XRGB8888, 0);
    if (!lcdCRPtr->scanout)
    {
        printf("failed to create new scanout bo\n");
        return -5;
    }


    printf("fill test color\n");

    fill_bo(lcdCRPtr->scanout, 0xff, 0xff, 0x0, 0x0);

    ret = drmModeSetCrtc(mDev->fd, lcdEncoderPtr->crtc_id, lcdCRPtr->scanout->fb_id, 0, 0, &lcdConnectorPtr->connector_id, 1, lcdModInfoPtr);
    if (ret)
    {
        printf("failed to set crtc mode ret=%d\n", ret);
        return -6;
    }
    lcdCRPtr->crtc = drmModeGetCrtc(mDev->fd, lcdCRPtr->crtc->crtc_id);
    memcpy(&lcdCRPtr->crtc->mode, lcdModInfoPtr, sizeof(*lcdModInfoPtr));

    return 0;
}



int drm_dis_init(void)
{
	 int ret = 0;
    int i = 0;
    printf("create sp dev\n");
    // 创建显示设备
    mDev = create_sp_dev();
    if (!mDev)
    {
        printf("failed to exec create_sp_dev.\n");
        return -10;
    }

    printf("init_screen\n");

    // 初始化屏幕
    ret = init_screens();
    if (ret != 0)
    {
        printf("failed to exec initialize_screens.\n");
        return -11;
    }
    return 0;
}

void draw_lcd_screen_rgb_960(uint8_t *data, uint32_t dataSize)
{
    uint32_t colIdx = 0;
    uint32_t rowIdx = 0;
    uint8_t *dataPtr = data;
    for (rowIdx = 0; rowIdx < 1280; rowIdx++)
    {
        uint8_t *rowPtr = (uint8_t *)lcdCRPtr->scanout->map_addr + rowIdx * lcdCRPtr->scanout->pitch;
        for (colIdx = 0; colIdx < 720; colIdx++)
        {
            uint8_t *pixel = rowPtr + colIdx * 4;
            pixel[0] = *dataPtr;
            dataPtr++;
            pixel[1] = *dataPtr;
            dataPtr++;
            pixel[2] = *dataPtr;
            dataPtr++;
            pixel[3] = 0xff;
        }
    }
}

int main(int argc, char **argv)
{
	int i,j;


	drm_dis_init();
	
	cv::VideoCapture cap(0);
	if (!cap.isOpened())
	{
		std::cout << "无法打开摄像头" << std::endl;
		return -1;
	}
	cap.set(cv::CAP_PROP_FRAME_WIDTH, 1280);
	cap.set(cv::CAP_PROP_FRAME_HEIGHT, 720);
	// using opencv
	using namespace cv;
	using namespace std;
	cv::Mat orig_img;
	cv::Mat img;

	char  data_u8[4];
	while(1)
	{
		cap.read(orig_img);
      		cv::rotate(orig_img, orig_img, ROTATE_90_COUNTERCLOCKWISE);
      		cv::cvtColor(orig_img, img, cv::COLOR_BGR2RGB);

	
	    draw_lcd_screen_rgb_960((uint8_t *)img.data, 720 * 1280 * 3);
	}
	
	

	exit(0);
}

这里需要注意的是显示部分

显示是32位的RGBA,所以需要封装一下,初始化得到了map_addr映射地址,直接填充数据,就会显示。

使用正点原子需要先关闭QT应用程序,不然运行一会就会退出。

CMakeLists.txt

cmake_minimum_required(VERSION 3.4.1)

project(rknn_yolov5v7x_demo)

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(CMAKE_SKIP_INSTALL_RPATH FALSE)
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")

# rknn api
set(PLATFORM RK3566_3568)
set(LIB_ARCH aarch64)
set(RKNN_API_PATH ${CMAKE_CURRENT_SOURCE_DIR}/libs/rknpu2/runtime/RK356X/Linux/librknn_api)
set(RKNN_RT_LIB ${RKNN_API_PATH}/aarch64/librknnrt.so)
include_directories(${RKNN_API_PATH}/include)

# drm
set(DRM_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libs/common/drm)
set(DRM_RT_LIB ${CMAKE_CURRENT_SOURCE_DIR}/libs/libdrm/lib/libdrm.so)
include_directories(${DRM_DIR}/include)
include_directories(${DRM_DIR}/include/libdrm)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/libs/rkdrm/)


#rga
set(RGA_PATH ${CMAKE_CURRENT_SOURCE_DIR}/libs/librga)
set(RGA_LIB ${RGA_PATH}/libs/Linux/gcc-aarch64/librga.so)
include_directories(${RGA_PATH}/include)


set(OPENCV_LIBS opencv_core opencv_flann opencv_videoio opencv_video opencv_highgui opencv_imgcodecs opencv_imgproc)
set(TOOLCHAIN_DIR /opt/atk-dlrk356x-toolchain)
set(CMAKE_C_COMPILER ${TOOLCHAIN_DIR}/usr/bin/aarch64-buildroot-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_DIR}/usr/bin/aarch64-buildroot-linux-gnu-c++)
set(SYSROOT ${TOOLCHAIN_DIR}/aarch64-buildroot-linux-gnu//sysroot/usr/include)
set(CMAKE_SYSROOT ${TOOLCHAIN_DIR}/aarch64-buildroot-linux-gnu/sysroot)
include_directories(${SYSROOT})
include_directories(${SYSROOT}/opencv4)

set(CMAKE_INSTALL_RPATH "lib")


# rknn_yolo_demo
include_directories( ${CMAKE_SOURCE_DIR}/include)
add_executable(rknn_yolo_demo
        src/drm_func.c
        src/rga_func.c
        src/yolo.cc
        src/main.cc
        src/bo.cc
        src/dev.cc
        src/modeset.cc
)

target_link_libraries(rknn_yolo_demo
  ${RKNN_RT_LIB}
  ${RGA_LIB}
  ${OPENCV_LIBS}
  ${DRM_RT_LIB}
  dl
)

# install target and libraries
set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/install/atk_rknn_yolo_v5_demo)
install(TARGETS rknn_yolo_demo DESTINATION ./)

install(PROGRAMS ${RKNN_RT_LIB} DESTINATION lib)
install(PROGRAMS ${RGA_LIB} DESTINATION lib)
install(DIRECTORY model DESTINATION ./)

编译后,把可执行文件上传到板子运行后,测试正常。

如有侵权,或需要完整代码,请及时联系博主。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1387090.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构学习 jz59 滑动窗口的最大值

关键词&#xff1a;排序 大顶堆 双端队列 题目&#xff1a; 望远镜中最高的海拔 方法一&#xff1a;维护一个辅助队列。 方法二&#xff1a;大顶堆。 我还在主站 239 写了找最小值的方法。 方法一&#xff1a;最优解 这个方法和jz30维护一个非严格递减的辅助栈是基本一样的…

15.鸿蒙HarmonyOS App(JAVA)进度条与圆形进度条

15.鸿蒙HarmonyOS App(JAVA)进度条与圆形进度条 progressBar2.setIndeterminate(true);//设置无限模式,运行查看动态效果 //创建并设置无限模式元素 ShapeElement element new ShapeElement(); element.setBounds(0,0,50,50); element.setRgbColor(new RgbColor(255,0,0)); …

jetson nano VNC远程桌面配置及使用(nomachine)

文章目录 jetson nano VNC远程桌面配置及使用1.Nomachine介绍2.在电脑端安装Nomachine3.在Jetson Nano端安装Nomachine4.电脑端连接及使用步骤5.修改分辨率6.NoMachine常见问题6.1 黑屏6.2 白屏 jetson nano VNC远程桌面配置及使用 本节适用于Jetson Nano没有单独显示器可以给…

数据库结构文档生成方法二(EZDML)

EZDML 下载链接&#xff1a;EZDML - 下载 我们常用的是数据建模有PowerDesigner,EZDML也是一款数据建模工具&#xff0c;而且功能很多&#xff0c;除了生成sql&#xff0c;还可以生成前端后端代码等等。 我们直接下载最新版后点击安装&#xff0c;打开后会默认打开示例&#…

活动 | Mint Blockchain 将于 2024 年 1 月 17 号启动 MintID 限量发行活动

MintID 是 Mint Blockchain 生态的超级权益卡&#xff0c;用于探索 NFT PASS 在未来各种应用场景下的可能性。MintID 将通过限时限量有价发售的方式对外释放&#xff0c;持有人将成为 Mint Blockchain 的核心权益用户。 MintID 总量&#xff1a;10,000 枚 铸造价格&#xff1a…

linux docker安装 rustdesk

这里写自定义目录标题 1&#xff1a;软件介绍&#xff1a;2&#xff1a;安装1. 服务器端2. 客户端 3&#xff1a;配置5&#xff1a;其他1:rustdesk 官方Docker Compose 1&#xff1a;软件介绍&#xff1a; 名称作用官网项目地址rustdesk实现多端互控https://rustdesk.com/inde…

【闯关练习】—— 1400分(构造)

&#x1f30f;博客主页&#xff1a;PH_modest的博客主页 &#x1f6a9;当前专栏&#xff1a;cf闯关练习 &#x1f48c;其他专栏&#xff1a; &#x1f534;每日一题 &#x1f7e1; C跬步积累 &#x1f7e2; C语言跬步积累 &#x1f308;座右铭&#xff1a;广积粮&#xff0c;缓…

谁知道try里面放return,finally还会执行吗?

在前面几篇文章中&#xff0c;我们已经了解了关于执行上下文、作用域、闭包之间的关系。 今天&#xff0c;我们则要说一说更为细节的部分&#xff1a;语句。 语句是任何编程语言的基础结构&#xff0c;与 JavaScript 对象一样&#xff0c;JavaScript 语句同样具有“看起来很像…

孤儿进程与僵尸进程以及僵尸进程的解决

孤儿进程&#xff1a; 定义&#xff1a; 父进程运行结束&#xff0c;但子进程还在运行&#xff08;未运行结束&#xff09;&#xff0c;这样的子进程就称为孤儿进程&#xff08; Orphan Process &#xff09;。 过程&#xff1a; 每当出现一个孤儿进程的时候&#xff0c;内核就…

放空一下自我 free

文章目录 放空一下自我 free默认的效果使用易读的参数间隔显示内存状态查看meminfo文件更多信息 放空一下自我 free **free**这个命令在Linux系统监控的工具里面&#xff0c;算是使用的比较多的一个。 使用_man_查看可知&#xff0c;官方含义为&#xff1a; Display amount o…

Dockerfile: CMD与ENTRYPOINT区别

CMD和ENTRYPOINT的作用 CMD和ENTRYPOINT这两个命令&#xff0c;我接触到的是用在了Dockerfile中用于构建容器。 CMD&#xff1a;The main purpose of a CMD is to provide defaults for an executing container. CMD的主要用途是为正在执行的容器提供默认值。也就是指定这个容…

D课堂 | 为什么网站搭建好了却无法访问?(上)

在上一期D课堂中&#xff0c;D妹教大家如何用最简单的方法快速搭建一个网站&#xff0c;相信很多小伙伴已经跃跃欲试&#xff0c;尝试去搭建自己的网站。&#xff08;点击这里可以快速复习&#xff09; 然而&#xff0c;有不少人明明每个步骤都跟着做了&#xff0c;但最后在浏览…

企业销售获客难?分享一个精准筛查企业客户的技巧

作为企业销售经理&#xff0c;曾经一直让我们很头疼的问题之一就是获客困难。回想起以往&#xff0c;我们需要通过各种手段&#xff0c;手动查找电话名单、网络搜索到各种渠道&#xff0c;费尽心思的去筛查才能找到潜在客户。获客流程长还耗费很多的精力&#xff0c;拿到手的客…

【test】wsl2和win互ping

参考&#xff1a; https://zhuanlan.zhihu.com/p/365058237 https://blog.csdn.net/Cypher_X/article/details/123011200

RTKlib操作手册--使用样例数据演示

简介 RTKLIB&#xff08;Real-Time Kinematic Library&#xff09;是一款开源的实时差分全球导航卫星系统&#xff08;GNSS&#xff09;软件库。它旨在提供高精度的位置解算&#xff0c;特别是在实时应用中&#xff0c;如精密农业、测绘、无人机导航等领域。 RTKLIB支持多种G…

复杂经济时期下的企业财务规划战略

多重危机、通货膨胀、外汇波动和市场变化的交汇给经济世界带来了前所未有的挑战&#xff0c;这使得企业对预测精准度和及时性的需求大大增加。平衡营收增长与成本输出的稳定性、在不断变化的市场行为中抓住商机提高盈利能力是现阶段财务专业人士必须掌握的技能。情景规划与财务…

Ubuntu下,Flutter安装及在VScode中的配置

1、安装flutter 在自己指定的目录下&#xff0c;新建文件夹&#xff0c;并将源码git clone到本地 $ mkdir flutter $ cd flutter$ git clone -b master https://github.com/flutter/flutter.git2、给flutter添加环境变量 #编辑配置文件 $ vi ~/.bashrc #在末尾加入以下内容&…

分布式系统中的CAP原理

分布式系统中的CAP原理 本文已收录至我的个人网站&#xff1a;程序员波特&#xff0c;主要记录Java相关技术系列教程&#xff0c;共享电子书、Java学习路线、视频教程、简历模板和面试题等学习资源&#xff0c;让想要学习的你&#xff0c;不再迷茫。 简介 在分布式系统中&…

rtklib读取原始数据是一次读取了一个文件的全部数据

一般来说&#xff0c;rtklib读取观测值文件&#xff08;o文件&#xff09;和导航文件&#xff08;n文件&#xff09;进行解算。 读取文件的时候&#xff0c;并非一次读取一个历元&#xff0c;而是将一个文件所有历元的数据都读取完毕以后&#xff0c;再进行解算。 这看起来是…

Go 如何处理死锁以提供哪些工具来检测或防死锁?

并发是 Go 的核心特性&#xff0c;它使程序能够同时处理多个任务。它是现代编程的一个强大组件&#xff0c;如果使用正确&#xff0c;可以产生高效、高性能的应用程序。然而&#xff0c;并发性也带来了顺序编程中不存在的某些类型错误的可能性&#xff0c;其中最臭名昭著的是死…