画像保存のために非メモリ・マップドFlashを使用
このセクションでは、非メモリ・マップドFlash内に配置可能なバイナリ・ファイルにすべての画像をリンクさせる方法と、実行時にそのファイルをビットマップ・キャッシュと一緒に使用する方法について説明します。 TouchGFXでは、非メモリ・マップドFlashに保存されたビットマップを描画することはできませんが、RAM内にビットマップをキャッシュすることで、アプリケーションでそのビットマップを利用可能にできます。
ビットマップ・キャッシュの全般的な説明については、Caching Bitmapsの記事を参照してください。
この記事では、ユーザがビットマップ・キャッシュを設定済みであり、ビットマップを非メモリ・マップドFlashに保存することを想定しています。 非メモリ・マップドFlashの例としては、USBストレージやNAND Flashなどが考えられます。
ここでの目的は、画像を特定のアドレスにリンクし、その画像をファイルにコピーして、TouchGFXが画像をそのファイルからキャッシュにコピーできるようにすることです。
Flashからキャッシュへのビットマップ・データのコピー
ビットマップをキャッシュするときには、TouchGFXがピクセルを元の場所からビットマップ・キャッシュにコピーしていることを思い出してください。
このコピーは、HALクラスから次のメソッドを呼び出すことで実行されます。
HAL.hpp
bool HAL::blockCopy(void* RESTRICT dest, const void* RESTRICT src, uint32_t numBytes);
ビットマップが通常のアドレス指定可能なFlash(内部Flashやメモリ・マップド外部Flashなど)に保存されている場合は、TouchGFXライブラリで提供される通常のblockCopy関数が使えるので、ユーザは何も行う必要がありません。
一方で、ビットマップがアドレス指定できないストレージ(ファイルシステムなど)に保存されている場合は、通常の実装では不十分なので、特定のFlashストレージから読み取りできるよう更新されたバージョンを用意する必要があります。
しかしまずは、ビットマップが固定アドレスにリンクされていることを確認する必要があります。
Bitmapデータベーステーブル
TouchGFXのすべてのビットマップは、generated/images/srcフォルダ内の.cppファイルに生成されます。 ここではビットマップがバイト配列として表されます。
これらの配列は、他のすべてのソース・コードと同様にC++コンパイラによってコンパイルされ、アプリケーションにリンクされます。
次に、ボタンと回転するロゴを示すテクスチャ・マッパーによる、簡単なアプリケーションのスナップショットを示します。
このアプリケーションは、Button_Pressed、Button_Released、Logoという3つの画像を使用しています。
これら3つのビットマップは.cppファイルに変換され、アプリケーションの一部としてリンクされます。 これらの画像は、bitmap_databaseという名前のテーブル内で参照されます。 このテーブルはBitmapDatabase.cppファイル内にあります。 上の例によるテーブルを次に示します(詳細の一部は削除されています)。
BitmapDatabase.cpp
extern const unsigned char _blue_buttons_round_edge_small[];
extern const unsigned char _blue_buttons_round_edge_small_pressed[];
extern const unsigned char _blue_logo_touchgfx_logo[];
const touchgfx::Bitmap::BitmapData bitmap_database[] =
{
{ _blue_buttons_round_edge_small, ... },
{ _blue_buttons_round_edge_small_pressed, ... },
{ _blue_logo_touchgfx_logo, ... }
};
最初に宣言された配列は、個々のビットマップのピクセルを含む配列です。 これらの配列は他の.cppファイルで定義されます。 これらの配列のアドレスは、bitmap_database配列に保持されています。 TouchGFXはこの配列を使用して、ビットマップのピクセルのアドレスを見つけます。
プログラマがビットマップをキャッシュするようにリクエストすると、TouchGFXがFlash内(bitmap_database配列の中)のビットマップのアドレスを見つけ、ここからデータをコピーします。
リンカ・スクリプトの変更
リンカはビットマップのアドレスを設定します。 この設定は、ビットマップが配置されるセクションをベースにします。 TouchGFXのすべてのビットマップは、デフォルトでExtFlashSectionに配置されます。
標準のリンカ・スクリプト(ここではGCC用)は、このセクションを他の読み取り専用データと一緒にFlash内に配置します。
この例では、アドレスが0x24000000のExtFlashSectionに画像データを配置します。 他で使用されていない(コードまたはデータのアドレス空間の一部でない)任意のアドレスを選択できます。
まず、通常の内部FlashとRAMの領域に加えて、新しいメモリ領域(アドレスが0x24000000のUSB Flash)を定義します。
STM32F746.ld
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 320K
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K
USB(r) : ORIGIN = 0x24000000, LENGTH = 1M
}
次に、このUSB領域にExtFlashSectionを配置するようリンカに命令します。
STM32F746.ld
ExtFlashSection :
{
*(ExtFlashSection ExtFlashSection.*)
} >USB
リンク後に、マップ・ファイル(application.map)を調べることでビットマップのアドレスを確認できます。 関連する部分を以下に示します。
application.map
ExtFlashSection
0x24000000 0x23ec0
*(ExtFlashSection ExtFlashSection.*)
ExtFlashSection
0x24000000 0x10000 TouchGFX/build/.../Blue_Logo_touchgfx_logo.o
0x24000000 _blue_logo_touchgfx_logo
ExtFlashSection
0x24010000 0x9f60 TouchGFX/build/.../Blue_Buttons_Round_Edge_small.o
0x24010000 _blue_buttons_round_edge_small
ExtFlashSection
0x24019f60 0x9f60 TouchGFX/build/.../Blue_Buttons_Round_Edge_small_pressed.o
0x24019f60 _blue_buttons_round_edge_small_pressed
ここでは、画像の合計サイズが0x23ec0 = 147,136バイトであることがわかります。 ビットマップを保持する3つの配列は、アドレス0x24000000からシーケンシャルに配置されています。
では、ビットマップ・データをSDカードに移したい場合を考えてみましょう。 ビットマップのバイナリ・データは、単純なobjcopyコマンドを使用して.elfファイルから抽出できます。
$ arm-none-eabi-objcopy.exe --dump-section ExtFlashSection=images.bin TouchGFX/build/bin/target.elf
$ ls -l images.bin
-rw-r--r-- 1 christef Administrators 147136 Feb 20 11:56 images.bin
これにより、画像のバイト配列のみを含むファイル(images.bin)を入手できます。 このファイルはUSB FlashまたはSDカードにコピーしたり、Flashチップにプログラムしたりすることもできます。
ここで行いたい事は、TouchGFXが上記のアドレス0x24000000のデータを求めたときに、SDカード上のimages.binファイルからデータを取り出すことです。
BlockCopy関数の変更
ビットマップをRAMにキャッシュしたとき、TouchGFXはHAL::BlockCopyを呼び出してデータを取得することを思い出してください。
これをSDカード上のデータにリンクさせるために、特定のHALクラスに新しいBlockCopyを実装できます。 ここでは、このクラスの名前をTouchGFXHAL(TouchGFX Generatorによって生成されるため)にします。
TouchGFXHal.hpp
class TouchGFXHAL : public TouchGFXGeneratedHAL
{
public:
...
virtual bool blockCopy(void* RESTRICT dest, const void* RESTRICT src, uint32_t numBytes);
}
TouchGFXHal.cpp
// This function is called whenever a bitmap is cached. Must copy a number of bytes from the (non-memory-mapped) source
// to the cache.
bool TouchGFXHAL::blockCopy(void* RESTRICT dest, const void* RESTRICT src, uint32_t numBytes)
{
// If requested data is located within the memory block we have assigned for ExtFlashSection,
// provide an implementation for data copying.
if (src >= 0x24000000 && src < 0x24100000)
{
void* dataOffset = src - 0x24000000;
// In this example we assume graphics is placed in SD card, and that we have an appropriate function
// for copying data from there.
sdcard_read(dest, dataOffset, numBytes);
return true;
}
else
{
// For all other addresses, just use the default implementation.
// This is important, as blockCopy is also used for other things in the core framework.
return HAL::blockCopy(dest, src, numBytes);
}
}
これで、SDカードからビットマップをキャッシュできるようになりました。
TouchGFXは、キャッシュされていないビットマップを描画しようとする場合、bitmap_database
テーブルで見つかったアドレスからピクセルを読み取ろうとします。 このアドレスは、0x24000000~0x24100000の範囲内になるので、読み取りによって例外が発生します。
画像のRAMへのリンク
すべての画像を保持できる十分な量のRAM(上の例では147,136バイト超)が使用可能な場合は、ビットマップ・キャッシュを使用して画像をコピーする必要はありません。
この場合の方策は、次のとおりです。
- 画像用の固定アドレスとRAMの範囲を選択する
- リンカ・スクリプトでRAM領域からその範囲を削除する
- 選択したアドレスとサイズで、新しい領域IMAGESを作成する
- IMAGES領域にExtFlashSectionを配置する
- アプリケーションをリンクし、.mapファイルを確認する
- application.elfからimages.binファイルを作成する
- TouchGFXを開始する前に、images.binファイル全体をSDカードからRAM内の選択されたアドレスにコピーする
このソリューションはシンプルですが、いくつか欠点があります。
- すべての画像を保持できる十分な量のRAMが使用可能であること
- SDカードからのコピーなので起動に時間がかかる(数MBに数秒かかる可能性がある)