DRM是现在主流的显示框架了,比起framebuffer来讲,有很多优势,对应的userspace库就是libdrm。
本文拟在设备上,通过libdrm
来显示bmp图片
/drv/drm /
/dev/dri/card0
路径的生成#define _GNU_SOURCE
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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;
}
/**
* Copyright(C) 2022 Raynen Technology Co.,Ltd. All rights reserved.
*
* bmp.h
* Original Author: [email protected], 2022-02-18
*
* History
*
*/
#ifndef __BMP_H__
#define __BMP_H__
#include
#include
#include
#include
#include
#include
#include
#include
#include
//文件头结构体
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__
/**
* Copyright(C) 2022 Raynen Technology Co.,Ltd. All rights reserved.
*
* bmp.c
* Original Author: [email protected], 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的话比较难理解,如果有示例跑一下的话,就比较容易学习了。
2023-7-5 Created