你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】TouchGFX 中 Callback 模板实现原理

[复制链接]
STMCU小助手 发布时间:2022-2-18 23:02
前言
6 ^) \, d. O0 C8 A( \$ NTouchGFX 为 MCU 带来了炫彩丰富的 GUI 界面,使得基于 STM32 芯片的人机界面开发非常方便而友好,比如可以在 TouchGFX Designer 中创建一个按键,在 interaction 中给按键添加响应;或者创建多个界面,在界面间进行切换;这些功能由 designer 帮我们自动生成代码实现了,那与之对应的功能响应代码具体是如何实现的呢?
! g4 U! r- e" `7 V) VTouchGFX 是用 C++编写的,借助 C++的模板特性,TouchGFX 定义了一组Callback 模板,基于此模板来实现上述响应的功能。' m2 l( }" o4 B% s0 g
/ |  U8 g7 i" M
Callback 模板
* s  o. a! W( G  B  [2 Z在 TouchGFX 中,Callback 模板的描述放在 Callback.hpp 文件中,在此定义了两组模板:GenericCallback 与 Callback 模板。+ I2 N2 h+ `- c4 ~9 X

; }# m  {5 |- i& F# i$ VGenericCallback 模板组
1 ]8 w4 _1 Z0 s9 N7 b" k' `GenericCallback 为 Callback 模板的模板基类。在 GenericCallback 模板中,定义了两个接口函数:isValid 与 execute;其中 isValid 是来检测 Callback 是否被初始化过,而 execute 函数用于调用实际要执行的函数。GenericCallback 模板组总共定义了 4 个模板,模板之间的差别在于 execute 函数的参数个数不同,4个模板分别对应 execute 函数带有 0 个参数,1 个参数,2 个参数与 3 个参数。本文中仅列出 execute 带一个参数的情况。9 T1 h' S, m. f! ^0 O

; y" i- C* A" l6 M1 H下面是 execute 函数带 1 个参数的 GenericCallback 模板:$ d/ o' J5 k3 J/ e. I3 g; q+ H
  1. template" b& ^) f+ w2 m9 i* Q* Q2 M
  2. class GenericCallback3 T7 G: L. p; G! _6 c% d1 p) j% L- U
  3. {
    6 q' }; I; T$ p) I
  4. public:- A6 I" C- Q4 H5 J( g% i
  5. virtual ~GenericCallback()) Y0 W1 I/ F/ B- b$ \5 b) B
  6. {! B/ z6 j- ?" t2 B) a0 ^
  7. }
    % I# @$ X5 |  B
  8. virtual void execute(T1 val1) = 0;9 I6 T* w5 _  X
  9. virtual bool isValid() const = 0;4 t- ~6 I) _- _# ~+ F2 }% h
  10. };
复制代码

% A  p) E% u, G3 m. nCallback 模板组* {: J5 K: K2 G5 `  E6 a+ [1 C* ^
Callback 模板由 GenericCallback 派生而来。Callback 模板组也有 4 个模板,分别对应于包含不同参数个数 execute 函数的 GenericCallback 模板,继承关系如下图:
- s. X7 B5 Q; u9 a1 m2 c& f! i- h7 h6 K
8 _+ {' R5 E3 r; s% W- A4 Q LH1L0XH(MS(ZZT15{_2O]FE.png
9 e$ O8 ]  e* U3 |3 Z# J- l0 N& _# {
8 n  }% n+ d7 m7 h: |4 [' W下面是 execute 函数带 1 个参数的 Callback 模板:
& \3 V( Z6 c. N& b+ C* r( v
  1. template <class dest_type, typename T1>1 K; {. o* G; T% D+ W. P# {
  2. struct Callback : public GenericCallback
    : C' C! W* d4 K
  3. {
    , l9 t6 [1 T! i% h* _
  4. Callback() : pobject(0), pmemfun_1(0) { }+ P+ \% O- O: G
  5. Callback(dest_type* pobject, void (dest_type::*pmemfun_1)(T1))
    2 J0 _% g* C. T8 W3 D
  6. {+ `" B, p8 x8 W/ @' T
  7. this->pobject = pobject;
    : C4 _# _. j+ H% y
  8. this->pmemfun_1 = pmemfun_1;5 e9 @$ T( W$ |5 x# a, A# L
  9. Figure 1 Callback 继承关系- F4 Z5 F; P" u
  10. }
    . L6 r. M, M7 H% i6 j
  11. virtual void execute(T1 t1)
    5 Z: S4 I  f3 V" k
  12. {7 Z. p2 R) g6 W# L5 ]' q2 i* V
  13. (pobject->*pmemfun_1)(t1);: Y% m  c9 `9 p$ T
  14. }7 A" |& Z. f  E
  15. virtual bool isValid() const
    ! z  B0 k: k( @3 a
  16. {) [! W0 Z/ |# V7 i/ n
  17. return (pobject != 0) && (pmemfun_1 != 0);
    & }5 Z: y+ }  H  {5 R0 Z
  18. }: F0 a# U" f( B! B$ m' f
  19. private:3 L% M) \& D+ l# A( w, z7 Z
  20. dest_type* pobject;
    0 k* z+ l. X+ `& K5 y% z
  21. void (dest_type::* pmemfun_1)(T1);) K: R$ ^/ [6 ?( U' w1 v
  22. };
复制代码
1 z( H8 {8 M- w" S3 g: E
在 Callback 结构中定义了两个私有的成员指针,pobject 为 dest_type 类型对象的指针,pmemfun_1 为 dest_type 对象的成员函数指针,是指向 dest_type 类型的成员函数。这样当 Callback 结构初始化好了以后,再调用 execute 函数时,就是直接调用了 dest_type 类型对象的成员函数 pmemfun_1。+ w0 X' p! g- m4 c% k

; ]+ P' v3 E2 v: L1 b7 \在使用 Callback 模板时,根据模板的特性,C++编译器会自动选择最准确的模板匹配,来生成对应的模板类或模板函数。因此在使用 Callback 模板时,可以使用不同的类型参数个数来匹配使用不同的模板。( B; z2 d* u/ s- F. s$ A
: i/ p: ?& X6 ?/ R  m1 L
Callback 模板在 TouchGFX 中的使用0 z1 r. j1 z& O. A' o7 c
页面切换 - pendingScreenTransitionCallback

. p5 h" w) T3 E3 z1 W! g1 i' s0 ]在 TouchGFX 启动过程中会初始化 MVPApplication 对象,在 MVPApplication类中定义了一个成员 pendingScreenTransitionCallback,它是GenericCallback*类型的指针,并初始化为 0;; @% O! d: {8 r4 w1 W; f

; Q# r1 U5 t# I. E" ^. H$ ~TouchGFX 会调用 handlePendingScreenTransition 函数进行页面切换,最终调用 evaluatePendingScreenTransition 函数,在此函数中会用 isValid()函数来检查 Callback 是否已经初始化;如果初始化了,就调用 execute 函数 (因为Callback 为 GenericCallback*类型,因此 execute 函数不带参数) 。9 z- |% f( P2 _" u( w3 v( Q: }
  1. void evaluatePendingScreenTransition()- p: m3 R- x, i* v
  2. {
    7 _& E2 \: Y1 r7 a# [: H7 b
  3. if (pendingScreenTransitionCallback &&
    - \6 p) |. ?) i% u- n, r0 ?
  4. pendingScreenTransitionCallback->isValid()): R- N8 x) n; Z
  5. {
    # M; }9 [9 m2 X/ J: _0 `; [; |
  6. pendingScreenTransitionCallback->execute();9 h1 u! \% k$ W) n# R% G
  7. pendingScreenTransitionCallback = 0;0 s* M' |1 ]; V# f# n2 Q( Z
  8. }
    + ~* |$ ?  m# |# |0 l% n
  9. }
复制代码

, r+ V4 B. N% J1 s  b具体的页面切换过程(进入第一个 screen):7 l( u; g6 ]% |. C
在 TouchGFX 初始化过程中,会初始化 FrontendApplication 对象 app,在此对象的基类中包含一个 transitionCallback 成员,其类型为:
3 M; X" Y* c( h0 J, M% ztouchgfx::Callback transitionCallback2 R( O! V' l) {( R' u
在进入第一个 Screen 时,会调用 app.gotoScreen1ScreenNoTransition(),在此函数中,会将 pendingScreenTransitionCallback 设置为指向 transitionCallback的指针,最终由 TouchGFX 框架调用 execute 函数,执行 makeTransition 完成页面的切换。
. P/ G& }" N" W% Q; t( T// 初始化 FrontendApplicationBase::transitionCallback,transitionCallback 的私有成员初始化:
; n' t- _" A0 @/ B" k9 b// pobject 赋值为 this(指向 FrontendApplicationBase)
# n( F/ M, P- X1 V// pmemfun_1 赋值为
& K5 r: P9 `9 N) l. l&FrontendApplication::gotoScreen1ScreenNoTransitionImpl, i: @! z: C: {- k8 K; d2 h* z1 f
  1. <div>void FrontendApplicationBase::gotoScreen1ScreenNoTransition()</div><div>{</div><div> transitionCallback = touchgfx::Callback<FrontendApplicationBase>(this,</div><div>&FrontendApplication::gotoScreen1ScreenNoTransitionImpl);</div><div> pendingScreenTransitionCallback = &transitionCallback;</div><div>}</div>
复制代码
5 n  z& F' H3 ]/ R
6 U" h! K% z. _$ A
//execute 函数内实际执行的函数(pobject->*pmemfun_1)(),即为如下函数:
+ b9 N) ^/ e5 R( {2 c) |/ l
  1. <div>void FrontendApplicationBase::gotoScreen1ScreenNoTransitionImpl()</div><div>{</div><div> makeTransition<Screen1View, Screen1Presenter, touchgfx::NoTransition,</div><div>Model >(&currentScreen, &currentPresenter, frontendHeap, &currentTransition,</div><div>&model);</div><div>}</div>
复制代码

' I* E& E. u; z0 U
4 }0 T9 Q6 v! T

: a  `5 z2 W0 R8 k按键响应 - flexButtonCallback
, e/ Q, k+ b3 Z% J# p7 l7 B( A在 TouchGFX designer 中创建一个按键,并设置 interaction 后,会在MainViewBase 类中创建两个私有成员:
# ?" W4 u# q$ R# G# \2 K7 l! r9 n//按键响应函数,实现用户的动作响应
# \4 \; |9 O  x2 M2 r; \# q( B5 Wvoid flexButtonCallbackHandler(const touchgfx::AbstractButtonContainer& src)8 s5 O& U5 [9 ?# o
//Callback 对象( T+ q0 r1 M* T, q$ _3 U
touchgfx::Callback0 p/ o# S% i7 X1 ]& d5 G( F
flexButtonCallback
: w" k! Z; M& T以及一个按键成员,例如 clickButton:
9 }2 C1 @& h' ktouchgfx::TextButtonStyle > clickButton;
8 j; v; R$ C* e
5 m( M3 d$ R& e在 MainViewBase 的构造函数中,会初始化 flexButtonCallback 成员,并且调用clickButton->setAction 函数,设置此按键的 AbstractButton::action 为! k  h" Y& G; m
flexButtonCallback,: J9 ~( E. l6 G9 y. J/ b/ P1 `8 ], j
MainViewBase::MainViewBase():flexButtonCallback(this,. J2 T  _. i  P! o  ~4 d' r8 ]
MainViewBase::flexButtonCallbackHandler), \% K' \& [' L0 [4 w6 L* r+ M
其中:
( L6 Q2 |4 U/ t/ r: p5 fflexButtonCallback 的 pobject 成员初始化为 this(指向 MainViewBase 的指针)6 P% P: G) h! H7 v2 V9 ?
flexButtonCallback 的 pmemfun_1 成员初始化为5 D8 V$ i3 D, H$ b' }5 e
&MainViewBase::flexButtonCallbackHandler  B/ p- j* ^* A5 b
由于 flexButtonCallback 的类型是
" o/ v3 K' V3 B, _8 ~5 xstruct Callback<dest_type, T1, void, void> : public GenericCallback<T1>/ |; d/ l9 W  k% m6 \
因此 flexButtonCallback 的 execute 函数带有一个参数,参数类型为 const touchgfx::AbstractButtonContainer&,最终此参数会传递给flexButtonCallbackHandler 函数。
, d* `+ [& Y( k& A8 s8 X+ x) x
$ E( s: C' g- M& a/ f# w在程序运行时,点击按键后,TouchGFX 框架会捕获此事件,并在AbstractButton::handleClickEvent 函数中调用 action->execute(*this)函数,最终即执行了 flexButtonCallbackHandler 函数,实现了用户的动作响应。
" F5 y+ }2 Z, g5 R6 Y
7 a% \9 z- V7 G% R  F3 v' `小结
5 P9 Z5 h9 h6 P6 F本文介绍了 TouchGFX 中 Callback 模板的基本原理,并结合两个例子,简要说明了 Callback 模板在框架中的使用方法。只要注意 Callback 的类型、初始化过程,以及最终 execute 函数执行的函数主体,就能理解 Callback 模板了,这对深入理解 TouchGFX 也有一定帮助。
0 K/ j% ^2 S# b% B% h7 ~4 Q) Q( X5 I% W
赞 收藏 评论0 发布时间:2022-2-18 23:02

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版