メイン・コンテンツまでスキップ

チュートリアル7: TSDノブ・ディスプレイ向けアプリケーションの作成

このチュートリアルでは、TSD TSS013004A-ADディスプレイ・ノブ・モジュール用のアプリケーションを作成して書き込みます。 これは、STM32C091 + 8MB外部Flashをベースとする1.3インチ円形ディスプレイ・モジュールを使用したエントリレベルのグラフィックスで、パーシャル・フレームバッファで実行されます。 このアプリケーションは簡単なGUIを備えており、ディスプレイ・モジュール(物理コントロール、周辺光、UART通信)のインタフェースの使用方法を表示します。 TSD TSS013004A-ADディスプレイ・ノブ・モジュールのマイコン・プラットフォームの仕様は以下のとおりです。

  • 48MHz CPU(STM32C091)
  • 36KB内蔵SRAM
  • 256KB内蔵Flash
  • 8MB外部シリアルFlash
  • 24MHz SPIディスプレイ・インタフェース

このチュートリアルの内容を理解するための前提条件として、チュートリアル1チュートリアル2を学習することをお勧めします。

ステップ1: TouchGFXプロジェクトの開始

TouchGFX Designerでは、ディスプレイ・モジュール用のアプリケーション・テンプレート(TBS)が提供されており、それを使用してすべての基本機能をセットアップします。 以下に基づいて新しいプロジェクトを作成します。

  1. [Create]タブで、"TSDHD TSS013-1"という名前のボードを選択します。
  2. [Application Name]を設定します(例: KnobApplication)。
  3. [Create]をクリックします。
    123

    ディスプレイ・モジュール用のプロジェクトを作成

ステップ2: 基本的なGUIの構築

プロジェクトを作成すると、240x240ピクセルの空白のキャンバスが表示されます。 このステップでは、アプリケーション用のGUIをデザインします。 次に示すような1つのスクリーン(Screen1)を作成します。

このチュートリアルで構築するGUIのスクリーンショット

このスクリーン用の画像アセットはこのリンクからダウンロードできます。 このファイルを、自分のプロジェクト内のTouchGFX/assets/imagesフォルダに解凍します。 GUIを作成するには、次の表に従って、TouchGFX Designerにウィジェットを追加して設定します。

順序ウィジェット名前プロパティ
1ImagebackgroundImage
  • X: 0
  • Y: 0
  • Image: meteor_rain.png
2CircleProgressvalueCircleProgress
  • X: 25
  • Y: 25
  • Preset: "Action Large" (from "Alternate Theme")
3ImagewifiImage
  • X: 90
  • Y: 155
  • Image: wifi.png
4ImagealarmImage
  • X: 120
  • Y: 155
  • Image: alarm.png
5ImageunlockedImage
  • X: 85
  • Y: 85
  • Image: lock_open.png
6ImagelockedImage
  • X: 85
  • Y: 85
  • Image: lock.png

ステップ3: ディスプレイに対するアプリケーションのプログラミング

次に、ディスプレイ上にGUIを表示するために、ディスプレイ・モジュールに対する予備的なアプリケーションをプログラムします。 STマイクロエレクトロニクスが作成したすべてのDiscovery KitとNUCLEOボードにはST-LINKハードウェアが搭載されており、通常はこれを使用して、こうしたアプリケーションをプログラムします。 しかし、このサードパーティのディスプレイ・モジュールにはST-LINKが組み込まれていません。 その代わりに、以下の2つの代替方法を使用してプログラムできます。

UARTを使用するプログラミング

このディスプレイ・モジュールのアダプタ・ボードにはUSB-to-UARTチップが搭載されており、これによってPCとディスプレイ・モジュール内のマイクロコントローラとの通信が可能になります。 マイコン・ブートローダのUARTインタフェースを利用して、この方法でディスプレイ・モジュールをプログラムすることもできます。 この方法は、TouchGFX Designerで[Run target]をクリックしたときに、このプロジェクトのmakefileによって使用されます。 このプロジェクトの"Run target"スクリプトが機能するには、以下の要件があります。

  • ディスプレイ・モジュールがデータUSBケーブル経由でPCに接続されていること。
  • STM32CubeProgrammer(v2.18.0以降)がデフォルトの場所にインストール済みであること。
  • USB-to-UARTのドライバがインストール済みであること。 ここからダウンロード可能: https://www.wch-ic.com/downloads/ch341ser_exe.html
  • マイクロコントローラのnBOOT_SELオプションバイトがFALSEに設定されていること。 これは配布されたモジュールのデフォルトです。

[Run target]をクリックし、コンパイルとプログラミングが完了するまで待つと、DesignerキャンバスのGUIがモジュール・ディスプレイに表示されます。

Tip
アプリケーション全体のプログラミングが一度完了したら、残りのチュートリアルでは"Run target - Internal"が使用可能になります。 これは内部Flash内のアプリケーション・コードを再プログラムするだけで、外部Flash内のアセットのプログラムはスキップするので、プログラミング時間を短縮できます。 アセットが変更されない限り機能します。
Further reading
UARTを使用して内部および外部Flashをプログラムするプロセスは、実際には非常に包括的なタスクです。 ただし、プロジェクトに含まれるrubyスクリプトによってすべて処理できます(Loading/Load_OpenBootloader.rb)。 このスクリプトでは以下のステップでプログラムします。
  1. マイクロコントローラのRESETおよびBOOT0ピンを制御して、マイクロコントローラを再起動し、内蔵ブートローダを実行します。
  2. マイクロコントローラのRAM(ST OpenBootloaderに基づく)にカスタム・ブートローダをプログラムして実行し、外部Flashをプログラムできるようにします。
  3. マイクロコントローラの内部Flashと外部FlashにTouchGFXアプリケーションをプログラムします。
  4. マイクロコントローラのRESETピンを制御し、マイクロコントローラを再起動してTouchGFXアプリケーションを実行します。

外部ST-LINKを使用するプログラミング

モジュールのもう1つのプログラム方法では、ディスプレイ・モジュールのアダプタ・ボード上のピンに有線接続されている外部デバッガを使用します。 ST-LINK/V2デバッガを使用する際には、以下の表に示すような有線接続が必要です。

ST-LINK/V2、20ピン・コネクタノブ・アダプタ・ボード
15V
4GND
7SWD
9SWC

ST-LINKとSTM32CubeProgrammerを使用して外部Flashも書き込むことができるように、プロジェクトのLoading/TSDHD-TSS013-1.stldrに外部ローダ・ファイルが提供されています。 ST-LINKを使用する利点の1つは、プログラミングに加えて、(STM32CubeIDEなどを使用して)実行しているアプリケーションのデバッグも可能になることです。

外部ST-LINKはこのチュートリアルでは必須ではありません。

ステップ4: 物理入力へのインタラクションの追加

ディスプレイ・ノブ・モジュールには3つの物理的なユーザ入力メカニズム(左回し、右回し、押下)が存在します。 このステップでは、ノブを回したときにGUIのCircleProgressの値が変更され、ノブを押したときに画面のロックがオン/オフされるようにします。

TouchGFX/target/KeySampler.cppにあるコードはハードウェア入力を読み出し、さまざまなキーIDにマッピングします。 これらを使用して、以下に示すようなTouchGFX Designerのハードウェア・ボタン・インタラクションをトリガできます。

ハードウェア・ボタンをTouchGFX Designerで入力として使用する方法

次の表に従い、入力ごとに1つのインタラクションを追加します。

インタラクションプロパティ
Knob_Turn_Left
  • Trigger: Hardware button is clicked
  • Button key: 52
  • Action: Call new virtual function
  • Function Name: decrementValue
Knob_Turn_Right
  • Trigger: Hardware button is clicked
  • Button key: 54
  • Action: Call new virtual function
  • Function Name: incrementValue
Knob_Press
  • Trigger: Hardware button is clicked
  • Button key: 53
  • Action: Call new virtual function
  • Function Name: toggleLock

TouchGFX Designerでインタラクションを追加してコードを生成すると、TouchGFX/generated/gui_generated/src/screen1_screen/Screen1ViewBase.cppに、指定された仮想関数を呼び出すコードが生成されます。 目的の機能を実装するには、これら(およびヘルパ関数)を自分のユーザ・コードのスクリーン・クラスのヘッダ・ファイルに追加する必要があります。

TouchGFX/gui/include/gui/screen1_screen/Screen1View.hpp
public:
...
virtual void toggleLock();
virtual void decrementValue();
virtual void incrementValue();
void changeValue(int change);

次に、これらをユーザ・コード・スクリーン・クラスのソース・ファイルに実装します。

TouchGFX/gui/src/screen1_screen/Screen1View.cpp
void Screen1View::toggleLock()
{
lockedImage.setVisible(!lockedImage.isVisible());
lockedImage.invalidate();
}

void Screen1View::decrementValue()
{
if (!lockedImage.isVisible())
{
changeValue(-5);
}
}

void Screen1View::incrementValue()
{
if (!lockedImage.isVisible())
{
changeValue(5);
}
}

void Screen1View::changeValue(int change)
{
const int newValue = valueCircleProgress.getValue() + change;
valueCircleProgress.updateValue(newValue, 0);
}

更新されたアプリケーションでディスプレイ・モジュールをプログラムできるようになりました。 これで、ノブを押してGUIをロック解除し、その後にノブを回してCircleProgressの値を変更できます。

Tip
選択されたキーID(52、53、54)は任意のように見えるかもしれませんが、これらは4、5、6のASCII値に対応して選択されたものです。 これによって、TouchGFX Designerでシミュレータを実行しているときにハードウェア入力をシミュレーションできるようになります。キーボードの押下がハードウェア・ボタン・キーのトリガに変換されるからです。 これによって、キーボード・キーの4、5、6を使用して、シミュレータでGUIの実行を制御できるようになります。 シミュレータの使用方法の詳細については、こちらを参照してください。

ステップ5: LED周辺光の制御

ディスプレイ・ノブ・モジュールには赤色LED、緑色LED、青色LEDのリングがあり、これらをオンにして周辺光として照らすことができます。 各カラー・チャネルはマイクロコントローラからのPWM対応出力で制御されます。 ここで使用するアプリケーション・テンプレートでは、この照明を制御する機能がCore/Inc/knob_interface.cppに実装されており、Core/Src/knob_interface.hppを介して入手できます。

Core/Src/knob_interface.hpp
public:
...
void knobSetAmbientLightRGB(uint8_t red, uint8_t green, uint8_t blue);
...

このデモでは、CircleProgressの値に基づいて照明の色を制御します。 値がゼロのときの照明は青色で、値が大きくなるにつれて徐々に紫色から赤色へと変化していくように制御します。 そのためには、Screen1Viewに以下のコードを追加します。

TouchGFX/gui/src/screen1_screen/Screen1View.cpp
#include <gui/screen1_screen/Screen1View.hpp>
#ifndef SIMULATOR
#include "knob_interface.hpp"
#endif

void Screen1View::setupScreen()
{
Screen1ViewBase::setupScreen();
changeValue(0); // Ensure that the system state is synchronized at start-up
}

void Screen1View::changeValue(int change)
{
const int newValue = valueCircleProgress.getValue() + change;
valueCircleProgress.updateValue(newValue, 0);

#ifndef SIMULATOR
const int value = valueCircleProgress.getValue(); // Always inside the range set in TouchGFX Designer (0-100)

//Update ambient light
const uint8_t red = (value * 255) / 100;
const uint8_t green = 0;
const uint8_t blue = 255 - red;
knobSetAmbientLightRGB(red, green, blue);
#endif
}

ディスプレイ・モジュールでこのアプリケーションを実行すると、ノブをロック解除して回したときに、周辺光が有効になり色が変更されます。

Tip
#ifndef SIMULATORコンパイル・スイッチを使用すると、TouchGFX Designerでシミュレータを実行するときにハードウェア固有のコードを排除できます。

ステップ6: UARTでのデータ送信

ノブ・ディスプレイ・モジュールにはUART通信インタフェースがあり、コネクタ・ケーブルを介して6つのGPIO(PA0-5)を外部使用できます。 このステップでは、CircleProgressの値が更新されるたびに、アプリケーションがUART上でASCII文字列を送信するようにします。 UARTインタフェースはディスプレイ・モジュールのテンプレート・プロジェクト内で設定され、Core/Src/knob_interface.hppを通じて使用できます。

Core/Src/knob_interface.hpp
public:
...
void knobUartSendByte(uint8_t data);
void knobUartSendBytes(uint8_t* data, uint16_t size);
void knobUartSendString(uint8_t* data);
...

ここでは、knobUartSendByte関数を複数回使用して、値を表す文字列のさまざまなASCII文字を送信します。

TouchGFX/gui/src/screen1_screen/Screen1View.cpp
void Screen1View::changeValue(int change)
{
const int newValue = valueCircleProgress.getValue() + change;
valueCircleProgress.updateValue(newValue, 0);

#ifndef SIMULATOR
const int value = valueCircleProgress.getValue(); // Always inside the range set in TouchGFX Designer (0-100)

//Update ambient light
const uint8_t red = (value * 255) / 100;
const uint8_t green = 0;
const uint8_t blue = 255 - red;
knobSetAmbientLightRGB(red, green, blue);

// Send new value on UART
knobUartSendByte('0' + value / 100);
knobUartSendByte('0' + (value % 100) / 10);
knobUartSendByte('0' + value % 10);
knobUartSendByte('\r');
knobUartSendByte('\n');
#endif
}

ディスプレイ・モジュールでこのアプリケーションを実行すると、ノブをロック解除して回したときにUART上でデータが送信されるようになります。 このUARTデータは、UARTプログラミングで使用するものと同じインタフェースを介してPC上で受信できます。 そのためには、PC上にターミナル・アプリケーション(Tera TermやPuTTYなど)をインストールする必要があります。 例として、Tera Termで必要なインタフェース設定を示します。

Tera Termのインタフェース設定のスクリーンショットでは、 COMポートはさまざまに異なるため、お使いのセットアップと合うように選択する必要があります。

PC上のターミナル・アプリケーションに接続した後、ノブを回すと値の文字列の受信と表示が開始されます。

Tera Termで受信した値の文字列のスクリーンショット

ステップ7: UARTからのデータ受信

このステップでは、PCからUARTコマンドを受信したときに、以下の簡単なプロトコルに従ってアプリケーションが反応するようにします。

コマンドアクション
'q'wifiアイコンを非表示にする
'w'wifiアイコンを表示する
'e'アラーム・アイコンを非表示にする
'r'アラーム・アイコンを表示する

Core/Src/knob_interface.hppには、プロジェクト・テンプレートで使用可能なUARTデータを受信するための関数が含まれています。

Core/Src/knob_interface.hpp
public:
...
uint8_t knobUartReceiveByte(uint8_t* data);
...

新しいデータを受信し、実際のバイトを指定のポインタにコピーすると、この関数はtrueを返します。 この関数を使用して、ToucHGFXアプリケーションのティックごとにUART受信バッファをポーリングします。 そのためには、ティックごとにメソッドを呼び出すためのTouchGFXインタラクションを追加する必要があります。

インタラクションプロパティ
Poll UART communication
  • Trigger: On every N tick
  • Tick count: 1
  • Action: Call new virtual function
  • Function Name: receiveUartCommand

次に以下のコードを追加することで、UARTを読み出し、上記のプロトコルを実装します。

TouchGFX/gui/include/gui/screen1_screen/Screen1View.hpp
public:
...
virtual void toggleLock();
virtual void decrementValue();
virtual void incrementValue();
void changeValue(int change);
virtual void receiveUartCommand();
TouchGFX/gui/src/screen1_screen/Screen1View.cpp
void Screen1View::receiveUartCommand()
{
#ifndef SIMULATOR
uint8_t data;
if (knobUartReceiveByte(&data))
{
switch (data)
{
case 'q':
wifiImage.setVisible(false);
wifiImage.invalidate();
break;
case 'w':
wifiImage.setVisible(true);
wifiImage.invalidate();
break;
case 'e':
alarmImage.setVisible(false);
alarmImage.invalidate();
break;
case 'r':
alarmImage.setVisible(true);
alarmImage.invalidate();
break;
}
}
#endif
}

更新されたアプリケーションを実行すると、上の表に示したプロトコルに従って、ディスプレイ上で2つのアイコンを表示/非表示にできるようになります。 これは前のステップで使用したターミナル・プログラムによってテストできます。 ここでは、接続されたターミナル・プログラムのウィンドウを選択し、キーボード上でq、w、e、rキーをクリックすることで、簡単にコマンドを送信できます。

これでチュートリアル7は終了です。

Tip
このチュートリアルで構築したサンプル・アプリケーションでは、UARTとLEDの制御をViewコード内に直接配置しました。 ただし、ほとんどのアプリケーションでは、ここで説明されているパターン(Model-View-Presenter)を使用して、すべてのハードウェア制御をModelクラス内で処理することが推奨されます。
Further reading
このチュートリアルで取り上げた概念を理解する1つの方法として、TouchGFX Designerにある「Knob Demo」をご確認いただくことをお勧めします。 このデモは同じディスプレイ・ノブ・モジュール向けに設計されており、同じメカニズムを使用していますが、はるかに包括的なGUIアプリケーションになっています。