【Linuxアプリプログラミング】フレームバッファデバイスアプリプログラミング例


1はじめに

  LCDデバイスは、組み込み機器で一般的に使用されている周辺機器であり、優れた視覚化機能を備えています。家電だけでなく、産業用制御、医療、自動車などの業界の電子機器も、LCDを表示媒体として使用する傾向にあります。この記事では、LinuxシステムでのLCDデバイスのアプリケーションについて説明します。LCDシステムは、Linuxシステムでは「フレームバッファデバイス」と呼ばれます。


2フレームバッファ

  フレームバッファーデバイス(フレームバッファーデバイス)は、Linuxデバイスによって抽象化されたディスプレイデバイス用のキャラクターデバイスです。ユーザーレイヤーの上位に統一されたアクセスインターフェイスを提供し、下部にあるさまざまなハードウェアディスプレイデバイス間の違いをシールドし、フレームバッファーデバイスアプリケーションの互換性を向上させます。そして携帯性。


2.1フレームバッファ機能

  フレームバッファーには、アプリケーション層とドライバー層にとって、疑いのない利点があります。

  • アプリケーションプログラミングの場合、アプリケーションは、ディスプレイデバイスの基礎となる実装、メモリページング、およびデータフローを考慮する必要がなく、標準のフレームバッファーインターフェイスを介してディスプレイデバイスを制御できます。
  • アプリケーションの移植性のために、フレームバッファーは根本的な違いを隠蔽し、CPUプラットフォームが異なり、表示デバイスが異なる場合でも、フレームバッファーをサポートするLinuxデバイスにアプリケーションをシームレスに移植できます; qtやminiguiなどのUIプログラムの移植性。
  • 複数のディスプレイデバイスの場合、linuxシステムは複数のフレームバッファデバイスをサポートし、基盤となるメモリデータをフレームバッファデバイスにリダイレクトすることにより、ディスプレイインターフェイスを指定したディスプレイデバイスにすばやく切り替えることができます。
  • フレームバッファードライバーについては、標準ドライバーインターフェイスに従い、ディスプレイデバイスを交換または追加するときに標準コールバックインターフェイスを実装するだけでよいので、開発プロセスが簡素化されます。

2.2フレームバッファデバイス

  フレームバッファーデバイスは、標準のキャラクターデバイスです。他のデバイスと同じ“/dev”ディレクトリにあります。通常、名前はfbX( "/ dev / fb0")です。Xは、通常0から始まるディスプレイデバイスのシリアル番号を表します。Linuxシステムは、最大32のフレームバッファーデバイスをサポートしています。

  以下のように、私のHi3520Dボードは5つのフレームバッファデバイスをサポートしています。

#ls /dev/fb*
/dev/fb0  /dev/fb1  /dev/fb2  /dev/fb3  /dev/fb4

  フレームバッファーデバイスは標準のキャラクターデバイスで、メジャーデバイス番号は29で、マイナーデバイス番号の範囲は0〜31で、fb0〜fb31に対応します。


3フレームバッファインターフェイス

  フレームバッファアプリケーションの開発に対応して、最下層の特定の実装を気にする必要はありません。アプリケーションを開発するには、特定のアクセスインターフェイス、データ構造、およびディスプレイデバイスの必要な情報を知っていれば十分です。


3.1フレームバッファデバイスの説明情報

  フレームバッファデバイス情報には、変更できない情報と変更可能な情報の2つの部分があります。デバイス情報記述データ構造は“include/uaip/linux/fb.h”、宣言にあります。

  変更不可能な情報は、ディスプレイデバイスの固有の属性、物理メモリアドレス、物理メモリサイズなどを説明します。アプリケーションレイヤーの場合、この情報の部分を使用して、ディスプレイデバイスの固有の属性情報をクエリできます。特定のアプリケーションプロセスとの関連性はほとんどありません。

struct fb_fix_screeninfo{
    
    
	char id[16];			/* identification string eg "TT Builtin" */
	unsigned long smem_start;	/* Start of frame buffer mem */
					/* (physical address) */
	__u32 smem_len;			/* Length of frame buffer mem */
	__u32 type;			/* see FB_TYPE_*		*/
	__u32 type_aux;			/* Interleave for interleaved Planes */
	__u32 visual;			/* see FB_VISUAL_*		*/ 
	__u16 xpanstep;			/* zero if no hardware panning  */
	__u16 ypanstep;			/* zero if no hardware panning  */
	__u16 ywrapstep;		/* zero if no hardware ywrap    */
	__u32 line_length;		/* length of a line in bytes    */
	unsigned long mmio_start;	/* Start of Memory Mapped I/O   */
					/* (physical address) */
	__u32 mmio_len;			/* Length of Memory Mapped I/O  */
	__u32 accel;			/* Indicate to driver which	*/
					/*  specific chip/card we have	*/
	__u16 capabilities;		/* see FB_CAP_*			*/
	__u16 reserved[2];		/* Reserved for future compatibility */
};

  変更できる情報の2番目の部分には、ディスプレイデバイスの物理解像度、仮想解像度、ピクセル幅が含まれます。この情報のこの部分は、アプリケーションのプログラミングで、表示位置、色、マップされたメモリなどを計算するために注意する必要があります。この部分もデバイス固有の情報を表示するように設計されていますが、アプリケーションはインターフェースを介して変更できます。

struct fb_var_screeninfo{
    
    
	__u32 xres;			/* 可视分辨率(物理分辨率) */
	__u32 yres;
    
	__u32 xres_virtual;	/* 虚拟分辨率 */
	__u32 yres_virtual;
	__u32 xoffset;		/* 虚拟分辨率相对可视分配率偏移值 */
	__u32 yoffset;			

	__u32 bits_per_pixel;	/* 像素位宽,每个像素用多少未表示 */
	__u32 grayscale;		/* 灰度等级,0表示彩色,1表示灰度(黑白屏)*/
	
    /* 缓存RGB位域 */
	struct fb_bitfield red;		/* bitfield in fb mem if true color, */
	struct fb_bitfield green;	/* else only length is significant */
	struct fb_bitfield blue;
	struct fb_bitfield transp;	/* transparency	*/	

	__u32 nonstd;			/* != 0 Non standard pixel format */

	__u32 activate;			/* see FB_ACTIVATE_* */

	__u32 height;			/* height of picture in mm    */
	__u32 width;			/* width of picture in mm     */

	__u32 accel_flags;		/* (OBSOLETE) see fb_info.flags */

	/* 实际显示屏(LCD)参数,根据LCD手册设置,用户层一般不用更改 */
	__u32 pixclock;			/* 像素时钟 */
	__u32 left_margin;		/* 显示设备水平方向前肩,单位:时钟 */
	__u32 right_margin;		/* 显示设备水平方向后肩,单位:时钟 */
	__u32 upper_margin;		/* 显示设备垂直方式前肩,单位:行 */
	__u32 lower_margin;		/* 显示设备垂直方向后肩,单位:行 */
	__u32 hsync_len;		/* 显示设备水平方向有效区域,单位:时钟 */
	__u32 vsync_len;		/* 显示设备垂直方向有效区域,单位:行 */
    
	__u32 sync;				/* see FB_SYNC_*		*/
	__u32 vmode;			/* see FB_VMODE_*		*/
	__u32 rotate;			/* angle we rotate counter clockwise */
	__u32 colorspace;		/* colorspace for FOURCC-based modes */
	__u32 reserved[4];		/* Reserved for future compatibility */
}

  フレームバッファーアプリケーション開発の場合、主に関係するメンバーパラメーターは、物理解像度、仮想解像度、およびピクセル幅です。物理解像度とピクセル幅によって、マップされたメモリのサイズが決まります。たとえば、LCD解像度は1024 * 768で、ピクセル幅は32ビットです。必要なメモリスペースサイズは、1024 * 768 * 32/8 = 3145728バイトです。 。


  • 物理的解像度と仮想解像度

  物理解像度は簡単に理解できます。つまり、ディスプレイデバイスの実際の解像度です。仮想解像度は、物理解像度よりも低いまたは高い、ドライバーレイヤーによって実装される仮想解像度です。たとえば、コンピューターモニターの物理的な解像度は1920 * 1080であり、ディスプレイの解像度を低くしたり高くしたりできますが、ディスプレイの品質が低下したり、グラフィックが歪んだりします。


  • ピクセル幅

  ピクセルビット幅は、ディスプレイピクセルを表すために使用されるビット数を表します。一般的なカラーモードは24ビットRGBです。つまり、RGBカラーは、RGB888と呼ばれる8ビットで表されます。その後、透明度(Alpha)要素が導入され、これも8ビットで記述されていますが、現時点では、ARGB32と呼ばれるピクセルを表すために32ビットが使用されています。表示デバイスは色標準から派生しているため、透明度パラメーターも増加します。実際、組み込みデバイスのCPUおよびメモリリソースを節約するために、RGB565やRGB555などのフォーマットは、色品質が高くない場合によく使用されます。その他の色の形式と違いについては、記事の最後にある記事を参照してください


  ディスプレイデバイスのパラメーターは通常、ドライバーレイヤーで設定され、ユーザーレイヤーの変更は必要ありません。図に示すように、これは1024 * 768のLCD画面パラメーターのスクリーンショットです。

ここに画像の説明を挿入

  図ではフロントショルダーのパラメータは示していませんが、総面積から計算でき、計算式は総面積=有効面積+フロントショルダー+バックショルダーとなります。この部分はフレームバッファドライバ開発のカテゴリに属しているため、詳細には触れません。フレームバッファドライバの記事で詳しく説明します。


3.2フレームバッファアクセスインターフェイス

  フレームバッファデバイスは、標準のLinuxファイルデバイスであり、標準の仮想ファイルインターフェイスを介してアクセスできますopen/read/write/ioctl/closeデバイスアクセスは、制御データフローと表示データフローの2つの部分に分かれています。制御データフローはioctl、指定されたコマンドを組み合わせることによって実現されます。表示フローデータの場合、比較的大量のデータがmmap関数と組み合わされているため、フレームバッファーの物理メモリがユーザーモードにマップされ、物理メモリが直接アクセスされます。

ioctl(fd, cmd, param); /* 文件描述符, 命令字, 参数信息 */

注:mmap関数の使用方法について
は、「mmapメモリーマッピング」の記事参照してください


  フレームバッファデバイスioctlコマンドは“include/uapi/linux/fb.h”定義にあります。

/* include/uaip/linux/fb.h */
/* ioctls
   0x46 is 'F'			*/
#define FBIOGET_VSCREENINFO	0x4600	/* 查询显示设备可更改信息 */
#define FBIOPUT_VSCREENINFO	0x4601	/* 设置显示设备可更改信息 */
#define FBIOGET_FSCREENINFO	0x4602	/* 查询显示设备不可更改信息 */
#define FBIOGETCMAP			0x4604
#define FBIOPUTCMAP			0x4605
#define FBIOPAN_DISPLAY		0x4606
......

3.2.1クエリ/変更可能な情報の設定

関数プロトタイプ:

int ioctl(int fd, int cmd, struct fb_var_screeninfo *var);
  • cmd、コマンドワード:FBIOGET_VSCREENINFO

  • var、デバイスは情報データ構造を変更できます

  • 0を返すと成功、それ以外の場合は-1が返され、エラーコードerrno


  情報を変更できるディスプレイデバイスの疑似コードを取得します。

int fb = 0;
struct fb_var_screeninfo var;

fb = open("/dev/fb0", O_RDWR);
if (fb < 0)
{
    
    
	printf("open fb device failed:%s\n",strerror(errno));
	return -1;
}
if (ioctl(fb, FBIOGET_VSCREENINFO, &var))
{
    
    
	printf("read fb device param failed:%s\n",strerror(errno));
    return -1;
}	

3.2.2変更不可能な情報のクエリ

関数プロトタイプ:

int ioctl(int fd, int cmd, struct  fb_fix_screeninfo  *var);
  • cmd、コマンドワード:FBIOGET_FSCREENINFO

  • var、デバイスは情報データ構造を変更できません

  • 0を返すと成功、それ以外の場合は-1が返され、エラーコードerrno


  表示デバイスの変更不可能な情報を取得するための疑似コード:

int fb = 0;
struct fb_fix_screeninfo fix;

fb = open("/dev/fb0", O_RDWR);
if (fb < 0)
{
    
    
	printf("open fb device failed:%s\n",strerror(errno));
	return -1;
}
if (ioctl(fb, FBIOGET_FSCREENINFO, &fix))
{
    
    
	printf("read fb device param failed:%s\n",strerror(errno));
    return -1;
}	

3.2.3デバイストレースの表示

  表面は線で構成され、線は点で構成されて表示画面を実現し、最下層の本質は点をトレースすることです。ユーザー層はフレームバッファーデバイスの物理バッファーをユーザー状態にマップし、アプリケーションはピクセルデータを直接「マップされたメモリ」に書き込み、フレームバッファードライバーはデータをディスプレイデバイスに出力して表示します。


トレース機能を実現する手順:

[1]ディスプレイデバイスの物理的な解像度とピクセル幅を取得します。
[2]ターゲットXY座標に従ってオフセットメモリを計算します。
[3]ピクセル幅、16ビット、32ビット幅などに従ってメモリを書き込みます。

  疑似コード:

void draw_pixel(int x, int y, uint32_t color)
{
    
    
	uint8_t *poffset_buf = NULL;

	poffset_buf = fb_base + (x*var.bits_per_pixel/8) 
				  + (y*fb->var.xres*var.bits_per_pixel/8);	/* 计算内存偏移地址 */
	*(uint32_t*)poffset_buf = color;	/* ARGB32格式 */
}

4フレームバッファアプリケーションプログラミング

  フレームバッファデバイスにアクセスするアプリケーションの全体的なフローを次の図に示します。

ここに画像の説明を挿入

4.1例

  • フレームバッファデバイス情報を取得する
  • 「Red-Green-Blue」1秒サイクルのリフレッシュ出力
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/fb.h>

struct _fb_info
{
    
    
	int fd;	/* framebuffer 文件描述符 */
	uint8_t *pbuf; /* 映射内存 */
	struct fb_fix_screeninfo fix;
	struct fb_var_screeninfo var;/* framebuffer设备信息*/
};

struct _fb_info fb_app = {
    
    0};

/* 画点函数 */
static void draw_pixel(struct _fb_info *fb, int x, int y, uint32_t color)
{
    
    
	uint8_t *poffset_buf = NULL;

	poffset_buf = fb->pbuf + (x*fb->var.bits_per_pixel/8) 
				  + (y*fb->var.xres*fb->var.bits_per_pixel/8);	/* 计算内存偏移地址 */
	*(uint32_t*)poffset_buf = color;	/* ARGB32格式 */

}

/* 全屏画点函数 */
static void fill_pixel(struct _fb_info *fb, uint32_t color)
{
    
    
    int i, j;

    for (i=0; i<fb->var.xres; i ++) 
	{
    
    
        for (j=0; j<fb->var.yres; j ++) 
		{
    
    
			draw_pixel(fb, i, j, color);
        }
    }
}

int main(int argc, char *argv[])
{
    
    
	int ret = 0;
	int mem_size = 0;
	
	if (argc < 2)
	{
    
    
		printf("parameter invalid\n");
		return -1;
	}
	
	fb_app.fd = open(argv[1], O_RDWR);
	if (fb_app.fd < 0)
	{
    
    
		printf("open device [%s] failed:%s\n", argv[1], strerror(errno));
		return -1;
	}
	printf("framebuffer device:%s\n", argv[1]);
	
	/* 读取不可更改信息 */
	ret = ioctl(fb_app.fd, FBIOGET_FSCREENINFO, &fb_app.fix);    
	if (ret < 0)
	{
    
    
		printf("read fb device fscreeninfo failed:%s\n", strerror(errno));
		close(fb_app.fd);
		return -1;
	}
	printf("device id:%s\n", fb_app.fix.id);
	printf("smem_start:0x%x, smem_len:%u\n", fb_app.fix.smem_start, fb_app.fix.smem_len);    

	/* 读取可更改信息 */
	ret = ioctl(fb_app.fd, FBIOGET_VSCREENINFO, &fb_app.var);
	if (ret < 0)
	{
    
    
		printf("read fb device vscreeninfo failed:%s\n", strerror(errno));
		close(fb_app.fd);
		return -1;
	}
	printf("visible resolution:%d*%d\n", fb_app.var.xres, fb_app.var.yres);
	printf("virtual resolution:%d*%d\n", fb_app.var.xres_virtual, fb_app.var.yres_virtual);
	printf("pixel bits wide:%d\n", fb_app.var.bits_per_pixel);
	printf("grayscale:%d\n", fb_app.var.grayscale);
	
	mem_size = fb_app.var.xres * fb_app.var.yres * fb_app.var.bits_per_pixel / 8;	/* 计算内存 */
	fb_app.pbuf = (uint8_t *)mmap(NULL, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, fb_app.fd, 0);
	if (fb_app.pbuf == NULL)
	{
    
    
		printf("fb device mmap failed:%s\n", strerror(errno));
		close(fb_app.fd);
		return -1;
	}
	memset(fb_app.pbuf, 0, mem_size);	/* 清屏操作(黑屏)*/
	for (;;)
	{
    
    
		fill_pixel(&fb_app, 0xffff0000);/* 红 */
		sleep(1);
		fill_pixel(&fb_app, 0xff00ff00);/* 绿 */
		sleep(1);
		fill_pixel(&fb_app, 0xff0000ff);/* 蓝 */
		sleep(1);
	}
	munmap(fb_app.pbuf, mem_size);
	close(fb_app.fd);
	
	return 0;	
}

コンパイルテスト

  • プラットフォーム:Hid3520D
  • フレームバッファ:fb0,1024 * 768LCD
/* 编译 */
# arm-hisiv500-linux fb.c -o fb

/* 执行 */
# ./fb /dev/fb0
framebuffer device:/dev/fb0
device id:hifb
smem_start:0x90082000, smem_len:24883200
visible resolution:1024*768
virtual resolution:1024*1536
pixel bits wide:32
grayscale:0

5参考記事

【1】fb_var_screeninfo和fb_fix_screeninfo

[2] RGB565、RGB555、RGB16、RGB24、RGB32、ARGB32、およびその他のフォーマット間のグラフィックの違い

[3] mmapメモリーマッピング

おすすめ

転載: blog.csdn.net/qq_20553613/article/details/107882790