この記事では主に Frmebuffer アプリケーション プログラミングを紹介します。ドライバーではなくアプリケーションに注目してください。したがって、開発ボードが必要であり、すでに Linux システムを実行できるようになっています。
組み込み Linux 入門学習チュートリアルの概要:組み込み Linux チュートリアル - ベア メタル、アプリケーション、ドライバーの完全なチュートリアル ディレクトリ
1. フレームバッファ アプリケーション プログラミングの基礎知識
Linux システムでは、LCD はフレームバッファ ドライバを通じて制御されます。Frame はフレーム、buffer はバッファを意味します。つまり、Framebuffer は画像のフレームを格納するメモリの一部です。
本質は、アプリケーション プログラムがフレームバッファを変更し、ドライバがフレームバッファからデータを自動的に読み取り、 LCD ディスプレイを駆動することです。
1.1 フレームバッファのサイズとアドレスの計算
サイズの計算
フレームバッファは画像フレームの各ピクセルのカラー値を保存します。LCD の解像度が 1024x768 で、各ピクセルのカラーが 32 ビットで表されると仮定すると、フレームバッファのサイズは 1024x768x32/8=3145728 バイトになります。
ピクセルアドレスの計算
LCD の座標 (x, y) にあるピクセルの色を設定する必要があるとします。まず、このピクセルに対応するメモリを見つけてから、その BPP 値に従って色を設定する必要があります。fb_base が APP が mmap を実行した後に取得したフレームバッファー アドレスであると仮定すると、次の式を使用して、(x, y) 座標のピクセルに対応するフレームバッファー アドレスを計算できます。
(x, y)像素起始地址=fb_base+(xres*bpp/8)*y + x*bpp/8
1.2 bpp
ピクセルあたりのビット数 ピクセルあたりのビット数。
ピクセルの色をどう表現するか?RGB の 3 原色 (赤、緑、青) で表され、BPP 形式が異なると、それぞれ R、G、B を表すために異なるビットが使用されます。
32BPP の場合、通常は下位 24 ビットのみが設定され、上位 8 ビットは透明度を示しますが、これは通常の LCD ではサポートされていません。
24BPP の場合、ハードウェアでの処理の都合上、フレームバッファ内でも 32 ビットで表現され、効果は 32BPP と同じになります。
16BPP の場合、RGB565 が一般的に使用されますが、まれに RGB555 が使用されますが、ioctl を介してドライバーの RGB ビット オフセットを読み取ることで、使用する形式を決定できます。
1.3mmap
mmap はメモリマップを作成するために使用されます。Munmap は、mmap によって確立されたメモリ マッピングをキャンセルするために使用されます。
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
int munmap(void *addr, size_t length);
関数パラメータの説明:
-
addr は指定されたマッピングのメモリ開始アドレスを示します。通常は NULL に設定され、システムがアドレスを自動的に選択し、マッピングが正常に確立された後に変更されたアドレスに戻ります。
-
長さは、ファイル内のコンテンツがメモリにマップされる量を示します。
-
prot はマッピング領域の保護方法を示し、次の 4 つの方法で組み合わせることができます:
a. PROT_EXEC マッピング領域は実行可能
b. PROT_READ マッピング領域は読み取り可能
c. PROT_WRITE マッピング領域は書き込み可能
d. PROT_NONE マッピング領域は不可アクセスされる -
a. MAP_SHAREDは、マッピング領域に書き込ま
れたデータがファイルにコピーされ、元のファイルが変更されることを示します。
b. MAP_PRIVATE は、マップされた領域に対する操作によってマップされたファイルのコピーが生成され、この領域への変更は元のファイルの内容に書き戻されないことを示します。
戻り値: マッピングが成功した場合はマッピングされた領域へのポインタが返され、失敗した場合は -1 が返され、errno が設定されます。
2. フレームバッファアプリケーションプログラミング
手順の概要:
1. デバイス ノードを開きます: open("/dev/fb0", O_RDWR)
2. LCD パラメータを取得します: ioctl(fd_fb, FBIOGET_VSCREENINFO, &var)
3.映射フレームバッファ:mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0)
4. フレームバッファの操作
2.1. デバイスの電源を入れる
まずデバイス ノードを開きます。
fd_fb = open("/dev/fb0", O_RDWR);
if (fd_fb < 0)
{
printf("can't open /dev/fb0\n");
return -1;
}
2.2 LCDパラメータの取得
LCD ドライバは、可変パラメータ fb_var_screeninfo と固定パラメータ fb_fix_screeninfo の 2 種類のパラメータを APP に提供します。
2.2.1 fb_var_screeninfo
アプリケーションを作成するとき、主に考慮するのは変数パラメーターであり、その構造は次のように定義されます (#include<linux/fb.h>)。
fb_var_screeninfo は、次のコードを使用して取得できます。
static struct fb_var_screeninfo var; /* Current var */
……
if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))
{
printf("can't get var\n");
return -1;
}
ioctl で使用されるパラメータは次のとおりであることに注意してください: FBIOGET_VSCREENINFO、これは画面の変数情報を取得するための var screen info の取得を意味します; もちろん、FBIOPUT_VSCREENINFO を使用してこれらのパラメータを調整することもできますが、これはほとんど使用されません。
2.2.2 fb_fix_screeninfo
固定パラメータ fb_fix_screeninfo については、アプリケーション プログラミングではほとんど使用されません。その構造は次のように定義されます。
この情報は、ioctl FBIOGET_FSCREENINFO を使用して読み取ることができますが、使用されることはほとんどありません。
2.3 フレームバッファのマッピング
メモリのブロックをマップするには、そのアドレス (これはドライバーによって設定されます) を知る必要があります。また、そのサイズ (アプリケーションによって決定される) を知る必要があります。コードは以下のように表示されます:
line_width = var.xres * var.bits_per_pixel / 8;
pixel_width = var.bits_per_pixel / 8;
screen_size = var.xres * var.yres * var.bits_per_pixel / 8;
fb_base = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, M
AP_SHARED, fd_fb, 0);
if (fb_base == (unsigned char *)-1)
{
printf("can't mmap\n");
return -1;
}
screen_size はフレームバッファ全体のサイズです。
PROT_READ | PROT_WRITE は、その領域が読み取りおよび書き込み可能であることを示します。
MAP_SHARED は、領域が共有されていることを示し、APP がデータを書き込むとき、それはドライバーに直接送信されます。
2.4 記述点関数のコンパイル
LCD上に指定したピクセルを描画できるようになると、書き込みや描画ができるようになり、描画機能が基本となります。コードは以下のように表示されます:
void lcd_put_pixel(int x, int y, unsigned int color)
//传入的 color 表示颜色,它的格式永远是 0x00RRGGBB,即 RGB888。
//当 LCD 是 16bpp 时,要把 color 变量中的 R、 G、 B 抽出来再合并成 RGB565 格式。
{
unsigned char *pen_8 = fb_base+y*line_width+x*pixel_width;
//计算(x,y)坐标上像素对应的 Framebuffer 地址。
unsigned short *pen_16;
unsigned int *pen_32;
unsigned int red, green, blue;
pen_16 = (unsigned short *)pen_8;
pen_32 = (unsigned int *)pen_8;
switch (var.bits_per_pixel)
{
//对于 8bpp, color 就不再表示 RBG 三原色了,这涉及调色板的概念, color 是调色板的值。
case 8:
{
*pen_8 = color;
break;
}
case 16:
{
/* 565 */
//先从 color 变量中把 R、 G、 B 抽出来。
red = (color >> 16) & 0xff;
green = (color >> 8) & 0xff;
blue = (color >> 0) & 0xff;
//把 red、 green、 blue 这三种 8 位颜色值,根据 RGB565 的格式,
//只保留 red 中的高 5 位、 green 中的高 6 位、 blue 中的高 5 位,
//组合成一个新的 16 位颜色值。
color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
//把新的 16 位颜色值写入 Framebuffer
*pen_16 = color;
break;
}
case 32:
{
//对于 32bpp,颜色格式跟 color 参数一致,可以直接写入Framebuffer
*pen_32 = color;
break;
}
default:
{
printf("can't surport %dbpp\n",var.bits_per_pixel);
break;
}
}
}
2.5 ランダムにいくつかの点を引く
このプログラムの主な機能は、最後にいくつかの点を描画するだけです。
/* 清屏: 全部设为白色 */
memset(fbmem, 0xff, screen_size);
/* 随便设置出 100 个为红色 */
for (i = 0; i < 100; i++)
lcd_put_pixel(var.xres/2+i, var.yres/2, 0xFF0000);
3. プログラムを完成させ、コンパイルして実行する
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>
#include <linux/fb.h>
#include <unistd.h>
static int fd_fb;
static struct fb_var_screeninfo var; /* Current var */
static int screen_size;
static unsigned char *fb_base;
static unsigned int line_width;
static unsigned int pixel_width;
void lcd_put_pixel(int x, int y, unsigned int color)
//传入的 color 表示颜色,它的格式永远是 0x00RRGGBB,即 RGB888。
//当 LCD 是 16bpp 时,要把 color 变量中的 R、 G、 B 抽出来再合并成 RGB565 格式。
{
unsigned char *pen_8 = fb_base+y*line_width+x*pixel_width;
//计算(x,y)坐标上像素对应的 Framebuffer 地址。
unsigned short *pen_16;
unsigned int *pen_32;
unsigned int red, green, blue;
pen_16 = (unsigned short *)pen_8;
pen_32 = (unsigned int *)pen_8;
switch (var.bits_per_pixel)
{
//对于 8bpp, color 就不再表示 RBG 三原色了,这涉及调色板的概念, color 是调色板的值。
case 8:
{
*pen_8 = color;
break;
}
case 16:
{
/* 565 */
//先从 color 变量中把 R、 G、 B 抽出来。
red = (color >> 16) & 0xff;
green = (color >> 8) & 0xff;
blue = (color >> 0) & 0xff;
//把 red、 green、 blue 这三种 8 位颜色值,根据 RGB565 的格式,
//只保留 red 中的高 5 位、 green 中的高 6 位、 blue 中的高 5 位,
//组合成一个新的 16 位颜色值。
color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
//把新的 16 位颜色值写入 Framebuffer
*pen_16 = color;
break;
}
case 32:
{
//对于 32bpp,颜色格式跟 color 参数一致,可以直接写入Framebuffer
*pen_32 = color;
break;
}
default:
{
printf("can't surport %dbpp\n",var.bits_per_pixel);
break;
}
}
}
int main(int argc,int **argv)
{
int i;
fd_fb = open("/dev/fb0", O_RDWR);
if (fd_fb < 0)
{
printf("can't open /dev/fb0\n");
return -1;
}
if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))
{
printf("can't get var\n");
return -1;
}
line_width = var.xres * var.bits_per_pixel / 8;
pixel_width = var.bits_per_pixel / 8;
screen_size = var.xres * var.yres * var.bits_per_pixel / 8;
fb_base = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
if (fb_base == (unsigned char *)-1)
{
printf("can't mmap\n");
return -1;
}
/* 清屏: 全部设为白色 */
memset(fb_base, 0xff, screen_size);
/* 随便设置出 100 个为红色 */
for (i = 0; i < 100; i++)
lcd_put_pixel(var.xres/2+i, var.yres/2, 0xFF0000);
close(fd_fb);
return 0;
}
ubuntu でコンパイルし、開発ボード上で実行すると、システム GUI を無効にしていないため、右下に時間が表示されます。