低コスト・ハードウェア上のTouchGFX
このセクションでは、RAMやFlashの搭載量が制限され、アクセラレーション機能がなく、外部FlashやディスプレイへのSPI接続が低速である低コスト・ハードウェア上でTouchGFXを使用する方法について説明します。
また、与えられたハードウェアで最適なアプリケーションを作成するための、いくつかアドバイスを提示したいと思います。
このセクションを通して、ハードウェア例として、X-Nucleo-GFX01M1拡張ボード付きSTM32G071 Nucleoボード向けのTouchGFXボード設定を使用します。 この拡張ボードには16ビット・ディスプレイとシリアルFlashが搭載されています。
ハードウェアの概要
このキットのハードウェア構成は、STM32G071マイクロコントローラ、SPI NOR Flash、SPIディスプレイ、ジョイスティック・ボタンから成り立っています。
コンポーネント | |
---|---|
マイクロコントローラ | STM32G071RB |
マイクロコントローラ内蔵RAM | 32KB |
マイクロコントローラ内蔵Flash | 128KB |
ディスプレイ | Displaytech DT022CTFT |
ディスプレイ解像度 | 240x320 |
ディスプレイ・フォーマット | 16ビットRGB565 |
ディスプレイ・コントローラ | ILI9341V |
ディスプレイ接続 | SPI |
ディスプレイ接続速度 | 32MHz |
NOR Flash | Macronix MX25L6433F |
NOR Flashサイズ | 64Mbit |
NOR Flash接続速度 | 32MHz |
ディスプレイはSPI1ペリフェラルに、FlashはSPI2ペリフェラルに接続されています。 これにより、マイクロコントローラはデータをディスプレイに転送中に、Flashからデータを読み込むことができます。
GPIO割り当て
信号 | GPIOピン |
---|---|
ディスプレイCS | PB5 |
ディスプレイDCX | PB3 |
ディスプレイSCK | PA5 |
ディスプレイMOSI | PA7 |
ディスプレイTE | PA0 |
Flash CS | PB9 |
Flash SCK | PB13 |
Flash MOSI | PC3 |
Flash MISO | PC2 |
上のテーブルは、Flashおよびディスプレイへの信号のGPIO割り当てを示しています。 これらの信号は、オシロスコープまたはロジック・アナライザを使用してモニタできます。 これはパフォーマンスに関する問題などのデバッグ時に非常に役立ちます。
プロジェクトの開始
STM32G071RB Nucleo評価キットのプロジェクトは、TouchGFX Designerで簡単に開始できます。 "Create New"ボタンをクリックして、STM32G071 Nucleoを検索します。 このボード設定は、X-Nucleo-GFX01M1ディスプレイ・シールド付きのNucleo-G071RBキット用に特に開発されたものです。
TouchGFXボード設定は、NOR Flash、ディスプレイ、ボタンをサポートします。 ディスプレイは縦向きと横向きの両方のモードで使用できます。
ディスプレイの向きは、TouchGFX DesignerのConfig -> Displayセクションで変更できます。
X-Nucleo-GFX01M1シールドのディスプレイは、元々は縦向き(幅より高さの方が長い)ですが、横向きでも簡単に使用できます。
ディスプレイの更新
上に示したディスプレイの解像度は240x320ピクセルです。 合計で76,800ピクセル(153,600バイト)です。 マイクロコントローラとディスプレイ間の接続はSPI、32MHzです。 これにより、4MB/秒(2Mピクセル/秒)の転送が可能になります。
ディスプレイのリフレッシュ・レートは76.1Hzで、これにより1フレームあたり13.14msのフレーム時間となります。
つまり、次のフレーム用のデータ転送にかけられる時間は、長くて13ms程度ということです。 この時間内で、200万ピクセル/秒 / 76 fps = 26,280ピクセル/フレーム(または、フル・スクリーンの34%)を転送できます。 実際には、プロトコル・オーバーヘッドのために、SPIバス上でこの転送速度を維持することはできないので、フレーム全体の約30%以上を送信することは期待できません。
アプリケーションの更新がこのピクセル量を超える場合、ハードウェアはフレーム時間内に転送を完了できません。 その結果、ディスプレイには更新全体が完了する前に、更新中のフレームが表示されることになります。 このため、古いフレームと新しいフレームが混じった状態で表示される場合があります。
この現象は、一部のアニメーションではユーザにとって目立つものではありませんが、特定のアプリケーションでは受け入れがたいものとなります。
このためSTでは、更新のレベルを30%未満に抑えることを推奨しています。 たとえば、 フレームの更新を少しずつ行う方法です。
このことから、一般的にはアイテムの移動よりも画面上でアイテムを展開する方がうまくいきます。
星を右側に移動する場合は、その星によってカバーされているすべてのピクセルを更新する必要があります。 星を展開する場合には、新しいピクセルのみを更新する必要があります。 前のフレームで更新されたピクセルは変更されずにそのまま残ります。
描画速度
ディスプレイへの転送は、最大32MHzで実行されます。
シリアルFlashはディスプレイと同じ転送速度で動作できます。 つまり、シリアルFlashには、ディスプレイに最大速度でビットを転送するための十分な速度があるということです。
これは、Flash内の画像のピクセル・フォーマットがRGB565である場合にのみ成り立ちます。 この場合は、Flashからの2バイトの読み取りが1ピクセルに相当し、ディスプレイ上でも2バイトになります。 Flash内のピクセル・フォーマットがARGB8888の場合、ディスプレイ上で1ピクセルを生成するにはFlashから2倍の量のデータを読み取る必要があり、シリアルFlashはディスプレイに追いつけなくなります。
この場合、ディスプレイにデータが連続的に転送されなくなり、1フレームでディスプレイの30%を完全に更新することが不可能となります。 可能な解決法の1つは、画像を内蔵Flashに移動することで、もう1つは当然ながらピクセル・フォーマットを変更することです。
その他のウィジェットはFlashの速度に縛られません。 たとえば、 色の付いた長方形を描画するボックス・ウィジェットがあります。 もちろんこのウィジェットは非常に高速で、ディスプレイの転送速度を大きく上回っています。 ラインやサークルなどのその他のウィジェットは、CPUリソースを非常に多く使用します。 これらのウィジェットでは、ディスプレイに転送可能な速度でピクセルを生成することができません。 これらのウィジェットを使用する場合、アプリケーションは、すべてのフレームでディスプレイの30%を更新することを期待できません。
ピクセルのレンダリングの複雑さについては、こちらをご覧ください。
シリアルFlash搭載のTouchGFXの制限事項
シリアルFlashを搭載したSTM32G0上のTouchGFXには、アプリケーション・プログラマが注意する必要のあるいくつかの制限事項があります。
テクスチャ・マッパー
テクスチャ・マッパー・ウィジェット(Texture Mapper、Animation Texture Mapper、Scalable Image)では、外部SPI Flashに保存される画像を描画できません。 シリアルFlashに保存されている画像を使用した画像回転などでは、許容可能な性能を得ることができないからです。
テクスチャ・マッパーで画像を使用するには、その画像を内蔵FlashまたはRAMに保存する必要があります。 TouchGFX Designerで画像設定を変更すれば、簡単に画像を内部Flashに保存できます。
Imagesタブに移動して、画像を選択します。 ウィンドウの右側で、"Section"属性を"IntFlashSection"に変更します。
テクスチャ・マッパー・コードは大きすぎるので、すべてのプロジェクトに含めることができません。 このため、STM32G0プロジェクトではデフォルトで無効化されています。 つまり、テクスチャ・マッパーをSTM32G0プロジェクトで使用するには、使用前に有効化する必要があります。
"Config"タブに移動して、"Framework Features"を選択し、関連するテクスチャ・マッパーまたはテクスチャ・マッパーのグループをクリックします。
Bitmap Cacheを使用して、画像を一時的にRAMに移動することもできます。
ビットマップ・ペインタ
ライン、サークル、ダイナミック・グラフのウィジェットでは、画像に色付けできます。 ただし、SPI Flashにある画像ではこれはできません。 これらのウィジェットで使用する画像は、内蔵FlashまたはRAMに配置する必要があります。
L8パレット
L8フォーマットの画像をシリアルFlash搭載のハードウェアで使用できます。 ただし、パレット・データを内蔵Flashに配置する必要があるという制限があります(これも性能上の理由です)。
画像のパレットを内蔵Flashに移動するには、TouchGFX Designerで"Extra Section"を"IntFlashSection"に変更します([Images]タブを開いて、画像を見つけ、右端の列に移動します)。
ドライバ
TouchGFXボード設定はTouchGFX Generatorを使用して作成します。 Generatorの詳細については、こちらを参照してください。 TouchGFX GeneratorはHALレイヤを生成します。HALレイヤは、TouchGFXフレームワークを一連のローレベル・ドライバ(このTouchGFXボード設定ですでに実装済み)とリンクさせます。 このアプリケーション・テンプレートのローレベル・ドライバは、ユーザのプロジェクトのCore/Src
フォルダにあります。
ドライバは、次の3つのファイルの中にあります。
ドライバ | ファイル |
---|---|
ディスプレイ | Core/Src/MB1642BDisplayDriver.c |
Flash | Core/Src/MB1642BDataReader.c |
ボタン | Core/Src/MB1642BButtonController.cpp |
ディスプレイ
ディスプレイではごく標準的なSPIプロトコルが使用されます。 いくつかのレジスタは、ディスプレイを設定するために書き込むことが可能です。 データをディスプレイに転送するときに、チップ・セレクトがアサートされます。 その他のGPIOやDCXは、コマンド・バイトとデータ・バイトを区別するために使用されます。
ドライバはDMAチャネルを使用して、ディスプレイのピクセル・データを転送します。 これにより、マイクロコントローラがピクセルを計算中でも、データを転送することができます。 DMA完了の割込みを使用して、今後の描画での再利用のために伝送済みのメモリを開放したり、新しいデータがすでに用意されていれば転送を再開したりします。
設定データはDMAでは送られません。このディスプレイのプロトコルに応じて、小型パッケージ間(またはパッケージ内)でCSピンとCDXピンを切り替える必要があるからです。
ドライバは設定データの送信時には8bitモードでSPIを使用しますが、ピクセル・データの転送時には16bitモードに変更します。 これは、マイクロコントローラのメモリがリトル・エンディアン・モードで読み出されるからです。 RGB565フォーマットのピクセルは、最初に下位バイト(GおよびB)が、2番目に上位バイト(RおよびG)がRAMに保存されます。 この順序は、8bit SPIが転送のためにメモリを読み取るときにも保持されます。 SPIが16bitモードの場合、データはメモリから16bit RGB565として読み取られ、ディスプレイにとって正しい順序で転送されます。
16bit DMAを使用しないドライバは、転送前にピクセル内のバイトをスワップする必要があります。
初期化
ディスプレイの初期化は、MB1642BDisplayDriver_DisplayInit(void)
という関数にあります。
ドライバは6つのコマンドをディスプレイに送信しますが、これは推奨されるパワー・オン・シーケンスに従っています。
- Sleep Mode(11h)を終了する
- Normal Mode(13h)に入る
- MXおよびBGRビットを使用して、Memory Access Control(36h)を設定する
- 16bitフォーマットで、Pixel Format(3Ah)を設定する
- Tearing Effect Line On(35h)
- ライン = 0で、Tear Scanline(44h)を設定する
これらのコマンドの間に、ドライバは100msスリープします。
ティアリング効果
ディスプレイからのティアリング効果(TE)信号は非常に重要です。 これにより、アプリケーションはディスプレイ・メモリの更新をディスプレイのリフレッシュ・レートと正確に同期することができます。 アプリケーションが、ディスプレイ上のティアリングを防止するのにも役立ちます。 ディスプレイは更新サイクルを開始すると、必ずこの信号をアサートします。 マイクロコントローラはこの信号を使用して、ディスプレイへのデータ転送を開始します。
TE信号は、マイクロコントローラの外部割込み入力に接続されています。 CubeMxは、このピン上に割込みを生成して設定します。
ドライバ内のコールバックが、TouchGFXに描画開始の信号を送ります。
MB1642BDisplayDriver.c
void HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin)
{
...
touchgfxSignalVSync();
}
外部Flash
ディスプレイ・シールド上のSPI NOR Flashは、標準のSPI Flashです。 このドライバはディスプレイ・ドライバよりシンプルです。 Flashからデータを読み取るために、特別な初期化の必要はありません。
ドライバはポーリングされたSPI(全バイトでビジー・ウェイト)を使用してデータを読み取るか、DMAを使用することができます。 DMAでデータ受信の開始までの時間は、ポーリング・モードで20バイトを読み取るためにかかる時間とほぼ同じなので、短いデータの読み取りではポーリングより遅くなることがよくあります。 一方、DMAは割込み中(sysTickまたはアプリケーション割込みなど)でも実行を続け、マイクロコントローラがピクセルの描画でビジー状態にあるときでも、バックグラウンドで実行可能です。 こうした理由から両方の方法が使用されます。
Flashドライバはディスプレイ・ドライバとは別のDMAチャネルを使用するので、新しいデータの受信とすでに描画済みのピクセルの転送を同時に実行できます。
リンカ・スクリプト
リンカによって、アプリケーション内のさまざまなデータの配置場所をコントロールします。 これはリンカ・スクリプトで指定します。 gccコンパイラ用のリンカ・スクリプトの最初の部分を以下に示します。
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 36K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 128K
SPI_FLASH (r) : ORIGIN = 0x90000000, LENGTH = 8M
}
ここでは、NOR Flashのアドレスが0x90000000から始まると宣言しています。 ターゲット・アプリケーションのFlashドライバは、SPI Flashから0x90000000アドレスにあるデータを読み出します(下位24ビットをFlash内のアドレスとして使用)。
STM32CubeProgrammerで使用される外部Flashローダは、この範囲のデータをSPI Flashに書き込むことができます(下記を参照)。
この次のセクションでは、SPI Flashに画像(ExtFlashSection)データとフォント(FontFlashSection)データを配置しています。
ExtFlashSection :
{
*(ExtFlashSection ExtFlashSection.*)
*(.gnu.linkonce.r.*)
. = ALIGN(0x4);
} >SPI_FLASH
FontFlashSection :
{
*(FontFlashSection FontFlashSection.*)
*(.gnu.linkonce.r.*)
. = ALIGN(0x4);
} >SPI_FLASH
リンカ・スクリプトに同様のセクションを追加することで、その他のデータもSPI Flashに配置することができます。
Flashローダ
G071 TouchGFXボード設定には、STM32CubeProgrammer用のFlashローダが含まれています。 Flashローダは、SPI NOR Flashにデータを書き込むことができます。
このFlashローダは、gcc/S25FL032P_STM32G071B-NUCLEO.stldr
というファイルにあります。
STM32CubeIDEプロジェクトはCubeIDEから直接書き込めますが、IARまたはKeilアプリケーションはSTM32CubeProgrammerを使用して書き込む必要があります。
FlashローダはSTM32CubeProgrammerで最初から使用することはできず、インストール場所にstldrファイルをコピーすることでインストールする必要があります。
これで通常どおりに、STM32CubeProgrammerでFlashローダを選択できるようになります。
Tip
シリアルFlash用の別のGPIO設定がカスタム・ハードウェア上で使用されている場合は、Flashローダを同じように変更する必要があります。
ボタン
ボタン・ドライバは非常にシンプルです。 ここでは、MB1642B上でジョイスティック用に使用される5つのGPIOと、Nucleoボード上の青色のユーザ・ボタンの状態を取得します。
このボタン・ドライバは、TouchGFXでBottonControllerとしてインストールされています。 つまり、ボタンの押下はTouchGFX Designerのインタラクション内で直接利用可能だということです。 以下のように、ユーザ・コード内で使用することもできます。
void Screen1View::handleKeyEvent(uint8_t key)
{
if (key == '6')
{
application().gotoScreen2Screen();
}
}
使用されるキー・コードは以下のとおりです。
キー | コード |
---|---|
左(Left) | '4' |
右(Right) | '6' |
上(Up) | '8' |
下(Down) | '2' |
中央(Center) | '5' |
青色のユーザ・ボタン(Blue User Button) | '0' |
これらのキーは、キーボードのテンキーを使用してシミュレータを実行する(TouchGFX DesignerでF5
キーを押す)場合にも使用できます。