今日は、MCUXpresso SDKにフリーライセンスとして付属してくる組み込みグラフィックスライブラリーemWInの使い方を紹介したいと思います。
コンテンツ
商用利用フリーなGUI
GUIとは、Graphical User Interfaceで、クリック一つで直感的に機器を操作できるインターフェースです。操作パネルの見栄えも良くなり、操作が直感的で簡単になるので、画面が付いている機器のほとんどに採用されています。
Segger社からリリースされているGUI emWin(エムウィン )が、MCUXpresso SDKのmiddlewareにライブラリ提供されています。オブジェクトコード(ライブラリ)での提供で、ソースファイルはありません。
NXP製MCUで使用する限りは、商用利用でも無償で使用できるんです。ライセンスフィーやロイヤリティがフリーなので、ローコストでGUIを採用する場合には見逃せません。
ソースコードの入手は、有償になるようですが開示してもらえるらしいです。ホビーで使う分には、ライブラリでの使用で全く問題ありません。
でも、emWinをいざ使おうと思ったら、MCUXpresso SDKにドキュメントが付属していないし、使い方が良く分からないんです。
そんなemWinの初心者向けで最低限の使い方を説明します。
では、行ってみましょう。
最低限行う作業は?
emWinを使うためには、大きく分けると、最低限以下の5つの作業が必要です。
- LCDとEMCのピン配を設定(MCUXpresso Config Tool)
- 初期化
- バックグランド(LCD画面全体)の設定
- 親(または子)ウィンドウの生成
- GUI部品を配置する(ボタンなど)
- コールバック関数の作成(タッチイベントなど)
今回は、NXP社から提供されているemWinのサンプルプロジェクトをベースに説明していきます。サンプルプロジェクトは、7色のボタンをタッチして選択し、画面をなぞると選択した色のフリーハンド描画ができるというものです。
説明に使用するサンプルプロジェクト
MCUXpresso SDKビルダーでemWInのmiddlewareを追加してダウンロードすると、下記のようにemWinのサンプルプロジェクトが付属してきます。今回は、下記のプロジェクトを使用して説明していきます。
MCUXpresso SDKビルダーの使い方は、こちらを参考にしてください。
プロジェクト名:emWin_examples/emWin_touch_and_draw


フローチャート
大まかなフローチャートは、以下のようになっています。一つ一つ順を追ってみていきます。



0. emWIN GUIに関するヘッダーファイルとピン配設定
emWINで用意されている変数やマクロ、API関数を使用するために、以下の.hファイルをインクルードしておく必要があります。
#include “emwin_support.h”
#include “GUI.h”
#include “GUIDRV_Lin.h”
#include “BUTTON.h” //ボタンウィジェットを使う時にインクルード
ピン配設定は、MCUXpresso Config toolから簡単に設定が可能です。サンプルプロジェクトを使用する場合は、既に設定されているので、気にする必要はありませんが、ピンの配置を変更する場合や、スクラッチから開発する場合には、ピンのコンフィグをする必要があります。
MCUXpresso Config Toolのピン設定で、「EMC」「LCD」をチェックするだけで設定は可能です。
1. 初期化
まずは、emWinの初期化をする必要があります。当然、emWINのGUIを使う前に、デバイスのクロック設定やペリフェラル関連の初期化は終わらせておきます。
GUIの初期化は、
GUI_Init();
はい、これだけです。
2. バックグランド(LCD画面全体)の設定
これは、LCD画面のサイズを設定します。この画面が親画面として設定されます。
GUIの表示できる画面サイズですね。
WM_SetSize(WM_HBKWIN, LCD_WIDTH, LCD_HEIGHT);
WM_SetDesktopColor(GUI_WHITE);
WM_HBKWINは、ウィンドウマネージャで管理する画面のハンドル、LCD_WIDTHとLCD_HEIGHTは、LCD画面のサイズです。
それぞれのパラメータは、emWINのサポートファイル(ヘッダーファイル)で#defineされています。
LPCXpresso54608ボードに付属のLCDサイズは、480 x 272です。したがって、以下のように#defineされています。
#define LCD_WIDTH 480
#define LCD_HEIGHT 272
そして、その画面をバックグランド(親画面)として取り扱われ、その色設定(WM_SetDesktopColor(GUI_WHITE))は白(GUI_WHITE)と言うことです。
3. 子画面の生成
必要最低限行う事の3つ目として、子画面を生成していきます。
サンプルコードでは、順番が逆になっていますが、ボタンの生成から行なっています。その後、ペンで絵を描くキャンバス画面(子画面)を生成しています。
まず、キャンバス画面の生成から説明します。
WM_HWIN hCanvas;
hCanvas = WM_CreateWindowAsChild(35, 0, WM_GetWindowSizeX(WM_HBKWIN) - 35, WM_GetWindowSizeY(WM_HBKWIN), 0, WM_CF_SHOW, cbCanvasWin, 0);
/* Select canvas window and leave it selected forever */
WM_SelectWindow(hCanvas);
まず、ウィンドウマネージャのWM_HWIN型の子画面用ハンドル(hCanvas)を作成します。このハンドルに画面生成関数で子画面を取得しています。
WM_CreateWindowAsChild()は、子画面を生成する関数で、パラメータは以下のように設定します。
WM_CreateWindowAsChild(int x, int y, int width, int height, WM_HWIN hWinParent, U8 style, WM_CallBack *callback, 0)
xとyは、子画面の左上角のアドレス(ポジション)です。このアドレスは、親画面との相対アドレスです。
widthは幅、heightは高さを指定します。もし、それぞれ0を入力した場合は、親画面と同じ幅と高さになります。
hWinParentは、親画面のハンドル。0を設定すると、親画面全体が設定されます。
WM_CF_SHOWで画面生成後に表示されるようになります。
最後に、コールバック関数の指定です。
コールバック関数の次のパラメータは、通常0を指定します。
サンプルコードでは、xに35を指定していて、ペンの色選択用のボタン幅(35)だけ右にズラしています。そして、幅の設定は、WM_GetWindowSizeX()関数で親画面のXのサイズを取得して35引いています。
そして、WM_SelectWindow()関数で、取得した子画面のハンドル(hCanvas)を引数に設定して子画面の生成は終わりです。
4. GUI部品を配置する(ボタン)
BUTTON_Handle hButtonClear;
hButtonClear = BUTTON_CreateEx(4, 2, 30, 25, 0, WM_CF_SHOW, 0, CLEAR_BUTTON_ID);
BUTTON_SetText(hButtonClear, "CLR");
BUTTON_Handle型の変数を用意して、その変数にBUTTON_CreateEx関数でボタンを生成してボタンのハンドルを取得しています。
BUTTON_CreateEx関数のパラメータは、画面の生成関数のそれと同じです。ただ、一番最後の引数はボタンのIDを設定することができ、サンプルコードでは各色毎のボタンIDを指定しています。
そして、BUTTON_SetText()関数で、文字列(”CLR”)を書き込んでいます。この後、同様に各色のボタンをfor文で繰り返し生成し配置しています。
5. タッチイベントのコールバック
一つ目のコールバック処理
static void cbBackgroundWin(WM_MESSAGE *pMsg)
{
int widget_id;
switch (pMsg->MsgId)
{
case WM_NOTIFY_PARENT:
widget_id = WM_GetId(pMsg->hWinSrc);
if (widget_id >= COLOR_BUTTON_FIRST_ID && widget_id <= COLOR_BUTTON_LAST_ID)
{
GUI_SetColor(button_color[widget_id - COLOR_BUTTON_FIRST_ID]);
} else if (widget_id == CLEAR_BUTTON_ID && pMsg->Data.v == WM_NOTIFICATION_CLICKED)
{
GUI_Clear();
}
break;
default:
WM_DefaultProc(pMsg);
}
}
}
このコールバック関数は、親画面(LCD画面全体)を設定した後、コールバック関数を設定します。
WM_SetCallback(WM_HBKWIN, cbBackgroundWin);
これは、Widgetから上がってくるコールバックに対する処理を記述していきます。
ここでは、親画面のコールバックで、ボタンがタッチされると、WM_NOTIFY_PARENTのパラメータでコールバックを上げてきます。
ボタンIDが設定した色のボタンIDであれば、GUI_SetColor()関数でその色に設定し、それ以外のID(クリアボタンの場合)は画面を消去(GUI_Clear())しています。
二つ目のコールバック
static void cbCanvasWin(WM_MESSAGE *pMsg)
{
GUI_PID_STATE *pid_state;
static GUI_PID_STATE pid_state_0;
switch (pMsg->MsgId)
{
case WM_TOUCH:
pid_state = (GUI_PID_STATE *)pMsg->Data.p;
if (pid_state->Pressed)
{
if (pid_state_0.Pressed)
{
GUI_DrawLine(pid_state_0.x, pid_state_0.y, pid_state->x, pid_state->y);
}
else
{
GUI_DrawPoint(pid_state->x, pid_state->y);
}
}
pid_state_0 = *pid_state;
break;
default:
WM_DefaultProc(pMsg);
}
}
このコールバックは、画面のイベントで、自動的にコールされる関数で、メッセージ(pMsg)をパラメータとして渡してきます。
このメッセージがWM_TOUCHならタッチイベントで、状態がタッチして押されている(Pressed)なら、線を引くと言うコードです。押されていない場合は、クリックと認識して点を描くと言うコードです。
メイン関数では、emWINの実行をwhile文で画面を更新して、再度タッチイベントが発生していれば、押されている間だけ線を引くと言う具合になります。
emWINに関する情報
emWINに関する情報は、Segger社ウェブページからダウンロードが可能です。
参考:https://www.segger.com/products/user-interface/emwin/
まとめ
emWinを使うためには、最低限5つの作業をする必要があります。初期化、親画面(LCD)の設定、子画面の生成、GUI部品の配置、そしてコールバック関数の5つの作業です。
もちろん、これ以外にも色々なGUI部品もあります。さらに詳しい情報は、Segger社のWebページから入手することが可能です。
今回は、emWinの使い方を紹介しました。是非参考にしてみてください。