はじめに
前回に引き続きRaspiのベアメタルプログラムを解説していきたいと思います。
今回はディスプレイに色をつけていきたいと思います。
またいちいちコマンドを打ってコンパイル、リンクするのは面倒なので、Makefileも作っていきたいと思います。
概要
RaspiのCPUとGPU(VideoCore)はMailboxによって通信します。
そのMailboxにフレームバッファのアドレスを聞いて、そのアドレスに色やピクセルの座標などの情報を書き込むとそのデータに基づいて画面が構成される仕組みになっています。
characterディレクトリはHello World!を表示するプログラム
exhibitionディレクトリは文字に加えて背景に色をつけるプログラム
一部ここのプログラムのrpi2_boot2.zipを使わせて頂いています。
mailboxについて
今回mailboxについては簡単に解説しておこうと思います。
Mailboxにはwriteとread関数があります。
write 関数には以下のような構造体を使い、ディスプレイの設定(解像度や色深度など)を書き込みます。
typedef struct mailbox_fb_t {
uint32_t width; // 実際の横幅
uint32_t height; // 実際の縦幅
uint32_t vwidth; // 仮想の横幅
uint32_t vheight; // 仮想の縦幅
uint32_t pitch; // 1行あたりのバイト数(GPUが計算して返す)
uint32_t depth; // 色深度(例:32bit)
uint32_t x; // Xオフセット
uint32_t y; // Yオフセット
uint32_t pointer; // フレームバッファ先頭アドレス(GPUが返す)
uint32_t size; // フレームバッファのサイズ(バイト)
} mailbox_fb;
read 関数を呼ぶと、VideoCore(GPU)が確保したフレームバッファのアドレスを教えてくれます。
このとき、ARM CPUが持つアドレスをGPU用のアドレスに変換して渡す必要があります(例:+ 0xC0000000)。
#define VCADDR_BASE 0xC0000000
uint32_t ArmToVc(void *p) { return ((uint32_t)p) + VCADDR_BASE; }
uint32_t VcToArm(uint32_t p) { return (uint32_t)(p) & ~(VCADDR_BASE) ; }
参考サイトでは5.のレジスタをメモリに書き込む前に
bic addr, #0xC0000000
strh fore,[addr]
.unreq fore
.unreq addr
mov pc,lr
bicという命令(VcToArm関数とやっていることは同じ)をする必要があります。
Mailboxのread,write関数の詳細については以下が参考になります:
ピクセルに色を付ける
例えば画面を真っ白にしたかったら
uint16_t *buf = (uint16_t *)VcToArm(mailbox_fb_ptr());
for(int y = 0; y < HEIGHT; y++) {
for(int x = 0; x < WIDTH; x++) {
buf[x + y * WIDTH] = 0xFFFF;
}
}
VideoCoreから教えてもらったフレームバッファのアドレスをArmアドレスに変換してX,Yのピクセルに色を書き込みます。
今回、色深度を16ビットに設定しているので16ビットの白を書き込んでいます。
Makefile
前回コンパイル、リンクをコマンドで行なってきましたが、Makefileを作ってmakeコマンドだけでコンパイル、リンクできるようにしました。
ARMGNU ?= arm-none-eabi
TARGET = kernel.img
LINKER = memorymap.ld
SRC = $(wildcard *.c)
SRO = $(wildcard *.s)
OBJECTS = $(SRO:%.s=%.o)
GCC := $(SRC:%.c=%.o)
all: $(TARGET)
$(TARGET): kernel.elf
$(ARMGNU)-objcopy -O binary kernel.elf $(TARGET)
kernel.elf: $(OBJECTS) $(GCC) $(LINKER)
$(ARMGNU)-ld -T $(LINKER) *.o -o kernel.elf
# .c.o:ターゲットは.oというファイルが必要になれば、これを.cから作るというルール。
# すべての依存するファイル名は.cだから{$^}、ターゲットファイル名は.oだから{$@}
.c.o:
$(ARMGNU)-gcc -c -mcpu=cortex-a53 -g $^ -o $@
%.o: %.s
$(ARMGNU)-as -mcpu=cortex-a53 -mfpu=vfpv3 $^ -o $@
基本的にMakefileの書き方はこちらのサイトを参考にさせてもらいました。
Makefileの処理は以下のように再帰的に処理をしていきます。
-
all: kernel.img
まずallターゲットを探します。このallは処理の入り口みたいなものです。
kernel.img を作る必要があるので、そのターゲットを探す -
kernel.img: kernel.elf
kernel.elf を作る必要があるので、そのターゲットを探す -
kernel.elf: $(OBJECTS) $(GCC) $(LINKER)
.oファイルやリンカスクリプトが必要
.oファイルがなければ、.c.oルールや%.o: %.sルールで生成 - すべての.oが揃ったら kernel.elf をリンク
- kernel.elfができたら kernel.img を生成
最後に
今回はピクセルに色を付けるプログラムを作成しました。
これができるようになればプログラムで絵を描いたり、ビットマップフォントを使って文字を表示できるようになったり、できることの幅が広がります。
次はこのプログラムを使ってドット絵のアニメーションみたいなことをやっていきたいと思います。