TouchGFX 설치하고 사용해보기
설치하기
1. 공식 웹사이트에서 TOUCHGFX를 다운받아 설치한다.
https://www.st.com/en/embedded-software/x-cube-touchgfx.html
압축 해제 후 아래 경로에 .msi 설치파일이 있다.
Utilities\PC_Software\TouchGFXDesigner
2. STM32CubeMX 툴에서 TouchGFX Generator를 설치한다.
최신버전을 보기위해 Refresh를 누르고 설치할 버전을 선택한 뒤 Install Now를 누른다.
X-CUBE-TOUCHGFX 배포판은 아래 경로에 위치한다.
C:\Users\<user>\STM32Cube\Repository\Packs\STMicroelectronics\X-CUBE-TOUCHGFX\4.13.0
3. TouchGFX designer를 실행한다.
메뉴
좌측의 Create 버튼을 누르면 Simulator 또는 보드를 선택할 수 있다. 보드를 선택하는 경우 Color Depth와 Resolution은 고정이다.
좌측의 Example 버튼을 누르면 UI Component 사용법을 익힐 수 있고 그 아래 Demo를 누르면 많은 Demonstration 프로젝트를 확인할 수 있다.
Widget과 Interaction 사용하기
✔ Image
Image의 Properties에서 Image를 누르고 touchGFX에서 기본제공하는 Image들을 불러올 수 있다. 또는 Project를 눌러 로컬이미지를 프로젝트에 포함시킬 수 있다.
✔ Button
Button의 Trigger는 Click, Touch, Toggle, Repeat가 있다.
Button의 Visual Elements는 Box With Border, Text, Icon, Image가 있으며 이중에 몇개를 조합할 수 있다.
Elements들도 Widget과 마찬가지로 맨 위에 있는 것이 제일 앞에 보이고 밑에있는게 뒤에서 보인다.
그리고 각 Element는 Position 오프셋을 조정할 수있고 Released 상태일 때와 Pressed 상태일 때의 이미지 등을 다르게 설정할 수 있다.
✔ Interaction 스크린 전환
Screens에서 Add Screen을 눌러 스크린을 추가할 수 있다.
그리고 화면 우측 Interaction에 + 버튼을 누른 뒤 특정 버튼이 클릭되었을 때 Change screen Action이 실행되도록 추가할 수 있다.
✔ Text Area
✔ 한글폰트 추가
화면 우측 Texts - Texts 를 누르고 GB 옆의 + Add Language 버튼을 눌러 KO를 추가한다.
그리고 KO열에 각 텍스트에 대한 한국어 번역을 수동으로 추가할 수 있고, 번역하지 않을 Text는 영어로 둘 수 있다.
화면 우측 하단의 Config - General - Selected Language를 통해 기본 언어를 바꿀 수 있다.
이 내용은 TextKeysAndLanguages.hpp 파일에서 볼 수 있다.
<프로젝트명>\generated\texts\include\texts
/* DO NOT EDIT THIS FILE */
/* This file is autogenerated by the text-database code generator */
#ifndef TOUCHGFX_TEXTKEYSANDLANGUAGES_HPP
#define TOUCHGFX_TEXTKEYSANDLANGUAGES_HPP
enum LANGUAGES
{
GB,
KO,
NUMBER_OF_LANGUAGES
};
enum TEXTS
{
T_RESETTEXTID,
T___SINGLEUSE_X0HV,
T___SINGLEUSE_O0W6,
T___SINGLEUSE_6LO5,
T___SINGLEUSE_BVQI,
T___SINGLEUSE_KDW3,
T___SINGLEUSE_R0GT,
T_RESETINBUTTONID,
T___SINGLEUSE_DOWR,
T___SINGLEUSE_QR2V,
T___SINGLEUSE_O7KU,
NUMBER_OF_TEXT_KEYS
};
#endif // TOUCHGFX_TEXTKEYSANDLANGUAGES_HPP
그리고 한글을 지원하는 무료 오픈소스 글꼴을 다운받은 뒤 프로젝트 폴더의 assets - fonts 폴더 안에 넣고 touchGFX를 재시작한다.
디폴트 경로는 아래와 같다.
C:\TouchGFXProjects\<프로젝트명>\assets\fonts
Typographies에서 한영전환할 Typograpy를 만들고 Font에서 방금 추가한 폰트를 선택할 수 있다.
✔ Interaction으로 호출될 가상메소드 추가하기
Action을 Call new virtual function을 선택하고 Funtion Name을 알아보기 쉽게 짓는다.
F4를 누르거나 하단의 Generate Code를 누른다.
아래 경로에 MainViewBase.hpp 파일이 있다.
<프로젝트명>\generated\gui_generated\include\gui_generated\main_screen
열어보면 아까 생성한 가상 메소드가 보이고 그 구현체는 비어있다. 이는 하위클래스 MainView에서 메소드를 직접 구현해야 한다.
/*********************************************************************************/
/********** THIS FILE IS GENERATED BY TOUCHGFX DESIGNER, DO NOT MODIFY ***********/
/*********************************************************************************/
#ifndef MAINVIEWBASE_HPP
#define MAINVIEWBASE_HPP
#include <gui/common/FrontendApplication.hpp>
#include <mvp/View.hpp>
#include <gui/main_screen/MainPresenter.hpp>
#include <touchgfx/widgets/Box.hpp>
#include <touchgfx/widgets/Image.hpp>
#include <touchgfx/widgets/TextArea.hpp>
#include <touchgfx/widgets/ToggleButton.hpp>
#include <touchgfx/containers/buttons/Buttons.hpp>
#include <touchgfx/containers/progress_indicators/LineProgress.hpp>
#include <touchgfx/widgets/canvas/PainterRGB565Bitmap.hpp>
#include <touchgfx/containers/Slider.hpp>
class MainViewBase : public touchgfx::View<MainPresenter>
{
public:
MainViewBase();
virtual ~MainViewBase();
virtual void setupScreen();
/*
* Virtual Action Handlers
*/
virtual void languageSwitchButtonClicked()
{
// Override and implement this function in Main
}
...
아래 경로에서 MainView.hpp를 열고 메소드를 다시 선언한다.
<프로젝트명>\gui\include\gui\main_screen
#ifndef MAINVIEW_HPP
#define MAINVIEW_HPP
#include <gui_generated/main_screen/MainViewBase.hpp>
#include <gui/main_screen/MainPresenter.hpp>
class MainView : public MainViewBase
{
public:
MainView();
virtual ~MainView() {}
virtual void setupScreen();
virtual void tearDownScreen();
virtual void languageSwitchButtonClicked();
protected:
};
#endif // MAINVIEW_HPP
아래 경로의 MainView.cpp에 실체를 구현한다.
<프로젝트명>\gui\src\main_screen
#include <gui/main_screen/MainView.hpp>
#include <touchgfx/utils.hpp>
#include <texts/TextKeysAndLanguages.hpp>
MainView::MainView()
{
}
void MainView::setupScreen()
{
MainViewBase::setupScreen();
}
void MainView::tearDownScreen()
{
MainViewBase::tearDownScreen();
}
void MainView::languageSwitchButtonClicked()
{
touchgfx_printf("languageSwitchButtonClicked\n");
int idLanguage = Texts::getLanguage() + 1;
if(idLanguage == NUMBER_OF_LANGUAGES)
{
idLanguage = 0;
}
Texts::setLanguage(idLanguage);
MainView::invalidate();
}
가상메소드 안에서는 현재 LANGUAGE ENUM 값이 0이면 1로 1이면 0으로 세팅한 후 MainView::invalidate()를 통해 화면을 다시 그린다.
참고로 #include <touchgfx/utils.hpp> 를 추가한 뒤에 touchgfx_printf() 함수를 사용하여 디버깅용으로 쓸 수 있다. 이 코드는 타겟에서 실행되도 미치는 영향이 없다.
✔ Model에 데이터 추가하기
TouchGFX 사용자 인터페이스는 Model-View-Controller(MVC) 패턴에서 파생된 Model-View-Presenter(MVP)라는 아키텍처 패턴을 따른다.
MVP 패턴의 이점은 다음과 같다.
- 관심 영역 분리: 코드를 각자의 책임을 가진 별개의 영역으로 분할한다. 이렇게 하면 코드가 더 단순해져서 재사용하기 쉽고 유지 관리도 쉬워진다.
- 유닛 테스트: UI 로직(presenter)이 시각적 계층(view)과 분리되어 있기 때문에 이러한 부분을 훨씬 쉽게 단독으로 테스트할 수 있다.
- model : 사용자 인터페이스에 표시하거나, 그 밖에 실행할 데이터를 정의하는 인터페이스.
- view : model에서 데이터를 표시하고 해당 데이터를 실행할 프리젠터에게 사용자 명령(이벤트)을 전달하는 패시브 인터페이스.
- presenter는 model과 view에 따라 실행된다. 저장소(model)에서 데이터를 가져온 후 뷰에 표시할 수 있도록 형식을 지정한다.
멀티 스크린에서 공유할 수 있는 데이터를 모델에 추가하기.
아래 경로에서 Model.hpp 파일을 연다.
<프로젝트명>\gui\include\gui\model
#ifndef MODEL_HPP
#define MODEL_HPP
#include <touchgfx/hal/types.hpp>
class ModelListener;
class Model
{
public:
Model();
void bind(ModelListener* listener)
{
modelListener = listener;
}
void tick();
void setSharedVal(uint8_t val)
{
sharedVal = val;
}
uint8_t getSharedVal()
{
return sharedVal;
}
protected:
ModelListener* modelListener;
uint8_t sharedVal;
};
#endif // MODEL_HPP
변수와 getter, setter를 추가한다.
참고로 #include <touchgfx/hal/types.hpp> 를 추가하면 stdint 자료형을 쓸 수 있다.
아래 경로에서 Model.cpp를 열고 생성자에서 변수를 초기화한다.
<프로젝트명>\gui\src\model
#include <gui/model/Model.hpp>
#include <gui/model/ModelListener.hpp>
Model::Model() : modelListener(0), sharedVal(0)
{
}
void Model::tick()
{
}
✔ View에서 Model 접근하기
아래 경로에서 각각 스크린의 -Presenter.hpp 파일을 열어 Model에 접근할 수 있도록한다.
<프로젝트명>\gui\include\-_screen
#ifndef MAINPRESENTER_HPP
#define MAINPRESENTER_HPP
#include <gui/model/ModelListener.hpp>
#include <mvp/Presenter.hpp>
#include <touchgfx/hal/types.hpp>
using namespace touchgfx;
class MainView;
class MainPresenter : public touchgfx::Presenter, public ModelListener
{
public:
MainPresenter(MainView& v);
/**
* The activate function is called automatically when this screen is "switched in"
* (ie. made active). Initialization logic can be placed here.
*/
virtual void activate();
/**
* The deactivate function is called automatically when this screen is "switched out"
* (ie. made inactive). Teardown functionality can be placed here.
*/
virtual void deactivate();
virtual ~MainPresenter() {}
void setSharedVal(uint8_t val)
{
model->setSharedVal(val);
}
uint8_t getSharedVal()
{
return model->getSharedVal();
}
private:
MainPresenter();
MainView& view;
};
#endif // MAINPRESENTER_HPP
이제 여러 스크린의 -View.cpp에서 presenter->getSharedVal(), presenter->setSharedVal() 을 통해 모델 데이터에 접근할 수 있다.
✔ Interaction, Screen transition begins 사용해서 위젯의 속성 값 바꾸기
여러 스크린에서 공유하는 변수의 상태값을 스크린 트랜지션 이벤트가 시작할 때 확인해서 토글 스위치가 Released 상태 또는 Pressed 상태로 바꿀 수 있다.
디자이너 툴에서 위젯의 시작속성을 바꾸면 그에대한 코드는 gui_generated의 -ViewBase.hpp, -ViewBase.cpp 에 생성된다. 해당 코드를 보고 위젯의 속성을 어떻게 제어할지 알 수 있다.
#include <gui/main_screen/MainView.hpp>
MainView::MainView()
{
}
void MainView::setupScreen()
{
MainViewBase::setupScreen();
}
void MainView::tearDownScreen()
{
MainViewBase::tearDownScreen();
}
void MainView::languageSwitchButtonClicked()
{
Texts::setLanguage(!Texts::getLanguage());
MainView::invalidate();
}
void MainView::checkLanguage()
{
if (Texts::getLanguage())
{
languageSwitchButton.forceState(true);
}
else
{
languageSwitchButton.forceState(false);
}
languageSwitchButton.invalidate();
}
Texts::getLanguage()로 현재 값을 확인해서 기본값(GB)인 0이면 토글스위치를 false로 KO인 경우 true로 설정한다.
그리고 화면을 전부 다시 그릴 필요없이 languageSwitchButton만 다시 그린다.
혹은 Interaction을 사용하지 않고 기존에 있는 setupScreen, tearDownScreen에 포함시키는 방법도 있따.
✔ 슬라이더와 게이지 연동하기
화면전환 이후에도 유지되는 변수를 만들기 위해서는 ScreenView 클래스의 멤버변수가 아니라 Model에 데이터를 추가해야한다. ScreenView 클래스는 화면전환시마다 constructor와 destructor가 호출된다.
void MusicView::screenTransitionBegins()
{
if (Texts::getLanguage())
{
languageSwitchButton.forceState(true);
}
else
{
languageSwitchButton.forceState(false);
}
languageSwitchButton.invalidate();
volumeSlider.setValue(presenter->getVolume());
volumeSlider.invalidate();
volumeGauge.setValue(presenter->getVolume());
volumeGauge.invalidate();
}
void MusicView::sliderValueChanged(int value)
{
presenter->setVolume(value);
volumeGauge.setValue(presenter->getVolume());
volumeGauge.invalidate();
}
✔ Wildcard Text 사용하기
와일드카드란 텍스트에서 런타임 시 숫자와 같이 다른 것으로 대체할 수 있는 마커("<...>")를 말한다.
여기서는 볼륨이 int형이기 때문에 <d>를 사용할거다.
와일드카드는 코정 텍스트와 결합해서 사용할 수 있다. Wildcard를 추가하고 Use wildcard buffer를 체크한다.
Auto-size를 풀고 Alignment를 변경한다.
// ...
Unicode::snprintf(dbTextBuffer, DBTEXT_SIZE, "%d", presenter->getVolume());
dbText.invalidate();
// ...
TextArea 위젯은 Unicode를 사용하기 때문에 Unicode 버퍼 작성을 지원하는 snprintf 함수를 사용해야한다.
그리고 Texts - Typographies 에 들어가 해당 Typography의 Wildcard Ranges에 문자 0-9를 추가해야다. TouchGFX에는 필요한 문자만 포함되도록 되어있기 때문이다.
✔ code에서 screen transition 하기
먼저 screen 프로퍼티에서 Actions +를 눌러 추가한다.
다음 Interactions를 눌러 방금 만든 screen의 Action이 호출될 때 Change screen 하도록 추가한다.
코드를 생성하면 -ViewBase.hpp와 -ViewBase.cpp에 아래와 같이 구현된다.
/*
* Custom Actions
*/
virtual void changeToMain();
void WashViewBase::changeToMain()
{
//changeToMainIsCalled
//When changeToMain is called change screen to Main
//Go to Main with screen transition towards West
application().gotoMainScreenSlideTransitionWest();
}
이제는 -View.cpp에서 Action을 호출하기만 하면 된다.
void WashView::backStopButtonClicked()
{
switch (washType)
{
case NOTUSED:
changeToMain();
break;
case ING:
case PAUSE:
// ...
break;
default:
break;
}
}
✔ 런타임에서 다른 Text로 변경하기
Texts - Texts에서 + Add new text 버튼을 눌러 텍스트를 직접 추가해준다. Typography, Alignment도 선택하고 알아보기 쉽게 아이디를 지정한다.
코드를 생성하면 아래 경로 TextKeysAndLanguages.hpp 파일에 id가 보인다.
gui_generated\texts\include\texts
enum TEXTS
{
T_WASHSTOPID,
T_WASHPAUSEID,
T___SINGLEUSE_WZ1T,
T___SINGLEUSE_5U34,
T_WASHSTARTID,
T_WASHBACKID,
T___SINGLEUSE_GGBH,
T___SINGLEUSE_3D1G,
T___SINGLEUSE_ZDA4,
T___SINGLEUSE_H3YX,
T___SINGLEUSE_YSAA,
T___SINGLEUSE_IL7G,
T___SINGLEUSE_MNZM,
T___SINGLEUSE_X0HV,
T___SINGLEUSE_O0W6,
T___SINGLEUSE_6LO5,
T___SINGLEUSE_BVQI,
T___SINGLEUSE_KDW3,
T___SINGLEUSE_R0GT,
T_RESETINBUTTONID,
T___SINGLEUSE_DOWR,
T___SINGLEUSE_QR2V,
T___SINGLEUSE_O7KU,
NUMBER_OF_TEXT_KEYS
};
#include <texts/TextKeysAndLanguages.hpp> 하고
<widgetName>.setText(TypedText(<idName>)) 으로 텍스트를 변경한다.
#include <texts/TextKeysAndLanguages.hpp>
// ...
void WashView::backStopButtonClicked()
{
switch (washType)
{
case NOTUSED:
changeToMain();
break;
case ING:
case PAUSE:
washType = NOTUSED;
washCounter = 0;
backStopButton.setText(TypedText(T_WASHBACKID));
backStopButton.invalidate();
startPauseButton.setText(TypedText(T_WASHSTARTID));
startPauseButton.invalidate();
Unicode::snprintf(numberTextBuffer, NUMBERTEXT_SIZE, "%d", washCounter);
numberText.invalidate();
washProgress.setValue(washCounter);
washProgress.invalidate();
break;
default:
break;
}
}
✔ handleTickEvent() 사용하기
model.hpp/cpp에도 tick()이 있으니 공동으로 사용하는 거라면 거기에 추가해도 된다. 여기선 -View.hpp/cpp에 추가했다.
virtual void handleTickEvent(); 를 -View.hpp에 추가하고 -View.cpp에 구현한다.
참고로 handleTickEvent()는 프레임마다 호출된다. 60프레임이기 때문에 tickCount % 60 == 0 으로 1초를 얻어낼수도 있다.
아래는 Tick 이벤트 핸들러 안에서 세탁기 상태머신에 따라 다르게 동작하도록 하였다.
#ifndef WASHVIEW_HPP
#define WASHVIEW_HPP
#include <gui_generated/wash_screen/WashViewBase.hpp>
#include <gui/wash_screen/WashPresenter.hpp>
enum WASHTYPE
{
NOTUSED,
ING,
PAUSE
};
class WashView : public WashViewBase
{
public:
WashView();
virtual ~WashView() {}
virtual void setupScreen();
virtual void tearDownScreen();
virtual void screenTransitionBegins();
virtual void languageSwitchButtonClicked();
virtual void handleTickEvent();
virtual void backStopButtonClicked();
virtual void startPauseButtonClicked();
protected:
int tickCounter;
int washCounter;
WASHTYPE washType;
};
#endif // WASHVIEW_HPP
void WashView::handleTickEvent()
{
tickCounter++;
if (tickCounter % 5 == 0)
{
tickCounter = 0;
int temp;
switch (washType)
{
case NOTUSED:
break;
case ING:
washCounter++;
temp = washCounter;
if (washCounter > 100)
{
temp = 100;
if (washCounter > 110)
{
washCounter = 0;
temp = 0;
washType = NOTUSED;
backStopButton.setText(TypedText(T_WASHBACKID));
backStopButton.invalidate();
startPauseButton.setText(TypedText(T_WASHSTARTID));
startPauseButton.invalidate();
}
}
washProgress.setValue(temp);
washProgress.invalidate();
Unicode::snprintf(numberTextBuffer, NUMBERTEXT_SIZE, "%d", temp);
numberText.invalidate();
break;
case PAUSE:
break;
default:
break;
}
}
}
✔ Run Simulator
F5를 눌러 Simulator를 실행할 수 있고 아래 경로에서 실행파일을 실행해도 된다.
C:\TouchGFXProjects\<ApplicationName>\build\bin
'임베디드 개발 > STM32 (ARM Cortex-M)' 카테고리의 다른 글
STM32 ] microseconds 딜레이 함수 만들기 (1) | 2024.06.04 |
---|---|
STM32 ] TouchGFX, 하드웨어와 상호작용하기 (0) | 2023.07.18 |
STM32 ] TFTP Bootloader (0) | 2023.04.16 |
STM32 ] TFTP Server 파일 송수신 + USB Host MSC (9) | 2023.04.15 |
STM32 ] TFTP Server (0) | 2023.04.14 |