【无标题】

说明

DRM是现在主流的显示框架了,比起framebuffer来讲,有很多优势,对应的userspace库就是libdrm。
本文拟在设备上,通过libdrm来显示bmp图片

libdrm运行前提

  1. kernel DRM驱动支持,需要/drv/drm / /dev/dri/card0 路径的生成
  2. libdrm交叉编译, 编译后把 头文件和库挑出来备用

显示程序源码

main.c

#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include "bmp.h"

struct buffer_object {
	uint32_t width;
	uint32_t height;
	uint32_t pitch;
	uint32_t handle;
	uint32_t size;
	uint32_t *vaddr;
	uint32_t fb_id;
};

struct buffer_object buf[3];

static int modeset_create_fb(int fd, struct buffer_object *bo, uint32_t color, char *bmpfile)
{
	struct drm_mode_create_dumb create = {};
 	struct drm_mode_map_dumb map = {};
	uint32_t i;

	create.width = bo->width;
	create.height = bo->height;
	create.bpp = 32;
	drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create);

	bo->pitch = create.pitch;
	bo->size = create.size;
	bo->handle = create.handle;
	drmModeAddFB(fd, bo->width, bo->height, 24, 32, bo->pitch,
			   bo->handle, &bo->fb_id);

	map.handle = create.handle;
	drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &map);

	bo->vaddr = mmap(0, create.size, PROT_READ | PROT_WRITE,
			MAP_SHARED, fd, map.offset);

    // 填充FB内容
    printf("bo->size %d\n", bo->size);

#if 1
    show_photo((char *)bo->vaddr, bo->width, bo->height, 32, bmpfile);
#else
	for (i = 0; i < (bo->size / 4); i++)
		bo->vaddr[i] = color;
#endif
	return 0;
}

static void modeset_destroy_fb(int fd, struct buffer_object *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 main(int argc, char **argv)
{
	int fd;
	drmModeConnector *conn;
	drmModeRes *res;
	uint32_t conn_id;
	uint32_t crtc_id;
    char *bmpfile;

    printf("argc %d\n");
    if (argc != 2) {

        printf("usage: xxx  bmpfile\n");
        return 0;
    }

    printf("bmp path: %s\n", argv[1]);

    bmpfile = argv[1];

	fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);

	res = drmModeGetResources(fd);
	crtc_id = res->crtcs[0];
	conn_id = res->connectors[0];

	conn = drmModeGetConnector(fd, conn_id);
	
    printf("conn->modes[0].hdisplay %d conn->modes[0].vdisplay %d \n", 
        conn->modes[0].hdisplay, conn->modes[0].vdisplay);
    
    buf[0].width = conn->modes[0].hdisplay;
	buf[0].height = conn->modes[0].vdisplay;
	// buf[1].width = conn->modes[0].hdisplay;
	// buf[1].height = conn->modes[0].vdisplay;
	// buf[2].width = conn->modes[0].hdisplay;
	// buf[2].height = conn->modes[0].vdisplay;

    // 显示bmp图片
    modeset_create_fb(fd, &buf[0], 0, bmpfile);

	// modeset_create_fb(fd, &buf[0], 0xff0000);  // red
	// modeset_create_fb(fd, &buf[1], 0x0000ff);   // green
    // modeset_create_fb(fd, &buf[2], 0x00ff00);   // blue

	drmModeSetCrtc(fd, crtc_id, buf[0].fb_id,
			0, 0, &conn_id, 1, &conn->modes[0]);

//	getchar();

    printf("Enter sleep\n");
    sleep(3);
    // while (1) {
    //     sleep(1);
    // }

	// drmModeSetCrtc(fd, crtc_id, buf[1].fb_id,
	// 		0, 0, &conn_id, 1, &conn->modes[0]);

	// getchar();

	// drmModeSetCrtc(fd, crtc_id, buf[2].fb_id,
	// 		0, 0, &conn_id, 1, &conn->modes[0]);

	// getchar();

    // modeset_destroy_fb(fd, &buf[2]);
	// modeset_destroy_fb(fd, &buf[1]);
	modeset_destroy_fb(fd, &buf[0]);

	drmModeFreeConnector(conn);
	drmModeFreeResources(res);

	close(fd);

	return 0;
}

bmp.h

/**
 * Copyright(C) 2022 Raynen Technology Co.,Ltd. All rights reserved.
 *
 * bmp.h
 * Original Author: zhangcs@raynen.cn, 2022-02-18 
 * 
 * History
 *
 */

#ifndef __BMP_H__
#define __BMP_H__

#include <unistd.h>
#include <stdio.h> 
#include <stdlib.h>    
#include <fcntl.h>
#include <string.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>

//文件头结构体
typedef struct {
    unsigned char    bfType[2];      //文件类型
    unsigned long bfSize;         //位图大小
    unsigned short bfReserved1;  //位0 
    unsigned short bfReserved2;  //位0
    unsigned long bfOffBits;      //到数据偏移量
} __attribute__((packed)) BitMapFileHeader;     //使编译器不优化,其大小为14字节 

//信息头结构体
typedef struct {
    unsigned long biSize;                  // BitMapFileHeader 字节数
    long biWidth;                           //位图宽度 
    long biHeight;                         //位图高度,正位正向,反之为倒图 
    unsigned short biPlanes;              //为目标设备说明位面数,其值将总是被设为1
    unsigned short biBitCount;            //说明比特数/象素,为1、4、8、16、24、或32。 
    unsigned long biCompression;         //图象数据压缩的类型没有压缩的类型:BI_RGB 
    unsigned long biSizeImage;          //说明图象的大小,以字节为单位 
    long biXPelsPerMeter;                 //说明水平分辨率 
    long biYPelsPerMeter;                //说明垂直分辨率 
    unsigned long biClrUsed;             //说明位图实际使用的彩色表中的颜色索引数
    unsigned long biClrImportant;        //对图象显示有重要影响的索引数,0都重要。 
} __attribute__((packed)) BitMapInfoHeader; 

//像素点结构体
typedef struct {
    unsigned char Blue;          //该颜色的蓝色分量 
    unsigned char Green;        //该颜色的绿色分量 
    unsigned char Red;            //该颜色的红色分量 
    unsigned char Reserved;     //保留值(亮度)   
} __attribute__((packed)) RgbQuad;



int show_photo(char *fbp, int w, int h, int bpp, const char *bmpname);


#endif //__BMP_H__

bmp.c

/**
 * Copyright(C) 2022 Raynen Technology Co.,Ltd. All rights reserved.
 *
 * bmp.c
 * Original Author: zhangcs@raynen.cn, 2022-02-18 
 * 
 * History
 *
 */

 #include "bmp.h"

/*************************
 * fbp,映射内存起始地址
 * w  屏幕宽  
 * h  屏幕高    
 * bpp  每个像素的位数  RGB888时是 24
 * bmpname,.bmp位图文件名
 * 本函数仅测试用,填写地址时,有可能溢出导致段错误
*************************/
int show_photo(char *fbp, int w, int h, int bpp, const char *bmpname)
{

    char blue, green, red;
    int line_x, line_y;
    int len, bits_len;
    unsigned long location;
    int xres, bits_per_pixel;
    BitMapFileHeader FileHead;
    BitMapInfoHeader InfoHead;
    RgbQuad rgb;
    
    unsigned long c;

    if(NULL == fbp || NULL == bmpname){
        return -1;
    }

    xres = w;   //屏幕宽(虚拟)
    bits_per_pixel = bpp;   //屏幕位数

    //打开.bmp文件
    FILE *fb = fopen(bmpname, "rb");
    if (fb == NULL){
        printf("fopen bmp error\r\n");
        return -1;
    }

    //读文件信息
    if (fread( &FileHead, sizeof(BitMapFileHeader),1, fb) != 1){
        printf("read BitMapFileHeader error!\n");
        fclose(fb);
        return -1;
    }

    if (memcmp(FileHead.bfType, "BM", 2) != 0){
        printf("it's not a BMP file\n");
        fclose(fb);
        return -1;
    }

    //读位图信息
    if (fread( (char *)&InfoHead, sizeof(BitMapInfoHeader),1, fb) != 1){
        printf("read BitMapInfoHeader error!\n");
        fclose(fb);
        return -1;
    }

    //跳转至数据区
    fseek(fb, FileHead.bfOffBits, SEEK_SET);

    len = InfoHead.biBitCount / 8;    //原图一个像素占几字节
    bits_len = bits_per_pixel / 8;    //屏幕一个像素占几字节    //循环显示

    printf("file pixel len  %d  screen pixel len %d\n", len, bits_len);
    line_x = 0;
    line_y = 0;

    while(!feof(fb)){

       if (len != fread((char *)&rgb, 1, len, fb)){
           break;
       }

       rgb.Reserved = 0xFF;
       //计算该像素在映射内存起始地址的偏移量
       location = line_x * bits_len + (InfoHead.biHeight - line_y - 1) * xres * bits_len;

        /* 红绿蓝的颜色,可能需要调整位置 */
        blue = rgb.Blue;
        green = rgb.Green; 
        red = rgb.Red; 
        c = (red << 16) | (green << 8) | (blue);

        /* 严格点这里需要判断是否越界,这里仅作测试用了 */
        *((unsigned long *)(fbp + location)) = c;
       line_x++;
       if (line_x == InfoHead.biWidth ){
           line_x = 0;
           line_y++;
           if(line_y == InfoHead.biHeight){
                break;
           }
       }
    }

    fclose(fb);

    return 0;
 }

编译与运行

arm-buildroot-linux-gnueabihf-gcc -o drmtest drm-test.c bmp.c -Iinclude -Iinclude/drm -L. -ldrm

运行

drmtest hello.bmp

总结

代码比较粗糙,但是也能运行,在网上找了很多示例,刚刚接触DRM的话比较难理解,如果有示例跑一下的话,就比较容易学习了。

History

2023-7-5 Created

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值